[VBSCRIPT] Sync with Wine Staging 3.9. CORE-14656
[reactos.git] / dll / win32 / vbscript / compile.c
index efb8e91..302804f 100644 (file)
@@ -20,9 +20,9 @@
 
 #include "vbscript.h"
 #include "parse.h"
-//#include "parser.tab.h"
+#include "parser.tab.h"
 
-#include <wine/debug.h>
+#include "wine/debug.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(vbscript);
 WINE_DECLARE_DEBUG_CHANNEL(vbscript_disas);
@@ -54,6 +54,7 @@ typedef struct {
     unsigned prop_end_label;
 
     dim_decl_t *dim_decls;
+    dim_decl_t *dim_decls_tail;
     dynamic_var_t *global_vars;
 
     const_decl_t *const_decls;
@@ -345,6 +346,35 @@ static inline void label_set_addr(compile_ctx_t *ctx, unsigned label)
     ctx->labels[label & ~LABEL_FLAG] = ctx->instr_cnt;
 }
 
+static inline unsigned stack_offset(compile_ctx_t *ctx)
+{
+    statement_ctx_t *iter;
+    unsigned ret = 0;
+
+    for(iter = ctx->stat_ctx; iter; iter = iter->next)
+        ret += iter->stack_use;
+
+    return ret;
+}
+
+static BOOL emit_catch_jmp(compile_ctx_t *ctx, unsigned stack_off, unsigned code_off)
+{
+    unsigned code;
+
+    code = push_instr(ctx, OP_catch);
+    if(!code)
+        return FALSE;
+
+    instr_ptr(ctx, code)->arg1.uint = code_off;
+    instr_ptr(ctx, code)->arg2.uint = stack_off + stack_offset(ctx);
+    return TRUE;
+}
+
+static inline BOOL emit_catch(compile_ctx_t *ctx, unsigned off)
+{
+    return emit_catch_jmp(ctx, off, ctx->instr_cnt);
+}
+
 static expression_t *lookup_const_decls(compile_ctx_t *ctx, const WCHAR *name, BOOL lookup_global)
 {
     const_decl_t *decl;
@@ -492,6 +522,8 @@ static HRESULT compile_expression(compile_ctx_t *ctx, expression_t *expr)
         return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_nequal);
     case EXPR_NEW:
         return push_instr_str(ctx, OP_new, ((string_expression_t*)expr)->value);
+    case EXPR_NOARG:
+        return push_instr_int(ctx, OP_hres, DISP_E_PARAMNOTFOUND);
     case EXPR_NOT:
         return compile_unary_expression(ctx, (unary_expression_t*)expr, OP_not);
     case EXPR_NOTHING:
@@ -532,6 +564,9 @@ static HRESULT compile_if_statement(compile_ctx_t *ctx, if_statement_t *stat)
     if(!cnd_jmp)
         return E_OUTOFMEMORY;
 
+    if(!emit_catch(ctx, 0))
+        return E_OUTOFMEMORY;
+
     hres = compile_statement(ctx, NULL, stat->if_stat);
     if(FAILED(hres))
         return hres;
@@ -557,6 +592,9 @@ static HRESULT compile_if_statement(compile_ctx_t *ctx, if_statement_t *stat)
         if(!cnd_jmp)
             return E_OUTOFMEMORY;
 
+        if(!emit_catch(ctx, 0))
+            return E_OUTOFMEMORY;
+
         hres = compile_statement(ctx, NULL, elseif_decl->stat);
         if(FAILED(hres))
             return hres;
@@ -596,6 +634,9 @@ static HRESULT compile_while_statement(compile_ctx_t *ctx, while_statement_t *st
     if(!jmp_end)
         return E_OUTOFMEMORY;
 
+    if(!emit_catch(ctx, 0))
+        return E_OUTOFMEMORY;
+
     if(stat->stat.type == STAT_WHILE) {
         loop_ctx = NULL;
     }else {
@@ -651,6 +692,10 @@ static HRESULT compile_dowhile_statement(compile_ctx_t *ctx, while_statement_t *
         return hres;
 
     label_set_addr(ctx, loop_ctx.while_end_label);
+
+    if(!emit_catch(ctx, 0))
+        return E_OUTOFMEMORY;
+
     return S_OK;
 }
 
@@ -660,6 +705,10 @@ static HRESULT compile_foreach_statement(compile_ctx_t *ctx, foreach_statement_t
     unsigned loop_start;
     HRESULT hres;
 
+    /* Preserve a place on the stack in case we throw before having proper enum collection. */
+    if(!push_instr(ctx, OP_empty))
+        return E_OUTOFMEMORY;
+
     hres = compile_expression(ctx, stat->group_expr);
     if(FAILED(hres))
         return hres;
@@ -667,7 +716,6 @@ static HRESULT compile_foreach_statement(compile_ctx_t *ctx, foreach_statement_t
     if(!push_instr(ctx, OP_newenum))
         return E_OUTOFMEMORY;
 
-    loop_start = ctx->instr_cnt;
     if(!(loop_ctx.for_end_label = alloc_label(ctx)))
         return E_OUTOFMEMORY;
 
@@ -675,10 +723,19 @@ static HRESULT compile_foreach_statement(compile_ctx_t *ctx, foreach_statement_t
     if(FAILED(hres))
         return hres;
 
+    if(!emit_catch(ctx, 1))
+        return E_OUTOFMEMORY;
+
+    loop_start = ctx->instr_cnt;
     hres = compile_statement(ctx, &loop_ctx, stat->body);
     if(FAILED(hres))
         return hres;
 
+    /* We need a separated enumnext here, because we need to jump out of the loop on exception. */
+    hres = push_instr_uint_bstr(ctx, OP_enumnext, loop_ctx.for_end_label, stat->identifier);
+    if(FAILED(hres))
+        return hres;
+
     hres = push_instr_addr(ctx, OP_jmp, loop_start);
     if(FAILED(hres))
         return hres;
@@ -702,6 +759,7 @@ static HRESULT compile_forto_statement(compile_ctx_t *ctx, forto_statement_t *st
     if(FAILED(hres))
         return hres;
 
+    /* FIXME: Assign should happen after both expressions evaluation. */
     instr = push_instr(ctx, OP_assign_ident);
     if(!instr)
         return E_OUTOFMEMORY;
@@ -738,10 +796,14 @@ static HRESULT compile_forto_statement(compile_ctx_t *ctx, forto_statement_t *st
     instr_ptr(ctx, step_instr)->arg2.bstr = identifier;
     instr_ptr(ctx, step_instr)->arg1.uint = loop_ctx.for_end_label;
 
+    if(!emit_catch(ctx, 2))
+        return E_OUTOFMEMORY;
+
     hres = compile_statement(ctx, &loop_ctx, stat->body);
     if(FAILED(hres))
         return hres;
 
+    /* FIXME: Error handling can't be done compatible with native using OP_incc here. */
     instr = push_instr(ctx, OP_incc);
     if(!instr)
         return E_OUTOFMEMORY;
@@ -756,6 +818,11 @@ static HRESULT compile_forto_statement(compile_ctx_t *ctx, forto_statement_t *st
         return hres;
 
     label_set_addr(ctx, loop_ctx.for_end_label);
+
+    /* FIXME: reconsider after OP_incc fixup. */
+    if(!emit_catch(ctx, 0))
+        return E_OUTOFMEMORY;
+
     return S_OK;
 }
 
@@ -777,6 +844,9 @@ static HRESULT compile_select_statement(compile_ctx_t *ctx, select_statement_t *
     if(!end_label)
         return E_OUTOFMEMORY;
 
+    if(!emit_catch_jmp(ctx, 0, end_label))
+        return E_OUTOFMEMORY;
+
     for(case_iter = stat->case_clausules; case_iter; case_iter = case_iter->next)
         case_cnt++;
 
@@ -804,6 +874,11 @@ static HRESULT compile_select_statement(compile_ctx_t *ctx, select_statement_t *
             hres = push_instr_addr(ctx, OP_case, case_labels[i]);
             if(FAILED(hres))
                 break;
+
+            if(!emit_catch_jmp(ctx, 0, case_labels[i])) {
+                hres = E_OUTOFMEMORY;
+                break;
+            }
         }
     }
 
@@ -870,7 +945,14 @@ static HRESULT compile_assignment(compile_ctx_t *ctx, member_expression_t *membe
     if(FAILED(hres))
         return hres;
 
-    return push_instr_bstr_uint(ctx, op, member_expr->identifier, args_cnt);
+    hres = push_instr_bstr_uint(ctx, op, member_expr->identifier, args_cnt);
+    if(FAILED(hres))
+        return hres;
+
+    if(!emit_catch(ctx, 0))
+        return E_OUTOFMEMORY;
+
+    return S_OK;
 }
 
 static HRESULT compile_assign_statement(compile_ctx_t *ctx, assign_statement_t *stat, BOOL is_set)
@@ -880,6 +962,8 @@ static HRESULT compile_assign_statement(compile_ctx_t *ctx, assign_statement_t *
 
 static HRESULT compile_call_statement(compile_ctx_t *ctx, call_statement_t *stat)
 {
+    HRESULT hres;
+
     /* It's challenging for parser to distinguish parameterized assignment with one argument from call
      * with equality expression argument, so we do it in compiler. */
     if(!stat->is_strict && stat->expr->args && !stat->expr->args->next && stat->expr->args->type == EXPR_EQUAL) {
@@ -895,7 +979,14 @@ static HRESULT compile_call_statement(compile_ctx_t *ctx, call_statement_t *stat
         }
     }
 
-    return compile_member_expression(ctx, stat->expr, FALSE);
+    hres = compile_member_expression(ctx, stat->expr, FALSE);
+    if(FAILED(hres))
+        return hres;
+
+    if(!emit_catch(ctx, 0))
+        return E_OUTOFMEMORY;
+
+    return S_OK;
 }
 
 static BOOL lookup_dim_decls(compile_ctx_t *ctx, const WCHAR *name)
@@ -934,13 +1025,26 @@ static HRESULT compile_dim_statement(compile_ctx_t *ctx, dim_statement_t *stat)
         }
 
         ctx->func->var_cnt++;
+
+        if(dim_decl->is_array) {
+            HRESULT hres = push_instr_bstr_uint(ctx, OP_dim, dim_decl->name, ctx->func->array_cnt++);
+            if(FAILED(hres))
+                return hres;
+
+            if(!emit_catch(ctx, 0))
+                return E_OUTOFMEMORY;
+        }
+
         if(!dim_decl->next)
             break;
         dim_decl = dim_decl->next;
     }
 
-    dim_decl->next = ctx->dim_decls;
-    ctx->dim_decls = stat->dim_decls;
+    if(ctx->dim_decls_tail)
+        ctx->dim_decls_tail->next = stat->dim_decls;
+    else
+        ctx->dim_decls = stat->dim_decls;
+    ctx->dim_decls_tail = dim_decl;
     return S_OK;
 }
 
@@ -967,6 +1071,9 @@ static HRESULT compile_const_statement(compile_ctx_t *ctx, const_statement_t *st
             hres = push_instr_bstr(ctx, OP_const, decl->name);
             if(FAILED(hres))
                 return hres;
+
+            if(!emit_catch(ctx, 0))
+                return E_OUTOFMEMORY;
         }
 
         next_decl = decl->next;
@@ -1043,11 +1150,7 @@ static HRESULT compile_exitfor_statement(compile_ctx_t *ctx)
 
 static HRESULT exit_label(compile_ctx_t *ctx, unsigned jmp_label)
 {
-    statement_ctx_t *iter;
-    unsigned pop_cnt = 0;
-
-    for(iter = ctx->stat_ctx; iter; iter = iter->next)
-        pop_cnt += iter->stack_use;
+    unsigned pop_cnt = stack_offset(ctx);
 
     if(pop_cnt) {
         HRESULT hres;
@@ -1199,6 +1302,28 @@ static void resolve_labels(compile_ctx_t *ctx, unsigned off)
     ctx->labels_cnt = 0;
 }
 
+static HRESULT fill_array_desc(compile_ctx_t *ctx, dim_decl_t *dim_decl, array_desc_t *array_desc)
+{
+    unsigned dim_cnt = 0, i;
+    dim_list_t *iter;
+
+    for(iter = dim_decl->dims; iter; iter = iter->next)
+        dim_cnt++;
+
+    array_desc->bounds = compiler_alloc(ctx->code, dim_cnt * sizeof(SAFEARRAYBOUND));
+    if(!array_desc->bounds)
+        return E_OUTOFMEMORY;
+
+    array_desc->dim_cnt = dim_cnt;
+
+    for(iter = dim_decl->dims, i=0; iter; iter = iter->next, i++) {
+        array_desc->bounds[i].cElements = iter->val+1;
+        array_desc->bounds[i].lLbound = 0;
+    }
+
+    return S_OK;
+}
+
 static HRESULT compile_func(compile_ctx_t *ctx, statement_t *stat, function_t *func)
 {
     HRESULT hres;
@@ -1233,7 +1358,7 @@ static HRESULT compile_func(compile_ctx_t *ctx, statement_t *stat, function_t *f
     }
 
     ctx->func = func;
-    ctx->dim_decls = NULL;
+    ctx->dim_decls = ctx->dim_decls_tail = NULL;
     ctx->const_decls = NULL;
     hres = compile_statement(ctx, NULL, stat);
     ctx->func = NULL;
@@ -1292,6 +1417,25 @@ static HRESULT compile_func(compile_ctx_t *ctx, statement_t *stat, function_t *f
         }
     }
 
+    if(func->array_cnt) {
+        unsigned array_id = 0;
+        dim_decl_t *dim_decl;
+
+        func->array_descs = compiler_alloc(ctx->code, func->array_cnt * sizeof(array_desc_t));
+        if(!func->array_descs)
+            return E_OUTOFMEMORY;
+
+        for(dim_decl = ctx->dim_decls; dim_decl; dim_decl = dim_decl->next) {
+            if(dim_decl->is_array) {
+                hres = fill_array_desc(ctx, dim_decl, func->array_descs + array_id++);
+                if(FAILED(hres))
+                    return hres;
+            }
+        }
+
+        assert(array_id == func->array_cnt);
+    }
+
     return S_OK;
 }
 
@@ -1327,6 +1471,7 @@ static HRESULT create_function(compile_ctx_t *ctx, function_decl_t *decl, functi
 
     func->vars = NULL;
     func->var_cnt = 0;
+    func->array_cnt = 0;
     func->code_ctx = ctx->code;
     func->type = decl->type;
     func->is_public = decl->is_public;
@@ -1428,8 +1573,8 @@ static BOOL lookup_class_funcs(class_desc_t *class_desc, const WCHAR *name)
 static HRESULT compile_class(compile_ctx_t *ctx, class_decl_t *class_decl)
 {
     function_decl_t *func_decl, *func_prop_decl;
-    class_prop_decl_t *prop_decl;
     class_desc_t *class_desc;
+    dim_decl_t *prop_decl;
     unsigned i;
     HRESULT hres;
 
@@ -1513,6 +1658,24 @@ static HRESULT compile_class(compile_ctx_t *ctx, class_decl_t *class_decl)
             return E_OUTOFMEMORY;
 
         class_desc->props[i].is_public = prop_decl->is_public;
+        class_desc->props[i].is_array = prop_decl->is_array;
+
+        if(prop_decl->is_array)
+            class_desc->array_cnt++;
+    }
+
+    if(class_desc->array_cnt) {
+        class_desc->array_descs = compiler_alloc(ctx->code, class_desc->array_cnt*sizeof(*class_desc->array_descs));
+        if(!class_desc->array_descs)
+            return E_OUTOFMEMORY;
+
+        for(prop_decl = class_decl->props, i=0; prop_decl; prop_decl = prop_decl->next) {
+            if(prop_decl->is_array) {
+                hres = fill_array_desc(ctx, prop_decl, class_desc->array_descs + i++);
+                if(FAILED(hres))
+                    return hres;
+            }
+        }
     }
 
     class_desc->next = ctx->classes;
@@ -1583,6 +1746,8 @@ void release_vbscode(vbscode_t *code)
     for(i=0; i < code->bstr_cnt; i++)
         SysFreeString(code->bstr_pool[i]);
 
+    if(code->context)
+        IDispatch_Release(code->context);
     heap_pool_free(&code->heap);
 
     heap_free(code->bstr_pool);
@@ -1595,7 +1760,7 @@ static vbscode_t *alloc_vbscode(compile_ctx_t *ctx, const WCHAR *source)
 {
     vbscode_t *ret;
 
-    ret = heap_alloc(sizeof(*ret));
+    ret = heap_alloc_zero(sizeof(*ret));
     if(!ret)
         return NULL;
 
@@ -1617,18 +1782,8 @@ static vbscode_t *alloc_vbscode(compile_ctx_t *ctx, const WCHAR *source)
 
     ret->option_explicit = ctx->parser.option_explicit;
 
-    ret->bstr_pool = NULL;
-    ret->bstr_pool_size = 0;
-    ret->bstr_cnt = 0;
-    ret->pending_exec = FALSE;
-
     ret->main_code.type = FUNC_GLOBAL;
-    ret->main_code.name = NULL;
     ret->main_code.code_ctx = ret;
-    ret->main_code.vars = NULL;
-    ret->main_code.var_cnt = 0;
-    ret->main_code.arg_cnt = 0;
-    ret->main_code.args = NULL;
 
     list_init(&ret->entry);
     return ret;
@@ -1662,7 +1817,6 @@ HRESULT compile_script(script_ctx_t *script, const WCHAR *src, const WCHAR *deli
     ctx.funcs = NULL;
     ctx.func_decls = NULL;
     ctx.global_vars = NULL;
-    ctx.dim_decls = NULL;
     ctx.classes = NULL;
     ctx.labels = NULL;
     ctx.global_consts = NULL;