From ae89b1ca95eb63656fd69bc5603bf72ab7fb6519 Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Wed, 15 May 2013 17:33:01 +0000 Subject: [PATCH] [VBSCRIPT] * Import from Wine 1.5.26. svn path=/trunk/; revision=59017 --- reactos/dll/win32/CMakeLists.txt | 1 + reactos/dll/win32/vbscript/CMakeLists.txt | 40 + reactos/dll/win32/vbscript/compile.c | 1744 ++++++++ reactos/dll/win32/vbscript/error.c | 91 + reactos/dll/win32/vbscript/global.c | 1839 +++++++++ reactos/dll/win32/vbscript/interp.c | 1942 +++++++++ reactos/dll/win32/vbscript/lex.c | 444 ++ reactos/dll/win32/vbscript/parse.h | 272 ++ reactos/dll/win32/vbscript/parser.tab.c | 3658 +++++++++++++++++ reactos/dll/win32/vbscript/parser.tab.h | 150 + reactos/dll/win32/vbscript/parser.y | 965 +++++ reactos/dll/win32/vbscript/regexp.c | 3306 +++++++++++++++ reactos/dll/win32/vbscript/regexp.h | 82 + reactos/dll/win32/vbscript/vbdisp.c | 971 +++++ reactos/dll/win32/vbscript/vbregexp.c | 1462 +++++++ reactos/dll/win32/vbscript/vbscript.c | 871 ++++ reactos/dll/win32/vbscript/vbscript.h | 412 ++ reactos/dll/win32/vbscript/vbscript.rc | 42 + reactos/dll/win32/vbscript/vbscript.rgs | 82 + reactos/dll/win32/vbscript/vbscript.spec | 4 + .../dll/win32/vbscript/vbscript_classes.idl | 50 + .../dll/win32/vbscript/vbscript_classes.rgs | 46 + reactos/dll/win32/vbscript/vbscript_defs.h | 236 ++ reactos/dll/win32/vbscript/vbscript_main.c | 383 ++ reactos/dll/win32/vbscript/vbsglobal.idl | 755 ++++ reactos/dll/win32/vbscript/vbsglobal.rgs | 32 + reactos/dll/win32/vbscript/vbsregexp10.idl | 144 + reactos/dll/win32/vbscript/vbsregexp10.rgs | 38 + reactos/dll/win32/vbscript/vbsregexp55.idl | 270 ++ reactos/dll/win32/vbscript/vbsregexp55.rgs | 62 + reactos/media/doc/README.WINE | 1 + 31 files changed, 20395 insertions(+) create mode 100644 reactos/dll/win32/vbscript/CMakeLists.txt create mode 100644 reactos/dll/win32/vbscript/compile.c create mode 100644 reactos/dll/win32/vbscript/error.c create mode 100644 reactos/dll/win32/vbscript/global.c create mode 100644 reactos/dll/win32/vbscript/interp.c create mode 100644 reactos/dll/win32/vbscript/lex.c create mode 100644 reactos/dll/win32/vbscript/parse.h create mode 100644 reactos/dll/win32/vbscript/parser.tab.c create mode 100644 reactos/dll/win32/vbscript/parser.tab.h create mode 100644 reactos/dll/win32/vbscript/parser.y create mode 100644 reactos/dll/win32/vbscript/regexp.c create mode 100644 reactos/dll/win32/vbscript/regexp.h create mode 100644 reactos/dll/win32/vbscript/vbdisp.c create mode 100644 reactos/dll/win32/vbscript/vbregexp.c create mode 100644 reactos/dll/win32/vbscript/vbscript.c create mode 100644 reactos/dll/win32/vbscript/vbscript.h create mode 100644 reactos/dll/win32/vbscript/vbscript.rc create mode 100644 reactos/dll/win32/vbscript/vbscript.rgs create mode 100644 reactos/dll/win32/vbscript/vbscript.spec create mode 100644 reactos/dll/win32/vbscript/vbscript_classes.idl create mode 100644 reactos/dll/win32/vbscript/vbscript_classes.rgs create mode 100644 reactos/dll/win32/vbscript/vbscript_defs.h create mode 100644 reactos/dll/win32/vbscript/vbscript_main.c create mode 100644 reactos/dll/win32/vbscript/vbsglobal.idl create mode 100644 reactos/dll/win32/vbscript/vbsglobal.rgs create mode 100644 reactos/dll/win32/vbscript/vbsregexp10.idl create mode 100644 reactos/dll/win32/vbscript/vbsregexp10.rgs create mode 100644 reactos/dll/win32/vbscript/vbsregexp55.idl create mode 100644 reactos/dll/win32/vbscript/vbsregexp55.rgs diff --git a/reactos/dll/win32/CMakeLists.txt b/reactos/dll/win32/CMakeLists.txt index 34fca7eaa5f..4f082d3c448 100644 --- a/reactos/dll/win32/CMakeLists.txt +++ b/reactos/dll/win32/CMakeLists.txt @@ -207,6 +207,7 @@ add_subdirectory(urlmon) add_subdirectory(userenv) add_subdirectory(usp10) add_subdirectory(uxtheme) +add_subdirectory(vbscript) add_subdirectory(vdmdbg) add_subdirectory(version) if(ARCH STREQUAL "i386") diff --git a/reactos/dll/win32/vbscript/CMakeLists.txt b/reactos/dll/win32/vbscript/CMakeLists.txt new file mode 100644 index 00000000000..b86777ad217 --- /dev/null +++ b/reactos/dll/win32/vbscript/CMakeLists.txt @@ -0,0 +1,40 @@ + +add_definitions(-D__WINESRC__) +include_directories(${REACTOS_SOURCE_DIR}/include/reactos/wine) +spec2def(vbscript.dll vbscript.spec) + +list(APPEND SOURCE + compile.c + error.c + global.c + interp.c + lex.c + parser.tab.c + regexp.c + vbdisp.c + vbregexp.c + vbscript.c + vbscript_main.c + ${CMAKE_CURRENT_BINARY_DIR}/vbscript.def) + +add_library(vbscript SHARED ${SOURCE} vbscript.rc) +add_idl_headers(vbscript_idlheader vbscript_classes.idl vbsglobal.idl vbsregexp55.idl) + +add_typelib(vbsglobal.idl vbsregexp10.idl vbsregexp55.idl) + +list(APPEND vbscript_rc_deps + ${CMAKE_CURRENT_SOURCE_DIR}/vbscript_classes.rgs + ${CMAKE_CURRENT_SOURCE_DIR}/vbsglobal.rgs + ${CMAKE_CURRENT_SOURCE_DIR}/vbsregexp10.rgs + ${CMAKE_CURRENT_SOURCE_DIR}/vbsregexp55.rgs + ${CMAKE_CURRENT_BINARY_DIR}/vbsglobal.tlb + ${CMAKE_CURRENT_BINARY_DIR}/vbsregexp10.tlb + ${CMAKE_CURRENT_BINARY_DIR}/vbsregexp55.tlb) + +set_source_files_properties(vbscript.rc PROPERTIES OBJECT_DEPENDS "${vbscript_rc_deps}") + +add_dependencies(vbscript vbscript_idlheader stdole2) +set_module_type(vbscript win32dll) +target_link_libraries(vbscript wine) +add_importlibs(vbscript oleaut32 ole32 user32 msvcrt kernel32 ntdll) +add_cd_file(TARGET vbscript DESTINATION reactos/system32 FOR all) diff --git a/reactos/dll/win32/vbscript/compile.c b/reactos/dll/win32/vbscript/compile.c new file mode 100644 index 00000000000..efb8e9187f6 --- /dev/null +++ b/reactos/dll/win32/vbscript/compile.c @@ -0,0 +1,1744 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "vbscript.h" +#include "parse.h" +//#include "parser.tab.h" + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(vbscript); +WINE_DECLARE_DEBUG_CHANNEL(vbscript_disas); + +typedef struct _statement_ctx_t { + unsigned stack_use; + + unsigned while_end_label; + unsigned for_end_label; + + struct _statement_ctx_t *next; +} statement_ctx_t; + +typedef struct { + parser_ctx_t parser; + + unsigned instr_cnt; + unsigned instr_size; + vbscode_t *code; + + statement_ctx_t *stat_ctx; + + unsigned *labels; + unsigned labels_size; + unsigned labels_cnt; + + unsigned sub_end_label; + unsigned func_end_label; + unsigned prop_end_label; + + dim_decl_t *dim_decls; + dynamic_var_t *global_vars; + + const_decl_t *const_decls; + const_decl_t *global_consts; + + function_t *func; + function_t *funcs; + function_decl_t *func_decls; + + class_desc_t *classes; +} compile_ctx_t; + +static HRESULT compile_expression(compile_ctx_t*,expression_t*); +static HRESULT compile_statement(compile_ctx_t*,statement_ctx_t*,statement_t*); + +static const struct { + const char *op_str; + instr_arg_type_t arg1_type; + instr_arg_type_t arg2_type; +} instr_info[] = { +#define X(n,a,b,c) {#n,b,c}, +OP_LIST +#undef X +}; + +static void dump_instr_arg(instr_arg_type_t type, instr_arg_t *arg) +{ + switch(type) { + case ARG_STR: + case ARG_BSTR: + TRACE_(vbscript_disas)("\t%s", debugstr_w(arg->str)); + break; + case ARG_INT: + TRACE_(vbscript_disas)("\t%d", arg->uint); + break; + case ARG_UINT: + case ARG_ADDR: + TRACE_(vbscript_disas)("\t%u", arg->uint); + break; + case ARG_DOUBLE: + TRACE_(vbscript_disas)("\t%lf", *arg->dbl); + break; + case ARG_NONE: + break; + DEFAULT_UNREACHABLE; + } +} + +static void dump_code(compile_ctx_t *ctx) +{ + instr_t *instr; + + for(instr = ctx->code->instrs+1; instr < ctx->code->instrs+ctx->instr_cnt; instr++) { + assert(instr->op < OP_LAST); + TRACE_(vbscript_disas)("%d:\t%s", (int)(instr-ctx->code->instrs), instr_info[instr->op].op_str); + dump_instr_arg(instr_info[instr->op].arg1_type, &instr->arg1); + dump_instr_arg(instr_info[instr->op].arg2_type, &instr->arg2); + TRACE_(vbscript_disas)("\n"); + } +} + +static inline void *compiler_alloc(vbscode_t *vbscode, size_t size) +{ + return heap_pool_alloc(&vbscode->heap, size); +} + +static inline void *compiler_alloc_zero(vbscode_t *vbscode, size_t size) +{ + void *ret; + + ret = heap_pool_alloc(&vbscode->heap, size); + if(ret) + memset(ret, 0, size); + return ret; +} + +static WCHAR *compiler_alloc_string(vbscode_t *vbscode, const WCHAR *str) +{ + size_t size; + WCHAR *ret; + + size = (strlenW(str)+1)*sizeof(WCHAR); + ret = compiler_alloc(vbscode, size); + if(ret) + memcpy(ret, str, size); + return ret; +} + +static inline instr_t *instr_ptr(compile_ctx_t *ctx, unsigned id) +{ + assert(id < ctx->instr_cnt); + return ctx->code->instrs + id; +} + +static unsigned push_instr(compile_ctx_t *ctx, vbsop_t op) +{ + assert(ctx->instr_size && ctx->instr_size >= ctx->instr_cnt); + + if(ctx->instr_size == ctx->instr_cnt) { + instr_t *new_instr; + + new_instr = heap_realloc(ctx->code->instrs, ctx->instr_size*2*sizeof(instr_t)); + if(!new_instr) + return 0; + + ctx->code->instrs = new_instr; + ctx->instr_size *= 2; + } + + ctx->code->instrs[ctx->instr_cnt].op = op; + return ctx->instr_cnt++; +} + +static HRESULT push_instr_int(compile_ctx_t *ctx, vbsop_t op, LONG arg) +{ + unsigned ret; + + ret = push_instr(ctx, op); + if(!ret) + return E_OUTOFMEMORY; + + instr_ptr(ctx, ret)->arg1.lng = arg; + return S_OK; +} + +static HRESULT push_instr_uint(compile_ctx_t *ctx, vbsop_t op, unsigned arg) +{ + unsigned ret; + + ret = push_instr(ctx, op); + if(!ret) + return E_OUTOFMEMORY; + + instr_ptr(ctx, ret)->arg1.uint = arg; + return S_OK; +} + +static HRESULT push_instr_addr(compile_ctx_t *ctx, vbsop_t op, unsigned arg) +{ + unsigned ret; + + ret = push_instr(ctx, op); + if(!ret) + return E_OUTOFMEMORY; + + instr_ptr(ctx, ret)->arg1.uint = arg; + return S_OK; +} + +static HRESULT push_instr_str(compile_ctx_t *ctx, vbsop_t op, const WCHAR *arg) +{ + unsigned instr; + WCHAR *str; + + str = compiler_alloc_string(ctx->code, arg); + if(!str) + return E_OUTOFMEMORY; + + instr = push_instr(ctx, op); + if(!instr) + return E_OUTOFMEMORY; + + instr_ptr(ctx, instr)->arg1.str = str; + return S_OK; +} + +static HRESULT push_instr_double(compile_ctx_t *ctx, vbsop_t op, double arg) +{ + unsigned instr; + double *d; + + d = compiler_alloc(ctx->code, sizeof(double)); + if(!d) + return E_OUTOFMEMORY; + + instr = push_instr(ctx, op); + if(!instr) + return E_OUTOFMEMORY; + + *d = arg; + instr_ptr(ctx, instr)->arg1.dbl = d; + return S_OK; +} + +static BSTR alloc_bstr_arg(compile_ctx_t *ctx, const WCHAR *str) +{ + if(!ctx->code->bstr_pool_size) { + ctx->code->bstr_pool = heap_alloc(8 * sizeof(BSTR)); + if(!ctx->code->bstr_pool) + return NULL; + ctx->code->bstr_pool_size = 8; + }else if(ctx->code->bstr_pool_size == ctx->code->bstr_cnt) { + BSTR *new_pool; + + new_pool = heap_realloc(ctx->code->bstr_pool, ctx->code->bstr_pool_size*2*sizeof(BSTR)); + if(!new_pool) + return NULL; + + ctx->code->bstr_pool = new_pool; + ctx->code->bstr_pool_size *= 2; + } + + ctx->code->bstr_pool[ctx->code->bstr_cnt] = SysAllocString(str); + if(!ctx->code->bstr_pool[ctx->code->bstr_cnt]) + return NULL; + + return ctx->code->bstr_pool[ctx->code->bstr_cnt++]; +} + +static HRESULT push_instr_bstr(compile_ctx_t *ctx, vbsop_t op, const WCHAR *arg) +{ + unsigned instr; + BSTR bstr; + + bstr = alloc_bstr_arg(ctx, arg); + if(!bstr) + return E_OUTOFMEMORY; + + instr = push_instr(ctx, op); + if(!instr) + return E_OUTOFMEMORY; + + instr_ptr(ctx, instr)->arg1.bstr = bstr; + return S_OK; +} + +static HRESULT push_instr_bstr_uint(compile_ctx_t *ctx, vbsop_t op, const WCHAR *arg1, unsigned arg2) +{ + unsigned instr; + BSTR bstr; + + bstr = alloc_bstr_arg(ctx, arg1); + if(!bstr) + return E_OUTOFMEMORY; + + instr = push_instr(ctx, op); + if(!instr) + return E_OUTOFMEMORY; + + instr_ptr(ctx, instr)->arg1.bstr = bstr; + instr_ptr(ctx, instr)->arg2.uint = arg2; + return S_OK; +} + +static HRESULT push_instr_uint_bstr(compile_ctx_t *ctx, vbsop_t op, unsigned arg1, const WCHAR *arg2) +{ + unsigned instr; + BSTR bstr; + + bstr = alloc_bstr_arg(ctx, arg2); + if(!bstr) + return E_OUTOFMEMORY; + + instr = push_instr(ctx, op); + if(!instr) + return E_OUTOFMEMORY; + + instr_ptr(ctx, instr)->arg1.uint = arg1; + instr_ptr(ctx, instr)->arg2.bstr = bstr; + return S_OK; +} + +#define LABEL_FLAG 0x80000000 + +static unsigned alloc_label(compile_ctx_t *ctx) +{ + if(!ctx->labels_size) { + ctx->labels = heap_alloc(8 * sizeof(*ctx->labels)); + if(!ctx->labels) + return 0; + ctx->labels_size = 8; + }else if(ctx->labels_size == ctx->labels_cnt) { + unsigned *new_labels; + + new_labels = heap_realloc(ctx->labels, 2*ctx->labels_size*sizeof(*ctx->labels)); + if(!new_labels) + return 0; + + ctx->labels = new_labels; + ctx->labels_size *= 2; + } + + return ctx->labels_cnt++ | LABEL_FLAG; +} + +static inline void label_set_addr(compile_ctx_t *ctx, unsigned label) +{ + assert(label & LABEL_FLAG); + ctx->labels[label & ~LABEL_FLAG] = ctx->instr_cnt; +} + +static expression_t *lookup_const_decls(compile_ctx_t *ctx, const WCHAR *name, BOOL lookup_global) +{ + const_decl_t *decl; + + for(decl = ctx->const_decls; decl; decl = decl->next) { + if(!strcmpiW(decl->name, name)) + return decl->value_expr; + } + + if(!lookup_global) + return NULL; + + for(decl = ctx->global_consts; decl; decl = decl->next) { + if(!strcmpiW(decl->name, name)) + return decl->value_expr; + } + + return NULL; +} + +static HRESULT compile_args(compile_ctx_t *ctx, expression_t *args, unsigned *ret) +{ + unsigned arg_cnt = 0; + HRESULT hres; + + while(args) { + hres = compile_expression(ctx, args); + if(FAILED(hres)) + return hres; + + arg_cnt++; + args = args->next; + } + + *ret = arg_cnt; + return S_OK; +} + +static HRESULT compile_member_expression(compile_ctx_t *ctx, member_expression_t *expr, BOOL ret_val) +{ + unsigned arg_cnt = 0; + HRESULT hres; + + if(ret_val && !expr->args) { + expression_t *const_expr; + + const_expr = lookup_const_decls(ctx, expr->identifier, TRUE); + if(const_expr) + return compile_expression(ctx, const_expr); + } + + hres = compile_args(ctx, expr->args, &arg_cnt); + if(FAILED(hres)) + return hres; + + if(expr->obj_expr) { + hres = compile_expression(ctx, expr->obj_expr); + if(FAILED(hres)) + return hres; + + hres = push_instr_bstr_uint(ctx, ret_val ? OP_mcall : OP_mcallv, expr->identifier, arg_cnt); + }else { + hres = push_instr_bstr_uint(ctx, ret_val ? OP_icall : OP_icallv, expr->identifier, arg_cnt); + } + + return hres; +} + +static HRESULT compile_unary_expression(compile_ctx_t *ctx, unary_expression_t *expr, vbsop_t op) +{ + HRESULT hres; + + hres = compile_expression(ctx, expr->subexpr); + if(FAILED(hres)) + return hres; + + return push_instr(ctx, op) ? S_OK : E_OUTOFMEMORY; +} + +static HRESULT compile_binary_expression(compile_ctx_t *ctx, binary_expression_t *expr, vbsop_t op) +{ + HRESULT hres; + + hres = compile_expression(ctx, expr->left); + if(FAILED(hres)) + return hres; + + hres = compile_expression(ctx, expr->right); + if(FAILED(hres)) + return hres; + + return push_instr(ctx, op) ? S_OK : E_OUTOFMEMORY; +} + +static HRESULT compile_expression(compile_ctx_t *ctx, expression_t *expr) +{ + switch(expr->type) { + case EXPR_ADD: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_add); + case EXPR_AND: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_and); + case EXPR_BOOL: + return push_instr_int(ctx, OP_bool, ((bool_expression_t*)expr)->value); + case EXPR_BRACKETS: + return compile_expression(ctx, ((unary_expression_t*)expr)->subexpr); + case EXPR_CONCAT: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_concat); + case EXPR_DIV: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_div); + case EXPR_DOUBLE: + return push_instr_double(ctx, OP_double, ((double_expression_t*)expr)->value); + case EXPR_EMPTY: + return push_instr(ctx, OP_empty) ? S_OK : E_OUTOFMEMORY; + case EXPR_EQUAL: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_equal); + case EXPR_EQV: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_eqv); + case EXPR_EXP: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_exp); + case EXPR_GT: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_gt); + case EXPR_GTEQ: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_gteq); + case EXPR_IDIV: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_idiv); + case EXPR_IS: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_is); + case EXPR_IMP: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_imp); + case EXPR_LT: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_lt); + case EXPR_LTEQ: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_lteq); + case EXPR_ME: + return push_instr(ctx, OP_me) ? S_OK : E_OUTOFMEMORY; + case EXPR_MEMBER: + return compile_member_expression(ctx, (member_expression_t*)expr, TRUE); + case EXPR_MOD: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_mod); + case EXPR_MUL: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_mul); + case EXPR_NEG: + return compile_unary_expression(ctx, (unary_expression_t*)expr, OP_neg); + case EXPR_NEQUAL: + 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_NOT: + return compile_unary_expression(ctx, (unary_expression_t*)expr, OP_not); + case EXPR_NOTHING: + return push_instr(ctx, OP_nothing) ? S_OK : E_OUTOFMEMORY; + case EXPR_NULL: + return push_instr(ctx, OP_null) ? S_OK : E_OUTOFMEMORY; + case EXPR_OR: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_or); + case EXPR_STRING: + return push_instr_str(ctx, OP_string, ((string_expression_t*)expr)->value); + case EXPR_SUB: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_sub); + case EXPR_USHORT: + return push_instr_int(ctx, OP_short, ((int_expression_t*)expr)->value); + case EXPR_ULONG: + return push_instr_int(ctx, OP_long, ((int_expression_t*)expr)->value); + case EXPR_XOR: + return compile_binary_expression(ctx, (binary_expression_t*)expr, OP_xor); + default: + FIXME("Unimplemented expression type %d\n", expr->type); + return E_NOTIMPL; + } + + return S_OK; +} + +static HRESULT compile_if_statement(compile_ctx_t *ctx, if_statement_t *stat) +{ + unsigned cnd_jmp, endif_label = 0; + elseif_decl_t *elseif_decl; + HRESULT hres; + + hres = compile_expression(ctx, stat->expr); + if(FAILED(hres)) + return hres; + + cnd_jmp = push_instr(ctx, OP_jmp_false); + if(!cnd_jmp) + return E_OUTOFMEMORY; + + hres = compile_statement(ctx, NULL, stat->if_stat); + if(FAILED(hres)) + return hres; + + if(stat->else_stat || stat->elseifs) { + endif_label = alloc_label(ctx); + if(!endif_label) + return E_OUTOFMEMORY; + + hres = push_instr_addr(ctx, OP_jmp, endif_label); + if(FAILED(hres)) + return hres; + } + + for(elseif_decl = stat->elseifs; elseif_decl; elseif_decl = elseif_decl->next) { + instr_ptr(ctx, cnd_jmp)->arg1.uint = ctx->instr_cnt; + + hres = compile_expression(ctx, elseif_decl->expr); + if(FAILED(hres)) + return hres; + + cnd_jmp = push_instr(ctx, OP_jmp_false); + if(!cnd_jmp) + return E_OUTOFMEMORY; + + hres = compile_statement(ctx, NULL, elseif_decl->stat); + if(FAILED(hres)) + return hres; + + hres = push_instr_addr(ctx, OP_jmp, endif_label); + if(FAILED(hres)) + return hres; + } + + instr_ptr(ctx, cnd_jmp)->arg1.uint = ctx->instr_cnt; + + if(stat->else_stat) { + hres = compile_statement(ctx, NULL, stat->else_stat); + if(FAILED(hres)) + return hres; + } + + if(endif_label) + label_set_addr(ctx, endif_label); + return S_OK; +} + +static HRESULT compile_while_statement(compile_ctx_t *ctx, while_statement_t *stat) +{ + statement_ctx_t stat_ctx = {0}, *loop_ctx; + unsigned start_addr; + unsigned jmp_end; + HRESULT hres; + + start_addr = ctx->instr_cnt; + + hres = compile_expression(ctx, stat->expr); + if(FAILED(hres)) + return hres; + + jmp_end = push_instr(ctx, stat->stat.type == STAT_UNTIL ? OP_jmp_true : OP_jmp_false); + if(!jmp_end) + return E_OUTOFMEMORY; + + if(stat->stat.type == STAT_WHILE) { + loop_ctx = NULL; + }else { + if(!(stat_ctx.while_end_label = alloc_label(ctx))) + return E_OUTOFMEMORY; + loop_ctx = &stat_ctx; + } + + hres = compile_statement(ctx, loop_ctx, stat->body); + if(FAILED(hres)) + return hres; + + hres = push_instr_addr(ctx, OP_jmp, start_addr); + if(FAILED(hres)) + return hres; + + instr_ptr(ctx, jmp_end)->arg1.uint = ctx->instr_cnt; + + if(loop_ctx) + label_set_addr(ctx, stat_ctx.while_end_label); + + return S_OK; +} + +static HRESULT compile_dowhile_statement(compile_ctx_t *ctx, while_statement_t *stat) +{ + statement_ctx_t loop_ctx = {0}; + unsigned start_addr; + vbsop_t jmp_op; + HRESULT hres; + + start_addr = ctx->instr_cnt; + + if(!(loop_ctx.while_end_label = alloc_label(ctx))) + return E_OUTOFMEMORY; + + hres = compile_statement(ctx, &loop_ctx, stat->body); + if(FAILED(hres)) + return hres; + + if(stat->expr) { + hres = compile_expression(ctx, stat->expr); + if(FAILED(hres)) + return hres; + + jmp_op = stat->stat.type == STAT_DOUNTIL ? OP_jmp_false : OP_jmp_true; + }else { + jmp_op = OP_jmp; + } + + hres = push_instr_addr(ctx, jmp_op, start_addr); + if(FAILED(hres)) + return hres; + + label_set_addr(ctx, loop_ctx.while_end_label); + return S_OK; +} + +static HRESULT compile_foreach_statement(compile_ctx_t *ctx, foreach_statement_t *stat) +{ + statement_ctx_t loop_ctx = {1}; + unsigned loop_start; + HRESULT hres; + + hres = compile_expression(ctx, stat->group_expr); + if(FAILED(hres)) + return hres; + + 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; + + hres = push_instr_uint_bstr(ctx, OP_enumnext, loop_ctx.for_end_label, stat->identifier); + if(FAILED(hres)) + return hres; + + hres = compile_statement(ctx, &loop_ctx, stat->body); + if(FAILED(hres)) + return hres; + + hres = push_instr_addr(ctx, OP_jmp, loop_start); + if(FAILED(hres)) + return hres; + + label_set_addr(ctx, loop_ctx.for_end_label); + return S_OK; +} + +static HRESULT compile_forto_statement(compile_ctx_t *ctx, forto_statement_t *stat) +{ + statement_ctx_t loop_ctx = {2}; + unsigned step_instr, instr; + BSTR identifier; + HRESULT hres; + + identifier = alloc_bstr_arg(ctx, stat->identifier); + if(!identifier) + return E_OUTOFMEMORY; + + hres = compile_expression(ctx, stat->from_expr); + if(FAILED(hres)) + return hres; + + instr = push_instr(ctx, OP_assign_ident); + if(!instr) + return E_OUTOFMEMORY; + instr_ptr(ctx, instr)->arg1.bstr = identifier; + instr_ptr(ctx, instr)->arg2.uint = 0; + + hres = compile_expression(ctx, stat->to_expr); + if(FAILED(hres)) + return hres; + + if(!push_instr(ctx, OP_val)) + return E_OUTOFMEMORY; + + if(stat->step_expr) { + hres = compile_expression(ctx, stat->step_expr); + if(FAILED(hres)) + return hres; + + if(!push_instr(ctx, OP_val)) + return E_OUTOFMEMORY; + }else { + hres = push_instr_int(ctx, OP_short, 1); + if(FAILED(hres)) + return hres; + } + + loop_ctx.for_end_label = alloc_label(ctx); + if(!loop_ctx.for_end_label) + return E_OUTOFMEMORY; + + step_instr = push_instr(ctx, OP_step); + if(!step_instr) + return E_OUTOFMEMORY; + instr_ptr(ctx, step_instr)->arg2.bstr = identifier; + instr_ptr(ctx, step_instr)->arg1.uint = loop_ctx.for_end_label; + + hres = compile_statement(ctx, &loop_ctx, stat->body); + if(FAILED(hres)) + return hres; + + instr = push_instr(ctx, OP_incc); + if(!instr) + return E_OUTOFMEMORY; + instr_ptr(ctx, instr)->arg1.bstr = identifier; + + hres = push_instr_addr(ctx, OP_jmp, step_instr); + if(FAILED(hres)) + return hres; + + hres = push_instr_uint(ctx, OP_pop, 2); + if(FAILED(hres)) + return hres; + + label_set_addr(ctx, loop_ctx.for_end_label); + return S_OK; +} + +static HRESULT compile_select_statement(compile_ctx_t *ctx, select_statement_t *stat) +{ + unsigned end_label, case_cnt = 0, *case_labels = NULL, i; + case_clausule_t *case_iter; + expression_t *expr_iter; + HRESULT hres; + + hres = compile_expression(ctx, stat->expr); + if(FAILED(hres)) + return hres; + + if(!push_instr(ctx, OP_val)) + return E_OUTOFMEMORY; + + end_label = alloc_label(ctx); + if(!end_label) + return E_OUTOFMEMORY; + + for(case_iter = stat->case_clausules; case_iter; case_iter = case_iter->next) + case_cnt++; + + if(case_cnt) { + case_labels = heap_alloc(case_cnt*sizeof(*case_labels)); + if(!case_labels) + return E_OUTOFMEMORY; + } + + for(case_iter = stat->case_clausules, i=0; case_iter; case_iter = case_iter->next, i++) { + case_labels[i] = alloc_label(ctx); + if(!case_labels[i]) { + hres = E_OUTOFMEMORY; + break; + } + + if(!case_iter->expr) + break; + + for(expr_iter = case_iter->expr; expr_iter; expr_iter = expr_iter->next) { + hres = compile_expression(ctx, expr_iter); + if(FAILED(hres)) + break; + + hres = push_instr_addr(ctx, OP_case, case_labels[i]); + if(FAILED(hres)) + break; + } + } + + if(FAILED(hres)) { + heap_free(case_labels); + return hres; + } + + hres = push_instr_uint(ctx, OP_pop, 1); + if(FAILED(hres)) { + heap_free(case_labels); + return hres; + } + + hres = push_instr_addr(ctx, OP_jmp, case_iter ? case_labels[i] : end_label); + if(FAILED(hres)) { + heap_free(case_labels); + return hres; + } + + for(case_iter = stat->case_clausules, i=0; case_iter; case_iter = case_iter->next, i++) { + label_set_addr(ctx, case_labels[i]); + hres = compile_statement(ctx, NULL, case_iter->stat); + if(FAILED(hres)) + break; + + if(!case_iter->next) + break; + + hres = push_instr_addr(ctx, OP_jmp, end_label); + if(FAILED(hres)) + break; + } + + heap_free(case_labels); + if(FAILED(hres)) + return hres; + + label_set_addr(ctx, end_label); + return S_OK; +} + +static HRESULT compile_assignment(compile_ctx_t *ctx, member_expression_t *member_expr, expression_t *value_expr, BOOL is_set) +{ + unsigned args_cnt; + vbsop_t op; + HRESULT hres; + + if(member_expr->obj_expr) { + hres = compile_expression(ctx, member_expr->obj_expr); + if(FAILED(hres)) + return hres; + + op = is_set ? OP_set_member : OP_assign_member; + }else { + op = is_set ? OP_set_ident : OP_assign_ident; + } + + hres = compile_expression(ctx, value_expr); + if(FAILED(hres)) + return hres; + + hres = compile_args(ctx, member_expr->args, &args_cnt); + if(FAILED(hres)) + return hres; + + return push_instr_bstr_uint(ctx, op, member_expr->identifier, args_cnt); +} + +static HRESULT compile_assign_statement(compile_ctx_t *ctx, assign_statement_t *stat, BOOL is_set) +{ + return compile_assignment(ctx, stat->member_expr, stat->value_expr, is_set); +} + +static HRESULT compile_call_statement(compile_ctx_t *ctx, call_statement_t *stat) +{ + /* 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) { + binary_expression_t *eqexpr = (binary_expression_t*)stat->expr->args; + + if(eqexpr->left->type == EXPR_BRACKETS) { + member_expression_t new_member = *stat->expr; + + WARN("converting call expr to assign expr\n"); + + new_member.args = ((unary_expression_t*)eqexpr->left)->subexpr; + return compile_assignment(ctx, &new_member, eqexpr->right, FALSE); + } + } + + return compile_member_expression(ctx, stat->expr, FALSE); +} + +static BOOL lookup_dim_decls(compile_ctx_t *ctx, const WCHAR *name) +{ + dim_decl_t *dim_decl; + + for(dim_decl = ctx->dim_decls; dim_decl; dim_decl = dim_decl->next) { + if(!strcmpiW(dim_decl->name, name)) + return TRUE; + } + + return FALSE; +} + +static BOOL lookup_args_name(compile_ctx_t *ctx, const WCHAR *name) +{ + unsigned i; + + for(i = 0; i < ctx->func->arg_cnt; i++) { + if(!strcmpiW(ctx->func->args[i].name, name)) + return TRUE; + } + + return FALSE; +} + +static HRESULT compile_dim_statement(compile_ctx_t *ctx, dim_statement_t *stat) +{ + dim_decl_t *dim_decl = stat->dim_decls; + + while(1) { + if(lookup_dim_decls(ctx, dim_decl->name) || lookup_args_name(ctx, dim_decl->name) + || lookup_const_decls(ctx, dim_decl->name, FALSE)) { + FIXME("dim %s name redefined\n", debugstr_w(dim_decl->name)); + return E_FAIL; + } + + ctx->func->var_cnt++; + if(!dim_decl->next) + break; + dim_decl = dim_decl->next; + } + + dim_decl->next = ctx->dim_decls; + ctx->dim_decls = stat->dim_decls; + return S_OK; +} + +static HRESULT compile_const_statement(compile_ctx_t *ctx, const_statement_t *stat) +{ + const_decl_t *decl, *next_decl = stat->decls; + + do { + decl = next_decl; + + if(lookup_const_decls(ctx, decl->name, FALSE) || lookup_args_name(ctx, decl->name) + || lookup_dim_decls(ctx, decl->name)) { + FIXME("%s redefined\n", debugstr_w(decl->name)); + return E_FAIL; + } + + if(ctx->func->type == FUNC_GLOBAL) { + HRESULT hres; + + hres = compile_expression(ctx, decl->value_expr); + if(FAILED(hres)) + return hres; + + hres = push_instr_bstr(ctx, OP_const, decl->name); + if(FAILED(hres)) + return hres; + } + + next_decl = decl->next; + decl->next = ctx->const_decls; + ctx->const_decls = decl; + } while(next_decl); + + return S_OK; +} + +static HRESULT compile_function_statement(compile_ctx_t *ctx, function_statement_t *stat) +{ + if(ctx->func != &ctx->code->main_code) { + FIXME("Function is not in the global code\n"); + return E_FAIL; + } + + stat->func_decl->next = ctx->func_decls; + ctx->func_decls = stat->func_decl; + return S_OK; +} + +static HRESULT compile_exitdo_statement(compile_ctx_t *ctx) +{ + statement_ctx_t *iter; + unsigned pop_cnt = 0; + + for(iter = ctx->stat_ctx; iter; iter = iter->next) { + pop_cnt += iter->stack_use; + if(iter->while_end_label) + break; + } + if(!iter) { + FIXME("Exit Do outside Do Loop\n"); + return E_FAIL; + } + + if(pop_cnt) { + HRESULT hres; + + hres = push_instr_uint(ctx, OP_pop, pop_cnt); + if(FAILED(hres)) + return hres; + } + + return push_instr_addr(ctx, OP_jmp, iter->while_end_label); +} + +static HRESULT compile_exitfor_statement(compile_ctx_t *ctx) +{ + statement_ctx_t *iter; + unsigned pop_cnt = 0; + + for(iter = ctx->stat_ctx; iter; iter = iter->next) { + pop_cnt += iter->stack_use; + if(iter->for_end_label) + break; + } + if(!iter) { + FIXME("Exit For outside For loop\n"); + return E_FAIL; + } + + if(pop_cnt) { + HRESULT hres; + + hres = push_instr_uint(ctx, OP_pop, pop_cnt); + if(FAILED(hres)) + return hres; + } + + return push_instr_addr(ctx, OP_jmp, iter->for_end_label); +} + +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; + + if(pop_cnt) { + HRESULT hres; + + hres = push_instr_uint(ctx, OP_pop, pop_cnt); + if(FAILED(hres)) + return hres; + } + + return push_instr_addr(ctx, OP_jmp, jmp_label); +} + +static HRESULT compile_exitsub_statement(compile_ctx_t *ctx) +{ + if(!ctx->sub_end_label) { + FIXME("Exit Sub outside Sub?\n"); + return E_FAIL; + } + + return exit_label(ctx, ctx->sub_end_label); +} + +static HRESULT compile_exitfunc_statement(compile_ctx_t *ctx) +{ + if(!ctx->func_end_label) { + FIXME("Exit Function outside Function?\n"); + return E_FAIL; + } + + return exit_label(ctx, ctx->func_end_label); +} + +static HRESULT compile_exitprop_statement(compile_ctx_t *ctx) +{ + if(!ctx->prop_end_label) { + FIXME("Exit Property outside Property?\n"); + return E_FAIL; + } + + return exit_label(ctx, ctx->prop_end_label); +} + +static HRESULT compile_onerror_statement(compile_ctx_t *ctx, onerror_statement_t *stat) +{ + return push_instr_int(ctx, OP_errmode, stat->resume_next); +} + +static HRESULT compile_statement(compile_ctx_t *ctx, statement_ctx_t *stat_ctx, statement_t *stat) +{ + HRESULT hres; + + if(stat_ctx) { + stat_ctx->next = ctx->stat_ctx; + ctx->stat_ctx = stat_ctx; + } + + while(stat) { + switch(stat->type) { + case STAT_ASSIGN: + hres = compile_assign_statement(ctx, (assign_statement_t*)stat, FALSE); + break; + case STAT_CALL: + hres = compile_call_statement(ctx, (call_statement_t*)stat); + break; + case STAT_CONST: + hres = compile_const_statement(ctx, (const_statement_t*)stat); + break; + case STAT_DIM: + hres = compile_dim_statement(ctx, (dim_statement_t*)stat); + break; + case STAT_DOWHILE: + case STAT_DOUNTIL: + hres = compile_dowhile_statement(ctx, (while_statement_t*)stat); + break; + case STAT_EXITDO: + hres = compile_exitdo_statement(ctx); + break; + case STAT_EXITFOR: + hres = compile_exitfor_statement(ctx); + break; + case STAT_EXITFUNC: + hres = compile_exitfunc_statement(ctx); + break; + case STAT_EXITPROP: + hres = compile_exitprop_statement(ctx); + break; + case STAT_EXITSUB: + hres = compile_exitsub_statement(ctx); + break; + case STAT_FOREACH: + hres = compile_foreach_statement(ctx, (foreach_statement_t*)stat); + break; + case STAT_FORTO: + hres = compile_forto_statement(ctx, (forto_statement_t*)stat); + break; + case STAT_FUNC: + hres = compile_function_statement(ctx, (function_statement_t*)stat); + break; + case STAT_IF: + hres = compile_if_statement(ctx, (if_statement_t*)stat); + break; + case STAT_ONERROR: + hres = compile_onerror_statement(ctx, (onerror_statement_t*)stat); + break; + case STAT_SELECT: + hres = compile_select_statement(ctx, (select_statement_t*)stat); + break; + case STAT_SET: + hres = compile_assign_statement(ctx, (assign_statement_t*)stat, TRUE); + break; + case STAT_STOP: + hres = push_instr(ctx, OP_stop) ? S_OK : E_OUTOFMEMORY; + break; + case STAT_UNTIL: + case STAT_WHILE: + case STAT_WHILELOOP: + hres = compile_while_statement(ctx, (while_statement_t*)stat); + break; + default: + FIXME("Unimplemented statement type %d\n", stat->type); + hres = E_NOTIMPL; + } + + if(FAILED(hres)) + return hres; + stat = stat->next; + } + + if(stat_ctx) { + assert(ctx->stat_ctx == stat_ctx); + ctx->stat_ctx = stat_ctx->next; + } + + return S_OK; +} + +static void resolve_labels(compile_ctx_t *ctx, unsigned off) +{ + instr_t *instr; + + for(instr = ctx->code->instrs+off; instr < ctx->code->instrs+ctx->instr_cnt; instr++) { + if(instr_info[instr->op].arg1_type == ARG_ADDR && (instr->arg1.uint & LABEL_FLAG)) { + assert((instr->arg1.uint & ~LABEL_FLAG) < ctx->labels_cnt); + instr->arg1.uint = ctx->labels[instr->arg1.uint & ~LABEL_FLAG]; + } + assert(instr_info[instr->op].arg2_type != ARG_ADDR); + } + + ctx->labels_cnt = 0; +} + +static HRESULT compile_func(compile_ctx_t *ctx, statement_t *stat, function_t *func) +{ + HRESULT hres; + + func->code_off = ctx->instr_cnt; + + ctx->sub_end_label = 0; + ctx->func_end_label = 0; + ctx->prop_end_label = 0; + + switch(func->type) { + case FUNC_FUNCTION: + ctx->func_end_label = alloc_label(ctx); + if(!ctx->func_end_label) + return E_OUTOFMEMORY; + break; + case FUNC_SUB: + ctx->sub_end_label = alloc_label(ctx); + if(!ctx->sub_end_label) + return E_OUTOFMEMORY; + break; + case FUNC_PROPGET: + case FUNC_PROPLET: + case FUNC_PROPSET: + case FUNC_DEFGET: + ctx->prop_end_label = alloc_label(ctx); + if(!ctx->prop_end_label) + return E_OUTOFMEMORY; + break; + case FUNC_GLOBAL: + break; + } + + ctx->func = func; + ctx->dim_decls = NULL; + ctx->const_decls = NULL; + hres = compile_statement(ctx, NULL, stat); + ctx->func = NULL; + if(FAILED(hres)) + return hres; + + if(ctx->sub_end_label) + label_set_addr(ctx, ctx->sub_end_label); + if(ctx->func_end_label) + label_set_addr(ctx, ctx->func_end_label); + if(ctx->prop_end_label) + label_set_addr(ctx, ctx->prop_end_label); + + if(!push_instr(ctx, OP_ret)) + return E_OUTOFMEMORY; + + resolve_labels(ctx, func->code_off); + + if(func->var_cnt) { + dim_decl_t *dim_decl; + + if(func->type == FUNC_GLOBAL) { + dynamic_var_t *new_var; + + func->var_cnt = 0; + + for(dim_decl = ctx->dim_decls; dim_decl; dim_decl = dim_decl->next) { + new_var = compiler_alloc(ctx->code, sizeof(*new_var)); + if(!new_var) + return E_OUTOFMEMORY; + + new_var->name = compiler_alloc_string(ctx->code, dim_decl->name); + if(!new_var->name) + return E_OUTOFMEMORY; + + V_VT(&new_var->v) = VT_EMPTY; + new_var->is_const = FALSE; + + new_var->next = ctx->global_vars; + ctx->global_vars = new_var; + } + }else { + unsigned i; + + func->vars = compiler_alloc(ctx->code, func->var_cnt * sizeof(var_desc_t)); + if(!func->vars) + return E_OUTOFMEMORY; + + for(dim_decl = ctx->dim_decls, i=0; dim_decl; dim_decl = dim_decl->next, i++) { + func->vars[i].name = compiler_alloc_string(ctx->code, dim_decl->name); + if(!func->vars[i].name) + return E_OUTOFMEMORY; + } + + assert(i == func->var_cnt); + } + } + + return S_OK; +} + +static BOOL lookup_funcs_name(compile_ctx_t *ctx, const WCHAR *name) +{ + function_t *iter; + + for(iter = ctx->funcs; iter; iter = iter->next) { + if(!strcmpiW(iter->name, name)) + return TRUE; + } + + return FALSE; +} + +static HRESULT create_function(compile_ctx_t *ctx, function_decl_t *decl, function_t **ret) +{ + function_t *func; + HRESULT hres; + + if(lookup_dim_decls(ctx, decl->name) || lookup_funcs_name(ctx, decl->name) || lookup_const_decls(ctx, decl->name, FALSE)) { + FIXME("%s: redefinition\n", debugstr_w(decl->name)); + return E_FAIL; + } + + func = compiler_alloc(ctx->code, sizeof(*func)); + if(!func) + return E_OUTOFMEMORY; + + func->name = compiler_alloc_string(ctx->code, decl->name); + if(!func->name) + return E_OUTOFMEMORY; + + func->vars = NULL; + func->var_cnt = 0; + func->code_ctx = ctx->code; + func->type = decl->type; + func->is_public = decl->is_public; + + func->arg_cnt = 0; + if(decl->args) { + arg_decl_t *arg; + unsigned i; + + for(arg = decl->args; arg; arg = arg->next) + func->arg_cnt++; + + func->args = compiler_alloc(ctx->code, func->arg_cnt * sizeof(arg_desc_t)); + if(!func->args) + return E_OUTOFMEMORY; + + for(i = 0, arg = decl->args; arg; arg = arg->next, i++) { + func->args[i].name = compiler_alloc_string(ctx->code, arg->name); + if(!func->args[i].name) + return E_OUTOFMEMORY; + func->args[i].by_ref = arg->by_ref; + } + }else { + func->args = NULL; + } + + hres = compile_func(ctx, decl->body, func); + if(FAILED(hres)) + return hres; + + *ret = func; + return S_OK; +} + +static BOOL lookup_class_name(compile_ctx_t *ctx, const WCHAR *name) +{ + class_desc_t *iter; + + for(iter = ctx->classes; iter; iter = iter->next) { + if(!strcmpiW(iter->name, name)) + return TRUE; + } + + return FALSE; +} + +static HRESULT create_class_funcprop(compile_ctx_t *ctx, function_decl_t *func_decl, vbdisp_funcprop_desc_t *desc) +{ + vbdisp_invoke_type_t invoke_type; + function_decl_t *funcprop_decl; + HRESULT hres; + + desc->name = compiler_alloc_string(ctx->code, func_decl->name); + if(!desc->name) + return E_OUTOFMEMORY; + + for(funcprop_decl = func_decl; funcprop_decl; funcprop_decl = funcprop_decl->next_prop_func) { + switch(funcprop_decl->type) { + case FUNC_FUNCTION: + case FUNC_SUB: + case FUNC_PROPGET: + case FUNC_DEFGET: + invoke_type = VBDISP_CALLGET; + break; + case FUNC_PROPLET: + invoke_type = VBDISP_LET; + break; + case FUNC_PROPSET: + invoke_type = VBDISP_SET; + break; + DEFAULT_UNREACHABLE; + } + + assert(!desc->entries[invoke_type]); + + if(funcprop_decl->is_public) + desc->is_public = TRUE; + + hres = create_function(ctx, funcprop_decl, desc->entries+invoke_type); + if(FAILED(hres)) + return hres; + } + + return S_OK; +} + +static BOOL lookup_class_funcs(class_desc_t *class_desc, const WCHAR *name) +{ + unsigned i; + + for(i=0; i < class_desc->func_cnt; i++) { + if(class_desc->funcs[i].name && !strcmpiW(class_desc->funcs[i].name, name)) + return TRUE; + } + + return FALSE; +} + +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; + unsigned i; + HRESULT hres; + + static const WCHAR class_initializeW[] = {'c','l','a','s','s','_','i','n','i','t','i','a','l','i','z','e',0}; + static const WCHAR class_terminateW[] = {'c','l','a','s','s','_','t','e','r','m','i','n','a','t','e',0}; + + if(lookup_dim_decls(ctx, class_decl->name) || lookup_funcs_name(ctx, class_decl->name) + || lookup_const_decls(ctx, class_decl->name, FALSE) || lookup_class_name(ctx, class_decl->name)) { + FIXME("%s: redefinition\n", debugstr_w(class_decl->name)); + return E_FAIL; + } + + class_desc = compiler_alloc_zero(ctx->code, sizeof(*class_desc)); + if(!class_desc) + return E_OUTOFMEMORY; + + class_desc->name = compiler_alloc_string(ctx->code, class_decl->name); + if(!class_desc->name) + return E_OUTOFMEMORY; + + class_desc->func_cnt = 1; /* always allocate slot for default getter */ + + for(func_decl = class_decl->funcs; func_decl; func_decl = func_decl->next) { + for(func_prop_decl = func_decl; func_prop_decl; func_prop_decl = func_prop_decl->next_prop_func) { + if(func_prop_decl->type == FUNC_DEFGET) + break; + } + if(!func_prop_decl) + class_desc->func_cnt++; + } + + class_desc->funcs = compiler_alloc(ctx->code, class_desc->func_cnt*sizeof(*class_desc->funcs)); + if(!class_desc->funcs) + return E_OUTOFMEMORY; + memset(class_desc->funcs, 0, class_desc->func_cnt*sizeof(*class_desc->funcs)); + + for(func_decl = class_decl->funcs, i=1; func_decl; func_decl = func_decl->next, i++) { + for(func_prop_decl = func_decl; func_prop_decl; func_prop_decl = func_prop_decl->next_prop_func) { + if(func_prop_decl->type == FUNC_DEFGET) { + i--; + break; + } + } + + if(!strcmpiW(class_initializeW, func_decl->name)) { + if(func_decl->type != FUNC_SUB) { + FIXME("class initializer is not sub\n"); + return E_FAIL; + } + + class_desc->class_initialize_id = i; + }else if(!strcmpiW(class_terminateW, func_decl->name)) { + if(func_decl->type != FUNC_SUB) { + FIXME("class terminator is not sub\n"); + return E_FAIL; + } + + class_desc->class_terminate_id = i; + } + + hres = create_class_funcprop(ctx, func_decl, class_desc->funcs + (func_prop_decl ? 0 : i)); + if(FAILED(hres)) + return hres; + } + + for(prop_decl = class_decl->props; prop_decl; prop_decl = prop_decl->next) + class_desc->prop_cnt++; + + class_desc->props = compiler_alloc(ctx->code, class_desc->prop_cnt*sizeof(*class_desc->props)); + if(!class_desc->props) + return E_OUTOFMEMORY; + + for(prop_decl = class_decl->props, i=0; prop_decl; prop_decl = prop_decl->next, i++) { + if(lookup_class_funcs(class_desc, prop_decl->name)) { + FIXME("Property %s redefined\n", debugstr_w(prop_decl->name)); + return E_FAIL; + } + + class_desc->props[i].name = compiler_alloc_string(ctx->code, prop_decl->name); + if(!class_desc->props[i].name) + return E_OUTOFMEMORY; + + class_desc->props[i].is_public = prop_decl->is_public; + } + + class_desc->next = ctx->classes; + ctx->classes = class_desc; + return S_OK; +} + +static BOOL lookup_script_identifier(script_ctx_t *script, const WCHAR *identifier) +{ + class_desc_t *class; + dynamic_var_t *var; + function_t *func; + + for(var = script->global_vars; var; var = var->next) { + if(!strcmpiW(var->name, identifier)) + return TRUE; + } + + for(func = script->global_funcs; func; func = func->next) { + if(!strcmpiW(func->name, identifier)) + return TRUE; + } + + for(class = script->classes; class; class = class->next) { + if(!strcmpiW(class->name, identifier)) + return TRUE; + } + + return FALSE; +} + +static HRESULT check_script_collisions(compile_ctx_t *ctx, script_ctx_t *script) +{ + class_desc_t *class; + dynamic_var_t *var; + function_t *func; + + for(var = ctx->global_vars; var; var = var->next) { + if(lookup_script_identifier(script, var->name)) { + FIXME("%s: redefined\n", debugstr_w(var->name)); + return E_FAIL; + } + } + + for(func = ctx->funcs; func; func = func->next) { + if(lookup_script_identifier(script, func->name)) { + FIXME("%s: redefined\n", debugstr_w(func->name)); + return E_FAIL; + } + } + + for(class = ctx->classes; class; class = class->next) { + if(lookup_script_identifier(script, class->name)) { + FIXME("%s: redefined\n", debugstr_w(class->name)); + return E_FAIL; + } + } + + return S_OK; +} + +void release_vbscode(vbscode_t *code) +{ + unsigned i; + + list_remove(&code->entry); + + for(i=0; i < code->bstr_cnt; i++) + SysFreeString(code->bstr_pool[i]); + + heap_pool_free(&code->heap); + + heap_free(code->bstr_pool); + heap_free(code->source); + heap_free(code->instrs); + heap_free(code); +} + +static vbscode_t *alloc_vbscode(compile_ctx_t *ctx, const WCHAR *source) +{ + vbscode_t *ret; + + ret = heap_alloc(sizeof(*ret)); + if(!ret) + return NULL; + + ret->source = heap_strdupW(source); + if(!ret->source) { + heap_free(ret); + return NULL; + } + + ret->instrs = heap_alloc(32*sizeof(instr_t)); + if(!ret->instrs) { + release_vbscode(ret); + return NULL; + } + + ctx->instr_cnt = 1; + ctx->instr_size = 32; + heap_pool_init(&ret->heap); + + 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; +} + +static void release_compiler(compile_ctx_t *ctx) +{ + parser_release(&ctx->parser); + heap_free(ctx->labels); + if(ctx->code) + release_vbscode(ctx->code); +} + +HRESULT compile_script(script_ctx_t *script, const WCHAR *src, const WCHAR *delimiter, vbscode_t **ret) +{ + function_t *new_func; + function_decl_t *func_decl; + class_decl_t *class_decl; + compile_ctx_t ctx; + vbscode_t *code; + HRESULT hres; + + hres = parse_script(&ctx.parser, src, delimiter); + if(FAILED(hres)) + return hres; + + code = ctx.code = alloc_vbscode(&ctx, src); + if(!ctx.code) + return E_OUTOFMEMORY; + + 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; + ctx.stat_ctx = NULL; + ctx.labels_cnt = ctx.labels_size = 0; + + hres = compile_func(&ctx, ctx.parser.stats, &ctx.code->main_code); + if(FAILED(hres)) { + release_compiler(&ctx); + return hres; + } + + ctx.global_consts = ctx.const_decls; + + for(func_decl = ctx.func_decls; func_decl; func_decl = func_decl->next) { + hres = create_function(&ctx, func_decl, &new_func); + if(FAILED(hres)) { + release_compiler(&ctx); + return hres; + } + + new_func->next = ctx.funcs; + ctx.funcs = new_func; + } + + for(class_decl = ctx.parser.class_decls; class_decl; class_decl = class_decl->next) { + hres = compile_class(&ctx, class_decl); + if(FAILED(hres)) { + release_compiler(&ctx); + return hres; + } + } + + hres = check_script_collisions(&ctx, script); + if(FAILED(hres)) { + release_compiler(&ctx); + return hres; + } + + if(ctx.global_vars) { + dynamic_var_t *var; + + for(var = ctx.global_vars; var->next; var = var->next); + + var->next = script->global_vars; + script->global_vars = ctx.global_vars; + } + + if(ctx.funcs) { + for(new_func = ctx.funcs; new_func->next; new_func = new_func->next); + + new_func->next = script->global_funcs; + script->global_funcs = ctx.funcs; + } + + if(ctx.classes) { + class_desc_t *class = ctx.classes; + + while(1) { + class->ctx = script; + if(!class->next) + break; + class = class->next; + } + + class->next = script->classes; + script->classes = ctx.classes; + } + + if(TRACE_ON(vbscript_disas)) + dump_code(&ctx); + + ctx.code = NULL; + release_compiler(&ctx); + + list_add_tail(&script->code_list, &code->entry); + *ret = code; + return S_OK; +} diff --git a/reactos/dll/win32/vbscript/error.c b/reactos/dll/win32/vbscript/error.c new file mode 100644 index 00000000000..8753bbaf3a4 --- /dev/null +++ b/reactos/dll/win32/vbscript/error.c @@ -0,0 +1,91 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "vbscript.h" +#include "vbscript_defs.h" + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(vbscript); + +static HRESULT Err_Description(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Err_HelpContext(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Err_HelpFile(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Err_Number(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Err_Source(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Err_Clear(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Err_Raise(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static const builtin_prop_t err_props[] = { + {DISPID_ERR_DESCRIPTION, Err_Description, BP_GETPUT}, + {DISPID_ERR_HELPCONTEXT, Err_HelpContext, BP_GETPUT}, + {DISPID_ERR_HELPFILE, Err_HelpFile, BP_GETPUT}, + {DISPID_ERR_NUMBER, Err_Number, BP_GETPUT}, + {DISPID_ERR_SOURCE, Err_Source, BP_GETPUT}, + {DISPID_ERR_CLEAR, Err_Clear}, + {DISPID_ERR_RAISE, Err_Raise, 0, 5}, +}; + +HRESULT init_err(script_ctx_t *ctx) +{ + HRESULT hres; + + ctx->err_desc.ctx = ctx; + ctx->err_desc.builtin_prop_cnt = sizeof(err_props)/sizeof(*err_props); + ctx->err_desc.builtin_props = err_props; + + hres = get_typeinfo(ErrObj_tid, &ctx->err_desc.typeinfo); + if(FAILED(hres)) + return hres; + + return create_vbdisp(&ctx->err_desc, &ctx->err_obj); +} diff --git a/reactos/dll/win32/vbscript/global.c b/reactos/dll/win32/vbscript/global.c new file mode 100644 index 00000000000..1494ee8367e --- /dev/null +++ b/reactos/dll/win32/vbscript/global.c @@ -0,0 +1,1839 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include +//#include + +#include "vbscript.h" +#include "vbscript_defs.h" + +#include +#include + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(vbscript); + +#define VB_E_CANNOT_CREATE_OBJ 0x800a01ad +#define VB_E_MK_PARSE_ERROR 0x800a01b0 + +#define round(x) (((x) < 0) ? (int)((x) - 0.5) : (int)((x) + 0.5)) + +/* Defined as extern in urlmon.idl, but not exported by uuid.lib */ +const GUID GUID_CUSTOM_CONFIRMOBJECTSAFETY = + {0x10200490,0xfa38,0x11d0,{0xac,0x0e,0x00,0xa0,0xc9,0xf,0xff,0xc0}}; + +static const WCHAR emptyW[] = {0}; +static const WCHAR vbscriptW[] = {'V','B','S','c','r','i','p','t',0}; + +static IInternetHostSecurityManager *get_sec_mgr(script_ctx_t *ctx) +{ + IInternetHostSecurityManager *secmgr; + IServiceProvider *sp; + HRESULT hres; + + if(!ctx->site) + return NULL; + + if(ctx->secmgr) + return ctx->secmgr; + + hres = IActiveScriptSite_QueryInterface(ctx->site, &IID_IServiceProvider, (void**)&sp); + if(FAILED(hres)) + return NULL; + + hres = IServiceProvider_QueryService(sp, &SID_SInternetHostSecurityManager, &IID_IInternetHostSecurityManager, + (void**)&secmgr); + IServiceProvider_Release(sp); + if(FAILED(hres)) + return NULL; + + return ctx->secmgr = secmgr; +} + +static HRESULT return_string(VARIANT *res, const WCHAR *str) +{ + BSTR ret; + + if(!res) + return S_OK; + + ret = SysAllocString(str); + if(!ret) + return E_OUTOFMEMORY; + + V_VT(res) = VT_BSTR; + V_BSTR(res) = ret; + return S_OK; +} + +static HRESULT return_bstr(VARIANT *res, BSTR str) +{ + if(res) { + V_VT(res) = VT_BSTR; + V_BSTR(res) = str; + }else { + SysFreeString(str); + } + return S_OK; +} + +static HRESULT return_short(VARIANT *res, short val) +{ + if(res) { + V_VT(res) = VT_I2; + V_I2(res) = val; + } + + return S_OK; +} + +static HRESULT return_int(VARIANT *res, int val) +{ + if((short)val == val) + return return_short(res, val); + + if(res) { + V_VT(res) = VT_I4; + V_I4(res) = val; + } + + return S_OK; +} + +static inline HRESULT return_double(VARIANT *res, double val) +{ + if(res) { + V_VT(res) = VT_R8; + V_R8(res) = val; + } + + return S_OK; +} + +static inline HRESULT return_null(VARIANT *res) +{ + if(res) + V_VT(res) = VT_NULL; + return S_OK; +} + +static inline HRESULT return_date(VARIANT *res, double date) +{ + if(res) { + V_VT(res) = VT_DATE; + V_DATE(res) = date; + } + return S_OK; +} + +static HRESULT to_int(VARIANT *v, int *ret) +{ + switch(V_VT(v)) { + case VT_I2: + *ret = V_I2(v); + break; + case VT_I4: + *ret = V_I4(v); + break; + case VT_R8: { + double n = round(V_R8(v)); + if(!is_int32(n)) { + FIXME("%lf is out of int range\n", n); + return E_FAIL; + } + *ret = n; + break; + } + case VT_BOOL: + *ret = V_BOOL(v) ? -1 : 0; + break; + default: + FIXME("not supported %s\n", debugstr_variant(v)); + return E_NOTIMPL; + } + + return S_OK; +} + +static HRESULT to_double(VARIANT *v, double *ret) +{ + switch(V_VT(v)) { + case VT_I2: + *ret = V_I2(v); + break; + case VT_I4: + *ret = V_I4(v); + break; + case VT_R4: + *ret = V_R4(v); + break; + case VT_R8: + *ret = V_R8(v); + break; + case VT_BSTR: { + VARIANT dst; + HRESULT hres; + + V_VT(&dst) = VT_EMPTY; + hres = VariantChangeType(&dst, v, VARIANT_LOCALBOOL, VT_R8); + if(FAILED(hres)) + return hres; + *ret = V_R8(&dst); + break; + } + default: + FIXME("arg %s not supported\n", debugstr_variant(v)); + return E_NOTIMPL; + } + + return S_OK; +} + +static HRESULT to_string(VARIANT *v, BSTR *ret) +{ + VARIANT dst; + HRESULT hres; + + V_VT(&dst) = VT_EMPTY; + hres = VariantChangeType(&dst, v, VARIANT_LOCALBOOL, VT_BSTR); + if(FAILED(hres)) + return hres; + + *ret = V_BSTR(&dst); + return S_OK; +} + +static HRESULT set_object_site(script_ctx_t *ctx, IUnknown *obj) +{ + IObjectWithSite *obj_site; + IUnknown *ax_site; + HRESULT hres; + + hres = IUnknown_QueryInterface(obj, &IID_IObjectWithSite, (void**)&obj_site); + if(FAILED(hres)) + return S_OK; + + ax_site = create_ax_site(ctx); + if(ax_site) + hres = IObjectWithSite_SetSite(obj_site, ax_site); + else + hres = E_OUTOFMEMORY; + IUnknown_Release(ax_site); + IObjectWithSite_Release(obj_site); + return hres; +} + +static IUnknown *create_object(script_ctx_t *ctx, const WCHAR *progid) +{ + IInternetHostSecurityManager *secmgr = NULL; + struct CONFIRMSAFETY cs; + IClassFactoryEx *cfex; + IClassFactory *cf; + DWORD policy_size; + BYTE *bpolicy; + IUnknown *obj; + DWORD policy; + GUID guid; + HRESULT hres; + + hres = CLSIDFromProgID(progid, &guid); + if(FAILED(hres)) + return NULL; + + TRACE("GUID %s\n", debugstr_guid(&guid)); + + if(ctx->safeopt & INTERFACE_USES_SECURITY_MANAGER) { + secmgr = get_sec_mgr(ctx); + if(!secmgr) + return NULL; + + policy = 0; + hres = IInternetHostSecurityManager_ProcessUrlAction(secmgr, URLACTION_ACTIVEX_RUN, + (BYTE*)&policy, sizeof(policy), (BYTE*)&guid, sizeof(GUID), 0, 0); + if(FAILED(hres) || policy != URLPOLICY_ALLOW) + return NULL; + } + + hres = CoGetClassObject(&guid, CLSCTX_INPROC_SERVER|CLSCTX_LOCAL_SERVER, NULL, &IID_IClassFactory, (void**)&cf); + if(FAILED(hres)) + return NULL; + + hres = IClassFactory_QueryInterface(cf, &IID_IClassFactoryEx, (void**)&cfex); + if(SUCCEEDED(hres)) { + FIXME("Use IClassFactoryEx\n"); + IClassFactoryEx_Release(cfex); + } + + hres = IClassFactory_CreateInstance(cf, NULL, &IID_IUnknown, (void**)&obj); + if(FAILED(hres)) + return NULL; + + if(secmgr) { + cs.clsid = guid; + cs.pUnk = obj; + cs.dwFlags = 0; + hres = IInternetHostSecurityManager_QueryCustomPolicy(secmgr, &GUID_CUSTOM_CONFIRMOBJECTSAFETY, + &bpolicy, &policy_size, (BYTE*)&cs, sizeof(cs), 0); + if(SUCCEEDED(hres)) { + policy = policy_size >= sizeof(DWORD) ? *(DWORD*)bpolicy : URLPOLICY_DISALLOW; + CoTaskMemFree(bpolicy); + } + + if(FAILED(hres) || policy != URLPOLICY_ALLOW) { + IUnknown_Release(obj); + return NULL; + } + } + + hres = set_object_site(ctx, obj); + if(FAILED(hres)) { + IUnknown_Release(obj); + return NULL; + } + + return obj; +} + +static HRESULT show_msgbox(script_ctx_t *ctx, BSTR prompt, VARIANT *res) +{ + SCRIPTUICHANDLING uic_handling = SCRIPTUICHANDLING_ALLOW; + IActiveScriptSiteUIControl *ui_control; + IActiveScriptSiteWindow *acts_window; + const WCHAR *title; + HWND hwnd = NULL; + int ret; + HRESULT hres; + + hres = IActiveScriptSite_QueryInterface(ctx->site, &IID_IActiveScriptSiteUIControl, (void**)&ui_control); + if(SUCCEEDED(hres)) { + hres = IActiveScriptSiteUIControl_GetUIBehavior(ui_control, SCRIPTUICITEM_MSGBOX, &uic_handling); + IActiveScriptSiteUIControl_Release(ui_control); + if(FAILED(hres)) + uic_handling = SCRIPTUICHANDLING_ALLOW; + } + + switch(uic_handling) { + case SCRIPTUICHANDLING_ALLOW: + break; + case SCRIPTUICHANDLING_NOUIDEFAULT: + return return_short(res, 0); + default: + FIXME("blocked\n"); + return E_FAIL; + } + + title = (ctx->safeopt & INTERFACE_USES_SECURITY_MANAGER) ? vbscriptW : emptyW; + + hres = IActiveScriptSite_QueryInterface(ctx->site, &IID_IActiveScriptSiteWindow, (void**)&acts_window); + if(FAILED(hres)) { + FIXME("No IActiveScriptSiteWindow\n"); + return hres; + } + + hres = IActiveScriptSiteWindow_GetWindow(acts_window, &hwnd); + if(SUCCEEDED(hres)) { + hres = IActiveScriptSiteWindow_EnableModeless(acts_window, FALSE); + if(SUCCEEDED(hres)) { + ret = MessageBoxW(hwnd, prompt, title, MB_OK); + hres = IActiveScriptSiteWindow_EnableModeless(acts_window, TRUE); + } + } + + IActiveScriptSiteWindow_Release(acts_window); + if(FAILED(hres)) { + FIXME("failed: %08x\n", hres); + return hres; + } + + return return_short(res, ret); +} + +static HRESULT Global_CCur(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_CInt(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_CLng(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_CBool(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_CByte(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_CDate(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_CDbl(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_CSng(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_CStr(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + BSTR str; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + hres = to_string(arg, &str); + if(FAILED(hres)) + return hres; + + return return_bstr(res, str); +} + +static inline WCHAR hex_char(unsigned n) +{ + return n < 10 ? '0'+n : 'A'+n-10; +} + +static HRESULT Global_Hex(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + WCHAR buf[17], *ptr; + DWORD n; + + TRACE("%s\n", debugstr_variant(arg)); + + switch(V_VT(arg)) { + case VT_I2: + n = (WORD)V_I2(arg); + break; + case VT_I4: + n = V_I4(arg); + break; + case VT_EMPTY: + n = 0; + break; + case VT_NULL: + if(res) + V_VT(res) = VT_NULL; + return S_OK; + default: + FIXME("unsupported type %s\n", debugstr_variant(arg)); + return E_NOTIMPL; + } + + buf[16] = 0; + ptr = buf+15; + + if(n) { + do { + *ptr-- = hex_char(n & 0xf); + n >>= 4; + }while(n); + ptr++; + }else { + *ptr = '0'; + } + + return return_string(res, ptr); +} + +static HRESULT Global_Oct(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_VarType(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_IsDate(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_IsEmpty(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + TRACE("(%s)\n", debugstr_variant(arg)); + + assert(args_cnt == 1); + + if(res) { + V_VT(res) = VT_BOOL; + V_BOOL(res) = V_VT(arg) == VT_EMPTY ? VARIANT_TRUE : VARIANT_FALSE; + } + return S_OK; +} + +static HRESULT Global_IsNull(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + TRACE("(%s)\n", debugstr_variant(arg)); + + assert(args_cnt == 1); + + if(res) { + V_VT(res) = VT_BOOL; + V_BOOL(res) = V_VT(arg) == VT_NULL ? VARIANT_TRUE : VARIANT_FALSE; + } + return S_OK; +} + +static HRESULT Global_IsNumeric(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_IsArray(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_IsObject(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + TRACE("(%s)\n", debugstr_variant(arg)); + + assert(args_cnt == 1); + + if(res) { + V_VT(res) = VT_BOOL; + V_BOOL(res) = V_VT(arg) == VT_DISPATCH ? VARIANT_TRUE : VARIANT_FALSE; + } + return S_OK; +} + +static HRESULT Global_Ant(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Cos(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Sin(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Tan(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Exp(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Log(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Sqr(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Randomize(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Rnd(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Timer(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_LBound(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_UBound(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_RGB(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Len(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + DWORD len; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + if(V_VT(arg) == VT_NULL) + return return_null(res); + + if(V_VT(arg) != VT_BSTR) { + BSTR str; + + hres = to_string(arg, &str); + if(FAILED(hres)) + return hres; + + len = SysStringLen(str); + SysFreeString(str); + }else { + len = SysStringLen(V_BSTR(arg)); + } + + return return_int(res, len); +} + +static HRESULT Global_LenB(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Left(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + BSTR str, ret, conv_str = NULL; + int len, str_len; + HRESULT hres; + + TRACE("(%s %s)\n", debugstr_variant(args+1), debugstr_variant(args)); + + if(V_VT(args) == VT_BSTR) { + str = V_BSTR(args); + }else { + hres = to_string(args, &conv_str); + if(FAILED(hres)) + return hres; + str = conv_str; + } + + hres = to_int(args+1, &len); + if(FAILED(hres)) + return hres; + + if(len < 0) { + FIXME("len = %d\n", len); + return E_FAIL; + } + + str_len = SysStringLen(str); + if(len > str_len) + len = str_len; + + ret = SysAllocStringLen(str, len); + SysFreeString(conv_str); + if(!ret) + return E_OUTOFMEMORY; + + return return_bstr(res, ret); +} + +static HRESULT Global_LeftB(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Right(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + BSTR str, ret, conv_str = NULL; + int len, str_len; + HRESULT hres; + + TRACE("(%s %s)\n", debugstr_variant(args), debugstr_variant(args+1)); + + if(V_VT(args+1) == VT_BSTR) { + str = V_BSTR(args); + }else { + hres = to_string(args, &conv_str); + if(FAILED(hres)) + return hres; + str = conv_str; + } + + hres = to_int(args+1, &len); + if(FAILED(hres)) + return hres; + + if(len < 0) { + FIXME("len = %d\n", len); + return E_FAIL; + } + + str_len = SysStringLen(str); + if(len > str_len) + len = str_len; + + ret = SysAllocStringLen(str+str_len-len, len); + SysFreeString(conv_str); + if(!ret) + return E_OUTOFMEMORY; + + return return_bstr(res, ret); +} + +static HRESULT Global_RightB(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Mid(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + int len = -1, start, str_len; + BSTR str; + HRESULT hres; + + TRACE("(%s %s ...)\n", debugstr_variant(args), debugstr_variant(args+1)); + + assert(args_cnt == 2 || args_cnt == 3); + + if(V_VT(args) != VT_BSTR) { + FIXME("args[0] = %s\n", debugstr_variant(args)); + return E_NOTIMPL; + } + + str = V_BSTR(args); + + hres = to_int(args+1, &start); + if(FAILED(hres)) + return hres; + + if(args_cnt == 3) { + hres = to_int(args+2, &len); + if(FAILED(hres)) + return hres; + + if(len < 0) { + FIXME("len = %d\n", len); + return E_FAIL; + } + } + + + str_len = SysStringLen(str); + start--; + if(start > str_len) + start = str_len; + + if(len == -1) + len = str_len-start; + else if(len > str_len-start) + len = str_len-start; + + if(res) { + V_VT(res) = VT_BSTR; + V_BSTR(res) = SysAllocStringLen(str+start, len); + if(!V_BSTR(res)) + return E_OUTOFMEMORY; + } + + return S_OK; +} + +static HRESULT Global_MidB(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_StrComp(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_LCase(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + BSTR str; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + if(V_VT(arg) == VT_NULL) { + if(res) + V_VT(res) = VT_NULL; + return S_OK; + } + + hres = to_string(arg, &str); + if(FAILED(hres)) + return hres; + + if(res) { + WCHAR *ptr; + + for(ptr = str; *ptr; ptr++) + *ptr = tolowerW(*ptr); + + V_VT(res) = VT_BSTR; + V_BSTR(res) = str; + }else { + SysFreeString(str); + } + return S_OK; +} + +static HRESULT Global_UCase(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + BSTR str; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + if(V_VT(arg) == VT_NULL) { + if(res) + V_VT(res) = VT_NULL; + return S_OK; + } + + hres = to_string(arg, &str); + if(FAILED(hres)) + return hres; + + if(res) { + WCHAR *ptr; + + for(ptr = str; *ptr; ptr++) + *ptr = toupperW(*ptr); + + V_VT(res) = VT_BSTR; + V_BSTR(res) = str; + }else { + SysFreeString(str); + } + return S_OK; +} + +static HRESULT Global_LTrim(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + BSTR str, conv_str = NULL; + WCHAR *ptr; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + if(V_VT(arg) == VT_BSTR) { + str = V_BSTR(arg); + }else { + hres = to_string(arg, &conv_str); + if(FAILED(hres)) + return hres; + str = conv_str; + } + + for(ptr = str; *ptr && isspaceW(*ptr); ptr++); + + str = SysAllocString(ptr); + SysFreeString(conv_str); + if(!str) + return E_OUTOFMEMORY; + + return return_bstr(res, str); +} + +static HRESULT Global_RTrim(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + BSTR str, conv_str = NULL; + WCHAR *ptr; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + if(V_VT(arg) == VT_BSTR) { + str = V_BSTR(arg); + }else { + hres = to_string(arg, &conv_str); + if(FAILED(hres)) + return hres; + str = conv_str; + } + + for(ptr = str+SysStringLen(str); ptr-1 > str && isspaceW(*(ptr-1)); ptr--); + + str = SysAllocStringLen(str, ptr-str); + SysFreeString(conv_str); + if(!str) + return E_OUTOFMEMORY; + + return return_bstr(res, str); +} + +static HRESULT Global_Trim(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + BSTR str, conv_str = NULL; + WCHAR *begin_ptr, *end_ptr; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + if(V_VT(arg) == VT_BSTR) { + str = V_BSTR(arg); + }else { + hres = to_string(arg, &conv_str); + if(FAILED(hres)) + return hres; + str = conv_str; + } + + for(begin_ptr = str; *begin_ptr && isspaceW(*begin_ptr); begin_ptr++); + for(end_ptr = str+SysStringLen(str); end_ptr-1 > begin_ptr && isspaceW(*(end_ptr-1)); end_ptr--); + + str = SysAllocStringLen(begin_ptr, end_ptr-begin_ptr); + SysFreeString(conv_str); + if(!str) + return E_OUTOFMEMORY; + + return return_bstr(res, str); +} + +static HRESULT Global_Space(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + BSTR str; + int n, i; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + hres = to_int(arg, &n); + if(FAILED(hres)) + return hres; + + if(n < 0) { + FIXME("n = %d\n", n); + return E_NOTIMPL; + } + + if(!res) + return S_OK; + + str = SysAllocStringLen(NULL, n); + if(!str) + return E_OUTOFMEMORY; + + for(i=0; i= 0x100) { + FIXME("invalid arg\n"); + return E_FAIL; + } + + if(res) { + WCHAR ch = c; + + V_VT(res) = VT_BSTR; + V_BSTR(res) = SysAllocStringLen(&ch, 1); + if(!V_BSTR(res)) + return E_OUTOFMEMORY; + } + return S_OK; +} + +static HRESULT Global_AscW(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_ChrW(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Abs(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Fix(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Int(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Sgn(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Now(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + SYSTEMTIME lt; + double date; + + TRACE("\n"); + + GetLocalTime(<); + SystemTimeToVariantTime(<, &date); + return return_date(res, date); +} + +static HRESULT Global_Date(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Time(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Day(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Month(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Weekday(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Year(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Hour(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Minute(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Second(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_DateValue(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_TimeValue(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_DateSerial(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_TimeSerial(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_InputBox(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_MsgBox(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + BSTR prompt; + HRESULT hres; + + TRACE("\n"); + + if(args_cnt != 1) { + FIXME("unsupported arg_cnt %d\n", args_cnt); + return E_NOTIMPL; + } + + hres = to_string(args, &prompt); + if(FAILED(hres)) + return hres; + + hres = show_msgbox(This->desc->ctx, prompt, res); + SysFreeString(prompt); + return hres; +} + +static HRESULT Global_CreateObject(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + IUnknown *obj; + HRESULT hres; + + TRACE("(%s)\n", debugstr_variant(arg)); + + if(V_VT(arg) != VT_BSTR) { + FIXME("non-bstr arg\n"); + return E_INVALIDARG; + } + + obj = create_object(This->desc->ctx, V_BSTR(arg)); + if(!obj) + return VB_E_CANNOT_CREATE_OBJ; + + if(res) { + hres = IUnknown_QueryInterface(obj, &IID_IDispatch, (void**)&V_DISPATCH(res)); + if(FAILED(hres)) + return hres; + + V_VT(res) = VT_DISPATCH; + } + + IUnknown_Release(obj); + return S_OK; +} + +static HRESULT Global_GetObject(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + IBindCtx *bind_ctx; + IUnknown *obj_unk; + IDispatch *disp; + ULONG eaten = 0; + IMoniker *mon; + HRESULT hres; + + TRACE("%s %s\n", args_cnt ? debugstr_variant(args) : "", args_cnt > 1 ? debugstr_variant(args+1) : ""); + + if(args_cnt != 1 || V_VT(args) != VT_BSTR) { + FIXME("unsupported args\n"); + return E_NOTIMPL; + } + + if(This->desc->ctx->safeopt & (INTERFACE_USES_SECURITY_MANAGER|INTERFACESAFE_FOR_UNTRUSTED_DATA)) { + WARN("blocked in current safety mode\n"); + return VB_E_CANNOT_CREATE_OBJ; + } + + hres = CreateBindCtx(0, &bind_ctx); + if(FAILED(hres)) + return hres; + + hres = MkParseDisplayName(bind_ctx, V_BSTR(args), &eaten, &mon); + if(SUCCEEDED(hres)) { + hres = IMoniker_BindToObject(mon, bind_ctx, NULL, &IID_IUnknown, (void**)&obj_unk); + IMoniker_Release(mon); + }else { + hres = MK_E_SYNTAX; + } + IBindCtx_Release(bind_ctx); + if(FAILED(hres)) + return hres; + + hres = set_object_site(This->desc->ctx, obj_unk); + if(FAILED(hres)) { + IUnknown_Release(obj_unk); + return hres; + } + + hres = IUnknown_QueryInterface(obj_unk, &IID_IDispatch, (void**)&disp); + if(SUCCEEDED(hres)) { + if(res) { + V_VT(res) = VT_DISPATCH; + V_DISPATCH(res) = disp; + }else { + IDispatch_Release(disp); + } + }else { + FIXME("object does not support IDispatch\n"); + } + + return hres; +} + +static HRESULT Global_DateAdd(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_DateDiff(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_DatePart(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_TypeName(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Array(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Erase(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Filter(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Join(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Split(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Replace(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_StrReverse(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + WCHAR *ptr1, *ptr2, ch; + BSTR ret; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + hres = to_string(arg, &ret); + if(FAILED(hres)) + return hres; + + ptr1 = ret; + ptr2 = ret + SysStringLen(ret)-1; + while(ptr1 < ptr2) { + ch = *ptr1; + *ptr1++ = *ptr2; + *ptr2-- = ch; + } + + return return_bstr(res, ret); +} + +static HRESULT Global_InStrRev(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_LoadPicture(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_ScriptEngine(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_ScriptEngineMajorVersion(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_ScriptEngineMinorVersion(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_ScriptEngineBuildVersion(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_FormatNumber(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_FormatCurrency(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_FormatPercent(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_FormatDateTime(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_WeekdayName(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + int weekday, first_day = 1, abbrev = 0; + BSTR ret; + HRESULT hres; + + TRACE("\n"); + + assert(1 <= args_cnt && args_cnt <= 3); + + hres = to_int(args, &weekday); + if(FAILED(hres)) + return hres; + + if(args_cnt > 1) { + hres = to_int(args+1, &abbrev); + if(FAILED(hres)) + return hres; + + if(args_cnt == 3) { + hres = to_int(args+2, &first_day); + if(FAILED(hres)) + return hres; + } + } + + hres = VarWeekdayName(weekday, abbrev, first_day, 0, &ret); + if(FAILED(hres)) + return hres; + + return return_bstr(res, ret); +} + +static HRESULT Global_MonthName(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + int month, abbrev = 0; + BSTR ret; + HRESULT hres; + + TRACE("\n"); + + assert(args_cnt == 1 || args_cnt == 2); + + hres = to_int(args, &month); + if(FAILED(hres)) + return hres; + + if(args_cnt == 2) { + hres = to_int(args+1, &abbrev); + if(FAILED(hres)) + return hres; + } + + hres = VarMonthName(month, abbrev, 0, &ret); + if(FAILED(hres)) + return hres; + + return return_bstr(res, ret); +} + +static HRESULT Global_Round(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + double n; + HRESULT hres; + + TRACE("%s\n", debugstr_variant(arg)); + + if(!res) + return S_OK; + + switch(V_VT(arg)) { + case VT_I2: + case VT_I4: + case VT_BOOL: + *res = *arg; + return S_OK; + case VT_R8: + n = V_R8(arg); + break; + default: + hres = to_double(arg, &n); + if(FAILED(hres)) + return hres; + } + + return return_double(res, round(n)); +} + +static HRESULT Global_Escape(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Unescape(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Eval(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_Execute(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_ExecuteGlobal(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Global_GetRef(vbdisp_t *This, VARIANT *arg, unsigned args_cnt, VARIANT *res) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static const string_constant_t vbCr = {1, {'\r'}}; +static const string_constant_t vbCrLf = {2, {'\r','\n'}}; +static const string_constant_t vbNewLine = {2, {'\r','\n'}}; +static const string_constant_t vbFormFeed = {1, {0xc}}; +static const string_constant_t vbLf = {1, {'\n'}}; +static const string_constant_t vbNullChar = {1}; +static const string_constant_t vbNullString = {0}; +static const string_constant_t vbTab = {1, {'\t'}}; +static const string_constant_t vbVerticalTab = {1, {0xb}}; + +static const builtin_prop_t global_props[] = { + {DISPID_GLOBAL_VBUSESYSTEM, NULL, BP_GET, VT_I2, 0}, + {DISPID_GLOBAL_USESYSTEMDAYOFWEEK, NULL, BP_GET, VT_I2, 0}, + {DISPID_GLOBAL_VBSUNDAY, NULL, BP_GET, VT_I2, 1}, + {DISPID_GLOBAL_VBMONDAY, NULL, BP_GET, VT_I2, 2}, + {DISPID_GLOBAL_VBTUESDAY, NULL, BP_GET, VT_I2, 3}, + {DISPID_GLOBAL_VBWEDNESDAY, NULL, BP_GET, VT_I2, 4}, + {DISPID_GLOBAL_VBTHURSDAY, NULL, BP_GET, VT_I2, 5}, + {DISPID_GLOBAL_VBFRIDAY, NULL, BP_GET, VT_I2, 6}, + {DISPID_GLOBAL_VBSATURDAY, NULL, BP_GET, VT_I2, 7}, + {DISPID_GLOBAL_VBFIRSTJAN1, NULL, BP_GET, VT_I2, 1}, + {DISPID_GLOBAL_VBFIRSTFOURDAYS, NULL, BP_GET, VT_I2, 2}, + {DISPID_GLOBAL_VBFIRSTFULLWEEK, NULL, BP_GET, VT_I2, 3}, + {DISPID_GLOBAL_VBOKONLY, NULL, BP_GET, VT_I2, MB_OK}, + {DISPID_GLOBAL_VBOKCANCEL, NULL, BP_GET, VT_I2, MB_OKCANCEL}, + {DISPID_GLOBAL_VBABORTRETRYIGNORE, NULL, BP_GET, VT_I2, MB_ABORTRETRYIGNORE}, + {DISPID_GLOBAL_VBYESNOCANCEL, NULL, BP_GET, VT_I2, MB_YESNOCANCEL}, + {DISPID_GLOBAL_VBYESNO, NULL, BP_GET, VT_I2, MB_YESNO}, + {DISPID_GLOBAL_VBRETRYCANCEL, NULL, BP_GET, VT_I2, MB_RETRYCANCEL}, + {DISPID_GLOBAL_VBCRITICAL, NULL, BP_GET, VT_I2, MB_ICONHAND}, + {DISPID_GLOBAL_VBQUESTION, NULL, BP_GET, VT_I2, MB_ICONQUESTION}, + {DISPID_GLOBAL_VBEXCLAMATION, NULL, BP_GET, VT_I2, MB_ICONEXCLAMATION}, + {DISPID_GLOBAL_VBINFORMATION, NULL, BP_GET, VT_I2, MB_ICONASTERISK}, + {DISPID_GLOBAL_VBDEFAULTBUTTON1, NULL, BP_GET, VT_I2, MB_DEFBUTTON1}, + {DISPID_GLOBAL_VBDEFAULTBUTTON2, NULL, BP_GET, VT_I2, MB_DEFBUTTON2}, + {DISPID_GLOBAL_VBDEFAULTBUTTON3, NULL, BP_GET, VT_I2, MB_DEFBUTTON3}, + {DISPID_GLOBAL_VBDEFAULTBUTTON4, NULL, BP_GET, VT_I2, MB_DEFBUTTON4}, + {DISPID_GLOBAL_VBAPPLICATIONMODAL, NULL, BP_GET, VT_I2, MB_APPLMODAL}, + {DISPID_GLOBAL_VBSYSTEMMODAL, NULL, BP_GET, VT_I2, MB_SYSTEMMODAL}, + {DISPID_GLOBAL_VBOK, NULL, BP_GET, VT_I2, IDOK}, + {DISPID_GLOBAL_VBCANCEL, NULL, BP_GET, VT_I2, IDCANCEL}, + {DISPID_GLOBAL_VBABORT, NULL, BP_GET, VT_I2, IDABORT}, + {DISPID_GLOBAL_VBRETRY, NULL, BP_GET, VT_I2, IDRETRY}, + {DISPID_GLOBAL_VBIGNORE, NULL, BP_GET, VT_I2, IDIGNORE}, + {DISPID_GLOBAL_VBYES, NULL, BP_GET, VT_I2, IDYES}, + {DISPID_GLOBAL_VBNO, NULL, BP_GET, VT_I2, IDNO}, + {DISPID_GLOBAL_VBEMPTY, NULL, BP_GET, VT_I2, VT_EMPTY}, + {DISPID_GLOBAL_VBNULL, NULL, BP_GET, VT_I2, VT_NULL}, + {DISPID_GLOBAL_VBINTEGER, NULL, BP_GET, VT_I2, VT_I2}, + {DISPID_GLOBAL_VBLONG, NULL, BP_GET, VT_I2, VT_I4}, + {DISPID_GLOBAL_VBSINGLE, NULL, BP_GET, VT_I2, VT_R4}, + {DISPID_GLOBAL_VBDOUBLE, NULL, BP_GET, VT_I2, VT_R8}, + {DISPID_GLOBAL_VBCURRENCY, NULL, BP_GET, VT_I2, VT_CY}, + {DISPID_GLOBAL_VBDATE, NULL, BP_GET, VT_I2, VT_DATE}, + {DISPID_GLOBAL_VBSTRING, NULL, BP_GET, VT_I2, VT_BSTR}, + {DISPID_GLOBAL_VBOBJECT, NULL, BP_GET, VT_I2, VT_DISPATCH}, + {DISPID_GLOBAL_VBERROR, NULL, BP_GET, VT_I2, VT_ERROR}, + {DISPID_GLOBAL_VBBOOLEAN, NULL, BP_GET, VT_I2, VT_BOOL}, + {DISPID_GLOBAL_VBVARIANT, NULL, BP_GET, VT_I2, VT_VARIANT}, + {DISPID_GLOBAL_VBDATAOBJECT, NULL, BP_GET, VT_I2, VT_UNKNOWN}, + {DISPID_GLOBAL_VBDECIMAL, NULL, BP_GET, VT_I2, VT_DECIMAL}, + {DISPID_GLOBAL_VBBYTE, NULL, BP_GET, VT_I2, VT_UI1}, + {DISPID_GLOBAL_VBARRAY, NULL, BP_GET, VT_I2, VT_ARRAY}, + {DISPID_GLOBAL_VBTRUE, NULL, BP_GET, VT_I2, VARIANT_TRUE}, + {DISPID_GLOBAL_VBFALSE, NULL, BP_GET, VT_I2, VARIANT_FALSE}, + {DISPID_GLOBAL_VBUSEDEFAULT, NULL, BP_GET, VT_I2, -2}, + {DISPID_GLOBAL_VBBINARYCOMPARE, NULL, BP_GET, VT_I2, 0}, + {DISPID_GLOBAL_VBTEXTCOMPARE, NULL, BP_GET, VT_I2, 1}, + {DISPID_GLOBAL_VBDATABASECOMPARE, NULL, BP_GET, VT_I2, 2}, + {DISPID_GLOBAL_VBGENERALDATE, NULL, BP_GET, VT_I2, 0}, + {DISPID_GLOBAL_VBLONGDATE, NULL, BP_GET, VT_I2, 1}, + {DISPID_GLOBAL_VBSHORTDATE, NULL, BP_GET, VT_I2, 2}, + {DISPID_GLOBAL_VBLONGTIME, NULL, BP_GET, VT_I2, 3}, + {DISPID_GLOBAL_VBSHORTTIME, NULL, BP_GET, VT_I2, 4}, + {DISPID_GLOBAL_VBOBJECTERROR, NULL, BP_GET, VT_I4, 0x80040000}, + {DISPID_GLOBAL_VBBLACK, NULL, BP_GET, VT_I4, 0x000000}, + {DISPID_GLOBAL_VBBLUE, NULL, BP_GET, VT_I4, 0xff0000}, + {DISPID_GLOBAL_VBCYAN, NULL, BP_GET, VT_I4, 0xffff00}, + {DISPID_GLOBAL_VBGREEN, NULL, BP_GET, VT_I4, 0x00ff00}, + {DISPID_GLOBAL_VBMAGENTA, NULL, BP_GET, VT_I4, 0xff00ff}, + {DISPID_GLOBAL_VBRED, NULL, BP_GET, VT_I4, 0x0000ff}, + {DISPID_GLOBAL_VBWHITE, NULL, BP_GET, VT_I4, 0xffffff}, + {DISPID_GLOBAL_VBYELLOW, NULL, BP_GET, VT_I4, 0x00ffff}, + {DISPID_GLOBAL_VBCR, NULL, BP_GET, VT_BSTR, (UINT_PTR)&vbCr}, + {DISPID_GLOBAL_VBCRLF, NULL, BP_GET, VT_BSTR, (UINT_PTR)&vbCrLf}, + {DISPID_GLOBAL_VBNEWLINE, NULL, BP_GET, VT_BSTR, (UINT_PTR)&vbNewLine}, + {DISPID_GLOBAL_VBFORMFEED, NULL, BP_GET, VT_BSTR, (UINT_PTR)&vbFormFeed}, + {DISPID_GLOBAL_VBLF, NULL, BP_GET, VT_BSTR, (UINT_PTR)&vbLf}, + {DISPID_GLOBAL_VBNULLCHAR, NULL, BP_GET, VT_BSTR, (UINT_PTR)&vbNullChar}, + {DISPID_GLOBAL_VBNULLSTRING, NULL, BP_GET, VT_BSTR, (UINT_PTR)&vbNullString}, + {DISPID_GLOBAL_VBTAB, NULL, BP_GET, VT_BSTR, (UINT_PTR)&vbTab}, + {DISPID_GLOBAL_VBVERTICALTAB, NULL, BP_GET, VT_BSTR, (UINT_PTR)&vbVerticalTab}, + {DISPID_GLOBAL_CCUR, Global_CCur, 0, 1}, + {DISPID_GLOBAL_CINT, Global_CInt, 0, 1}, + {DISPID_GLOBAL_CLNG, Global_CLng, 0, 1}, + {DISPID_GLOBAL_CBOOL, Global_CBool, 0, 1}, + {DISPID_GLOBAL_CBYTE, Global_CByte, 0, 1}, + {DISPID_GLOBAL_CDATE, Global_CDate, 0, 1}, + {DISPID_GLOBAL_CDBL, Global_CDbl, 0, 1}, + {DISPID_GLOBAL_CSNG, Global_CSng, 0, 1}, + {DISPID_GLOBAL_CSTR, Global_CStr, 0, 1}, + {DISPID_GLOBAL_HEX, Global_Hex, 0, 1}, + {DISPID_GLOBAL_OCT, Global_Oct, 0, 1}, + {DISPID_GLOBAL_VARTYPE, Global_VarType, 0, 1}, + {DISPID_GLOBAL_ISDATE, Global_IsDate, 0, 1}, + {DISPID_GLOBAL_ISEMPTY, Global_IsEmpty, 0, 1}, + {DISPID_GLOBAL_ISNULL, Global_IsNull, 0, 1}, + {DISPID_GLOBAL_ISNUMERIC, Global_IsNumeric, 0, 1}, + {DISPID_GLOBAL_ISARRAY, Global_IsArray, 0, 1}, + {DISPID_GLOBAL_ISOBJECT, Global_IsObject, 0, 1}, + {DISPID_GLOBAL_ATN, Global_Ant, 0, 1}, + {DISPID_GLOBAL_COS, Global_Cos, 0, 1}, + {DISPID_GLOBAL_SIN, Global_Sin, 0, 1}, + {DISPID_GLOBAL_TAN, Global_Tan, 0, 1}, + {DISPID_GLOBAL_EXP, Global_Exp, 0, 1}, + {DISPID_GLOBAL_LOG, Global_Log, 0, 1}, + {DISPID_GLOBAL_SQR, Global_Sqr, 0, 1}, + {DISPID_GLOBAL_RANDOMIZE, Global_Randomize, 0, 1}, + {DISPID_GLOBAL_RND, Global_Rnd, 0, 1}, + {DISPID_GLOBAL_TIMER, Global_Timer, 0, 0}, + {DISPID_GLOBAL_LBOUND, Global_LBound, 0, 1}, + {DISPID_GLOBAL_UBOUND, Global_UBound, 0, 1}, + {DISPID_GLOBAL_RGB, Global_RGB, 0, 3}, + {DISPID_GLOBAL_LEN, Global_Len, 0, 1}, + {DISPID_GLOBAL_LENB, Global_LenB, 0, 1}, + {DISPID_GLOBAL_LEFT, Global_Left, 0, 2}, + {DISPID_GLOBAL_LEFTB, Global_LeftB, 0, 2}, + {DISPID_GLOBAL_RIGHT, Global_Right, 0, 2}, + {DISPID_GLOBAL_RIGHTB, Global_RightB, 0, 2}, + {DISPID_GLOBAL_MID, Global_Mid, 0, 2, 3}, + {DISPID_GLOBAL_MIDB, Global_MidB, 0, 2, 3}, + {DISPID_GLOBAL_STRCOMP, Global_StrComp, 0, 2, 3}, + {DISPID_GLOBAL_LCASE, Global_LCase, 0, 1}, + {DISPID_GLOBAL_UCASE, Global_UCase, 0, 1}, + {DISPID_GLOBAL_LTRIM, Global_LTrim, 0, 1}, + {DISPID_GLOBAL_RTRIM, Global_RTrim, 0, 1}, + {DISPID_GLOBAL_TRIM, Global_Trim, 0, 1}, + {DISPID_GLOBAL_SPACE, Global_Space, 0, 1}, + {DISPID_GLOBAL_STRING, Global_String, 0, 0, 2}, + {DISPID_GLOBAL_INSTR, Global_InStr, 0, 2, 4}, + {DISPID_GLOBAL_INSTRB, Global_InStrB, 0, 3, 4}, + {DISPID_GLOBAL_ASCB, Global_AscB, 0, 1}, + {DISPID_GLOBAL_CHRB, Global_ChrB, 0, 1}, + {DISPID_GLOBAL_ASC, Global_Asc, 0, 1}, + {DISPID_GLOBAL_CHR, Global_Chr, 0, 1}, + {DISPID_GLOBAL_ASCW, Global_AscW, 0, 1}, + {DISPID_GLOBAL_CHRW, Global_ChrW, 0, 1}, + {DISPID_GLOBAL_ABS, Global_Abs, 0, 1}, + {DISPID_GLOBAL_FIX, Global_Fix, 0, 1}, + {DISPID_GLOBAL_INT, Global_Int, 0, 1}, + {DISPID_GLOBAL_SGN, Global_Sgn, 0, 1}, + {DISPID_GLOBAL_NOW, Global_Now, 0, 0}, + {DISPID_GLOBAL_DATE, Global_Date, 0, 0}, + {DISPID_GLOBAL_TIME, Global_Time, 0, 0}, + {DISPID_GLOBAL_DAY, Global_Day, 0, 1}, + {DISPID_GLOBAL_MONTH, Global_Month, 0, 1}, + {DISPID_GLOBAL_WEEKDAY, Global_Weekday, 0, 1, 2}, + {DISPID_GLOBAL_YEAR, Global_Year, 0, 1}, + {DISPID_GLOBAL_HOUR, Global_Hour, 0, 1}, + {DISPID_GLOBAL_MINUTE, Global_Minute, 0, 1}, + {DISPID_GLOBAL_SECOND, Global_Second, 0, 1}, + {DISPID_GLOBAL_DATEVALUE, Global_DateValue, 0, 1}, + {DISPID_GLOBAL_TIMEVALUE, Global_TimeValue, 0, 1}, + {DISPID_GLOBAL_DATESERIAL, Global_DateSerial, 0, 3}, + {DISPID_GLOBAL_TIMESERIAL, Global_TimeSerial, 0, 3}, + {DISPID_GLOBAL_INPUTBOX, Global_InputBox, 0, 1, 7}, + {DISPID_GLOBAL_MSGBOX, Global_MsgBox, 0, 1, 5}, + {DISPID_GLOBAL_CREATEOBJECT, Global_CreateObject, 0, 1}, + {DISPID_GLOBAL_GETOBJECT, Global_GetObject, 0, 0, 2}, + {DISPID_GLOBAL_DATEADD, Global_DateAdd, 0, 3}, + {DISPID_GLOBAL_DATEDIFF, Global_DateDiff, 0, 3, 5}, + {DISPID_GLOBAL_DATEPART, Global_DatePart, 0, 2, 4}, + {DISPID_GLOBAL_TYPENAME, Global_TypeName, 0, 1}, + {DISPID_GLOBAL_ARRAY, Global_Array, 0, 1}, + {DISPID_GLOBAL_ERASE, Global_Erase, 0, 1}, + {DISPID_GLOBAL_FILTER, Global_Filter, 0, 2, 4}, + {DISPID_GLOBAL_JOIN, Global_Join, 0, 1, 2}, + {DISPID_GLOBAL_SPLIT, Global_Split, 0, 1, 4}, + {DISPID_GLOBAL_REPLACE, Global_Replace, 0, 3, 6}, + {DISPID_GLOBAL_STRREVERSE, Global_StrReverse, 0, 1}, + {DISPID_GLOBAL_INSTRREV, Global_InStrRev, 0, 2, 4}, + {DISPID_GLOBAL_LOADPICTURE, Global_LoadPicture, 0, 1}, + {DISPID_GLOBAL_SCRIPTENGINE, Global_ScriptEngine, 0, 0}, + {DISPID_GLOBAL_SCRIPTENGINEMAJORVERSION, Global_ScriptEngineMajorVersion, 0, 0}, + {DISPID_GLOBAL_SCRIPTENGINEMINORVERSION, Global_ScriptEngineMinorVersion, 0, 0}, + {DISPID_GLOBAL_SCRIPTENGINEBUILDVERSION, Global_ScriptEngineBuildVersion, 0, 0}, + {DISPID_GLOBAL_FORMATNUMBER, Global_FormatNumber, 0, 1, 5}, + {DISPID_GLOBAL_FORMATCURRENCY, Global_FormatCurrency, 0, 1, 5}, + {DISPID_GLOBAL_FORMATPERCENT, Global_FormatPercent, 0, 1, 5}, + {DISPID_GLOBAL_FORMATDATETIME, Global_FormatDateTime, 0, 1, 2}, + {DISPID_GLOBAL_WEEKDAYNAME, Global_WeekdayName, 0, 1, 3}, + {DISPID_GLOBAL_MONTHNAME, Global_MonthName, 0, 1, 2}, + {DISPID_GLOBAL_ROUND, Global_Round, 0, 1, 2}, + {DISPID_GLOBAL_ESCAPE, Global_Escape, 0, 1}, + {DISPID_GLOBAL_UNESCAPE, Global_Unescape, 0, 1}, + {DISPID_GLOBAL_EVAL, Global_Eval, 0, 1}, + {DISPID_GLOBAL_EXECUTE, Global_Execute, 0, 1}, + {DISPID_GLOBAL_EXECUTEGLOBAL, Global_ExecuteGlobal, 0, 1}, + {DISPID_GLOBAL_GETREF, Global_GetRef, 0, 1}, + {DISPID_GLOBAL_VBMSGBOXHELPBUTTON, NULL, BP_GET, VT_I4, MB_HELP}, + {DISPID_GLOBAL_VBMSGBOXSETFOREGROUND, NULL, BP_GET, VT_I4, MB_SETFOREGROUND}, + {DISPID_GLOBAL_VBMSGBOXRIGHT, NULL, BP_GET, VT_I4, MB_RIGHT}, + {DISPID_GLOBAL_VBMSGBOXRTLREADING, NULL, BP_GET, VT_I4, MB_RTLREADING} +}; + +HRESULT init_global(script_ctx_t *ctx) +{ + HRESULT hres; + + ctx->global_desc.ctx = ctx; + ctx->global_desc.builtin_prop_cnt = sizeof(global_props)/sizeof(*global_props); + ctx->global_desc.builtin_props = global_props; + + hres = get_typeinfo(GlobalObj_tid, &ctx->global_desc.typeinfo); + if(FAILED(hres)) + return hres; + + hres = create_vbdisp(&ctx->global_desc, &ctx->global_obj); + if(FAILED(hres)) + return hres; + + hres = create_script_disp(ctx, &ctx->script_obj); + if(FAILED(hres)) + return hres; + + return init_err(ctx); +} diff --git a/reactos/dll/win32/vbscript/interp.c b/reactos/dll/win32/vbscript/interp.c new file mode 100644 index 00000000000..bbbf226cc9e --- /dev/null +++ b/reactos/dll/win32/vbscript/interp.c @@ -0,0 +1,1942 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "vbscript.h" + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(vbscript); + +static DISPID propput_dispid = DISPID_PROPERTYPUT; + +typedef struct { + vbscode_t *code; + instr_t *instr; + script_ctx_t *script; + function_t *func; + IDispatch *this_obj; + + VARIANT *args; + VARIANT *vars; + + dynamic_var_t *dynamic_vars; + heap_pool_t heap; + + BOOL resume_next; + + unsigned stack_size; + unsigned top; + VARIANT *stack; + + VARIANT ret_val; +} exec_ctx_t; + +typedef HRESULT (*instr_func_t)(exec_ctx_t*); + +typedef enum { + REF_NONE, + REF_DISP, + REF_VAR, + REF_OBJ, + REF_CONST, + REF_FUNC +} ref_type_t; + +typedef struct { + ref_type_t type; + union { + struct { + IDispatch *disp; + DISPID id; + } d; + VARIANT *v; + function_t *f; + IDispatch *obj; + } u; +} ref_t; + +typedef struct { + VARIANT *v; + VARIANT store; + BOOL owned; +} variant_val_t; + +static BOOL lookup_dynamic_vars(dynamic_var_t *var, const WCHAR *name, ref_t *ref) +{ + while(var) { + if(!strcmpiW(var->name, name)) { + ref->type = var->is_const ? REF_CONST : REF_VAR; + ref->u.v = &var->v; + return TRUE; + } + + var = var->next; + } + + return FALSE; +} + +static HRESULT lookup_identifier(exec_ctx_t *ctx, BSTR name, vbdisp_invoke_type_t invoke_type, ref_t *ref) +{ + named_item_t *item; + function_t *func; + unsigned i; + DISPID id; + HRESULT hres; + + static const WCHAR errW[] = {'e','r','r',0}; + + if(invoke_type == VBDISP_LET + && (ctx->func->type == FUNC_FUNCTION || ctx->func->type == FUNC_PROPGET || ctx->func->type == FUNC_DEFGET) + && !strcmpiW(name, ctx->func->name)) { + ref->type = REF_VAR; + ref->u.v = &ctx->ret_val; + return S_OK; + } + + for(i=0; i < ctx->func->var_cnt; i++) { + if(!strcmpiW(ctx->func->vars[i].name, name)) { + ref->type = REF_VAR; + ref->u.v = ctx->vars+i; + return TRUE; + } + } + + for(i=0; i < ctx->func->arg_cnt; i++) { + if(!strcmpiW(ctx->func->args[i].name, name)) { + ref->type = REF_VAR; + ref->u.v = ctx->args+i; + return S_OK; + } + } + + if(lookup_dynamic_vars(ctx->func->type == FUNC_GLOBAL ? ctx->script->global_vars : ctx->dynamic_vars, name, ref)) + return S_OK; + + if(ctx->func->type != FUNC_GLOBAL) { + hres = disp_get_id(ctx->this_obj, name, invoke_type, TRUE, &id); + if(SUCCEEDED(hres)) { + ref->type = REF_DISP; + ref->u.d.disp = ctx->this_obj; + ref->u.d.id = id; + return S_OK; + } + } + + if(ctx->func->type != FUNC_GLOBAL && lookup_dynamic_vars(ctx->script->global_vars, name, ref)) + return S_OK; + + for(func = ctx->script->global_funcs; func; func = func->next) { + if(!strcmpiW(func->name, name)) { + ref->type = REF_FUNC; + ref->u.f = func; + return S_OK; + } + } + + if(!strcmpiW(name, errW)) { + ref->type = REF_OBJ; + ref->u.obj = (IDispatch*)&ctx->script->err_obj->IDispatchEx_iface; + return S_OK; + } + + hres = vbdisp_get_id(ctx->script->global_obj, name, invoke_type, TRUE, &id); + if(SUCCEEDED(hres)) { + ref->type = REF_DISP; + ref->u.d.disp = (IDispatch*)&ctx->script->global_obj->IDispatchEx_iface; + ref->u.d.id = id; + return S_OK; + } + + LIST_FOR_EACH_ENTRY(item, &ctx->script->named_items, named_item_t, entry) { + if((item->flags & SCRIPTITEM_ISVISIBLE) && !strcmpiW(item->name, name)) { + if(!item->disp) { + IUnknown *unk; + + hres = IActiveScriptSite_GetItemInfo(ctx->script->site, name, SCRIPTINFO_IUNKNOWN, &unk, NULL); + if(FAILED(hres)) { + WARN("GetItemInfo failed: %08x\n", hres); + continue; + } + + hres = IUnknown_QueryInterface(unk, &IID_IDispatch, (void**)&item->disp); + IUnknown_Release(unk); + if(FAILED(hres)) { + WARN("object does not implement IDispatch\n"); + continue; + } + } + + ref->type = REF_OBJ; + ref->u.obj = item->disp; + return S_OK; + } + } + + LIST_FOR_EACH_ENTRY(item, &ctx->script->named_items, named_item_t, entry) { + if((item->flags & SCRIPTITEM_GLOBALMEMBERS)) { + hres = disp_get_id(item->disp, name, invoke_type, FALSE, &id); + if(SUCCEEDED(hres)) { + ref->type = REF_DISP; + ref->u.d.disp = item->disp; + ref->u.d.id = id; + return S_OK; + } + } + } + + ref->type = REF_NONE; + return S_OK; +} + +static HRESULT add_dynamic_var(exec_ctx_t *ctx, const WCHAR *name, + BOOL is_const, VARIANT *val, BOOL own_val, VARIANT **out_var) +{ + dynamic_var_t *new_var; + heap_pool_t *heap; + WCHAR *str; + unsigned size; + HRESULT hres; + + heap = ctx->func->type == FUNC_GLOBAL ? &ctx->script->heap : &ctx->heap; + + new_var = heap_pool_alloc(heap, sizeof(*new_var)); + if(!new_var) + return E_OUTOFMEMORY; + + size = (strlenW(name)+1)*sizeof(WCHAR); + str = heap_pool_alloc(heap, size); + if(!str) + return E_OUTOFMEMORY; + memcpy(str, name, size); + new_var->name = str; + new_var->is_const = is_const; + + if(own_val) { + new_var->v = *val; + }else { + V_VT(&new_var->v) = VT_EMPTY; + hres = VariantCopy(&new_var->v, val); + if(FAILED(hres)) + return hres; + } + + if(ctx->func->type == FUNC_GLOBAL) { + new_var->next = ctx->script->global_vars; + ctx->script->global_vars = new_var; + }else { + new_var->next = ctx->dynamic_vars; + ctx->dynamic_vars = new_var; + } + + if(out_var) + *out_var = &new_var->v; + + return S_OK; +} + +static inline VARIANT *stack_pop(exec_ctx_t *ctx) +{ + assert(ctx->top); + return ctx->stack + --ctx->top; +} + +static inline VARIANT *stack_top(exec_ctx_t *ctx, unsigned n) +{ + assert(ctx->top >= n); + return ctx->stack + (ctx->top-n-1); +} + +static HRESULT stack_push(exec_ctx_t *ctx, VARIANT *v) +{ + if(ctx->stack_size == ctx->top) { + VARIANT *new_stack; + + new_stack = heap_realloc(ctx->stack, ctx->stack_size*2*sizeof(*ctx->stack)); + if(!new_stack) { + VariantClear(v); + return E_OUTOFMEMORY; + } + + ctx->stack = new_stack; + ctx->stack_size *= 2; + } + + ctx->stack[ctx->top++] = *v; + return S_OK; +} + +static inline HRESULT stack_push_null(exec_ctx_t *ctx) +{ + VARIANT v; + V_VT(&v) = VT_NULL; + return stack_push(ctx, &v); +} + +static void stack_popn(exec_ctx_t *ctx, unsigned n) +{ + while(n--) + VariantClear(stack_pop(ctx)); +} + +static HRESULT stack_pop_val(exec_ctx_t *ctx, variant_val_t *v) +{ + VARIANT *var; + + var = stack_pop(ctx); + + if(V_VT(var) == (VT_BYREF|VT_VARIANT)) { + v->owned = FALSE; + var = V_VARIANTREF(var); + }else { + v->owned = TRUE; + } + + if(V_VT(var) == VT_DISPATCH) { + DISPPARAMS dp = {0}; + HRESULT hres; + + hres = disp_call(ctx->script, V_DISPATCH(var), DISPID_VALUE, &dp, &v->store); + if(v->owned) + IDispatch_Release(V_DISPATCH(var)); + if(FAILED(hres)) + return hres; + + v->owned = TRUE; + v->v = &v->store; + }else { + v->v = var; + } + + return S_OK; +} + +static HRESULT stack_assume_val(exec_ctx_t *ctx, unsigned n) +{ + VARIANT *v = stack_top(ctx, n); + HRESULT hres; + + if(V_VT(v) == (VT_BYREF|VT_VARIANT)) { + VARIANT *ref = V_VARIANTREF(v); + + V_VT(v) = VT_EMPTY; + hres = VariantCopy(v, ref); + if(FAILED(hres)) + return hres; + } + + if(V_VT(v) == VT_DISPATCH) { + DISPPARAMS dp = {0}; + IDispatch *disp; + + disp = V_DISPATCH(v); + V_VT(v) = VT_EMPTY; + hres = disp_call(ctx->script, disp, DISPID_VALUE, &dp, v); + IDispatch_Release(disp); + if(FAILED(hres)) + return hres; + } + + return S_OK; +} + +static inline void release_val(variant_val_t *v) +{ + if(v->owned) + VariantClear(v->v); +} + +static int stack_pop_bool(exec_ctx_t *ctx, BOOL *b) +{ + variant_val_t val; + HRESULT hres; + + hres = stack_pop_val(ctx, &val); + if(FAILED(hres)) + return hres; + + switch (V_VT(val.v)) + { + case VT_BOOL: + *b = V_BOOL(val.v); + break; + case VT_NULL: + *b = FALSE; + break; + case VT_I2: + *b = V_I2(val.v); + break; + case VT_I4: + *b = V_I4(val.v); + break; + default: + FIXME("unsupported for %s\n", debugstr_variant(val.v)); + release_val(&val); + return E_NOTIMPL; + } + return S_OK; +} + +static HRESULT stack_pop_disp(exec_ctx_t *ctx, IDispatch **ret) +{ + VARIANT *v = stack_pop(ctx); + + if(V_VT(v) == VT_DISPATCH) { + *ret = V_DISPATCH(v); + return S_OK; + } + + if(V_VT(v) != (VT_VARIANT|VT_BYREF)) { + FIXME("not supported type: %s\n", debugstr_variant(v)); + VariantClear(v); + return E_FAIL; + } + + v = V_BYREF(v); + if(V_VT(v) != VT_DISPATCH) { + FIXME("not disp %s\n", debugstr_variant(v)); + return E_FAIL; + } + + if(V_DISPATCH(v)) + IDispatch_AddRef(V_DISPATCH(v)); + *ret = V_DISPATCH(v); + return S_OK; +} + +static HRESULT stack_assume_disp(exec_ctx_t *ctx, unsigned n, IDispatch **disp) +{ + VARIANT *v = stack_top(ctx, n), *ref; + + if(V_VT(v) != VT_DISPATCH) { + if(V_VT(v) != (VT_VARIANT|VT_BYREF)) { + FIXME("not supported type: %s\n", debugstr_variant(v)); + return E_FAIL; + } + + ref = V_VARIANTREF(v); + if(V_VT(ref) != VT_DISPATCH) { + FIXME("not disp %s\n", debugstr_variant(ref)); + return E_FAIL; + } + + V_VT(v) = VT_DISPATCH; + V_DISPATCH(v) = V_DISPATCH(ref); + if(V_DISPATCH(v)) + IDispatch_AddRef(V_DISPATCH(v)); + } + + if(disp) + *disp = V_DISPATCH(v); + return S_OK; +} + +static inline void instr_jmp(exec_ctx_t *ctx, unsigned addr) +{ + ctx->instr = ctx->code->instrs + addr; +} + +static void vbstack_to_dp(exec_ctx_t *ctx, unsigned arg_cnt, BOOL is_propput, DISPPARAMS *dp) +{ + dp->cNamedArgs = is_propput ? 1 : 0; + dp->cArgs = arg_cnt + dp->cNamedArgs; + dp->rgdispidNamedArgs = is_propput ? &propput_dispid : NULL; + + if(arg_cnt) { + VARIANT tmp; + unsigned i; + + assert(ctx->top >= arg_cnt); + + for(i=1; i*2 <= arg_cnt; i++) { + tmp = ctx->stack[ctx->top-i]; + ctx->stack[ctx->top-i] = ctx->stack[ctx->top-arg_cnt+i-1]; + ctx->stack[ctx->top-arg_cnt+i-1] = tmp; + } + + dp->rgvarg = ctx->stack + ctx->top-dp->cArgs; + }else { + dp->rgvarg = is_propput ? ctx->stack+ctx->top-1 : NULL; + } +} + +static HRESULT do_icall(exec_ctx_t *ctx, VARIANT *res) +{ + BSTR identifier = ctx->instr->arg1.bstr; + const unsigned arg_cnt = ctx->instr->arg2.uint; + DISPPARAMS dp; + ref_t ref; + HRESULT hres; + + hres = lookup_identifier(ctx, identifier, VBDISP_CALLGET, &ref); + if(FAILED(hres)) + return hres; + + vbstack_to_dp(ctx, arg_cnt, FALSE, &dp); + + switch(ref.type) { + case REF_VAR: + case REF_CONST: + if(!res) { + FIXME("REF_VAR no res\n"); + return E_NOTIMPL; + } + + if(arg_cnt) { + FIXME("arguments not implemented\n"); + return E_NOTIMPL; + } + + V_VT(res) = VT_BYREF|VT_VARIANT; + V_BYREF(res) = V_VT(ref.u.v) == (VT_VARIANT|VT_BYREF) ? V_VARIANTREF(ref.u.v) : ref.u.v; + break; + case REF_DISP: + hres = disp_call(ctx->script, ref.u.d.disp, ref.u.d.id, &dp, res); + if(FAILED(hres)) + return hres; + break; + case REF_FUNC: + hres = exec_script(ctx->script, ref.u.f, NULL, &dp, res); + if(FAILED(hres)) + return hres; + break; + case REF_OBJ: + if(arg_cnt) { + FIXME("arguments on object\n"); + return E_NOTIMPL; + } + + if(res) { + IDispatch_AddRef(ref.u.obj); + V_VT(res) = VT_DISPATCH; + V_DISPATCH(res) = ref.u.obj; + } + break; + case REF_NONE: + if(res && !ctx->func->code_ctx->option_explicit && arg_cnt == 0) { + VARIANT v, *new; + VariantInit(&v); + hres = add_dynamic_var(ctx, identifier, FALSE, &v, FALSE, &new); + if(FAILED(hres)) + return hres; + V_VT(res) = VT_BYREF|VT_VARIANT; + V_BYREF(res) = new; + break; + } + FIXME("%s not found\n", debugstr_w(identifier)); + return DISP_E_UNKNOWNNAME; + } + + stack_popn(ctx, arg_cnt); + return S_OK; +} + +static HRESULT interp_icall(exec_ctx_t *ctx) +{ + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = do_icall(ctx, &v); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_icallv(exec_ctx_t *ctx) +{ + TRACE("\n"); + return do_icall(ctx, NULL); +} + +static HRESULT do_mcall(exec_ctx_t *ctx, VARIANT *res) +{ + const BSTR identifier = ctx->instr->arg1.bstr; + const unsigned arg_cnt = ctx->instr->arg2.uint; + IDispatch *obj; + DISPPARAMS dp; + DISPID id; + HRESULT hres; + + hres = stack_pop_disp(ctx, &obj); + if(FAILED(hres)) + return hres; + + if(!obj) { + FIXME("NULL obj\n"); + return E_FAIL; + } + + vbstack_to_dp(ctx, arg_cnt, FALSE, &dp); + + hres = disp_get_id(obj, identifier, VBDISP_CALLGET, FALSE, &id); + if(SUCCEEDED(hres)) + hres = disp_call(ctx->script, obj, id, &dp, res); + IDispatch_Release(obj); + if(FAILED(hres)) + return hres; + + stack_popn(ctx, arg_cnt); + return S_OK; +} + +static HRESULT interp_mcall(exec_ctx_t *ctx) +{ + VARIANT res; + HRESULT hres; + + TRACE("\n"); + + hres = do_mcall(ctx, &res); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &res); +} + +static HRESULT interp_mcallv(exec_ctx_t *ctx) +{ + TRACE("\n"); + + return do_mcall(ctx, NULL); +} + +static HRESULT assign_ident(exec_ctx_t *ctx, BSTR name, DISPPARAMS *dp) +{ + ref_t ref; + HRESULT hres; + + hres = lookup_identifier(ctx, name, VBDISP_LET, &ref); + if(FAILED(hres)) + return hres; + + switch(ref.type) { + case REF_VAR: { + VARIANT *v = ref.u.v; + + if(arg_cnt(dp)) { + FIXME("arg_cnt %d not supported\n", arg_cnt(dp)); + return E_NOTIMPL; + } + + if(V_VT(v) == (VT_VARIANT|VT_BYREF)) + v = V_VARIANTREF(v); + + hres = VariantCopy(v, dp->rgvarg); + break; + } + case REF_DISP: + hres = disp_propput(ctx->script, ref.u.d.disp, ref.u.d.id, dp); + break; + case REF_FUNC: + FIXME("functions not implemented\n"); + return E_NOTIMPL; + case REF_OBJ: + FIXME("REF_OBJ\n"); + return E_NOTIMPL; + case REF_CONST: + FIXME("REF_CONST\n"); + return E_NOTIMPL; + case REF_NONE: + if(ctx->func->code_ctx->option_explicit) { + FIXME("throw exception\n"); + hres = E_FAIL; + }else { + if(arg_cnt(dp)) { + FIXME("arg_cnt %d not supported\n", arg_cnt(dp)); + return E_NOTIMPL; + } + + TRACE("creating variable %s\n", debugstr_w(name)); + hres = add_dynamic_var(ctx, name, FALSE, dp->rgvarg, FALSE, NULL); + } + } + + return hres; +} + +static HRESULT interp_assign_ident(exec_ctx_t *ctx) +{ + const BSTR arg = ctx->instr->arg1.bstr; + const unsigned arg_cnt = ctx->instr->arg2.uint; + DISPPARAMS dp; + HRESULT hres; + + TRACE("%s\n", debugstr_w(arg)); + + hres = stack_assume_val(ctx, arg_cnt); + if(FAILED(hres)) + return hres; + + vbstack_to_dp(ctx, arg_cnt, TRUE, &dp); + hres = assign_ident(ctx, arg, &dp); + if(FAILED(hres)) + return hres; + + stack_popn(ctx, arg_cnt+1); + return S_OK; +} + +static HRESULT interp_set_ident(exec_ctx_t *ctx) +{ + const BSTR arg = ctx->instr->arg1.bstr; + const unsigned arg_cnt = ctx->instr->arg2.uint; + DISPPARAMS dp; + HRESULT hres; + + TRACE("%s\n", debugstr_w(arg)); + + if(arg_cnt) { + FIXME("arguments not supported\n"); + return E_NOTIMPL; + } + + hres = stack_assume_disp(ctx, 0, NULL); + if(FAILED(hres)) + return hres; + + vbstack_to_dp(ctx, 0, TRUE, &dp); + hres = assign_ident(ctx, ctx->instr->arg1.bstr, &dp); + if(FAILED(hres)) + return hres; + + stack_popn(ctx, 1); + return S_OK; +} + +static HRESULT interp_assign_member(exec_ctx_t *ctx) +{ + BSTR identifier = ctx->instr->arg1.bstr; + const unsigned arg_cnt = ctx->instr->arg2.uint; + IDispatch *obj; + DISPPARAMS dp; + DISPID id; + HRESULT hres; + + TRACE("%s\n", debugstr_w(identifier)); + + hres = stack_assume_disp(ctx, arg_cnt+1, &obj); + if(FAILED(hres)) + return hres; + + if(!obj) { + FIXME("NULL obj\n"); + return E_FAIL; + } + + hres = stack_assume_val(ctx, arg_cnt); + if(FAILED(hres)) + return hres; + + hres = disp_get_id(obj, identifier, VBDISP_LET, FALSE, &id); + if(SUCCEEDED(hres)) { + vbstack_to_dp(ctx, arg_cnt, TRUE, &dp); + hres = disp_propput(ctx->script, obj, id, &dp); + } + if(FAILED(hres)) + return hres; + + stack_popn(ctx, arg_cnt+2); + return S_OK; +} + +static HRESULT interp_set_member(exec_ctx_t *ctx) +{ + BSTR identifier = ctx->instr->arg1.bstr; + const unsigned arg_cnt = ctx->instr->arg2.uint; + IDispatch *obj; + DISPPARAMS dp; + DISPID id; + HRESULT hres; + + TRACE("%s\n", debugstr_w(identifier)); + + if(arg_cnt) { + FIXME("arguments not supported\n"); + return E_NOTIMPL; + } + + hres = stack_assume_disp(ctx, 1, &obj); + if(FAILED(hres)) + return hres; + + if(!obj) { + FIXME("NULL obj\n"); + return E_FAIL; + } + + hres = stack_assume_disp(ctx, 0, NULL); + if(FAILED(hres)) + return hres; + + hres = disp_get_id(obj, identifier, VBDISP_SET, FALSE, &id); + if(SUCCEEDED(hres)) { + vbstack_to_dp(ctx, arg_cnt, TRUE, &dp); + hres = disp_propput(ctx->script, obj, id, &dp); + } + if(FAILED(hres)) + return hres; + + stack_popn(ctx, 2); + return S_OK; +} + +static HRESULT interp_const(exec_ctx_t *ctx) +{ + BSTR arg = ctx->instr->arg1.bstr; + variant_val_t val; + ref_t ref; + HRESULT hres; + + TRACE("%s\n", debugstr_w(arg)); + + assert(ctx->func->type == FUNC_GLOBAL); + + hres = lookup_identifier(ctx, arg, VBDISP_CALLGET, &ref); + if(FAILED(hres)) + return hres; + + if(ref.type != REF_NONE) { + FIXME("%s already defined\n", debugstr_w(arg)); + return E_FAIL; + } + + hres = stack_pop_val(ctx, &val); + if(FAILED(hres)) + return hres; + + return add_dynamic_var(ctx, arg, TRUE, val.v, val.owned, NULL); +} + +static HRESULT interp_val(exec_ctx_t *ctx) +{ + variant_val_t val; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &val); + if(FAILED(hres)) + return hres; + + if(!val.owned) { + V_VT(&v) = VT_EMPTY; + hres = VariantCopy(&v, val.v); + if(FAILED(hres)) + return hres; + } + + return stack_push(ctx, val.owned ? val.v : &v); +} + +static HRESULT interp_pop(exec_ctx_t *ctx) +{ + const unsigned n = ctx->instr->arg1.uint; + + TRACE("%u\n", n); + + stack_popn(ctx, n); + return S_OK; +} + +static HRESULT interp_new(exec_ctx_t *ctx) +{ + const WCHAR *arg = ctx->instr->arg1.bstr; + class_desc_t *class_desc; + vbdisp_t *obj; + VARIANT v; + HRESULT hres; + + TRACE("%s\n", debugstr_w(arg)); + + for(class_desc = ctx->script->classes; class_desc; class_desc = class_desc->next) { + if(!strcmpiW(class_desc->name, arg)) + break; + } + if(!class_desc) { + FIXME("Class %s not found\n", debugstr_w(arg)); + return E_FAIL; + } + + hres = create_vbdisp(class_desc, &obj); + if(FAILED(hres)) + return hres; + + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = (IDispatch*)&obj->IDispatchEx_iface; + return stack_push(ctx, &v); +} + +static HRESULT interp_step(exec_ctx_t *ctx) +{ + const BSTR ident = ctx->instr->arg2.bstr; + BOOL gteq_zero; + VARIANT zero; + ref_t ref; + HRESULT hres; + + TRACE("%s\n", debugstr_w(ident)); + + V_VT(&zero) = VT_I2; + V_I2(&zero) = 0; + hres = VarCmp(stack_top(ctx, 0), &zero, ctx->script->lcid, 0); + if(FAILED(hres)) + return hres; + + gteq_zero = hres == VARCMP_GT || hres == VARCMP_EQ; + + hres = lookup_identifier(ctx, ident, VBDISP_ANY, &ref); + if(FAILED(hres)) + return hres; + + if(ref.type != REF_VAR) { + FIXME("%s is not REF_VAR\n", debugstr_w(ident)); + return E_FAIL; + } + + hres = VarCmp(ref.u.v, stack_top(ctx, 1), ctx->script->lcid, 0); + if(FAILED(hres)) + return hres; + + if(hres == VARCMP_EQ || hres == (gteq_zero ? VARCMP_LT : VARCMP_GT)) { + ctx->instr++; + }else { + stack_popn(ctx, 2); + instr_jmp(ctx, ctx->instr->arg1.uint); + } + return S_OK; +} + +static HRESULT interp_newenum(exec_ctx_t *ctx) +{ + VARIANT *v, r; + HRESULT hres; + + TRACE("\n"); + + v = stack_pop(ctx); + switch(V_VT(v)) { + case VT_DISPATCH: { + IEnumVARIANT *iter; + DISPPARAMS dp = {0}; + VARIANT iterv; + + hres = disp_call(ctx->script, V_DISPATCH(v), DISPID_NEWENUM, &dp, &iterv); + VariantClear(v); + if(FAILED(hres)) + return hres; + + if(V_VT(&iterv) != VT_UNKNOWN && V_VT(&iterv) != VT_DISPATCH) { + FIXME("Unsupported iterv %s\n", debugstr_variant(&iterv)); + VariantClear(&iterv); + return hres; + } + + hres = IUnknown_QueryInterface(V_UNKNOWN(&iterv), &IID_IEnumVARIANT, (void**)&iter); + IUnknown_Release(V_UNKNOWN(&iterv)); + if(FAILED(hres)) { + FIXME("Could not get IEnumVARIANT iface: %08x\n", hres); + return hres; + } + + V_VT(&r) = VT_UNKNOWN; + V_UNKNOWN(&r) = (IUnknown*)iter; + break; + } + default: + FIXME("Unsupported for %s\n", debugstr_variant(v)); + VariantClear(v); + return E_NOTIMPL; + } + + return stack_push(ctx, &r); +} + +static HRESULT interp_enumnext(exec_ctx_t *ctx) +{ + const unsigned loop_end = ctx->instr->arg1.uint; + const BSTR ident = ctx->instr->arg2.bstr; + VARIANT v; + DISPPARAMS dp = {&v, &propput_dispid, 1, 1}; + IEnumVARIANT *iter; + BOOL do_continue; + HRESULT hres; + + TRACE("\n"); + + assert(V_VT(stack_top(ctx, 0)) == VT_UNKNOWN); + iter = (IEnumVARIANT*)V_UNKNOWN(stack_top(ctx, 0)); + + V_VT(&v) = VT_EMPTY; + hres = IEnumVARIANT_Next(iter, 1, &v, NULL); + if(FAILED(hres)) + return hres; + + do_continue = hres == S_OK; + hres = assign_ident(ctx, ident, &dp); + VariantClear(&v); + if(FAILED(hres)) + return hres; + + if(do_continue) { + ctx->instr++; + }else { + stack_pop(ctx); + instr_jmp(ctx, loop_end); + } + return S_OK; +} + +static HRESULT interp_jmp(exec_ctx_t *ctx) +{ + const unsigned arg = ctx->instr->arg1.uint; + + TRACE("%u\n", arg); + + instr_jmp(ctx, arg); + return S_OK; +} + +static HRESULT interp_jmp_false(exec_ctx_t *ctx) +{ + const unsigned arg = ctx->instr->arg1.uint; + HRESULT hres; + BOOL b; + + TRACE("%u\n", arg); + + hres = stack_pop_bool(ctx, &b); + if(FAILED(hres)) + return hres; + + if(b) + ctx->instr++; + else + instr_jmp(ctx, ctx->instr->arg1.uint); + return S_OK; +} + +static HRESULT interp_jmp_true(exec_ctx_t *ctx) +{ + const unsigned arg = ctx->instr->arg1.uint; + HRESULT hres; + BOOL b; + + TRACE("%u\n", arg); + + hres = stack_pop_bool(ctx, &b); + if(FAILED(hres)) + return hres; + + if(b) + instr_jmp(ctx, ctx->instr->arg1.uint); + else + ctx->instr++; + return S_OK; +} + +static HRESULT interp_ret(exec_ctx_t *ctx) +{ + TRACE("\n"); + + ctx->instr = NULL; + return S_OK; +} + +static HRESULT interp_stop(exec_ctx_t *ctx) +{ + WARN("\n"); + + /* NOTE: this should have effect in debugging mode (that we don't support yet) */ + return S_OK; +} + +static HRESULT interp_me(exec_ctx_t *ctx) +{ + VARIANT v; + + TRACE("\n"); + + IDispatch_AddRef(ctx->this_obj); + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = ctx->this_obj; + return stack_push(ctx, &v); +} + +static HRESULT interp_bool(exec_ctx_t *ctx) +{ + const VARIANT_BOOL arg = ctx->instr->arg1.lng; + VARIANT v; + + TRACE("%s\n", arg ? "true" : "false"); + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = arg; + return stack_push(ctx, &v); +} + +static HRESULT interp_errmode(exec_ctx_t *ctx) +{ + const int err_mode = ctx->instr->arg1.uint; + + TRACE("%d\n", err_mode); + + ctx->resume_next = err_mode; + return S_OK; +} + +static HRESULT interp_string(exec_ctx_t *ctx) +{ + VARIANT v; + + TRACE("\n"); + + V_VT(&v) = VT_BSTR; + V_BSTR(&v) = SysAllocString(ctx->instr->arg1.str); + if(!V_BSTR(&v)) + return E_OUTOFMEMORY; + + return stack_push(ctx, &v); +} + +static HRESULT interp_long(exec_ctx_t *ctx) +{ + const LONG arg = ctx->instr->arg1.lng; + VARIANT v; + + TRACE("%d\n", arg); + + V_VT(&v) = VT_I4; + V_I4(&v) = arg; + return stack_push(ctx, &v); +} + +static HRESULT interp_short(exec_ctx_t *ctx) +{ + const LONG arg = ctx->instr->arg1.lng; + VARIANT v; + + TRACE("%d\n", arg); + + V_VT(&v) = VT_I2; + V_I2(&v) = arg; + return stack_push(ctx, &v); +} + +static HRESULT interp_double(exec_ctx_t *ctx) +{ + const DOUBLE *arg = ctx->instr->arg1.dbl; + VARIANT v; + + TRACE("%lf\n", *arg); + + V_VT(&v) = VT_R8; + V_R8(&v) = *arg; + return stack_push(ctx, &v); +} + +static HRESULT interp_empty(exec_ctx_t *ctx) +{ + VARIANT v; + + TRACE("\n"); + + V_VT(&v) = VT_EMPTY; + return stack_push(ctx, &v); +} + +static HRESULT interp_null(exec_ctx_t *ctx) +{ + TRACE("\n"); + return stack_push_null(ctx); +} + +static HRESULT interp_nothing(exec_ctx_t *ctx) +{ + VARIANT v; + + TRACE("\n"); + + V_VT(&v) = VT_DISPATCH; + V_DISPATCH(&v) = NULL; + return stack_push(ctx, &v); +} + +static HRESULT interp_not(exec_ctx_t *ctx) +{ + variant_val_t val; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &val); + if(FAILED(hres)) + return hres; + + hres = VarNot(val.v, &v); + release_val(&val); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_and(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarAnd(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_or(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarOr(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_xor(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarXor(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_eqv(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarEqv(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_imp(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarImp(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT var_cmp(exec_ctx_t *ctx, VARIANT *l, VARIANT *r) +{ + TRACE("%s %s\n", debugstr_variant(l), debugstr_variant(r)); + + /* FIXME: Fix comparing string to number */ + + return VarCmp(l, r, ctx->script->lcid, 0); + } + +static HRESULT cmp_oper(exec_ctx_t *ctx) +{ + variant_val_t l, r; + HRESULT hres; + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = var_cmp(ctx, l.v, r.v); + release_val(&l); + } + + release_val(&r); + return hres; +} + +static HRESULT interp_equal(exec_ctx_t *ctx) +{ + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = cmp_oper(ctx); + if(FAILED(hres)) + return hres; + if(hres == VARCMP_NULL) + return stack_push_null(ctx); + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = hres == VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE; + return stack_push(ctx, &v); +} + +static HRESULT interp_nequal(exec_ctx_t *ctx) +{ + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = cmp_oper(ctx); + if(FAILED(hres)) + return hres; + if(hres == VARCMP_NULL) + return stack_push_null(ctx); + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = hres != VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE; + return stack_push(ctx, &v); +} + +static HRESULT interp_gt(exec_ctx_t *ctx) +{ + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = cmp_oper(ctx); + if(FAILED(hres)) + return hres; + if(hres == VARCMP_NULL) + return stack_push_null(ctx); + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = hres == VARCMP_GT ? VARIANT_TRUE : VARIANT_FALSE; + return stack_push(ctx, &v); +} + +static HRESULT interp_gteq(exec_ctx_t *ctx) +{ + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = cmp_oper(ctx); + if(FAILED(hres)) + return hres; + if(hres == VARCMP_NULL) + return stack_push_null(ctx); + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = hres == VARCMP_GT || hres == VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE; + return stack_push(ctx, &v); +} + +static HRESULT interp_lt(exec_ctx_t *ctx) +{ + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = cmp_oper(ctx); + if(FAILED(hres)) + return hres; + if(hres == VARCMP_NULL) + return stack_push_null(ctx); + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = hres == VARCMP_LT ? VARIANT_TRUE : VARIANT_FALSE; + return stack_push(ctx, &v); +} + +static HRESULT interp_lteq(exec_ctx_t *ctx) +{ + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = cmp_oper(ctx); + if(FAILED(hres)) + return hres; + if(hres == VARCMP_NULL) + return stack_push_null(ctx); + + V_VT(&v) = VT_BOOL; + V_BOOL(&v) = hres == VARCMP_LT || hres == VARCMP_EQ ? VARIANT_TRUE : VARIANT_FALSE; + return stack_push(ctx, &v); +} + +static HRESULT interp_case(exec_ctx_t *ctx) +{ + const unsigned arg = ctx->instr->arg1.uint; + variant_val_t v; + HRESULT hres; + + TRACE("%d\n", arg); + + hres = stack_pop_val(ctx, &v); + if(FAILED(hres)) + return hres; + + hres = var_cmp(ctx, stack_top(ctx, 0), v.v); + release_val(&v); + if(FAILED(hres)) + return hres; + + if(hres == VARCMP_EQ) { + stack_popn(ctx, 1); + instr_jmp(ctx, arg); + }else { + ctx->instr++; + } + + return S_OK; +} + +static HRESULT disp_cmp(IDispatch *disp1, IDispatch *disp2, VARIANT_BOOL *ret) +{ + IObjectIdentity *identity; + IUnknown *unk1, *unk2; + HRESULT hres; + + if(disp1 == disp2) { + *ret = VARIANT_TRUE; + return S_OK; + } + + if(!disp1 || !disp2) { + *ret = VARIANT_FALSE; + return S_OK; + } + + hres = IDispatch_QueryInterface(disp1, &IID_IUnknown, (void**)&unk1); + if(FAILED(hres)) + return hres; + + hres = IDispatch_QueryInterface(disp2, &IID_IUnknown, (void**)&unk2); + if(FAILED(hres)) { + IUnknown_Release(unk1); + return hres; + } + + if(unk1 == unk2) { + *ret = VARIANT_TRUE; + }else { + hres = IUnknown_QueryInterface(unk1, &IID_IObjectIdentity, (void**)&identity); + if(SUCCEEDED(hres)) { + hres = IObjectIdentity_IsEqualObject(identity, unk2); + IObjectIdentity_Release(identity); + *ret = hres == S_OK ? VARIANT_TRUE : VARIANT_FALSE; + }else { + *ret = VARIANT_FALSE; + } + } + + IUnknown_Release(unk1); + IUnknown_Release(unk2); + return S_OK; +} + +static HRESULT interp_is(exec_ctx_t *ctx) +{ + IDispatch *l, *r; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_disp(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_disp(ctx, &l); + if(SUCCEEDED(hres)) { + V_VT(&v) = VT_BOOL; + hres = disp_cmp(l, r, &V_BOOL(&v)); + if(l) + IDispatch_Release(l); + } + if(r) + IDispatch_Release(r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_concat(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarCat(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_add(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarAdd(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_sub(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarSub(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_mod(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarMod(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_idiv(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarIdiv(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_div(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarDiv(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_mul(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarMul(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_exp(exec_ctx_t *ctx) +{ + variant_val_t r, l; + VARIANT v; + HRESULT hres; + + TRACE("\n"); + + hres = stack_pop_val(ctx, &r); + if(FAILED(hres)) + return hres; + + hres = stack_pop_val(ctx, &l); + if(SUCCEEDED(hres)) { + hres = VarPow(l.v, r.v, &v); + release_val(&l); + } + release_val(&r); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_neg(exec_ctx_t *ctx) +{ + variant_val_t val; + VARIANT v; + HRESULT hres; + + hres = stack_pop_val(ctx, &val); + if(FAILED(hres)) + return hres; + + hres = VarNeg(val.v, &v); + release_val(&val); + if(FAILED(hres)) + return hres; + + return stack_push(ctx, &v); +} + +static HRESULT interp_incc(exec_ctx_t *ctx) +{ + const BSTR ident = ctx->instr->arg1.bstr; + VARIANT v; + ref_t ref; + HRESULT hres; + + TRACE("\n"); + + hres = lookup_identifier(ctx, ident, VBDISP_LET, &ref); + if(FAILED(hres)) + return hres; + + if(ref.type != REF_VAR) { + FIXME("ref.type is not REF_VAR\n"); + return E_FAIL; + } + + hres = VarAdd(stack_top(ctx, 0), ref.u.v, &v); + if(FAILED(hres)) + return hres; + + VariantClear(ref.u.v); + *ref.u.v = v; + return S_OK; +} + +static const instr_func_t op_funcs[] = { +#define X(x,n,a,b) interp_ ## x, +OP_LIST +#undef X +}; + +static const unsigned op_move[] = { +#define X(x,n,a,b) n, +OP_LIST +#undef X +}; + +void release_dynamic_vars(dynamic_var_t *var) +{ + while(var) { + VariantClear(&var->v); + var = var->next; + } +} + +static void release_exec(exec_ctx_t *ctx) +{ + unsigned i; + + VariantClear(&ctx->ret_val); + release_dynamic_vars(ctx->dynamic_vars); + + if(ctx->this_obj) + IDispatch_Release(ctx->this_obj); + + if(ctx->args) { + for(i=0; i < ctx->func->arg_cnt; i++) + VariantClear(ctx->args+i); + } + + if(ctx->vars) { + for(i=0; i < ctx->func->var_cnt; i++) + VariantClear(ctx->vars+i); + } + + heap_pool_free(&ctx->heap); + heap_free(ctx->args); + heap_free(ctx->vars); + heap_free(ctx->stack); +} + +HRESULT exec_script(script_ctx_t *ctx, function_t *func, IDispatch *this_obj, DISPPARAMS *dp, VARIANT *res) +{ + exec_ctx_t exec = {func->code_ctx}; + vbsop_t op; + HRESULT hres = S_OK; + + exec.code = func->code_ctx; + + if(dp ? func->arg_cnt != arg_cnt(dp) : func->arg_cnt) { + FIXME("wrong arg_cnt %d, expected %d\n", dp ? arg_cnt(dp) : 0, func->arg_cnt); + return E_FAIL; + } + + heap_pool_init(&exec.heap); + + if(func->arg_cnt) { + VARIANT *v; + unsigned i; + + exec.args = heap_alloc_zero(func->arg_cnt * sizeof(VARIANT)); + if(!exec.args) { + release_exec(&exec); + return E_OUTOFMEMORY; + } + + for(i=0; i < func->arg_cnt; i++) { + v = get_arg(dp, i); + if(V_VT(v) == (VT_VARIANT|VT_BYREF)) { + if(func->args[i].by_ref) + exec.args[i] = *v; + else + hres = VariantCopy(exec.args+i, V_VARIANTREF(v)); + }else { + hres = VariantCopy(exec.args+i, v); + } + if(FAILED(hres)) { + release_exec(&exec); + return hres; + } + } + }else { + exec.args = NULL; + } + + if(func->var_cnt) { + exec.vars = heap_alloc_zero(func->var_cnt * sizeof(VARIANT)); + if(!exec.vars) { + release_exec(&exec); + return E_OUTOFMEMORY; + } + }else { + exec.vars = NULL; + } + + exec.stack_size = 16; + exec.top = 0; + exec.stack = heap_alloc(exec.stack_size * sizeof(VARIANT)); + if(!exec.stack) { + release_exec(&exec); + return E_OUTOFMEMORY; + } + + if(this_obj) + exec.this_obj = this_obj; + else if (ctx->host_global) + exec.this_obj = ctx->host_global; + else + exec.this_obj = (IDispatch*)&ctx->script_obj->IDispatchEx_iface; + IDispatch_AddRef(exec.this_obj); + + exec.instr = exec.code->instrs + func->code_off; + exec.script = ctx; + exec.func = func; + + while(exec.instr) { + op = exec.instr->op; + hres = op_funcs[op](&exec); + if(FAILED(hres)) { + if(exec.resume_next) + FIXME("Failed %08x in resume next mode\n", hres); + else + WARN("Failed %08x\n", hres); + stack_popn(&exec, exec.top); + break; + } + + exec.instr += op_move[op]; + } + + assert(!exec.top); + if(func->type != FUNC_FUNCTION && func->type != FUNC_PROPGET && func->type != FUNC_DEFGET) + assert(V_VT(&exec.ret_val) == VT_EMPTY); + + if(SUCCEEDED(hres) && res) { + *res = exec.ret_val; + V_VT(&exec.ret_val) = VT_EMPTY; + } + + release_exec(&exec); + return hres; +} diff --git a/reactos/dll/win32/vbscript/lex.c b/reactos/dll/win32/vbscript/lex.c new file mode 100644 index 00000000000..a858b767d6c --- /dev/null +++ b/reactos/dll/win32/vbscript/lex.c @@ -0,0 +1,444 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +//#include + +#include "vbscript.h" +#include "parse.h" +#include "parser.tab.h" + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(vbscript); + +static const WCHAR andW[] = {'a','n','d',0}; +static const WCHAR byrefW[] = {'b','y','r','e','f',0}; +static const WCHAR byvalW[] = {'b','y','v','a','l',0}; +static const WCHAR callW[] = {'c','a','l','l',0}; +static const WCHAR caseW[] = {'c','a','s','e',0}; +static const WCHAR classW[] = {'c','l','a','s','s',0}; +static const WCHAR constW[] = {'c','o','n','s','t',0}; +static const WCHAR defaultW[] = {'d','e','f','a','u','l','t',0}; +static const WCHAR dimW[] = {'d','i','m',0}; +static const WCHAR doW[] = {'d','o',0}; +static const WCHAR eachW[] = {'e','a','c','h',0}; +static const WCHAR elseW[] = {'e','l','s','e',0}; +static const WCHAR elseifW[] = {'e','l','s','e','i','f',0}; +static const WCHAR emptyW[] = {'e','m','p','t','y',0}; +static const WCHAR endW[] = {'e','n','d',0}; +static const WCHAR eqvW[] = {'e','q','v',0}; +static const WCHAR errorW[] = {'e','r','r','o','r',0}; +static const WCHAR exitW[] = {'e','x','i','t',0}; +static const WCHAR explicitW[] = {'e','x','p','l','i','c','i','t',0}; +static const WCHAR falseW[] = {'f','a','l','s','e',0}; +static const WCHAR forW[] = {'f','o','r',0}; +static const WCHAR functionW[] = {'f','u','n','c','t','i','o','n',0}; +static const WCHAR getW[] = {'g','e','t',0}; +static const WCHAR gotoW[] = {'g','o','t','o',0}; +static const WCHAR ifW[] = {'i','f',0}; +static const WCHAR impW[] = {'i','m','p',0}; +static const WCHAR inW[] = {'i','n',0}; +static const WCHAR isW[] = {'i','s',0}; +static const WCHAR letW[] = {'l','e','t',0}; +static const WCHAR loopW[] = {'l','o','o','p',0}; +static const WCHAR meW[] = {'m','e',0}; +static const WCHAR modW[] = {'m','o','d',0}; +static const WCHAR newW[] = {'n','e','w',0}; +static const WCHAR nextW[] = {'n','e','x','t',0}; +static const WCHAR notW[] = {'n','o','t',0}; +static const WCHAR nothingW[] = {'n','o','t','h','i','n','g',0}; +static const WCHAR nullW[] = {'n','u','l','l',0}; +static const WCHAR onW[] = {'o','n',0}; +static const WCHAR optionW[] = {'o','p','t','i','o','n',0}; +static const WCHAR orW[] = {'o','r',0}; +static const WCHAR privateW[] = {'p','r','i','v','a','t','e',0}; +static const WCHAR propertyW[] = {'p','r','o','p','e','r','t','y',0}; +static const WCHAR publicW[] = {'p','u','b','l','i','c',0}; +static const WCHAR remW[] = {'r','e','m',0}; +static const WCHAR resumeW[] = {'r','e','s','u','m','e',0}; +static const WCHAR selectW[] = {'s','e','l','e','c','t',0}; +static const WCHAR setW[] = {'s','e','t',0}; +static const WCHAR stepW[] = {'s','t','e','p',0}; +static const WCHAR stopW[] = {'s','t','o','p',0}; +static const WCHAR subW[] = {'s','u','b',0}; +static const WCHAR thenW[] = {'t','h','e','n',0}; +static const WCHAR toW[] = {'t','o',0}; +static const WCHAR trueW[] = {'t','r','u','e',0}; +static const WCHAR untilW[] = {'u','n','t','i','l',0}; +static const WCHAR wendW[] = {'w','e','n','d',0}; +static const WCHAR whileW[] = {'w','h','i','l','e',0}; +static const WCHAR xorW[] = {'x','o','r',0}; + +static const struct { + const WCHAR *word; + int token; +} keywords[] = { + {andW, tAND}, + {byrefW, tBYREF}, + {byvalW, tBYVAL}, + {callW, tCALL}, + {caseW, tCASE}, + {classW, tCLASS}, + {constW, tCONST}, + {defaultW, tDEFAULT}, + {dimW, tDIM}, + {doW, tDO}, + {eachW, tEACH}, + {elseW, tELSE}, + {elseifW, tELSEIF}, + {emptyW, tEMPTY}, + {endW, tEND}, + {eqvW, tEQV}, + {errorW, tERROR}, + {exitW, tEXIT}, + {explicitW, tEXPLICIT}, + {falseW, tFALSE}, + {forW, tFOR}, + {functionW, tFUNCTION}, + {getW, tGET}, + {gotoW, tGOTO}, + {ifW, tIF}, + {impW, tIMP}, + {inW, tIN}, + {isW, tIS}, + {letW, tLET}, + {loopW, tLOOP}, + {meW, tME}, + {modW, tMOD}, + {newW, tNEW}, + {nextW, tNEXT}, + {notW, tNOT}, + {nothingW, tNOTHING}, + {nullW, tNULL}, + {onW, tON}, + {optionW, tOPTION}, + {orW, tOR}, + {privateW, tPRIVATE}, + {propertyW, tPROPERTY}, + {publicW, tPUBLIC}, + {remW, tREM}, + {resumeW, tRESUME}, + {selectW, tSELECT}, + {setW, tSET}, + {stepW, tSTEP}, + {stopW, tSTOP}, + {subW, tSUB}, + {thenW, tTHEN}, + {toW, tTO}, + {trueW, tTRUE}, + {untilW, tUNTIL}, + {wendW, tWEND}, + {whileW, tWHILE}, + {xorW, tXOR} +}; + +static inline BOOL is_identifier_char(WCHAR c) +{ + return isalnumW(c) || c == '_'; +} + +static int check_keyword(parser_ctx_t *ctx, const WCHAR *word) +{ + const WCHAR *p1 = ctx->ptr; + const WCHAR *p2 = word; + WCHAR c; + + while(p1 < ctx->end && *p2) { + c = tolowerW(*p1); + if(c != *p2) + return c - *p2; + p1++; + p2++; + } + + if(*p2 || (p1 < ctx->end && is_identifier_char(*p1))) + return 1; + + ctx->ptr = p1; + return 0; +} + +static int check_keywords(parser_ctx_t *ctx) +{ + int min = 0, max = sizeof(keywords)/sizeof(keywords[0])-1, r, i; + + while(min <= max) { + i = (min+max)/2; + + r = check_keyword(ctx, keywords[i].word); + if(!r) + return keywords[i].token; + + if(r > 0) + min = i+1; + else + max = i-1; + } + + return 0; +} + +static int parse_identifier(parser_ctx_t *ctx, const WCHAR **ret) +{ + const WCHAR *ptr = ctx->ptr++; + WCHAR *str; + int len; + + while(ctx->ptr < ctx->end && is_identifier_char(*ctx->ptr)) + ctx->ptr++; + len = ctx->ptr-ptr; + + str = parser_alloc(ctx, (len+1)*sizeof(WCHAR)); + if(!str) + return 0; + + memcpy(str, ptr, (len+1)*sizeof(WCHAR)); + str[len] = 0; + *ret = str; + return tIdentifier; +} + +static int parse_string_literal(parser_ctx_t *ctx, const WCHAR **ret) +{ + const WCHAR *ptr = ++ctx->ptr; + WCHAR *rptr; + int len = 0; + + while(ctx->ptr < ctx->end) { + if(*ctx->ptr == '\n') { + FIXME("newline inside string literal\n"); + return 0; + } + + if(*ctx->ptr == '"') { + if(ctx->ptr[1] != '"') + break; + len--; + ctx->ptr++; + } + ctx->ptr++; + } + + if(ctx->ptr == ctx->end) { + FIXME("unterminated string literal\n"); + return 0; + } + + len += ctx->ptr-ptr; + + *ret = rptr = parser_alloc(ctx, (len+1)*sizeof(WCHAR)); + if(!rptr) + return 0; + + while(ptr < ctx->ptr) { + if(*ptr == '"') + ptr++; + *rptr++ = *ptr++; + } + + *rptr = 0; + ctx->ptr++; + return tString; +} + +static int parse_numeric_literal(parser_ctx_t *ctx, void **ret) +{ + double n = 0; + + if(*ctx->ptr == '0' && !('0' <= ctx->ptr[1] && ctx->ptr[1] <= '9') && ctx->ptr[1] != '.') + return *ctx->ptr++; + + do { + n = n*10 + *ctx->ptr++ - '0'; + }while('0' <= *ctx->ptr && *ctx->ptr <= '9'); + + if(*ctx->ptr != '.') { + if((LONG)n == n) { + LONG l = n; + *(LONG*)ret = l; + return (short)l == l ? tShort : tLong; + } + }else { + double e = 1.0; + while('0' <= *++ctx->ptr && *ctx->ptr <= '9') + n += (e /= 10.0)*(*ctx->ptr-'0'); + } + + *(double*)ret = n; + return tDouble; +} + +static int hex_to_int(WCHAR c) +{ + if('0' <= c && c <= '9') + return c-'0'; + if('a' <= c && c <= 'f') + return c+10-'a'; + if('A' <= c && c <= 'F') + return c+10-'A'; + return -1; +} + +static int parse_hex_literal(parser_ctx_t *ctx, LONG *ret) +{ + const WCHAR *begin = ctx->ptr; + LONG l = 0, d; + + while((d = hex_to_int(*++ctx->ptr)) != -1) + l = l*16 + d; + + if(begin + 9 /* max digits+1 */ < ctx->ptr || (*ctx->ptr != '&' && is_identifier_char(*ctx->ptr))) { + FIXME("invalid literal\n"); + return 0; + } + + if(*ctx->ptr == '&') + ctx->ptr++; + + *ret = l; + return (short)l == l ? tShort : tLong; +} + +static void skip_spaces(parser_ctx_t *ctx) +{ + while(*ctx->ptr == ' ' || *ctx->ptr == '\t' || *ctx->ptr == '\r') + ctx->ptr++; +} + +static int comment_line(parser_ctx_t *ctx) +{ + ctx->ptr = strchrW(ctx->ptr, '\n'); + if(ctx->ptr) + ctx->ptr++; + else + ctx->ptr = ctx->end; + return tNL; +} + +static int parse_next_token(void *lval, parser_ctx_t *ctx) +{ + WCHAR c; + + skip_spaces(ctx); + if(ctx->ptr == ctx->end) + return ctx->last_token == tNL ? tEOF : tNL; + + c = *ctx->ptr; + + if('0' <= c && c <= '9') + return parse_numeric_literal(ctx, lval); + + if(isalphaW(c)) { + int ret = check_keywords(ctx); + if(!ret) + return parse_identifier(ctx, lval); + if(ret != tREM) + return ret; + c = '\''; + } + + switch(c) { + case '\n': + ctx->ptr++; + return tNL; + case '\'': + return comment_line(ctx); + case ':': + case ')': + case ',': + case '=': + case '+': + case '*': + case '/': + case '^': + case '\\': + case '.': + case '_': + return *ctx->ptr++; + case '-': + if(ctx->is_html && ctx->ptr[1] == '-' && ctx->ptr[2] == '>') + return comment_line(ctx); + ctx->ptr++; + return '-'; + case '(': + /* NOTE: + * We resolve empty brackets in lexer instead of parser to avoid complex conflicts + * in call statement special case |f()| without 'call' keyword + */ + ctx->ptr++; + skip_spaces(ctx); + if(*ctx->ptr == ')') { + ctx->ptr++; + return tEMPTYBRACKETS; + } + return '('; + case '"': + return parse_string_literal(ctx, lval); + case '&': + if(*++ctx->ptr == 'h' || *ctx->ptr == 'H') + return parse_hex_literal(ctx, lval); + return '&'; + case '<': + switch(*++ctx->ptr) { + case '>': + ctx->ptr++; + return tNEQ; + case '=': + ctx->ptr++; + return tLTEQ; + case '!': + if(ctx->is_html && ctx->ptr[1] == '-' && ctx->ptr[2] == '-') + return comment_line(ctx); + } + return '<'; + case '>': + if(*++ctx->ptr == '=') { + ctx->ptr++; + return tGTEQ; + } + return '>'; + default: + FIXME("Unhandled char %c in %s\n", *ctx->ptr, debugstr_w(ctx->ptr)); + } + + return 0; +} + +int parser_lex(void *lval, parser_ctx_t *ctx) +{ + int ret; + + while(1) { + ret = parse_next_token(lval, ctx); + if(ret == '_') { + skip_spaces(ctx); + if(*ctx->ptr != '\n') { + FIXME("'_' not followed by newline\n"); + return 0; + } + ctx->ptr++; + continue; + } + if(ret != tNL || ctx->last_token != tNL) + break; + + ctx->last_nl = ctx->ptr-ctx->code; + } + + return (ctx->last_token = ret); +} diff --git a/reactos/dll/win32/vbscript/parse.h b/reactos/dll/win32/vbscript/parse.h new file mode 100644 index 00000000000..ba1a3115cca --- /dev/null +++ b/reactos/dll/win32/vbscript/parse.h @@ -0,0 +1,272 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +typedef enum { + EXPR_ADD, + EXPR_AND, + EXPR_BOOL, + EXPR_BRACKETS, + EXPR_CONCAT, + EXPR_DIV, + EXPR_DOUBLE, + EXPR_EMPTY, + EXPR_EQUAL, + EXPR_EQV, + EXPR_EXP, + EXPR_GT, + EXPR_GTEQ, + EXPR_IDIV, + EXPR_IMP, + EXPR_IS, + EXPR_LT, + EXPR_LTEQ, + EXPR_ME, + EXPR_MEMBER, + EXPR_MOD, + EXPR_MUL, + EXPR_NEG, + EXPR_NEQUAL, + EXPR_NEW, + EXPR_NOT, + EXPR_NOTHING, + EXPR_NULL, + EXPR_OR, + EXPR_STRING, + EXPR_SUB, + EXPR_ULONG, + EXPR_USHORT, + EXPR_XOR +} expression_type_t; + +typedef struct _expression_t { + expression_type_t type; + struct _expression_t *next; +} expression_t; + +typedef struct { + expression_t expr; + VARIANT_BOOL value; +} bool_expression_t; + +typedef struct { + expression_t expr; + LONG value; +} int_expression_t; + +typedef struct { + expression_t expr; + double value; +} double_expression_t; + +typedef struct { + expression_t expr; + const WCHAR *value; +} string_expression_t; + +typedef struct { + expression_t expr; + expression_t *subexpr; +} unary_expression_t; + +typedef struct { + expression_t expr; + expression_t *left; + expression_t *right; +} binary_expression_t; + +typedef struct { + expression_t expr; + expression_t *obj_expr; + const WCHAR *identifier; + expression_t *args; +} member_expression_t; + +typedef enum { + STAT_ASSIGN, + STAT_CALL, + STAT_CONST, + STAT_DIM, + STAT_DOUNTIL, + STAT_DOWHILE, + STAT_EXITDO, + STAT_EXITFOR, + STAT_EXITFUNC, + STAT_EXITPROP, + STAT_EXITSUB, + STAT_FOREACH, + STAT_FORTO, + STAT_FUNC, + STAT_IF, + STAT_ONERROR, + STAT_SELECT, + STAT_SET, + STAT_STOP, + STAT_UNTIL, + STAT_WHILE, + STAT_WHILELOOP +} statement_type_t; + +typedef struct _statement_t { + statement_type_t type; + struct _statement_t *next; +} statement_t; + +typedef struct { + statement_t stat; + member_expression_t *expr; + BOOL is_strict; +} call_statement_t; + +typedef struct { + statement_t stat; + member_expression_t *member_expr; + expression_t *value_expr; +} assign_statement_t; + +typedef struct _dim_decl_t { + const WCHAR *name; + struct _dim_decl_t *next; +} dim_decl_t; + +typedef struct _dim_statement_t { + statement_t stat; + dim_decl_t *dim_decls; +} dim_statement_t; + +typedef struct _arg_decl_t { + const WCHAR *name; + BOOL by_ref; + struct _arg_decl_t *next; +} arg_decl_t; + +typedef struct _function_decl_t { + const WCHAR *name; + function_type_t type; + BOOL is_public; + arg_decl_t *args; + statement_t *body; + struct _function_decl_t *next; + struct _function_decl_t *next_prop_func; +} function_decl_t; + +typedef struct { + statement_t stat; + function_decl_t *func_decl; +} function_statement_t; + +typedef struct _class_prop_decl_t { + BOOL is_public; + const WCHAR *name; + struct _class_prop_decl_t *next; +} class_prop_decl_t; + +typedef struct _class_decl_t { + const WCHAR *name; + function_decl_t *funcs; + class_prop_decl_t *props; + struct _class_decl_t *next; +} class_decl_t; + +typedef struct _elseif_decl_t { + expression_t *expr; + statement_t *stat; + struct _elseif_decl_t *next; +} elseif_decl_t; + +typedef struct { + statement_t stat; + expression_t *expr; + statement_t *if_stat; + elseif_decl_t *elseifs; + statement_t *else_stat; +} if_statement_t; + +typedef struct { + statement_t stat; + expression_t *expr; + statement_t *body; +} while_statement_t; + +typedef struct { + statement_t stat; + const WCHAR *identifier; + expression_t *from_expr; + expression_t *to_expr; + expression_t *step_expr; + statement_t *body; +} forto_statement_t; + +typedef struct { + statement_t stat; + const WCHAR *identifier; + expression_t *group_expr; + statement_t *body; +} foreach_statement_t; + +typedef struct { + statement_t stat; + BOOL resume_next; +} onerror_statement_t; + +typedef struct _const_decl_t { + const WCHAR *name; + expression_t *value_expr; + struct _const_decl_t *next; +} const_decl_t; + +typedef struct { + statement_t stat; + const_decl_t *decls; +} const_statement_t; + +typedef struct _case_clausule_t { + expression_t *expr; + statement_t *stat; + struct _case_clausule_t *next; +} case_clausule_t; + +typedef struct { + statement_t stat; + expression_t *expr; + case_clausule_t *case_clausules; +} select_statement_t; + +typedef struct { + const WCHAR *code; + const WCHAR *ptr; + const WCHAR *end; + + BOOL option_explicit; + BOOL parse_complete; + BOOL is_html; + HRESULT hres; + + int last_token; + unsigned last_nl; + + statement_t *stats; + statement_t *stats_tail; + class_decl_t *class_decls; + + heap_pool_t heap; +} parser_ctx_t; + +HRESULT parse_script(parser_ctx_t*,const WCHAR*,const WCHAR*) DECLSPEC_HIDDEN; +void parser_release(parser_ctx_t*) DECLSPEC_HIDDEN; +int parser_lex(void*,parser_ctx_t*) DECLSPEC_HIDDEN; +void *parser_alloc(parser_ctx_t*,size_t) DECLSPEC_HIDDEN; diff --git a/reactos/dll/win32/vbscript/parser.tab.c b/reactos/dll/win32/vbscript/parser.tab.c new file mode 100644 index 00000000000..1c41c8ea655 --- /dev/null +++ b/reactos/dll/win32/vbscript/parser.tab.c @@ -0,0 +1,3658 @@ + +/* A Bison parser, made by GNU Bison 2.4.1. */ + +/* Skeleton implementation for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + +/* C LALR(1) parser skeleton written by Richard Stallman, by + simplifying the original so-called "semantic" parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Bison version. */ +#define YYBISON_VERSION "2.4.1" + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Push parsers. */ +#define YYPUSH 0 + +/* Pull parsers. */ +#define YYPULL 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* Substitute the variable and function names. */ +#define yyparse parser_parse +#define yylex parser_lex +#define yyerror parser_error +#define yylval parser_lval +#define yychar parser_char +#define yydebug parser_debug +#define yynerrs parser_nerrs + + +/* Copy the first part of user declarations. */ + +/* Line 189 of yacc.c */ +#line 19 "parser.y" + + +#include "vbscript.h" +#include "parse.h" + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(vbscript); + +#define YYLEX_PARAM ctx +#define YYPARSE_PARAM ctx + +static int parser_error(const char*); + +static void parse_complete(parser_ctx_t*,BOOL); + +static void source_add_statement(parser_ctx_t*,statement_t*); +static void source_add_class(parser_ctx_t*,class_decl_t*); + +static void *new_expression(parser_ctx_t*,expression_type_t,size_t); +static expression_t *new_bool_expression(parser_ctx_t*,VARIANT_BOOL); +static expression_t *new_string_expression(parser_ctx_t*,const WCHAR*); +static expression_t *new_long_expression(parser_ctx_t*,expression_type_t,LONG); +static expression_t *new_double_expression(parser_ctx_t*,double); +static expression_t *new_unary_expression(parser_ctx_t*,expression_type_t,expression_t*); +static expression_t *new_binary_expression(parser_ctx_t*,expression_type_t,expression_t*,expression_t*); +static expression_t *new_new_expression(parser_ctx_t*,const WCHAR*); + +static member_expression_t *new_member_expression(parser_ctx_t*,expression_t*,const WCHAR*); + +static void *new_statement(parser_ctx_t*,statement_type_t,size_t); +static statement_t *new_call_statement(parser_ctx_t*,BOOL,member_expression_t*); +static statement_t *new_assign_statement(parser_ctx_t*,member_expression_t*,expression_t*); +static statement_t *new_set_statement(parser_ctx_t*,member_expression_t*,expression_t*); +static statement_t *new_dim_statement(parser_ctx_t*,dim_decl_t*); +static statement_t *new_while_statement(parser_ctx_t*,statement_type_t,expression_t*,statement_t*); +static statement_t *new_forto_statement(parser_ctx_t*,const WCHAR*,expression_t*,expression_t*,expression_t*,statement_t*); +static statement_t *new_foreach_statement(parser_ctx_t*,const WCHAR*,expression_t*,statement_t*); +static statement_t *new_if_statement(parser_ctx_t*,expression_t*,statement_t*,elseif_decl_t*,statement_t*); +static statement_t *new_function_statement(parser_ctx_t*,function_decl_t*); +static statement_t *new_onerror_statement(parser_ctx_t*,BOOL); +static statement_t *new_const_statement(parser_ctx_t*,const_decl_t*); +static statement_t *new_select_statement(parser_ctx_t*,expression_t*,case_clausule_t*); + +static dim_decl_t *new_dim_decl(parser_ctx_t*,const WCHAR*,dim_decl_t*); +static elseif_decl_t *new_elseif_decl(parser_ctx_t*,expression_t*,statement_t*); +static function_decl_t *new_function_decl(parser_ctx_t*,const WCHAR*,function_type_t,unsigned,arg_decl_t*,statement_t*); +static arg_decl_t *new_argument_decl(parser_ctx_t*,const WCHAR*,BOOL); +static const_decl_t *new_const_decl(parser_ctx_t*,const WCHAR*,expression_t*); +static case_clausule_t *new_case_clausule(parser_ctx_t*,expression_t*,statement_t*,case_clausule_t*); + +static class_decl_t *new_class_decl(parser_ctx_t*); +static class_decl_t *add_class_function(parser_ctx_t*,class_decl_t*,function_decl_t*); +static class_decl_t *add_variant_prop(parser_ctx_t*,class_decl_t*,const WCHAR*,unsigned); + +static statement_t *link_statements(statement_t*,statement_t*); + +static const WCHAR propertyW[] = {'p','r','o','p','e','r','t','y',0}; + +#define STORAGE_IS_PRIVATE 1 +#define STORAGE_IS_DEFAULT 2 + +#define CHECK_ERROR if(((parser_ctx_t*)ctx)->hres != S_OK) YYABORT + + + +/* Line 189 of yacc.c */ +#line 148 "parser.tab.c" + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +/* Enabling the token table. */ +#ifndef YYTOKEN_TABLE +# define YYTOKEN_TABLE 0 +#endif + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + tEOF = 258, + tNL = 259, + tREM = 260, + tEMPTYBRACKETS = 261, + tTRUE = 262, + tFALSE = 263, + tNOT = 264, + tAND = 265, + tOR = 266, + tXOR = 267, + tEQV = 268, + tIMP = 269, + tNEQ = 270, + tIS = 271, + tLTEQ = 272, + tGTEQ = 273, + tMOD = 274, + tCALL = 275, + tDIM = 276, + tSUB = 277, + tFUNCTION = 278, + tPROPERTY = 279, + tGET = 280, + tLET = 281, + tCONST = 282, + tIF = 283, + tELSE = 284, + tELSEIF = 285, + tEND = 286, + tTHEN = 287, + tEXIT = 288, + tWHILE = 289, + tWEND = 290, + tDO = 291, + tLOOP = 292, + tUNTIL = 293, + tFOR = 294, + tTO = 295, + tSTEP = 296, + tEACH = 297, + tIN = 298, + tSELECT = 299, + tCASE = 300, + tBYREF = 301, + tBYVAL = 302, + tOPTION = 303, + tEXPLICIT = 304, + tSTOP = 305, + tNOTHING = 306, + tEMPTY = 307, + tNULL = 308, + tCLASS = 309, + tSET = 310, + tNEW = 311, + tPUBLIC = 312, + tPRIVATE = 313, + tDEFAULT = 314, + tME = 315, + tERROR = 316, + tNEXT = 317, + tON = 318, + tRESUME = 319, + tGOTO = 320, + tIdentifier = 321, + tString = 322, + tLong = 323, + tShort = 324, + tDouble = 325 + }; +#endif + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 214 of yacc.c */ +#line 88 "parser.y" + + const WCHAR *string; + statement_t *statement; + expression_t *expression; + member_expression_t *member; + elseif_decl_t *elseif; + dim_decl_t *dim_decl; + function_decl_t *func_decl; + arg_decl_t *arg_decl; + class_decl_t *class_decl; + const_decl_t *const_decl; + case_clausule_t *case_clausule; + unsigned uint; + LONG lng; + BOOL bool; + double dbl; + + + +/* Line 214 of yacc.c */ +#line 274 "parser.tab.c" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + +/* Copy the second part of user declarations. */ + + +/* Line 264 of yacc.c */ +#line 286 "parser.tab.c" + +#ifdef short +# undef short +#endif + +#ifdef YYTYPE_UINT8 +typedef YYTYPE_UINT8 yytype_uint8; +#else +typedef unsigned char yytype_uint8; +#endif + +#ifdef YYTYPE_INT8 +typedef YYTYPE_INT8 yytype_int8; +#elif (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +typedef signed char yytype_int8; +#else +typedef short int yytype_int8; +#endif + +#ifdef YYTYPE_UINT16 +typedef YYTYPE_UINT16 yytype_uint16; +#else +typedef unsigned short int yytype_uint16; +#endif + +#ifdef YYTYPE_INT16 +typedef YYTYPE_INT16 yytype_int16; +#else +typedef short int yytype_int16; +#endif + +#ifndef YYSIZE_T +# ifdef __SIZE_TYPE__ +# define YYSIZE_T __SIZE_TYPE__ +# elif defined size_t +# define YYSIZE_T size_t +# elif ! defined YYSIZE_T && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# else +# define YYSIZE_T unsigned int +# endif +#endif + +#define YYSIZE_MAXIMUM ((YYSIZE_T) -1) + +#ifndef YY_ +# if YYENABLE_NLS +# if ENABLE_NLS +# include /* INFRINGES ON USER NAME SPACE */ +# define YY_(msgid) dgettext ("bison-runtime", msgid) +# endif +# endif +# ifndef YY_ +# define YY_(msgid) msgid +# endif +#endif + +/* Suppress unused-variable warnings by "using" E. */ +#if ! defined lint || defined __GNUC__ +# define YYUSE(e) ((void) (e)) +#else +# define YYUSE(e) /* empty */ +#endif + +/* Identity function, used to suppress warnings about constant conditions. */ +#ifndef lint +# define YYID(n) (n) +#else +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static int +YYID (int yyi) +#else +static int +YYID (yyi) + int yyi; +#endif +{ + return yyi; +} +#endif + +#if ! defined yyoverflow || YYERROR_VERBOSE + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# elif defined __BUILTIN_VA_ARG_INCR +# include /* INFRINGES ON USER NAME SPACE */ +# elif defined _AIX +# define YYSTACK_ALLOC __alloca +# elif defined _MSC_VER +# include /* INFRINGES ON USER NAME SPACE */ +# define alloca _alloca +# else +# define YYSTACK_ALLOC alloca +# if ! defined _ALLOCA_H && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (YYID (0)) +# ifndef YYSTACK_ALLOC_MAXIMUM + /* The OS might guarantee only one guard page at the bottom of the stack, + and a page size can be as small as 4096 bytes. So we cannot safely + invoke alloca (N) if N exceeds 4096. Use a slightly smaller number + to allow for a few compiler-allocated temporary stack slots. */ +# define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */ +# endif +# else +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# ifndef YYSTACK_ALLOC_MAXIMUM +# define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM +# endif +# if (defined __cplusplus && ! defined _STDLIB_H \ + && ! ((defined YYMALLOC || defined malloc) \ + && (defined YYFREE || defined free))) +# include /* INFRINGES ON USER NAME SPACE */ +# ifndef _STDLIB_H +# define _STDLIB_H 1 +# endif +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# if ! defined malloc && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# ifndef YYFREE +# define YYFREE free +# if ! defined free && ! defined _STDLIB_H && (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +void free (void *); /* INFRINGES ON USER NAME SPACE */ +# endif +# endif +# endif +#endif /* ! defined yyoverflow || YYERROR_VERBOSE */ + + +#if (! defined yyoverflow \ + && (! defined __cplusplus \ + || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + yytype_int16 yyss_alloc; + YYSTYPE yyvs_alloc; +}; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined __GNUC__ && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (YYID (0)) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack_alloc, Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack_alloc, Stack, yysize); \ + Stack = &yyptr->Stack_alloc; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (YYID (0)) + +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 5 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 800 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 87 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 55 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 153 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 315 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 325 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const yytype_uint8 yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 81, 2, + 77, 78, 84, 82, 75, 76, 74, 85, 73, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 71, 2, + 80, 72, 79, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 83, 2, 86, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const yytype_uint16 yyprhs[] = +{ + 0, 0, 3, 7, 8, 12, 13, 16, 19, 20, + 22, 24, 27, 30, 32, 35, 37, 41, 44, 47, + 51, 56, 59, 61, 67, 74, 81, 86, 88, 91, + 94, 97, 100, 103, 109, 111, 116, 121, 124, 135, + 144, 152, 154, 158, 160, 164, 166, 170, 174, 176, + 179, 181, 183, 184, 187, 197, 202, 210, 211, 214, + 215, 217, 219, 222, 228, 229, 233, 234, 239, 245, + 247, 251, 253, 255, 256, 258, 260, 264, 266, 270, + 272, 276, 278, 282, 284, 288, 290, 294, 296, 299, + 301, 305, 309, 313, 317, 321, 325, 329, 331, 335, + 337, 341, 345, 347, 351, 353, 357, 359, 363, 367, + 369, 373, 375, 377, 380, 383, 385, 388, 390, 392, + 394, 396, 398, 400, 402, 404, 406, 408, 410, 414, + 416, 424, 425, 429, 434, 438, 448, 460, 472, 481, + 490, 491, 493, 496, 498, 500, 502, 506, 508, 512, + 514, 517, 520, 522 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yytype_int16 yyrhs[] = +{ + 88, 0, -1, 89, 90, 3, -1, -1, 48, 49, + 4, -1, -1, 90, 93, -1, 90, 132, -1, -1, + 92, -1, 93, -1, 93, 92, -1, 94, 4, -1, + 71, -1, 71, 94, -1, 95, -1, 95, 71, 94, + -1, 95, 71, -1, 96, 111, -1, 20, 96, 110, + -1, 96, 110, 72, 114, -1, 21, 97, -1, 103, + -1, 34, 114, 4, 91, 35, -1, 36, 101, 114, + 4, 91, 37, -1, 36, 4, 91, 37, 101, 114, + -1, 36, 4, 91, 37, -1, 135, -1, 33, 36, + -1, 33, 39, -1, 33, 23, -1, 33, 24, -1, + 33, 22, -1, 55, 96, 110, 72, 114, -1, 50, + -1, 63, 61, 64, 62, -1, 63, 61, 65, 73, + -1, 27, 98, -1, 39, 141, 72, 114, 40, 114, + 102, 4, 91, 62, -1, 39, 42, 141, 43, 114, + 4, 91, 62, -1, 44, 45, 114, 4, 109, 31, + 44, -1, 141, -1, 128, 74, 141, -1, 141, -1, + 141, 75, 97, -1, 99, -1, 99, 75, 98, -1, + 141, 72, 100, -1, 129, -1, 76, 130, -1, 34, + -1, 38, -1, -1, 41, 114, -1, 28, 114, 32, + 4, 91, 105, 108, 31, 28, -1, 28, 114, 32, + 94, -1, 28, 114, 32, 94, 29, 94, 104, -1, + -1, 31, 28, -1, -1, 106, -1, 107, -1, 107, + 106, -1, 30, 114, 32, 4, 91, -1, -1, 29, + 4, 91, -1, -1, 45, 29, 4, 92, -1, 45, + 113, 4, 91, 109, -1, 112, -1, 77, 113, 78, + -1, 112, -1, 113, -1, -1, 6, -1, 114, -1, + 114, 75, 113, -1, 115, -1, 114, 14, 115, -1, + 116, -1, 115, 13, 116, -1, 117, -1, 116, 12, + 117, -1, 118, -1, 117, 11, 118, -1, 119, -1, + 118, 10, 119, -1, 120, -1, 9, 119, -1, 121, + -1, 120, 72, 121, -1, 120, 15, 121, -1, 120, + 79, 121, -1, 120, 80, 121, -1, 120, 18, 121, + -1, 120, 17, 121, -1, 120, 16, 121, -1, 122, + -1, 121, 81, 122, -1, 123, -1, 122, 82, 123, + -1, 122, 76, 123, -1, 124, -1, 123, 19, 124, + -1, 125, -1, 124, 83, 125, -1, 126, -1, 125, + 84, 126, -1, 125, 85, 126, -1, 127, -1, 126, + 86, 127, -1, 129, -1, 128, -1, 56, 141, -1, + 76, 127, -1, 131, -1, 96, 110, -1, 7, -1, + 8, -1, 67, -1, 130, -1, 52, -1, 53, -1, + 51, -1, 69, -1, 73, -1, 68, -1, 70, -1, + 77, 114, 78, -1, 60, -1, 54, 141, 4, 133, + 31, 54, 4, -1, -1, 135, 4, 133, -1, 137, + 66, 4, 133, -1, 134, 4, 133, -1, 136, 24, + 25, 66, 112, 4, 91, 31, 24, -1, 136, 24, + 26, 66, 77, 140, 78, 4, 91, 31, 24, -1, + 136, 24, 55, 66, 77, 140, 78, 4, 91, 31, + 24, -1, 136, 22, 141, 138, 4, 91, 31, 22, + -1, 136, 23, 141, 138, 4, 91, 31, 23, -1, + -1, 137, -1, 57, 59, -1, 57, -1, 58, -1, + 112, -1, 77, 139, 78, -1, 140, -1, 140, 75, + 139, -1, 141, -1, 46, 141, -1, 47, 141, -1, + 66, -1, 24, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const yytype_uint16 yyrline[] = +{ + 0, 145, 145, 148, 149, 151, 153, 154, 157, 158, + 161, 162, 165, 168, 169, 170, 171, 172, 175, 176, + 177, 179, 180, 181, 183, 186, 189, 190, 191, 192, + 193, 194, 195, 196, 198, 199, 200, 201, 202, 204, + 206, 210, 211, 214, 215, 218, 219, 222, 225, 226, + 229, 230, 233, 234, 237, 239, 240, 243, 245, 248, + 249, 252, 253, 256, 260, 261, 264, 265, 266, 270, + 271, 274, 275, 277, 279, 282, 283, 286, 287, 290, + 291, 294, 295, 298, 299, 302, 303, 306, 307, 310, + 311, 312, 313, 314, 315, 316, 317, 320, 321, 324, + 325, 326, 329, 330, 333, 334, 338, 339, 341, 345, + 346, 349, 350, 351, 352, 355, 356, 359, 360, 361, + 362, 363, 364, 365, 368, 369, 370, 371, 375, 376, + 379, 382, 383, 384, 385, 388, 390, 392, 396, 398, + 402, 403, 406, 407, 408, 411, 412, 415, 416, 419, + 420, 421, 425, 426 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE || YYTOKEN_TABLE +/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "tEOF", "tNL", "tREM", "tEMPTYBRACKETS", + "tTRUE", "tFALSE", "tNOT", "tAND", "tOR", "tXOR", "tEQV", "tIMP", "tNEQ", + "tIS", "tLTEQ", "tGTEQ", "tMOD", "tCALL", "tDIM", "tSUB", "tFUNCTION", + "tPROPERTY", "tGET", "tLET", "tCONST", "tIF", "tELSE", "tELSEIF", "tEND", + "tTHEN", "tEXIT", "tWHILE", "tWEND", "tDO", "tLOOP", "tUNTIL", "tFOR", + "tTO", "tSTEP", "tEACH", "tIN", "tSELECT", "tCASE", "tBYREF", "tBYVAL", + "tOPTION", "tEXPLICIT", "tSTOP", "tNOTHING", "tEMPTY", "tNULL", "tCLASS", + "tSET", "tNEW", "tPUBLIC", "tPRIVATE", "tDEFAULT", "tME", "tERROR", + "tNEXT", "tON", "tRESUME", "tGOTO", "tIdentifier", "tString", "tLong", + "tShort", "tDouble", "':'", "'='", "'0'", "'.'", "','", "'-'", "'('", + "')'", "'>'", "'<'", "'&'", "'+'", "'\\\\'", "'*'", "'/'", "'^'", + "$accept", "Program", "OptionExplicit_opt", "SourceElements", + "StatementsNl_opt", "StatementsNl", "StatementNl", "Statement", + "SimpleStatement", "MemberExpression", "DimDeclList", "ConstDeclList", + "ConstDecl", "ConstExpression", "DoType", "Step_opt", "IfStatement", + "EndIf_opt", "ElseIfs_opt", "ElseIfs", "ElseIf", "Else_opt", + "CaseClausules", "Arguments_opt", "ArgumentList_opt", + "EmptyBrackets_opt", "ExpressionList", "Expression", "EqvExpression", + "XorExpression", "OrExpression", "AndExpression", "NotExpression", + "EqualityExpression", "ConcatExpression", "AdditiveExpression", + "ModExpression", "IntdivExpression", "MultiplicativeExpression", + "ExpExpression", "UnaryExpression", "CallExpression", + "LiteralExpression", "NumericLiteralExpression", "PrimaryExpression", + "ClassDeclaration", "ClassBody", "PropertyDecl", "FunctionDecl", + "Storage_opt", "Storage", "ArgumentsDecl_opt", "ArgumentDeclList", + "ArgumentDecl", "Identifier", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const yytype_uint16 yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 58, 61, 48, 46, 44, 45, 40, 41, 62, + 60, 38, 43, 92, 42, 47, 94 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const yytype_uint8 yyr1[] = +{ + 0, 87, 88, 89, 89, 90, 90, 90, 91, 91, + 92, 92, 93, 94, 94, 94, 94, 94, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 95, 95, 95, 95, 95, 95, 95, 95, 95, + 95, 96, 96, 97, 97, 98, 98, 99, 100, 100, + 101, 101, 102, 102, 103, 103, 103, 104, 104, 105, + 105, 106, 106, 107, 108, 108, 109, 109, 109, 110, + 110, 111, 111, 112, 112, 113, 113, 114, 114, 115, + 115, 116, 116, 117, 117, 118, 118, 119, 119, 120, + 120, 120, 120, 120, 120, 120, 120, 121, 121, 122, + 122, 122, 123, 123, 124, 124, 125, 125, 125, 126, + 126, 127, 127, 127, 127, 128, 128, 129, 129, 129, + 129, 129, 129, 129, 130, 130, 130, 130, 131, 131, + 132, 133, 133, 133, 133, 134, 134, 134, 135, 135, + 136, 136, 137, 137, 137, 138, 138, 139, 139, 140, + 140, 140, 141, 141 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const yytype_uint8 yyr2[] = +{ + 0, 2, 3, 0, 3, 0, 2, 2, 0, 1, + 1, 2, 2, 1, 2, 1, 3, 2, 2, 3, + 4, 2, 1, 5, 6, 6, 4, 1, 2, 2, + 2, 2, 2, 5, 1, 4, 4, 2, 10, 8, + 7, 1, 3, 1, 3, 1, 3, 3, 1, 2, + 1, 1, 0, 2, 9, 4, 7, 0, 2, 0, + 1, 1, 2, 5, 0, 3, 0, 4, 5, 1, + 3, 1, 1, 0, 1, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 3, 1, 2, 1, + 3, 3, 3, 3, 3, 3, 3, 1, 3, 1, + 3, 3, 1, 3, 1, 3, 1, 3, 3, 1, + 3, 1, 1, 2, 2, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, + 7, 0, 3, 4, 3, 9, 11, 11, 8, 8, + 0, 1, 2, 1, 1, 1, 3, 1, 3, 1, + 2, 2, 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const yytype_uint8 yydefact[] = +{ + 3, 0, 0, 5, 0, 1, 140, 4, 2, 0, + 0, 153, 0, 0, 0, 0, 0, 0, 0, 34, + 0, 0, 143, 144, 129, 0, 152, 13, 0, 6, + 0, 15, 73, 22, 0, 115, 7, 27, 0, 141, + 41, 73, 21, 43, 37, 45, 0, 117, 118, 0, + 123, 121, 122, 0, 119, 126, 124, 127, 125, 0, + 73, 0, 77, 79, 81, 83, 85, 87, 89, 97, + 99, 102, 104, 106, 109, 112, 111, 120, 32, 30, + 31, 28, 29, 0, 140, 50, 51, 0, 0, 0, + 0, 0, 73, 142, 0, 14, 0, 12, 17, 74, + 0, 116, 18, 71, 72, 75, 0, 0, 0, 0, + 19, 69, 0, 0, 0, 88, 113, 114, 116, 0, + 140, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 140, 0, 9, 10, 0, 0, 0, 0, 140, 116, + 0, 0, 128, 16, 0, 0, 0, 0, 42, 73, + 73, 44, 46, 0, 47, 48, 78, 8, 55, 80, + 82, 84, 86, 91, 96, 95, 94, 90, 92, 93, + 98, 101, 100, 103, 105, 107, 108, 110, 0, 26, + 11, 140, 0, 0, 66, 0, 0, 0, 0, 141, + 0, 35, 36, 70, 20, 76, 0, 145, 0, 0, + 49, 59, 140, 23, 0, 0, 0, 0, 0, 0, + 0, 140, 140, 0, 0, 33, 0, 0, 0, 147, + 149, 140, 140, 0, 64, 60, 61, 57, 25, 24, + 140, 52, 0, 0, 0, 0, 134, 132, 0, 0, + 0, 140, 150, 151, 146, 0, 0, 0, 0, 0, + 0, 62, 0, 56, 0, 0, 0, 140, 8, 40, + 130, 73, 0, 0, 133, 148, 0, 0, 0, 140, + 0, 58, 39, 53, 140, 67, 66, 0, 0, 0, + 138, 139, 8, 65, 54, 0, 68, 140, 0, 0, + 63, 38, 0, 0, 0, 0, 140, 140, 135, 0, + 0, 0, 0, 136, 137 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yytype_int16 yydefgoto[] = +{ + -1, 2, 3, 6, 141, 142, 143, 30, 31, 60, + 42, 44, 45, 164, 87, 266, 33, 263, 234, 235, + 236, 260, 219, 101, 102, 111, 154, 105, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 35, 36, 195, 196, 37, 38, + 39, 208, 228, 229, 40 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -184 +static const yytype_int16 yypact[] = +{ + -21, -15, 46, -184, 51, -184, 287, -184, -184, 82, + 17, -184, 17, 395, 147, 395, 20, 7, 14, -184, + 17, 82, 11, -184, -184, 34, -184, 483, 395, -184, + 70, 29, 144, -184, 56, -184, -184, -184, 44, -184, + -184, 0, -184, 43, -184, 80, 31, -184, -184, 395, + -184, -184, -184, 17, -184, -184, -184, -184, -184, 425, + 0, 21, 66, 168, 173, 178, -184, 47, 122, 84, + 186, 124, 62, 123, -184, 56, -184, -184, -184, -184, + -184, -184, -184, 36, 531, -184, -184, 395, 17, 146, + 395, 215, 0, -184, 126, -184, 6, -184, 483, -184, + 395, 151, -184, 61, -184, -3, 17, 17, 17, 395, + 150, -184, 17, 17, 105, -184, -184, -184, -184, 395, + 335, 395, 395, 395, 395, 425, 425, 425, 425, 425, + 425, 425, 425, 425, 425, 425, 425, 425, 425, 425, + 579, 192, -184, 483, 94, 190, 395, 95, -1, 165, + 177, 167, -184, -184, 163, 18, 395, 395, -184, 13, + 13, -184, -184, 162, -184, -184, 66, 483, 216, 168, + 173, 178, -184, 122, 122, 122, 122, 122, 122, 122, + 84, 186, 186, 124, 62, 123, 123, -184, 209, 9, + -184, 531, 395, 8, 201, 218, 243, 246, 121, 185, + 395, -184, -184, -184, 251, -184, 5, -184, 256, 262, + -184, 237, 723, -184, 395, 231, 117, 395, 358, 238, + 220, -1, -1, 139, 266, 251, 17, 17, 194, 202, + -184, 627, 627, 395, 247, -184, 237, 248, 251, -184, + 675, 4, 277, 278, 239, 281, -184, -184, 221, 223, + 228, -1, -184, -184, -184, 5, 264, 265, 28, 293, + 267, -184, 271, -184, 250, 395, 306, 723, 483, -184, + -184, 307, 240, 241, -184, -184, 294, 296, 318, 627, + 299, -184, -184, 251, 675, -184, 201, 320, 5, 5, + -184, -184, 483, -184, -184, 268, -184, 627, 254, 255, + -184, -184, 297, 325, 330, 311, 627, 627, -184, 305, + 309, 314, 319, -184, -184 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yytype_int16 yypgoto[] = +{ + -184, -184, -184, -184, -130, -142, 340, -23, -184, -6, + 236, 257, -184, -184, 160, -184, -184, -184, -184, 115, + -184, -184, 68, -16, -184, -19, -25, 92, 233, 252, + 253, 249, -33, -184, 128, 229, 65, 225, 242, 64, + -50, -4, 263, 213, -184, -184, -183, -184, -140, -136, + -134, 224, 125, -73, 16 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -141 +static const yytype_int16 yytable[] = +{ + 32, 190, 34, 41, 95, 34, 99, 104, 197, 117, + 188, 119, 198, 103, 199, 92, 115, 34, 119, 99, + 119, 32, 119, 34, 84, 110, 43, 1, 46, 11, + -131, 11, 119, 89, 4, 119, 91, 211, 246, 247, + 140, 11, 119, 85, 118, 265, 5, 86, 217, 88, + 119, 226, 227, 120, 85, 7, 22, 23, 86, 90, + 278, 215, 125, 126, 127, 128, 107, 108, 274, 116, + 93, 26, 157, 26, 97, 153, 149, 109, 32, 121, + 34, 197, 197, 26, 152, 198, 198, 199, 199, 187, + 206, 172, 32, 157, 34, 94, 152, 168, 191, 194, + 98, 256, 257, 114, 145, 61, 11, 83, 119, 119, + 264, 197, 47, 48, 32, 198, 34, 199, 112, 129, + 96, 240, 158, 159, 160, 285, 130, 131, 43, 46, + 106, 119, 205, -69, 32, -69, 34, 32, 286, 34, + 207, 207, 24, 107, 108, 223, 137, 138, 26, 293, + 99, 47, 48, 49, 295, 113, 50, 51, 52, 28, + 133, 32, 300, 34, 248, 249, 134, 302, 11, 78, + 79, 80, 54, 55, 56, 57, 309, 310, 58, 144, + 122, 163, 147, 81, 123, 32, 82, 34, 124, 237, + 150, 151, 155, 243, 250, 50, 51, 52, 181, 182, + 53, 185, 186, 132, 24, 135, 32, 136, 34, 139, + 26, 54, 55, 56, 57, 298, 299, 58, 146, 148, + 59, 100, 230, 156, -116, 32, 32, 34, 34, 189, + 55, 56, 57, 192, 32, 58, 34, 200, 193, 201, + 202, 203, 252, 253, 213, 212, 218, 221, 204, 220, + 222, 224, 287, 173, 174, 175, 176, 177, 178, 179, + 231, 32, 32, 34, 34, 119, 232, 233, 239, 244, + 251, 230, 254, 32, 245, 34, 259, 255, 32, 262, + 34, 267, 268, 269, 216, 270, 32, 271, 34, 272, + 8, 32, 225, 34, 273, 276, 277, 279, 280, 281, + 32, 32, 34, 34, 230, 230, 238, 9, 10, 241, + 284, 11, 282, 99, 12, 13, 290, 288, 289, 291, + 14, 15, 292, 16, 297, 258, 17, 294, 305, 306, + 301, 18, 303, 304, 307, 308, 311, 19, 313, 167, + 312, 20, 21, 314, 22, 23, 29, 24, 161, 214, + 25, 261, 166, 26, 296, 9, 10, 283, 27, 11, + 183, 180, 12, 13, 28, 47, 48, 49, 14, 15, + 162, 16, 171, 169, 17, 170, 210, 165, 184, 18, + 275, 0, 11, 0, 209, 19, 0, 242, 0, 0, + 21, 0, 22, 23, 0, 24, 0, 0, 25, 0, + 0, 26, 47, 48, 49, 0, 27, 0, 0, 50, + 51, 52, 28, 0, 53, 0, 0, 0, 24, 11, + 0, 0, 0, 0, 26, 54, 55, 56, 57, 0, + 0, 58, 47, 48, 59, 28, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 50, 51, 52, 11, + 0, 53, 0, 0, 0, 24, 0, 0, 0, 0, + 0, 26, 54, 55, 56, 57, 0, 0, 58, 0, + 0, 59, 28, 0, 0, 0, 50, 51, 52, 0, + 0, 53, 0, 0, 0, 24, 0, 0, 0, 0, + 0, 26, 54, 55, 56, 57, 0, 0, 58, 0, + 0, 59, 28, 9, 10, -140, -140, 11, 0, 0, + 12, 13, 0, 0, 0, 0, 14, 15, 0, 16, + 0, 0, 17, 0, 0, 0, 0, 18, 0, 0, + 0, 0, 0, 19, 0, 0, 0, 0, 21, 0, + 22, 23, 0, 24, 0, 0, 25, 0, 0, 26, + 0, 9, 10, 0, 27, 11, 0, 0, 12, 13, + 28, 0, 0, 0, 14, 15, 0, 16, -8, 0, + 17, 0, 0, 0, 0, 18, 0, 0, 0, 0, + 0, 19, 0, 0, 0, 0, 21, 0, 22, 23, + 0, 24, 0, 0, 25, 0, 0, 26, 0, 9, + 10, 0, 27, 11, 0, 0, 12, 13, 28, 0, + 0, 0, 14, 15, -8, 16, 0, 0, 17, 0, + 0, 0, 0, 18, 0, 0, 0, 0, 0, 19, + 0, 0, 0, 0, 21, 0, 22, 23, 0, 24, + 0, 0, 25, 0, 0, 26, 0, 9, 10, 0, + 27, 11, 0, 0, 12, 13, 28, 0, -8, 0, + 14, 15, 0, 16, 0, 0, 17, 0, 0, 0, + 0, 18, 0, 0, 0, 0, 0, 19, 0, 0, + 0, 0, 21, 0, 22, 23, 0, 24, 0, 0, + 25, 0, 0, 26, 0, 9, 10, 0, 27, 11, + 0, 0, 12, 13, 28, 0, 0, 0, 14, 15, + 0, 16, 0, 0, 17, 0, 0, 0, 0, 18, + 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, + 21, 0, 22, 23, 0, 24, 0, -8, 25, 0, + 0, 26, 0, 9, 10, 0, 27, 11, 0, 0, + 12, 13, 28, 0, 0, 0, 14, 15, 0, 16, + 0, 0, 17, 0, 0, 0, 0, 18, 0, 0, + 0, 0, 0, 19, 0, 0, 0, 0, 21, 0, + 22, 23, 0, 24, 0, 0, 25, 0, 0, 26, + 0, 0, 0, 0, 27, 0, 0, 0, 0, 0, + 28 +}; + +static const yytype_int16 yycheck[] = +{ + 6, 143, 6, 9, 27, 9, 6, 32, 148, 59, + 140, 14, 148, 32, 148, 21, 49, 21, 14, 6, + 14, 27, 14, 27, 4, 41, 10, 48, 12, 24, + 31, 24, 14, 17, 49, 14, 20, 167, 221, 222, + 4, 24, 14, 34, 60, 41, 0, 38, 40, 42, + 14, 46, 47, 32, 34, 4, 57, 58, 38, 45, + 32, 191, 15, 16, 17, 18, 22, 23, 251, 53, + 59, 66, 75, 66, 4, 98, 92, 77, 84, 13, + 84, 221, 222, 66, 78, 221, 222, 221, 222, 139, + 77, 124, 98, 75, 98, 61, 78, 120, 4, 4, + 71, 231, 232, 72, 88, 13, 24, 15, 14, 14, + 240, 251, 7, 8, 120, 251, 120, 251, 75, 72, + 28, 4, 106, 107, 108, 267, 79, 80, 112, 113, + 74, 14, 157, 72, 140, 74, 140, 143, 268, 143, + 159, 160, 60, 22, 23, 24, 84, 85, 66, 279, + 6, 7, 8, 9, 284, 75, 51, 52, 53, 77, + 76, 167, 292, 167, 25, 26, 82, 297, 24, 22, + 23, 24, 67, 68, 69, 70, 306, 307, 73, 87, + 12, 76, 90, 36, 11, 191, 39, 191, 10, 212, + 64, 65, 100, 218, 55, 51, 52, 53, 133, 134, + 56, 137, 138, 81, 60, 19, 212, 83, 212, 86, + 66, 67, 68, 69, 70, 288, 289, 73, 72, 4, + 76, 77, 206, 72, 74, 231, 232, 231, 232, 37, + 68, 69, 70, 43, 240, 73, 240, 72, 146, 62, + 73, 78, 226, 227, 35, 29, 45, 4, 156, 31, + 4, 66, 271, 125, 126, 127, 128, 129, 130, 131, + 4, 267, 268, 267, 268, 14, 4, 30, 37, 31, + 4, 255, 78, 279, 54, 279, 29, 75, 284, 31, + 284, 4, 4, 44, 192, 4, 292, 66, 292, 66, + 3, 297, 200, 297, 66, 31, 31, 4, 31, 28, + 306, 307, 306, 307, 288, 289, 214, 20, 21, 217, + 4, 24, 62, 6, 27, 28, 22, 77, 77, 23, + 33, 34, 4, 36, 4, 233, 39, 28, 31, 4, + 62, 44, 78, 78, 4, 24, 31, 50, 24, 4, + 31, 54, 55, 24, 57, 58, 6, 60, 112, 189, + 63, 236, 119, 66, 286, 20, 21, 265, 71, 24, + 135, 132, 27, 28, 77, 7, 8, 9, 33, 34, + 113, 36, 123, 121, 39, 122, 163, 114, 136, 44, + 255, -1, 24, -1, 160, 50, -1, 29, -1, -1, + 55, -1, 57, 58, -1, 60, -1, -1, 63, -1, + -1, 66, 7, 8, 9, -1, 71, -1, -1, 51, + 52, 53, 77, -1, 56, -1, -1, -1, 60, 24, + -1, -1, -1, -1, 66, 67, 68, 69, 70, -1, + -1, 73, 7, 8, 76, 77, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 51, 52, 53, 24, + -1, 56, -1, -1, -1, 60, -1, -1, -1, -1, + -1, 66, 67, 68, 69, 70, -1, -1, 73, -1, + -1, 76, 77, -1, -1, -1, 51, 52, 53, -1, + -1, 56, -1, -1, -1, 60, -1, -1, -1, -1, + -1, 66, 67, 68, 69, 70, -1, -1, 73, -1, + -1, 76, 77, 20, 21, 22, 23, 24, -1, -1, + 27, 28, -1, -1, -1, -1, 33, 34, -1, 36, + -1, -1, 39, -1, -1, -1, -1, 44, -1, -1, + -1, -1, -1, 50, -1, -1, -1, -1, 55, -1, + 57, 58, -1, 60, -1, -1, 63, -1, -1, 66, + -1, 20, 21, -1, 71, 24, -1, -1, 27, 28, + 77, -1, -1, -1, 33, 34, -1, 36, 37, -1, + 39, -1, -1, -1, -1, 44, -1, -1, -1, -1, + -1, 50, -1, -1, -1, -1, 55, -1, 57, 58, + -1, 60, -1, -1, 63, -1, -1, 66, -1, 20, + 21, -1, 71, 24, -1, -1, 27, 28, 77, -1, + -1, -1, 33, 34, 35, 36, -1, -1, 39, -1, + -1, -1, -1, 44, -1, -1, -1, -1, -1, 50, + -1, -1, -1, -1, 55, -1, 57, 58, -1, 60, + -1, -1, 63, -1, -1, 66, -1, 20, 21, -1, + 71, 24, -1, -1, 27, 28, 77, -1, 31, -1, + 33, 34, -1, 36, -1, -1, 39, -1, -1, -1, + -1, 44, -1, -1, -1, -1, -1, 50, -1, -1, + -1, -1, 55, -1, 57, 58, -1, 60, -1, -1, + 63, -1, -1, 66, -1, 20, 21, -1, 71, 24, + -1, -1, 27, 28, 77, -1, -1, -1, 33, 34, + -1, 36, -1, -1, 39, -1, -1, -1, -1, 44, + -1, -1, -1, -1, -1, 50, -1, -1, -1, -1, + 55, -1, 57, 58, -1, 60, -1, 62, 63, -1, + -1, 66, -1, 20, 21, -1, 71, 24, -1, -1, + 27, 28, 77, -1, -1, -1, 33, 34, -1, 36, + -1, -1, 39, -1, -1, -1, -1, 44, -1, -1, + -1, -1, -1, 50, -1, -1, -1, -1, 55, -1, + 57, 58, -1, 60, -1, -1, 63, -1, -1, 66, + -1, -1, -1, -1, 71, -1, -1, -1, -1, -1, + 77 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const yytype_uint8 yystos[] = +{ + 0, 48, 88, 89, 49, 0, 90, 4, 3, 20, + 21, 24, 27, 28, 33, 34, 36, 39, 44, 50, + 54, 55, 57, 58, 60, 63, 66, 71, 77, 93, + 94, 95, 96, 103, 128, 131, 132, 135, 136, 137, + 141, 96, 97, 141, 98, 99, 141, 7, 8, 9, + 51, 52, 53, 56, 67, 68, 69, 70, 73, 76, + 96, 114, 115, 116, 117, 118, 119, 120, 121, 122, + 123, 124, 125, 126, 127, 128, 129, 130, 22, 23, + 24, 36, 39, 114, 4, 34, 38, 101, 42, 141, + 45, 141, 96, 59, 61, 94, 114, 4, 71, 6, + 77, 110, 111, 112, 113, 114, 74, 22, 23, 77, + 110, 112, 75, 75, 72, 119, 141, 127, 110, 14, + 32, 13, 12, 11, 10, 15, 16, 17, 18, 72, + 79, 80, 81, 76, 82, 19, 83, 84, 85, 86, + 4, 91, 92, 93, 114, 141, 72, 114, 4, 110, + 64, 65, 78, 94, 113, 114, 72, 75, 141, 141, + 141, 97, 98, 76, 100, 129, 115, 4, 94, 116, + 117, 118, 119, 121, 121, 121, 121, 121, 121, 121, + 122, 123, 123, 124, 125, 126, 126, 127, 91, 37, + 92, 4, 43, 114, 4, 133, 134, 135, 136, 137, + 72, 62, 73, 78, 114, 113, 77, 112, 138, 138, + 130, 91, 29, 35, 101, 91, 114, 40, 45, 109, + 31, 4, 4, 24, 66, 114, 46, 47, 139, 140, + 141, 4, 4, 30, 105, 106, 107, 94, 114, 37, + 4, 114, 29, 113, 31, 54, 133, 133, 25, 26, + 55, 4, 141, 141, 78, 75, 91, 91, 114, 29, + 108, 106, 31, 104, 91, 41, 102, 4, 4, 44, + 4, 66, 66, 66, 133, 139, 31, 31, 32, 4, + 31, 28, 62, 114, 4, 92, 91, 112, 77, 77, + 22, 23, 4, 91, 28, 91, 109, 4, 140, 140, + 91, 62, 91, 78, 78, 31, 4, 4, 24, 91, + 91, 31, 31, 24, 24 +}; + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK (1); \ + goto yybackup; \ + } \ + else \ + { \ + yyerror (YY_("syntax error: cannot back up")); \ + YYERROR; \ + } \ +while (YYID (0)) + + +#define YYTERROR 1 +#define YYERRCODE 256 + + +/* YYLLOC_DEFAULT -- Set CURRENT to span from RHS[1] to RHS[N]. + If N is 0, then set CURRENT to the empty location which ends + the previous symbol: RHS[0] (always defined). */ + +#define YYRHSLOC(Rhs, K) ((Rhs)[K]) +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + do \ + if (YYID (N)) \ + { \ + (Current).first_line = YYRHSLOC (Rhs, 1).first_line; \ + (Current).first_column = YYRHSLOC (Rhs, 1).first_column; \ + (Current).last_line = YYRHSLOC (Rhs, N).last_line; \ + (Current).last_column = YYRHSLOC (Rhs, N).last_column; \ + } \ + else \ + { \ + (Current).first_line = (Current).last_line = \ + YYRHSLOC (Rhs, 0).last_line; \ + (Current).first_column = (Current).last_column = \ + YYRHSLOC (Rhs, 0).last_column; \ + } \ + while (YYID (0)) +#endif + + +/* YY_LOCATION_PRINT -- Print the location on the stream. + This macro was not mandated originally: define only if we know + we won't break user code: when these are the locations we know. */ + +#ifndef YY_LOCATION_PRINT +# if YYLTYPE_IS_TRIVIAL +# define YY_LOCATION_PRINT(File, Loc) \ + fprintf (File, "%d.%d-%d.%d", \ + (Loc).first_line, (Loc).first_column, \ + (Loc).last_line, (Loc).last_column) +# else +# define YY_LOCATION_PRINT(File, Loc) ((void) 0) +# endif +#endif + + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (YYID (0)) + +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yy_symbol_print (stderr, \ + Type, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (YYID (0)) + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_value_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (!yyvaluep) + return; +# ifdef YYPRINT + if (yytype < YYNTOKENS) + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# else + YYUSE (yyoutput); +# endif + switch (yytype) + { + default: + break; + } +} + + +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep) +#else +static void +yy_symbol_print (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE const * const yyvaluep; +#endif +{ + if (yytype < YYNTOKENS) + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + yy_symbol_value_print (yyoutput, yytype, yyvaluep); + YYFPRINTF (yyoutput, ")"); +} + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop) +#else +static void +yy_stack_print (yybottom, yytop) + yytype_int16 *yybottom; + yytype_int16 *yytop; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (; yybottom <= yytop; yybottom++) + { + int yybot = *yybottom; + YYFPRINTF (stderr, " %d", yybot); + } + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (YYID (0)) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yy_reduce_print (YYSTYPE *yyvsp, int yyrule) +#else +static void +yy_reduce_print (yyvsp, yyrule) + YYSTYPE *yyvsp; + int yyrule; +#endif +{ + int yynrhs = yyr2[yyrule]; + int yyi; + unsigned long int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n", + yyrule - 1, yylno); + /* The symbols being reduced. */ + for (yyi = 0; yyi < yynrhs; yyi++) + { + YYFPRINTF (stderr, " $%d = ", yyi + 1); + yy_symbol_print (stderr, yyrhs[yyprhs[yyrule] + yyi], + &(yyvsp[(yyi + 1) - (yynrhs)]) + ); + YYFPRINTF (stderr, "\n"); + } +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (yyvsp, Rule); \ +} while (YYID (0)) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YY_SYMBOL_PRINT(Title, Type, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined __GLIBC__ && defined _STRING_H +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static YYSIZE_T +yystrlen (const char *yystr) +#else +static YYSIZE_T +yystrlen (yystr) + const char *yystr; +#endif +{ + YYSIZE_T yylen; + for (yylen = 0; yystr[yylen]; yylen++) + continue; + return yylen; +} +# endif +# endif + +# ifndef yystpcpy +# if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static char * +yystpcpy (char *yydest, const char *yysrc) +#else +static char * +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +#endif +{ + char *yyd = yydest; + const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +# ifndef yytnamerr +/* Copy to YYRES the contents of YYSTR after stripping away unnecessary + quotes and backslashes, so that it's suitable for yyerror. The + heuristic is that double-quoting is unnecessary unless the string + contains an apostrophe, a comma, or backslash (other than + backslash-backslash). YYSTR is taken from yytname. If YYRES is + null, do not copy; instead, return the length of what the result + would have been. */ +static YYSIZE_T +yytnamerr (char *yyres, const char *yystr) +{ + if (*yystr == '"') + { + YYSIZE_T yyn = 0; + char const *yyp = yystr; + + for (;;) + switch (*++yyp) + { + case '\'': + case ',': + goto do_not_strip_quotes; + + case '\\': + if (*++yyp != '\\') + goto do_not_strip_quotes; + /* Fall through. */ + default: + if (yyres) + yyres[yyn] = *yyp; + yyn++; + break; + + case '"': + if (yyres) + yyres[yyn] = '\0'; + return yyn; + } + do_not_strip_quotes: ; + } + + if (! yyres) + return yystrlen (yystr); + + return yystpcpy (yyres, yystr) - yyres; +} +# endif + +/* Copy into YYRESULT an error message about the unexpected token + YYCHAR while in state YYSTATE. Return the number of bytes copied, + including the terminating null byte. If YYRESULT is null, do not + copy anything; just return the number of bytes that would be + copied. As a special case, return 0 if an ordinary "syntax error" + message will do. Return YYSIZE_MAXIMUM if overflow occurs during + size calculation. */ +static YYSIZE_T +yysyntax_error (char *yyresult, int yystate, int yychar) +{ + int yyn = yypact[yystate]; + + if (! (YYPACT_NINF < yyn && yyn <= YYLAST)) + return 0; + else + { + int yytype = YYTRANSLATE (yychar); + YYSIZE_T yysize0 = yytnamerr (0, yytname[yytype]); + YYSIZE_T yysize = yysize0; + YYSIZE_T yysize1; + int yysize_overflow = 0; + enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 }; + char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM]; + int yyx; + +# if 0 + /* This is so xgettext sees the translatable formats that are + constructed on the fly. */ + YY_("syntax error, unexpected %s"); + YY_("syntax error, unexpected %s, expecting %s"); + YY_("syntax error, unexpected %s, expecting %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s"); + YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"); +# endif + char *yyfmt; + char const *yyf; + static char const yyunexpected[] = "syntax error, unexpected %s"; + static char const yyexpecting[] = ", expecting %s"; + static char const yyor[] = " or %s"; + char yyformat[sizeof yyunexpected + + sizeof yyexpecting - 1 + + ((YYERROR_VERBOSE_ARGS_MAXIMUM - 2) + * (sizeof yyor - 1))]; + char const *yyprefix = yyexpecting; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn + 1; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 1; + + yyarg[0] = yytname[yytype]; + yyfmt = yystpcpy (yyformat, yyunexpected); + + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM) + { + yycount = 1; + yysize = yysize0; + yyformat[sizeof yyunexpected - 1] = '\0'; + break; + } + yyarg[yycount++] = yytname[yyx]; + yysize1 = yysize + yytnamerr (0, yytname[yyx]); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + yyfmt = yystpcpy (yyfmt, yyprefix); + yyprefix = yyor; + } + + yyf = YY_(yyformat); + yysize1 = yysize + yystrlen (yyf); + yysize_overflow |= (yysize1 < yysize); + yysize = yysize1; + + if (yysize_overflow) + return YYSIZE_MAXIMUM; + + if (yyresult) + { + /* Avoid sprintf, as that infringes on the user's name space. + Don't have undefined behavior even if the translation + produced a string with the wrong number of "%s"s. */ + char *yyp = yyresult; + int yyi = 0; + while ((*yyp = *yyf) != '\0') + { + if (*yyp == '%' && yyf[1] == 's' && yyi < yycount) + { + yyp += yytnamerr (yyp, yyarg[yyi++]); + yyf += 2; + } + else + { + yyp++; + yyf++; + } + } + } + return yysize; + } +} +#endif /* YYERROR_VERBOSE */ + + +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +/*ARGSUSED*/ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +static void +yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yymsg, yytype, yyvaluep) + const char *yymsg; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + YYUSE (yyvaluep); + + if (!yymsg) + yymsg = "Deleting"; + YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp); + + switch (yytype) + { + + default: + break; + } +} + +/* Prevent warnings from -Wmissing-prototypes. */ +#ifdef YYPARSE_PARAM +#if defined __STDC__ || defined __cplusplus +int yyparse (void *YYPARSE_PARAM); +#else +int yyparse (); +#endif +#else /* ! YYPARSE_PARAM */ +#if defined __STDC__ || defined __cplusplus +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + +/*-------------------------. +| yyparse or yypush_parse. | +`-------------------------*/ + +#ifdef YYPARSE_PARAM +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void *YYPARSE_PARAM) +#else +int +yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +#endif +#else /* ! YYPARSE_PARAM */ +#if (defined __STDC__ || defined __C99__FUNC__ \ + || defined __cplusplus || defined _MSC_VER) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ +/* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + + /* Number of syntax errors so far. */ + int yynerrs; + + int yystate; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + + /* The stacks and their tools: + `yyss': related to states. + `yyvs': related to semantic values. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + yytype_int16 yyssa[YYINITDEPTH]; + yytype_int16 *yyss; + yytype_int16 *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs; + YYSTYPE *yyvsp; + + YYSIZE_T yystacksize; + + int yyn; + int yyresult; + /* Lookahead token as an internal (translated) token number. */ + int yytoken; + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + +#if YYERROR_VERBOSE + /* Buffer for error messages, and its allocated size. */ + char yymsgbuf[128]; + char *yymsg = yymsgbuf; + YYSIZE_T yymsg_alloc = sizeof yymsgbuf; +#endif + +#define YYPOPSTACK(N) (yyvsp -= (N), yyssp -= (N)) + + /* The number of symbols on the RHS of the reduced rule. + Keep to zero when no symbol should be popped. */ + int yylen = 0; + + yytoken = 0; + yyss = yyssa; + yyvs = yyvsa; + yystacksize = YYINITDEPTH; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. So pushing a state here evens the stacks. */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + yytype_int16 *yyss1 = yyss; + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow (YY_("memory exhausted"), + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyexhaustedlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyexhaustedlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + yytype_int16 *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyexhaustedlab; + YYSTACK_RELOCATE (yyss_alloc, yyss); + YYSTACK_RELOCATE (yyvs_alloc, yyvs); +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + if (yystate == YYFINAL) + YYACCEPT; + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + + /* Do appropriate processing given the current state. Read a + lookahead token if we need one and don't already have one. */ + + /* First try to decide what to do without reference to lookahead token. */ + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + /* Shift the lookahead token. */ + YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc); + + /* Discard the shifted token. */ + yychar = YYEMPTY; + + yystate = yyn; + *++yyvsp = yylval; + + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: + +/* Line 1455 of yacc.c */ +#line 145 "parser.y" + { parse_complete(ctx, (yyvsp[(1) - (3)].bool)); ;} + break; + + case 3: + +/* Line 1455 of yacc.c */ +#line 148 "parser.y" + { (yyval.bool) = FALSE; ;} + break; + + case 4: + +/* Line 1455 of yacc.c */ +#line 149 "parser.y" + { (yyval.bool) = TRUE; ;} + break; + + case 6: + +/* Line 1455 of yacc.c */ +#line 153 "parser.y" + { source_add_statement(ctx, (yyvsp[(2) - (2)].statement)); ;} + break; + + case 7: + +/* Line 1455 of yacc.c */ +#line 154 "parser.y" + { source_add_class(ctx, (yyvsp[(2) - (2)].class_decl)); ;} + break; + + case 8: + +/* Line 1455 of yacc.c */ +#line 157 "parser.y" + { (yyval.statement) = NULL; ;} + break; + + case 9: + +/* Line 1455 of yacc.c */ +#line 158 "parser.y" + { (yyval.statement) = (yyvsp[(1) - (1)].statement); ;} + break; + + case 10: + +/* Line 1455 of yacc.c */ +#line 161 "parser.y" + { (yyval.statement) = (yyvsp[(1) - (1)].statement); ;} + break; + + case 11: + +/* Line 1455 of yacc.c */ +#line 162 "parser.y" + { (yyval.statement) = link_statements((yyvsp[(1) - (2)].statement), (yyvsp[(2) - (2)].statement)); ;} + break; + + case 12: + +/* Line 1455 of yacc.c */ +#line 165 "parser.y" + { (yyval.statement) = (yyvsp[(1) - (2)].statement); ;} + break; + + case 13: + +/* Line 1455 of yacc.c */ +#line 168 "parser.y" + { (yyval.statement) = NULL; ;} + break; + + case 14: + +/* Line 1455 of yacc.c */ +#line 169 "parser.y" + { (yyval.statement) = (yyvsp[(2) - (2)].statement); ;} + break; + + case 15: + +/* Line 1455 of yacc.c */ +#line 170 "parser.y" + { (yyval.statement) = (yyvsp[(1) - (1)].statement); ;} + break; + + case 16: + +/* Line 1455 of yacc.c */ +#line 171 "parser.y" + { (yyvsp[(1) - (3)].statement)->next = (yyvsp[(3) - (3)].statement); (yyval.statement) = (yyvsp[(1) - (3)].statement); ;} + break; + + case 17: + +/* Line 1455 of yacc.c */ +#line 172 "parser.y" + { (yyval.statement) = (yyvsp[(1) - (2)].statement); ;} + break; + + case 18: + +/* Line 1455 of yacc.c */ +#line 175 "parser.y" + { (yyvsp[(1) - (2)].member)->args = (yyvsp[(2) - (2)].expression); (yyval.statement) = new_call_statement(ctx, FALSE, (yyvsp[(1) - (2)].member)); CHECK_ERROR; ;} + break; + + case 19: + +/* Line 1455 of yacc.c */ +#line 176 "parser.y" + { (yyvsp[(2) - (3)].member)->args = (yyvsp[(3) - (3)].expression); (yyval.statement) = new_call_statement(ctx, TRUE, (yyvsp[(2) - (3)].member)); CHECK_ERROR; ;} + break; + + case 20: + +/* Line 1455 of yacc.c */ +#line 178 "parser.y" + { (yyvsp[(1) - (4)].member)->args = (yyvsp[(2) - (4)].expression); (yyval.statement) = new_assign_statement(ctx, (yyvsp[(1) - (4)].member), (yyvsp[(4) - (4)].expression)); CHECK_ERROR; ;} + break; + + case 21: + +/* Line 1455 of yacc.c */ +#line 179 "parser.y" + { (yyval.statement) = new_dim_statement(ctx, (yyvsp[(2) - (2)].dim_decl)); CHECK_ERROR; ;} + break; + + case 22: + +/* Line 1455 of yacc.c */ +#line 180 "parser.y" + { (yyval.statement) = (yyvsp[(1) - (1)].statement); ;} + break; + + case 23: + +/* Line 1455 of yacc.c */ +#line 182 "parser.y" + { (yyval.statement) = new_while_statement(ctx, STAT_WHILE, (yyvsp[(2) - (5)].expression), (yyvsp[(4) - (5)].statement)); CHECK_ERROR; ;} + break; + + case 24: + +/* Line 1455 of yacc.c */ +#line 184 "parser.y" + { (yyval.statement) = new_while_statement(ctx, (yyvsp[(2) - (6)].bool) ? STAT_WHILELOOP : STAT_UNTIL, (yyvsp[(3) - (6)].expression), (yyvsp[(5) - (6)].statement)); + CHECK_ERROR; ;} + break; + + case 25: + +/* Line 1455 of yacc.c */ +#line 187 "parser.y" + { (yyval.statement) = new_while_statement(ctx, (yyvsp[(5) - (6)].bool) ? STAT_DOWHILE : STAT_DOUNTIL, (yyvsp[(6) - (6)].expression), (yyvsp[(3) - (6)].statement)); + CHECK_ERROR; ;} + break; + + case 26: + +/* Line 1455 of yacc.c */ +#line 189 "parser.y" + { (yyval.statement) = new_while_statement(ctx, STAT_DOWHILE, NULL, (yyvsp[(3) - (4)].statement)); CHECK_ERROR; ;} + break; + + case 27: + +/* Line 1455 of yacc.c */ +#line 190 "parser.y" + { (yyval.statement) = new_function_statement(ctx, (yyvsp[(1) - (1)].func_decl)); CHECK_ERROR; ;} + break; + + case 28: + +/* Line 1455 of yacc.c */ +#line 191 "parser.y" + { (yyval.statement) = new_statement(ctx, STAT_EXITDO, 0); CHECK_ERROR; ;} + break; + + case 29: + +/* Line 1455 of yacc.c */ +#line 192 "parser.y" + { (yyval.statement) = new_statement(ctx, STAT_EXITFOR, 0); CHECK_ERROR; ;} + break; + + case 30: + +/* Line 1455 of yacc.c */ +#line 193 "parser.y" + { (yyval.statement) = new_statement(ctx, STAT_EXITFUNC, 0); CHECK_ERROR; ;} + break; + + case 31: + +/* Line 1455 of yacc.c */ +#line 194 "parser.y" + { (yyval.statement) = new_statement(ctx, STAT_EXITPROP, 0); CHECK_ERROR; ;} + break; + + case 32: + +/* Line 1455 of yacc.c */ +#line 195 "parser.y" + { (yyval.statement) = new_statement(ctx, STAT_EXITSUB, 0); CHECK_ERROR; ;} + break; + + case 33: + +/* Line 1455 of yacc.c */ +#line 197 "parser.y" + { (yyvsp[(2) - (5)].member)->args = (yyvsp[(3) - (5)].expression); (yyval.statement) = new_set_statement(ctx, (yyvsp[(2) - (5)].member), (yyvsp[(5) - (5)].expression)); CHECK_ERROR; ;} + break; + + case 34: + +/* Line 1455 of yacc.c */ +#line 198 "parser.y" + { (yyval.statement) = new_statement(ctx, STAT_STOP, 0); CHECK_ERROR; ;} + break; + + case 35: + +/* Line 1455 of yacc.c */ +#line 199 "parser.y" + { (yyval.statement) = new_onerror_statement(ctx, TRUE); CHECK_ERROR; ;} + break; + + case 36: + +/* Line 1455 of yacc.c */ +#line 200 "parser.y" + { (yyval.statement) = new_onerror_statement(ctx, FALSE); CHECK_ERROR; ;} + break; + + case 37: + +/* Line 1455 of yacc.c */ +#line 201 "parser.y" + { (yyval.statement) = new_const_statement(ctx, (yyvsp[(2) - (2)].const_decl)); CHECK_ERROR; ;} + break; + + case 38: + +/* Line 1455 of yacc.c */ +#line 203 "parser.y" + { (yyval.statement) = new_forto_statement(ctx, (yyvsp[(2) - (10)].string), (yyvsp[(4) - (10)].expression), (yyvsp[(6) - (10)].expression), (yyvsp[(7) - (10)].expression), (yyvsp[(9) - (10)].statement)); CHECK_ERROR; ;} + break; + + case 39: + +/* Line 1455 of yacc.c */ +#line 205 "parser.y" + { (yyval.statement) = new_foreach_statement(ctx, (yyvsp[(3) - (8)].string), (yyvsp[(5) - (8)].expression), (yyvsp[(7) - (8)].statement)); ;} + break; + + case 40: + +/* Line 1455 of yacc.c */ +#line 207 "parser.y" + { (yyval.statement) = new_select_statement(ctx, (yyvsp[(3) - (7)].expression), (yyvsp[(5) - (7)].case_clausule)); ;} + break; + + case 41: + +/* Line 1455 of yacc.c */ +#line 210 "parser.y" + { (yyval.member) = new_member_expression(ctx, NULL, (yyvsp[(1) - (1)].string)); CHECK_ERROR; ;} + break; + + case 42: + +/* Line 1455 of yacc.c */ +#line 211 "parser.y" + { (yyval.member) = new_member_expression(ctx, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].string)); CHECK_ERROR; ;} + break; + + case 43: + +/* Line 1455 of yacc.c */ +#line 214 "parser.y" + { (yyval.dim_decl) = new_dim_decl(ctx, (yyvsp[(1) - (1)].string), NULL); CHECK_ERROR; ;} + break; + + case 44: + +/* Line 1455 of yacc.c */ +#line 215 "parser.y" + { (yyval.dim_decl) = new_dim_decl(ctx, (yyvsp[(1) - (3)].string), (yyvsp[(3) - (3)].dim_decl)); CHECK_ERROR; ;} + break; + + case 45: + +/* Line 1455 of yacc.c */ +#line 218 "parser.y" + { (yyval.const_decl) = (yyvsp[(1) - (1)].const_decl); ;} + break; + + case 46: + +/* Line 1455 of yacc.c */ +#line 219 "parser.y" + { (yyvsp[(1) - (3)].const_decl)->next = (yyvsp[(3) - (3)].const_decl); (yyval.const_decl) = (yyvsp[(1) - (3)].const_decl); ;} + break; + + case 47: + +/* Line 1455 of yacc.c */ +#line 222 "parser.y" + { (yyval.const_decl) = new_const_decl(ctx, (yyvsp[(1) - (3)].string), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 48: + +/* Line 1455 of yacc.c */ +#line 225 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 49: + +/* Line 1455 of yacc.c */ +#line 226 "parser.y" + { (yyval.expression) = new_unary_expression(ctx, EXPR_NEG, (yyvsp[(2) - (2)].expression)); CHECK_ERROR; ;} + break; + + case 50: + +/* Line 1455 of yacc.c */ +#line 229 "parser.y" + { (yyval.bool) = TRUE; ;} + break; + + case 51: + +/* Line 1455 of yacc.c */ +#line 230 "parser.y" + { (yyval.bool) = FALSE; ;} + break; + + case 52: + +/* Line 1455 of yacc.c */ +#line 233 "parser.y" + { (yyval.expression) = NULL;;} + break; + + case 53: + +/* Line 1455 of yacc.c */ +#line 234 "parser.y" + { (yyval.expression) = (yyvsp[(2) - (2)].expression); ;} + break; + + case 54: + +/* Line 1455 of yacc.c */ +#line 238 "parser.y" + { (yyval.statement) = new_if_statement(ctx, (yyvsp[(2) - (9)].expression), (yyvsp[(5) - (9)].statement), (yyvsp[(6) - (9)].elseif), (yyvsp[(7) - (9)].statement)); CHECK_ERROR; ;} + break; + + case 55: + +/* Line 1455 of yacc.c */ +#line 239 "parser.y" + { (yyval.statement) = new_if_statement(ctx, (yyvsp[(2) - (4)].expression), (yyvsp[(4) - (4)].statement), NULL, NULL); CHECK_ERROR; ;} + break; + + case 56: + +/* Line 1455 of yacc.c */ +#line 241 "parser.y" + { (yyval.statement) = new_if_statement(ctx, (yyvsp[(2) - (7)].expression), (yyvsp[(4) - (7)].statement), NULL, (yyvsp[(6) - (7)].statement)); CHECK_ERROR; ;} + break; + + case 59: + +/* Line 1455 of yacc.c */ +#line 248 "parser.y" + { (yyval.elseif) = NULL; ;} + break; + + case 60: + +/* Line 1455 of yacc.c */ +#line 249 "parser.y" + { (yyval.elseif) = (yyvsp[(1) - (1)].elseif); ;} + break; + + case 61: + +/* Line 1455 of yacc.c */ +#line 252 "parser.y" + { (yyval.elseif) = (yyvsp[(1) - (1)].elseif); ;} + break; + + case 62: + +/* Line 1455 of yacc.c */ +#line 253 "parser.y" + { (yyvsp[(1) - (2)].elseif)->next = (yyvsp[(2) - (2)].elseif); (yyval.elseif) = (yyvsp[(1) - (2)].elseif); ;} + break; + + case 63: + +/* Line 1455 of yacc.c */ +#line 257 "parser.y" + { (yyval.elseif) = new_elseif_decl(ctx, (yyvsp[(2) - (5)].expression), (yyvsp[(5) - (5)].statement)); ;} + break; + + case 64: + +/* Line 1455 of yacc.c */ +#line 260 "parser.y" + { (yyval.statement) = NULL; ;} + break; + + case 65: + +/* Line 1455 of yacc.c */ +#line 261 "parser.y" + { (yyval.statement) = (yyvsp[(3) - (3)].statement); ;} + break; + + case 66: + +/* Line 1455 of yacc.c */ +#line 264 "parser.y" + { (yyval.case_clausule) = NULL; ;} + break; + + case 67: + +/* Line 1455 of yacc.c */ +#line 265 "parser.y" + { (yyval.case_clausule) = new_case_clausule(ctx, NULL, (yyvsp[(4) - (4)].statement), NULL); ;} + break; + + case 68: + +/* Line 1455 of yacc.c */ +#line 267 "parser.y" + { (yyval.case_clausule) = new_case_clausule(ctx, (yyvsp[(2) - (5)].expression), (yyvsp[(4) - (5)].statement), (yyvsp[(5) - (5)].case_clausule)); ;} + break; + + case 69: + +/* Line 1455 of yacc.c */ +#line 270 "parser.y" + { (yyval.expression) = NULL; ;} + break; + + case 70: + +/* Line 1455 of yacc.c */ +#line 271 "parser.y" + { (yyval.expression) = (yyvsp[(2) - (3)].expression); ;} + break; + + case 71: + +/* Line 1455 of yacc.c */ +#line 274 "parser.y" + { (yyval.expression) = NULL; ;} + break; + + case 72: + +/* Line 1455 of yacc.c */ +#line 275 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 75: + +/* Line 1455 of yacc.c */ +#line 282 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 76: + +/* Line 1455 of yacc.c */ +#line 283 "parser.y" + { (yyvsp[(1) - (3)].expression)->next = (yyvsp[(3) - (3)].expression); (yyval.expression) = (yyvsp[(1) - (3)].expression); ;} + break; + + case 77: + +/* Line 1455 of yacc.c */ +#line 286 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 78: + +/* Line 1455 of yacc.c */ +#line 287 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_IMP, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 79: + +/* Line 1455 of yacc.c */ +#line 290 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 80: + +/* Line 1455 of yacc.c */ +#line 291 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_EQV, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 81: + +/* Line 1455 of yacc.c */ +#line 294 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 82: + +/* Line 1455 of yacc.c */ +#line 295 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_XOR, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 83: + +/* Line 1455 of yacc.c */ +#line 298 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 84: + +/* Line 1455 of yacc.c */ +#line 299 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_OR, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 85: + +/* Line 1455 of yacc.c */ +#line 302 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 86: + +/* Line 1455 of yacc.c */ +#line 303 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_AND, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 87: + +/* Line 1455 of yacc.c */ +#line 306 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 88: + +/* Line 1455 of yacc.c */ +#line 307 "parser.y" + { (yyval.expression) = new_unary_expression(ctx, EXPR_NOT, (yyvsp[(2) - (2)].expression)); CHECK_ERROR; ;} + break; + + case 89: + +/* Line 1455 of yacc.c */ +#line 310 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 90: + +/* Line 1455 of yacc.c */ +#line 311 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_EQUAL, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 91: + +/* Line 1455 of yacc.c */ +#line 312 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_NEQUAL, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 92: + +/* Line 1455 of yacc.c */ +#line 313 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_GT, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 93: + +/* Line 1455 of yacc.c */ +#line 314 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_LT, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 94: + +/* Line 1455 of yacc.c */ +#line 315 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_GTEQ, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 95: + +/* Line 1455 of yacc.c */ +#line 316 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_LTEQ, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 96: + +/* Line 1455 of yacc.c */ +#line 317 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_IS, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 97: + +/* Line 1455 of yacc.c */ +#line 320 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 98: + +/* Line 1455 of yacc.c */ +#line 321 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_CONCAT, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 99: + +/* Line 1455 of yacc.c */ +#line 324 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 100: + +/* Line 1455 of yacc.c */ +#line 325 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_ADD, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 101: + +/* Line 1455 of yacc.c */ +#line 326 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_SUB, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 102: + +/* Line 1455 of yacc.c */ +#line 329 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 103: + +/* Line 1455 of yacc.c */ +#line 330 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_MOD, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 104: + +/* Line 1455 of yacc.c */ +#line 333 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 105: + +/* Line 1455 of yacc.c */ +#line 335 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_IDIV, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 106: + +/* Line 1455 of yacc.c */ +#line 338 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 107: + +/* Line 1455 of yacc.c */ +#line 340 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_MUL, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 108: + +/* Line 1455 of yacc.c */ +#line 342 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_DIV, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 109: + +/* Line 1455 of yacc.c */ +#line 345 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 110: + +/* Line 1455 of yacc.c */ +#line 346 "parser.y" + { (yyval.expression) = new_binary_expression(ctx, EXPR_EXP, (yyvsp[(1) - (3)].expression), (yyvsp[(3) - (3)].expression)); CHECK_ERROR; ;} + break; + + case 111: + +/* Line 1455 of yacc.c */ +#line 349 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 112: + +/* Line 1455 of yacc.c */ +#line 350 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 113: + +/* Line 1455 of yacc.c */ +#line 351 "parser.y" + { (yyval.expression) = new_new_expression(ctx, (yyvsp[(2) - (2)].string)); CHECK_ERROR; ;} + break; + + case 114: + +/* Line 1455 of yacc.c */ +#line 352 "parser.y" + { (yyval.expression) = new_unary_expression(ctx, EXPR_NEG, (yyvsp[(2) - (2)].expression)); CHECK_ERROR; ;} + break; + + case 115: + +/* Line 1455 of yacc.c */ +#line 355 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 116: + +/* Line 1455 of yacc.c */ +#line 356 "parser.y" + { (yyvsp[(1) - (2)].member)->args = (yyvsp[(2) - (2)].expression); (yyval.expression) = &(yyvsp[(1) - (2)].member)->expr; ;} + break; + + case 117: + +/* Line 1455 of yacc.c */ +#line 359 "parser.y" + { (yyval.expression) = new_bool_expression(ctx, VARIANT_TRUE); CHECK_ERROR; ;} + break; + + case 118: + +/* Line 1455 of yacc.c */ +#line 360 "parser.y" + { (yyval.expression) = new_bool_expression(ctx, VARIANT_FALSE); CHECK_ERROR; ;} + break; + + case 119: + +/* Line 1455 of yacc.c */ +#line 361 "parser.y" + { (yyval.expression) = new_string_expression(ctx, (yyvsp[(1) - (1)].string)); CHECK_ERROR; ;} + break; + + case 120: + +/* Line 1455 of yacc.c */ +#line 362 "parser.y" + { (yyval.expression) = (yyvsp[(1) - (1)].expression); ;} + break; + + case 121: + +/* Line 1455 of yacc.c */ +#line 363 "parser.y" + { (yyval.expression) = new_expression(ctx, EXPR_EMPTY, 0); CHECK_ERROR; ;} + break; + + case 122: + +/* Line 1455 of yacc.c */ +#line 364 "parser.y" + { (yyval.expression) = new_expression(ctx, EXPR_NULL, 0); CHECK_ERROR; ;} + break; + + case 123: + +/* Line 1455 of yacc.c */ +#line 365 "parser.y" + { (yyval.expression) = new_expression(ctx, EXPR_NOTHING, 0); CHECK_ERROR; ;} + break; + + case 124: + +/* Line 1455 of yacc.c */ +#line 368 "parser.y" + { (yyval.expression) = new_long_expression(ctx, EXPR_USHORT, (yyvsp[(1) - (1)].lng)); CHECK_ERROR; ;} + break; + + case 125: + +/* Line 1455 of yacc.c */ +#line 369 "parser.y" + { (yyval.expression) = new_long_expression(ctx, EXPR_USHORT, 0); CHECK_ERROR; ;} + break; + + case 126: + +/* Line 1455 of yacc.c */ +#line 370 "parser.y" + { (yyval.expression) = new_long_expression(ctx, EXPR_ULONG, (yyvsp[(1) - (1)].lng)); CHECK_ERROR; ;} + break; + + case 127: + +/* Line 1455 of yacc.c */ +#line 371 "parser.y" + { (yyval.expression) = new_double_expression(ctx, (yyvsp[(1) - (1)].dbl)); CHECK_ERROR; ;} + break; + + case 128: + +/* Line 1455 of yacc.c */ +#line 375 "parser.y" + { (yyval.expression) = new_unary_expression(ctx, EXPR_BRACKETS, (yyvsp[(2) - (3)].expression)); ;} + break; + + case 129: + +/* Line 1455 of yacc.c */ +#line 376 "parser.y" + { (yyval.expression) = new_expression(ctx, EXPR_ME, 0); CHECK_ERROR; ;} + break; + + case 130: + +/* Line 1455 of yacc.c */ +#line 379 "parser.y" + { (yyvsp[(4) - (7)].class_decl)->name = (yyvsp[(2) - (7)].string); (yyval.class_decl) = (yyvsp[(4) - (7)].class_decl); ;} + break; + + case 131: + +/* Line 1455 of yacc.c */ +#line 382 "parser.y" + { (yyval.class_decl) = new_class_decl(ctx); ;} + break; + + case 132: + +/* Line 1455 of yacc.c */ +#line 383 "parser.y" + { (yyval.class_decl) = add_class_function(ctx, (yyvsp[(3) - (3)].class_decl), (yyvsp[(1) - (3)].func_decl)); CHECK_ERROR; ;} + break; + + case 133: + +/* Line 1455 of yacc.c */ +#line 384 "parser.y" + { (yyval.class_decl) = add_variant_prop(ctx, (yyvsp[(4) - (4)].class_decl), (yyvsp[(2) - (4)].string), (yyvsp[(1) - (4)].uint)); CHECK_ERROR; ;} + break; + + case 134: + +/* Line 1455 of yacc.c */ +#line 385 "parser.y" + { (yyval.class_decl) = add_class_function(ctx, (yyvsp[(3) - (3)].class_decl), (yyvsp[(1) - (3)].func_decl)); CHECK_ERROR; ;} + break; + + case 135: + +/* Line 1455 of yacc.c */ +#line 389 "parser.y" + { (yyval.func_decl) = new_function_decl(ctx, (yyvsp[(4) - (9)].string), FUNC_PROPGET, (yyvsp[(1) - (9)].uint), NULL, (yyvsp[(7) - (9)].statement)); CHECK_ERROR; ;} + break; + + case 136: + +/* Line 1455 of yacc.c */ +#line 391 "parser.y" + { (yyval.func_decl) = new_function_decl(ctx, (yyvsp[(4) - (11)].string), FUNC_PROPLET, (yyvsp[(1) - (11)].uint), (yyvsp[(6) - (11)].arg_decl), (yyvsp[(9) - (11)].statement)); CHECK_ERROR; ;} + break; + + case 137: + +/* Line 1455 of yacc.c */ +#line 393 "parser.y" + { (yyval.func_decl) = new_function_decl(ctx, (yyvsp[(4) - (11)].string), FUNC_PROPSET, (yyvsp[(1) - (11)].uint), (yyvsp[(6) - (11)].arg_decl), (yyvsp[(9) - (11)].statement)); CHECK_ERROR; ;} + break; + + case 138: + +/* Line 1455 of yacc.c */ +#line 397 "parser.y" + { (yyval.func_decl) = new_function_decl(ctx, (yyvsp[(3) - (8)].string), FUNC_SUB, (yyvsp[(1) - (8)].uint), (yyvsp[(4) - (8)].arg_decl), (yyvsp[(6) - (8)].statement)); CHECK_ERROR; ;} + break; + + case 139: + +/* Line 1455 of yacc.c */ +#line 399 "parser.y" + { (yyval.func_decl) = new_function_decl(ctx, (yyvsp[(3) - (8)].string), FUNC_FUNCTION, (yyvsp[(1) - (8)].uint), (yyvsp[(4) - (8)].arg_decl), (yyvsp[(6) - (8)].statement)); CHECK_ERROR; ;} + break; + + case 140: + +/* Line 1455 of yacc.c */ +#line 402 "parser.y" + { (yyval.uint) = 0; ;} + break; + + case 141: + +/* Line 1455 of yacc.c */ +#line 403 "parser.y" + { (yyval.uint) = (yyvsp[(1) - (1)].uint); ;} + break; + + case 142: + +/* Line 1455 of yacc.c */ +#line 406 "parser.y" + { (yyval.uint) = STORAGE_IS_DEFAULT; ;} + break; + + case 143: + +/* Line 1455 of yacc.c */ +#line 407 "parser.y" + { (yyval.uint) = 0; ;} + break; + + case 144: + +/* Line 1455 of yacc.c */ +#line 408 "parser.y" + { (yyval.uint) = STORAGE_IS_PRIVATE; ;} + break; + + case 145: + +/* Line 1455 of yacc.c */ +#line 411 "parser.y" + { (yyval.arg_decl) = NULL; ;} + break; + + case 146: + +/* Line 1455 of yacc.c */ +#line 412 "parser.y" + { (yyval.arg_decl) = (yyvsp[(2) - (3)].arg_decl); ;} + break; + + case 147: + +/* Line 1455 of yacc.c */ +#line 415 "parser.y" + { (yyval.arg_decl) = (yyvsp[(1) - (1)].arg_decl); ;} + break; + + case 148: + +/* Line 1455 of yacc.c */ +#line 416 "parser.y" + { (yyvsp[(1) - (3)].arg_decl)->next = (yyvsp[(3) - (3)].arg_decl); (yyval.arg_decl) = (yyvsp[(1) - (3)].arg_decl); ;} + break; + + case 149: + +/* Line 1455 of yacc.c */ +#line 419 "parser.y" + { (yyval.arg_decl) = new_argument_decl(ctx, (yyvsp[(1) - (1)].string), TRUE); ;} + break; + + case 150: + +/* Line 1455 of yacc.c */ +#line 420 "parser.y" + { (yyval.arg_decl) = new_argument_decl(ctx, (yyvsp[(2) - (2)].string), TRUE); ;} + break; + + case 151: + +/* Line 1455 of yacc.c */ +#line 421 "parser.y" + { (yyval.arg_decl) = new_argument_decl(ctx, (yyvsp[(2) - (2)].string), FALSE); ;} + break; + + case 152: + +/* Line 1455 of yacc.c */ +#line 425 "parser.y" + { (yyval.string) = (yyvsp[(1) - (1)].string); ;} + break; + + case 153: + +/* Line 1455 of yacc.c */ +#line 426 "parser.y" + { (yyval.string) = propertyW; ;} + break; + + + +/* Line 1455 of yacc.c */ +#line 2907 "parser.tab.c" + default: break; + } + YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc); + + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if ! YYERROR_VERBOSE + yyerror (YY_("syntax error")); +#else + { + YYSIZE_T yysize = yysyntax_error (0, yystate, yychar); + if (yymsg_alloc < yysize && yymsg_alloc < YYSTACK_ALLOC_MAXIMUM) + { + YYSIZE_T yyalloc = 2 * yysize; + if (! (yysize <= yyalloc && yyalloc <= YYSTACK_ALLOC_MAXIMUM)) + yyalloc = YYSTACK_ALLOC_MAXIMUM; + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); + yymsg = (char *) YYSTACK_ALLOC (yyalloc); + if (yymsg) + yymsg_alloc = yyalloc; + else + { + yymsg = yymsgbuf; + yymsg_alloc = sizeof yymsgbuf; + } + } + + if (0 < yysize && yysize <= yymsg_alloc) + { + (void) yysyntax_error (yymsg, yystate, yychar); + yyerror (yymsg); + } + else + { + yyerror (YY_("syntax error")); + if (yysize != 0) + goto yyexhaustedlab; + } + } +#endif + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* Return failure if at end of input. */ + if (yychar == YYEOF) + YYABORT; + } + else + { + yydestruct ("Error: discarding", + yytoken, &yylval); + yychar = YYEMPTY; + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + + /* Pacify compilers like GCC when the user code never invokes + YYERROR and the label yyerrorlab therefore never appears in user + code. */ + if (/*CONSTCOND*/ 0) + goto yyerrorlab; + + /* Do not reclaim the symbols of the rule which action triggered + this YYERROR. */ + YYPOPSTACK (yylen); + yylen = 0; + YY_STACK_PRINT (yyss, yyssp); + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + + yydestruct ("Error: popping", + yystos[yystate], yyvsp); + YYPOPSTACK (1); + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + *++yyvsp = yylval; + + + /* Shift the error token. */ + YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp); + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#if !defined(yyoverflow) || YYERROR_VERBOSE +/*-------------------------------------------------. +| yyexhaustedlab -- memory exhaustion comes here. | +`-------------------------------------------------*/ +yyexhaustedlab: + yyerror (YY_("memory exhausted")); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: + if (yychar != YYEMPTY) + yydestruct ("Cleanup: discarding lookahead", + yytoken, &yylval); + /* Do not reclaim the symbols of the rule which action triggered + this YYABORT or YYACCEPT. */ + YYPOPSTACK (yylen); + YY_STACK_PRINT (yyss, yyssp); + while (yyssp != yyss) + { + yydestruct ("Cleanup: popping", + yystos[*yyssp], yyvsp); + YYPOPSTACK (1); + } +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif +#if YYERROR_VERBOSE + if (yymsg != yymsgbuf) + YYSTACK_FREE (yymsg); +#endif + /* Make sure YYID is used. */ + return YYID (yyresult); +} + + + +/* Line 1675 of yacc.c */ +#line 427 "parser.y" + + +static int parser_error(const char *str) +{ + return 0; +} + +static void source_add_statement(parser_ctx_t *ctx, statement_t *stat) +{ + if(!stat) + return; + + if(ctx->stats) { + ctx->stats_tail->next = stat; + ctx->stats_tail = stat; + }else { + ctx->stats = ctx->stats_tail = stat; + } +} + +static void source_add_class(parser_ctx_t *ctx, class_decl_t *class_decl) +{ + class_decl->next = ctx->class_decls; + ctx->class_decls = class_decl; +} + +static void parse_complete(parser_ctx_t *ctx, BOOL option_explicit) +{ + ctx->parse_complete = TRUE; + ctx->option_explicit = option_explicit; +} + +static void *new_expression(parser_ctx_t *ctx, expression_type_t type, size_t size) +{ + expression_t *expr; + + expr = parser_alloc(ctx, size ? size : sizeof(*expr)); + if(expr) { + expr->type = type; + expr->next = NULL; + } + + return expr; +} + +static expression_t *new_bool_expression(parser_ctx_t *ctx, VARIANT_BOOL value) +{ + bool_expression_t *expr; + + expr = new_expression(ctx, EXPR_BOOL, sizeof(*expr)); + if(!expr) + return NULL; + + expr->value = value; + return &expr->expr; +} + +static expression_t *new_string_expression(parser_ctx_t *ctx, const WCHAR *value) +{ + string_expression_t *expr; + + expr = new_expression(ctx, EXPR_STRING, sizeof(*expr)); + if(!expr) + return NULL; + + expr->value = value; + return &expr->expr; +} + +static expression_t *new_long_expression(parser_ctx_t *ctx, expression_type_t type, LONG value) +{ + int_expression_t *expr; + + expr = new_expression(ctx, type, sizeof(*expr)); + if(!expr) + return NULL; + + expr->value = value; + return &expr->expr; +} + +static expression_t *new_double_expression(parser_ctx_t *ctx, double value) +{ + double_expression_t *expr; + + expr = new_expression(ctx, EXPR_DOUBLE, sizeof(*expr)); + if(!expr) + return NULL; + + expr->value = value; + return &expr->expr; +} + +static expression_t *new_unary_expression(parser_ctx_t *ctx, expression_type_t type, expression_t *subexpr) +{ + unary_expression_t *expr; + + expr = new_expression(ctx, type, sizeof(*expr)); + if(!expr) + return NULL; + + expr->subexpr = subexpr; + return &expr->expr; +} + +static expression_t *new_binary_expression(parser_ctx_t *ctx, expression_type_t type, expression_t *left, expression_t *right) +{ + binary_expression_t *expr; + + expr = new_expression(ctx, type, sizeof(*expr)); + if(!expr) + return NULL; + + expr->left = left; + expr->right = right; + return &expr->expr; +} + +static member_expression_t *new_member_expression(parser_ctx_t *ctx, expression_t *obj_expr, const WCHAR *identifier) +{ + member_expression_t *expr; + + expr = new_expression(ctx, EXPR_MEMBER, sizeof(*expr)); + if(!expr) + return NULL; + + expr->obj_expr = obj_expr; + expr->identifier = identifier; + expr->args = NULL; + return expr; +} + +static expression_t *new_new_expression(parser_ctx_t *ctx, const WCHAR *identifier) +{ + string_expression_t *expr; + + expr = new_expression(ctx, EXPR_NEW, sizeof(*expr)); + if(!expr) + return NULL; + + expr->value = identifier; + return &expr->expr; +} + +static void *new_statement(parser_ctx_t *ctx, statement_type_t type, size_t size) +{ + statement_t *stat; + + stat = parser_alloc(ctx, size ? size : sizeof(*stat)); + if(stat) { + stat->type = type; + stat->next = NULL; + } + + return stat; +} + +static statement_t *new_call_statement(parser_ctx_t *ctx, BOOL is_strict, member_expression_t *expr) +{ + call_statement_t *stat; + + stat = new_statement(ctx, STAT_CALL, sizeof(*stat)); + if(!stat) + return NULL; + + stat->expr = expr; + stat->is_strict = is_strict; + return &stat->stat; +} + +static statement_t *new_assign_statement(parser_ctx_t *ctx, member_expression_t *left, expression_t *right) +{ + assign_statement_t *stat; + + stat = new_statement(ctx, STAT_ASSIGN, sizeof(*stat)); + if(!stat) + return NULL; + + stat->member_expr = left; + stat->value_expr = right; + return &stat->stat; +} + +static statement_t *new_set_statement(parser_ctx_t *ctx, member_expression_t *left, expression_t *right) +{ + assign_statement_t *stat; + + stat = new_statement(ctx, STAT_SET, sizeof(*stat)); + if(!stat) + return NULL; + + stat->member_expr = left; + stat->value_expr = right; + return &stat->stat; +} + +static dim_decl_t *new_dim_decl(parser_ctx_t *ctx, const WCHAR *name, dim_decl_t *next) +{ + dim_decl_t *decl; + + decl = parser_alloc(ctx, sizeof(*decl)); + if(!decl) + return NULL; + + decl->name = name; + decl->next = next; + return decl; +} + +static statement_t *new_dim_statement(parser_ctx_t *ctx, dim_decl_t *decls) +{ + dim_statement_t *stat; + + stat = new_statement(ctx, STAT_DIM, sizeof(*stat)); + if(!stat) + return NULL; + + stat->dim_decls = decls; + return &stat->stat; +} + +static elseif_decl_t *new_elseif_decl(parser_ctx_t *ctx, expression_t *expr, statement_t *stat) +{ + elseif_decl_t *decl; + + decl = parser_alloc(ctx, sizeof(*decl)); + if(!decl) + return NULL; + + decl->expr = expr; + decl->stat = stat; + decl->next = NULL; + return decl; +} + +static statement_t *new_while_statement(parser_ctx_t *ctx, statement_type_t type, expression_t *expr, statement_t *body) +{ + while_statement_t *stat; + + stat = new_statement(ctx, type, sizeof(*stat)); + if(!stat) + return NULL; + + stat->expr = expr; + stat->body = body; + return &stat->stat; +} + +static statement_t *new_forto_statement(parser_ctx_t *ctx, const WCHAR *identifier, expression_t *from_expr, + expression_t *to_expr, expression_t *step_expr, statement_t *body) +{ + forto_statement_t *stat; + + stat = new_statement(ctx, STAT_FORTO, sizeof(*stat)); + if(!stat) + return NULL; + + stat->identifier = identifier; + stat->from_expr = from_expr; + stat->to_expr = to_expr; + stat->step_expr = step_expr; + stat->body = body; + return &stat->stat; +} + +static statement_t *new_foreach_statement(parser_ctx_t *ctx, const WCHAR *identifier, expression_t *group_expr, + statement_t *body) +{ + foreach_statement_t *stat; + + stat = new_statement(ctx, STAT_FOREACH, sizeof(*stat)); + if(!stat) + return NULL; + + stat->identifier = identifier; + stat->group_expr = group_expr; + stat->body = body; + return &stat->stat; +} + +static statement_t *new_if_statement(parser_ctx_t *ctx, expression_t *expr, statement_t *if_stat, elseif_decl_t *elseif_decl, + statement_t *else_stat) +{ + if_statement_t *stat; + + stat = new_statement(ctx, STAT_IF, sizeof(*stat)); + if(!stat) + return NULL; + + stat->expr = expr; + stat->if_stat = if_stat; + stat->elseifs = elseif_decl; + stat->else_stat = else_stat; + return &stat->stat; +} + +static statement_t *new_select_statement(parser_ctx_t *ctx, expression_t *expr, case_clausule_t *case_clausules) +{ + select_statement_t *stat; + + stat = new_statement(ctx, STAT_SELECT, sizeof(*stat)); + if(!stat) + return NULL; + + stat->expr = expr; + stat->case_clausules = case_clausules; + return &stat->stat; +} + +static case_clausule_t *new_case_clausule(parser_ctx_t *ctx, expression_t *expr, statement_t *stat, case_clausule_t *next) +{ + case_clausule_t *ret; + + ret = parser_alloc(ctx, sizeof(*ret)); + if(!ret) + return NULL; + + ret->expr = expr; + ret->stat = stat; + ret->next = next; + return ret; +} + +static statement_t *new_onerror_statement(parser_ctx_t *ctx, BOOL resume_next) +{ + onerror_statement_t *stat; + + stat = new_statement(ctx, STAT_ONERROR, sizeof(*stat)); + if(!stat) + return NULL; + + stat->resume_next = resume_next; + return &stat->stat; +} + +static arg_decl_t *new_argument_decl(parser_ctx_t *ctx, const WCHAR *name, BOOL by_ref) +{ + arg_decl_t *arg_decl; + + arg_decl = parser_alloc(ctx, sizeof(*arg_decl)); + if(!arg_decl) + return NULL; + + arg_decl->name = name; + arg_decl->by_ref = by_ref; + arg_decl->next = NULL; + return arg_decl; +} + +static function_decl_t *new_function_decl(parser_ctx_t *ctx, const WCHAR *name, function_type_t type, + unsigned storage_flags, arg_decl_t *arg_decl, statement_t *body) +{ + function_decl_t *decl; + + if(storage_flags & STORAGE_IS_DEFAULT) { + if(type == FUNC_PROPGET) { + type = FUNC_DEFGET; + }else { + FIXME("Invalid default property\n"); + ctx->hres = E_FAIL; + return NULL; + } + } + + decl = parser_alloc(ctx, sizeof(*decl)); + if(!decl) + return NULL; + + decl->name = name; + decl->type = type; + decl->is_public = !(storage_flags & STORAGE_IS_PRIVATE); + decl->args = arg_decl; + decl->body = body; + decl->next = NULL; + decl->next_prop_func = NULL; + return decl; +} + +static statement_t *new_function_statement(parser_ctx_t *ctx, function_decl_t *decl) +{ + function_statement_t *stat; + + stat = new_statement(ctx, STAT_FUNC, sizeof(*stat)); + if(!stat) + return NULL; + + stat->func_decl = decl; + return &stat->stat; +} + +static class_decl_t *new_class_decl(parser_ctx_t *ctx) +{ + class_decl_t *class_decl; + + class_decl = parser_alloc(ctx, sizeof(*class_decl)); + if(!class_decl) + return NULL; + + class_decl->funcs = NULL; + class_decl->props = NULL; + class_decl->next = NULL; + return class_decl; +} + +static class_decl_t *add_class_function(parser_ctx_t *ctx, class_decl_t *class_decl, function_decl_t *decl) +{ + function_decl_t *iter; + + for(iter = class_decl->funcs; iter; iter = iter->next) { + if(!strcmpiW(iter->name, decl->name)) { + if(decl->type == FUNC_SUB || decl->type == FUNC_FUNCTION) { + FIXME("Redefinition of %s::%s\n", debugstr_w(class_decl->name), debugstr_w(decl->name)); + ctx->hres = E_FAIL; + return NULL; + } + + while(1) { + if(iter->type == decl->type) { + FIXME("Redefinition of %s::%s\n", debugstr_w(class_decl->name), debugstr_w(decl->name)); + ctx->hres = E_FAIL; + return NULL; + } + if(!iter->next_prop_func) + break; + iter = iter->next_prop_func; + } + + iter->next_prop_func = decl; + return class_decl; + } + } + + decl->next = class_decl->funcs; + class_decl->funcs = decl; + return class_decl; +} + +static class_decl_t *add_variant_prop(parser_ctx_t *ctx, class_decl_t *class_decl, const WCHAR *identifier, unsigned storage_flags) +{ + class_prop_decl_t *prop; + + if(storage_flags & STORAGE_IS_DEFAULT) { + FIXME("variant prop van't be default value\n"); + ctx->hres = E_FAIL; + return NULL; + } + + prop = parser_alloc(ctx, sizeof(*prop)); + if(!prop) + return NULL; + + prop->name = identifier; + prop->is_public = !(storage_flags & STORAGE_IS_PRIVATE); + prop->next = class_decl->props; + class_decl->props = prop; + return class_decl; +} + +static const_decl_t *new_const_decl(parser_ctx_t *ctx, const WCHAR *name, expression_t *expr) +{ + const_decl_t *decl; + + decl = parser_alloc(ctx, sizeof(*decl)); + if(!decl) + return NULL; + + decl->name = name; + decl->value_expr = expr; + decl->next = NULL; + return decl; +} + +static statement_t *new_const_statement(parser_ctx_t *ctx, const_decl_t *decls) +{ + const_statement_t *stat; + + stat = new_statement(ctx, STAT_CONST, sizeof(*stat)); + if(!stat) + return NULL; + + stat->decls = decls; + return &stat->stat; +} + +static statement_t *link_statements(statement_t *head, statement_t *tail) +{ + statement_t *iter; + + for(iter = head; iter->next; iter = iter->next); + iter->next = tail; + + return head; +} + +void *parser_alloc(parser_ctx_t *ctx, size_t size) +{ + void *ret; + + ret = heap_pool_alloc(&ctx->heap, size); + if(!ret) + ctx->hres = E_OUTOFMEMORY; + return ret; +} + +HRESULT parse_script(parser_ctx_t *ctx, const WCHAR *code, const WCHAR *delimiter) +{ + const WCHAR html_delimiterW[] = {'<','/','s','c','r','i','p','t','>',0}; + + ctx->code = ctx->ptr = code; + ctx->end = ctx->code + strlenW(ctx->code); + + heap_pool_init(&ctx->heap); + + ctx->parse_complete = FALSE; + ctx->hres = S_OK; + + ctx->last_token = tNL; + ctx->last_nl = 0; + ctx->stats = ctx->stats_tail = NULL; + ctx->class_decls = NULL; + ctx->option_explicit = FALSE; + ctx->is_html = delimiter && !strcmpiW(delimiter, html_delimiterW); + + parser_parse(ctx); + + if(FAILED(ctx->hres)) + return ctx->hres; + if(!ctx->parse_complete) { + FIXME("parser failed around %s\n", debugstr_w(ctx->code+20 > ctx->ptr ? ctx->code : ctx->ptr-20)); + return E_FAIL; + } + + return S_OK; +} + +void parser_release(parser_ctx_t *ctx) +{ + heap_pool_free(&ctx->heap); +} + diff --git a/reactos/dll/win32/vbscript/parser.tab.h b/reactos/dll/win32/vbscript/parser.tab.h new file mode 100644 index 00000000000..31352892cd2 --- /dev/null +++ b/reactos/dll/win32/vbscript/parser.tab.h @@ -0,0 +1,150 @@ + +/* A Bison parser, made by GNU Bison 2.4.1. */ + +/* Skeleton interface for Bison's Yacc-like parsers in C + + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003, 2004, 2005, 2006 + Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* As a special exception, you may create a larger work that contains + part or all of the Bison parser skeleton and distribute that work + under terms of your choice, so long as that work isn't itself a + parser generator using the skeleton or a modified version thereof + as a parser skeleton. Alternatively, if you modify or redistribute + the parser skeleton itself, you may (at your option) remove this + special exception, which will cause the skeleton and the resulting + Bison output files to be licensed under the GNU General Public + License without this special exception. + + This special exception was added by the Free Software Foundation in + version 2.2 of Bison. */ + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + tEOF = 258, + tNL = 259, + tREM = 260, + tEMPTYBRACKETS = 261, + tTRUE = 262, + tFALSE = 263, + tNOT = 264, + tAND = 265, + tOR = 266, + tXOR = 267, + tEQV = 268, + tIMP = 269, + tNEQ = 270, + tIS = 271, + tLTEQ = 272, + tGTEQ = 273, + tMOD = 274, + tCALL = 275, + tDIM = 276, + tSUB = 277, + tFUNCTION = 278, + tPROPERTY = 279, + tGET = 280, + tLET = 281, + tCONST = 282, + tIF = 283, + tELSE = 284, + tELSEIF = 285, + tEND = 286, + tTHEN = 287, + tEXIT = 288, + tWHILE = 289, + tWEND = 290, + tDO = 291, + tLOOP = 292, + tUNTIL = 293, + tFOR = 294, + tTO = 295, + tSTEP = 296, + tEACH = 297, + tIN = 298, + tSELECT = 299, + tCASE = 300, + tBYREF = 301, + tBYVAL = 302, + tOPTION = 303, + tEXPLICIT = 304, + tSTOP = 305, + tNOTHING = 306, + tEMPTY = 307, + tNULL = 308, + tCLASS = 309, + tSET = 310, + tNEW = 311, + tPUBLIC = 312, + tPRIVATE = 313, + tDEFAULT = 314, + tME = 315, + tERROR = 316, + tNEXT = 317, + tON = 318, + tRESUME = 319, + tGOTO = 320, + tIdentifier = 321, + tString = 322, + tLong = 323, + tShort = 324, + tDouble = 325 + }; +#endif + + + +#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED +typedef union YYSTYPE +{ + +/* Line 1676 of yacc.c */ +#line 88 "parser.y" + + const WCHAR *string; + statement_t *statement; + expression_t *expression; + member_expression_t *member; + elseif_decl_t *elseif; + dim_decl_t *dim_decl; + function_decl_t *func_decl; + arg_decl_t *arg_decl; + class_decl_t *class_decl; + const_decl_t *const_decl; + case_clausule_t *case_clausule; + unsigned uint; + LONG lng; + BOOL bool; + double dbl; + + + +/* Line 1676 of yacc.c */ +#line 142 "parser.tab.h" +} YYSTYPE; +# define YYSTYPE_IS_TRIVIAL 1 +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +#endif + + + + diff --git a/reactos/dll/win32/vbscript/parser.y b/reactos/dll/win32/vbscript/parser.y new file mode 100644 index 00000000000..4b380ef0bc0 --- /dev/null +++ b/reactos/dll/win32/vbscript/parser.y @@ -0,0 +1,965 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +%{ + +#include "vbscript.h" +#include "parse.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(vbscript); + +#define YYLEX_PARAM ctx +#define YYPARSE_PARAM ctx + +static int parser_error(const char*); + +static void parse_complete(parser_ctx_t*,BOOL); + +static void source_add_statement(parser_ctx_t*,statement_t*); +static void source_add_class(parser_ctx_t*,class_decl_t*); + +static void *new_expression(parser_ctx_t*,expression_type_t,size_t); +static expression_t *new_bool_expression(parser_ctx_t*,VARIANT_BOOL); +static expression_t *new_string_expression(parser_ctx_t*,const WCHAR*); +static expression_t *new_long_expression(parser_ctx_t*,expression_type_t,LONG); +static expression_t *new_double_expression(parser_ctx_t*,double); +static expression_t *new_unary_expression(parser_ctx_t*,expression_type_t,expression_t*); +static expression_t *new_binary_expression(parser_ctx_t*,expression_type_t,expression_t*,expression_t*); +static expression_t *new_new_expression(parser_ctx_t*,const WCHAR*); + +static member_expression_t *new_member_expression(parser_ctx_t*,expression_t*,const WCHAR*); + +static void *new_statement(parser_ctx_t*,statement_type_t,size_t); +static statement_t *new_call_statement(parser_ctx_t*,BOOL,member_expression_t*); +static statement_t *new_assign_statement(parser_ctx_t*,member_expression_t*,expression_t*); +static statement_t *new_set_statement(parser_ctx_t*,member_expression_t*,expression_t*); +static statement_t *new_dim_statement(parser_ctx_t*,dim_decl_t*); +static statement_t *new_while_statement(parser_ctx_t*,statement_type_t,expression_t*,statement_t*); +static statement_t *new_forto_statement(parser_ctx_t*,const WCHAR*,expression_t*,expression_t*,expression_t*,statement_t*); +static statement_t *new_foreach_statement(parser_ctx_t*,const WCHAR*,expression_t*,statement_t*); +static statement_t *new_if_statement(parser_ctx_t*,expression_t*,statement_t*,elseif_decl_t*,statement_t*); +static statement_t *new_function_statement(parser_ctx_t*,function_decl_t*); +static statement_t *new_onerror_statement(parser_ctx_t*,BOOL); +static statement_t *new_const_statement(parser_ctx_t*,const_decl_t*); +static statement_t *new_select_statement(parser_ctx_t*,expression_t*,case_clausule_t*); + +static dim_decl_t *new_dim_decl(parser_ctx_t*,const WCHAR*,dim_decl_t*); +static elseif_decl_t *new_elseif_decl(parser_ctx_t*,expression_t*,statement_t*); +static function_decl_t *new_function_decl(parser_ctx_t*,const WCHAR*,function_type_t,unsigned,arg_decl_t*,statement_t*); +static arg_decl_t *new_argument_decl(parser_ctx_t*,const WCHAR*,BOOL); +static const_decl_t *new_const_decl(parser_ctx_t*,const WCHAR*,expression_t*); +static case_clausule_t *new_case_clausule(parser_ctx_t*,expression_t*,statement_t*,case_clausule_t*); + +static class_decl_t *new_class_decl(parser_ctx_t*); +static class_decl_t *add_class_function(parser_ctx_t*,class_decl_t*,function_decl_t*); +static class_decl_t *add_variant_prop(parser_ctx_t*,class_decl_t*,const WCHAR*,unsigned); + +static statement_t *link_statements(statement_t*,statement_t*); + +static const WCHAR propertyW[] = {'p','r','o','p','e','r','t','y',0}; + +#define STORAGE_IS_PRIVATE 1 +#define STORAGE_IS_DEFAULT 2 + +#define CHECK_ERROR if(((parser_ctx_t*)ctx)->hres != S_OK) YYABORT + +%} + +%pure_parser +%start Program + +%union { + const WCHAR *string; + statement_t *statement; + expression_t *expression; + member_expression_t *member; + elseif_decl_t *elseif; + dim_decl_t *dim_decl; + function_decl_t *func_decl; + arg_decl_t *arg_decl; + class_decl_t *class_decl; + const_decl_t *const_decl; + case_clausule_t *case_clausule; + unsigned uint; + LONG lng; + BOOL bool; + double dbl; +} + +%token tEOF tNL tREM tEMPTYBRACKETS +%token tTRUE tFALSE +%token tNOT tAND tOR tXOR tEQV tIMP tNEQ +%token tIS tLTEQ tGTEQ tMOD +%token tCALL tDIM tSUB tFUNCTION tPROPERTY tGET tLET tCONST +%token tIF tELSE tELSEIF tEND tTHEN tEXIT +%token tWHILE tWEND tDO tLOOP tUNTIL tFOR tTO tSTEP tEACH tIN +%token tSELECT tCASE +%token tBYREF tBYVAL +%token tOPTION tEXPLICIT +%token tSTOP +%token tNOTHING tEMPTY tNULL +%token tCLASS tSET tNEW tPUBLIC tPRIVATE tDEFAULT tME +%token tERROR tNEXT tON tRESUME tGOTO +%token tIdentifier tString +%token tLong tShort +%token tDouble + +%type Statement SimpleStatement StatementNl StatementsNl StatementsNl_opt IfStatement Else_opt +%type Expression LiteralExpression PrimaryExpression EqualityExpression CallExpression +%type ConcatExpression AdditiveExpression ModExpression IntdivExpression MultiplicativeExpression ExpExpression +%type NotExpression UnaryExpression AndExpression OrExpression XorExpression EqvExpression +%type ConstExpression NumericLiteralExpression +%type MemberExpression +%type Arguments_opt ArgumentList_opt Step_opt ExpressionList +%type OptionExplicit_opt DoType +%type ArgumentsDecl_opt ArgumentDeclList ArgumentDecl +%type FunctionDecl PropertyDecl +%type ElseIfs_opt ElseIfs ElseIf +%type ClassDeclaration ClassBody +%type Storage Storage_opt +%type DimDeclList +%type ConstDecl ConstDeclList +%type Identifier +%type CaseClausules + +%% + +Program + : OptionExplicit_opt SourceElements tEOF { parse_complete(ctx, $1); } + +OptionExplicit_opt + : /* empty */ { $$ = FALSE; } + | tOPTION tEXPLICIT tNL { $$ = TRUE; } + +SourceElements + : /* empty */ + | SourceElements StatementNl { source_add_statement(ctx, $2); } + | SourceElements ClassDeclaration { source_add_class(ctx, $2); } + +StatementsNl_opt + : /* empty */ { $$ = NULL; } + | StatementsNl { $$ = $1; } + +StatementsNl + : StatementNl { $$ = $1; } + | StatementNl StatementsNl { $$ = link_statements($1, $2); } + +StatementNl + : Statement tNL { $$ = $1; } + +Statement + : ':' { $$ = NULL; } + | ':' Statement { $$ = $2; } + | SimpleStatement { $$ = $1; } + | SimpleStatement ':' Statement { $1->next = $3; $$ = $1; } + | SimpleStatement ':' { $$ = $1; } + +SimpleStatement + : MemberExpression ArgumentList_opt { $1->args = $2; $$ = new_call_statement(ctx, FALSE, $1); CHECK_ERROR; } + | tCALL MemberExpression Arguments_opt { $2->args = $3; $$ = new_call_statement(ctx, TRUE, $2); CHECK_ERROR; } + | MemberExpression Arguments_opt '=' Expression + { $1->args = $2; $$ = new_assign_statement(ctx, $1, $4); CHECK_ERROR; } + | tDIM DimDeclList { $$ = new_dim_statement(ctx, $2); CHECK_ERROR; } + | IfStatement { $$ = $1; } + | tWHILE Expression tNL StatementsNl_opt tWEND + { $$ = new_while_statement(ctx, STAT_WHILE, $2, $4); CHECK_ERROR; } + | tDO DoType Expression tNL StatementsNl_opt tLOOP + { $$ = new_while_statement(ctx, $2 ? STAT_WHILELOOP : STAT_UNTIL, $3, $5); + CHECK_ERROR; } + | tDO tNL StatementsNl_opt tLOOP DoType Expression + { $$ = new_while_statement(ctx, $5 ? STAT_DOWHILE : STAT_DOUNTIL, $6, $3); + CHECK_ERROR; } + | tDO tNL StatementsNl_opt tLOOP { $$ = new_while_statement(ctx, STAT_DOWHILE, NULL, $3); CHECK_ERROR; } + | FunctionDecl { $$ = new_function_statement(ctx, $1); CHECK_ERROR; } + | tEXIT tDO { $$ = new_statement(ctx, STAT_EXITDO, 0); CHECK_ERROR; } + | tEXIT tFOR { $$ = new_statement(ctx, STAT_EXITFOR, 0); CHECK_ERROR; } + | tEXIT tFUNCTION { $$ = new_statement(ctx, STAT_EXITFUNC, 0); CHECK_ERROR; } + | tEXIT tPROPERTY { $$ = new_statement(ctx, STAT_EXITPROP, 0); CHECK_ERROR; } + | tEXIT tSUB { $$ = new_statement(ctx, STAT_EXITSUB, 0); CHECK_ERROR; } + | tSET MemberExpression Arguments_opt '=' Expression + { $2->args = $3; $$ = new_set_statement(ctx, $2, $5); CHECK_ERROR; } + | tSTOP { $$ = new_statement(ctx, STAT_STOP, 0); CHECK_ERROR; } + | tON tERROR tRESUME tNEXT { $$ = new_onerror_statement(ctx, TRUE); CHECK_ERROR; } + | tON tERROR tGOTO '0' { $$ = new_onerror_statement(ctx, FALSE); CHECK_ERROR; } + | tCONST ConstDeclList { $$ = new_const_statement(ctx, $2); CHECK_ERROR; } + | tFOR Identifier '=' Expression tTO Expression Step_opt tNL StatementsNl_opt tNEXT + { $$ = new_forto_statement(ctx, $2, $4, $6, $7, $9); CHECK_ERROR; } + | tFOR tEACH Identifier tIN Expression tNL StatementsNl_opt tNEXT + { $$ = new_foreach_statement(ctx, $3, $5, $7); } + | tSELECT tCASE Expression tNL CaseClausules tEND tSELECT + { $$ = new_select_statement(ctx, $3, $5); } + +MemberExpression + : Identifier { $$ = new_member_expression(ctx, NULL, $1); CHECK_ERROR; } + | CallExpression '.' Identifier { $$ = new_member_expression(ctx, $1, $3); CHECK_ERROR; } + +DimDeclList /* FIXME: Support arrays */ + : Identifier { $$ = new_dim_decl(ctx, $1, NULL); CHECK_ERROR; } + | Identifier ',' DimDeclList { $$ = new_dim_decl(ctx, $1, $3); CHECK_ERROR; } + +ConstDeclList + : ConstDecl { $$ = $1; } + | ConstDecl ',' ConstDeclList { $1->next = $3; $$ = $1; } + +ConstDecl + : Identifier '=' ConstExpression { $$ = new_const_decl(ctx, $1, $3); CHECK_ERROR; } + +ConstExpression + : LiteralExpression { $$ = $1; } + | '-' NumericLiteralExpression { $$ = new_unary_expression(ctx, EXPR_NEG, $2); CHECK_ERROR; } + +DoType + : tWHILE { $$ = TRUE; } + | tUNTIL { $$ = FALSE; } + +Step_opt + : /* empty */ { $$ = NULL;} + | tSTEP Expression { $$ = $2; } + +IfStatement + : tIF Expression tTHEN tNL StatementsNl_opt ElseIfs_opt Else_opt tEND tIF + { $$ = new_if_statement(ctx, $2, $5, $6, $7); CHECK_ERROR; } + | tIF Expression tTHEN Statement { $$ = new_if_statement(ctx, $2, $4, NULL, NULL); CHECK_ERROR; } + | tIF Expression tTHEN Statement tELSE Statement EndIf_opt + { $$ = new_if_statement(ctx, $2, $4, NULL, $6); CHECK_ERROR; } + +EndIf_opt + : /* empty */ + | tEND tIF + +ElseIfs_opt + : /* empty */ { $$ = NULL; } + | ElseIfs { $$ = $1; } + +ElseIfs + : ElseIf { $$ = $1; } + | ElseIf ElseIfs { $1->next = $2; $$ = $1; } + +ElseIf + : tELSEIF Expression tTHEN tNL StatementsNl_opt + { $$ = new_elseif_decl(ctx, $2, $5); } + +Else_opt + : /* empty */ { $$ = NULL; } + | tELSE tNL StatementsNl_opt { $$ = $3; } + +CaseClausules + : /* empty */ { $$ = NULL; } + | tCASE tELSE tNL StatementsNl { $$ = new_case_clausule(ctx, NULL, $4, NULL); } + | tCASE ExpressionList tNL StatementsNl_opt CaseClausules + { $$ = new_case_clausule(ctx, $2, $4, $5); } + +Arguments_opt + : EmptyBrackets_opt { $$ = NULL; } + | '(' ExpressionList ')' { $$ = $2; } + +ArgumentList_opt + : EmptyBrackets_opt { $$ = NULL; } + | ExpressionList { $$ = $1; } + +EmptyBrackets_opt + : /* empty */ + | tEMPTYBRACKETS + +ExpressionList + : Expression { $$ = $1; } + | Expression ',' ExpressionList { $1->next = $3; $$ = $1; } + +Expression + : EqvExpression { $$ = $1; } + | Expression tIMP EqvExpression { $$ = new_binary_expression(ctx, EXPR_IMP, $1, $3); CHECK_ERROR; } + +EqvExpression + : XorExpression { $$ = $1; } + | EqvExpression tEQV XorExpression { $$ = new_binary_expression(ctx, EXPR_EQV, $1, $3); CHECK_ERROR; } + +XorExpression + : OrExpression { $$ = $1; } + | XorExpression tXOR OrExpression { $$ = new_binary_expression(ctx, EXPR_XOR, $1, $3); CHECK_ERROR; } + +OrExpression + : AndExpression { $$ = $1; } + | OrExpression tOR AndExpression { $$ = new_binary_expression(ctx, EXPR_OR, $1, $3); CHECK_ERROR; } + +AndExpression + : NotExpression { $$ = $1; } + | AndExpression tAND NotExpression { $$ = new_binary_expression(ctx, EXPR_AND, $1, $3); CHECK_ERROR; } + +NotExpression + : EqualityExpression { $$ = $1; } + | tNOT NotExpression { $$ = new_unary_expression(ctx, EXPR_NOT, $2); CHECK_ERROR; } + +EqualityExpression + : ConcatExpression { $$ = $1; } + | EqualityExpression '=' ConcatExpression { $$ = new_binary_expression(ctx, EXPR_EQUAL, $1, $3); CHECK_ERROR; } + | EqualityExpression tNEQ ConcatExpression { $$ = new_binary_expression(ctx, EXPR_NEQUAL, $1, $3); CHECK_ERROR; } + | EqualityExpression '>' ConcatExpression { $$ = new_binary_expression(ctx, EXPR_GT, $1, $3); CHECK_ERROR; } + | EqualityExpression '<' ConcatExpression { $$ = new_binary_expression(ctx, EXPR_LT, $1, $3); CHECK_ERROR; } + | EqualityExpression tGTEQ ConcatExpression { $$ = new_binary_expression(ctx, EXPR_GTEQ, $1, $3); CHECK_ERROR; } + | EqualityExpression tLTEQ ConcatExpression { $$ = new_binary_expression(ctx, EXPR_LTEQ, $1, $3); CHECK_ERROR; } + | EqualityExpression tIS ConcatExpression { $$ = new_binary_expression(ctx, EXPR_IS, $1, $3); CHECK_ERROR; } + +ConcatExpression + : AdditiveExpression { $$ = $1; } + | ConcatExpression '&' AdditiveExpression { $$ = new_binary_expression(ctx, EXPR_CONCAT, $1, $3); CHECK_ERROR; } + +AdditiveExpression + : ModExpression { $$ = $1; } + | AdditiveExpression '+' ModExpression { $$ = new_binary_expression(ctx, EXPR_ADD, $1, $3); CHECK_ERROR; } + | AdditiveExpression '-' ModExpression { $$ = new_binary_expression(ctx, EXPR_SUB, $1, $3); CHECK_ERROR; } + +ModExpression + : IntdivExpression { $$ = $1; } + | ModExpression tMOD IntdivExpression { $$ = new_binary_expression(ctx, EXPR_MOD, $1, $3); CHECK_ERROR; } + +IntdivExpression + : MultiplicativeExpression { $$ = $1; } + | IntdivExpression '\\' MultiplicativeExpression + { $$ = new_binary_expression(ctx, EXPR_IDIV, $1, $3); CHECK_ERROR; } + +MultiplicativeExpression + : ExpExpression { $$ = $1; } + | MultiplicativeExpression '*' ExpExpression + { $$ = new_binary_expression(ctx, EXPR_MUL, $1, $3); CHECK_ERROR; } + | MultiplicativeExpression '/' ExpExpression + { $$ = new_binary_expression(ctx, EXPR_DIV, $1, $3); CHECK_ERROR; } + +ExpExpression + : UnaryExpression { $$ = $1; } + | ExpExpression '^' UnaryExpression { $$ = new_binary_expression(ctx, EXPR_EXP, $1, $3); CHECK_ERROR; } + +UnaryExpression + : LiteralExpression { $$ = $1; } + | CallExpression { $$ = $1; } + | tNEW Identifier { $$ = new_new_expression(ctx, $2); CHECK_ERROR; } + | '-' UnaryExpression { $$ = new_unary_expression(ctx, EXPR_NEG, $2); CHECK_ERROR; } + +CallExpression + : PrimaryExpression { $$ = $1; } + | MemberExpression Arguments_opt { $1->args = $2; $$ = &$1->expr; } + +LiteralExpression + : tTRUE { $$ = new_bool_expression(ctx, VARIANT_TRUE); CHECK_ERROR; } + | tFALSE { $$ = new_bool_expression(ctx, VARIANT_FALSE); CHECK_ERROR; } + | tString { $$ = new_string_expression(ctx, $1); CHECK_ERROR; } + | NumericLiteralExpression { $$ = $1; } + | tEMPTY { $$ = new_expression(ctx, EXPR_EMPTY, 0); CHECK_ERROR; } + | tNULL { $$ = new_expression(ctx, EXPR_NULL, 0); CHECK_ERROR; } + | tNOTHING { $$ = new_expression(ctx, EXPR_NOTHING, 0); CHECK_ERROR; } + +NumericLiteralExpression + : tShort { $$ = new_long_expression(ctx, EXPR_USHORT, $1); CHECK_ERROR; } + | '0' { $$ = new_long_expression(ctx, EXPR_USHORT, 0); CHECK_ERROR; } + | tLong { $$ = new_long_expression(ctx, EXPR_ULONG, $1); CHECK_ERROR; } + | tDouble { $$ = new_double_expression(ctx, $1); CHECK_ERROR; } + + +PrimaryExpression + : '(' Expression ')' { $$ = new_unary_expression(ctx, EXPR_BRACKETS, $2); } + | tME { $$ = new_expression(ctx, EXPR_ME, 0); CHECK_ERROR; } + +ClassDeclaration + : tCLASS Identifier tNL ClassBody tEND tCLASS tNL { $4->name = $2; $$ = $4; } + +ClassBody + : /* empty */ { $$ = new_class_decl(ctx); } + | FunctionDecl tNL ClassBody { $$ = add_class_function(ctx, $3, $1); CHECK_ERROR; } + | Storage tIdentifier tNL ClassBody { $$ = add_variant_prop(ctx, $4, $2, $1); CHECK_ERROR; } + | PropertyDecl tNL ClassBody { $$ = add_class_function(ctx, $3, $1); CHECK_ERROR; } + +PropertyDecl + : Storage_opt tPROPERTY tGET tIdentifier EmptyBrackets_opt tNL StatementsNl_opt tEND tPROPERTY + { $$ = new_function_decl(ctx, $4, FUNC_PROPGET, $1, NULL, $7); CHECK_ERROR; } + | Storage_opt tPROPERTY tLET tIdentifier '(' ArgumentDecl ')' tNL StatementsNl_opt tEND tPROPERTY + { $$ = new_function_decl(ctx, $4, FUNC_PROPLET, $1, $6, $9); CHECK_ERROR; } + | Storage_opt tPROPERTY tSET tIdentifier '(' ArgumentDecl ')' tNL StatementsNl_opt tEND tPROPERTY + { $$ = new_function_decl(ctx, $4, FUNC_PROPSET, $1, $6, $9); CHECK_ERROR; } + +FunctionDecl + : Storage_opt tSUB Identifier ArgumentsDecl_opt tNL StatementsNl_opt tEND tSUB + { $$ = new_function_decl(ctx, $3, FUNC_SUB, $1, $4, $6); CHECK_ERROR; } + | Storage_opt tFUNCTION Identifier ArgumentsDecl_opt tNL StatementsNl_opt tEND tFUNCTION + { $$ = new_function_decl(ctx, $3, FUNC_FUNCTION, $1, $4, $6); CHECK_ERROR; } + +Storage_opt + : /* empty*/ { $$ = 0; } + | Storage { $$ = $1; } + +Storage + : tPUBLIC tDEFAULT { $$ = STORAGE_IS_DEFAULT; } + | tPUBLIC { $$ = 0; } + | tPRIVATE { $$ = STORAGE_IS_PRIVATE; } + +ArgumentsDecl_opt + : EmptyBrackets_opt { $$ = NULL; } + | '(' ArgumentDeclList ')' { $$ = $2; } + +ArgumentDeclList + : ArgumentDecl { $$ = $1; } + | ArgumentDecl ',' ArgumentDeclList { $1->next = $3; $$ = $1; } + +ArgumentDecl + : Identifier { $$ = new_argument_decl(ctx, $1, TRUE); } + | tBYREF Identifier { $$ = new_argument_decl(ctx, $2, TRUE); } + | tBYVAL Identifier { $$ = new_argument_decl(ctx, $2, FALSE); } + +/* 'property' may be both keyword and identifier, depending on context */ +Identifier + : tIdentifier { $$ = $1; } + | tPROPERTY { $$ = propertyW; } +%% + +static int parser_error(const char *str) +{ + return 0; +} + +static void source_add_statement(parser_ctx_t *ctx, statement_t *stat) +{ + if(!stat) + return; + + if(ctx->stats) { + ctx->stats_tail->next = stat; + ctx->stats_tail = stat; + }else { + ctx->stats = ctx->stats_tail = stat; + } +} + +static void source_add_class(parser_ctx_t *ctx, class_decl_t *class_decl) +{ + class_decl->next = ctx->class_decls; + ctx->class_decls = class_decl; +} + +static void parse_complete(parser_ctx_t *ctx, BOOL option_explicit) +{ + ctx->parse_complete = TRUE; + ctx->option_explicit = option_explicit; +} + +static void *new_expression(parser_ctx_t *ctx, expression_type_t type, size_t size) +{ + expression_t *expr; + + expr = parser_alloc(ctx, size ? size : sizeof(*expr)); + if(expr) { + expr->type = type; + expr->next = NULL; + } + + return expr; +} + +static expression_t *new_bool_expression(parser_ctx_t *ctx, VARIANT_BOOL value) +{ + bool_expression_t *expr; + + expr = new_expression(ctx, EXPR_BOOL, sizeof(*expr)); + if(!expr) + return NULL; + + expr->value = value; + return &expr->expr; +} + +static expression_t *new_string_expression(parser_ctx_t *ctx, const WCHAR *value) +{ + string_expression_t *expr; + + expr = new_expression(ctx, EXPR_STRING, sizeof(*expr)); + if(!expr) + return NULL; + + expr->value = value; + return &expr->expr; +} + +static expression_t *new_long_expression(parser_ctx_t *ctx, expression_type_t type, LONG value) +{ + int_expression_t *expr; + + expr = new_expression(ctx, type, sizeof(*expr)); + if(!expr) + return NULL; + + expr->value = value; + return &expr->expr; +} + +static expression_t *new_double_expression(parser_ctx_t *ctx, double value) +{ + double_expression_t *expr; + + expr = new_expression(ctx, EXPR_DOUBLE, sizeof(*expr)); + if(!expr) + return NULL; + + expr->value = value; + return &expr->expr; +} + +static expression_t *new_unary_expression(parser_ctx_t *ctx, expression_type_t type, expression_t *subexpr) +{ + unary_expression_t *expr; + + expr = new_expression(ctx, type, sizeof(*expr)); + if(!expr) + return NULL; + + expr->subexpr = subexpr; + return &expr->expr; +} + +static expression_t *new_binary_expression(parser_ctx_t *ctx, expression_type_t type, expression_t *left, expression_t *right) +{ + binary_expression_t *expr; + + expr = new_expression(ctx, type, sizeof(*expr)); + if(!expr) + return NULL; + + expr->left = left; + expr->right = right; + return &expr->expr; +} + +static member_expression_t *new_member_expression(parser_ctx_t *ctx, expression_t *obj_expr, const WCHAR *identifier) +{ + member_expression_t *expr; + + expr = new_expression(ctx, EXPR_MEMBER, sizeof(*expr)); + if(!expr) + return NULL; + + expr->obj_expr = obj_expr; + expr->identifier = identifier; + expr->args = NULL; + return expr; +} + +static expression_t *new_new_expression(parser_ctx_t *ctx, const WCHAR *identifier) +{ + string_expression_t *expr; + + expr = new_expression(ctx, EXPR_NEW, sizeof(*expr)); + if(!expr) + return NULL; + + expr->value = identifier; + return &expr->expr; +} + +static void *new_statement(parser_ctx_t *ctx, statement_type_t type, size_t size) +{ + statement_t *stat; + + stat = parser_alloc(ctx, size ? size : sizeof(*stat)); + if(stat) { + stat->type = type; + stat->next = NULL; + } + + return stat; +} + +static statement_t *new_call_statement(parser_ctx_t *ctx, BOOL is_strict, member_expression_t *expr) +{ + call_statement_t *stat; + + stat = new_statement(ctx, STAT_CALL, sizeof(*stat)); + if(!stat) + return NULL; + + stat->expr = expr; + stat->is_strict = is_strict; + return &stat->stat; +} + +static statement_t *new_assign_statement(parser_ctx_t *ctx, member_expression_t *left, expression_t *right) +{ + assign_statement_t *stat; + + stat = new_statement(ctx, STAT_ASSIGN, sizeof(*stat)); + if(!stat) + return NULL; + + stat->member_expr = left; + stat->value_expr = right; + return &stat->stat; +} + +static statement_t *new_set_statement(parser_ctx_t *ctx, member_expression_t *left, expression_t *right) +{ + assign_statement_t *stat; + + stat = new_statement(ctx, STAT_SET, sizeof(*stat)); + if(!stat) + return NULL; + + stat->member_expr = left; + stat->value_expr = right; + return &stat->stat; +} + +static dim_decl_t *new_dim_decl(parser_ctx_t *ctx, const WCHAR *name, dim_decl_t *next) +{ + dim_decl_t *decl; + + decl = parser_alloc(ctx, sizeof(*decl)); + if(!decl) + return NULL; + + decl->name = name; + decl->next = next; + return decl; +} + +static statement_t *new_dim_statement(parser_ctx_t *ctx, dim_decl_t *decls) +{ + dim_statement_t *stat; + + stat = new_statement(ctx, STAT_DIM, sizeof(*stat)); + if(!stat) + return NULL; + + stat->dim_decls = decls; + return &stat->stat; +} + +static elseif_decl_t *new_elseif_decl(parser_ctx_t *ctx, expression_t *expr, statement_t *stat) +{ + elseif_decl_t *decl; + + decl = parser_alloc(ctx, sizeof(*decl)); + if(!decl) + return NULL; + + decl->expr = expr; + decl->stat = stat; + decl->next = NULL; + return decl; +} + +static statement_t *new_while_statement(parser_ctx_t *ctx, statement_type_t type, expression_t *expr, statement_t *body) +{ + while_statement_t *stat; + + stat = new_statement(ctx, type, sizeof(*stat)); + if(!stat) + return NULL; + + stat->expr = expr; + stat->body = body; + return &stat->stat; +} + +static statement_t *new_forto_statement(parser_ctx_t *ctx, const WCHAR *identifier, expression_t *from_expr, + expression_t *to_expr, expression_t *step_expr, statement_t *body) +{ + forto_statement_t *stat; + + stat = new_statement(ctx, STAT_FORTO, sizeof(*stat)); + if(!stat) + return NULL; + + stat->identifier = identifier; + stat->from_expr = from_expr; + stat->to_expr = to_expr; + stat->step_expr = step_expr; + stat->body = body; + return &stat->stat; +} + +static statement_t *new_foreach_statement(parser_ctx_t *ctx, const WCHAR *identifier, expression_t *group_expr, + statement_t *body) +{ + foreach_statement_t *stat; + + stat = new_statement(ctx, STAT_FOREACH, sizeof(*stat)); + if(!stat) + return NULL; + + stat->identifier = identifier; + stat->group_expr = group_expr; + stat->body = body; + return &stat->stat; +} + +static statement_t *new_if_statement(parser_ctx_t *ctx, expression_t *expr, statement_t *if_stat, elseif_decl_t *elseif_decl, + statement_t *else_stat) +{ + if_statement_t *stat; + + stat = new_statement(ctx, STAT_IF, sizeof(*stat)); + if(!stat) + return NULL; + + stat->expr = expr; + stat->if_stat = if_stat; + stat->elseifs = elseif_decl; + stat->else_stat = else_stat; + return &stat->stat; +} + +static statement_t *new_select_statement(parser_ctx_t *ctx, expression_t *expr, case_clausule_t *case_clausules) +{ + select_statement_t *stat; + + stat = new_statement(ctx, STAT_SELECT, sizeof(*stat)); + if(!stat) + return NULL; + + stat->expr = expr; + stat->case_clausules = case_clausules; + return &stat->stat; +} + +static case_clausule_t *new_case_clausule(parser_ctx_t *ctx, expression_t *expr, statement_t *stat, case_clausule_t *next) +{ + case_clausule_t *ret; + + ret = parser_alloc(ctx, sizeof(*ret)); + if(!ret) + return NULL; + + ret->expr = expr; + ret->stat = stat; + ret->next = next; + return ret; +} + +static statement_t *new_onerror_statement(parser_ctx_t *ctx, BOOL resume_next) +{ + onerror_statement_t *stat; + + stat = new_statement(ctx, STAT_ONERROR, sizeof(*stat)); + if(!stat) + return NULL; + + stat->resume_next = resume_next; + return &stat->stat; +} + +static arg_decl_t *new_argument_decl(parser_ctx_t *ctx, const WCHAR *name, BOOL by_ref) +{ + arg_decl_t *arg_decl; + + arg_decl = parser_alloc(ctx, sizeof(*arg_decl)); + if(!arg_decl) + return NULL; + + arg_decl->name = name; + arg_decl->by_ref = by_ref; + arg_decl->next = NULL; + return arg_decl; +} + +static function_decl_t *new_function_decl(parser_ctx_t *ctx, const WCHAR *name, function_type_t type, + unsigned storage_flags, arg_decl_t *arg_decl, statement_t *body) +{ + function_decl_t *decl; + + if(storage_flags & STORAGE_IS_DEFAULT) { + if(type == FUNC_PROPGET) { + type = FUNC_DEFGET; + }else { + FIXME("Invalid default property\n"); + ctx->hres = E_FAIL; + return NULL; + } + } + + decl = parser_alloc(ctx, sizeof(*decl)); + if(!decl) + return NULL; + + decl->name = name; + decl->type = type; + decl->is_public = !(storage_flags & STORAGE_IS_PRIVATE); + decl->args = arg_decl; + decl->body = body; + decl->next = NULL; + decl->next_prop_func = NULL; + return decl; +} + +static statement_t *new_function_statement(parser_ctx_t *ctx, function_decl_t *decl) +{ + function_statement_t *stat; + + stat = new_statement(ctx, STAT_FUNC, sizeof(*stat)); + if(!stat) + return NULL; + + stat->func_decl = decl; + return &stat->stat; +} + +static class_decl_t *new_class_decl(parser_ctx_t *ctx) +{ + class_decl_t *class_decl; + + class_decl = parser_alloc(ctx, sizeof(*class_decl)); + if(!class_decl) + return NULL; + + class_decl->funcs = NULL; + class_decl->props = NULL; + class_decl->next = NULL; + return class_decl; +} + +static class_decl_t *add_class_function(parser_ctx_t *ctx, class_decl_t *class_decl, function_decl_t *decl) +{ + function_decl_t *iter; + + for(iter = class_decl->funcs; iter; iter = iter->next) { + if(!strcmpiW(iter->name, decl->name)) { + if(decl->type == FUNC_SUB || decl->type == FUNC_FUNCTION) { + FIXME("Redefinition of %s::%s\n", debugstr_w(class_decl->name), debugstr_w(decl->name)); + ctx->hres = E_FAIL; + return NULL; + } + + while(1) { + if(iter->type == decl->type) { + FIXME("Redefinition of %s::%s\n", debugstr_w(class_decl->name), debugstr_w(decl->name)); + ctx->hres = E_FAIL; + return NULL; + } + if(!iter->next_prop_func) + break; + iter = iter->next_prop_func; + } + + iter->next_prop_func = decl; + return class_decl; + } + } + + decl->next = class_decl->funcs; + class_decl->funcs = decl; + return class_decl; +} + +static class_decl_t *add_variant_prop(parser_ctx_t *ctx, class_decl_t *class_decl, const WCHAR *identifier, unsigned storage_flags) +{ + class_prop_decl_t *prop; + + if(storage_flags & STORAGE_IS_DEFAULT) { + FIXME("variant prop van't be default value\n"); + ctx->hres = E_FAIL; + return NULL; + } + + prop = parser_alloc(ctx, sizeof(*prop)); + if(!prop) + return NULL; + + prop->name = identifier; + prop->is_public = !(storage_flags & STORAGE_IS_PRIVATE); + prop->next = class_decl->props; + class_decl->props = prop; + return class_decl; +} + +static const_decl_t *new_const_decl(parser_ctx_t *ctx, const WCHAR *name, expression_t *expr) +{ + const_decl_t *decl; + + decl = parser_alloc(ctx, sizeof(*decl)); + if(!decl) + return NULL; + + decl->name = name; + decl->value_expr = expr; + decl->next = NULL; + return decl; +} + +static statement_t *new_const_statement(parser_ctx_t *ctx, const_decl_t *decls) +{ + const_statement_t *stat; + + stat = new_statement(ctx, STAT_CONST, sizeof(*stat)); + if(!stat) + return NULL; + + stat->decls = decls; + return &stat->stat; +} + +static statement_t *link_statements(statement_t *head, statement_t *tail) +{ + statement_t *iter; + + for(iter = head; iter->next; iter = iter->next); + iter->next = tail; + + return head; +} + +void *parser_alloc(parser_ctx_t *ctx, size_t size) +{ + void *ret; + + ret = heap_pool_alloc(&ctx->heap, size); + if(!ret) + ctx->hres = E_OUTOFMEMORY; + return ret; +} + +HRESULT parse_script(parser_ctx_t *ctx, const WCHAR *code, const WCHAR *delimiter) +{ + const WCHAR html_delimiterW[] = {'<','/','s','c','r','i','p','t','>',0}; + + ctx->code = ctx->ptr = code; + ctx->end = ctx->code + strlenW(ctx->code); + + heap_pool_init(&ctx->heap); + + ctx->parse_complete = FALSE; + ctx->hres = S_OK; + + ctx->last_token = tNL; + ctx->last_nl = 0; + ctx->stats = ctx->stats_tail = NULL; + ctx->class_decls = NULL; + ctx->option_explicit = FALSE; + ctx->is_html = delimiter && !strcmpiW(delimiter, html_delimiterW); + + parser_parse(ctx); + + if(FAILED(ctx->hres)) + return ctx->hres; + if(!ctx->parse_complete) { + FIXME("parser failed around %s\n", debugstr_w(ctx->code+20 > ctx->ptr ? ctx->code : ctx->ptr-20)); + return E_FAIL; + } + + return S_OK; +} + +void parser_release(parser_ctx_t *ctx) +{ + heap_pool_free(&ctx->heap); +} diff --git a/reactos/dll/win32/vbscript/regexp.c b/reactos/dll/win32/vbscript/regexp.c new file mode 100644 index 00000000000..648c166276d --- /dev/null +++ b/reactos/dll/win32/vbscript/regexp.c @@ -0,0 +1,3306 @@ +/* + * Copyright 2008 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * Code in this file is based on files: + * js/src/jsregexp.h + * js/src/jsregexp.c + * from Mozilla project, released under LGPL 2.1 or later. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + */ + +#include + +#include "vbscript.h" +#include "regexp.h" + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(jscript); + +/* FIXME: Better error handling */ +#define ReportRegExpError(a,b,c) +#define ReportRegExpErrorHelper(a,b,c,d) +#define JS_ReportErrorNumber(a,b,c,d) +#define JS_ReportErrorFlagsAndNumber(a,b,c,d,e,f) +#define js_ReportOutOfScriptQuota(a) +#define JS_ReportOutOfMemory(a) +#define JS_COUNT_OPERATION(a,b) + + +typedef BYTE JSPackedBool; + +/* + * This struct holds a bitmap representation of a class from a regexp. + * There's a list of these referenced by the classList field in the regexp_t + * struct below. The initial state has startIndex set to the offset in the + * original regexp source of the beginning of the class contents. The first + * use of the class converts the source representation into a bitmap. + * + */ +typedef struct RECharSet { + JSPackedBool converted; + JSPackedBool sense; + WORD length; + union { + BYTE *bits; + struct { + size_t startIndex; + size_t length; + } src; + } u; +} RECharSet; + +#define JSMSG_MIN_TOO_BIG 47 +#define JSMSG_MAX_TOO_BIG 48 +#define JSMSG_OUT_OF_ORDER 49 +#define JSMSG_OUT_OF_MEMORY 137 + +#define LINE_SEPARATOR 0x2028 +#define PARA_SEPARATOR 0x2029 + +#define RE_IS_LETTER(c) (((c >= 'A') && (c <= 'Z')) || \ + ((c >= 'a') && (c <= 'z')) ) +#define RE_IS_LINE_TERM(c) ((c == '\n') || (c == '\r') || \ + (c == LINE_SEPARATOR) || (c == PARA_SEPARATOR)) + +#define JS_ISWORD(c) ((c) < 128 && (isalnum(c) || (c) == '_')) + +#define JS7_ISDEC(c) ((((unsigned)(c)) - '0') <= 9) +#define JS7_UNDEC(c) ((c) - '0') + +typedef enum REOp { + REOP_EMPTY, + REOP_BOL, + REOP_EOL, + REOP_WBDRY, + REOP_WNONBDRY, + REOP_DOT, + REOP_DIGIT, + REOP_NONDIGIT, + REOP_ALNUM, + REOP_NONALNUM, + REOP_SPACE, + REOP_NONSPACE, + REOP_BACKREF, + REOP_FLAT, + REOP_FLAT1, + REOP_FLATi, + REOP_FLAT1i, + REOP_UCFLAT1, + REOP_UCFLAT1i, + REOP_UCFLAT, + REOP_UCFLATi, + REOP_CLASS, + REOP_NCLASS, + REOP_ALT, + REOP_QUANT, + REOP_STAR, + REOP_PLUS, + REOP_OPT, + REOP_LPAREN, + REOP_RPAREN, + REOP_JUMP, + REOP_DOTSTAR, + REOP_LPARENNON, + REOP_ASSERT, + REOP_ASSERT_NOT, + REOP_ASSERTTEST, + REOP_ASSERTNOTTEST, + REOP_MINIMALSTAR, + REOP_MINIMALPLUS, + REOP_MINIMALOPT, + REOP_MINIMALQUANT, + REOP_ENDCHILD, + REOP_REPEAT, + REOP_MINIMALREPEAT, + REOP_ALTPREREQ, + REOP_ALTPREREQ2, + REOP_ENDALT, + REOP_CONCAT, + REOP_END, + REOP_LIMIT /* META: no operator >= to this */ +} REOp; + +#define REOP_IS_SIMPLE(op) ((op) <= REOP_NCLASS) + +static const char *reop_names[] = { + "empty", + "bol", + "eol", + "wbdry", + "wnonbdry", + "dot", + "digit", + "nondigit", + "alnum", + "nonalnum", + "space", + "nonspace", + "backref", + "flat", + "flat1", + "flati", + "flat1i", + "ucflat1", + "ucflat1i", + "ucflat", + "ucflati", + "class", + "nclass", + "alt", + "quant", + "star", + "plus", + "opt", + "lparen", + "rparen", + "jump", + "dotstar", + "lparennon", + "assert", + "assert_not", + "asserttest", + "assertnottest", + "minimalstar", + "minimalplus", + "minimalopt", + "minimalquant", + "endchild", + "repeat", + "minimalrepeat", + "altprereq", + "alrprereq2", + "endalt", + "concat", + "end", + NULL +}; + +typedef struct REProgState { + jsbytecode *continue_pc; /* current continuation data */ + jsbytecode continue_op; + ptrdiff_t index; /* progress in text */ + size_t parenSoFar; /* highest indexed paren started */ + union { + struct { + UINT min; /* current quantifier limits */ + UINT max; + } quantifier; + struct { + size_t top; /* backtrack stack state */ + size_t sz; + } assertion; + } u; +} REProgState; + +typedef struct REBackTrackData { + size_t sz; /* size of previous stack entry */ + jsbytecode *backtrack_pc; /* where to backtrack to */ + jsbytecode backtrack_op; + const WCHAR *cp; /* index in text of match at backtrack */ + size_t parenIndex; /* start index of saved paren contents */ + size_t parenCount; /* # of saved paren contents */ + size_t saveStateStackTop; /* number of parent states */ + /* saved parent states follow */ + /* saved paren contents follow */ +} REBackTrackData; + +#define INITIAL_STATESTACK 100 +#define INITIAL_BACKTRACK 8000 + +typedef struct REGlobalData { + void *cx; + regexp_t *regexp; /* the RE in execution */ + BOOL ok; /* runtime error (out_of_memory only?) */ + size_t start; /* offset to start at */ + ptrdiff_t skipped; /* chars skipped anchoring this r.e. */ + const WCHAR *cpbegin; /* text base address */ + const WCHAR *cpend; /* text limit address */ + + REProgState *stateStack; /* stack of state of current parents */ + size_t stateStackTop; + size_t stateStackLimit; + + REBackTrackData *backTrackStack;/* stack of matched-so-far positions */ + REBackTrackData *backTrackSP; + size_t backTrackStackSize; + size_t cursz; /* size of current stack entry */ + size_t backTrackCount; /* how many times we've backtracked */ + size_t backTrackLimit; /* upper limit on backtrack states */ + + heap_pool_t *pool; /* It's faster to use one malloc'd pool + than to malloc/free the three items + that are allocated from this pool */ +} REGlobalData; + +typedef struct RENode RENode; +struct RENode { + REOp op; /* r.e. op bytecode */ + RENode *next; /* next in concatenation order */ + void *kid; /* first operand */ + union { + void *kid2; /* second operand */ + INT num; /* could be a number */ + size_t parenIndex; /* or a parenthesis index */ + struct { /* or a quantifier range */ + UINT min; + UINT max; + JSPackedBool greedy; + } range; + struct { /* or a character class */ + size_t startIndex; + size_t kidlen; /* length of string at kid, in jschars */ + size_t index; /* index into class list */ + WORD bmsize; /* bitmap size, based on max char code */ + JSPackedBool sense; + } ucclass; + struct { /* or a literal sequence */ + WCHAR chr; /* of one character */ + size_t length; /* or many (via the kid) */ + } flat; + struct { + RENode *kid2; /* second operand from ALT */ + WCHAR ch1; /* match char for ALTPREREQ */ + WCHAR ch2; /* ditto, or class index for ALTPREREQ2 */ + } altprereq; + } u; +}; + +#define CLASS_CACHE_SIZE 4 + +typedef struct CompilerState { + void *context; + const WCHAR *cpbegin; + const WCHAR *cpend; + const WCHAR *cp; + size_t parenCount; + size_t classCount; /* number of [] encountered */ + size_t treeDepth; /* maximum depth of parse tree */ + size_t progLength; /* estimated bytecode length */ + RENode *result; + size_t classBitmapsMem; /* memory to hold all class bitmaps */ + struct { + const WCHAR *start; /* small cache of class strings */ + size_t length; /* since they're often the same */ + size_t index; + } classCache[CLASS_CACHE_SIZE]; + WORD flags; + + heap_pool_t *pool; /* It's faster to use one malloc'd pool + than to malloc/free */ +} CompilerState; + +typedef struct EmitStateStackEntry { + jsbytecode *altHead; /* start of REOP_ALT* opcode */ + jsbytecode *nextAltFixup; /* fixup pointer to next-alt offset */ + jsbytecode *nextTermFixup; /* fixup ptr. to REOP_JUMP offset */ + jsbytecode *endTermFixup; /* fixup ptr. to REOPT_ALTPREREQ* offset */ + RENode *continueNode; /* original REOP_ALT* node being stacked */ + jsbytecode continueOp; /* REOP_JUMP or REOP_ENDALT continuation */ + JSPackedBool jumpToJumpFlag; /* true if we've patched jump-to-jump to + avoid 16-bit unsigned offset overflow */ +} EmitStateStackEntry; + +/* + * Immediate operand sizes and getter/setters. Unlike the ones in jsopcode.h, + * the getters and setters take the pc of the offset, not of the opcode before + * the offset. + */ +#define ARG_LEN 2 +#define GET_ARG(pc) ((WORD)(((pc)[0] << 8) | (pc)[1])) +#define SET_ARG(pc, arg) ((pc)[0] = (jsbytecode) ((arg) >> 8), \ + (pc)[1] = (jsbytecode) (arg)) + +#define OFFSET_LEN ARG_LEN +#define OFFSET_MAX ((1 << (ARG_LEN * 8)) - 1) +#define GET_OFFSET(pc) GET_ARG(pc) + +static BOOL ParseRegExp(CompilerState*); + +/* + * Maximum supported tree depth is maximum size of EmitStateStackEntry stack. + * For sanity, we limit it to 2^24 bytes. + */ +#define TREE_DEPTH_MAX ((1 << 24) / sizeof(EmitStateStackEntry)) + +/* + * The maximum memory that can be allocated for class bitmaps. + * For sanity, we limit it to 2^24 bytes. + */ +#define CLASS_BITMAPS_MEM_LIMIT (1 << 24) + +/* + * Functions to get size and write/read bytecode that represent small indexes + * compactly. + * Each byte in the code represent 7-bit chunk of the index. 8th bit when set + * indicates that the following byte brings more bits to the index. Otherwise + * this is the last byte in the index bytecode representing highest index bits. + */ +static size_t +GetCompactIndexWidth(size_t index) +{ + size_t width; + + for (width = 1; (index >>= 7) != 0; ++width) { } + return width; +} + +static inline jsbytecode * +WriteCompactIndex(jsbytecode *pc, size_t index) +{ + size_t next; + + while ((next = index >> 7) != 0) { + *pc++ = (jsbytecode)(index | 0x80); + index = next; + } + *pc++ = (jsbytecode)index; + return pc; +} + +static inline jsbytecode * +ReadCompactIndex(jsbytecode *pc, size_t *result) +{ + size_t nextByte; + + nextByte = *pc++; + if ((nextByte & 0x80) == 0) { + /* + * Short-circuit the most common case when compact index <= 127. + */ + *result = nextByte; + } else { + size_t shift = 7; + *result = 0x7F & nextByte; + do { + nextByte = *pc++; + *result |= (nextByte & 0x7F) << shift; + shift += 7; + } while ((nextByte & 0x80) != 0); + } + return pc; +} + +/* Construct and initialize an RENode, returning NULL for out-of-memory */ +static RENode * +NewRENode(CompilerState *state, REOp op) +{ + RENode *ren; + + ren = heap_pool_alloc(state->pool, sizeof(*ren)); + if (!ren) { + /* js_ReportOutOfScriptQuota(cx); */ + return NULL; + } + ren->op = op; + ren->next = NULL; + ren->kid = NULL; + return ren; +} + +/* + * Validates and converts hex ascii value. + */ +static BOOL +isASCIIHexDigit(WCHAR c, UINT *digit) +{ + UINT cv = c; + + if (cv < '0') + return FALSE; + if (cv <= '9') { + *digit = cv - '0'; + return TRUE; + } + cv |= 0x20; + if (cv >= 'a' && cv <= 'f') { + *digit = cv - 'a' + 10; + return TRUE; + } + return FALSE; +} + +typedef struct { + REOp op; + const WCHAR *errPos; + size_t parenIndex; +} REOpData; + +#define JUMP_OFFSET_HI(off) ((jsbytecode)((off) >> 8)) +#define JUMP_OFFSET_LO(off) ((jsbytecode)(off)) + +static BOOL +SetForwardJumpOffset(jsbytecode *jump, jsbytecode *target) +{ + ptrdiff_t offset = target - jump; + + /* Check that target really points forward. */ + assert(offset >= 2); + if ((size_t)offset > OFFSET_MAX) + return FALSE; + + jump[0] = JUMP_OFFSET_HI(offset); + jump[1] = JUMP_OFFSET_LO(offset); + return TRUE; +} + +/* + * Generate bytecode for the tree rooted at t using an explicit stack instead + * of recursion. + */ +static jsbytecode * +EmitREBytecode(CompilerState *state, regexp_t *re, size_t treeDepth, + jsbytecode *pc, RENode *t) +{ + EmitStateStackEntry *emitStateSP, *emitStateStack; + RECharSet *charSet; + REOp op; + + if (treeDepth == 0) { + emitStateStack = NULL; + } else { + emitStateStack = heap_alloc(sizeof(EmitStateStackEntry) * treeDepth); + if (!emitStateStack) + return NULL; + } + emitStateSP = emitStateStack; + op = t->op; + assert(op < REOP_LIMIT); + + for (;;) { + *pc++ = op; + switch (op) { + case REOP_EMPTY: + --pc; + break; + + case REOP_ALTPREREQ2: + case REOP_ALTPREREQ: + assert(emitStateSP); + emitStateSP->altHead = pc - 1; + emitStateSP->endTermFixup = pc; + pc += OFFSET_LEN; + SET_ARG(pc, t->u.altprereq.ch1); + pc += ARG_LEN; + SET_ARG(pc, t->u.altprereq.ch2); + pc += ARG_LEN; + + emitStateSP->nextAltFixup = pc; /* offset to next alternate */ + pc += OFFSET_LEN; + + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_JUMP; + emitStateSP->jumpToJumpFlag = FALSE; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = t->kid; + op = t->op; + assert(op < REOP_LIMIT); + continue; + + case REOP_JUMP: + emitStateSP->nextTermFixup = pc; /* offset to following term */ + pc += OFFSET_LEN; + if (!SetForwardJumpOffset(emitStateSP->nextAltFixup, pc)) + goto jump_too_big; + emitStateSP->continueOp = REOP_ENDALT; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = t->u.kid2; + op = t->op; + assert(op < REOP_LIMIT); + continue; + + case REOP_ENDALT: + /* + * If we already patched emitStateSP->nextTermFixup to jump to + * a nearer jump, to avoid 16-bit immediate offset overflow, we + * are done here. + */ + if (emitStateSP->jumpToJumpFlag) + break; + + /* + * Fix up the REOP_JUMP offset to go to the op after REOP_ENDALT. + * REOP_ENDALT is executed only on successful match of the last + * alternate in a group. + */ + if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) + goto jump_too_big; + if (t->op != REOP_ALT) { + if (!SetForwardJumpOffset(emitStateSP->endTermFixup, pc)) + goto jump_too_big; + } + + /* + * If the program is bigger than the REOP_JUMP offset range, then + * we must check for alternates before this one that are part of + * the same group, and fix up their jump offsets to target jumps + * close enough to fit in a 16-bit unsigned offset immediate. + */ + if ((size_t)(pc - re->program) > OFFSET_MAX && + emitStateSP > emitStateStack) { + EmitStateStackEntry *esp, *esp2; + jsbytecode *alt, *jump; + ptrdiff_t span, header; + + esp2 = emitStateSP; + alt = esp2->altHead; + for (esp = esp2 - 1; esp >= emitStateStack; --esp) { + if (esp->continueOp == REOP_ENDALT && + !esp->jumpToJumpFlag && + esp->nextTermFixup + OFFSET_LEN == alt && + (size_t)(pc - ((esp->continueNode->op != REOP_ALT) + ? esp->endTermFixup + : esp->nextTermFixup)) > OFFSET_MAX) { + alt = esp->altHead; + jump = esp->nextTermFixup; + + /* + * The span must be 1 less than the distance from + * jump offset to jump offset, so we actually jump + * to a REOP_JUMP bytecode, not to its offset! + */ + for (;;) { + assert(jump < esp2->nextTermFixup); + span = esp2->nextTermFixup - jump - 1; + if ((size_t)span <= OFFSET_MAX) + break; + do { + if (--esp2 == esp) + goto jump_too_big; + } while (esp2->continueOp != REOP_ENDALT); + } + + jump[0] = JUMP_OFFSET_HI(span); + jump[1] = JUMP_OFFSET_LO(span); + + if (esp->continueNode->op != REOP_ALT) { + /* + * We must patch the offset at esp->endTermFixup + * as well, for the REOP_ALTPREREQ{,2} opcodes. + * If we're unlucky and endTermFixup is more than + * OFFSET_MAX bytes from its target, we cheat by + * jumping 6 bytes to the jump whose offset is at + * esp->nextTermFixup, which has the same target. + */ + jump = esp->endTermFixup; + header = esp->nextTermFixup - jump; + span += header; + if ((size_t)span > OFFSET_MAX) + span = header; + + jump[0] = JUMP_OFFSET_HI(span); + jump[1] = JUMP_OFFSET_LO(span); + } + + esp->jumpToJumpFlag = TRUE; + } + } + } + break; + + case REOP_ALT: + assert(emitStateSP); + emitStateSP->altHead = pc - 1; + emitStateSP->nextAltFixup = pc; /* offset to next alternate */ + pc += OFFSET_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_JUMP; + emitStateSP->jumpToJumpFlag = FALSE; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = t->kid; + op = t->op; + assert(op < REOP_LIMIT); + continue; + + case REOP_FLAT: + /* + * Coalesce FLATs if possible and if it would not increase bytecode + * beyond preallocated limit. The latter happens only when bytecode + * size for coalesced string with offset p and length 2 exceeds 6 + * bytes preallocated for 2 single char nodes, i.e. when + * 1 + GetCompactIndexWidth(p) + GetCompactIndexWidth(2) > 6 or + * GetCompactIndexWidth(p) > 4. + * Since when GetCompactIndexWidth(p) <= 4 coalescing of 3 or more + * nodes strictly decreases bytecode size, the check has to be + * done only for the first coalescing. + */ + if (t->kid && + GetCompactIndexWidth((WCHAR*)t->kid - state->cpbegin) <= 4) + { + while (t->next && + t->next->op == REOP_FLAT && + (WCHAR*)t->kid + t->u.flat.length == + t->next->kid) { + t->u.flat.length += t->next->u.flat.length; + t->next = t->next->next; + } + } + if (t->kid && t->u.flat.length > 1) { + pc[-1] = (state->flags & REG_FOLD) ? REOP_FLATi : REOP_FLAT; + pc = WriteCompactIndex(pc, (WCHAR*)t->kid - state->cpbegin); + pc = WriteCompactIndex(pc, t->u.flat.length); + } else if (t->u.flat.chr < 256) { + pc[-1] = (state->flags & REG_FOLD) ? REOP_FLAT1i : REOP_FLAT1; + *pc++ = (jsbytecode) t->u.flat.chr; + } else { + pc[-1] = (state->flags & REG_FOLD) + ? REOP_UCFLAT1i + : REOP_UCFLAT1; + SET_ARG(pc, t->u.flat.chr); + pc += ARG_LEN; + } + break; + + case REOP_LPAREN: + assert(emitStateSP); + pc = WriteCompactIndex(pc, t->u.parenIndex); + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_RPAREN; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = t->kid; + op = t->op; + continue; + + case REOP_RPAREN: + pc = WriteCompactIndex(pc, t->u.parenIndex); + break; + + case REOP_BACKREF: + pc = WriteCompactIndex(pc, t->u.parenIndex); + break; + + case REOP_ASSERT: + assert(emitStateSP); + emitStateSP->nextTermFixup = pc; + pc += OFFSET_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_ASSERTTEST; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = t->kid; + op = t->op; + continue; + + case REOP_ASSERTTEST: + case REOP_ASSERTNOTTEST: + if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) + goto jump_too_big; + break; + + case REOP_ASSERT_NOT: + assert(emitStateSP); + emitStateSP->nextTermFixup = pc; + pc += OFFSET_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_ASSERTNOTTEST; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = t->kid; + op = t->op; + continue; + + case REOP_QUANT: + assert(emitStateSP); + if (t->u.range.min == 0 && t->u.range.max == (UINT)-1) { + pc[-1] = (t->u.range.greedy) ? REOP_STAR : REOP_MINIMALSTAR; + } else if (t->u.range.min == 0 && t->u.range.max == 1) { + pc[-1] = (t->u.range.greedy) ? REOP_OPT : REOP_MINIMALOPT; + } else if (t->u.range.min == 1 && t->u.range.max == (UINT) -1) { + pc[-1] = (t->u.range.greedy) ? REOP_PLUS : REOP_MINIMALPLUS; + } else { + if (!t->u.range.greedy) + pc[-1] = REOP_MINIMALQUANT; + pc = WriteCompactIndex(pc, t->u.range.min); + /* + * Write max + 1 to avoid using size_t(max) + 1 bytes + * for (UINT)-1 sentinel. + */ + pc = WriteCompactIndex(pc, t->u.range.max + 1); + } + emitStateSP->nextTermFixup = pc; + pc += OFFSET_LEN; + emitStateSP->continueNode = t; + emitStateSP->continueOp = REOP_ENDCHILD; + ++emitStateSP; + assert((size_t)(emitStateSP - emitStateStack) <= treeDepth); + t = t->kid; + op = t->op; + continue; + + case REOP_ENDCHILD: + if (!SetForwardJumpOffset(emitStateSP->nextTermFixup, pc)) + goto jump_too_big; + break; + + case REOP_CLASS: + if (!t->u.ucclass.sense) + pc[-1] = REOP_NCLASS; + pc = WriteCompactIndex(pc, t->u.ucclass.index); + charSet = &re->classList[t->u.ucclass.index]; + charSet->converted = FALSE; + charSet->length = t->u.ucclass.bmsize; + charSet->u.src.startIndex = t->u.ucclass.startIndex; + charSet->u.src.length = t->u.ucclass.kidlen; + charSet->sense = t->u.ucclass.sense; + break; + + default: + break; + } + + t = t->next; + if (t) { + op = t->op; + } else { + if (emitStateSP == emitStateStack) + break; + --emitStateSP; + t = emitStateSP->continueNode; + op = (REOp) emitStateSP->continueOp; + } + } + + cleanup: + heap_free(emitStateStack); + return pc; + + jump_too_big: + ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); + pc = NULL; + goto cleanup; +} + +/* + * Process the op against the two top operands, reducing them to a single + * operand in the penultimate slot. Update progLength and treeDepth. + */ +static BOOL +ProcessOp(CompilerState *state, REOpData *opData, RENode **operandStack, + INT operandSP) +{ + RENode *result; + + switch (opData->op) { + case REOP_ALT: + result = NewRENode(state, REOP_ALT); + if (!result) + return FALSE; + result->kid = operandStack[operandSP - 2]; + result->u.kid2 = operandStack[operandSP - 1]; + operandStack[operandSP - 2] = result; + + if (state->treeDepth == TREE_DEPTH_MAX) { + ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); + return FALSE; + } + ++state->treeDepth; + + /* + * Look at both alternates to see if there's a FLAT or a CLASS at + * the start of each. If so, use a prerequisite match. + */ + if (((RENode *) result->kid)->op == REOP_FLAT && + ((RENode *) result->u.kid2)->op == REOP_FLAT && + (state->flags & REG_FOLD) == 0) { + result->op = REOP_ALTPREREQ; + result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; + result->u.altprereq.ch2 = ((RENode *) result->u.kid2)->u.flat.chr; + /* ALTPREREQ, , uch1, uch2, , ..., + JUMP, ... ENDALT */ + state->progLength += 13; + } + else + if (((RENode *) result->kid)->op == REOP_CLASS && + ((RENode *) result->kid)->u.ucclass.index < 256 && + ((RENode *) result->u.kid2)->op == REOP_FLAT && + (state->flags & REG_FOLD) == 0) { + result->op = REOP_ALTPREREQ2; + result->u.altprereq.ch1 = ((RENode *) result->u.kid2)->u.flat.chr; + result->u.altprereq.ch2 = ((RENode *) result->kid)->u.ucclass.index; + /* ALTPREREQ2, , uch1, uch2, , ..., + JUMP, ... ENDALT */ + state->progLength += 13; + } + else + if (((RENode *) result->kid)->op == REOP_FLAT && + ((RENode *) result->u.kid2)->op == REOP_CLASS && + ((RENode *) result->u.kid2)->u.ucclass.index < 256 && + (state->flags & REG_FOLD) == 0) { + result->op = REOP_ALTPREREQ2; + result->u.altprereq.ch1 = ((RENode *) result->kid)->u.flat.chr; + result->u.altprereq.ch2 = + ((RENode *) result->u.kid2)->u.ucclass.index; + /* ALTPREREQ2, , uch1, uch2, , ..., + JUMP, ... ENDALT */ + state->progLength += 13; + } + else { + /* ALT, , ..., JUMP, ... ENDALT */ + state->progLength += 7; + } + break; + + case REOP_CONCAT: + result = operandStack[operandSP - 2]; + while (result->next) + result = result->next; + result->next = operandStack[operandSP - 1]; + break; + + case REOP_ASSERT: + case REOP_ASSERT_NOT: + case REOP_LPARENNON: + case REOP_LPAREN: + /* These should have been processed by a close paren. */ + ReportRegExpErrorHelper(state, JSREPORT_ERROR, JSMSG_MISSING_PAREN, + opData->errPos); + return FALSE; + + default:; + } + return TRUE; +} + +/* + * Hack two bits in CompilerState.flags, for use within FindParenCount to flag + * its being on the stack, and to propagate errors to its callers. + */ +#define JSREG_FIND_PAREN_COUNT 0x8000 +#define JSREG_FIND_PAREN_ERROR 0x4000 + +/* + * Magic return value from FindParenCount and GetDecimalValue, to indicate + * overflow beyond GetDecimalValue's max parameter, or a computed maximum if + * its findMax parameter is non-null. + */ +#define OVERFLOW_VALUE ((UINT)-1) + +static UINT +FindParenCount(CompilerState *state) +{ + CompilerState temp; + int i; + + if (state->flags & JSREG_FIND_PAREN_COUNT) + return OVERFLOW_VALUE; + + /* + * Copy state into temp, flag it so we never report an invalid backref, + * and reset its members to parse the entire regexp. This is obviously + * suboptimal, but GetDecimalValue calls us only if a backref appears to + * refer to a forward parenthetical, which is rare. + */ + temp = *state; + temp.flags |= JSREG_FIND_PAREN_COUNT; + temp.cp = temp.cpbegin; + temp.parenCount = 0; + temp.classCount = 0; + temp.progLength = 0; + temp.treeDepth = 0; + temp.classBitmapsMem = 0; + for (i = 0; i < CLASS_CACHE_SIZE; i++) + temp.classCache[i].start = NULL; + + if (!ParseRegExp(&temp)) { + state->flags |= JSREG_FIND_PAREN_ERROR; + return OVERFLOW_VALUE; + } + return temp.parenCount; +} + +/* + * Extract and return a decimal value at state->cp. The initial character c + * has already been read. Return OVERFLOW_VALUE if the result exceeds max. + * Callers who pass a non-null findMax should test JSREG_FIND_PAREN_ERROR in + * state->flags to discover whether an error occurred under findMax. + */ +static UINT +GetDecimalValue(WCHAR c, UINT max, UINT (*findMax)(CompilerState *state), + CompilerState *state) +{ + UINT value = JS7_UNDEC(c); + BOOL overflow = (value > max && (!findMax || value > findMax(state))); + + /* The following restriction allows simpler overflow checks. */ + assert(max <= ((UINT)-1 - 9) / 10); + while (state->cp < state->cpend) { + c = *state->cp; + if (!JS7_ISDEC(c)) + break; + value = 10 * value + JS7_UNDEC(c); + if (!overflow && value > max && (!findMax || value > findMax(state))) + overflow = TRUE; + ++state->cp; + } + return overflow ? OVERFLOW_VALUE : value; +} + +/* + * Calculate the total size of the bitmap required for a class expression. + */ +static BOOL +CalculateBitmapSize(CompilerState *state, RENode *target, const WCHAR *src, + const WCHAR *end) +{ + UINT max = 0; + BOOL inRange = FALSE; + WCHAR c, rangeStart = 0; + UINT n, digit, nDigits, i; + + target->u.ucclass.bmsize = 0; + target->u.ucclass.sense = TRUE; + + if (src == end) + return TRUE; + + if (*src == '^') { + ++src; + target->u.ucclass.sense = FALSE; + } + + while (src != end) { + BOOL canStartRange = TRUE; + UINT localMax = 0; + + switch (*src) { + case '\\': + ++src; + c = *src++; + switch (c) { + case 'b': + localMax = 0x8; + break; + case 'f': + localMax = 0xC; + break; + case 'n': + localMax = 0xA; + break; + case 'r': + localMax = 0xD; + break; + case 't': + localMax = 0x9; + break; + case 'v': + localMax = 0xB; + break; + case 'c': + if (src < end && RE_IS_LETTER(*src)) { + localMax = (UINT) (*src++) & 0x1F; + } else { + --src; + localMax = '\\'; + } + break; + case 'x': + nDigits = 2; + goto lexHex; + case 'u': + nDigits = 4; +lexHex: + n = 0; + for (i = 0; (i < nDigits) && (src < end); i++) { + c = *src++; + if (!isASCIIHexDigit(c, &digit)) { + /* + * Back off to accepting the original + *'\' as a literal. + */ + src -= i + 1; + n = '\\'; + break; + } + n = (n << 4) | digit; + } + localMax = n; + break; + case 'd': + canStartRange = FALSE; + if (inRange) { + JS_ReportErrorNumber(state->context, + js_GetErrorMessage, NULL, + JSMSG_BAD_CLASS_RANGE); + return FALSE; + } + localMax = '9'; + break; + case 'D': + case 's': + case 'S': + case 'w': + case 'W': + canStartRange = FALSE; + if (inRange) { + JS_ReportErrorNumber(state->context, + js_GetErrorMessage, NULL, + JSMSG_BAD_CLASS_RANGE); + return FALSE; + } + max = 65535; + + /* + * If this is the start of a range, ensure that it's less than + * the end. + */ + localMax = 0; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + /* + * This is a non-ECMA extension - decimal escapes (in this + * case, octal!) are supposed to be an error inside class + * ranges, but supported here for backwards compatibility. + * + */ + n = JS7_UNDEC(c); + c = *src; + if ('0' <= c && c <= '7') { + src++; + n = 8 * n + JS7_UNDEC(c); + c = *src; + if ('0' <= c && c <= '7') { + src++; + i = 8 * n + JS7_UNDEC(c); + if (i <= 0377) + n = i; + else + src--; + } + } + localMax = n; + break; + + default: + localMax = c; + break; + } + break; + default: + localMax = *src++; + break; + } + + if (inRange) { + /* Throw a SyntaxError here, per ECMA-262, 15.10.2.15. */ + if (rangeStart > localMax) { + JS_ReportErrorNumber(state->context, + js_GetErrorMessage, NULL, + JSMSG_BAD_CLASS_RANGE); + return FALSE; + } + inRange = FALSE; + } else { + if (canStartRange && src < end - 1) { + if (*src == '-') { + ++src; + inRange = TRUE; + rangeStart = (WCHAR)localMax; + continue; + } + } + if (state->flags & REG_FOLD) + rangeStart = localMax; /* one run of the uc/dc loop below */ + } + + if (state->flags & REG_FOLD) { + WCHAR maxch = localMax; + + for (i = rangeStart; i <= localMax; i++) { + WCHAR uch, dch; + + uch = toupperW(i); + dch = tolowerW(i); + if(maxch < uch) + maxch = uch; + if(maxch < dch) + maxch = dch; + } + localMax = maxch; + } + + if (localMax > max) + max = localMax; + } + target->u.ucclass.bmsize = max; + return TRUE; +} + +static INT +ParseMinMaxQuantifier(CompilerState *state, BOOL ignoreValues) +{ + UINT min, max; + WCHAR c; + const WCHAR *errp = state->cp++; + + c = *state->cp; + if (JS7_ISDEC(c)) { + ++state->cp; + min = GetDecimalValue(c, 0xFFFF, NULL, state); + c = *state->cp; + + if (!ignoreValues && min == OVERFLOW_VALUE) + return JSMSG_MIN_TOO_BIG; + + if (c == ',') { + c = *++state->cp; + if (JS7_ISDEC(c)) { + ++state->cp; + max = GetDecimalValue(c, 0xFFFF, NULL, state); + c = *state->cp; + if (!ignoreValues && max == OVERFLOW_VALUE) + return JSMSG_MAX_TOO_BIG; + if (!ignoreValues && min > max) + return JSMSG_OUT_OF_ORDER; + } else { + max = (UINT)-1; + } + } else { + max = min; + } + if (c == '}') { + state->result = NewRENode(state, REOP_QUANT); + if (!state->result) + return JSMSG_OUT_OF_MEMORY; + state->result->u.range.min = min; + state->result->u.range.max = max; + /* + * QUANT, , , ... + * where is written as compact(max+1) to make + * (UINT)-1 sentinel to occupy 1 byte, not width_of(max)+1. + */ + state->progLength += (1 + GetCompactIndexWidth(min) + + GetCompactIndexWidth(max + 1) + +3); + return 0; + } + } + + state->cp = errp; + return -1; +} + +static BOOL +ParseQuantifier(CompilerState *state) +{ + RENode *term; + term = state->result; + if (state->cp < state->cpend) { + switch (*state->cp) { + case '+': + state->result = NewRENode(state, REOP_QUANT); + if (!state->result) + return FALSE; + state->result->u.range.min = 1; + state->result->u.range.max = (UINT)-1; + /* , ... */ + state->progLength += 4; + goto quantifier; + case '*': + state->result = NewRENode(state, REOP_QUANT); + if (!state->result) + return FALSE; + state->result->u.range.min = 0; + state->result->u.range.max = (UINT)-1; + /* , ... */ + state->progLength += 4; + goto quantifier; + case '?': + state->result = NewRENode(state, REOP_QUANT); + if (!state->result) + return FALSE; + state->result->u.range.min = 0; + state->result->u.range.max = 1; + /* , ... */ + state->progLength += 4; + goto quantifier; + case '{': /* balance '}' */ + { + INT err; + + err = ParseMinMaxQuantifier(state, FALSE); + if (err == 0) + goto quantifier; + if (err == -1) + return TRUE; + + ReportRegExpErrorHelper(state, JSREPORT_ERROR, err, errp); + return FALSE; + } + default:; + } + } + return TRUE; + +quantifier: + if (state->treeDepth == TREE_DEPTH_MAX) { + ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); + return FALSE; + } + + ++state->treeDepth; + ++state->cp; + state->result->kid = term; + if (state->cp < state->cpend && *state->cp == '?') { + ++state->cp; + state->result->u.range.greedy = FALSE; + } else { + state->result->u.range.greedy = TRUE; + } + return TRUE; +} + +/* + * item: assertion An item is either an assertion or + * quantatom a quantified atom. + * + * assertion: '^' Assertions match beginning of string + * (or line if the class static property + * RegExp.multiline is true). + * '$' End of string (or line if the class + * static property RegExp.multiline is + * true). + * '\b' Word boundary (between \w and \W). + * '\B' Word non-boundary. + * + * quantatom: atom An unquantified atom. + * quantatom '{' n ',' m '}' + * Atom must occur between n and m times. + * quantatom '{' n ',' '}' Atom must occur at least n times. + * quantatom '{' n '}' Atom must occur exactly n times. + * quantatom '*' Zero or more times (same as {0,}). + * quantatom '+' One or more times (same as {1,}). + * quantatom '?' Zero or one time (same as {0,1}). + * + * any of which can be optionally followed by '?' for ungreedy + * + * atom: '(' regexp ')' A parenthesized regexp (what matched + * can be addressed using a backreference, + * see '\' n below). + * '.' Matches any char except '\n'. + * '[' classlist ']' A character class. + * '[' '^' classlist ']' A negated character class. + * '\f' Form Feed. + * '\n' Newline (Line Feed). + * '\r' Carriage Return. + * '\t' Horizontal Tab. + * '\v' Vertical Tab. + * '\d' A digit (same as [0-9]). + * '\D' A non-digit. + * '\w' A word character, [0-9a-z_A-Z]. + * '\W' A non-word character. + * '\s' A whitespace character, [ \b\f\n\r\t\v]. + * '\S' A non-whitespace character. + * '\' n A backreference to the nth (n decimal + * and positive) parenthesized expression. + * '\' octal An octal escape sequence (octal must be + * two or three digits long, unless it is + * 0 for the null character). + * '\x' hex A hex escape (hex must be two digits). + * '\u' unicode A unicode escape (must be four digits). + * '\c' ctrl A control character, ctrl is a letter. + * '\' literalatomchar Any character except one of the above + * that follow '\' in an atom. + * otheratomchar Any character not first among the other + * atom right-hand sides. + */ +static BOOL +ParseTerm(CompilerState *state) +{ + WCHAR c = *state->cp++; + UINT nDigits; + UINT num, tmp, n, i; + const WCHAR *termStart; + + switch (c) { + /* assertions and atoms */ + case '^': + state->result = NewRENode(state, REOP_BOL); + if (!state->result) + return FALSE; + state->progLength++; + return TRUE; + case '$': + state->result = NewRENode(state, REOP_EOL); + if (!state->result) + return FALSE; + state->progLength++; + return TRUE; + case '\\': + if (state->cp >= state->cpend) { + /* a trailing '\' is an error */ + ReportRegExpError(state, JSREPORT_ERROR, JSMSG_TRAILING_SLASH); + return FALSE; + } + c = *state->cp++; + switch (c) { + /* assertion escapes */ + case 'b' : + state->result = NewRENode(state, REOP_WBDRY); + if (!state->result) + return FALSE; + state->progLength++; + return TRUE; + case 'B': + state->result = NewRENode(state, REOP_WNONBDRY); + if (!state->result) + return FALSE; + state->progLength++; + return TRUE; + /* Decimal escape */ + case '0': + /* Give a strict warning. See also the note below. */ + WARN("non-octal digit in an escape sequence that doesn't match a back-reference\n"); + doOctal: + num = 0; + while (state->cp < state->cpend) { + c = *state->cp; + if (c < '0' || '7' < c) + break; + state->cp++; + tmp = 8 * num + (UINT)JS7_UNDEC(c); + if (tmp > 0377) + break; + num = tmp; + } + c = (WCHAR)num; + doFlat: + state->result = NewRENode(state, REOP_FLAT); + if (!state->result) + return FALSE; + state->result->u.flat.chr = c; + state->result->u.flat.length = 1; + state->progLength += 3; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + termStart = state->cp - 1; + num = GetDecimalValue(c, state->parenCount, FindParenCount, state); + if (state->flags & JSREG_FIND_PAREN_ERROR) + return FALSE; + if (num == OVERFLOW_VALUE) { + /* Give a strict mode warning. */ + WARN("back-reference exceeds number of capturing parentheses\n"); + + /* + * Note: ECMA 262, 15.10.2.9 says that we should throw a syntax + * error here. However, for compatibility with IE, we treat the + * whole backref as flat if the first character in it is not a + * valid octal character, and as an octal escape otherwise. + */ + state->cp = termStart; + if (c >= '8') { + /* Treat this as flat. termStart - 1 is the \. */ + c = '\\'; + goto asFlat; + } + + /* Treat this as an octal escape. */ + goto doOctal; + } + assert(1 <= num && num <= 0x10000); + state->result = NewRENode(state, REOP_BACKREF); + if (!state->result) + return FALSE; + state->result->u.parenIndex = num - 1; + state->progLength + += 1 + GetCompactIndexWidth(state->result->u.parenIndex); + break; + /* Control escape */ + case 'f': + c = 0xC; + goto doFlat; + case 'n': + c = 0xA; + goto doFlat; + case 'r': + c = 0xD; + goto doFlat; + case 't': + c = 0x9; + goto doFlat; + case 'v': + c = 0xB; + goto doFlat; + /* Control letter */ + case 'c': + if (state->cp < state->cpend && RE_IS_LETTER(*state->cp)) { + c = (WCHAR) (*state->cp++ & 0x1F); + } else { + /* back off to accepting the original '\' as a literal */ + --state->cp; + c = '\\'; + } + goto doFlat; + /* HexEscapeSequence */ + case 'x': + nDigits = 2; + goto lexHex; + /* UnicodeEscapeSequence */ + case 'u': + nDigits = 4; +lexHex: + n = 0; + for (i = 0; i < nDigits && state->cp < state->cpend; i++) { + UINT digit; + c = *state->cp++; + if (!isASCIIHexDigit(c, &digit)) { + /* + * Back off to accepting the original 'u' or 'x' as a + * literal. + */ + state->cp -= i + 2; + n = *state->cp++; + break; + } + n = (n << 4) | digit; + } + c = (WCHAR) n; + goto doFlat; + /* Character class escapes */ + case 'd': + state->result = NewRENode(state, REOP_DIGIT); +doSimple: + if (!state->result) + return FALSE; + state->progLength++; + break; + case 'D': + state->result = NewRENode(state, REOP_NONDIGIT); + goto doSimple; + case 's': + state->result = NewRENode(state, REOP_SPACE); + goto doSimple; + case 'S': + state->result = NewRENode(state, REOP_NONSPACE); + goto doSimple; + case 'w': + state->result = NewRENode(state, REOP_ALNUM); + goto doSimple; + case 'W': + state->result = NewRENode(state, REOP_NONALNUM); + goto doSimple; + /* IdentityEscape */ + default: + state->result = NewRENode(state, REOP_FLAT); + if (!state->result) + return FALSE; + state->result->u.flat.chr = c; + state->result->u.flat.length = 1; + state->result->kid = (void *) (state->cp - 1); + state->progLength += 3; + break; + } + break; + case '[': + state->result = NewRENode(state, REOP_CLASS); + if (!state->result) + return FALSE; + termStart = state->cp; + state->result->u.ucclass.startIndex = termStart - state->cpbegin; + for (;;) { + if (state->cp == state->cpend) { + ReportRegExpErrorHelper(state, JSREPORT_ERROR, + JSMSG_UNTERM_CLASS, termStart); + + return FALSE; + } + if (*state->cp == '\\') { + state->cp++; + if (state->cp != state->cpend) + state->cp++; + continue; + } + if (*state->cp == ']') { + state->result->u.ucclass.kidlen = state->cp - termStart; + break; + } + state->cp++; + } + for (i = 0; i < CLASS_CACHE_SIZE; i++) { + if (!state->classCache[i].start) { + state->classCache[i].start = termStart; + state->classCache[i].length = state->result->u.ucclass.kidlen; + state->classCache[i].index = state->classCount; + break; + } + if (state->classCache[i].length == + state->result->u.ucclass.kidlen) { + for (n = 0; ; n++) { + if (n == state->classCache[i].length) { + state->result->u.ucclass.index + = state->classCache[i].index; + goto claim; + } + if (state->classCache[i].start[n] != termStart[n]) + break; + } + } + } + state->result->u.ucclass.index = state->classCount++; + + claim: + /* + * Call CalculateBitmapSize now as we want any errors it finds + * to be reported during the parse phase, not at execution. + */ + if (!CalculateBitmapSize(state, state->result, termStart, state->cp++)) + return FALSE; + /* + * Update classBitmapsMem with number of bytes to hold bmsize bits, + * which is (bitsCount + 7) / 8 or (highest_bit + 1 + 7) / 8 + * or highest_bit / 8 + 1 where highest_bit is u.ucclass.bmsize. + */ + n = (state->result->u.ucclass.bmsize >> 3) + 1; + if (n > CLASS_BITMAPS_MEM_LIMIT - state->classBitmapsMem) { + ReportRegExpError(state, JSREPORT_ERROR, JSMSG_REGEXP_TOO_COMPLEX); + return FALSE; + } + state->classBitmapsMem += n; + /* CLASS, */ + state->progLength + += 1 + GetCompactIndexWidth(state->result->u.ucclass.index); + break; + + case '.': + state->result = NewRENode(state, REOP_DOT); + goto doSimple; + + case '{': + { + const WCHAR *errp = state->cp--; + INT err; + + err = ParseMinMaxQuantifier(state, TRUE); + state->cp = errp; + + if (err < 0) + goto asFlat; + + /* FALL THROUGH */ + } + case '*': + case '+': + case '?': + ReportRegExpErrorHelper(state, JSREPORT_ERROR, + JSMSG_BAD_QUANTIFIER, state->cp - 1); + return FALSE; + default: +asFlat: + state->result = NewRENode(state, REOP_FLAT); + if (!state->result) + return FALSE; + state->result->u.flat.chr = c; + state->result->u.flat.length = 1; + state->result->kid = (void *) (state->cp - 1); + state->progLength += 3; + break; + } + return ParseQuantifier(state); +} + +/* + * Top-down regular expression grammar, based closely on Perl4. + * + * regexp: altern A regular expression is one or more + * altern '|' regexp alternatives separated by vertical bar. + */ +#define INITIAL_STACK_SIZE 128 + +static BOOL +ParseRegExp(CompilerState *state) +{ + size_t parenIndex; + RENode *operand; + REOpData *operatorStack; + RENode **operandStack; + REOp op; + INT i; + BOOL result = FALSE; + + INT operatorSP = 0, operatorStackSize = INITIAL_STACK_SIZE; + INT operandSP = 0, operandStackSize = INITIAL_STACK_SIZE; + + /* Watch out for empty regexp */ + if (state->cp == state->cpend) { + state->result = NewRENode(state, REOP_EMPTY); + return (state->result != NULL); + } + + operatorStack = heap_alloc(sizeof(REOpData) * operatorStackSize); + if (!operatorStack) + return FALSE; + + operandStack = heap_alloc(sizeof(RENode *) * operandStackSize); + if (!operandStack) + goto out; + + for (;;) { + parenIndex = state->parenCount; + if (state->cp == state->cpend) { + /* + * If we are at the end of the regexp and we're short one or more + * operands, the regexp must have the form /x|/ or some such, with + * left parentheses making us short more than one operand. + */ + if (operatorSP >= operandSP) { + operand = NewRENode(state, REOP_EMPTY); + if (!operand) + goto out; + goto pushOperand; + } + } else { + switch (*state->cp) { + case '(': + ++state->cp; + if (state->cp + 1 < state->cpend && + *state->cp == '?' && + (state->cp[1] == '=' || + state->cp[1] == '!' || + state->cp[1] == ':')) { + switch (state->cp[1]) { + case '=': + op = REOP_ASSERT; + /* ASSERT, , ... ASSERTTEST */ + state->progLength += 4; + break; + case '!': + op = REOP_ASSERT_NOT; + /* ASSERTNOT, , ... ASSERTNOTTEST */ + state->progLength += 4; + break; + default: + op = REOP_LPARENNON; + break; + } + state->cp += 2; + } else { + op = REOP_LPAREN; + /* LPAREN, , ... RPAREN, */ + state->progLength + += 2 * (1 + GetCompactIndexWidth(parenIndex)); + state->parenCount++; + if (state->parenCount == 65535) { + ReportRegExpError(state, JSREPORT_ERROR, + JSMSG_TOO_MANY_PARENS); + goto out; + } + } + goto pushOperator; + + case ')': + /* + * If there's no stacked open parenthesis, throw syntax error. + */ + for (i = operatorSP - 1; ; i--) { + if (i < 0) { + ReportRegExpError(state, JSREPORT_ERROR, + JSMSG_UNMATCHED_RIGHT_PAREN); + goto out; + } + if (operatorStack[i].op == REOP_ASSERT || + operatorStack[i].op == REOP_ASSERT_NOT || + operatorStack[i].op == REOP_LPARENNON || + operatorStack[i].op == REOP_LPAREN) { + break; + } + } + /* FALL THROUGH */ + + case '|': + /* Expected an operand before these, so make an empty one */ + operand = NewRENode(state, REOP_EMPTY); + if (!operand) + goto out; + goto pushOperand; + + default: + if (!ParseTerm(state)) + goto out; + operand = state->result; +pushOperand: + if (operandSP == operandStackSize) { + RENode **tmp; + operandStackSize += operandStackSize; + tmp = heap_realloc(operandStack, sizeof(RENode *) * operandStackSize); + if (!tmp) + goto out; + operandStack = tmp; + } + operandStack[operandSP++] = operand; + break; + } + } + + /* At the end; process remaining operators. */ +restartOperator: + if (state->cp == state->cpend) { + while (operatorSP) { + --operatorSP; + if (!ProcessOp(state, &operatorStack[operatorSP], + operandStack, operandSP)) + goto out; + --operandSP; + } + assert(operandSP == 1); + state->result = operandStack[0]; + result = TRUE; + goto out; + } + + switch (*state->cp) { + case '|': + /* Process any stacked 'concat' operators */ + ++state->cp; + while (operatorSP && + operatorStack[operatorSP - 1].op == REOP_CONCAT) { + --operatorSP; + if (!ProcessOp(state, &operatorStack[operatorSP], + operandStack, operandSP)) { + goto out; + } + --operandSP; + } + op = REOP_ALT; + goto pushOperator; + + case ')': + /* + * If there's no stacked open parenthesis, throw syntax error. + */ + for (i = operatorSP - 1; ; i--) { + if (i < 0) { + ReportRegExpError(state, JSREPORT_ERROR, + JSMSG_UNMATCHED_RIGHT_PAREN); + goto out; + } + if (operatorStack[i].op == REOP_ASSERT || + operatorStack[i].op == REOP_ASSERT_NOT || + operatorStack[i].op == REOP_LPARENNON || + operatorStack[i].op == REOP_LPAREN) { + break; + } + } + ++state->cp; + + /* Process everything on the stack until the open parenthesis. */ + for (;;) { + assert(operatorSP); + --operatorSP; + switch (operatorStack[operatorSP].op) { + case REOP_ASSERT: + case REOP_ASSERT_NOT: + case REOP_LPAREN: + operand = NewRENode(state, operatorStack[operatorSP].op); + if (!operand) + goto out; + operand->u.parenIndex = + operatorStack[operatorSP].parenIndex; + assert(operandSP); + operand->kid = operandStack[operandSP - 1]; + operandStack[operandSP - 1] = operand; + if (state->treeDepth == TREE_DEPTH_MAX) { + ReportRegExpError(state, JSREPORT_ERROR, + JSMSG_REGEXP_TOO_COMPLEX); + goto out; + } + ++state->treeDepth; + /* FALL THROUGH */ + + case REOP_LPARENNON: + state->result = operandStack[operandSP - 1]; + if (!ParseQuantifier(state)) + goto out; + operandStack[operandSP - 1] = state->result; + goto restartOperator; + default: + if (!ProcessOp(state, &operatorStack[operatorSP], + operandStack, operandSP)) + goto out; + --operandSP; + break; + } + } + break; + + case '{': + { + const WCHAR *errp = state->cp; + + if (ParseMinMaxQuantifier(state, TRUE) < 0) { + /* + * This didn't even scan correctly as a quantifier, so we should + * treat it as flat. + */ + op = REOP_CONCAT; + goto pushOperator; + } + + state->cp = errp; + /* FALL THROUGH */ + } + + case '+': + case '*': + case '?': + ReportRegExpErrorHelper(state, JSREPORT_ERROR, JSMSG_BAD_QUANTIFIER, + state->cp); + result = FALSE; + goto out; + + default: + /* Anything else is the start of the next term. */ + op = REOP_CONCAT; +pushOperator: + if (operatorSP == operatorStackSize) { + REOpData *tmp; + operatorStackSize += operatorStackSize; + tmp = heap_realloc(operatorStack, sizeof(REOpData) * operatorStackSize); + if (!tmp) + goto out; + operatorStack = tmp; + } + operatorStack[operatorSP].op = op; + operatorStack[operatorSP].errPos = state->cp; + operatorStack[operatorSP++].parenIndex = parenIndex; + break; + } + } +out: + heap_free(operatorStack); + heap_free(operandStack); + return result; +} + +/* + * Save the current state of the match - the position in the input + * text as well as the position in the bytecode. The state of any + * parent expressions is also saved (preceding state). + * Contents of parenCount parentheses from parenIndex are also saved. + */ +static REBackTrackData * +PushBackTrackState(REGlobalData *gData, REOp op, + jsbytecode *target, match_state_t *x, const WCHAR *cp, + size_t parenIndex, size_t parenCount) +{ + size_t i; + REBackTrackData *result = + (REBackTrackData *) ((char *)gData->backTrackSP + gData->cursz); + + size_t sz = sizeof(REBackTrackData) + + gData->stateStackTop * sizeof(REProgState) + + parenCount * sizeof(RECapture); + + ptrdiff_t btsize = gData->backTrackStackSize; + ptrdiff_t btincr = ((char *)result + sz) - + ((char *)gData->backTrackStack + btsize); + + TRACE("\tBT_Push: %lu,%lu\n", (ULONG_PTR)parenIndex, (ULONG_PTR)parenCount); + + JS_COUNT_OPERATION(gData->cx, JSOW_JUMP * (1 + parenCount)); + if (btincr > 0) { + ptrdiff_t offset = (char *)result - (char *)gData->backTrackStack; + + JS_COUNT_OPERATION(gData->cx, JSOW_ALLOCATION); + btincr = ((btincr+btsize-1)/btsize)*btsize; + gData->backTrackStack = heap_pool_grow(gData->pool, gData->backTrackStack, btsize, btincr); + if (!gData->backTrackStack) { + js_ReportOutOfScriptQuota(gData->cx); + gData->ok = FALSE; + return NULL; + } + gData->backTrackStackSize = btsize + btincr; + result = (REBackTrackData *) ((char *)gData->backTrackStack + offset); + } + gData->backTrackSP = result; + result->sz = gData->cursz; + gData->cursz = sz; + + result->backtrack_op = op; + result->backtrack_pc = target; + result->cp = cp; + result->parenCount = parenCount; + result->parenIndex = parenIndex; + + result->saveStateStackTop = gData->stateStackTop; + assert(gData->stateStackTop); + memcpy(result + 1, gData->stateStack, + sizeof(REProgState) * result->saveStateStackTop); + + if (parenCount != 0) { + memcpy((char *)(result + 1) + + sizeof(REProgState) * result->saveStateStackTop, + &x->parens[parenIndex], + sizeof(RECapture) * parenCount); + for (i = 0; i != parenCount; i++) + x->parens[parenIndex + i].index = -1; + } + + return result; +} + +static inline match_state_t * +FlatNIMatcher(REGlobalData *gData, match_state_t *x, const WCHAR *matchChars, + size_t length) +{ + size_t i; + assert(gData->cpend >= x->cp); + if (length > (size_t)(gData->cpend - x->cp)) + return NULL; + for (i = 0; i != length; i++) { + if (toupperW(matchChars[i]) != toupperW(x->cp[i])) + return NULL; + } + x->cp += length; + return x; +} + +/* + * 1. Evaluate DecimalEscape to obtain an EscapeValue E. + * 2. If E is not a character then go to step 6. + * 3. Let ch be E's character. + * 4. Let A be a one-element RECharSet containing the character ch. + * 5. Call CharacterSetMatcher(A, false) and return its Matcher result. + * 6. E must be an integer. Let n be that integer. + * 7. If n=0 or n>NCapturingParens then throw a SyntaxError exception. + * 8. Return an internal Matcher closure that takes two arguments, a State x + * and a Continuation c, and performs the following: + * 1. Let cap be x's captures internal array. + * 2. Let s be cap[n]. + * 3. If s is undefined, then call c(x) and return its result. + * 4. Let e be x's endIndex. + * 5. Let len be s's length. + * 6. Let f be e+len. + * 7. If f>InputLength, return failure. + * 8. If there exists an integer i between 0 (inclusive) and len (exclusive) + * such that Canonicalize(s[i]) is not the same character as + * Canonicalize(Input [e+i]), then return failure. + * 9. Let y be the State (f, cap). + * 10. Call c(y) and return its result. + */ +static match_state_t * +BackrefMatcher(REGlobalData *gData, match_state_t *x, size_t parenIndex) +{ + size_t len, i; + const WCHAR *parenContent; + RECapture *cap = &x->parens[parenIndex]; + + if (cap->index == -1) + return x; + + len = cap->length; + if (x->cp + len > gData->cpend) + return NULL; + + parenContent = &gData->cpbegin[cap->index]; + if (gData->regexp->flags & REG_FOLD) { + for (i = 0; i < len; i++) { + if (toupperW(parenContent[i]) != toupperW(x->cp[i])) + return NULL; + } + } else { + for (i = 0; i < len; i++) { + if (parenContent[i] != x->cp[i]) + return NULL; + } + } + x->cp += len; + return x; +} + +/* Add a single character to the RECharSet */ +static void +AddCharacterToCharSet(RECharSet *cs, WCHAR c) +{ + UINT byteIndex = (UINT)(c >> 3); + assert(c <= cs->length); + cs->u.bits[byteIndex] |= 1 << (c & 0x7); +} + + +/* Add a character range, c1 to c2 (inclusive) to the RECharSet */ +static void +AddCharacterRangeToCharSet(RECharSet *cs, UINT c1, UINT c2) +{ + UINT i; + + UINT byteIndex1 = c1 >> 3; + UINT byteIndex2 = c2 >> 3; + + assert(c2 <= cs->length && c1 <= c2); + + c1 &= 0x7; + c2 &= 0x7; + + if (byteIndex1 == byteIndex2) { + cs->u.bits[byteIndex1] |= ((BYTE)0xFF >> (7 - (c2 - c1))) << c1; + } else { + cs->u.bits[byteIndex1] |= 0xFF << c1; + for (i = byteIndex1 + 1; i < byteIndex2; i++) + cs->u.bits[i] = 0xFF; + cs->u.bits[byteIndex2] |= (BYTE)0xFF >> (7 - c2); + } +} + +/* Compile the source of the class into a RECharSet */ +static BOOL +ProcessCharSet(REGlobalData *gData, RECharSet *charSet) +{ + const WCHAR *src, *end; + BOOL inRange = FALSE; + WCHAR rangeStart = 0; + UINT byteLength, n; + WCHAR c, thisCh; + INT nDigits, i; + + assert(!charSet->converted); + /* + * Assert that startIndex and length points to chars inside [] inside + * source string. + */ + assert(1 <= charSet->u.src.startIndex); + assert(charSet->u.src.startIndex < gData->regexp->source_len); + assert(charSet->u.src.length <= gData->regexp->source_len + - 1 - charSet->u.src.startIndex); + + charSet->converted = TRUE; + src = gData->regexp->source + charSet->u.src.startIndex; + + end = src + charSet->u.src.length; + + assert(src[-1] == '[' && end[0] == ']'); + + byteLength = (charSet->length >> 3) + 1; + charSet->u.bits = heap_alloc(byteLength); + if (!charSet->u.bits) { + JS_ReportOutOfMemory(gData->cx); + gData->ok = FALSE; + return FALSE; + } + memset(charSet->u.bits, 0, byteLength); + + if (src == end) + return TRUE; + + if (*src == '^') { + assert(charSet->sense == FALSE); + ++src; + } else { + assert(charSet->sense == TRUE); + } + + while (src != end) { + switch (*src) { + case '\\': + ++src; + c = *src++; + switch (c) { + case 'b': + thisCh = 0x8; + break; + case 'f': + thisCh = 0xC; + break; + case 'n': + thisCh = 0xA; + break; + case 'r': + thisCh = 0xD; + break; + case 't': + thisCh = 0x9; + break; + case 'v': + thisCh = 0xB; + break; + case 'c': + if (src < end && JS_ISWORD(*src)) { + thisCh = (WCHAR)(*src++ & 0x1F); + } else { + --src; + thisCh = '\\'; + } + break; + case 'x': + nDigits = 2; + goto lexHex; + case 'u': + nDigits = 4; + lexHex: + n = 0; + for (i = 0; (i < nDigits) && (src < end); i++) { + UINT digit; + c = *src++; + if (!isASCIIHexDigit(c, &digit)) { + /* + * Back off to accepting the original '\' + * as a literal + */ + src -= i + 1; + n = '\\'; + break; + } + n = (n << 4) | digit; + } + thisCh = (WCHAR)n; + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + /* + * This is a non-ECMA extension - decimal escapes (in this + * case, octal!) are supposed to be an error inside class + * ranges, but supported here for backwards compatibility. + */ + n = JS7_UNDEC(c); + c = *src; + if ('0' <= c && c <= '7') { + src++; + n = 8 * n + JS7_UNDEC(c); + c = *src; + if ('0' <= c && c <= '7') { + src++; + i = 8 * n + JS7_UNDEC(c); + if (i <= 0377) + n = i; + else + src--; + } + } + thisCh = (WCHAR)n; + break; + + case 'd': + AddCharacterRangeToCharSet(charSet, '0', '9'); + continue; /* don't need range processing */ + case 'D': + AddCharacterRangeToCharSet(charSet, 0, '0' - 1); + AddCharacterRangeToCharSet(charSet, + (WCHAR)('9' + 1), + (WCHAR)charSet->length); + continue; + case 's': + for (i = (INT)charSet->length; i >= 0; i--) + if (isspaceW(i)) + AddCharacterToCharSet(charSet, (WCHAR)i); + continue; + case 'S': + for (i = (INT)charSet->length; i >= 0; i--) + if (!isspaceW(i)) + AddCharacterToCharSet(charSet, (WCHAR)i); + continue; + case 'w': + for (i = (INT)charSet->length; i >= 0; i--) + if (JS_ISWORD(i)) + AddCharacterToCharSet(charSet, (WCHAR)i); + continue; + case 'W': + for (i = (INT)charSet->length; i >= 0; i--) + if (!JS_ISWORD(i)) + AddCharacterToCharSet(charSet, (WCHAR)i); + continue; + default: + thisCh = c; + break; + + } + break; + + default: + thisCh = *src++; + break; + + } + if (inRange) { + if (gData->regexp->flags & REG_FOLD) { + assert(rangeStart <= thisCh); + for (i = rangeStart; i <= thisCh; i++) { + WCHAR uch, dch; + + AddCharacterToCharSet(charSet, i); + uch = toupperW(i); + dch = tolowerW(i); + if (i != uch) + AddCharacterToCharSet(charSet, uch); + if (i != dch) + AddCharacterToCharSet(charSet, dch); + } + } else { + AddCharacterRangeToCharSet(charSet, rangeStart, thisCh); + } + inRange = FALSE; + } else { + if (gData->regexp->flags & REG_FOLD) { + AddCharacterToCharSet(charSet, toupperW(thisCh)); + AddCharacterToCharSet(charSet, tolowerW(thisCh)); + } else { + AddCharacterToCharSet(charSet, thisCh); + } + if (src < end - 1) { + if (*src == '-') { + ++src; + inRange = TRUE; + rangeStart = thisCh; + } + } + } + } + return TRUE; +} + +static BOOL +ReallocStateStack(REGlobalData *gData) +{ + size_t limit = gData->stateStackLimit; + size_t sz = sizeof(REProgState) * limit; + + gData->stateStack = heap_pool_grow(gData->pool, gData->stateStack, sz, sz); + if (!gData->stateStack) { + js_ReportOutOfScriptQuota(gData->cx); + gData->ok = FALSE; + return FALSE; + } + gData->stateStackLimit = limit + limit; + return TRUE; +} + +#define PUSH_STATE_STACK(data) \ + do { \ + ++(data)->stateStackTop; \ + if ((data)->stateStackTop == (data)->stateStackLimit && \ + !ReallocStateStack((data))) { \ + return NULL; \ + } \ + }while(0) + +/* + * Apply the current op against the given input to see if it's going to match + * or fail. Return false if we don't get a match, true if we do. If updatecp is + * true, then update the current state's cp. Always update startpc to the next + * op. + */ +static inline match_state_t * +SimpleMatch(REGlobalData *gData, match_state_t *x, REOp op, + jsbytecode **startpc, BOOL updatecp) +{ + match_state_t *result = NULL; + WCHAR matchCh; + size_t parenIndex; + size_t offset, length, index; + jsbytecode *pc = *startpc; /* pc has already been incremented past op */ + const WCHAR *source; + const WCHAR *startcp = x->cp; + WCHAR ch; + RECharSet *charSet; + + const char *opname = reop_names[op]; + TRACE("\n%06d: %*s%s\n", (int)(pc - gData->regexp->program), + (int)gData->stateStackTop * 2, "", opname); + + switch (op) { + case REOP_EMPTY: + result = x; + break; + case REOP_BOL: + if (x->cp != gData->cpbegin) { + if (/*!gData->cx->regExpStatics.multiline && FIXME !!! */ + !(gData->regexp->flags & REG_MULTILINE)) { + break; + } + if (!RE_IS_LINE_TERM(x->cp[-1])) + break; + } + result = x; + break; + case REOP_EOL: + if (x->cp != gData->cpend) { + if (/*!gData->cx->regExpStatics.multiline &&*/ + !(gData->regexp->flags & REG_MULTILINE)) { + break; + } + if (!RE_IS_LINE_TERM(*x->cp)) + break; + } + result = x; + break; + case REOP_WBDRY: + if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^ + !(x->cp != gData->cpend && JS_ISWORD(*x->cp))) { + result = x; + } + break; + case REOP_WNONBDRY: + if ((x->cp == gData->cpbegin || !JS_ISWORD(x->cp[-1])) ^ + (x->cp != gData->cpend && JS_ISWORD(*x->cp))) { + result = x; + } + break; + case REOP_DOT: + if (x->cp != gData->cpend && !RE_IS_LINE_TERM(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_DIGIT: + if (x->cp != gData->cpend && JS7_ISDEC(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_NONDIGIT: + if (x->cp != gData->cpend && !JS7_ISDEC(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_ALNUM: + if (x->cp != gData->cpend && JS_ISWORD(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_NONALNUM: + if (x->cp != gData->cpend && !JS_ISWORD(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_SPACE: + if (x->cp != gData->cpend && isspaceW(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_NONSPACE: + if (x->cp != gData->cpend && !isspaceW(*x->cp)) { + result = x; + result->cp++; + } + break; + case REOP_BACKREF: + pc = ReadCompactIndex(pc, &parenIndex); + assert(parenIndex < gData->regexp->parenCount); + result = BackrefMatcher(gData, x, parenIndex); + break; + case REOP_FLAT: + pc = ReadCompactIndex(pc, &offset); + assert(offset < gData->regexp->source_len); + pc = ReadCompactIndex(pc, &length); + assert(1 <= length); + assert(length <= gData->regexp->source_len - offset); + if (length <= (size_t)(gData->cpend - x->cp)) { + source = gData->regexp->source + offset; + TRACE("%s\n", debugstr_wn(source, length)); + for (index = 0; index != length; index++) { + if (source[index] != x->cp[index]) + return NULL; + } + x->cp += length; + result = x; + } + break; + case REOP_FLAT1: + matchCh = *pc++; + TRACE(" '%c' == '%c'\n", (char)matchCh, (char)*x->cp); + if (x->cp != gData->cpend && *x->cp == matchCh) { + result = x; + result->cp++; + } + break; + case REOP_FLATi: + pc = ReadCompactIndex(pc, &offset); + assert(offset < gData->regexp->source_len); + pc = ReadCompactIndex(pc, &length); + assert(1 <= length); + assert(length <= gData->regexp->source_len - offset); + source = gData->regexp->source; + result = FlatNIMatcher(gData, x, source + offset, length); + break; + case REOP_FLAT1i: + matchCh = *pc++; + if (x->cp != gData->cpend && toupperW(*x->cp) == toupperW(matchCh)) { + result = x; + result->cp++; + } + break; + case REOP_UCFLAT1: + matchCh = GET_ARG(pc); + TRACE(" '%c' == '%c'\n", (char)matchCh, (char)*x->cp); + pc += ARG_LEN; + if (x->cp != gData->cpend && *x->cp == matchCh) { + result = x; + result->cp++; + } + break; + case REOP_UCFLAT1i: + matchCh = GET_ARG(pc); + pc += ARG_LEN; + if (x->cp != gData->cpend && toupperW(*x->cp) == toupperW(matchCh)) { + result = x; + result->cp++; + } + break; + case REOP_CLASS: + pc = ReadCompactIndex(pc, &index); + assert(index < gData->regexp->classCount); + if (x->cp != gData->cpend) { + charSet = &gData->regexp->classList[index]; + assert(charSet->converted); + ch = *x->cp; + index = ch >> 3; + if (charSet->length != 0 && + ch <= charSet->length && + (charSet->u.bits[index] & (1 << (ch & 0x7)))) { + result = x; + result->cp++; + } + } + break; + case REOP_NCLASS: + pc = ReadCompactIndex(pc, &index); + assert(index < gData->regexp->classCount); + if (x->cp != gData->cpend) { + charSet = &gData->regexp->classList[index]; + assert(charSet->converted); + ch = *x->cp; + index = ch >> 3; + if (charSet->length == 0 || + ch > charSet->length || + !(charSet->u.bits[index] & (1 << (ch & 0x7)))) { + result = x; + result->cp++; + } + } + break; + + default: + assert(FALSE); + } + if (result) { + if (!updatecp) + x->cp = startcp; + *startpc = pc; + TRACE(" *\n"); + return result; + } + x->cp = startcp; + return NULL; +} + +static inline match_state_t * +ExecuteREBytecode(REGlobalData *gData, match_state_t *x) +{ + match_state_t *result = NULL; + REBackTrackData *backTrackData; + jsbytecode *nextpc, *testpc; + REOp nextop; + RECapture *cap; + REProgState *curState; + const WCHAR *startcp; + size_t parenIndex, k; + size_t parenSoFar = 0; + + WCHAR matchCh1, matchCh2; + RECharSet *charSet; + + BOOL anchor; + jsbytecode *pc = gData->regexp->program; + REOp op = (REOp) *pc++; + + /* + * If the first node is a simple match, step the index into the string + * until that match is made, or fail if it can't be found at all. + */ + if (REOP_IS_SIMPLE(op) && !(gData->regexp->flags & REG_STICKY)) { + anchor = FALSE; + while (x->cp <= gData->cpend) { + nextpc = pc; /* reset back to start each time */ + result = SimpleMatch(gData, x, op, &nextpc, TRUE); + if (result) { + anchor = TRUE; + x = result; + pc = nextpc; /* accept skip to next opcode */ + op = (REOp) *pc++; + assert(op < REOP_LIMIT); + break; + } + gData->skipped++; + x->cp++; + } + if (!anchor) + goto bad; + } + + for (;;) { + const char *opname = reop_names[op]; + TRACE("\n%06d: %*s%s\n", (int)(pc - gData->regexp->program), + (int)gData->stateStackTop * 2, "", opname); + + if (REOP_IS_SIMPLE(op)) { + result = SimpleMatch(gData, x, op, &pc, TRUE); + } else { + curState = &gData->stateStack[gData->stateStackTop]; + switch (op) { + case REOP_END: + goto good; + case REOP_ALTPREREQ2: + nextpc = pc + GET_OFFSET(pc); /* start of next op */ + pc += ARG_LEN; + matchCh2 = GET_ARG(pc); + pc += ARG_LEN; + k = GET_ARG(pc); + pc += ARG_LEN; + + if (x->cp != gData->cpend) { + if (*x->cp == matchCh2) + goto doAlt; + + charSet = &gData->regexp->classList[k]; + if (!charSet->converted && !ProcessCharSet(gData, charSet)) + goto bad; + matchCh1 = *x->cp; + k = matchCh1 >> 3; + if ((charSet->length == 0 || + matchCh1 > charSet->length || + !(charSet->u.bits[k] & (1 << (matchCh1 & 0x7)))) ^ + charSet->sense) { + goto doAlt; + } + } + result = NULL; + break; + + case REOP_ALTPREREQ: + nextpc = pc + GET_OFFSET(pc); /* start of next op */ + pc += ARG_LEN; + matchCh1 = GET_ARG(pc); + pc += ARG_LEN; + matchCh2 = GET_ARG(pc); + pc += ARG_LEN; + if (x->cp == gData->cpend || + (*x->cp != matchCh1 && *x->cp != matchCh2)) { + result = NULL; + break; + } + /* else fall through... */ + + case REOP_ALT: + doAlt: + nextpc = pc + GET_OFFSET(pc); /* start of next alternate */ + pc += ARG_LEN; /* start of this alternate */ + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + op = (REOp) *pc++; + startcp = x->cp; + if (REOP_IS_SIMPLE(op)) { + if (!SimpleMatch(gData, x, op, &pc, TRUE)) { + op = (REOp) *nextpc++; + pc = nextpc; + continue; + } + result = x; + op = (REOp) *pc++; + } + nextop = (REOp) *nextpc++; + if (!PushBackTrackState(gData, nextop, nextpc, x, startcp, 0, 0)) + goto bad; + continue; + + /* + * Occurs at (successful) end of REOP_ALT, + */ + case REOP_JUMP: + /* + * If we have not gotten a result here, it is because of an + * empty match. Do the same thing REOP_EMPTY would do. + */ + if (!result) + result = x; + + --gData->stateStackTop; + pc += GET_OFFSET(pc); + op = (REOp) *pc++; + continue; + + /* + * Occurs at last (successful) end of REOP_ALT, + */ + case REOP_ENDALT: + /* + * If we have not gotten a result here, it is because of an + * empty match. Do the same thing REOP_EMPTY would do. + */ + if (!result) + result = x; + + --gData->stateStackTop; + op = (REOp) *pc++; + continue; + + case REOP_LPAREN: + pc = ReadCompactIndex(pc, &parenIndex); + TRACE("[ %lu ]\n", (ULONG_PTR)parenIndex); + assert(parenIndex < gData->regexp->parenCount); + if (parenIndex + 1 > parenSoFar) + parenSoFar = parenIndex + 1; + x->parens[parenIndex].index = x->cp - gData->cpbegin; + x->parens[parenIndex].length = 0; + op = (REOp) *pc++; + continue; + + case REOP_RPAREN: + { + ptrdiff_t delta; + + pc = ReadCompactIndex(pc, &parenIndex); + assert(parenIndex < gData->regexp->parenCount); + cap = &x->parens[parenIndex]; + delta = x->cp - (gData->cpbegin + cap->index); + cap->length = (delta < 0) ? 0 : (size_t) delta; + op = (REOp) *pc++; + + if (!result) + result = x; + continue; + } + case REOP_ASSERT: + nextpc = pc + GET_OFFSET(pc); /* start of term after ASSERT */ + pc += ARG_LEN; /* start of ASSERT child */ + op = (REOp) *pc++; + testpc = pc; + if (REOP_IS_SIMPLE(op) && + !SimpleMatch(gData, x, op, &testpc, FALSE)) { + result = NULL; + break; + } + curState->u.assertion.top = + (char *)gData->backTrackSP - (char *)gData->backTrackStack; + curState->u.assertion.sz = gData->cursz; + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + if (!PushBackTrackState(gData, REOP_ASSERTTEST, + nextpc, x, x->cp, 0, 0)) { + goto bad; + } + continue; + + case REOP_ASSERT_NOT: + nextpc = pc + GET_OFFSET(pc); + pc += ARG_LEN; + op = (REOp) *pc++; + testpc = pc; + if (REOP_IS_SIMPLE(op) /* Note - fail to fail! */ && + SimpleMatch(gData, x, op, &testpc, FALSE) && + *testpc == REOP_ASSERTNOTTEST) { + result = NULL; + break; + } + curState->u.assertion.top + = (char *)gData->backTrackSP - + (char *)gData->backTrackStack; + curState->u.assertion.sz = gData->cursz; + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + if (!PushBackTrackState(gData, REOP_ASSERTNOTTEST, + nextpc, x, x->cp, 0, 0)) { + goto bad; + } + continue; + + case REOP_ASSERTTEST: + --gData->stateStackTop; + --curState; + x->cp = gData->cpbegin + curState->index; + gData->backTrackSP = + (REBackTrackData *) ((char *)gData->backTrackStack + + curState->u.assertion.top); + gData->cursz = curState->u.assertion.sz; + if (result) + result = x; + break; + + case REOP_ASSERTNOTTEST: + --gData->stateStackTop; + --curState; + x->cp = gData->cpbegin + curState->index; + gData->backTrackSP = + (REBackTrackData *) ((char *)gData->backTrackStack + + curState->u.assertion.top); + gData->cursz = curState->u.assertion.sz; + result = (!result) ? x : NULL; + break; + case REOP_STAR: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = (UINT)-1; + goto quantcommon; + case REOP_PLUS: + curState->u.quantifier.min = 1; + curState->u.quantifier.max = (UINT)-1; + goto quantcommon; + case REOP_OPT: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = 1; + goto quantcommon; + case REOP_QUANT: + pc = ReadCompactIndex(pc, &k); + curState->u.quantifier.min = k; + pc = ReadCompactIndex(pc, &k); + /* max is k - 1 to use one byte for (UINT)-1 sentinel. */ + curState->u.quantifier.max = k - 1; + assert(curState->u.quantifier.min <= curState->u.quantifier.max); + quantcommon: + if (curState->u.quantifier.max == 0) { + pc = pc + GET_OFFSET(pc); + op = (REOp) *pc++; + result = x; + continue; + } + /* Step over */ + nextpc = pc + ARG_LEN; + op = (REOp) *nextpc++; + startcp = x->cp; + if (REOP_IS_SIMPLE(op)) { + if (!SimpleMatch(gData, x, op, &nextpc, TRUE)) { + if (curState->u.quantifier.min == 0) + result = x; + else + result = NULL; + pc = pc + GET_OFFSET(pc); + break; + } + op = (REOp) *nextpc++; + result = x; + } + curState->index = startcp - gData->cpbegin; + curState->continue_op = REOP_REPEAT; + curState->continue_pc = pc; + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + if (curState->u.quantifier.min == 0 && + !PushBackTrackState(gData, REOP_REPEAT, pc, x, startcp, + 0, 0)) { + goto bad; + } + pc = nextpc; + continue; + + case REOP_ENDCHILD: /* marks the end of a quantifier child */ + pc = curState[-1].continue_pc; + op = (REOp) curState[-1].continue_op; + + if (!result) + result = x; + continue; + + case REOP_REPEAT: + --curState; + do { + --gData->stateStackTop; + if (!result) { + /* Failed, see if we have enough children. */ + if (curState->u.quantifier.min == 0) + goto repeatDone; + goto break_switch; + } + if (curState->u.quantifier.min == 0 && + x->cp == gData->cpbegin + curState->index) { + /* matched an empty string, that'll get us nowhere */ + result = NULL; + goto break_switch; + } + if (curState->u.quantifier.min != 0) + curState->u.quantifier.min--; + if (curState->u.quantifier.max != (UINT) -1) + curState->u.quantifier.max--; + if (curState->u.quantifier.max == 0) + goto repeatDone; + nextpc = pc + ARG_LEN; + nextop = (REOp) *nextpc; + startcp = x->cp; + if (REOP_IS_SIMPLE(nextop)) { + nextpc++; + if (!SimpleMatch(gData, x, nextop, &nextpc, TRUE)) { + if (curState->u.quantifier.min == 0) + goto repeatDone; + result = NULL; + goto break_switch; + } + result = x; + } + curState->index = startcp - gData->cpbegin; + PUSH_STATE_STACK(gData); + if (curState->u.quantifier.min == 0 && + !PushBackTrackState(gData, REOP_REPEAT, + pc, x, startcp, + curState->parenSoFar, + parenSoFar - + curState->parenSoFar)) { + goto bad; + } + } while (*nextpc == REOP_ENDCHILD); + pc = nextpc; + op = (REOp) *pc++; + parenSoFar = curState->parenSoFar; + continue; + + repeatDone: + result = x; + pc += GET_OFFSET(pc); + goto break_switch; + + case REOP_MINIMALSTAR: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = (UINT)-1; + goto minimalquantcommon; + case REOP_MINIMALPLUS: + curState->u.quantifier.min = 1; + curState->u.quantifier.max = (UINT)-1; + goto minimalquantcommon; + case REOP_MINIMALOPT: + curState->u.quantifier.min = 0; + curState->u.quantifier.max = 1; + goto minimalquantcommon; + case REOP_MINIMALQUANT: + pc = ReadCompactIndex(pc, &k); + curState->u.quantifier.min = k; + pc = ReadCompactIndex(pc, &k); + /* See REOP_QUANT comments about k - 1. */ + curState->u.quantifier.max = k - 1; + assert(curState->u.quantifier.min + <= curState->u.quantifier.max); + minimalquantcommon: + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + if (curState->u.quantifier.min != 0) { + curState->continue_op = REOP_MINIMALREPEAT; + curState->continue_pc = pc; + /* step over */ + pc += OFFSET_LEN; + op = (REOp) *pc++; + } else { + if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, + pc, x, x->cp, 0, 0)) { + goto bad; + } + --gData->stateStackTop; + pc = pc + GET_OFFSET(pc); + op = (REOp) *pc++; + } + continue; + + case REOP_MINIMALREPEAT: + --gData->stateStackTop; + --curState; + + TRACE("{%d,%d}\n", curState->u.quantifier.min, curState->u.quantifier.max); +#define PREPARE_REPEAT() \ + do { \ + curState->index = x->cp - gData->cpbegin; \ + curState->continue_op = REOP_MINIMALREPEAT; \ + curState->continue_pc = pc; \ + pc += ARG_LEN; \ + for (k = curState->parenSoFar; k < parenSoFar; k++) \ + x->parens[k].index = -1; \ + PUSH_STATE_STACK(gData); \ + op = (REOp) *pc++; \ + assert(op < REOP_LIMIT); \ + }while(0) + + if (!result) { + TRACE(" -\n"); + /* + * Non-greedy failure - try to consume another child. + */ + if (curState->u.quantifier.max == (UINT) -1 || + curState->u.quantifier.max > 0) { + PREPARE_REPEAT(); + continue; + } + /* Don't need to adjust pc since we're going to pop. */ + break; + } + if (curState->u.quantifier.min == 0 && + x->cp == gData->cpbegin + curState->index) { + /* Matched an empty string, that'll get us nowhere. */ + result = NULL; + break; + } + if (curState->u.quantifier.min != 0) + curState->u.quantifier.min--; + if (curState->u.quantifier.max != (UINT) -1) + curState->u.quantifier.max--; + if (curState->u.quantifier.min != 0) { + PREPARE_REPEAT(); + continue; + } + curState->index = x->cp - gData->cpbegin; + curState->parenSoFar = parenSoFar; + PUSH_STATE_STACK(gData); + if (!PushBackTrackState(gData, REOP_MINIMALREPEAT, + pc, x, x->cp, + curState->parenSoFar, + parenSoFar - curState->parenSoFar)) { + goto bad; + } + --gData->stateStackTop; + pc = pc + GET_OFFSET(pc); + op = (REOp) *pc++; + assert(op < REOP_LIMIT); + continue; + default: + assert(FALSE); + result = NULL; + } + break_switch:; + } + + /* + * If the match failed and there's a backtrack option, take it. + * Otherwise this is a complete and utter failure. + */ + if (!result) { + if (gData->cursz == 0) + return NULL; + + /* Potentially detect explosive regex here. */ + gData->backTrackCount++; + if (gData->backTrackLimit && + gData->backTrackCount >= gData->backTrackLimit) { + JS_ReportErrorNumber(gData->cx, js_GetErrorMessage, NULL, + JSMSG_REGEXP_TOO_COMPLEX); + gData->ok = FALSE; + return NULL; + } + + backTrackData = gData->backTrackSP; + gData->cursz = backTrackData->sz; + gData->backTrackSP = + (REBackTrackData *) ((char *)backTrackData - backTrackData->sz); + x->cp = backTrackData->cp; + pc = backTrackData->backtrack_pc; + op = (REOp) backTrackData->backtrack_op; + assert(op < REOP_LIMIT); + gData->stateStackTop = backTrackData->saveStateStackTop; + assert(gData->stateStackTop); + + memcpy(gData->stateStack, backTrackData + 1, + sizeof(REProgState) * backTrackData->saveStateStackTop); + curState = &gData->stateStack[gData->stateStackTop - 1]; + + if (backTrackData->parenCount) { + memcpy(&x->parens[backTrackData->parenIndex], + (char *)(backTrackData + 1) + + sizeof(REProgState) * backTrackData->saveStateStackTop, + sizeof(RECapture) * backTrackData->parenCount); + parenSoFar = backTrackData->parenIndex + backTrackData->parenCount; + } else { + for (k = curState->parenSoFar; k < parenSoFar; k++) + x->parens[k].index = -1; + parenSoFar = curState->parenSoFar; + } + + TRACE("\tBT_Pop: %ld,%ld\n", + (ULONG_PTR)backTrackData->parenIndex, + (ULONG_PTR)backTrackData->parenCount); + continue; + } + x = result; + + /* + * Continue with the expression. + */ + op = (REOp)*pc++; + assert(op < REOP_LIMIT); + } + +bad: + TRACE("\n"); + return NULL; + +good: + TRACE("\n"); + return x; +} + +static match_state_t *MatchRegExp(REGlobalData *gData, match_state_t *x) +{ + match_state_t *result; + const WCHAR *cp = x->cp; + const WCHAR *cp2; + UINT j; + + /* + * Have to include the position beyond the last character + * in order to detect end-of-input/line condition. + */ + for (cp2 = cp; cp2 <= gData->cpend; cp2++) { + gData->skipped = cp2 - cp; + x->cp = cp2; + for (j = 0; j < gData->regexp->parenCount; j++) + x->parens[j].index = -1; + result = ExecuteREBytecode(gData, x); + if (!gData->ok || result || (gData->regexp->flags & REG_STICKY)) + return result; + gData->backTrackSP = gData->backTrackStack; + gData->cursz = 0; + gData->stateStackTop = 0; + cp2 = cp + gData->skipped; + } + return NULL; +} + +static HRESULT InitMatch(regexp_t *re, void *cx, heap_pool_t *pool, REGlobalData *gData) +{ + UINT i; + + gData->backTrackStackSize = INITIAL_BACKTRACK; + gData->backTrackStack = heap_pool_alloc(gData->pool, INITIAL_BACKTRACK); + if (!gData->backTrackStack) + goto bad; + + gData->backTrackSP = gData->backTrackStack; + gData->cursz = 0; + gData->backTrackCount = 0; + gData->backTrackLimit = 0; + + gData->stateStackLimit = INITIAL_STATESTACK; + gData->stateStack = heap_pool_alloc(gData->pool, sizeof(REProgState) * INITIAL_STATESTACK); + if (!gData->stateStack) + goto bad; + + gData->stateStackTop = 0; + gData->cx = cx; + gData->pool = pool; + gData->regexp = re; + gData->ok = TRUE; + + for (i = 0; i < re->classCount; i++) { + if (!re->classList[i].converted && + !ProcessCharSet(gData, &re->classList[i])) { + return E_FAIL; + } + } + + return S_OK; + +bad: + js_ReportOutOfScriptQuota(cx); + gData->ok = FALSE; + return E_OUTOFMEMORY; +} + +HRESULT regexp_execute(regexp_t *regexp, void *cx, heap_pool_t *pool, + const WCHAR *str, DWORD str_len, match_state_t *result) +{ + match_state_t *res; + REGlobalData gData; + heap_pool_t *mark = heap_pool_mark(pool); + const WCHAR *str_beg = result->cp; + HRESULT hres; + + assert(result->cp != NULL); + + gData.cpbegin = str; + gData.cpend = str+str_len; + gData.start = result->cp-str; + gData.skipped = 0; + gData.pool = pool; + + hres = InitMatch(regexp, cx, pool, &gData); + if(FAILED(hres)) { + WARN("InitMatch failed\n"); + heap_pool_clear(mark); + return hres; + } + + res = MatchRegExp(&gData, result); + heap_pool_clear(mark); + if(!gData.ok) { + WARN("MatchRegExp failed\n"); + return E_FAIL; + } + + if(!res) { + result->match_len = 0; + return S_FALSE; + } + + result->match_len = (result->cp-str_beg) - gData.skipped; + result->paren_count = regexp->parenCount; + return S_OK; +} + +void regexp_destroy(regexp_t *re) +{ + if (re->classList) { + UINT i; + for (i = 0; i < re->classCount; i++) { + if (re->classList[i].converted) + heap_free(re->classList[i].u.bits); + re->classList[i].u.bits = NULL; + } + heap_free(re->classList); + } + heap_free(re); +} + +regexp_t* regexp_new(void *cx, heap_pool_t *pool, const WCHAR *str, + DWORD str_len, WORD flags, BOOL flat) +{ + regexp_t *re; + heap_pool_t *mark; + CompilerState state; + size_t resize; + jsbytecode *endPC; + UINT i; + size_t len; + + re = NULL; + mark = heap_pool_mark(pool); + len = str_len; + + state.context = cx; + state.pool = pool; + state.cp = str; + if (!state.cp) + goto out; + state.cpbegin = state.cp; + state.cpend = state.cp + len; + state.flags = flags; + state.parenCount = 0; + state.classCount = 0; + state.progLength = 0; + state.treeDepth = 0; + state.classBitmapsMem = 0; + for (i = 0; i < CLASS_CACHE_SIZE; i++) + state.classCache[i].start = NULL; + + if (len != 0 && flat) { + state.result = NewRENode(&state, REOP_FLAT); + if (!state.result) + goto out; + state.result->u.flat.chr = *state.cpbegin; + state.result->u.flat.length = len; + state.result->kid = (void *) state.cpbegin; + /* Flat bytecode: REOP_FLAT compact(string_offset) compact(len). */ + state.progLength += 1 + GetCompactIndexWidth(0) + + GetCompactIndexWidth(len); + } else { + if (!ParseRegExp(&state)) + goto out; + } + resize = offsetof(regexp_t, program) + state.progLength + 1; + re = heap_alloc(resize); + if (!re) + goto out; + + assert(state.classBitmapsMem <= CLASS_BITMAPS_MEM_LIMIT); + re->classCount = state.classCount; + if (re->classCount) { + re->classList = heap_alloc(re->classCount * sizeof(RECharSet)); + if (!re->classList) { + regexp_destroy(re); + re = NULL; + goto out; + } + for (i = 0; i < re->classCount; i++) + re->classList[i].converted = FALSE; + } else { + re->classList = NULL; + } + endPC = EmitREBytecode(&state, re, state.treeDepth, re->program, state.result); + if (!endPC) { + regexp_destroy(re); + re = NULL; + goto out; + } + *endPC++ = REOP_END; + /* + * Check whether size was overestimated and shrink using realloc. + * This is safe since no pointers to newly parsed regexp or its parts + * besides re exist here. + */ + if ((size_t)(endPC - re->program) != state.progLength + 1) { + regexp_t *tmp; + assert((size_t)(endPC - re->program) < state.progLength + 1); + resize = offsetof(regexp_t, program) + (endPC - re->program); + tmp = heap_realloc(re, resize); + if (tmp) + re = tmp; + } + + re->flags = flags; + re->parenCount = state.parenCount; + re->source = str; + re->source_len = str_len; + +out: + heap_pool_clear(mark); + return re; +} + +HRESULT regexp_set_flags(regexp_t **regexp, void *cx, heap_pool_t *pool, WORD flags) +{ + if(((*regexp)->flags & REG_FOLD) != (flags & REG_FOLD)) { + regexp_t *new_regexp = regexp_new(cx, pool, (*regexp)->source, + (*regexp)->source_len, flags, FALSE); + + if(!new_regexp) + return E_FAIL; + + regexp_destroy(*regexp); + *regexp = new_regexp; + }else { + (*regexp)->flags = flags; + } + + return S_OK; +} diff --git a/reactos/dll/win32/vbscript/regexp.h b/reactos/dll/win32/vbscript/regexp.h new file mode 100644 index 00000000000..5ceb8a0c77a --- /dev/null +++ b/reactos/dll/win32/vbscript/regexp.h @@ -0,0 +1,82 @@ +/* + * Copyright 2008 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* + * Code in this file is based on files: + * js/src/jsregexp.h + * js/src/jsregexp.c + * from Mozilla project, released under LGPL 2.1 or later. + * + * The Original Code is Mozilla Communicator client code, released + * March 31, 1998. + * + * The Initial Developer of the Original Code is + * Netscape Communications Corporation. + * Portions created by the Initial Developer are Copyright (C) 1998 + * the Initial Developer. All Rights Reserved. + */ + +#define REG_FOLD 0x01 /* fold uppercase to lowercase */ +#define REG_GLOB 0x02 /* global exec, creates array of matches */ +#define REG_MULTILINE 0x04 /* treat ^ and $ as begin and end of line */ +#define REG_STICKY 0x08 /* only match starting at lastIndex */ + +typedef struct RECapture { + ptrdiff_t index; /* start of contents, -1 for empty */ + size_t length; /* length of capture */ +} RECapture; + +typedef struct match_state_t { + const WCHAR *cp; + DWORD match_len; + + DWORD paren_count; + RECapture parens[1]; +} match_state_t; + +typedef BYTE jsbytecode; + +typedef struct regexp_t { + WORD flags; /* flags, see jsapi.h's REG_* defines */ + size_t parenCount; /* number of parenthesized submatches */ + size_t classCount; /* count [...] bitmaps */ + struct RECharSet *classList; /* list of [...] bitmaps */ + const WCHAR *source; /* locked source string, sans // */ + DWORD source_len; + jsbytecode program[1]; /* regular expression bytecode */ +} regexp_t; + +regexp_t* regexp_new(void*, heap_pool_t*, const WCHAR*, DWORD, WORD, BOOL) DECLSPEC_HIDDEN; +void regexp_destroy(regexp_t*) DECLSPEC_HIDDEN; +HRESULT regexp_execute(regexp_t*, void*, heap_pool_t*, const WCHAR*, + DWORD, match_state_t*) DECLSPEC_HIDDEN; +HRESULT regexp_set_flags(regexp_t**, void*, heap_pool_t*, WORD) DECLSPEC_HIDDEN; + +static inline match_state_t* alloc_match_state(regexp_t *regexp, + heap_pool_t *pool, const WCHAR *pos) +{ + size_t size = offsetof(match_state_t, parens) + regexp->parenCount*sizeof(RECapture); + match_state_t *ret; + + ret = pool ? heap_pool_alloc(pool, size) : heap_alloc(size); + if(!ret) + return NULL; + + ret->cp = pos; + return ret; +} diff --git a/reactos/dll/win32/vbscript/vbdisp.c b/reactos/dll/win32/vbscript/vbdisp.c new file mode 100644 index 00000000000..77cdcb95088 --- /dev/null +++ b/reactos/dll/win32/vbscript/vbdisp.c @@ -0,0 +1,971 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include "vbscript.h" + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(vbscript); + +#define FDEX_VERSION_MASK 0xf0000000 + +static inline BOOL is_func_id(vbdisp_t *This, DISPID id) +{ + return id < This->desc->func_cnt; +} + +static BOOL get_func_id(vbdisp_t *This, const WCHAR *name, vbdisp_invoke_type_t invoke_type, BOOL search_private, DISPID *id) +{ + unsigned i; + + for(i = invoke_type == VBDISP_ANY ? 0 : 1; i < This->desc->func_cnt; i++) { + if(invoke_type == VBDISP_ANY) { + if(!search_private && !This->desc->funcs[i].is_public) + continue; + if(!i && !This->desc->funcs[0].name) /* default value may not exist */ + continue; + }else { + if(!This->desc->funcs[i].entries[invoke_type] + || (!search_private && !This->desc->funcs[i].entries[invoke_type]->is_public)) + continue; + } + + if(!strcmpiW(This->desc->funcs[i].name, name)) { + *id = i; + return TRUE; + } + } + + return FALSE; +} + +HRESULT vbdisp_get_id(vbdisp_t *This, BSTR name, vbdisp_invoke_type_t invoke_type, BOOL search_private, DISPID *id) +{ + unsigned i; + + if(get_func_id(This, name, invoke_type, search_private, id)) + return S_OK; + + for(i=0; i < This->desc->prop_cnt; i++) { + if(!search_private && !This->desc->props[i].is_public) + continue; + + if(!strcmpiW(This->desc->props[i].name, name)) { + *id = i + This->desc->func_cnt; + return S_OK; + } + } + + if(This->desc->typeinfo) { + HRESULT hres; + + hres = ITypeInfo_GetIDsOfNames(This->desc->typeinfo, &name, 1, id); + if(SUCCEEDED(hres)) + return S_OK; + } + + *id = -1; + return DISP_E_UNKNOWNNAME; +} + +static VARIANT *get_propput_arg(const DISPPARAMS *dp) +{ + unsigned i; + + for(i=0; i < dp->cNamedArgs; i++) { + if(dp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT) + return dp->rgvarg+i; + } + + return NULL; +} + +static HRESULT invoke_variant_prop(VARIANT *v, WORD flags, DISPPARAMS *dp, VARIANT *res) +{ + HRESULT hres; + + switch(flags) { + case DISPATCH_PROPERTYGET|DISPATCH_METHOD: + case DISPATCH_PROPERTYGET: + if(dp->cArgs) { + WARN("called with arguments\n"); + return DISP_E_MEMBERNOTFOUND; /* That's what tests show */ + } + + hres = VariantCopy(res, v); + break; + + case DISPATCH_PROPERTYPUT: { + VARIANT *put_val; + + put_val = get_propput_arg(dp); + if(!put_val) { + WARN("no value to set\n"); + return DISP_E_PARAMNOTOPTIONAL; + } + + if(res) + V_VT(res) = VT_EMPTY; + + hres = VariantCopy(v, put_val); + break; + } + + default: + FIXME("unimplemented flags %x\n", flags); + return E_NOTIMPL; + } + + return hres; +} + +static HRESULT invoke_builtin(vbdisp_t *This, const builtin_prop_t *prop, WORD flags, DISPPARAMS *dp, VARIANT *res) +{ + VARIANT args[8]; + unsigned argn, i; + + switch(flags) { + case DISPATCH_PROPERTYGET: + if(!(prop->flags & (BP_GET|BP_GETPUT))) { + FIXME("property does not support DISPATCH_PROPERTYGET\n"); + return E_FAIL; + } + break; + case DISPATCH_PROPERTYGET|DISPATCH_METHOD: + if(!prop->proc && prop->flags == BP_GET) { + const int vt = prop->min_args, val = prop->max_args; + switch(vt) { + case VT_I2: + V_VT(res) = VT_I2; + V_I2(res) = val; + break; + case VT_I4: + V_VT(res) = VT_I4; + V_I4(res) = val; + break; + case VT_BSTR: { + const string_constant_t *str = (const string_constant_t*)prop->max_args; + BSTR ret; + + ret = SysAllocStringLen(str->buf, str->len); + if(!ret) + return E_OUTOFMEMORY; + + V_VT(res) = VT_BSTR; + V_BSTR(res) = ret; + break; + } + DEFAULT_UNREACHABLE; + } + return S_OK; + } + break; + case DISPATCH_METHOD: + if(prop->flags & (BP_GET|BP_GETPUT)) { + FIXME("Call on property\n"); + return E_FAIL; + } + break; + case DISPATCH_PROPERTYPUT: + if(!(prop->flags & BP_GETPUT)) { + FIXME("property does not support DISPATCH_PROPERTYPUT\n"); + return E_FAIL; + } + + FIXME("call put\n"); + return E_NOTIMPL; + default: + FIXME("unsupported flags %x\n", flags); + return E_NOTIMPL; + } + + argn = arg_cnt(dp); + + if(argn < prop->min_args || argn > (prop->max_args ? prop->max_args : prop->min_args)) { + FIXME("invalid number of arguments\n"); + return E_FAIL; + } + + assert(argn < sizeof(args)/sizeof(*args)); + + for(i=0; i < argn; i++) { + if(V_VT(dp->rgvarg+dp->cArgs-i-1) == (VT_BYREF|VT_VARIANT)) + args[i] = *V_VARIANTREF(dp->rgvarg+dp->cArgs-i-1); + else + args[i] = dp->rgvarg[dp->cArgs-i-1]; + } + + return prop->proc(This, args, dp->cArgs, res); +} + +static BOOL run_terminator(vbdisp_t *This) +{ + DISPPARAMS dp = {0}; + + if(This->terminator_ran) + return TRUE; + This->terminator_ran = TRUE; + + if(!This->desc->class_terminate_id) + return TRUE; + + This->ref++; + exec_script(This->desc->ctx, This->desc->funcs[This->desc->class_terminate_id].entries[VBDISP_CALLGET], + (IDispatch*)&This->IDispatchEx_iface, &dp, NULL); + return !--This->ref; +} + +static void clean_props(vbdisp_t *This) +{ + unsigned i; + + if(!This->desc) + return; + + for(i=0; i < This->desc->prop_cnt; i++) + VariantClear(This->props+i); +} + +static inline vbdisp_t *impl_from_IDispatchEx(IDispatchEx *iface) +{ + return CONTAINING_RECORD(iface, vbdisp_t, IDispatchEx_iface); +} + +static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + + if(IsEqualGUID(&IID_IUnknown, riid)) { + TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); + *ppv = &This->IDispatchEx_iface; + }else if(IsEqualGUID(&IID_IDispatch, riid)) { + TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv); + *ppv = &This->IDispatchEx_iface; + }else if(IsEqualGUID(&IID_IDispatchEx, riid)) { + TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv); + *ppv = &This->IDispatchEx_iface; + }else { + WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI DispatchEx_AddRef(IDispatchEx *iface) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if(!ref && run_terminator(This)) { + clean_props(This); + list_remove(&This->entry); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI DispatchEx_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + + TRACE("(%p)->(%p)\n", This, pctinfo); + + *pctinfo = 1; + return S_OK; +} + +static HRESULT WINAPI DispatchEx_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid, + ITypeInfo **ppTInfo) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetIDsOfNames(IDispatchEx *iface, REFIID riid, + LPOLESTR *rgszNames, UINT cNames, LCID lcid, + DISPID *rgDispId) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + FIXME("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, + lcid, rgDispId); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + FIXME("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), + lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + + TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid); + + grfdex &= ~FDEX_VERSION_MASK; + + if(!This->desc) + return E_UNEXPECTED; + + /* Tests show that fdexNameCaseSensitive is ignored */ + + if(grfdex & ~(fdexNameEnsure|fdexNameCaseInsensitive|fdexNameCaseSensitive)) { + FIXME("unsupported flags %x\n", grfdex); + return E_NOTIMPL; + } + + return vbdisp_get_id(This, bstrName, VBDISP_ANY, FALSE, pid); +} + +static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + + TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller); + + if(!This->desc) + return E_UNEXPECTED; + + if(pvarRes) + V_VT(pvarRes) = VT_EMPTY; + + if(id < 0) + return DISP_E_MEMBERNOTFOUND; + + if(is_func_id(This, id)) { + function_t *func; + + switch(wFlags) { + case DISPATCH_METHOD: + case DISPATCH_METHOD|DISPATCH_PROPERTYGET: + func = This->desc->funcs[id].entries[VBDISP_CALLGET]; + if(!func) { + FIXME("no invoke/getter\n"); + return DISP_E_MEMBERNOTFOUND; + } + + return exec_script(This->desc->ctx, func, (IDispatch*)&This->IDispatchEx_iface, pdp, pvarRes); + case DISPATCH_PROPERTYPUT: { + VARIANT *put_val; + DISPPARAMS dp = {NULL, NULL, 1, 0}; + + if(arg_cnt(pdp)) { + FIXME("arguments not implemented\n"); + return E_NOTIMPL; + } + + put_val = get_propput_arg(pdp); + if(!put_val) { + WARN("no value to set\n"); + return DISP_E_PARAMNOTOPTIONAL; + } + + dp.rgvarg = put_val; + func = This->desc->funcs[id].entries[V_VT(put_val) == VT_DISPATCH ? VBDISP_SET : VBDISP_LET]; + if(!func) { + FIXME("no letter/setter\n"); + return DISP_E_MEMBERNOTFOUND; + } + + return exec_script(This->desc->ctx, func, (IDispatch*)&This->IDispatchEx_iface, &dp, NULL); + } + default: + FIXME("flags %x\n", wFlags); + return DISP_E_MEMBERNOTFOUND; + } + } + + if(id < This->desc->prop_cnt + This->desc->func_cnt) + return invoke_variant_prop(This->props+(id-This->desc->func_cnt), wFlags, pdp, pvarRes); + + if(This->desc->builtin_prop_cnt) { + unsigned min = 0, max = This->desc->builtin_prop_cnt-1, i; + + while(min <= max) { + i = (min+max)/2; + if(This->desc->builtin_props[i].id == id) + return invoke_builtin(This, This->desc->builtin_props+i, wFlags, pdp, pvarRes); + if(This->desc->builtin_props[i].id < id) + min = i+1; + else + max = i-1; + } + } + + return DISP_E_MEMBERNOTFOUND; +} + +static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + FIXME("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + FIXME("(%p)->(%x)\n", This, id); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + FIXME("(%p)->(%x %p)\n", This, id, pbstrName); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + FIXME("(%p)->(%x %x %p)\n", This, grfdex, id, pid); + return E_NOTIMPL; +} + +static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk) +{ + vbdisp_t *This = impl_from_IDispatchEx(iface); + FIXME("(%p)->(%p)\n", This, ppunk); + return E_NOTIMPL; +} + +static IDispatchExVtbl DispatchExVtbl = { + DispatchEx_QueryInterface, + DispatchEx_AddRef, + DispatchEx_Release, + DispatchEx_GetTypeInfoCount, + DispatchEx_GetTypeInfo, + DispatchEx_GetIDsOfNames, + DispatchEx_Invoke, + DispatchEx_GetDispID, + DispatchEx_InvokeEx, + DispatchEx_DeleteMemberByName, + DispatchEx_DeleteMemberByDispID, + DispatchEx_GetMemberProperties, + DispatchEx_GetMemberName, + DispatchEx_GetNextDispID, + DispatchEx_GetNameSpaceParent +}; + +static inline vbdisp_t *unsafe_impl_from_IDispatch(IDispatch *iface) +{ + return iface->lpVtbl == (IDispatchVtbl*)&DispatchExVtbl + ? CONTAINING_RECORD(iface, vbdisp_t, IDispatchEx_iface) + : NULL; +} + +HRESULT create_vbdisp(const class_desc_t *desc, vbdisp_t **ret) +{ + vbdisp_t *vbdisp; + + vbdisp = heap_alloc_zero( FIELD_OFFSET( vbdisp_t, props[desc->prop_cnt] )); + if(!vbdisp) + return E_OUTOFMEMORY; + + vbdisp->IDispatchEx_iface.lpVtbl = &DispatchExVtbl; + vbdisp->ref = 1; + vbdisp->desc = desc; + + list_add_tail(&desc->ctx->objects, &vbdisp->entry); + + if(desc->class_initialize_id) { + DISPPARAMS dp = {0}; + HRESULT hres; + + hres = exec_script(desc->ctx, desc->funcs[desc->class_initialize_id].entries[VBDISP_CALLGET], + (IDispatch*)&vbdisp->IDispatchEx_iface, &dp, NULL); + if(FAILED(hres)) { + IDispatchEx_Release(&vbdisp->IDispatchEx_iface); + return hres; + } + } + + *ret = vbdisp; + return S_OK; +} + +static HRESULT Procedure_invoke(vbdisp_t *This, VARIANT *args, unsigned args_cnt, VARIANT *res) +{ + script_ctx_t *ctx = This->desc->ctx; + HRESULT hres; + + TRACE("\n"); + + IActiveScriptSite_OnEnterScript(ctx->site); + hres = exec_script(ctx, This->desc->value_func, NULL, NULL, NULL); + IActiveScriptSite_OnLeaveScript(ctx->site); + + return hres; +} + +static const builtin_prop_t procedure_props[] = { + {DISPID_VALUE, Procedure_invoke, 0} +}; + +HRESULT create_procedure_disp(script_ctx_t *ctx, vbscode_t *code, IDispatch **ret) +{ + class_desc_t *desc; + vbdisp_t *vbdisp; + HRESULT hres; + + desc = heap_alloc_zero(sizeof(*desc)); + if(!desc) + return E_OUTOFMEMORY; + + desc->ctx = ctx; + desc->builtin_prop_cnt = sizeof(procedure_props)/sizeof(*procedure_props); + desc->builtin_props = procedure_props; + desc->value_func = &code->main_code; + + hres = create_vbdisp(desc, &vbdisp); + if(FAILED(hres)) { + heap_free(desc); + return hres; + } + + desc->next = ctx->procs; + ctx->procs = desc; + + *ret = (IDispatch*)&vbdisp->IDispatchEx_iface; + return S_OK; +} + +struct _ident_map_t { + const WCHAR *name; + BOOL is_var; + union { + dynamic_var_t *var; + function_t *func; + } u; +}; + +static inline DISPID ident_to_id(ScriptDisp *This, ident_map_t *ident) +{ + return (ident-This->ident_map)+1; +} + +static inline ident_map_t *id_to_ident(ScriptDisp *This, DISPID id) +{ + return 0 < id && id <= This->ident_map_cnt ? This->ident_map+id-1 : NULL; +} + +static ident_map_t *add_ident(ScriptDisp *This, const WCHAR *name) +{ + ident_map_t *ret; + + if(!This->ident_map_size) { + This->ident_map = heap_alloc(4 * sizeof(*This->ident_map)); + if(!This->ident_map) + return NULL; + This->ident_map_size = 4; + }else if(This->ident_map_cnt == This->ident_map_size) { + ident_map_t *new_map; + + new_map = heap_realloc(This->ident_map, 2*This->ident_map_size*sizeof(*new_map)); + if(!new_map) + return NULL; + This->ident_map = new_map; + This->ident_map_size *= 2; + } + + ret = This->ident_map + This->ident_map_cnt++; + ret->name = name; + return ret; +} + +static inline ScriptDisp *ScriptDisp_from_IDispatchEx(IDispatchEx *iface) +{ + return CONTAINING_RECORD(iface, ScriptDisp, IDispatchEx_iface); +} + +static HRESULT WINAPI ScriptDisp_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + + if(IsEqualGUID(&IID_IUnknown, riid)) { + TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); + *ppv = &This->IDispatchEx_iface; + }else if(IsEqualGUID(&IID_IDispatch, riid)) { + TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv); + *ppv = &This->IDispatchEx_iface; + }else if(IsEqualGUID(&IID_IDispatchEx, riid)) { + TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv); + *ppv = &This->IDispatchEx_iface; + }else { + WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI ScriptDisp_AddRef(IDispatchEx *iface) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI ScriptDisp_Release(IDispatchEx *iface) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if(!ref) { + assert(!This->ctx); + heap_free(This->ident_map); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI ScriptDisp_GetTypeInfoCount(IDispatchEx *iface, UINT *pctinfo) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + + TRACE("(%p)->(%p)\n", This, pctinfo); + + *pctinfo = 1; + return S_OK; +} + +static HRESULT WINAPI ScriptDisp_GetTypeInfo(IDispatchEx *iface, UINT iTInfo, LCID lcid, + ITypeInfo **ppTInfo) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptDisp_GetIDsOfNames(IDispatchEx *iface, REFIID riid, + LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + UINT i; + HRESULT hres; + + TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), rgszNames, cNames, + lcid, rgDispId); + + for(i=0; i < cNames; i++) { + hres = IDispatchEx_GetDispID(&This->IDispatchEx_iface, rgszNames[i], 0, rgDispId+i); + if(FAILED(hres)) + return hres; + } + + return S_OK; +} + +static HRESULT WINAPI ScriptDisp_Invoke(IDispatchEx *iface, DISPID dispIdMember, REFIID riid, LCID lcid, + WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + + TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), + lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + + return IDispatchEx_InvokeEx(&This->IDispatchEx_iface, dispIdMember, lcid, wFlags, + pDispParams, pVarResult, pExcepInfo, NULL); +} + +static HRESULT WINAPI ScriptDisp_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + dynamic_var_t *var; + ident_map_t *ident; + function_t *func; + + TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid); + + if(!This->ctx) + return E_UNEXPECTED; + + for(ident = This->ident_map; ident < This->ident_map+This->ident_map_cnt; ident++) { + if(!strcmpiW(ident->name, bstrName)) { + *pid = ident_to_id(This, ident); + return S_OK; + } + } + + for(var = This->ctx->global_vars; var; var = var->next) { + if(!strcmpiW(var->name, bstrName)) { + ident = add_ident(This, var->name); + if(!ident) + return E_OUTOFMEMORY; + + ident->is_var = TRUE; + ident->u.var = var; + *pid = ident_to_id(This, ident); + return S_OK; + } + } + + for(func = This->ctx->global_funcs; func; func = func->next) { + if(!strcmpiW(func->name, bstrName)) { + ident = add_ident(This, func->name); + if(!ident) + return E_OUTOFMEMORY; + + ident->is_var = FALSE; + ident->u.func = func; + *pid = ident_to_id(This, ident); + return S_OK; + } + } + + *pid = -1; + return DISP_E_UNKNOWNNAME; +} + +static HRESULT WINAPI ScriptDisp_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, + VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + ident_map_t *ident; + HRESULT hres; + + TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller); + + ident = id_to_ident(This, id); + if(!ident) + return DISP_E_MEMBERNOTFOUND; + + if(ident->is_var) { + if(ident->u.var->is_const) { + FIXME("const not supported\n"); + return E_NOTIMPL; + } + + return invoke_variant_prop(&ident->u.var->v, wFlags, pdp, pvarRes); + } + + switch(wFlags) { + case DISPATCH_METHOD: + case DISPATCH_METHOD|DISPATCH_PROPERTYGET: + IActiveScriptSite_OnEnterScript(This->ctx->site); + hres = exec_script(This->ctx, ident->u.func, NULL, pdp, pvarRes); + IActiveScriptSite_OnLeaveScript(This->ctx->site); + break; + default: + FIXME("Unsupported flags %x\n", wFlags); + hres = E_NOTIMPL; + } + + return hres; +} + +static HRESULT WINAPI ScriptDisp_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + FIXME("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex); + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptDisp_DeleteMemberByDispID(IDispatchEx *iface, DISPID id) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + FIXME("(%p)->(%x)\n", This, id); + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptDisp_GetMemberProperties(IDispatchEx *iface, DISPID id, DWORD grfdexFetch, DWORD *pgrfdex) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + FIXME("(%p)->(%x %x %p)\n", This, id, grfdexFetch, pgrfdex); + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptDisp_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + FIXME("(%p)->(%x %p)\n", This, id, pbstrName); + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptDisp_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + FIXME("(%p)->(%x %x %p)\n", This, grfdex, id, pid); + return E_NOTIMPL; +} + +static HRESULT WINAPI ScriptDisp_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk) +{ + ScriptDisp *This = ScriptDisp_from_IDispatchEx(iface); + FIXME("(%p)->(%p)\n", This, ppunk); + return E_NOTIMPL; +} + +static IDispatchExVtbl ScriptDispVtbl = { + ScriptDisp_QueryInterface, + ScriptDisp_AddRef, + ScriptDisp_Release, + ScriptDisp_GetTypeInfoCount, + ScriptDisp_GetTypeInfo, + ScriptDisp_GetIDsOfNames, + ScriptDisp_Invoke, + ScriptDisp_GetDispID, + ScriptDisp_InvokeEx, + ScriptDisp_DeleteMemberByName, + ScriptDisp_DeleteMemberByDispID, + ScriptDisp_GetMemberProperties, + ScriptDisp_GetMemberName, + ScriptDisp_GetNextDispID, + ScriptDisp_GetNameSpaceParent +}; + +HRESULT create_script_disp(script_ctx_t *ctx, ScriptDisp **ret) +{ + ScriptDisp *script_disp; + + script_disp = heap_alloc_zero(sizeof(*script_disp)); + if(!script_disp) + return E_OUTOFMEMORY; + + script_disp->IDispatchEx_iface.lpVtbl = &ScriptDispVtbl; + script_disp->ref = 1; + script_disp->ctx = ctx; + + *ret = script_disp; + return S_OK; +} + +void collect_objects(script_ctx_t *ctx) +{ + vbdisp_t *iter, *iter2; + + LIST_FOR_EACH_ENTRY_SAFE(iter, iter2, &ctx->objects, vbdisp_t, entry) + run_terminator(iter); + + while(!list_empty(&ctx->objects)) { + iter = LIST_ENTRY(list_head(&ctx->objects), vbdisp_t, entry); + + IDispatchEx_AddRef(&iter->IDispatchEx_iface); + clean_props(iter); + iter->desc = NULL; + list_remove(&iter->entry); + list_init(&iter->entry); + IDispatchEx_Release(&iter->IDispatchEx_iface); + } +} + +HRESULT disp_get_id(IDispatch *disp, BSTR name, vbdisp_invoke_type_t invoke_type, BOOL search_private, DISPID *id) +{ + IDispatchEx *dispex; + vbdisp_t *vbdisp; + HRESULT hres; + + vbdisp = unsafe_impl_from_IDispatch(disp); + if(vbdisp) + return vbdisp_get_id(vbdisp, name, invoke_type, search_private, id); + + hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); + if(FAILED(hres)) { + TRACE("using IDispatch\n"); + return IDispatch_GetIDsOfNames(disp, &IID_NULL, &name, 1, 0, id); + } + + hres = IDispatchEx_GetDispID(dispex, name, fdexNameCaseInsensitive, id); + IDispatchEx_Release(dispex); + return hres; +} + +HRESULT disp_call(script_ctx_t *ctx, IDispatch *disp, DISPID id, DISPPARAMS *dp, VARIANT *retv) +{ + const WORD flags = DISPATCH_METHOD|(retv ? DISPATCH_PROPERTYGET : 0); + IDispatchEx *dispex; + EXCEPINFO ei; + HRESULT hres; + + memset(&ei, 0, sizeof(ei)); + if(retv) + V_VT(retv) = VT_EMPTY; + + hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); + if(FAILED(hres)) { + UINT err = 0; + + TRACE("using IDispatch\n"); + return IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, flags, dp, retv, &ei, &err); + } + + hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, flags, dp, retv, &ei, NULL /* CALLER_FIXME */); + IDispatchEx_Release(dispex); + return hres; +} + +HRESULT disp_propput(script_ctx_t *ctx, IDispatch *disp, DISPID id, DISPPARAMS *dp) +{ + IDispatchEx *dispex; + EXCEPINFO ei = {0}; + HRESULT hres; + + hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); + if(SUCCEEDED(hres)) { + hres = IDispatchEx_InvokeEx(dispex, id, ctx->lcid, DISPATCH_PROPERTYPUT, dp, NULL, &ei, NULL /* FIXME! */); + IDispatchEx_Release(dispex); + }else { + ULONG err = 0; + + TRACE("using IDispatch\n"); + hres = IDispatch_Invoke(disp, id, &IID_NULL, ctx->lcid, DISPATCH_PROPERTYPUT, dp, NULL, &ei, &err); + } + + return hres; +} diff --git a/reactos/dll/win32/vbscript/vbregexp.c b/reactos/dll/win32/vbscript/vbregexp.c new file mode 100644 index 00000000000..c556e541c37 --- /dev/null +++ b/reactos/dll/win32/vbscript/vbregexp.c @@ -0,0 +1,1462 @@ +/* + * Copyright 2013 Piotr Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include "vbscript.h" +#include "regexp.h" +#include "vbsregexp55.h" + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(vbscript); + +#define REGEXP_TID_LIST \ + XDIID(RegExp2), \ + XDIID(Match2), \ + XDIID(MatchCollection2), \ + XDIID(SubMatches) + +typedef enum { +#define XDIID(iface) iface ## _tid + REGEXP_TID_LIST, +#undef XDIID + REGEXP_LAST_tid +} regexp_tid_t; + +static REFIID tid_ids[] = { +#define XDIID(iface) &IID_I ## iface + REGEXP_TID_LIST +#undef XDIID +}; + +static ITypeLib *typelib; +static ITypeInfo *typeinfos[REGEXP_LAST_tid]; + +static HRESULT init_regexp_typeinfo(regexp_tid_t tid) +{ + HRESULT hres; + + if(!typelib) { + static const WCHAR vbscript_dll3W[] = {'v','b','s','c','r','i','p','t','.','d','l','l','\\','3',0}; + ITypeLib *tl; + + hres = LoadTypeLib(vbscript_dll3W, &tl); + if(FAILED(hres)) { + ERR("LoadRegTypeLib failed: %08x\n", hres); + return hres; + } + + if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL)) + ITypeLib_Release(tl); + } + + if(!typeinfos[tid]) { + ITypeInfo *ti; + + hres = ITypeLib_GetTypeInfoOfGuid(typelib, tid_ids[tid], &ti); + if(FAILED(hres)) { + ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(tid_ids[tid]), hres); + return hres; + } + + if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), ti, NULL)) + ITypeInfo_Release(ti); + } + + return S_OK; +} + +struct SubMatches { + ISubMatches ISubMatches_iface; + + LONG ref; + + WCHAR *match; + match_state_t *result; +}; + +typedef struct Match2 { + IMatch2 IMatch2_iface; + + LONG ref; + + DWORD index; + SubMatches *sub_matches; +} Match2; + +typedef struct MatchCollectionEnum { + IEnumVARIANT IEnumVARIANT_iface; + + LONG ref; + + IMatchCollection2 *mc; + LONG pos; + LONG count; +} MatchCollectionEnum; + +typedef struct MatchCollection2 { + IMatchCollection2 IMatchCollection2_iface; + + LONG ref; + + IMatch2 **matches; + DWORD count; + DWORD size; +} MatchCollection2; + +typedef struct RegExp2 { + IRegExp2 IRegExp2_iface; + IRegExp IRegExp_iface; + + LONG ref; + + WCHAR *pattern; + regexp_t *regexp; + heap_pool_t pool; + WORD flags; +} RegExp2; + +static inline SubMatches* impl_from_ISubMatches(ISubMatches *iface) +{ + return CONTAINING_RECORD(iface, SubMatches, ISubMatches_iface); +} + +static HRESULT WINAPI SubMatches_QueryInterface( + ISubMatches *iface, REFIID riid, void **ppv) +{ + SubMatches *This = impl_from_ISubMatches(iface); + + if(IsEqualGUID(riid, &IID_IUnknown)) { + TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); + *ppv = &This->ISubMatches_iface; + }else if(IsEqualGUID(riid, &IID_IDispatch)) { + TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv); + *ppv = &This->ISubMatches_iface; + }else if(IsEqualGUID(riid, &IID_ISubMatches)) { + TRACE("(%p)->(IID_ISubMatches %p)\n", This, ppv); + *ppv = &This->ISubMatches_iface; + }else if(IsEqualGUID(riid, &IID_IDispatchEx)) { + TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv); + *ppv = NULL; + return E_NOINTERFACE; + }else { + FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI SubMatches_AddRef(ISubMatches *iface) +{ + SubMatches *This = impl_from_ISubMatches(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI SubMatches_Release(ISubMatches *iface) +{ + SubMatches *This = impl_from_ISubMatches(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if(!ref) { + heap_free(This->match); + heap_free(This->result); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI SubMatches_GetTypeInfoCount(ISubMatches *iface, UINT *pctinfo) +{ + SubMatches *This = impl_from_ISubMatches(iface); + + TRACE("(%p)->(%p)\n", This, pctinfo); + + *pctinfo = 1; + return S_OK; +} + +static HRESULT WINAPI SubMatches_GetTypeInfo(ISubMatches *iface, + UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) +{ + SubMatches *This = impl_from_ISubMatches(iface); + FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); + return E_NOTIMPL; +} + +static HRESULT WINAPI SubMatches_GetIDsOfNames(ISubMatches *iface, + REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + SubMatches *This = impl_from_ISubMatches(iface); + + TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), + rgszNames, cNames, lcid, rgDispId); + + return ITypeInfo_GetIDsOfNames(typeinfos[SubMatches_tid], rgszNames, cNames, rgDispId); +} + +static HRESULT WINAPI SubMatches_Invoke(ISubMatches *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + SubMatches *This = impl_from_ISubMatches(iface); + + TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), + lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + + return ITypeInfo_Invoke(typeinfos[SubMatches_tid], iface, dispIdMember, wFlags, + pDispParams, pVarResult, pExcepInfo, puArgErr); +} + +static HRESULT WINAPI SubMatches_get_Item(ISubMatches *iface, + LONG index, VARIANT *pSubMatch) +{ + SubMatches *This = impl_from_ISubMatches(iface); + + TRACE("(%p)->(%d %p)\n", This, index, pSubMatch); + + if(!pSubMatch) + return E_POINTER; + + if(!This->result || index<0 || index>=This->result->paren_count) + return E_INVALIDARG; + + if(This->result->parens[index].index == -1) { + V_VT(pSubMatch) = VT_EMPTY; + }else { + V_VT(pSubMatch) = VT_BSTR; + V_BSTR(pSubMatch) = SysAllocStringLen( + This->match+This->result->parens[index].index, + This->result->parens[index].length); + + if(!V_BSTR(pSubMatch)) + return E_OUTOFMEMORY; + } + + return S_OK; +} + +static HRESULT WINAPI SubMatches_get_Count(ISubMatches *iface, LONG *pCount) +{ + SubMatches *This = impl_from_ISubMatches(iface); + + TRACE("(%p)->(%p)\n", This, pCount); + + if(!pCount) + return E_POINTER; + + if(!This->result) + *pCount = 0; + else + *pCount = This->result->paren_count; + return S_OK; +} + +static HRESULT WINAPI SubMatches_get__NewEnum(ISubMatches *iface, IUnknown **ppEnum) +{ + SubMatches *This = impl_from_ISubMatches(iface); + FIXME("(%p)->(%p)\n", This, ppEnum); + return E_NOTIMPL; +} + +static const ISubMatchesVtbl SubMatchesVtbl = { + SubMatches_QueryInterface, + SubMatches_AddRef, + SubMatches_Release, + SubMatches_GetTypeInfoCount, + SubMatches_GetTypeInfo, + SubMatches_GetIDsOfNames, + SubMatches_Invoke, + SubMatches_get_Item, + SubMatches_get_Count, + SubMatches_get__NewEnum +}; + +static HRESULT create_sub_matches(DWORD pos, match_state_t *result, SubMatches **sub_matches) +{ + SubMatches *ret; + DWORD i; + HRESULT hres; + + hres = init_regexp_typeinfo(SubMatches_tid); + if(FAILED(hres)) + return hres; + + ret = heap_alloc_zero(sizeof(*ret)); + if(!ret) + return E_OUTOFMEMORY; + + ret->ISubMatches_iface.lpVtbl = &SubMatchesVtbl; + + ret->result = result; + if(result) { + ret->match = heap_alloc((result->match_len+1) * sizeof(WCHAR)); + if(!ret->match) { + heap_free(ret); + return E_OUTOFMEMORY; + } + memcpy(ret->match, result->cp-result->match_len, result->match_len*sizeof(WCHAR)); + ret->match[result->match_len] = 0; + + result->cp = NULL; + for(i=0; iparen_count; i++) + if(result->parens[i].index != -1) + result->parens[i].index -= pos; + }else { + ret->match = NULL; + } + + ret->ref = 1; + *sub_matches = ret; + return hres; +} + +static inline Match2* impl_from_IMatch2(IMatch2 *iface) +{ + return CONTAINING_RECORD(iface, Match2, IMatch2_iface); +} + +static HRESULT WINAPI Match2_QueryInterface( + IMatch2 *iface, REFIID riid, void **ppv) +{ + Match2 *This = impl_from_IMatch2(iface); + + if(IsEqualGUID(riid, &IID_IUnknown)) { + TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); + *ppv = &This->IMatch2_iface; + }else if(IsEqualGUID(riid, &IID_IDispatch)) { + TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv); + *ppv = &This->IMatch2_iface; + }else if(IsEqualGUID(riid, &IID_IMatch2)) { + TRACE("(%p)->(IID_IMatch2 %p)\n", This, ppv); + *ppv = &This->IMatch2_iface; + }else if(IsEqualGUID(riid, &IID_IDispatchEx)) { + TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv); + *ppv = NULL; + return E_NOINTERFACE; + }else { + FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI Match2_AddRef(IMatch2 *iface) +{ + Match2 *This = impl_from_IMatch2(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI Match2_Release(IMatch2 *iface) +{ + Match2 *This = impl_from_IMatch2(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if(!ref) { + ISubMatches_Release(&This->sub_matches->ISubMatches_iface); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI Match2_GetTypeInfoCount(IMatch2 *iface, UINT *pctinfo) +{ + Match2 *This = impl_from_IMatch2(iface); + + TRACE("(%p)->(%p)\n", This, pctinfo); + + *pctinfo = 1; + return S_OK; +} + +static HRESULT WINAPI Match2_GetTypeInfo(IMatch2 *iface, + UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) +{ + Match2 *This = impl_from_IMatch2(iface); + FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); + return E_NOTIMPL; +} + +static HRESULT WINAPI Match2_GetIDsOfNames(IMatch2 *iface, + REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + Match2 *This = impl_from_IMatch2(iface); + + TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), + rgszNames, cNames, lcid, rgDispId); + + return ITypeInfo_GetIDsOfNames(typeinfos[Match2_tid], rgszNames, cNames, rgDispId); +} + +static HRESULT WINAPI Match2_Invoke(IMatch2 *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + Match2 *This = impl_from_IMatch2(iface); + + TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), + lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + + return ITypeInfo_Invoke(typeinfos[Match2_tid], iface, dispIdMember, wFlags, + pDispParams, pVarResult, pExcepInfo, puArgErr); +} + +static HRESULT WINAPI Match2_get_Value(IMatch2 *iface, BSTR *pValue) +{ + Match2 *This = impl_from_IMatch2(iface); + + TRACE("(%p)->(%p)\n", This, pValue); + + if(!pValue) + return E_POINTER; + + if(!This->sub_matches->match) { + *pValue = NULL; + return S_OK; + } + + *pValue = SysAllocString(This->sub_matches->match); + return *pValue ? S_OK : E_OUTOFMEMORY; +} + +static HRESULT WINAPI Match2_get_FirstIndex(IMatch2 *iface, LONG *pFirstIndex) +{ + Match2 *This = impl_from_IMatch2(iface); + + TRACE("(%p)->(%p)\n", This, pFirstIndex); + + if(!pFirstIndex) + return E_POINTER; + + *pFirstIndex = This->index; + return S_OK; +} + +static HRESULT WINAPI Match2_get_Length(IMatch2 *iface, LONG *pLength) +{ + Match2 *This = impl_from_IMatch2(iface); + + TRACE("(%p)->(%p)\n", This, pLength); + + if(!pLength) + return E_POINTER; + + if(This->sub_matches->result) + *pLength = This->sub_matches->result->match_len; + else + *pLength = 0; + return S_OK; +} + +static HRESULT WINAPI Match2_get_SubMatches(IMatch2 *iface, IDispatch **ppSubMatches) +{ + Match2 *This = impl_from_IMatch2(iface); + + TRACE("(%p)->(%p)\n", This, ppSubMatches); + + if(!ppSubMatches) + return E_POINTER; + + *ppSubMatches = (IDispatch*)&This->sub_matches->ISubMatches_iface; + ISubMatches_AddRef(&This->sub_matches->ISubMatches_iface); + return S_OK; +} + +static const IMatch2Vtbl Match2Vtbl = { + Match2_QueryInterface, + Match2_AddRef, + Match2_Release, + Match2_GetTypeInfoCount, + Match2_GetTypeInfo, + Match2_GetIDsOfNames, + Match2_Invoke, + Match2_get_Value, + Match2_get_FirstIndex, + Match2_get_Length, + Match2_get_SubMatches +}; + +static HRESULT create_match2(DWORD pos, match_state_t **result, IMatch2 **match) +{ + Match2 *ret; + HRESULT hres; + + hres = init_regexp_typeinfo(Match2_tid); + if(FAILED(hres)) + return hres; + + ret = heap_alloc_zero(sizeof(*ret)); + if(!ret) + return E_OUTOFMEMORY; + + ret->index = pos; + hres = create_sub_matches(pos, result ? *result : NULL, &ret->sub_matches); + if(FAILED(hres)) { + heap_free(ret); + return hres; + } + if(result) + *result = NULL; + + ret->IMatch2_iface.lpVtbl = &Match2Vtbl; + + ret->ref = 1; + *match = &ret->IMatch2_iface; + return hres; +} + +static inline MatchCollectionEnum* impl_from_IMatchCollectionEnum(IEnumVARIANT *iface) +{ + return CONTAINING_RECORD(iface, MatchCollectionEnum, IEnumVARIANT_iface); +} + +static HRESULT WINAPI MatchCollectionEnum_QueryInterface( + IEnumVARIANT *iface, REFIID riid, void **ppv) +{ + MatchCollectionEnum *This = impl_from_IMatchCollectionEnum(iface); + + if(IsEqualGUID(riid, &IID_IUnknown)) { + TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); + *ppv = &This->IEnumVARIANT_iface; + }else if(IsEqualGUID(riid, &IID_IEnumVARIANT)) { + TRACE("(%p)->(IID_IEnumVARIANT %p)\n", This, ppv); + *ppv = &This->IEnumVARIANT_iface; + }else { + FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI MatchCollectionEnum_AddRef(IEnumVARIANT *iface) +{ + MatchCollectionEnum *This = impl_from_IMatchCollectionEnum(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI MatchCollectionEnum_Release(IEnumVARIANT *iface) +{ + MatchCollectionEnum *This = impl_from_IMatchCollectionEnum(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if(!ref) { + IMatchCollection2_Release(This->mc); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI MatchCollectionEnum_Next(IEnumVARIANT *iface, + ULONG celt, VARIANT *rgVar, ULONG *pCeltFetched) +{ + MatchCollectionEnum *This = impl_from_IMatchCollectionEnum(iface); + DWORD i; + HRESULT hres = S_OK; + + TRACE("(%p)->(%u %p %p)\n", This, celt, rgVar, pCeltFetched); + + if(This->pos>=This->count) { + if(pCeltFetched) + *pCeltFetched = 0; + return S_FALSE; + } + + for(i=0; ipos+icount; i++) { + V_VT(rgVar+i) = VT_DISPATCH; + hres = IMatchCollection2_get_Item(This->mc, This->pos+i, &V_DISPATCH(rgVar+i)); + if(FAILED(hres)) + break; + } + if(FAILED(hres)) { + while(i--) + VariantClear(rgVar+i); + return hres; + } + + if(pCeltFetched) + *pCeltFetched = i; + This->pos += i; + return S_OK; +} + +static HRESULT WINAPI MatchCollectionEnum_Skip(IEnumVARIANT *iface, ULONG celt) +{ + MatchCollectionEnum *This = impl_from_IMatchCollectionEnum(iface); + + TRACE("(%p)->(%u)\n", This, celt); + + if(This->pos+celt <= This->count) + This->pos += celt; + else + This->pos = This->count; + return S_OK; +} + +static HRESULT WINAPI MatchCollectionEnum_Reset(IEnumVARIANT *iface) +{ + MatchCollectionEnum *This = impl_from_IMatchCollectionEnum(iface); + + TRACE("(%p)\n", This); + + This->pos = 0; + return S_OK; +} + +static HRESULT WINAPI MatchCollectionEnum_Clone(IEnumVARIANT *iface, IEnumVARIANT **ppEnum) +{ + MatchCollectionEnum *This = impl_from_IMatchCollectionEnum(iface); + FIXME("(%p)->(%p)\n", This, ppEnum); + return E_NOTIMPL; +} + +static const IEnumVARIANTVtbl MatchCollectionEnum_Vtbl = { + MatchCollectionEnum_QueryInterface, + MatchCollectionEnum_AddRef, + MatchCollectionEnum_Release, + MatchCollectionEnum_Next, + MatchCollectionEnum_Skip, + MatchCollectionEnum_Reset, + MatchCollectionEnum_Clone +}; + +static HRESULT create_enum_variant_mc2(IMatchCollection2 *mc, ULONG pos, IEnumVARIANT **enum_variant) +{ + MatchCollectionEnum *ret; + + ret = heap_alloc_zero(sizeof(*ret)); + if(!ret) + return E_OUTOFMEMORY; + + ret->IEnumVARIANT_iface.lpVtbl = &MatchCollectionEnum_Vtbl; + ret->ref = 1; + ret->pos = pos; + IMatchCollection2_get_Count(mc, &ret->count); + ret->mc = mc; + IMatchCollection2_AddRef(mc); + + *enum_variant = &ret->IEnumVARIANT_iface; + return S_OK; +} + +static inline MatchCollection2* impl_from_IMatchCollection2(IMatchCollection2 *iface) +{ + return CONTAINING_RECORD(iface, MatchCollection2, IMatchCollection2_iface); +} + +static HRESULT WINAPI MatchCollection2_QueryInterface( + IMatchCollection2 *iface, REFIID riid, void **ppv) +{ + MatchCollection2 *This = impl_from_IMatchCollection2(iface); + + if(IsEqualGUID(riid, &IID_IUnknown)) { + TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); + *ppv = &This->IMatchCollection2_iface; + }else if(IsEqualGUID(riid, &IID_IDispatch)) { + TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv); + *ppv = &This->IMatchCollection2_iface; + }else if(IsEqualGUID(riid, &IID_IMatchCollection2)) { + TRACE("(%p)->(IID_IMatchCollection2 %p)\n", This, ppv); + *ppv = &This->IMatchCollection2_iface; + }else if(IsEqualGUID(riid, &IID_IDispatchEx)) { + TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv); + *ppv = NULL; + return E_NOINTERFACE; + }else { + FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI MatchCollection2_AddRef(IMatchCollection2 *iface) +{ + MatchCollection2 *This = impl_from_IMatchCollection2(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI MatchCollection2_Release(IMatchCollection2 *iface) +{ + MatchCollection2 *This = impl_from_IMatchCollection2(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if(!ref) { + DWORD i; + + for(i=0; icount; i++) + IMatch2_Release(This->matches[i]); + heap_free(This->matches); + + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI MatchCollection2_GetTypeInfoCount(IMatchCollection2 *iface, UINT *pctinfo) +{ + MatchCollection2 *This = impl_from_IMatchCollection2(iface); + + TRACE("(%p)->(%p)\n", This, pctinfo); + + *pctinfo = 1; + return S_OK; +} + +static HRESULT WINAPI MatchCollection2_GetTypeInfo(IMatchCollection2 *iface, + UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) +{ + MatchCollection2 *This = impl_from_IMatchCollection2(iface); + FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); + return E_NOTIMPL; +} + +static HRESULT WINAPI MatchCollection2_GetIDsOfNames(IMatchCollection2 *iface, + REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + MatchCollection2 *This = impl_from_IMatchCollection2(iface); + + TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), + rgszNames, cNames, lcid, rgDispId); + + return ITypeInfo_GetIDsOfNames(typeinfos[MatchCollection2_tid], rgszNames, cNames, rgDispId); +} + +static HRESULT WINAPI MatchCollection2_Invoke(IMatchCollection2 *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + MatchCollection2 *This = impl_from_IMatchCollection2(iface); + + TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), + lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + + return ITypeInfo_Invoke(typeinfos[MatchCollection2_tid], iface, dispIdMember, wFlags, + pDispParams, pVarResult, pExcepInfo, puArgErr); +} + +static HRESULT WINAPI MatchCollection2_get_Item(IMatchCollection2 *iface, + LONG index, IDispatch **ppMatch) +{ + MatchCollection2 *This = impl_from_IMatchCollection2(iface); + + TRACE("(%p)->()\n", This); + + if(!ppMatch) + return E_POINTER; + + if(index<0 || index>=This->count) + return E_INVALIDARG; + + *ppMatch = (IDispatch*)This->matches[index]; + IMatch2_AddRef(This->matches[index]); + return S_OK; +} + +static HRESULT WINAPI MatchCollection2_get_Count(IMatchCollection2 *iface, LONG *pCount) +{ + MatchCollection2 *This = impl_from_IMatchCollection2(iface); + + TRACE("(%p)->()\n", This); + + if(!pCount) + return E_POINTER; + + *pCount = This->count; + return S_OK; +} + +static HRESULT WINAPI MatchCollection2_get__NewEnum(IMatchCollection2 *iface, IUnknown **ppEnum) +{ + MatchCollection2 *This = impl_from_IMatchCollection2(iface); + + TRACE("(%p)->(%p)\n", This, ppEnum); + + if(!ppEnum) + return E_POINTER; + + return create_enum_variant_mc2(&This->IMatchCollection2_iface, 0, (IEnumVARIANT**)ppEnum); +} + +static const IMatchCollection2Vtbl MatchCollection2Vtbl = { + MatchCollection2_QueryInterface, + MatchCollection2_AddRef, + MatchCollection2_Release, + MatchCollection2_GetTypeInfoCount, + MatchCollection2_GetTypeInfo, + MatchCollection2_GetIDsOfNames, + MatchCollection2_Invoke, + MatchCollection2_get_Item, + MatchCollection2_get_Count, + MatchCollection2_get__NewEnum +}; + +static HRESULT add_match(IMatchCollection2 *iface, IMatch2 *add) +{ + MatchCollection2 *This = impl_from_IMatchCollection2(iface); + + TRACE("(%p)->(%p)\n", This, add); + + if(!This->size) { + This->matches = heap_alloc(8*sizeof(IMatch*)); + if(!This->matches) + return E_OUTOFMEMORY; + This->size = 8; + }else if(This->size == This->count) { + IMatch2 **new_matches = heap_realloc(This->matches, 2*This->size*sizeof(IMatch*)); + if(!new_matches) + return E_OUTOFMEMORY; + + This->matches = new_matches; + This->size <<= 1; + } + + This->matches[This->count++] = add; + IMatch2_AddRef(add); + return S_OK; +} + +static HRESULT create_match_collection2(IMatchCollection2 **match_collection) +{ + MatchCollection2 *ret; + HRESULT hres; + + hres = init_regexp_typeinfo(MatchCollection2_tid); + if(FAILED(hres)) + return hres; + + ret = heap_alloc_zero(sizeof(*ret)); + if(!ret) + return E_OUTOFMEMORY; + + ret->IMatchCollection2_iface.lpVtbl = &MatchCollection2Vtbl; + + ret->ref = 1; + *match_collection = &ret->IMatchCollection2_iface; + return S_OK; +} + +static inline RegExp2 *impl_from_IRegExp2(IRegExp2 *iface) +{ + return CONTAINING_RECORD(iface, RegExp2, IRegExp2_iface); +} + +static HRESULT WINAPI RegExp2_QueryInterface(IRegExp2 *iface, REFIID riid, void **ppv) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + + if(IsEqualGUID(riid, &IID_IUnknown)) { + TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); + *ppv = &This->IRegExp2_iface; + }else if(IsEqualGUID(riid, &IID_IDispatch)) { + TRACE("(%p)->(IID_IDispatch %p)\n", This, ppv); + *ppv = &This->IRegExp2_iface; + }else if(IsEqualGUID(riid, &IID_IRegExp2)) { + TRACE("(%p)->(IID_IRegExp2 %p)\n", This, ppv); + *ppv = &This->IRegExp2_iface; + }else if(IsEqualGUID(riid, &IID_IRegExp)) { + TRACE("(%p)->(IID_IRegExp %p)\n", This, ppv); + *ppv = &This->IRegExp_iface; + }else if(IsEqualGUID(riid, &IID_IDispatchEx)) { + TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv); + *ppv = NULL; + return E_NOINTERFACE; + }else { + FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI RegExp2_AddRef(IRegExp2 *iface) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI RegExp2_Release(IRegExp2 *iface) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if(!ref) { + heap_free(This->pattern); + if(This->regexp) + regexp_destroy(This->regexp); + heap_pool_free(&This->pool); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI RegExp2_GetTypeInfoCount(IRegExp2 *iface, UINT *pctinfo) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + + TRACE("(%p)->(%p)\n", This, pctinfo); + + *pctinfo = 1; + return S_OK; +} + +static HRESULT WINAPI RegExp2_GetTypeInfo(IRegExp2 *iface, + UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + FIXME("(%p)->(%u %u %p)\n", This, iTInfo, lcid, ppTInfo); + return E_NOTIMPL; +} + +static HRESULT WINAPI RegExp2_GetIDsOfNames(IRegExp2 *iface, REFIID riid, + LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + + TRACE("(%p)->(%s %p %u %u %p)\n", This, debugstr_guid(riid), + rgszNames, cNames, lcid, rgDispId); + + return ITypeInfo_GetIDsOfNames(typeinfos[RegExp2_tid], rgszNames, cNames, rgDispId); +} + +static HRESULT WINAPI RegExp2_Invoke(IRegExp2 *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + + TRACE("(%p)->(%d %s %d %d %p %p %p %p)\n", This, dispIdMember, debugstr_guid(riid), + lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); + + return ITypeInfo_Invoke(typeinfos[RegExp2_tid], iface, dispIdMember, wFlags, + pDispParams, pVarResult, pExcepInfo, puArgErr); +} + +static HRESULT WINAPI RegExp2_get_Pattern(IRegExp2 *iface, BSTR *pPattern) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + + TRACE("(%p)->(%p)\n", This, pPattern); + + if(!pPattern) + return E_POINTER; + + if(!This->pattern) { + *pPattern = NULL; + return S_OK; + } + + *pPattern = SysAllocString(This->pattern); + return *pPattern ? S_OK : E_OUTOFMEMORY; +} + +static HRESULT WINAPI RegExp2_put_Pattern(IRegExp2 *iface, BSTR pattern) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + WCHAR *p; + DWORD size; + + TRACE("(%p)->(%s)\n", This, wine_dbgstr_w(pattern)); + + if(!pattern) { + heap_free(This->pattern); + if(This->regexp) { + regexp_destroy(This->regexp); + This->regexp = NULL; + } + This->pattern = NULL; + return S_OK; + } + + size = (SysStringLen(pattern)+1) * sizeof(WCHAR); + p = heap_alloc(size); + if(!p) + return E_OUTOFMEMORY; + + heap_free(This->pattern); + This->pattern = p; + memcpy(p, pattern, size); + if(This->regexp) { + regexp_destroy(This->regexp); + This->regexp = NULL; + } + return S_OK; +} + +static HRESULT WINAPI RegExp2_get_IgnoreCase(IRegExp2 *iface, VARIANT_BOOL *pIgnoreCase) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + + TRACE("(%p)->(%p)\n", This, pIgnoreCase); + + if(!pIgnoreCase) + return E_POINTER; + + *pIgnoreCase = This->flags & REG_FOLD ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; +} + +static HRESULT WINAPI RegExp2_put_IgnoreCase(IRegExp2 *iface, VARIANT_BOOL ignoreCase) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + + TRACE("(%p)->(%s)\n", This, ignoreCase ? "true" : "false"); + + if(ignoreCase) + This->flags |= REG_FOLD; + else + This->flags &= ~REG_FOLD; + return S_OK; +} + +static HRESULT WINAPI RegExp2_get_Global(IRegExp2 *iface, VARIANT_BOOL *pGlobal) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + + TRACE("(%p)->(%p)\n", This, pGlobal); + + if(!pGlobal) + return E_POINTER; + + *pGlobal = This->flags & REG_GLOB ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; +} + +static HRESULT WINAPI RegExp2_put_Global(IRegExp2 *iface, VARIANT_BOOL global) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + + TRACE("(%p)->(%s)\n", This, global ? "true" : "false"); + + if(global) + This->flags |= REG_GLOB; + else + This->flags &= ~REG_GLOB; + return S_OK; +} + +static HRESULT WINAPI RegExp2_get_Multiline(IRegExp2 *iface, VARIANT_BOOL *pMultiline) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + + TRACE("(%p)->(%p)\n", This, pMultiline); + + if(!pMultiline) + return E_POINTER; + + *pMultiline = This->flags & REG_MULTILINE ? VARIANT_TRUE : VARIANT_FALSE; + return S_OK; +} + +static HRESULT WINAPI RegExp2_put_Multiline(IRegExp2 *iface, VARIANT_BOOL multiline) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + + TRACE("(%p)->(%s)\n", This, multiline ? "true" : "false"); + + if(multiline) + This->flags |= REG_MULTILINE; + else + This->flags &= ~REG_MULTILINE; + return S_OK; +} + +static HRESULT WINAPI RegExp2_Execute(IRegExp2 *iface, + BSTR sourceString, IDispatch **ppMatches) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + match_state_t *result; + const WCHAR *pos; + IMatchCollection2 *match_collection; + IMatch2 *add = NULL; + HRESULT hres; + + TRACE("(%p)->(%s %p)\n", This, debugstr_w(sourceString), ppMatches); + + if(!This->pattern) { + DWORD i, len = SysStringLen(sourceString); + + hres = create_match_collection2(&match_collection); + if(FAILED(hres)) + return hres; + + for(i=0; i<=len; i++) { + hres = create_match2(i, NULL, &add); + if(FAILED(hres)) + break; + + hres = add_match(match_collection, add); + if(FAILED(hres)) + break; + IMatch2_Release(add); + + if(!(This->flags & REG_GLOB)) + break; + } + + if(FAILED(hres)) { + IMatchCollection2_Release(match_collection); + return hres; + } + + *ppMatches = (IDispatch*)match_collection; + return S_OK; + } + + if(!This->regexp) { + This->regexp = regexp_new(NULL, &This->pool, This->pattern, + strlenW(This->pattern), This->flags, FALSE); + if(!This->regexp) + return E_FAIL; + }else { + hres = regexp_set_flags(&This->regexp, NULL, &This->pool, This->flags); + if(FAILED(hres)) + return hres; + } + + hres = create_match_collection2(&match_collection); + if(FAILED(hres)) + return hres; + + pos = sourceString; + while(1) { + result = alloc_match_state(This->regexp, NULL, pos); + if(!result) { + hres = E_OUTOFMEMORY; + break; + } + + hres = regexp_execute(This->regexp, NULL, &This->pool, + sourceString, SysStringLen(sourceString), result); + if(hres != S_OK) { + heap_free(result); + break; + } + pos = result->cp; + + hres = create_match2(result->cp-result->match_len-sourceString, &result, &add); + heap_free(result); + if(FAILED(hres)) + break; + hres = add_match(match_collection, add); + IMatch2_Release(add); + if(FAILED(hres)) + break; + + if(!(This->flags & REG_GLOB)) + break; + } + + if(FAILED(hres)) { + IMatchCollection2_Release(match_collection); + return hres; + } + + *ppMatches = (IDispatch*)match_collection; + return S_OK; +} + +static HRESULT WINAPI RegExp2_Test(IRegExp2 *iface, BSTR sourceString, VARIANT_BOOL *pMatch) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + match_state_t *result; + heap_pool_t *mark; + HRESULT hres; + + TRACE("(%p)->(%s %p)\n", This, debugstr_w(sourceString), pMatch); + + if(!This->pattern) { + *pMatch = VARIANT_TRUE; + return S_OK; + } + + if(!This->regexp) { + This->regexp = regexp_new(NULL, &This->pool, This->pattern, + strlenW(This->pattern), This->flags, FALSE); + if(!This->regexp) + return E_FAIL; + }else { + hres = regexp_set_flags(&This->regexp, NULL, &This->pool, This->flags); + if(FAILED(hres)) + return hres; + } + + mark = heap_pool_mark(&This->pool); + result = alloc_match_state(This->regexp, &This->pool, sourceString); + if(!result) { + heap_pool_clear(mark); + return E_OUTOFMEMORY; + } + + hres = regexp_execute(This->regexp, NULL, &This->pool, + sourceString, SysStringLen(sourceString), result); + + heap_pool_clear(mark); + + if(hres == S_OK) { + *pMatch = VARIANT_TRUE; + }else if(hres == S_FALSE) { + *pMatch = VARIANT_FALSE; + hres = S_OK; + } + return hres; +} + +static HRESULT WINAPI RegExp2_Replace(IRegExp2 *iface, BSTR sourceString, + VARIANT replaceVar, BSTR *pDestString) +{ + RegExp2 *This = impl_from_IRegExp2(iface); + FIXME("(%p)->(%s %s %p)\n", This, debugstr_w(sourceString), + debugstr_variant(&replaceVar), pDestString); + return E_NOTIMPL; +} + +static const IRegExp2Vtbl RegExp2Vtbl = { + RegExp2_QueryInterface, + RegExp2_AddRef, + RegExp2_Release, + RegExp2_GetTypeInfoCount, + RegExp2_GetTypeInfo, + RegExp2_GetIDsOfNames, + RegExp2_Invoke, + RegExp2_get_Pattern, + RegExp2_put_Pattern, + RegExp2_get_IgnoreCase, + RegExp2_put_IgnoreCase, + RegExp2_get_Global, + RegExp2_put_Global, + RegExp2_get_Multiline, + RegExp2_put_Multiline, + RegExp2_Execute, + RegExp2_Test, + RegExp2_Replace +}; + +static inline RegExp2 *impl_from_IRegExp(IRegExp *iface) +{ + return CONTAINING_RECORD(iface, RegExp2, IRegExp_iface); +} + +static HRESULT WINAPI RegExp_QueryInterface(IRegExp *iface, REFIID riid, void **ppv) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_QueryInterface(&This->IRegExp2_iface, riid, ppv); +} + +static ULONG WINAPI RegExp_AddRef(IRegExp *iface) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_AddRef(&This->IRegExp2_iface); +} + +static ULONG WINAPI RegExp_Release(IRegExp *iface) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_Release(&This->IRegExp2_iface); +} + +static HRESULT WINAPI RegExp_GetTypeInfoCount(IRegExp *iface, UINT *pctinfo) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_GetTypeInfoCount(&This->IRegExp2_iface, pctinfo); +} + +static HRESULT WINAPI RegExp_GetTypeInfo(IRegExp *iface, + UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_GetTypeInfo(&This->IRegExp2_iface, iTInfo, lcid, ppTInfo); +} + +static HRESULT WINAPI RegExp_GetIDsOfNames(IRegExp *iface, REFIID riid, + LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_GetIDsOfNames(&This->IRegExp2_iface, riid, rgszNames, cNames, lcid, rgDispId); +} + +static HRESULT WINAPI RegExp_Invoke(IRegExp *iface, DISPID dispIdMember, + REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, + VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_Invoke(&This->IRegExp2_iface, dispIdMember, riid, lcid, + wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); +} + +static HRESULT WINAPI RegExp_get_Pattern(IRegExp *iface, BSTR *pPattern) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_get_Pattern(&This->IRegExp2_iface, pPattern); +} + +static HRESULT WINAPI RegExp_put_Pattern(IRegExp *iface, BSTR pPattern) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_put_Pattern(&This->IRegExp2_iface, pPattern); +} + +static HRESULT WINAPI RegExp_get_IgnoreCase(IRegExp *iface, VARIANT_BOOL *pIgnoreCase) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_get_IgnoreCase(&This->IRegExp2_iface, pIgnoreCase); +} + +static HRESULT WINAPI RegExp_put_IgnoreCase(IRegExp *iface, VARIANT_BOOL pIgnoreCase) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_put_IgnoreCase(&This->IRegExp2_iface, pIgnoreCase); +} + +static HRESULT WINAPI RegExp_get_Global(IRegExp *iface, VARIANT_BOOL *pGlobal) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_get_Global(&This->IRegExp2_iface, pGlobal); +} + +static HRESULT WINAPI RegExp_put_Global(IRegExp *iface, VARIANT_BOOL pGlobal) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_put_Global(&This->IRegExp2_iface, pGlobal); +} + +static HRESULT WINAPI RegExp_Execute(IRegExp *iface, + BSTR sourceString, IDispatch **ppMatches) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_Execute(&This->IRegExp2_iface, sourceString, ppMatches); +} + +static HRESULT WINAPI RegExp_Test(IRegExp *iface, BSTR sourceString, VARIANT_BOOL *pMatch) +{ + RegExp2 *This = impl_from_IRegExp(iface); + return IRegExp2_Test(&This->IRegExp2_iface, sourceString, pMatch); +} + +static HRESULT WINAPI RegExp_Replace(IRegExp *iface, BSTR sourceString, + BSTR replaceString, BSTR *pDestString) +{ + RegExp2 *This = impl_from_IRegExp(iface); + VARIANT replace; + + V_VT(&replace) = VT_BSTR; + V_BSTR(&replace) = replaceString; + return IRegExp2_Replace(&This->IRegExp2_iface, sourceString, replace, pDestString); +} + +static IRegExpVtbl RegExpVtbl = { + RegExp_QueryInterface, + RegExp_AddRef, + RegExp_Release, + RegExp_GetTypeInfoCount, + RegExp_GetTypeInfo, + RegExp_GetIDsOfNames, + RegExp_Invoke, + RegExp_get_Pattern, + RegExp_put_Pattern, + RegExp_get_IgnoreCase, + RegExp_put_IgnoreCase, + RegExp_get_Global, + RegExp_put_Global, + RegExp_Execute, + RegExp_Test, + RegExp_Replace +}; + +HRESULT WINAPI VBScriptRegExpFactory_CreateInstance(IClassFactory *iface, IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + RegExp2 *ret; + HRESULT hres; + + TRACE("(%p %s %p)\n", pUnkOuter, debugstr_guid(riid), ppv); + + hres = init_regexp_typeinfo(RegExp2_tid); + if(FAILED(hres)) + return hres; + + ret = heap_alloc_zero(sizeof(*ret)); + if(!ret) + return E_OUTOFMEMORY; + + ret->IRegExp2_iface.lpVtbl = &RegExp2Vtbl; + ret->IRegExp_iface.lpVtbl = &RegExpVtbl; + + ret->ref = 1; + heap_pool_init(&ret->pool); + + hres = IRegExp2_QueryInterface(&ret->IRegExp2_iface, riid, ppv); + IRegExp2_Release(&ret->IRegExp2_iface); + return hres; +} + +void release_regexp_typelib(void) +{ + DWORD i; + + for(i=0; i + +#include "vbscript.h" +#include + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(vbscript); + +#ifdef _WIN64 + +#define CTXARG_T DWORDLONG +#define IActiveScriptParseVtbl IActiveScriptParse64Vtbl +#define IActiveScriptParseProcedure2Vtbl IActiveScriptParseProcedure2_64Vtbl + +#else + +#define CTXARG_T DWORD +#define IActiveScriptParseVtbl IActiveScriptParse32Vtbl +#define IActiveScriptParseProcedure2Vtbl IActiveScriptParseProcedure2_32Vtbl + +#endif + +struct VBScript { + IActiveScript IActiveScript_iface; + IActiveScriptParse IActiveScriptParse_iface; + IActiveScriptParseProcedure2 IActiveScriptParseProcedure2_iface; + IObjectSafety IObjectSafety_iface; + + LONG ref; + + DWORD safeopt; + SCRIPTSTATE state; + IActiveScriptSite *site; + script_ctx_t *ctx; + LONG thread_id; + LCID lcid; +}; + +static void change_state(VBScript *This, SCRIPTSTATE state) +{ + if(This->state == state) + return; + + This->state = state; + if(This->site) + IActiveScriptSite_OnStateChange(This->site, state); +} + +static inline BOOL is_started(VBScript *This) +{ + return This->state == SCRIPTSTATE_STARTED + || This->state == SCRIPTSTATE_CONNECTED + || This->state == SCRIPTSTATE_DISCONNECTED; +} + +static HRESULT exec_global_code(script_ctx_t *ctx, vbscode_t *code) +{ + HRESULT hres; + + code->pending_exec = FALSE; + + IActiveScriptSite_OnEnterScript(ctx->site); + hres = exec_script(ctx, &code->main_code, NULL, NULL, NULL); + IActiveScriptSite_OnLeaveScript(ctx->site); + + return hres; +} + +static void exec_queued_code(script_ctx_t *ctx) +{ + vbscode_t *iter; + + LIST_FOR_EACH_ENTRY(iter, &ctx->code_list, vbscode_t, entry) { + if(iter->pending_exec) + exec_global_code(ctx, iter); + } +} + +static HRESULT set_ctx_site(VBScript *This) +{ + HRESULT hres; + + This->ctx->lcid = This->lcid; + + hres = init_global(This->ctx); + if(FAILED(hres)) + return hres; + + IActiveScriptSite_AddRef(This->site); + This->ctx->site = This->site; + + change_state(This, SCRIPTSTATE_INITIALIZED); + return S_OK; +} + +static void release_script(script_ctx_t *ctx) +{ + class_desc_t *class_desc; + + collect_objects(ctx); + + release_dynamic_vars(ctx->global_vars); + ctx->global_vars = NULL; + + while(!list_empty(&ctx->named_items)) { + named_item_t *iter = LIST_ENTRY(list_head(&ctx->named_items), named_item_t, entry); + + list_remove(&iter->entry); + if(iter->disp) + IDispatch_Release(iter->disp); + heap_free(iter->name); + heap_free(iter); + } + + while(ctx->procs) { + class_desc = ctx->procs; + ctx->procs = class_desc->next; + + heap_free(class_desc); + } + + if(ctx->host_global) { + IDispatch_Release(ctx->host_global); + ctx->host_global = NULL; + } + + if(ctx->secmgr) { + IInternetHostSecurityManager_Release(ctx->secmgr); + ctx->secmgr = NULL; + } + + if(ctx->site) { + IActiveScriptSite_Release(ctx->site); + ctx->site = NULL; + } + + if(ctx->err_obj) { + IDispatchEx_Release(&ctx->err_obj->IDispatchEx_iface); + ctx->err_obj = NULL; + } + + if(ctx->global_obj) { + IDispatchEx_Release(&ctx->global_obj->IDispatchEx_iface); + ctx->global_obj = NULL; + } + + if(ctx->script_obj) { + ScriptDisp *script_obj = ctx->script_obj; + + ctx->script_obj = NULL; + script_obj->ctx = NULL; + IDispatchEx_Release(&script_obj->IDispatchEx_iface); + } + + heap_pool_free(&ctx->heap); + heap_pool_init(&ctx->heap); +} + +static void destroy_script(script_ctx_t *ctx) +{ + while(!list_empty(&ctx->code_list)) + release_vbscode(LIST_ENTRY(list_head(&ctx->code_list), vbscode_t, entry)); + + release_script(ctx); + heap_free(ctx); +} + +static void decrease_state(VBScript *This, SCRIPTSTATE state) +{ + switch(This->state) { + case SCRIPTSTATE_CONNECTED: + change_state(This, SCRIPTSTATE_DISCONNECTED); + if(state == SCRIPTSTATE_DISCONNECTED) + return; + /* FALLTHROUGH */ + case SCRIPTSTATE_STARTED: + case SCRIPTSTATE_DISCONNECTED: + if(This->state == SCRIPTSTATE_DISCONNECTED) + change_state(This, SCRIPTSTATE_INITIALIZED); + if(state == SCRIPTSTATE_INITIALIZED) + break; + /* FALLTHROUGH */ + case SCRIPTSTATE_INITIALIZED: + case SCRIPTSTATE_UNINITIALIZED: + change_state(This, state); + + if(This->site) { + IActiveScriptSite_Release(This->site); + This->site = NULL; + } + + if(This->ctx) + release_script(This->ctx); + + This->thread_id = 0; + break; + case SCRIPTSTATE_CLOSED: + break; + DEFAULT_UNREACHABLE; + } +} + +static inline VBScript *impl_from_IActiveScript(IActiveScript *iface) +{ + return CONTAINING_RECORD(iface, VBScript, IActiveScript_iface); +} + +static HRESULT WINAPI VBScript_QueryInterface(IActiveScript *iface, REFIID riid, void **ppv) +{ + VBScript *This = impl_from_IActiveScript(iface); + + if(IsEqualGUID(riid, &IID_IUnknown)) { + TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); + *ppv = &This->IActiveScript_iface; + }else if(IsEqualGUID(riid, &IID_IActiveScript)) { + TRACE("(%p)->(IID_IActiveScript %p)\n", This, ppv); + *ppv = &This->IActiveScript_iface; + }else if(IsEqualGUID(riid, &IID_IActiveScriptParse)) { + TRACE("(%p)->(IID_IActiveScriptParse %p)\n", This, ppv); + *ppv = &This->IActiveScriptParse_iface; + }else if(IsEqualGUID(riid, &IID_IActiveScriptParseProcedure2)) { + TRACE("(%p)->(IID_IActiveScriptParseProcedure2 %p)\n", This, ppv); + *ppv = &This->IActiveScriptParseProcedure2_iface; + }else if(IsEqualGUID(riid, &IID_IObjectSafety)) { + TRACE("(%p)->(IID_IObjectSafety %p)\n", This, ppv); + *ppv = &This->IObjectSafety_iface; + }else { + FIXME("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI VBScript_AddRef(IActiveScript *iface) +{ + VBScript *This = impl_from_IActiveScript(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI VBScript_Release(IActiveScript *iface) +{ + VBScript *This = impl_from_IActiveScript(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", iface, ref); + + if(!ref) { + if(This->ctx) { + decrease_state(This, SCRIPTSTATE_CLOSED); + destroy_script(This->ctx); + This->ctx = NULL; + } + if(This->site) + IActiveScriptSite_Release(This->site); + heap_free(This); + } + + return ref; +} + +static HRESULT WINAPI VBScript_SetScriptSite(IActiveScript *iface, IActiveScriptSite *pass) +{ + VBScript *This = impl_from_IActiveScript(iface); + LCID lcid; + HRESULT hres; + + TRACE("(%p)->(%p)\n", This, pass); + + if(!pass) + return E_POINTER; + + if(This->site) + return E_UNEXPECTED; + + if(InterlockedCompareExchange(&This->thread_id, GetCurrentThreadId(), 0)) + return E_UNEXPECTED; + + This->site = pass; + IActiveScriptSite_AddRef(This->site); + + hres = IActiveScriptSite_GetLCID(This->site, &lcid); + if(hres == S_OK) + This->lcid = lcid; + + return This->ctx ? set_ctx_site(This) : S_OK; +} + +static HRESULT WINAPI VBScript_GetScriptSite(IActiveScript *iface, REFIID riid, + void **ppvObject) +{ + VBScript *This = impl_from_IActiveScript(iface); + FIXME("(%p)->()\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VBScript_SetScriptState(IActiveScript *iface, SCRIPTSTATE ss) +{ + VBScript *This = impl_from_IActiveScript(iface); + + TRACE("(%p)->(%d)\n", This, ss); + + if(This->thread_id && GetCurrentThreadId() != This->thread_id) + return E_UNEXPECTED; + + if(ss == SCRIPTSTATE_UNINITIALIZED) { + if(This->state == SCRIPTSTATE_CLOSED) + return E_UNEXPECTED; + + decrease_state(This, SCRIPTSTATE_UNINITIALIZED); + return S_OK; + } + + if(!This->ctx) + return E_UNEXPECTED; + + switch(ss) { + case SCRIPTSTATE_STARTED: + case SCRIPTSTATE_CONNECTED: /* FIXME */ + if(This->state == SCRIPTSTATE_CLOSED) + return E_UNEXPECTED; + + exec_queued_code(This->ctx); + break; + case SCRIPTSTATE_INITIALIZED: + FIXME("unimplemented SCRIPTSTATE_INITIALIZED\n"); + return S_OK; + default: + FIXME("unimplemented state %d\n", ss); + return E_NOTIMPL; + } + + change_state(This, ss); + return S_OK; +} + +static HRESULT WINAPI VBScript_GetScriptState(IActiveScript *iface, SCRIPTSTATE *pssState) +{ + VBScript *This = impl_from_IActiveScript(iface); + + TRACE("(%p)->(%p)\n", This, pssState); + + if(!pssState) + return E_POINTER; + + if(This->thread_id && This->thread_id != GetCurrentThreadId()) + return E_UNEXPECTED; + + *pssState = This->state; + return S_OK; +} + +static HRESULT WINAPI VBScript_Close(IActiveScript *iface) +{ + VBScript *This = impl_from_IActiveScript(iface); + + TRACE("(%p)->()\n", This); + + if(This->thread_id && This->thread_id != GetCurrentThreadId()) + return E_UNEXPECTED; + + decrease_state(This, SCRIPTSTATE_CLOSED); + return S_OK; +} + +static HRESULT WINAPI VBScript_AddNamedItem(IActiveScript *iface, LPCOLESTR pstrName, DWORD dwFlags) +{ + VBScript *This = impl_from_IActiveScript(iface); + named_item_t *item; + IDispatch *disp = NULL; + HRESULT hres; + + TRACE("(%p)->(%s %x)\n", This, debugstr_w(pstrName), dwFlags); + + if(This->thread_id != GetCurrentThreadId() || !This->ctx || This->state == SCRIPTSTATE_CLOSED) + return E_UNEXPECTED; + + if(dwFlags & SCRIPTITEM_GLOBALMEMBERS) { + IUnknown *unk; + + hres = IActiveScriptSite_GetItemInfo(This->site, pstrName, SCRIPTINFO_IUNKNOWN, &unk, NULL); + if(FAILED(hres)) { + WARN("GetItemInfo failed: %08x\n", hres); + return hres; + } + + hres = IUnknown_QueryInterface(unk, &IID_IDispatch, (void**)&disp); + IUnknown_Release(unk); + if(FAILED(hres)) { + WARN("object does not implement IDispatch\n"); + return hres; + } + + if(This->ctx->host_global) + IDispatch_Release(This->ctx->host_global); + IDispatch_AddRef(disp); + This->ctx->host_global = disp; + } + + item = heap_alloc(sizeof(*item)); + if(!item) { + if(disp) + IDispatch_Release(disp); + return E_OUTOFMEMORY; + } + + item->disp = disp; + item->flags = dwFlags; + item->name = heap_strdupW(pstrName); + if(!item->name) { + if(disp) + IDispatch_Release(disp); + heap_free(item); + return E_OUTOFMEMORY; + } + + list_add_tail(&This->ctx->named_items, &item->entry); + return S_OK; +} + +static HRESULT WINAPI VBScript_AddTypeLib(IActiveScript *iface, REFGUID rguidTypeLib, + DWORD dwMajor, DWORD dwMinor, DWORD dwFlags) +{ + VBScript *This = impl_from_IActiveScript(iface); + FIXME("(%p)->()\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VBScript_GetScriptDispatch(IActiveScript *iface, LPCOLESTR pstrItemName, IDispatch **ppdisp) +{ + VBScript *This = impl_from_IActiveScript(iface); + + TRACE("(%p)->(%p)\n", This, ppdisp); + + if(!ppdisp) + return E_POINTER; + + if(This->thread_id != GetCurrentThreadId() || !This->ctx || !This->ctx->script_obj) { + *ppdisp = NULL; + return E_UNEXPECTED; + } + + *ppdisp = (IDispatch*)&This->ctx->script_obj->IDispatchEx_iface; + IDispatch_AddRef(*ppdisp); + return S_OK; +} + +static HRESULT WINAPI VBScript_GetCurrentScriptThreadID(IActiveScript *iface, + SCRIPTTHREADID *pstridThread) +{ + VBScript *This = impl_from_IActiveScript(iface); + FIXME("(%p)->()\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VBScript_GetScriptThreadID(IActiveScript *iface, + DWORD dwWin32ThreadId, SCRIPTTHREADID *pstidThread) +{ + VBScript *This = impl_from_IActiveScript(iface); + FIXME("(%p)->()\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VBScript_GetScriptThreadState(IActiveScript *iface, + SCRIPTTHREADID stidThread, SCRIPTTHREADSTATE *pstsState) +{ + VBScript *This = impl_from_IActiveScript(iface); + FIXME("(%p)->()\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VBScript_InterruptScriptThread(IActiveScript *iface, + SCRIPTTHREADID stidThread, const EXCEPINFO *pexcepinfo, DWORD dwFlags) +{ + VBScript *This = impl_from_IActiveScript(iface); + FIXME("(%p)->()\n", This); + return E_NOTIMPL; +} + +static HRESULT WINAPI VBScript_Clone(IActiveScript *iface, IActiveScript **ppscript) +{ + VBScript *This = impl_from_IActiveScript(iface); + FIXME("(%p)->()\n", This); + return E_NOTIMPL; +} + +static const IActiveScriptVtbl VBScriptVtbl = { + VBScript_QueryInterface, + VBScript_AddRef, + VBScript_Release, + VBScript_SetScriptSite, + VBScript_GetScriptSite, + VBScript_SetScriptState, + VBScript_GetScriptState, + VBScript_Close, + VBScript_AddNamedItem, + VBScript_AddTypeLib, + VBScript_GetScriptDispatch, + VBScript_GetCurrentScriptThreadID, + VBScript_GetScriptThreadID, + VBScript_GetScriptThreadState, + VBScript_InterruptScriptThread, + VBScript_Clone +}; + +static inline VBScript *impl_from_IActiveScriptParse(IActiveScriptParse *iface) +{ + return CONTAINING_RECORD(iface, VBScript, IActiveScriptParse_iface); +} + +static HRESULT WINAPI VBScriptParse_QueryInterface(IActiveScriptParse *iface, REFIID riid, void **ppv) +{ + VBScript *This = impl_from_IActiveScriptParse(iface); + return IActiveScript_QueryInterface(&This->IActiveScript_iface, riid, ppv); +} + +static ULONG WINAPI VBScriptParse_AddRef(IActiveScriptParse *iface) +{ + VBScript *This = impl_from_IActiveScriptParse(iface); + return IActiveScript_AddRef(&This->IActiveScript_iface); +} + +static ULONG WINAPI VBScriptParse_Release(IActiveScriptParse *iface) +{ + VBScript *This = impl_from_IActiveScriptParse(iface); + return IActiveScript_Release(&This->IActiveScript_iface); +} + +static HRESULT WINAPI VBScriptParse_InitNew(IActiveScriptParse *iface) +{ + VBScript *This = impl_from_IActiveScriptParse(iface); + script_ctx_t *ctx, *old_ctx; + + TRACE("(%p)\n", This); + + if(This->ctx) + return E_UNEXPECTED; + + ctx = heap_alloc_zero(sizeof(script_ctx_t)); + if(!ctx) + return E_OUTOFMEMORY; + + ctx->safeopt = This->safeopt; + heap_pool_init(&ctx->heap); + list_init(&ctx->objects); + list_init(&ctx->code_list); + list_init(&ctx->named_items); + + old_ctx = InterlockedCompareExchangePointer((void**)&This->ctx, ctx, NULL); + if(old_ctx) { + destroy_script(ctx); + return E_UNEXPECTED; + } + + return This->site ? set_ctx_site(This) : S_OK; +} + +static HRESULT WINAPI VBScriptParse_AddScriptlet(IActiveScriptParse *iface, + LPCOLESTR pstrDefaultName, LPCOLESTR pstrCode, LPCOLESTR pstrItemName, + LPCOLESTR pstrSubItemName, LPCOLESTR pstrEventName, LPCOLESTR pstrDelimiter, + CTXARG_T dwSourceContextCookie, ULONG ulStartingLineNumber, DWORD dwFlags, + BSTR *pbstrName, EXCEPINFO *pexcepinfo) +{ + VBScript *This = impl_from_IActiveScriptParse(iface); + FIXME("(%p)->(%s %s %s %s %s %s %s %u %x %p %p)\n", This, debugstr_w(pstrDefaultName), + debugstr_w(pstrCode), debugstr_w(pstrItemName), debugstr_w(pstrSubItemName), + debugstr_w(pstrEventName), debugstr_w(pstrDelimiter), wine_dbgstr_longlong(dwSourceContextCookie), + ulStartingLineNumber, dwFlags, pbstrName, pexcepinfo); + return E_NOTIMPL; +} + +static HRESULT WINAPI VBScriptParse_ParseScriptText(IActiveScriptParse *iface, + LPCOLESTR pstrCode, LPCOLESTR pstrItemName, IUnknown *punkContext, + LPCOLESTR pstrDelimiter, CTXARG_T dwSourceContextCookie, ULONG ulStartingLine, + DWORD dwFlags, VARIANT *pvarResult, EXCEPINFO *pexcepinfo) +{ + VBScript *This = impl_from_IActiveScriptParse(iface); + vbscode_t *code; + HRESULT hres; + + TRACE("(%p)->(%s %s %p %s %s %u %x %p %p)\n", This, debugstr_w(pstrCode), + debugstr_w(pstrItemName), punkContext, debugstr_w(pstrDelimiter), + wine_dbgstr_longlong(dwSourceContextCookie), ulStartingLine, dwFlags, pvarResult, pexcepinfo); + + if(This->thread_id != GetCurrentThreadId() || This->state == SCRIPTSTATE_CLOSED) + return E_UNEXPECTED; + + hres = compile_script(This->ctx, pstrCode, pstrDelimiter, &code); + if(FAILED(hres)) + return hres; + + if(!is_started(This)) { + code->pending_exec = TRUE; + return S_OK; + } + + return exec_global_code(This->ctx, code); +} + +static const IActiveScriptParseVtbl VBScriptParseVtbl = { + VBScriptParse_QueryInterface, + VBScriptParse_AddRef, + VBScriptParse_Release, + VBScriptParse_InitNew, + VBScriptParse_AddScriptlet, + VBScriptParse_ParseScriptText +}; + +static inline VBScript *impl_from_IActiveScriptParseProcedure2(IActiveScriptParseProcedure2 *iface) +{ + return CONTAINING_RECORD(iface, VBScript, IActiveScriptParseProcedure2_iface); +} + +static HRESULT WINAPI VBScriptParseProcedure_QueryInterface(IActiveScriptParseProcedure2 *iface, REFIID riid, void **ppv) +{ + VBScript *This = impl_from_IActiveScriptParseProcedure2(iface); + return IActiveScript_QueryInterface(&This->IActiveScript_iface, riid, ppv); +} + +static ULONG WINAPI VBScriptParseProcedure_AddRef(IActiveScriptParseProcedure2 *iface) +{ + VBScript *This = impl_from_IActiveScriptParseProcedure2(iface); + return IActiveScript_AddRef(&This->IActiveScript_iface); +} + +static ULONG WINAPI VBScriptParseProcedure_Release(IActiveScriptParseProcedure2 *iface) +{ + VBScript *This = impl_from_IActiveScriptParseProcedure2(iface); + return IActiveScript_Release(&This->IActiveScript_iface); +} + +static HRESULT WINAPI VBScriptParseProcedure_ParseProcedureText(IActiveScriptParseProcedure2 *iface, + LPCOLESTR pstrCode, LPCOLESTR pstrFormalParams, LPCOLESTR pstrProcedureName, + LPCOLESTR pstrItemName, IUnknown *punkContext, LPCOLESTR pstrDelimiter, + CTXARG_T dwSourceContextCookie, ULONG ulStartingLineNumber, DWORD dwFlags, IDispatch **ppdisp) +{ + VBScript *This = impl_from_IActiveScriptParseProcedure2(iface); + vbscode_t *code; + HRESULT hres; + + TRACE("(%p)->(%s %s %s %s %p %s %s %u %x %p)\n", This, debugstr_w(pstrCode), debugstr_w(pstrFormalParams), + debugstr_w(pstrProcedureName), debugstr_w(pstrItemName), punkContext, debugstr_w(pstrDelimiter), + wine_dbgstr_longlong(dwSourceContextCookie), ulStartingLineNumber, dwFlags, ppdisp); + + if(This->thread_id != GetCurrentThreadId() || This->state == SCRIPTSTATE_CLOSED) + return E_UNEXPECTED; + + hres = compile_script(This->ctx, pstrCode, pstrDelimiter, &code); + if(FAILED(hres)) + return hres; + + return create_procedure_disp(This->ctx, code, ppdisp); +} + +static const IActiveScriptParseProcedure2Vtbl VBScriptParseProcedureVtbl = { + VBScriptParseProcedure_QueryInterface, + VBScriptParseProcedure_AddRef, + VBScriptParseProcedure_Release, + VBScriptParseProcedure_ParseProcedureText, +}; + +static inline VBScript *impl_from_IObjectSafety(IObjectSafety *iface) +{ + return CONTAINING_RECORD(iface, VBScript, IObjectSafety_iface); +} + +static HRESULT WINAPI VBScriptSafety_QueryInterface(IObjectSafety *iface, REFIID riid, void **ppv) +{ + VBScript *This = impl_from_IObjectSafety(iface); + return IActiveScript_QueryInterface(&This->IActiveScript_iface, riid, ppv); +} + +static ULONG WINAPI VBScriptSafety_AddRef(IObjectSafety *iface) +{ + VBScript *This = impl_from_IObjectSafety(iface); + return IActiveScript_AddRef(&This->IActiveScript_iface); +} + +static ULONG WINAPI VBScriptSafety_Release(IObjectSafety *iface) +{ + VBScript *This = impl_from_IObjectSafety(iface); + return IActiveScript_Release(&This->IActiveScript_iface); +} + +#define SUPPORTED_OPTIONS (INTERFACESAFE_FOR_UNTRUSTED_DATA|INTERFACE_USES_DISPEX|INTERFACE_USES_SECURITY_MANAGER) + +static HRESULT WINAPI VBScriptSafety_GetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid, + DWORD *pdwSupportedOptions, DWORD *pdwEnabledOptions) +{ + VBScript *This = impl_from_IObjectSafety(iface); + + TRACE("(%p)->(%s %p %p)\n", This, debugstr_guid(riid), pdwSupportedOptions, pdwEnabledOptions); + + if(!pdwSupportedOptions || !pdwEnabledOptions) + return E_POINTER; + + *pdwSupportedOptions = SUPPORTED_OPTIONS; + *pdwEnabledOptions = This->safeopt; + return S_OK; +} + +static HRESULT WINAPI VBScriptSafety_SetInterfaceSafetyOptions(IObjectSafety *iface, REFIID riid, + DWORD dwOptionSetMask, DWORD dwEnabledOptions) +{ + VBScript *This = impl_from_IObjectSafety(iface); + + TRACE("(%p)->(%s %x %x)\n", This, debugstr_guid(riid), dwOptionSetMask, dwEnabledOptions); + + if(dwOptionSetMask & ~SUPPORTED_OPTIONS) + return E_FAIL; + + This->safeopt = (dwEnabledOptions & dwOptionSetMask) | (This->safeopt & ~dwOptionSetMask) | INTERFACE_USES_DISPEX; + return S_OK; +} + +static const IObjectSafetyVtbl VBScriptSafetyVtbl = { + VBScriptSafety_QueryInterface, + VBScriptSafety_AddRef, + VBScriptSafety_Release, + VBScriptSafety_GetInterfaceSafetyOptions, + VBScriptSafety_SetInterfaceSafetyOptions +}; + +HRESULT WINAPI VBScriptFactory_CreateInstance(IClassFactory *iface, IUnknown *pUnkOuter, REFIID riid, void **ppv) +{ + VBScript *ret; + HRESULT hres; + + TRACE("(%p %s %p)\n", pUnkOuter, debugstr_guid(riid), ppv); + + ret = heap_alloc_zero(sizeof(*ret)); + if(!ret) + return E_OUTOFMEMORY; + + ret->IActiveScript_iface.lpVtbl = &VBScriptVtbl; + ret->IActiveScriptParse_iface.lpVtbl = &VBScriptParseVtbl; + ret->IActiveScriptParseProcedure2_iface.lpVtbl = &VBScriptParseProcedureVtbl; + ret->IObjectSafety_iface.lpVtbl = &VBScriptSafetyVtbl; + + ret->ref = 1; + ret->state = SCRIPTSTATE_UNINITIALIZED; + ret->safeopt = INTERFACE_USES_DISPEX; + + hres = IActiveScript_QueryInterface(&ret->IActiveScript_iface, riid, ppv); + IActiveScript_Release(&ret->IActiveScript_iface); + return hres; +} + +typedef struct { + IServiceProvider IServiceProvider_iface; + + LONG ref; + + IServiceProvider *sp; +} AXSite; + +static inline AXSite *impl_from_IServiceProvider(IServiceProvider *iface) +{ + return CONTAINING_RECORD(iface, AXSite, IServiceProvider_iface); +} + +static HRESULT WINAPI AXSite_QueryInterface(IServiceProvider *iface, REFIID riid, void **ppv) +{ + AXSite *This = impl_from_IServiceProvider(iface); + + if(IsEqualGUID(&IID_IUnknown, riid)) { + TRACE("(%p)->(IID_IUnknown %p)\n", This, ppv); + *ppv = &This->IServiceProvider_iface; + }else if(IsEqualGUID(&IID_IServiceProvider, riid)) { + TRACE("(%p)->(IID_IServiceProvider %p)\n", This, ppv); + *ppv = &This->IServiceProvider_iface; + }else { + TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI AXSite_AddRef(IServiceProvider *iface) +{ + AXSite *This = impl_from_IServiceProvider(iface); + LONG ref = InterlockedIncrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + return ref; +} + +static ULONG WINAPI AXSite_Release(IServiceProvider *iface) +{ + AXSite *This = impl_from_IServiceProvider(iface); + LONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p) ref=%d\n", This, ref); + + if(!ref) + heap_free(This); + + return ref; +} + +static HRESULT WINAPI AXSite_QueryService(IServiceProvider *iface, + REFGUID guidService, REFIID riid, void **ppv) +{ + AXSite *This = impl_from_IServiceProvider(iface); + + TRACE("(%p)->(%s %s %p)\n", This, debugstr_guid(guidService), debugstr_guid(riid), ppv); + + return IServiceProvider_QueryService(This->sp, guidService, riid, ppv); +} + +static IServiceProviderVtbl AXSiteVtbl = { + AXSite_QueryInterface, + AXSite_AddRef, + AXSite_Release, + AXSite_QueryService +}; + +IUnknown *create_ax_site(script_ctx_t *ctx) +{ + IServiceProvider *sp; + AXSite *ret; + HRESULT hres; + + hres = IActiveScriptSite_QueryInterface(ctx->site, &IID_IServiceProvider, (void**)&sp); + if(FAILED(hres)) { + ERR("Could not get IServiceProvider iface: %08x\n", hres); + return NULL; + } + + ret = heap_alloc(sizeof(*ret)); + if(!ret) { + IServiceProvider_Release(sp); + return NULL; + } + + ret->IServiceProvider_iface.lpVtbl = &AXSiteVtbl; + ret->ref = 1; + ret->sp = sp; + + return (IUnknown*)&ret->IServiceProvider_iface; +} diff --git a/reactos/dll/win32/vbscript/vbscript.h b/reactos/dll/win32/vbscript/vbscript.h new file mode 100644 index 00000000000..98a7ed3be86 --- /dev/null +++ b/reactos/dll/win32/vbscript/vbscript.h @@ -0,0 +1,412 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#define WIN32_NO_STATUS +#define _INC_WINDOWS +#define COM_NO_WINDOWS_H + +#define COBJMACROS + +#include +#include +#include +#include +#include + +#include + +#include +#include + +typedef struct { + void **blocks; + DWORD block_cnt; + DWORD last_block; + DWORD offset; + BOOL mark; + struct list custom_blocks; +} heap_pool_t; + +void heap_pool_init(heap_pool_t*) DECLSPEC_HIDDEN; +void *heap_pool_alloc(heap_pool_t*,size_t) __WINE_ALLOC_SIZE(2) DECLSPEC_HIDDEN; +void *heap_pool_grow(heap_pool_t*,void*,DWORD,DWORD) DECLSPEC_HIDDEN; +void heap_pool_clear(heap_pool_t*) DECLSPEC_HIDDEN; +void heap_pool_free(heap_pool_t*) DECLSPEC_HIDDEN; +heap_pool_t *heap_pool_mark(heap_pool_t*) DECLSPEC_HIDDEN; + +typedef struct _function_t function_t; +typedef struct _vbscode_t vbscode_t; +typedef struct _script_ctx_t script_ctx_t; +typedef struct _vbdisp_t vbdisp_t; + +typedef struct named_item_t { + IDispatch *disp; + DWORD flags; + LPWSTR name; + + struct list entry; +} named_item_t; + +typedef enum { + VBDISP_CALLGET, + VBDISP_LET, + VBDISP_SET, + VBDISP_ANY +} vbdisp_invoke_type_t; + +typedef struct { + BOOL is_public; + const WCHAR *name; +} vbdisp_prop_desc_t; + +typedef struct { + const WCHAR *name; + BOOL is_public; + function_t *entries[VBDISP_ANY]; +} vbdisp_funcprop_desc_t; + +#define BP_GET 1 +#define BP_GETPUT 2 + +typedef struct { + DISPID id; + HRESULT (*proc)(vbdisp_t*,VARIANT*,unsigned,VARIANT*); + DWORD flags; + unsigned min_args; + UINT_PTR max_args; +} builtin_prop_t; + +typedef struct _class_desc_t { + const WCHAR *name; + script_ctx_t *ctx; + + unsigned class_initialize_id; + unsigned class_terminate_id; + unsigned func_cnt; + vbdisp_funcprop_desc_t *funcs; + + unsigned prop_cnt; + vbdisp_prop_desc_t *props; + + unsigned builtin_prop_cnt; + const builtin_prop_t *builtin_props; + ITypeInfo *typeinfo; + function_t *value_func; + + struct _class_desc_t *next; +} class_desc_t; + +struct _vbdisp_t { + IDispatchEx IDispatchEx_iface; + + LONG ref; + BOOL terminator_ran; + struct list entry; + + const class_desc_t *desc; + VARIANT props[1]; +}; + +typedef struct _ident_map_t ident_map_t; + +typedef struct { + IDispatchEx IDispatchEx_iface; + LONG ref; + + ident_map_t *ident_map; + unsigned ident_map_cnt; + unsigned ident_map_size; + + script_ctx_t *ctx; +} ScriptDisp; + +HRESULT create_vbdisp(const class_desc_t*,vbdisp_t**) DECLSPEC_HIDDEN; +HRESULT disp_get_id(IDispatch*,BSTR,vbdisp_invoke_type_t,BOOL,DISPID*) DECLSPEC_HIDDEN; +HRESULT vbdisp_get_id(vbdisp_t*,BSTR,vbdisp_invoke_type_t,BOOL,DISPID*) DECLSPEC_HIDDEN; +HRESULT disp_call(script_ctx_t*,IDispatch*,DISPID,DISPPARAMS*,VARIANT*) DECLSPEC_HIDDEN; +HRESULT disp_propput(script_ctx_t*,IDispatch*,DISPID,DISPPARAMS*) DECLSPEC_HIDDEN; +void collect_objects(script_ctx_t*) DECLSPEC_HIDDEN; +HRESULT create_procedure_disp(script_ctx_t*,vbscode_t*,IDispatch**) DECLSPEC_HIDDEN; +HRESULT create_script_disp(script_ctx_t*,ScriptDisp**) DECLSPEC_HIDDEN; + +static inline unsigned arg_cnt(const DISPPARAMS *dp) +{ + return dp->cArgs - dp->cNamedArgs; +} + +static inline VARIANT *get_arg(DISPPARAMS *dp, DWORD i) +{ + return dp->rgvarg + dp->cArgs-i-1; +} + +typedef struct _dynamic_var_t { + struct _dynamic_var_t *next; + VARIANT v; + const WCHAR *name; + BOOL is_const; +} dynamic_var_t; + +struct _script_ctx_t { + IActiveScriptSite *site; + LCID lcid; + + IInternetHostSecurityManager *secmgr; + DWORD safeopt; + + IDispatch *host_global; + + ScriptDisp *script_obj; + + class_desc_t global_desc; + vbdisp_t *global_obj; + + class_desc_t err_desc; + vbdisp_t *err_obj; + + dynamic_var_t *global_vars; + function_t *global_funcs; + class_desc_t *classes; + class_desc_t *procs; + + heap_pool_t heap; + + struct list objects; + struct list code_list; + struct list named_items; +}; + +HRESULT init_global(script_ctx_t*) DECLSPEC_HIDDEN; +HRESULT init_err(script_ctx_t*) DECLSPEC_HIDDEN; + +IUnknown *create_ax_site(script_ctx_t*) DECLSPEC_HIDDEN; + +typedef enum { + ARG_NONE = 0, + ARG_STR, + ARG_BSTR, + ARG_INT, + ARG_UINT, + ARG_ADDR, + ARG_DOUBLE +} instr_arg_type_t; + +#define OP_LIST \ + X(add, 1, 0, 0) \ + X(and, 1, 0, 0) \ + X(assign_ident, 1, ARG_BSTR, ARG_UINT) \ + X(assign_member, 1, ARG_BSTR, ARG_UINT) \ + X(bool, 1, ARG_INT, 0) \ + X(case, 0, ARG_ADDR, 0) \ + X(concat, 1, 0, 0) \ + X(const, 1, ARG_BSTR, 0) \ + X(div, 1, 0, 0) \ + X(double, 1, ARG_DOUBLE, 0) \ + X(empty, 1, 0, 0) \ + X(enumnext, 0, ARG_ADDR, ARG_BSTR) \ + X(equal, 1, 0, 0) \ + X(errmode, 1, ARG_INT, 0) \ + X(eqv, 1, 0, 0) \ + X(exp, 1, 0, 0) \ + X(gt, 1, 0, 0) \ + X(gteq, 1, 0, 0) \ + X(icall, 1, ARG_BSTR, ARG_UINT) \ + X(icallv, 1, ARG_BSTR, ARG_UINT) \ + X(idiv, 1, 0, 0) \ + X(imp, 1, 0, 0) \ + X(incc, 1, ARG_BSTR, 0) \ + X(is, 1, 0, 0) \ + X(jmp, 0, ARG_ADDR, 0) \ + X(jmp_false, 0, ARG_ADDR, 0) \ + X(jmp_true, 0, ARG_ADDR, 0) \ + X(long, 1, ARG_INT, 0) \ + X(lt, 1, 0, 0) \ + X(lteq, 1, 0, 0) \ + X(mcall, 1, ARG_BSTR, ARG_UINT) \ + X(mcallv, 1, ARG_BSTR, ARG_UINT) \ + X(me, 1, 0, 0) \ + X(mod, 1, 0, 0) \ + X(mul, 1, 0, 0) \ + X(neg, 1, 0, 0) \ + X(nequal, 1, 0, 0) \ + X(new, 1, ARG_STR, 0) \ + X(newenum, 1, 0, 0) \ + X(not, 1, 0, 0) \ + X(nothing, 1, 0, 0) \ + X(null, 1, 0, 0) \ + X(or, 1, 0, 0) \ + X(pop, 1, ARG_UINT, 0) \ + X(ret, 0, 0, 0) \ + X(set_ident, 1, ARG_BSTR, ARG_UINT) \ + X(set_member, 1, ARG_BSTR, ARG_UINT) \ + X(short, 1, ARG_INT, 0) \ + X(step, 0, ARG_ADDR, ARG_BSTR) \ + X(stop, 1, 0, 0) \ + X(string, 1, ARG_STR, 0) \ + X(sub, 1, 0, 0) \ + X(val, 1, 0, 0) \ + X(xor, 1, 0, 0) + +typedef enum { +#define X(x,n,a,b) OP_##x, +OP_LIST +#undef X + OP_LAST +} vbsop_t; + +typedef union { + const WCHAR *str; + BSTR bstr; + unsigned uint; + LONG lng; + double *dbl; +} instr_arg_t; + +typedef struct { + vbsop_t op; + instr_arg_t arg1; + instr_arg_t arg2; +} instr_t; + +typedef struct { + const WCHAR *name; + BOOL by_ref; +} arg_desc_t; + +typedef enum { + FUNC_GLOBAL, + FUNC_FUNCTION, + FUNC_SUB, + FUNC_PROPGET, + FUNC_PROPLET, + FUNC_PROPSET, + FUNC_DEFGET +} function_type_t; + +typedef struct { + const WCHAR *name; +} var_desc_t; + +struct _function_t { + function_type_t type; + const WCHAR *name; + BOOL is_public; + arg_desc_t *args; + unsigned arg_cnt; + var_desc_t *vars; + unsigned var_cnt; + unsigned code_off; + vbscode_t *code_ctx; + function_t *next; +}; + +struct _vbscode_t { + instr_t *instrs; + WCHAR *source; + + BOOL option_explicit; + + BOOL pending_exec; + function_t main_code; + + BSTR *bstr_pool; + unsigned bstr_pool_size; + unsigned bstr_cnt; + heap_pool_t heap; + + struct list entry; +}; + +void release_vbscode(vbscode_t*) DECLSPEC_HIDDEN; +HRESULT compile_script(script_ctx_t*,const WCHAR*,const WCHAR*,vbscode_t**) DECLSPEC_HIDDEN; +HRESULT exec_script(script_ctx_t*,function_t*,IDispatch*,DISPPARAMS*,VARIANT*) DECLSPEC_HIDDEN; +void release_dynamic_vars(dynamic_var_t*) DECLSPEC_HIDDEN; + +typedef struct { + UINT16 len; + WCHAR buf[7]; +} string_constant_t; + +#define TID_LIST \ + XDIID(ErrObj) \ + XDIID(GlobalObj) + +typedef enum { +#define XDIID(iface) iface ## _tid, +TID_LIST +#undef XDIID + LAST_tid +} tid_t; + +HRESULT get_typeinfo(tid_t,ITypeInfo**) DECLSPEC_HIDDEN; +void release_regexp_typelib(void) DECLSPEC_HIDDEN; + +#ifndef INT32_MIN +#define INT32_MIN (-2147483647-1) +#endif + +#ifndef INT32_MAX +#define INT32_MAX (2147483647) +#endif + +static inline BOOL is_int32(double d) +{ + return INT32_MIN <= d && d <= INT32_MAX && (double)(int)d == d; +} + +HRESULT WINAPI VBScriptFactory_CreateInstance(IClassFactory*,IUnknown*,REFIID,void**) DECLSPEC_HIDDEN; +HRESULT WINAPI VBScriptRegExpFactory_CreateInstance(IClassFactory*,IUnknown*,REFIID,void**) DECLSPEC_HIDDEN; + +const char *debugstr_variant(const VARIANT*) DECLSPEC_HIDDEN; + +static inline void *heap_alloc(size_t len) +{ + return HeapAlloc(GetProcessHeap(), 0, len); +} + +static inline void *heap_alloc_zero(size_t len) +{ + return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len); +} + +static inline void *heap_realloc(void *mem, size_t len) +{ + return HeapReAlloc(GetProcessHeap(), 0, mem, len); +} + +static inline BOOL heap_free(void *mem) +{ + return HeapFree(GetProcessHeap(), 0, mem); +} + +static inline LPWSTR heap_strdupW(LPCWSTR str) +{ + LPWSTR ret = NULL; + + if(str) { + DWORD size; + + size = (strlenW(str)+1)*sizeof(WCHAR); + ret = heap_alloc(size); + if(ret) + memcpy(ret, str, size); + } + + return ret; +} diff --git a/reactos/dll/win32/vbscript/vbscript.rc b/reactos/dll/win32/vbscript/vbscript.rc new file mode 100644 index 00000000000..582e0b2f4db --- /dev/null +++ b/reactos/dll/win32/vbscript/vbscript.rc @@ -0,0 +1,42 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +/* @makedep: vbscript.rgs */ +2 WINE_REGISTRY vbscript.rgs + +3 WINE_REGISTRY vbsglobal.rgs +4 WINE_REGISTRY vbsregexp10.rgs +5 WINE_REGISTRY vbsregexp55.rgs +6 WINE_REGISTRY vbscript_classes.rgs + +1 TYPELIB vbsglobal.tlb +2 TYPELIB vbsregexp10.tlb +3 TYPELIB vbsregexp55.tlb + +#define WINE_FILEDESCRIPTION_STR "Wine VBScript" +#define WINE_FILENAME_STR "vbscript.dll" +#define WINE_FILEVERSION 5,8,7601,16978 +#define WINE_FILEVERSION_STR "5.8.7601.16978" +#define WINE_PRODUCTVERSION 5,8,0,16978 +#define WINE_PRODUCTVERSION_STR "5.8.7601.16978" + +#include diff --git a/reactos/dll/win32/vbscript/vbscript.rgs b/reactos/dll/win32/vbscript/vbscript.rgs new file mode 100644 index 00000000000..4513b4d7fb0 --- /dev/null +++ b/reactos/dll/win32/vbscript/vbscript.rgs @@ -0,0 +1,82 @@ +HKCR +{ + NoRemove CLSID + { + '{B54F3741-5B07-11CF-A4B0-00AA004A55E8}' + { + 'Implemented Categories' + { + '{f0b7a1a1-9847-11cf-8f20-00805f2cd064}' + '{f0b7a1a2-9847-11cf-8f20-00805f2cd064}' + } + OLEScript + } + '{B54F3742-5B07-11CF-A4B0-00AA004A55E8}' + { + 'Implemented Categories' + { + '{0aee2a92-bcbb-11d0-8c72-00c04fc2b085}' + } + OLEScript + } + '{B54F3743-5B07-11CF-A4B0-00AA004A55E8}' + { + 'Implemented Categories' + { + '{f0b7a1a1-9847-11cf-8f20-00805f2cd064}' + '{f0b7a1a2-9847-11cf-8f20-00805f2cd064}' + '{f0b7a1a3-9847-11cf-8f20-00805f2cd064}' + } + OLEScript + } + } + 'VBS' = s 'VB Script Language' + { + CLSID = s '{B54F3741-5B07-11CF-A4B0-00AA004A55E8}' + OLEScript + } + 'VBS Author' = s 'VB Script Language Authoring' + { + CLSID = s '{B54F3742-5B07-11CF-A4B0-00AA004A55E8}' + OLEScript + } + 'VBScript' + { + OLEScript + } + 'VBScript Author' + { + OLEScript + } + 'VBScript.Encode' + { + OLEScript + } + 'VBScript.RegExp' + { + OLEScript + } + ForceRemove VBSFile = s 'VBScript Script File' + { + ScriptEngine = s 'VBScript' + Shell = s 'Open' + { + Edit + { + Command = s '"%%SystemRoot%%\system32\notepad.exe" %%1' + } + Open + { + Command = s '"%%SystemRoot%%\system32\wscript.exe" "%%1" %%*' + } + Open2 + { + Command = s '"%%SystemRoot%%\system32\cscript.exe" "%%1" %%*' + } + Print + { + Command = s '"%%SystemRoot%%\system32\notepad.exe" /p %%1' + } + } + } +} diff --git a/reactos/dll/win32/vbscript/vbscript.spec b/reactos/dll/win32/vbscript/vbscript.spec new file mode 100644 index 00000000000..b16365d0c9f --- /dev/null +++ b/reactos/dll/win32/vbscript/vbscript.spec @@ -0,0 +1,4 @@ +@ stdcall -private DllCanUnloadNow() +@ stdcall -private DllGetClassObject(ptr ptr ptr) +@ stdcall -private DllRegisterServer() +@ stdcall -private DllUnregisterServer() diff --git a/reactos/dll/win32/vbscript/vbscript_classes.idl b/reactos/dll/win32/vbscript/vbscript_classes.idl new file mode 100644 index 00000000000..c800e1be58d --- /dev/null +++ b/reactos/dll/win32/vbscript/vbscript_classes.idl @@ -0,0 +1,50 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +[ + helpstring("VBScript Regular Expression"), + threading(apartment), + progid("VBScript.RegExp"), + version(5.5), + uuid(3f4daca4-160d-11d2-a8e9-00104b365c9f) +] +coclass VBScriptRegExp {} + +[ + helpstring("VB Script Language"), + threading(both), + progid("VBScript"), + uuid(b54f3741-5b07-11cf-a4b0-00aa004a55e8) +] +coclass VBScript {} + +[ + helpstring("VB Script Language Authoring"), + threading(both), + progid("VBScript Author"), + uuid(b54f3742-5b07-11cf-a4b0-00aa004a55e8) +] +coclass VBScriptAuthor {} + +[ + helpstring("VB Script Language Encoding"), + threading(both), + progid("VBScript.Encode"), + uuid(b54f3743-5b07-11cf-a4b0-00aa004a55e8) +] +coclass VBScriptEncode {} diff --git a/reactos/dll/win32/vbscript/vbscript_classes.rgs b/reactos/dll/win32/vbscript/vbscript_classes.rgs new file mode 100644 index 00000000000..134837107d8 --- /dev/null +++ b/reactos/dll/win32/vbscript/vbscript_classes.rgs @@ -0,0 +1,46 @@ +HKCR +{ + NoRemove Interface + { + } + NoRemove CLSID + { + '{3F4DACA4-160D-11D2-A8E9-00104B365C9F}' = s 'VBScript Regular Expression' + { + InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Apartment' } + ProgId = s 'VBScript.RegExp' + Version = s '5.5' + } + '{B54F3741-5B07-11CF-A4B0-00AA004A55E8}' = s 'VB Script Language' + { + InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Both' } + ProgId = s 'VBScript' + } + '{B54F3742-5B07-11CF-A4B0-00AA004A55E8}' = s 'VB Script Language Authoring' + { + InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Both' } + ProgId = s 'VBScript Author' + } + '{B54F3743-5B07-11CF-A4B0-00AA004A55E8}' = s 'VB Script Language Encoding' + { + InprocServer32 = s '%MODULE%' { val ThreadingModel = s 'Both' } + ProgId = s 'VBScript.Encode' + } + } + 'VBScript.RegExp' = s 'VBScript Regular Expression' + { + CLSID = s '{3F4DACA4-160D-11D2-A8E9-00104B365C9F}' + } + 'VBScript' = s 'VB Script Language' + { + CLSID = s '{B54F3741-5B07-11CF-A4B0-00AA004A55E8}' + } + 'VBScript Author' = s 'VB Script Language Authoring' + { + CLSID = s '{B54F3742-5B07-11CF-A4B0-00AA004A55E8}' + } + 'VBScript.Encode' = s 'VB Script Language Encoding' + { + CLSID = s '{B54F3743-5B07-11CF-A4B0-00AA004A55E8}' + } +} diff --git a/reactos/dll/win32/vbscript/vbscript_defs.h b/reactos/dll/win32/vbscript/vbscript_defs.h new file mode 100644 index 00000000000..b4b904a0103 --- /dev/null +++ b/reactos/dll/win32/vbscript/vbscript_defs.h @@ -0,0 +1,236 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define DISPID_GLOBAL_VBUSESYSTEM 0 +#define DISPID_GLOBAL_USESYSTEMDAYOFWEEK 1 +#define DISPID_GLOBAL_VBSUNDAY 2 +#define DISPID_GLOBAL_VBMONDAY 3 +#define DISPID_GLOBAL_VBTUESDAY 4 +#define DISPID_GLOBAL_VBWEDNESDAY 5 +#define DISPID_GLOBAL_VBTHURSDAY 6 +#define DISPID_GLOBAL_VBFRIDAY 7 +#define DISPID_GLOBAL_VBSATURDAY 8 +#define DISPID_GLOBAL_VBFIRSTJAN1 9 +#define DISPID_GLOBAL_VBFIRSTFOURDAYS 10 +#define DISPID_GLOBAL_VBFIRSTFULLWEEK 11 +#define DISPID_GLOBAL_VBOKONLY 12 +#define DISPID_GLOBAL_VBOKCANCEL 13 +#define DISPID_GLOBAL_VBABORTRETRYIGNORE 14 +#define DISPID_GLOBAL_VBYESNOCANCEL 15 +#define DISPID_GLOBAL_VBYESNO 16 +#define DISPID_GLOBAL_VBRETRYCANCEL 17 +#define DISPID_GLOBAL_VBCRITICAL 18 +#define DISPID_GLOBAL_VBQUESTION 19 +#define DISPID_GLOBAL_VBEXCLAMATION 20 +#define DISPID_GLOBAL_VBINFORMATION 21 +#define DISPID_GLOBAL_VBDEFAULTBUTTON1 22 +#define DISPID_GLOBAL_VBDEFAULTBUTTON2 23 +#define DISPID_GLOBAL_VBDEFAULTBUTTON3 24 +#define DISPID_GLOBAL_VBDEFAULTBUTTON4 25 +#define DISPID_GLOBAL_VBAPPLICATIONMODAL 26 +#define DISPID_GLOBAL_VBSYSTEMMODAL 27 +#define DISPID_GLOBAL_VBOK 28 +#define DISPID_GLOBAL_VBCANCEL 29 +#define DISPID_GLOBAL_VBABORT 30 +#define DISPID_GLOBAL_VBRETRY 31 +#define DISPID_GLOBAL_VBIGNORE 32 +#define DISPID_GLOBAL_VBYES 33 +#define DISPID_GLOBAL_VBNO 34 +#define DISPID_GLOBAL_VBEMPTY 35 +#define DISPID_GLOBAL_VBNULL 36 +#define DISPID_GLOBAL_VBINTEGER 37 +#define DISPID_GLOBAL_VBLONG 38 +#define DISPID_GLOBAL_VBSINGLE 39 +#define DISPID_GLOBAL_VBDOUBLE 40 +#define DISPID_GLOBAL_VBCURRENCY 41 +#define DISPID_GLOBAL_VBDATE 42 +#define DISPID_GLOBAL_VBSTRING 43 +#define DISPID_GLOBAL_VBOBJECT 44 +#define DISPID_GLOBAL_VBERROR 45 +#define DISPID_GLOBAL_VBBOOLEAN 46 +#define DISPID_GLOBAL_VBVARIANT 47 +#define DISPID_GLOBAL_VBDATAOBJECT 48 +#define DISPID_GLOBAL_VBDECIMAL 49 +#define DISPID_GLOBAL_VBBYTE 50 +#define DISPID_GLOBAL_VBARRAY 51 +#define DISPID_GLOBAL_VBTRUE 52 +#define DISPID_GLOBAL_VBFALSE 53 +#define DISPID_GLOBAL_VBUSEDEFAULT 54 +#define DISPID_GLOBAL_VBBINARYCOMPARE 55 +#define DISPID_GLOBAL_VBTEXTCOMPARE 56 +#define DISPID_GLOBAL_VBDATABASECOMPARE 57 +#define DISPID_GLOBAL_VBGENERALDATE 58 +#define DISPID_GLOBAL_VBLONGDATE 59 +#define DISPID_GLOBAL_VBSHORTDATE 60 +#define DISPID_GLOBAL_VBLONGTIME 61 +#define DISPID_GLOBAL_VBSHORTTIME 62 +#define DISPID_GLOBAL_VBOBJECTERROR 63 +#define DISPID_GLOBAL_VBBLACK 64 +#define DISPID_GLOBAL_VBBLUE 65 +#define DISPID_GLOBAL_VBCYAN 66 +#define DISPID_GLOBAL_VBGREEN 67 +#define DISPID_GLOBAL_VBMAGENTA 68 +#define DISPID_GLOBAL_VBRED 69 +#define DISPID_GLOBAL_VBWHITE 70 +#define DISPID_GLOBAL_VBYELLOW 71 +#define DISPID_GLOBAL_VBCR 72 +#define DISPID_GLOBAL_VBCRLF 73 +#define DISPID_GLOBAL_VBNEWLINE 74 +#define DISPID_GLOBAL_VBFORMFEED 75 +#define DISPID_GLOBAL_VBLF 76 +#define DISPID_GLOBAL_VBNULLCHAR 77 +#define DISPID_GLOBAL_VBNULLSTRING 78 +#define DISPID_GLOBAL_VBTAB 79 +#define DISPID_GLOBAL_VBVERTICALTAB 80 + +#define DISPID_GLOBAL_VBMSGBOXHELPBUTTON 207 +#define DISPID_GLOBAL_VBMSGBOXSETFOREGROUND 208 +#define DISPID_GLOBAL_VBMSGBOXRIGHT 209 +#define DISPID_GLOBAL_VBMSGBOXRTLREADING 210 + +#define DISPID_GLOBAL_CCUR 100 +#define DISPID_GLOBAL_CINT 101 +#define DISPID_GLOBAL_CLNG 102 +#define DISPID_GLOBAL_CBOOL 103 +#define DISPID_GLOBAL_CBYTE 104 +#define DISPID_GLOBAL_CDATE 105 +#define DISPID_GLOBAL_CDBL 106 +#define DISPID_GLOBAL_CSNG 107 +#define DISPID_GLOBAL_CSTR 108 +#define DISPID_GLOBAL_HEX 109 +#define DISPID_GLOBAL_OCT 110 +#define DISPID_GLOBAL_VARTYPE 111 +#define DISPID_GLOBAL_ISDATE 112 +#define DISPID_GLOBAL_ISEMPTY 113 +#define DISPID_GLOBAL_ISNULL 114 +#define DISPID_GLOBAL_ISNUMERIC 115 +#define DISPID_GLOBAL_ISARRAY 116 +#define DISPID_GLOBAL_ISOBJECT 117 +#define DISPID_GLOBAL_ATN 118 +#define DISPID_GLOBAL_COS 119 +#define DISPID_GLOBAL_SIN 120 +#define DISPID_GLOBAL_TAN 121 +#define DISPID_GLOBAL_EXP 122 +#define DISPID_GLOBAL_LOG 123 +#define DISPID_GLOBAL_SQR 124 +#define DISPID_GLOBAL_RANDOMIZE 125 +#define DISPID_GLOBAL_RND 126 +#define DISPID_GLOBAL_TIMER 127 +#define DISPID_GLOBAL_LBOUND 128 +#define DISPID_GLOBAL_UBOUND 129 +#define DISPID_GLOBAL_RGB 130 +#define DISPID_GLOBAL_LEN 131 +#define DISPID_GLOBAL_LENB 132 +#define DISPID_GLOBAL_LEFT 133 +#define DISPID_GLOBAL_LEFTB 134 +#define DISPID_GLOBAL_RIGHT 135 +#define DISPID_GLOBAL_RIGHTB 136 +#define DISPID_GLOBAL_MID 137 +#define DISPID_GLOBAL_MIDB 138 +#define DISPID_GLOBAL_STRCOMP 139 +#define DISPID_GLOBAL_LCASE 140 +#define DISPID_GLOBAL_UCASE 141 +#define DISPID_GLOBAL_LTRIM 142 +#define DISPID_GLOBAL_RTRIM 143 +#define DISPID_GLOBAL_TRIM 144 +#define DISPID_GLOBAL_SPACE 145 +#define DISPID_GLOBAL_STRING 146 +#define DISPID_GLOBAL_INSTR 147 +#define DISPID_GLOBAL_INSTRB 148 +#define DISPID_GLOBAL_ASCB 149 +#define DISPID_GLOBAL_CHRB 150 +#define DISPID_GLOBAL_ASC 151 +#define DISPID_GLOBAL_CHR 152 +#define DISPID_GLOBAL_ASCW 153 +#define DISPID_GLOBAL_CHRW 154 +#define DISPID_GLOBAL_ABS 155 +#define DISPID_GLOBAL_FIX 156 +#define DISPID_GLOBAL_INT 157 +#define DISPID_GLOBAL_SGN 158 +#define DISPID_GLOBAL_NOW 159 +#define DISPID_GLOBAL_DATE 160 +#define DISPID_GLOBAL_TIME 161 +#define DISPID_GLOBAL_DAY 162 +#define DISPID_GLOBAL_MONTH 163 +#define DISPID_GLOBAL_WEEKDAY 164 +#define DISPID_GLOBAL_YEAR 165 +#define DISPID_GLOBAL_HOUR 166 +#define DISPID_GLOBAL_MINUTE 167 +#define DISPID_GLOBAL_SECOND 168 +#define DISPID_GLOBAL_DATEVALUE 169 +#define DISPID_GLOBAL_TIMEVALUE 170 +#define DISPID_GLOBAL_DATESERIAL 171 +#define DISPID_GLOBAL_TIMESERIAL 172 +#define DISPID_GLOBAL_INPUTBOX 173 +#define DISPID_GLOBAL_MSGBOX 174 +#define DISPID_GLOBAL_CREATEOBJECT 175 +#define DISPID_GLOBAL_GETOBJECT 176 +#define DISPID_GLOBAL_DATEADD 177 +#define DISPID_GLOBAL_DATEDIFF 178 +#define DISPID_GLOBAL_DATEPART 179 +#define DISPID_GLOBAL_TYPENAME 180 +#define DISPID_GLOBAL_ARRAY 181 +#define DISPID_GLOBAL_ERASE 182 +#define DISPID_GLOBAL_FILTER 183 +#define DISPID_GLOBAL_JOIN 184 +#define DISPID_GLOBAL_SPLIT 185 +#define DISPID_GLOBAL_REPLACE 186 +#define DISPID_GLOBAL_STRREVERSE 187 +#define DISPID_GLOBAL_INSTRREV 188 +#define DISPID_GLOBAL_LOADPICTURE 189 +#define DISPID_GLOBAL_SCRIPTENGINE 190 +#define DISPID_GLOBAL_SCRIPTENGINEMAJORVERSION 191 +#define DISPID_GLOBAL_SCRIPTENGINEMINORVERSION 192 +#define DISPID_GLOBAL_SCRIPTENGINEBUILDVERSION 193 +#define DISPID_GLOBAL_FORMATNUMBER 194 +#define DISPID_GLOBAL_FORMATCURRENCY 195 +#define DISPID_GLOBAL_FORMATPERCENT 196 +#define DISPID_GLOBAL_FORMATDATETIME 197 +#define DISPID_GLOBAL_WEEKDAYNAME 198 +#define DISPID_GLOBAL_MONTHNAME 199 +#define DISPID_GLOBAL_ROUND 200 +#define DISPID_GLOBAL_ESCAPE 201 +#define DISPID_GLOBAL_UNESCAPE 202 +#define DISPID_GLOBAL_EVAL 203 +#define DISPID_GLOBAL_EXECUTE 204 +#define DISPID_GLOBAL_EXECUTEGLOBAL 205 +#define DISPID_GLOBAL_GETREF 206 + +#define DISPID_ERR_DESCRIPTION 0 +#define DISPID_ERR_HELPCONTEXT 1 +#define DISPID_ERR_HELPFILE 2 +#define DISPID_ERR_NUMBER 3 +#define DISPID_ERR_SOURCE 4 +#define DISPID_ERR_CLEAR 100 +#define DISPID_ERR_RAISE 101 + +#define DISPID_SUBMATCHES_COUNT 1 + +#define DISPID_MATCHCOLLECTION_COUNT 1 + +#define DISPID_MATCH_FIRSTINDEX 10001 +#define DISPID_MATCH_LENGTH 10002 +#define DISPID_MATCH_SUBMATCHES 10003 + +#define DISPID_REGEXP_PATTERN 10001 +#define DISPID_REGEXP_IGNORECASE 10002 +#define DISPID_REGEXP_GLOBAL 10003 +#define DISPID_REGEXP_EXECUTE 10004 +#define DISPID_REGEXP_TEST 10005 +#define DISPID_REGEXP_REPLACE 10006 +#define DISPID_REGEXP_MULTILINE 10007 diff --git a/reactos/dll/win32/vbscript/vbscript_main.c b/reactos/dll/win32/vbscript/vbscript_main.c new file mode 100644 index 00000000000..20caf7abe5b --- /dev/null +++ b/reactos/dll/win32/vbscript/vbscript_main.c @@ -0,0 +1,383 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#include + +#include +#include +#include +#include +//#include "vbscript_classes.h" +#include "vbsglobal.h" +#include "vbsregexp55.h" + +#include + +WINE_DEFAULT_DEBUG_CHANNEL(vbscript); +WINE_DECLARE_DEBUG_CHANNEL(heap); + +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); + +static HINSTANCE vbscript_hinstance; + +static ITypeLib *typelib; +static ITypeInfo *typeinfos[LAST_tid]; + +static REFIID tid_ids[] = { +#define XDIID(iface) &DIID_ ## iface, +TID_LIST +#undef XDIID +}; + +HRESULT get_typeinfo(tid_t tid, ITypeInfo **typeinfo) +{ + HRESULT hres; + + if (!typelib) { + ITypeLib *tl; + + static const WCHAR vbscript_dll1W[] = {'v','b','s','c','r','i','p','t','.','d','l','l','\\','1',0}; + + hres = LoadTypeLib(vbscript_dll1W, &tl); + if(FAILED(hres)) { + ERR("LoadRegTypeLib failed: %08x\n", hres); + return hres; + } + + if(InterlockedCompareExchangePointer((void**)&typelib, tl, NULL)) + ITypeLib_Release(tl); + } + + if(!typeinfos[tid]) { + ITypeInfo *ti; + + hres = ITypeLib_GetTypeInfoOfGuid(typelib, tid_ids[tid], &ti); + if(FAILED(hres)) { + ERR("GetTypeInfoOfGuid(%s) failed: %08x\n", debugstr_guid(tid_ids[tid]), hres); + return hres; + } + + if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), ti, NULL)) + ITypeInfo_Release(ti); + } + + *typeinfo = typeinfos[tid]; + return S_OK; +} + +static void release_typelib(void) +{ + unsigned i; + + if(!typelib) + return; + + for(i=0; i < sizeof(typeinfos)/sizeof(*typeinfos); i++) { + if(typeinfos[i]) + ITypeInfo_Release(typeinfos[i]); + } + + ITypeLib_Release(typelib); +} + +const char *debugstr_variant(const VARIANT *v) +{ + if(!v) + return "(null)"; + + if(V_ISBYREF(v)) + return wine_dbg_sprintf("{V_BYREF -> %s}", debugstr_variant(V_BYREF(v))); + + switch(V_VT(v)) { + case VT_EMPTY: + return "{VT_EMPTY}"; + case VT_NULL: + return "{VT_NULL}"; + case VT_I2: + return wine_dbg_sprintf("{VT_I2: %d}", V_I2(v)); + case VT_I4: + return wine_dbg_sprintf("{VT_I4: %d}", V_I4(v)); + case VT_UI4: + return wine_dbg_sprintf("{VT_UI4: %u}", V_UI4(v)); + case VT_R8: + return wine_dbg_sprintf("{VT_R8: %lf}", V_R8(v)); + case VT_BSTR: + return wine_dbg_sprintf("{VT_BSTR: %s}", debugstr_w(V_BSTR(v))); + case VT_DISPATCH: + return wine_dbg_sprintf("{VT_DISPATCH: %p}", V_DISPATCH(v)); + case VT_BOOL: + return wine_dbg_sprintf("{VT_BOOL: %x}", V_BOOL(v)); + default: + return wine_dbg_sprintf("{vt %d}", V_VT(v)); + } +} + +#define MIN_BLOCK_SIZE 128 +#define ARENA_FREE_FILLER 0xaa + +static inline DWORD block_size(DWORD block) +{ + return MIN_BLOCK_SIZE << block; +} + +void heap_pool_init(heap_pool_t *heap) +{ + memset(heap, 0, sizeof(*heap)); + list_init(&heap->custom_blocks); +} + +void *heap_pool_alloc(heap_pool_t *heap, size_t size) +{ + struct list *list; + void *tmp; + + size = (size+3)&~3; + + if(!heap->block_cnt) { + if(!heap->blocks) { + heap->blocks = heap_alloc(sizeof(void*)); + if(!heap->blocks) + return NULL; + } + + tmp = heap_alloc(block_size(0)); + if(!tmp) + return NULL; + + heap->blocks[0] = tmp; + heap->block_cnt = 1; + } + + if(heap->offset + size <= block_size(heap->last_block)) { + tmp = ((BYTE*)heap->blocks[heap->last_block])+heap->offset; + heap->offset += size; + return tmp; + } + + if(size <= block_size(heap->last_block+1)) { + if(heap->last_block+1 == heap->block_cnt) { + tmp = heap_realloc(heap->blocks, (heap->block_cnt+1)*sizeof(void*)); + if(!tmp) + return NULL; + + heap->blocks = tmp; + heap->blocks[heap->block_cnt] = heap_alloc(block_size(heap->block_cnt)); + if(!heap->blocks[heap->block_cnt]) + return NULL; + + heap->block_cnt++; + } + + heap->last_block++; + heap->offset = size; + return heap->blocks[heap->last_block]; + } + + list = heap_alloc(size + sizeof(struct list)); + if(!list) + return NULL; + + list_add_head(&heap->custom_blocks, list); + return list+1; +} + +void *heap_pool_grow(heap_pool_t *heap, void *mem, DWORD size, DWORD inc) +{ + void *ret; + + if(mem == (BYTE*)heap->blocks[heap->last_block] + heap->offset-size + && heap->offset+inc < block_size(heap->last_block)) { + heap->offset += inc; + return mem; + } + + ret = heap_pool_alloc(heap, size+inc); + if(ret) /* FIXME: avoid copying for custom blocks */ + memcpy(ret, mem, size); + return ret; +} + +void heap_pool_clear(heap_pool_t *heap) +{ + struct list *tmp; + + if(!heap) + return; + + while((tmp = list_next(&heap->custom_blocks, &heap->custom_blocks))) { + list_remove(tmp); + heap_free(tmp); + } + + if(WARN_ON(heap)) { + DWORD i; + + for(i=0; i < heap->block_cnt; i++) + memset(heap->blocks[i], ARENA_FREE_FILLER, block_size(i)); + } + + heap->last_block = heap->offset = 0; + heap->mark = FALSE; +} + +void heap_pool_free(heap_pool_t *heap) +{ + DWORD i; + + heap_pool_clear(heap); + + for(i=0; i < heap->block_cnt; i++) + heap_free(heap->blocks[i]); + heap_free(heap->blocks); + + heap_pool_init(heap); +} + +heap_pool_t *heap_pool_mark(heap_pool_t *heap) +{ + if(heap->mark) + return NULL; + + heap->mark = TRUE; + return heap; +} + +static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) +{ + *ppv = NULL; + + if(IsEqualGUID(&IID_IUnknown, riid)) { + TRACE("(%p)->(IID_IUnknown %p)\n", iface, ppv); + *ppv = iface; + }else if(IsEqualGUID(&IID_IClassFactory, riid)) { + TRACE("(%p)->(IID_IClassFactory %p)\n", iface, ppv); + *ppv = iface; + } + + if(*ppv) { + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; + } + + FIXME("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppv); + return E_NOINTERFACE; +} + +static ULONG WINAPI ClassFactory_AddRef(IClassFactory *iface) +{ + TRACE("(%p)\n", iface); + return 2; +} + +static ULONG WINAPI ClassFactory_Release(IClassFactory *iface) +{ + TRACE("(%p)\n", iface); + return 1; +} + +static HRESULT WINAPI ClassFactory_LockServer(IClassFactory *iface, BOOL fLock) +{ + TRACE("(%p)->(%x)\n", iface, fLock); + return S_OK; +} + +static const IClassFactoryVtbl VBScriptFactoryVtbl = { + ClassFactory_QueryInterface, + ClassFactory_AddRef, + ClassFactory_Release, + VBScriptFactory_CreateInstance, + ClassFactory_LockServer +}; + +static IClassFactory VBScriptFactory = { &VBScriptFactoryVtbl }; + +static const IClassFactoryVtbl VBScriptRegExpFactoryVtbl = { + ClassFactory_QueryInterface, + ClassFactory_AddRef, + ClassFactory_Release, + VBScriptRegExpFactory_CreateInstance, + ClassFactory_LockServer +}; + +static IClassFactory VBScriptRegExpFactory = { &VBScriptRegExpFactoryVtbl }; + +/****************************************************************** + * DllMain (vbscript.@) + */ +BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv) +{ + TRACE("(%p %d %p)\n", hInstDLL, fdwReason, lpv); + + switch(fdwReason) + { + case DLL_WINE_PREATTACH: + return FALSE; /* prefer native version */ + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hInstDLL); + vbscript_hinstance = hInstDLL; + break; + case DLL_PROCESS_DETACH: + release_typelib(); + release_regexp_typelib(); + } + + return TRUE; +} + +/*********************************************************************** + * DllGetClassObject (vbscript.@) + */ +HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) +{ + if(IsEqualGUID(&CLSID_VBScript, rclsid)) { + TRACE("(CLSID_VBScript %s %p)\n", debugstr_guid(riid), ppv); + return IClassFactory_QueryInterface(&VBScriptFactory, riid, ppv); + }else if(IsEqualGUID(&CLSID_VBScriptRegExp, rclsid)) { + TRACE("(CLSID_VBScriptRegExp %s %p)\n", debugstr_guid(riid), ppv); + return IClassFactory_QueryInterface(&VBScriptRegExpFactory, riid, ppv); + } + + FIXME("%s %s %p\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); + return CLASS_E_CLASSNOTAVAILABLE; +} + +/*********************************************************************** + * DllCanUnloadNow (vbscript.@) + */ +HRESULT WINAPI DllCanUnloadNow(void) +{ + return S_FALSE; +} + +/*********************************************************************** + * DllRegisterServer (vbscript.@) + */ +HRESULT WINAPI DllRegisterServer(void) +{ + TRACE("()\n"); + return __wine_register_resources(vbscript_hinstance); +} + +/*********************************************************************** + * DllUnregisterServer (vbscript.@) + */ +HRESULT WINAPI DllUnregisterServer(void) +{ + TRACE("()\n"); + return __wine_unregister_resources(vbscript_hinstance); +} diff --git a/reactos/dll/win32/vbscript/vbsglobal.idl b/reactos/dll/win32/vbscript/vbsglobal.idl new file mode 100644 index 00000000000..fb847c1b70d --- /dev/null +++ b/reactos/dll/win32/vbscript/vbsglobal.idl @@ -0,0 +1,755 @@ +/* + * Copyright 2011 Jacek Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +import "oaidl.idl"; + +#include "vbscript_defs.h" + +[ + helpstring("Microsoft VBScript Globals"), + uuid(3eef9758-35fc-11d1-8ce4-00c04fc2b185), + version(1.0) +] +library VBScript_Global +{ + importlib("stdole2.tlb"); + + [ + uuid(3eef9758-35fc-11d1-8ce4-00c04fc2B186) + ] + dispinterface GlobalObj { + properties: + [id(DISPID_GLOBAL_VBUSESYSTEM), readonly] + VARIANT vbUseSystem; + + [id(DISPID_GLOBAL_USESYSTEMDAYOFWEEK), readonly] + VARIANT vbUseSystemDayOfWeek; + + [id(DISPID_GLOBAL_VBSUNDAY), readonly] + VARIANT vbSunday; + + [id(DISPID_GLOBAL_VBMONDAY), readonly] + VARIANT vbMonday; + + [id(DISPID_GLOBAL_VBTUESDAY), readonly] + VARIANT vbTuesday; + + [id(DISPID_GLOBAL_VBWEDNESDAY), readonly] + VARIANT vbWednesday; + + [id(DISPID_GLOBAL_VBTHURSDAY), readonly] + VARIANT vbThursday; + + [id(DISPID_GLOBAL_VBFRIDAY), readonly] + VARIANT vbFriday; + + [id(DISPID_GLOBAL_VBSATURDAY), readonly] + VARIANT vbSaturday; + + [id(DISPID_GLOBAL_VBFIRSTJAN1), readonly] + VARIANT vbFirstJan1; + + [id(DISPID_GLOBAL_VBFIRSTFOURDAYS), readonly] + VARIANT vbFirstFourDays; + + [id(DISPID_GLOBAL_VBFIRSTFULLWEEK), readonly] + VARIANT vbFirstFullWeek; + + [id(DISPID_GLOBAL_VBOKONLY), readonly] + VARIANT vbOKOnly; + + [id(DISPID_GLOBAL_VBOKCANCEL), readonly] + VARIANT vbOKCancel; + + [id(DISPID_GLOBAL_VBABORTRETRYIGNORE), readonly] + VARIANT vbAbortRetryIgnore; + + [id(DISPID_GLOBAL_VBYESNOCANCEL), readonly] + VARIANT vbYesNoCancel; + + [id(DISPID_GLOBAL_VBYESNO), readonly] + VARIANT vbYesNo; + + [id(DISPID_GLOBAL_VBRETRYCANCEL), readonly] + VARIANT vbRetryCancel; + + [id(DISPID_GLOBAL_VBCRITICAL), readonly] + VARIANT vbCritical; + + [id(DISPID_GLOBAL_VBQUESTION), readonly] + VARIANT vbQuestion; + + [id(DISPID_GLOBAL_VBEXCLAMATION), readonly] + VARIANT vbExclamation; + + [id(DISPID_GLOBAL_VBINFORMATION), readonly] + VARIANT vbInformation; + + [id(DISPID_GLOBAL_VBDEFAULTBUTTON1), readonly] + VARIANT vbDefaultButton1; + + [id(DISPID_GLOBAL_VBDEFAULTBUTTON2), readonly] + VARIANT vbDefaultButton2; + + [id(DISPID_GLOBAL_VBDEFAULTBUTTON3), readonly] + VARIANT vbDefaultButton3; + + [id(DISPID_GLOBAL_VBDEFAULTBUTTON4), readonly] + VARIANT vbDefaultButton4; + + [id(DISPID_GLOBAL_VBAPPLICATIONMODAL), readonly] + VARIANT vbApplicationModal; + + [id(DISPID_GLOBAL_VBSYSTEMMODAL), readonly] + VARIANT vbSystemModal; + + [id(DISPID_GLOBAL_VBOK), readonly] + VARIANT vbOK; + + [id(DISPID_GLOBAL_VBCANCEL), readonly] + VARIANT vbCancel; + + [id(DISPID_GLOBAL_VBABORT), readonly] + VARIANT vbAbort; + + [id(DISPID_GLOBAL_VBRETRY), readonly] + VARIANT vbRetry; + + [id(DISPID_GLOBAL_VBIGNORE), readonly] + VARIANT vbIgnore; + + [id(DISPID_GLOBAL_VBYES), readonly] + VARIANT vbYes; + + [id(DISPID_GLOBAL_VBNO), readonly] + VARIANT vbNo; + + [id(DISPID_GLOBAL_VBEMPTY), readonly] + VARIANT vbEmpty; + + [id(DISPID_GLOBAL_VBNULL), readonly] + VARIANT vbNull; + + [id(DISPID_GLOBAL_VBINTEGER), readonly] + VARIANT vbInteger; + + [id(DISPID_GLOBAL_VBLONG), readonly] + VARIANT vbLong; + + [id(DISPID_GLOBAL_VBSINGLE), readonly] + VARIANT vbSingle; + + [id(DISPID_GLOBAL_VBDOUBLE), readonly] + VARIANT vbDouble; + + [id(DISPID_GLOBAL_VBCURRENCY), readonly] + VARIANT vbCurrency; + + [id(DISPID_GLOBAL_VBDATE), readonly] + VARIANT vbDate; + + [id(DISPID_GLOBAL_VBSTRING), readonly] + VARIANT vbString; + + [id(DISPID_GLOBAL_VBOBJECT), readonly] + VARIANT vbObject; + + [id(DISPID_GLOBAL_VBERROR), readonly] + VARIANT vbError; + + [id(DISPID_GLOBAL_VBBOOLEAN), readonly] + VARIANT vbBoolean; + + [id(DISPID_GLOBAL_VBVARIANT), readonly] + VARIANT vbVariant; + + [id(DISPID_GLOBAL_VBDATAOBJECT), readonly] + VARIANT vbDataObject; + + [id(DISPID_GLOBAL_VBDECIMAL), readonly] + VARIANT vbDecimal; + + [id(DISPID_GLOBAL_VBBYTE), readonly] + VARIANT vbByte; + + [id(DISPID_GLOBAL_VBARRAY), readonly] + VARIANT vbArray; + + [id(DISPID_GLOBAL_VBTRUE), readonly] + VARIANT vbTrue; + + [id(DISPID_GLOBAL_VBFALSE), readonly] + VARIANT vbFalse; + + [id(DISPID_GLOBAL_VBUSEDEFAULT), readonly] + VARIANT vbUseDefault; + + [id(DISPID_GLOBAL_VBBINARYCOMPARE), readonly] + VARIANT vbBinaryCompare; + + [id(DISPID_GLOBAL_VBTEXTCOMPARE), readonly] + VARIANT vbTextCompare; + + [id(DISPID_GLOBAL_VBDATABASECOMPARE), readonly] + VARIANT vbDatabaseCompare; + + [id(DISPID_GLOBAL_VBGENERALDATE), readonly] + VARIANT vbGeneralDate; + + [id(DISPID_GLOBAL_VBLONGDATE), readonly] + VARIANT vbLongDate; + + [id(DISPID_GLOBAL_VBSHORTDATE), readonly] + VARIANT vbShortDate; + + [id(DISPID_GLOBAL_VBLONGTIME), readonly] + VARIANT vbLongTime; + + [id(DISPID_GLOBAL_VBSHORTTIME), readonly] + VARIANT vbShortTime; + + [id(DISPID_GLOBAL_VBOBJECTERROR), readonly] + VARIANT vbObjectError; + + [id(DISPID_GLOBAL_VBBLACK), readonly] + VARIANT vbBlack; + + [id(DISPID_GLOBAL_VBBLUE), readonly] + VARIANT vbBlue; + + [id(DISPID_GLOBAL_VBCYAN), readonly] + VARIANT vbCyan; + + [id(DISPID_GLOBAL_VBGREEN), readonly] + VARIANT vbGreen; + + [id(DISPID_GLOBAL_VBMAGENTA), readonly] + VARIANT vbMagenta; + + [id(DISPID_GLOBAL_VBRED), readonly] + VARIANT vbRed; + + [id(DISPID_GLOBAL_VBWHITE), readonly] + VARIANT vbWhite; + + [id(DISPID_GLOBAL_VBYELLOW), readonly] + VARIANT vbYellow; + + [id(DISPID_GLOBAL_VBCR), readonly] + VARIANT vbCr; + + [id(DISPID_GLOBAL_VBCRLF), readonly] + VARIANT vbCrLf; + + [id(DISPID_GLOBAL_VBNEWLINE), readonly] + VARIANT vbNewLine; + + [id(DISPID_GLOBAL_VBFORMFEED), readonly] + VARIANT vbFormFeed; + + [id(DISPID_GLOBAL_VBLF), readonly] + VARIANT vbLf; + + [id(DISPID_GLOBAL_VBNULLCHAR), readonly] + VARIANT vbNullChar; + + [id(DISPID_GLOBAL_VBNULLSTRING), readonly] + VARIANT vbNullString; + + [id(DISPID_GLOBAL_VBTAB), readonly] + VARIANT vbTab; + + [id(DISPID_GLOBAL_VBVERTICALTAB), readonly] + VARIANT vbVerticalTab; + + [id(DISPID_GLOBAL_VBMSGBOXHELPBUTTON), readonly] + VARIANT vbMsgBoxHelpButton; + + [id(DISPID_GLOBAL_VBMSGBOXSETFOREGROUND), readonly] + VARIANT vbMsgBoxSetForeground; + + [id(DISPID_GLOBAL_VBMSGBOXRIGHT), readonly] + VARIANT vbMsgBoxRight; + + [id(DISPID_GLOBAL_VBMSGBOXRTLREADING), readonly] + VARIANT vbMsgBoxRtlReading; + + methods: + [id(DISPID_GLOBAL_CCUR)] + VARIANT CCur(VARIANT expression); + + [id(DISPID_GLOBAL_CINT)] + VARIANT CInt(VARIANT expression); + + [id(DISPID_GLOBAL_CLNG)] + VARIANT CLng(VARIANT expression); + + [id(DISPID_GLOBAL_CBOOL)] + VARIANT CBool(VARIANT expression); + + [id(DISPID_GLOBAL_CBYTE)] + VARIANT CByte(VARIANT expression); + + [id(DISPID_GLOBAL_CDATE)] + VARIANT CDate(VARIANT expression); + + [id(DISPID_GLOBAL_CDBL)] + VARIANT CDbl(VARIANT expression); + + [id(DISPID_GLOBAL_CSNG)] + VARIANT CSng(VARIANT expression); + + [id(DISPID_GLOBAL_CSTR)] + VARIANT CStr(VARIANT expression); + + [id(DISPID_GLOBAL_HEX)] + VARIANT Hex(VARIANT number); + + [id(DISPID_GLOBAL_OCT)] + VARIANT Oct(VARIANT number); + + [id(DISPID_GLOBAL_VARTYPE)] + VARIANT VarType(VARIANT varname); + + [id(DISPID_GLOBAL_ISDATE)] + VARIANT IsDate(VARIANT expression); + + [id(DISPID_GLOBAL_ISEMPTY)] + VARIANT IsEmpty(VARIANT expression); + + [id(DISPID_GLOBAL_ISNULL)] + VARIANT IsNull(VARIANT expression); + + [id(DISPID_GLOBAL_ISNUMERIC)] + VARIANT IsNumeric(VARIANT expression); + + [id(DISPID_GLOBAL_ISARRAY)] + VARIANT IsArray(VARIANT expression); + + [id(DISPID_GLOBAL_ISOBJECT)] + VARIANT IsObject(VARIANT identifier); + + [id(DISPID_GLOBAL_ATN)] + VARIANT Atn(VARIANT number); + + [id(DISPID_GLOBAL_COS)] + VARIANT Cos(VARIANT number); + + [id(DISPID_GLOBAL_SIN)] + VARIANT Sin(VARIANT number); + + [id(DISPID_GLOBAL_TAN)] + VARIANT Tan(VARIANT number); + + [id(DISPID_GLOBAL_EXP)] + VARIANT Exp(VARIANT number); + + [id(DISPID_GLOBAL_LOG)] + VARIANT Log(VARIANT number); + + [id(DISPID_GLOBAL_SQR)] + VARIANT Sqr(VARIANT number); + + [id(DISPID_GLOBAL_RANDOMIZE)] + VARIANT Randomize(VARIANT number); + + [id(DISPID_GLOBAL_RND)] + VARIANT Rnd(VARIANT number); + + [id(DISPID_GLOBAL_TIMER)] + VARIANT Timer(); + + [id(DISPID_GLOBAL_LBOUND)] + VARIANT LBound(VARIANT arrayname); + + [id(DISPID_GLOBAL_UBOUND)] + VARIANT UBound(VARIANT arrayname); + + [id(DISPID_GLOBAL_RGB)] + VARIANT RGB( + VARIANT red, + VARIANT green, + VARIANT blue); + + [id(DISPID_GLOBAL_LEN)] + VARIANT Len(VARIANT string); + + [id(DISPID_GLOBAL_LENB)] + VARIANT LenB(VARIANT string); + + [id(DISPID_GLOBAL_LEFT)] + VARIANT Left( + VARIANT string, + VARIANT length); + + [id(DISPID_GLOBAL_LEFTB)] + VARIANT LeftB( + VARIANT string, + VARIANT length); + + [id(DISPID_GLOBAL_RIGHT)] + VARIANT Right( + VARIANT string, + VARIANT length); + + [id(DISPID_GLOBAL_RIGHTB)] + VARIANT RightB( + VARIANT string, + VARIANT length); + + [id(DISPID_GLOBAL_MID)] + VARIANT Mid( + VARIANT string, + VARIANT start, + [optional] VARIANT length); + + [id(DISPID_GLOBAL_MIDB)] + VARIANT MidB( + VARIANT string, + VARIANT start, + [optional] VARIANT length); + + [id(DISPID_GLOBAL_STRCOMP)] + VARIANT StrComp( + VARIANT string1, + VARIANT string2, + [optional] VARIANT compare); + + [id(DISPID_GLOBAL_LCASE)] + VARIANT LCase(VARIANT string); + + [id(DISPID_GLOBAL_UCASE)] + VARIANT UCase(VARIANT string); + + [id(DISPID_GLOBAL_LTRIM)] + VARIANT LTrim(VARIANT string); + + [id(DISPID_GLOBAL_RTRIM)] + VARIANT RTrim(VARIANT string); + + [id(DISPID_GLOBAL_TRIM)] + VARIANT Trim(VARIANT string); + + [id(DISPID_GLOBAL_SPACE)] + VARIANT Space(VARIANT number); + + [id(DISPID_GLOBAL_STRING)] + VARIANT string( + [optional] VARIANT number, + [optional] VARIANT character); + + [id(DISPID_GLOBAL_INSTR)] + VARIANT InStr( + VARIANT start, + VARIANT string, + VARIANT string2, + [optional] VARIANT compare); + + [id(DISPID_GLOBAL_INSTRB)] + VARIANT InStrB( + VARIANT start, + VARIANT string, + VARIANT string2, + [optional] VARIANT compare); + + [id(DISPID_GLOBAL_ASCB)] + VARIANT AscB(VARIANT string); + + [id(DISPID_GLOBAL_CHRB)] + VARIANT ChrB(VARIANT charcode); + + [id(DISPID_GLOBAL_ASC)] + VARIANT Asc(VARIANT string); + + [id(DISPID_GLOBAL_CHR)] + VARIANT Chr(VARIANT charcode); + + [id(DISPID_GLOBAL_ASCW)] + VARIANT AscW(VARIANT string); + + [id(DISPID_GLOBAL_CHRW)] + VARIANT ChrW(VARIANT charcode); + + [id(DISPID_GLOBAL_ABS)] + VARIANT Abs(VARIANT number); + + [id(DISPID_GLOBAL_FIX)] + VARIANT Fix(VARIANT number); + + [id(DISPID_GLOBAL_INT)] + VARIANT Int(VARIANT number); + + [id(DISPID_GLOBAL_SGN)] + VARIANT Sgn(VARIANT number); + + [id(DISPID_GLOBAL_NOW)] + VARIANT Now(); + + [id(DISPID_GLOBAL_DATE)] + VARIANT Date(); + + [id(DISPID_GLOBAL_TIME)] + VARIANT Time(); + + [id(DISPID_GLOBAL_DAY)] + VARIANT Day(VARIANT Date); + + [id(DISPID_GLOBAL_MONTH)] + VARIANT Month(VARIANT Date); + + [id(DISPID_GLOBAL_WEEKDAY)] + VARIANT Weekday( + VARIANT Date, + [optional] VARIANT firstdayofweek); + + [id(DISPID_GLOBAL_YEAR)] + VARIANT Year(VARIANT Date); + + [id(DISPID_GLOBAL_HOUR)] + VARIANT Hour(VARIANT Time); + + [id(DISPID_GLOBAL_MINUTE)] + VARIANT Minute(VARIANT Time); + + [id(DISPID_GLOBAL_SECOND)] + VARIANT Second(VARIANT Time); + + [id(DISPID_GLOBAL_DATEVALUE)] + VARIANT DateValue(VARIANT Date); + + [id(DISPID_GLOBAL_TIMEVALUE)] + VARIANT TimeValue(VARIANT Time); + + [id(DISPID_GLOBAL_DATESERIAL)] + VARIANT DateSerial( + VARIANT Year, + VARIANT Month, + VARIANT Date); + + [id(DISPID_GLOBAL_TIMESERIAL)] + VARIANT TimeSerial( + VARIANT Hour, + VARIANT Minute, + VARIANT Second); + + [id(DISPID_GLOBAL_INPUTBOX)] + VARIANT InputBox( + VARIANT prompt, + [optional] VARIANT title, + [optional] VARIANT defaultValue, + [optional] VARIANT xpos, + [optional] VARIANT ypos, + [optional] VARIANT helpfile, + [optional] VARIANT context); + + [id(DISPID_GLOBAL_MSGBOX)] + VARIANT MsgBox( + VARIANT prompt, + [optional] VARIANT buttons, + [optional] VARIANT title, + [optional] VARIANT helpfile, + [optional] VARIANT context); + + [id(DISPID_GLOBAL_CREATEOBJECT)] + VARIANT CreateObject(VARIANT classValue); + + [id(DISPID_GLOBAL_GETOBJECT)] + VARIANT GetObject( + [optional] VARIANT pathname, + [optional] VARIANT classValue); + + [id(DISPID_GLOBAL_DATEADD)] + VARIANT DateAdd( + VARIANT interval, + VARIANT number, + VARIANT Date); + + [id(DISPID_GLOBAL_DATEDIFF)] + VARIANT DateDiff( + VARIANT interval, + VARIANT date1, + VARIANT date2, + [optional] VARIANT firstdayofweek, + [optional] VARIANT firstdayofyear); + + [id(DISPID_GLOBAL_DATEPART)] + VARIANT DatePart( + VARIANT interval, + VARIANT Date, + [optional] VARIANT firstdayofweek, + [optional] VARIANT firstdayofyear); + + [id(DISPID_GLOBAL_TYPENAME)] + VARIANT TypeName(VARIANT varname); + + [id(DISPID_GLOBAL_ARRAY)] + VARIANT Array(VARIANT arglist); + + [id(DISPID_GLOBAL_ERASE)] + VARIANT Erase(VARIANT arraylist); + + [id(DISPID_GLOBAL_FILTER)] + VARIANT Filter( + VARIANT InputStrings, + VARIANT Value, + [optional] VARIANT Include, + [optional] VARIANT compare); + + [id(DISPID_GLOBAL_JOIN)] + VARIANT Join( + VARIANT list, + [optional] VARIANT delimiter); + + [id(DISPID_GLOBAL_SPLIT)] + VARIANT Split( + VARIANT expression, + [optional] VARIANT delimiter, + [optional] VARIANT count, + [optional] VARIANT compare); + + [id(DISPID_GLOBAL_REPLACE)] + VARIANT Replace( + VARIANT expression, + VARIANT find, + VARIANT replacement, + [optional] VARIANT start, + [optional] VARIANT count, + [optional] VARIANT compare); + + [id(DISPID_GLOBAL_STRREVERSE)] + VARIANT StrReverse(VARIANT string1); + + [id(DISPID_GLOBAL_INSTRREV)] + VARIANT InStrRev( + VARIANT string1, + VARIANT string2, + [optional] VARIANT start, + [optional] VARIANT compare); + + [id(DISPID_GLOBAL_LOADPICTURE)] + VARIANT LoadPicture([optional] VARIANT stringexpression); + + [id(DISPID_GLOBAL_SCRIPTENGINE)] + VARIANT ScriptEngine(); + + [id(DISPID_GLOBAL_SCRIPTENGINEMAJORVERSION)] + VARIANT ScriptEngineMajorVersion(); + + [id(DISPID_GLOBAL_SCRIPTENGINEMINORVERSION)] + VARIANT ScriptEngineMinorVersion(); + + [id(DISPID_GLOBAL_SCRIPTENGINEBUILDVERSION)] + VARIANT ScriptEngineBuildVersion(); + + [id(DISPID_GLOBAL_FORMATNUMBER)] + VARIANT FormatNumber( + VARIANT expression, + [optional] VARIANT NumDigitsAfterDecimal, + [optional] VARIANT IncludeLeadingDigit, + [optional] VARIANT UseParensForNegativeNumbers, + [optional] VARIANT GroupDigits); + + [id(DISPID_GLOBAL_FORMATCURRENCY)] + VARIANT FormatCurrency( + VARIANT expression, + [optional] VARIANT NumDigitsAfterDecimal, + [optional] VARIANT IncludeLeadingDigit, + [optional] VARIANT UseParensForNegativeNumbers, + [optional] VARIANT GroupDigits); + + [id(DISPID_GLOBAL_FORMATPERCENT)] + VARIANT FormatPercent( + VARIANT expression, + [optional] VARIANT NumDigitsAfterDecimal, + [optional] VARIANT IncludeLeadingDigit, + [optional] VARIANT UseParensForNegativeNumbers, + [optional] VARIANT GroupDigits); + + [id(DISPID_GLOBAL_FORMATDATETIME)] + VARIANT FormatDateTime( + VARIANT Date, + [optional] VARIANT NamedFormat); + + [id(DISPID_GLOBAL_WEEKDAYNAME)] + VARIANT WeekdayName( + VARIANT Weekday, + VARIANT abbreviate, + VARIANT firstdayofweek); + + [id(DISPID_GLOBAL_MONTHNAME)] + VARIANT MonthName( + VARIANT Month, + [optional] VARIANT abbreviate); + + [id(DISPID_GLOBAL_ROUND)] + VARIANT Round( + VARIANT expression, + [optional] VARIANT numdecimalplaces); + + [id(DISPID_GLOBAL_ESCAPE)] + VARIANT Escape(VARIANT string); + + [id(DISPID_GLOBAL_UNESCAPE)] + VARIANT Unescape(VARIANT string); + + [id(DISPID_GLOBAL_EVAL)] + VARIANT Eval(VARIANT string); + + [id(DISPID_GLOBAL_EXECUTE)] + VARIANT Execute(VARIANT string); + + [id(DISPID_GLOBAL_EXECUTEGLOBAL)] + VARIANT ExecuteGlobal(VARIANT string); + + [id(DISPID_GLOBAL_GETREF)] + VARIANT GetRef(VARIANT string); + } + + [ + uuid(3eef9758-35fc-11d1-8ce4-00c04fc2b187) + ] + dispinterface ErrObj { + properties: + [id(DISPID_ERR_DESCRIPTION)] + VARIANT Description; + + [id(DISPID_ERR_HELPCONTEXT)] + VARIANT HelpContext; + + [id(DISPID_ERR_HELPFILE)] + VARIANT helpfile; + + [id(DISPID_ERR_NUMBER)] + VARIANT number; + + [id(DISPID_ERR_SOURCE)] + VARIANT Source; + + methods: + [id(DISPID_ERR_CLEAR)] + VARIANT Clear(); + + [id(DISPID_ERR_RAISE)] + VARIANT Raise( + VARIANT number, + VARIANT Source, + VARIANT Description, + VARIANT helpfile, + VARIANT HelpContext); + } +} diff --git a/reactos/dll/win32/vbscript/vbsglobal.rgs b/reactos/dll/win32/vbscript/vbsglobal.rgs new file mode 100644 index 00000000000..390968d559d --- /dev/null +++ b/reactos/dll/win32/vbscript/vbsglobal.rgs @@ -0,0 +1,32 @@ +HKCR +{ + NoRemove Typelib + { + NoRemove '{3EEF9758-35FC-11D1-8CE4-00C04FC2B185}' + { + '1.0' = s 'Microsoft VBScript Globals' + { + '0' { win32 = s '%MODULE%' } + FLAGS = s '0' + } + } + } + NoRemove Interface + { + '{3EEF9758-35FC-11D1-8CE4-00C04FC2B186}' = s 'GlobalObj' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{3EEF9758-35FC-11D1-8CE4-00C04FC2B185}' { val Version = s '1.0' } + } + '{3EEF9758-35FC-11D1-8CE4-00C04FC2B187}' = s 'ErrObj' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{3EEF9758-35FC-11D1-8CE4-00C04FC2B185}' { val Version = s '1.0' } + } + } + NoRemove CLSID + { + } +} diff --git a/reactos/dll/win32/vbscript/vbsregexp10.idl b/reactos/dll/win32/vbscript/vbsregexp10.idl new file mode 100644 index 00000000000..d1f181ea184 --- /dev/null +++ b/reactos/dll/win32/vbscript/vbsregexp10.idl @@ -0,0 +1,144 @@ +/* + * Copyright 2013 Piotr Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +import "oaidl.idl"; + +#include "vbscript_defs.h" + +[ + helpstring("Microsoft VBScript Regular Expressions 1.0"), + uuid(3f4daca7-160d-11d2-a8e9-00104b365c9f), + version(1.0) +] +library VBScript_RegExp_10 +{ + importlib("stdole2.tlb"); + + [ + dual, + hidden, + nonextensible, + odl, + oleautomation, + uuid(3f4daca0-160d-11d2-a8e9-00104b365c9f), + ] + interface IRegExp : IDispatch + { + [id(DISPID_REGEXP_PATTERN), propget] + HRESULT Pattern([out, retval] BSTR *pPattern); + + [id(DISPID_REGEXP_PATTERN), propput] + HRESULT Pattern([in] BSTR pPattern); + + [id(DISPID_REGEXP_IGNORECASE), propget] + HRESULT IgnoreCase([out, retval] VARIANT_BOOL *pIgnoreCase); + + [id(DISPID_REGEXP_IGNORECASE), propput] + HRESULT IgnoreCase([in] VARIANT_BOOL pIgnoreCase); + + [id(DISPID_REGEXP_GLOBAL), propget] + HRESULT Global([out, retval] VARIANT_BOOL *pGlobal); + + [id(DISPID_REGEXP_GLOBAL), propput] + HRESULT Global([in] VARIANT_BOOL pGlobal); + + [id(DISPID_REGEXP_EXECUTE)] + HRESULT Execute( + [in] BSTR sourceString, + [out, retval] IDispatch **ppMatches); + + [id(DISPID_REGEXP_TEST)] + HRESULT Test( + [in] BSTR sourceString, + [out, retval] VARIANT_BOOL *pMatch); + + [id(DISPID_REGEXP_REPLACE)] + HRESULT Replace( + [in] BSTR sourceString, + [in] BSTR replaceString, + [out, retval] BSTR *pDestString); + } + + [ + dual, + hidden, + nonextensible, + odl, + oleautomation, + uuid(3f4daca1-160d-11d2-a8e9-00104b365c9f) + ] + interface IMatch : IDispatch + { + [id(DISPID_VALUE), propget] + HRESULT Value([out, retval] BSTR *pValue); + + [id(DISPID_MATCH_FIRSTINDEX), propget] + HRESULT FirstIndex([out, retval] LONG *pFirstIndex); + + [id(DISPID_MATCH_LENGTH), propget] + HRESULT Length([out, retval] LONG *pLength); + } + + [ + dual, + hidden, + nonextensible, + odl, + oleautomation, + uuid(3f4daca2-160d-11d2-a8e9-00104b365c9f) + ] + interface IMatchCollection : IDispatch + { + [id(DISPID_VALUE), propget] + HRESULT Item( + [in] LONG index, + [out, retval] IDispatch **ppMatch); + + [id(DISPID_MATCHCOLLECTION_COUNT), propget] + HRESULT Count([out, retval] LONG *pCount); + + [id(DISPID_NEWENUM), propget] + HRESULT _NewEnum([out, retval] IUnknown **ppEnum); + } + + [ + uuid(3f4daca4-160d-11d2-a8e9-00104b365c9f) + ] + coclass RegExp + { + [default] interface IRegExp; + } + + [ + noncreatable, + uuid(3f4daca5-160d-11d2-a8e9-00104b365c9f) + ] + coclass Match + { + [default] interface IMatch; + } + + [ + noncreatable, + uuid(3f4daca6-160d-11d2-a8e9-00104b365c9f) + ] + coclass MatchCollection + { + [default] interface IMatchCollection; + } +} diff --git a/reactos/dll/win32/vbscript/vbsregexp10.rgs b/reactos/dll/win32/vbscript/vbsregexp10.rgs new file mode 100644 index 00000000000..da7876c4991 --- /dev/null +++ b/reactos/dll/win32/vbscript/vbsregexp10.rgs @@ -0,0 +1,38 @@ +HKCR +{ + NoRemove Typelib + { + NoRemove '{3F4DACA7-160D-11D2-A8E9-00104B365C9F}' + { + '1.0' = s 'Microsoft VBScript Regular Expressions 1.0' + { + '0' { win32 = s '%MODULE%' } + FLAGS = s '0' + } + } + } + NoRemove Interface + { + '{3F4DACA0-160D-11D2-A8E9-00104B365C9F}' = s 'IRegExp' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{3F4DACA7-160D-11D2-A8E9-00104B365C9F}' { val Version = s '1.0' } + } + '{3F4DACA1-160D-11D2-A8E9-00104B365C9F}' = s 'IMatch' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{3F4DACA7-160D-11D2-A8E9-00104B365C9F}' { val Version = s '1.0' } + } + '{3F4DACA2-160D-11D2-A8E9-00104B365C9F}' = s 'IMatchCollection' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{3F4DACA7-160D-11D2-A8E9-00104B365C9F}' { val Version = s '1.0' } + } + } + NoRemove CLSID + { + } +} diff --git a/reactos/dll/win32/vbscript/vbsregexp55.idl b/reactos/dll/win32/vbscript/vbsregexp55.idl new file mode 100644 index 00000000000..b6934e02711 --- /dev/null +++ b/reactos/dll/win32/vbscript/vbsregexp55.idl @@ -0,0 +1,270 @@ +/* + * Copyright 2013 Piotr Caban for CodeWeavers + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +import "oaidl.idl"; + +#include "vbscript_defs.h" + +[ + helpstring("Microsoft VBScript Regular Expressions 5.5"), + uuid(3f4daca7-160d-11d2-a8e9-00104b365c9f), + version(5.5) +] +library VBScript_RegExp_55 +{ + importlib("stdole2.tlb"); + + [ + dual, + hidden, + nonextensible, + odl, + oleautomation, + uuid(3f4daca0-160d-11d2-a8e9-00104b365c9f), + ] + interface IRegExp : IDispatch + { + [id(DISPID_REGEXP_PATTERN), propget] + HRESULT Pattern([out, retval] BSTR *pPattern); + + [id(DISPID_REGEXP_PATTERN), propput] + HRESULT Pattern([in] BSTR pPattern); + + [id(DISPID_REGEXP_IGNORECASE), propget] + HRESULT IgnoreCase([out, retval] VARIANT_BOOL *pIgnoreCase); + + [id(DISPID_REGEXP_IGNORECASE), propput] + HRESULT IgnoreCase([in] VARIANT_BOOL pIgnoreCase); + + [id(DISPID_REGEXP_GLOBAL), propget] + HRESULT Global([out, retval] VARIANT_BOOL *pGlobal); + + [id(DISPID_REGEXP_GLOBAL), propput] + HRESULT Global([in] VARIANT_BOOL pGlobal); + + [id(DISPID_REGEXP_EXECUTE)] + HRESULT Execute( + [in] BSTR sourceString, + [out, retval] IDispatch **ppMatches); + + [id(DISPID_REGEXP_TEST)] + HRESULT Test( + [in] BSTR sourceString, + [out, retval] VARIANT_BOOL *pMatch); + + [id(DISPID_REGEXP_REPLACE)] + HRESULT Replace( + [in] BSTR sourceString, + [in] BSTR replaceString, + [out, retval] BSTR *pDestString); + } + + [ + dual, + hidden, + nonextensible, + odl, + oleautomation, + uuid(3f4dacb0-160d-11d2-a8e9-00104b365c9f) + ] + interface IRegExp2 : IDispatch + { + [id(DISPID_REGEXP_PATTERN), propget] + HRESULT Pattern([out, retval] BSTR *pPattern); + + [id(DISPID_REGEXP_PATTERN), propput] + HRESULT Pattern([in] BSTR pPattern); + + [id(DISPID_REGEXP_IGNORECASE), propget] + HRESULT IgnoreCase([out, retval] VARIANT_BOOL *pIgnoreCase); + + [id(DISPID_REGEXP_IGNORECASE), propput] + HRESULT IgnoreCase([in] VARIANT_BOOL pIgnoreCase); + + [id(DISPID_REGEXP_GLOBAL), propget] + HRESULT Global([out, retval] VARIANT_BOOL *pGlobal); + + [id(DISPID_REGEXP_GLOBAL), propput] + HRESULT Global([in] VARIANT_BOOL pGlobal); + + [id(DISPID_REGEXP_MULTILINE), propget] + HRESULT Multiline([out, retval] VARIANT_BOOL *pMultiline); + + [id(DISPID_REGEXP_MULTILINE), propput] + HRESULT Multiline([in] VARIANT_BOOL pMultiline); + + [id(DISPID_REGEXP_EXECUTE)] + HRESULT Execute( + [in] BSTR sourceString, + [out, retval] IDispatch **ppMatches); + + [id(DISPID_REGEXP_TEST)] + HRESULT Test( + [in] BSTR sourceString, + [out, retval] VARIANT_BOOL *pMatch); + + [id(DISPID_REGEXP_REPLACE)] + HRESULT Replace( + [in] BSTR sourceString, + [in] VARIANT replaceVar, + [out, retval] BSTR *pDestString); + } + + [ + dual, + hidden, + nonextensible, + odl, + oleautomation, + uuid(3f4daca1-160d-11d2-a8e9-00104b365c9f) + ] + interface IMatch : IDispatch + { + [id(DISPID_VALUE), propget] + HRESULT Value([out, retval] BSTR *pValue); + + [id(DISPID_MATCH_FIRSTINDEX), propget] + HRESULT FirstIndex([out, retval] LONG *pFirstIndex); + + [id(DISPID_MATCH_LENGTH), propget] + HRESULT Length([out, retval] LONG *pLength); + } + + [ + odl, + uuid(3f4dacb1-160d-11d2-a8e9-00104b365c9f), + hidden, + dual, + nonextensible, + oleautomation + ] + interface IMatch2 : IDispatch + { + [id(DISPID_VALUE), propget] + HRESULT Value([out, retval] BSTR *pValue); + + [id(DISPID_MATCH_FIRSTINDEX), propget] + HRESULT FirstIndex([out, retval] LONG *pFirstIndex); + + [id(DISPID_MATCH_LENGTH), propget] + HRESULT Length([out, retval] LONG *pLength); + + [id(DISPID_MATCH_SUBMATCHES), propget] + HRESULT SubMatches([out, retval] IDispatch **ppSubMatches); + } + + [ + dual, + hidden, + nonextensible, + odl, + oleautomation, + uuid(3f4daca2-160d-11d2-a8e9-00104b365c9f) + ] + interface IMatchCollection : IDispatch + { + [id(DISPID_VALUE), propget] + HRESULT Item( + [in] LONG index, + [out, retval] IDispatch **ppMatch); + + [id(DISPID_MATCHCOLLECTION_COUNT), propget] + HRESULT Count([out, retval] LONG *pCount); + + [id(DISPID_NEWENUM), propget] + HRESULT _NewEnum([out, retval] IUnknown **ppEnum); + } + + [ + dual, + hidden, + nonextensible, + odl, + oleautomation, + uuid(3f4dacb2-160d-11d2-a8e9-00104b365c9f) + ] + interface IMatchCollection2 : IDispatch + { + [id(DISPID_VALUE), propget] + HRESULT Item( + [in] LONG index, + [out, retval] IDispatch **ppMatch); + + [id(DISPID_MATCHCOLLECTION_COUNT), propget] + HRESULT Count([out, retval] LONG *pCount); + + [id(DISPID_NEWENUM), propget] + HRESULT _NewEnum([out, retval] IUnknown **ppEnum); + } + + [ + dual, + hidden, + nonextensible, + odl, + oleautomation, + uuid(3f4dacb3-160d-11d2-a8e9-00104b365c9f) + ] + interface ISubMatches : IDispatch + { + [id(DISPID_VALUE), propget] + HRESULT Item( + [in] LONG index, + [out, retval] VARIANT *pSubMatch); + + [id(DISPID_SUBMATCHES_COUNT), propget] + HRESULT Count([out, retval] LONG *pCount); + + [id(DISPID_NEWENUM), propget] + HRESULT _NewEnum([out, retval] IUnknown **ppEnum); + } + + [ + uuid(3f4daca4-160d-11d2-a8e9-00104b365c9f) + ] + coclass RegExp + { + [default] interface IRegExp2; + } + + [ + noncreatable, + uuid(3f4daca5-160d-11d2-a8e9-00104b365c9f) + ] + coclass Match + { + [default] interface IMatch2; + } + + [ + noncreatable, + uuid(3f4daca6-160d-11d2-a8e9-00104b365c9f) + ] + coclass MatchCollection + { + [default] interface IMatchCollection2; + } + + [ + noncreatable, + uuid(3f4dacc0-160d-11d2-a8e9-00104b365c9f) + ] + coclass SubMatches { + [default] interface ISubMatches; + } +} diff --git a/reactos/dll/win32/vbscript/vbsregexp55.rgs b/reactos/dll/win32/vbscript/vbsregexp55.rgs new file mode 100644 index 00000000000..b685609aea5 --- /dev/null +++ b/reactos/dll/win32/vbscript/vbsregexp55.rgs @@ -0,0 +1,62 @@ +HKCR +{ + NoRemove Typelib + { + NoRemove '{3F4DACA7-160D-11D2-A8E9-00104B365C9F}' + { + '5.5' = s 'Microsoft VBScript Regular Expressions 5.5' + { + '0' { win32 = s '%MODULE%' } + FLAGS = s '0' + } + } + } + NoRemove Interface + { + '{3F4DACA0-160D-11D2-A8E9-00104B365C9F}' = s 'IRegExp' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{3F4DACA7-160D-11D2-A8E9-00104B365C9F}' { val Version = s '5.5' } + } + '{3F4DACB0-160D-11D2-A8E9-00104B365C9F}' = s 'IRegExp2' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{3F4DACA7-160D-11D2-A8E9-00104B365C9F}' { val Version = s '5.5' } + } + '{3F4DACA1-160D-11D2-A8E9-00104B365C9F}' = s 'IMatch' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{3F4DACA7-160D-11D2-A8E9-00104B365C9F}' { val Version = s '5.5' } + } + '{3F4DACB1-160D-11D2-A8E9-00104B365C9F}' = s 'IMatch2' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{3F4DACA7-160D-11D2-A8E9-00104B365C9F}' { val Version = s '5.5' } + } + '{3F4DACA2-160D-11D2-A8E9-00104B365C9F}' = s 'IMatchCollection' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{3F4DACA7-160D-11D2-A8E9-00104B365C9F}' { val Version = s '5.5' } + } + '{3F4DACB2-160D-11D2-A8E9-00104B365C9F}' = s 'IMatchCollection2' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{3F4DACA7-160D-11D2-A8E9-00104B365C9F}' { val Version = s '5.5' } + } + '{3F4DACB3-160D-11D2-A8E9-00104B365C9F}' = s 'ISubMatches' + { + ProxyStubClsid = s '{00020424-0000-0000-C000-000000000046}' + ProxyStubClsid32 = s '{00020424-0000-0000-C000-000000000046}' + TypeLib = s '{3F4DACA7-160D-11D2-A8E9-00104B365C9F}' { val Version = s '5.5' } + } + } + NoRemove CLSID + { + } +} diff --git a/reactos/media/doc/README.WINE b/reactos/media/doc/README.WINE index 78a954f7274..26b7e3c7f8c 100644 --- a/reactos/media/doc/README.WINE +++ b/reactos/media/doc/README.WINE @@ -184,6 +184,7 @@ reactos/dll/win32/url # Synced to Wine-1.5.19 reactos/dll/win32/urlmon # Synced to Wine-1.5.26 reactos/dll/win32/usp10 # Synced to Wine-1.5.26 reactos/dll/win32/uxtheme # Forked +reactos/dll/win32/vbscript # Synced to Wine-1.5.26 reactos/dll/win32/version # Autosync reactos/dll/win32/wer # Autosync reactos/dll/win32/windowscodecs # Synced to Wine-1.5.19