From fff44b9cb5ca585daf1937725871fc133c6b3b93 Mon Sep 17 00:00:00 2001 From: Christoph von Wittich Date: Sun, 21 Sep 2008 13:24:00 +0000 Subject: [PATCH] sync jscript with wine 1.1.5 svn path=/trunk/; revision=36376 --- reactos/dll/win32/jscript/array.c | 345 +++ reactos/dll/win32/jscript/bool.c | 164 + reactos/dll/win32/jscript/dispex.c | 765 ++++- reactos/dll/win32/jscript/engine.c | 2841 +++++++++++++++-- reactos/dll/win32/jscript/engine.h | 58 +- reactos/dll/win32/jscript/function.c | 496 +++ reactos/dll/win32/jscript/global.c | 394 +++ reactos/dll/win32/jscript/jscript.c | 97 +- reactos/dll/win32/jscript/jscript.h | 183 +- reactos/dll/win32/jscript/jscript.rbuild | 11 +- reactos/dll/win32/jscript/jscript_main.c | 2 + reactos/dll/win32/jscript/jsutils.c | 291 ++ reactos/dll/win32/jscript/lex.c | 49 +- reactos/dll/win32/jscript/math.c | 275 ++ reactos/dll/win32/jscript/number.c | 184 ++ reactos/dll/win32/jscript/object.c | 165 + reactos/dll/win32/jscript/parser.tab.c | 81 +- reactos/dll/win32/jscript/parser.tab.h | 14 +- reactos/dll/win32/jscript/parser.y | 69 +- reactos/dll/win32/jscript/regexp.c | 3600 ++++++++++++++++++++++ reactos/dll/win32/jscript/string.c | 579 ++++ 21 files changed, 10333 insertions(+), 330 deletions(-) create mode 100644 reactos/dll/win32/jscript/array.c create mode 100644 reactos/dll/win32/jscript/bool.c create mode 100644 reactos/dll/win32/jscript/function.c create mode 100644 reactos/dll/win32/jscript/global.c create mode 100644 reactos/dll/win32/jscript/math.c create mode 100644 reactos/dll/win32/jscript/number.c create mode 100644 reactos/dll/win32/jscript/object.c create mode 100644 reactos/dll/win32/jscript/regexp.c create mode 100644 reactos/dll/win32/jscript/string.c diff --git a/reactos/dll/win32/jscript/array.c b/reactos/dll/win32/jscript/array.c new file mode 100644 index 00000000000..c131865be11 --- /dev/null +++ b/reactos/dll/win32/jscript/array.c @@ -0,0 +1,345 @@ +/* + * 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 + */ + +#include "jscript.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(jscript); + +typedef struct { + DispatchEx dispex; + + DWORD length; +} ArrayInstance; + +static const WCHAR lengthW[] = {'l','e','n','g','t','h',0}; +static const WCHAR concatW[] = {'c','o','n','c','a','t',0}; +static const WCHAR joinW[] = {'j','o','i','n',0}; +static const WCHAR popW[] = {'p','o','p',0}; +static const WCHAR pushW[] = {'p','u','s','h',0}; +static const WCHAR reverseW[] = {'r','e','v','e','r','s','e',0}; +static const WCHAR shiftW[] = {'s','h','i','f','t',0}; +static const WCHAR sliceW[] = {'s','l','i','c','e',0}; +static const WCHAR sortW[] = {'s','o','r','t',0}; +static const WCHAR spliceW[] = {'s','p','l','i','c','e',0}; +static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0}; +static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0}; +static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0}; +static const WCHAR unshiftW[] = {'u','n','s','h','i','f','t',0}; +static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0}; +static const WCHAR propertyIsEnumerableW[] = + {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0}; +static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0}; + + +static HRESULT Array_length(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + ArrayInstance *This = (ArrayInstance*)dispex; + + TRACE("%p %d\n", This, This->length); + + switch(flags) { + case DISPATCH_PROPERTYGET: + V_VT(retv) = VT_I4; + V_I4(retv) = This->length; + break; + default: + FIXME("unimplemented flags %x\n", flags); + return E_NOTIMPL; + } + + return S_OK; +} + +static HRESULT Array_concat(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_join(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_pop(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_push(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_reverse(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_shift(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_slice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_sort(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_splice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_valueOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_unshift(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Array_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static void Array_destructor(DispatchEx *dispex) +{ + heap_free(dispex); +} + +static void Array_on_put(DispatchEx *dispex, const WCHAR *name) +{ + ArrayInstance *array = (ArrayInstance*)dispex; + const WCHAR *ptr = name; + DWORD id = 0; + + if(!isdigitW(*ptr)) + return; + + while(*ptr && isdigitW(*ptr)) { + id = id*10 + (*ptr-'0'); + ptr++; + } + + if(*ptr) + return; + + if(id >= array->length) + array->length = id+1; +} + +static const builtin_prop_t Array_props[] = { + {concatW, Array_concat, PROPF_METHOD}, + {hasOwnPropertyW, Array_hasOwnProperty, PROPF_METHOD}, + {isPrototypeOfW, Array_isPrototypeOf, PROPF_METHOD}, + {joinW, Array_join, PROPF_METHOD}, + {lengthW, Array_length, 0}, + {popW, Array_pop, PROPF_METHOD}, + {propertyIsEnumerableW, Array_propertyIsEnumerable, PROPF_METHOD}, + {pushW, Array_push, PROPF_METHOD}, + {reverseW, Array_reverse, PROPF_METHOD}, + {shiftW, Array_shift, PROPF_METHOD}, + {sliceW, Array_slice, PROPF_METHOD}, + {sortW, Array_sort, PROPF_METHOD}, + {spliceW, Array_splice, PROPF_METHOD}, + {toLocaleStringW, Array_toLocaleString, PROPF_METHOD}, + {toStringW, Array_toString, PROPF_METHOD}, + {unshiftW, Array_unshift, PROPF_METHOD}, + {valueOfW, Array_valueOf, PROPF_METHOD} +}; + +static const builtin_info_t Array_info = { + JSCLASS_ARRAY, + {NULL, Array_value, 0}, + sizeof(Array_props)/sizeof(*Array_props), + Array_props, + Array_destructor, + Array_on_put +}; + +static HRESULT ArrayConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller) +{ + DispatchEx *obj; + VARIANT *arg_var; + DWORD i; + HRESULT hres; + + TRACE("\n"); + + switch(flags) { + case DISPATCH_CONSTRUCT: { + if(arg_cnt(dp) == 1 && V_VT((arg_var = get_arg(dp, 0))) == VT_I4) { + if(V_I4(arg_var) < 0) { + FIXME("throw RangeError\n"); + return E_FAIL; + } + + hres = create_array(dispex->ctx, V_I4(arg_var), &obj); + if(FAILED(hres)) + return hres; + + V_VT(retv) = VT_DISPATCH; + V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj); + return S_OK; + } + + hres = create_array(dispex->ctx, arg_cnt(dp), &obj); + if(FAILED(hres)) + return hres; + + for(i=0; i < arg_cnt(dp); i++) { + hres = jsdisp_propput_idx(obj, i, lcid, get_arg(dp, i), ei, caller); + if(FAILED(hres)) + break; + } + if(FAILED(hres)) { + jsdisp_release(obj); + return hres; + } + + V_VT(retv) = VT_DISPATCH; + V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj); + break; + } + default: + FIXME("unimplemented flags: %x\n", flags); + return E_NOTIMPL; + } + + return S_OK; +} + +static HRESULT alloc_array(script_ctx_t *ctx, BOOL use_constr, ArrayInstance **ret) +{ + ArrayInstance *array = heap_alloc_zero(sizeof(ArrayInstance)); + HRESULT hres; + + if(use_constr) + hres = init_dispex_from_constr(&array->dispex, ctx, &Array_info, ctx->array_constr); + else + hres = init_dispex(&array->dispex, ctx, &Array_info, NULL); + + if(FAILED(hres)) { + heap_free(array); + return hres; + } + + *ret = array; + return S_OK; +} + +HRESULT create_array_constr(script_ctx_t *ctx, DispatchEx **ret) +{ + ArrayInstance *array; + HRESULT hres; + + hres = alloc_array(ctx, FALSE, &array); + if(FAILED(hres)) + return hres; + + hres = create_builtin_function(ctx, ArrayConstr_value, PROPF_CONSTR, &array->dispex, ret); + + IDispatchEx_Release(_IDispatchEx_(&array->dispex)); + return hres; +} + +HRESULT create_array(script_ctx_t *ctx, DWORD length, DispatchEx **ret) +{ + ArrayInstance *array; + HRESULT hres; + + hres = alloc_array(ctx, TRUE, &array); + if(FAILED(hres)) + return hres; + + array->length = length; + + *ret = &array->dispex; + return S_OK; +} diff --git a/reactos/dll/win32/jscript/bool.c b/reactos/dll/win32/jscript/bool.c new file mode 100644 index 00000000000..1add8efb768 --- /dev/null +++ b/reactos/dll/win32/jscript/bool.c @@ -0,0 +1,164 @@ +/* + * 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 + */ + +#include "jscript.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(jscript); + +typedef struct { + DispatchEx dispex; + + VARIANT_BOOL val; +} BoolInstance; + +static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0}; +static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0}; +static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0}; +static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0}; +static const WCHAR propertyIsEnumerableW[] = + {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0}; +static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0}; + +static HRESULT Bool_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Bool_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Bool_valueOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Bool_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Bool_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Bool_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Bool_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static const builtin_prop_t Bool_props[] = { + {hasOwnPropertyW, Bool_hasOwnProperty, PROPF_METHOD}, + {isPrototypeOfW, Bool_isPrototypeOf, PROPF_METHOD}, + {propertyIsEnumerableW, Bool_propertyIsEnumerable, PROPF_METHOD}, + {toLocaleStringW, Bool_toLocaleString, PROPF_METHOD}, + {toStringW, Bool_toString, PROPF_METHOD}, + {valueOfW, Bool_valueOf, PROPF_METHOD} +}; + +static const builtin_info_t Bool_info = { + JSCLASS_BOOLEAN, + {NULL, Bool_value, 0}, + sizeof(Bool_props)/sizeof(*Bool_props), + Bool_props, + NULL, + NULL +}; + +static HRESULT BoolConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT alloc_bool(script_ctx_t *ctx, BOOL use_constr, BoolInstance **ret) +{ + BoolInstance *bool; + HRESULT hres; + + bool = heap_alloc_zero(sizeof(BoolInstance)); + if(!bool) + return E_OUTOFMEMORY; + + if(use_constr) + hres = init_dispex_from_constr(&bool->dispex, ctx, &Bool_info, ctx->bool_constr); + else + hres = init_dispex(&bool->dispex, ctx, &Bool_info, NULL); + + if(FAILED(hres)) { + heap_free(bool); + return hres; + } + + *ret = bool; + return S_OK; +} + +HRESULT create_bool_constr(script_ctx_t *ctx, DispatchEx **ret) +{ + BoolInstance *bool; + HRESULT hres; + + hres = alloc_bool(ctx, FALSE, &bool); + if(FAILED(hres)) + return hres; + + hres = create_builtin_function(ctx, BoolConstr_value, PROPF_CONSTR, &bool->dispex, ret); + + jsdisp_release(&bool->dispex); + return hres; +} + +HRESULT create_bool(script_ctx_t *ctx, VARIANT_BOOL b, DispatchEx **ret) +{ + BoolInstance *bool; + HRESULT hres; + + hres = alloc_bool(ctx, TRUE, &bool); + if(FAILED(hres)) + return hres; + + bool->val = b; + + *ret = &bool->dispex; + return S_OK; +} diff --git a/reactos/dll/win32/jscript/dispex.c b/reactos/dll/win32/jscript/dispex.c index 3ea1ed14536..1c643f69c9a 100644 --- a/reactos/dll/win32/jscript/dispex.c +++ b/reactos/dll/win32/jscript/dispex.c @@ -23,6 +23,379 @@ WINE_DEFAULT_DEBUG_CHANNEL(jscript); +/* + * This IID is used to get DispatchEx objecto from interface. + * We might consider using private insteface instead. + */ +static const IID IID_IDispatchJS = + {0x719c3050,0xf9d3,0x11cf,{0xa4,0x93,0x00,0x40,0x05,0x23,0xa8,0xa6}}; + +typedef enum { + PROP_VARIANT, + PROP_BUILTIN, + PROP_PROTREF, + PROP_DELETED +} prop_type_t; + +struct _dispex_prop_t { + WCHAR *name; + prop_type_t type; + DWORD flags; + + union { + VARIANT var; + const builtin_prop_t *p; + DWORD ref; + } u; +}; + +static inline DISPID prop_to_id(DispatchEx *This, dispex_prop_t *prop) +{ + return prop - This->props; +} + +static inline dispex_prop_t *get_prop(DispatchEx *This, DISPID id) +{ + if(id < 0 || id >= This->prop_cnt || This->props[id].type == PROP_DELETED) + return NULL; + + return This->props+id; +} + +static DWORD get_flags(DispatchEx *This, dispex_prop_t *prop) +{ + if(prop->type == PROP_PROTREF) { + dispex_prop_t *parent = get_prop(This->prototype, prop->u.ref); + if(!parent) { + prop->type = PROP_DELETED; + return 0; + } + + return get_flags(This->prototype, parent); + } + + return prop->flags; +} + +static const builtin_prop_t *find_builtin_prop(DispatchEx *This, const WCHAR *name) +{ + int min = 0, max, i, r; + + max = This->builtin_info->props_cnt-1; + while(min <= max) { + i = (min+max)/2; + + r = strcmpW(name, This->builtin_info->props[i].name); + if(!r) + return This->builtin_info->props + i; + + if(r < 0) + max = i-1; + else + min = i+1; + } + + return NULL; +} + +static dispex_prop_t *alloc_prop(DispatchEx *This, const WCHAR *name, prop_type_t type, DWORD flags) +{ + dispex_prop_t *ret; + + if(This->buf_size == This->prop_cnt) { + dispex_prop_t *tmp = heap_realloc(This->props, (This->buf_size<<=1)*sizeof(*This->props)); + if(!tmp) + return NULL; + This->props = tmp; + } + + ret = This->props + This->prop_cnt++; + ret->type = type; + ret->flags = flags; + ret->name = heap_strdupW(name); + if(!ret->name) + return NULL; + + return ret; +} + +static dispex_prop_t *alloc_protref(DispatchEx *This, const WCHAR *name, DWORD ref) +{ + dispex_prop_t *ret; + + ret = alloc_prop(This, name, PROP_PROTREF, 0); + if(!ret) + return NULL; + + ret->u.ref = ref; + return ret; +} + +static HRESULT find_prop_name(DispatchEx *This, const WCHAR *name, dispex_prop_t **ret) +{ + const builtin_prop_t *builtin; + dispex_prop_t *prop; + + for(prop = This->props; prop < This->props+This->prop_cnt; prop++) { + if(prop->name && !strcmpW(prop->name, name)) { + *ret = prop; + return S_OK; + } + } + + builtin = find_builtin_prop(This, name); + if(builtin) { + prop = alloc_prop(This, name, PROP_BUILTIN, builtin->flags); + if(!prop) + return E_OUTOFMEMORY; + + prop->u.p = builtin; + *ret = prop; + return S_OK; + } + + *ret = NULL; + return S_OK; +} + +static HRESULT find_prop_name_prot(DispatchEx *This, const WCHAR *name, BOOL alloc, dispex_prop_t **ret) +{ + dispex_prop_t *prop; + HRESULT hres; + + hres = find_prop_name(This, name, &prop); + if(FAILED(hres)) + return hres; + if(prop) { + *ret = prop; + return S_OK; + } + + if(This->prototype) { + hres = find_prop_name_prot(This->prototype, name, FALSE, &prop); + if(FAILED(hres)) + return hres; + if(prop) { + prop = alloc_protref(This, prop->name, prop - This->prototype->props); + if(!prop) + return E_OUTOFMEMORY; + *ret = prop; + return S_OK; + } + } + + if(alloc) { + TRACE("creating prop %s\n", debugstr_w(name)); + + prop = alloc_prop(This, name, PROP_VARIANT, PROPF_ENUM); + if(!prop) + return E_OUTOFMEMORY; + VariantInit(&prop->u.var); + } + + *ret = prop; + return S_OK; +} + +static HRESULT set_this(DISPPARAMS *dp, DISPPARAMS *olddp, IDispatch *jsthis) +{ + VARIANTARG *oldargs; + int i; + + static DISPID this_id = DISPID_THIS; + + *dp = *olddp; + + for(i = 0; i < dp->cNamedArgs; i++) { + if(dp->rgdispidNamedArgs[i] == DISPID_THIS) + return S_OK; + } + + oldargs = dp->rgvarg; + dp->rgvarg = heap_alloc((dp->cArgs+1) * sizeof(VARIANTARG)); + if(!dp->rgvarg) + return E_OUTOFMEMORY; + memcpy(dp->rgvarg+1, oldargs, dp->cArgs*sizeof(VARIANTARG)); + V_VT(dp->rgvarg) = VT_DISPATCH; + V_DISPATCH(dp->rgvarg) = jsthis; + dp->cArgs++; + + if(dp->cNamedArgs) { + DISPID *old = dp->rgdispidNamedArgs; + dp->rgdispidNamedArgs = heap_alloc((dp->cNamedArgs+1)*sizeof(DISPID)); + if(!dp->rgdispidNamedArgs) { + heap_free(dp->rgvarg); + return E_OUTOFMEMORY; + } + + memcpy(dp->rgdispidNamedArgs+1, old, dp->cNamedArgs*sizeof(DISPID)); + dp->rgdispidNamedArgs[0] = DISPID_THIS; + dp->cNamedArgs++; + }else { + dp->rgdispidNamedArgs = &this_id; + dp->cNamedArgs = 1; + } + + return S_OK; +} + +static HRESULT invoke_prop_func(DispatchEx *This, DispatchEx *jsthis, dispex_prop_t *prop, LCID lcid, WORD flags, + DISPPARAMS *dp, VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller) +{ + HRESULT hres; + + switch(prop->type) { + case PROP_BUILTIN: + if(flags == DISPATCH_CONSTRUCT && (prop->flags & DISPATCH_METHOD)) { + WARN("%s is not a constructor\n", debugstr_w(prop->name)); + return E_INVALIDARG; + } + return prop->u.p->invoke(jsthis, lcid, flags, dp, retv, ei, caller); + case PROP_PROTREF: + return invoke_prop_func(This->prototype, jsthis, This->prototype->props+prop->u.ref, lcid, flags, dp, retv, ei, caller); + case PROP_VARIANT: { + DISPPARAMS new_dp; + + if(V_VT(&prop->u.var) != VT_DISPATCH) { + FIXME("invoke vt %d\n", V_VT(&prop->u.var)); + return E_FAIL; + } + + TRACE("call %s %p\n", debugstr_w(prop->name), V_DISPATCH(&prop->u.var)); + + hres = set_this(&new_dp, dp, (IDispatch*)_IDispatchEx_(jsthis)); + if(FAILED(hres)) + return hres; + + hres = disp_call(V_DISPATCH(&prop->u.var), DISPID_VALUE, lcid, flags, &new_dp, retv, ei, caller); + + if(new_dp.rgvarg != dp->rgvarg) { + heap_free(new_dp.rgvarg); + if(new_dp.cNamedArgs > 1) + heap_free(new_dp.rgdispidNamedArgs); + } + + return hres; + } + default: + ERR("type %d\n", prop->type); + } + + return E_FAIL; +} + +static HRESULT prop_get(DispatchEx *This, dispex_prop_t *prop, LCID lcid, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller) +{ + HRESULT hres; + + switch(prop->type) { + case PROP_BUILTIN: + if(prop->u.p->flags & PROPF_METHOD) { + DispatchEx *obj; + hres = create_builtin_function(This->ctx, prop->u.p->invoke, prop->u.p->flags, NULL, &obj); + if(FAILED(hres)) + break; + + prop->type = PROP_VARIANT; + V_VT(&prop->u.var) = VT_DISPATCH; + V_DISPATCH(&prop->u.var) = (IDispatch*)_IDispatchEx_(obj); + + hres = VariantCopy(retv, &prop->u.var); + }else { + hres = prop->u.p->invoke(This, lcid, DISPATCH_PROPERTYGET, dp, retv, ei, caller); + } + break; + case PROP_PROTREF: + hres = prop_get(This->prototype, This->prototype->props+prop->u.ref, lcid, dp, retv, ei, caller); + break; + case PROP_VARIANT: + hres = VariantCopy(retv, &prop->u.var); + break; + default: + ERR("type %d\n", prop->type); + return E_FAIL; + } + + if(FAILED(hres)) { + TRACE("fail %08x\n", hres); + return hres; + } + + TRACE("%s ret %s\n", debugstr_w(prop->name), debugstr_variant(retv)); + return hres; +} + +static HRESULT prop_put(DispatchEx *This, dispex_prop_t *prop, LCID lcid, DISPPARAMS *dp, + jsexcept_t *ei, IServiceProvider *caller) +{ + DWORD i; + HRESULT hres; + + switch(prop->type) { + case PROP_BUILTIN: + if(!(prop->flags & PROPF_METHOD)) + return prop->u.p->invoke(This, lcid, DISPATCH_PROPERTYPUT, dp, NULL, ei, caller); + case PROP_PROTREF: + prop->type = PROP_VARIANT; + prop->flags = PROPF_ENUM; + V_VT(&prop->u.var) = VT_EMPTY; + break; + case PROP_VARIANT: + VariantClear(&prop->u.var); + break; + default: + ERR("type %d\n", prop->type); + return E_FAIL; + } + + for(i=0; i < dp->cNamedArgs; i++) { + if(dp->rgdispidNamedArgs[i] == DISPID_PROPERTYPUT) + break; + } + + if(i == dp->cNamedArgs) { + TRACE("no value to set\n"); + return DISP_E_PARAMNOTOPTIONAL; + } + + hres = VariantCopy(&prop->u.var, dp->rgvarg+i); + if(FAILED(hres)) + return hres; + + if(This->builtin_info->on_put) + This->builtin_info->on_put(This, prop->name); + + TRACE("%s = %s\n", debugstr_w(prop->name), debugstr_variant(dp->rgvarg+i)); + return S_OK; +} + +static HRESULT fill_protrefs(DispatchEx *This) +{ + dispex_prop_t *iter, *prop; + HRESULT hres; + + if(!This->prototype) + return S_OK; + + fill_protrefs(This->prototype); + + for(iter = This->prototype->props; iter < This->prototype->props+This->prototype->prop_cnt; iter++) { + if(!iter->name) + continue; + hres = find_prop_name(This, iter->name, &prop); + if(FAILED(hres)) + return hres; + if(!prop) { + prop = alloc_protref(This, iter->name, iter - This->prototype->props); + if(!prop) + return E_OUTOFMEMORY; + } + } + + return S_OK; +} + #define DISPATCHEX_THIS(iface) DEFINE_THIS(DispatchEx, IDispatchEx, iface) static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, void **ppv) @@ -38,6 +411,11 @@ static HRESULT WINAPI DispatchEx_QueryInterface(IDispatchEx *iface, REFIID riid, }else if(IsEqualGUID(&IID_IDispatchEx, riid)) { TRACE("(%p)->(IID_IDispatchEx %p)\n", This, ppv); *ppv = _IDispatchEx_(This); + }else if(IsEqualGUID(&IID_IDispatchJS, riid)) { + TRACE("(%p)->(IID_IDispatchJS %p)\n", This, ppv); + IUnknown_AddRef(_IDispatchEx_(This)); + *ppv = This; + return S_OK; }else { WARN("(%p)->(%s %p)\n", This, debugstr_guid(riid), ppv); *ppv = NULL; @@ -66,8 +444,20 @@ static ULONG WINAPI DispatchEx_Release(IDispatchEx *iface) TRACE("(%p) ref=%d\n", This, ref); if(!ref) { + dispex_prop_t *prop; + + for(prop = This->props; prop < This->props+This->prop_cnt; prop++) { + if(prop->type == PROP_VARIANT) + VariantClear(&prop->u.var); + heap_free(prop->name); + } + heap_free(This->props); script_release(This->ctx); - heap_free(This); + + if(This->builtin_info->destructor) + This->builtin_info->destructor(This); + else + heap_free(This); } return ref; @@ -127,23 +517,94 @@ static HRESULT WINAPI DispatchEx_Invoke(IDispatchEx *iface, DISPID dispIdMember, static HRESULT WINAPI DispatchEx_GetDispID(IDispatchEx *iface, BSTR bstrName, DWORD grfdex, DISPID *pid) { DispatchEx *This = DISPATCHEX_THIS(iface); - FIXME("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid); - return E_NOTIMPL; + dispex_prop_t *prop; + HRESULT hres; + + TRACE("(%p)->(%s %x %p)\n", This, debugstr_w(bstrName), grfdex, pid); + + if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit)) { + FIXME("Unsupported grfdex %x\n", grfdex); + return E_NOTIMPL; + } + + hres = find_prop_name_prot(This, bstrName, (grfdex&fdexNameEnsure) != 0, &prop); + if(FAILED(hres)) + return hres; + if(prop) { + *pid = prop_to_id(This, prop); + return S_OK; + } + + TRACE("not found %s\n", debugstr_w(bstrName)); + return DISP_E_UNKNOWNNAME; } static HRESULT WINAPI DispatchEx_InvokeEx(IDispatchEx *iface, DISPID id, LCID lcid, WORD wFlags, DISPPARAMS *pdp, VARIANT *pvarRes, EXCEPINFO *pei, IServiceProvider *pspCaller) { DispatchEx *This = DISPATCHEX_THIS(iface); - FIXME("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller); - return E_NOTIMPL; + dispex_prop_t *prop; + jsexcept_t jsexcept; + HRESULT hres; + + TRACE("(%p)->(%x %x %x %p %p %p %p)\n", This, id, lcid, wFlags, pdp, pvarRes, pei, pspCaller); + + if(pvarRes) + V_VT(pvarRes) = VT_EMPTY; + + prop = get_prop(This, id); + if(!prop || prop->type == PROP_DELETED) { + TRACE("invalid id\n"); + return DISP_E_MEMBERNOTFOUND; + } + + memset(&jsexcept, 0, sizeof(jsexcept)); + + switch(wFlags) { + case DISPATCH_METHOD: + case DISPATCH_CONSTRUCT: + hres = invoke_prop_func(This, This, prop, lcid, wFlags, pdp, pvarRes, &jsexcept, pspCaller); + break; + case DISPATCH_PROPERTYGET: + hres = prop_get(This, prop, lcid, pdp, pvarRes, &jsexcept, pspCaller); + break; + case DISPATCH_PROPERTYPUT: + hres = prop_put(This, prop, lcid, pdp, &jsexcept, pspCaller); + break; + default: + FIXME("Unimplemented flags %x\n", wFlags); + return E_INVALIDARG; + } + + if(pei) + *pei = jsexcept.ei; + + return hres; } static HRESULT WINAPI DispatchEx_DeleteMemberByName(IDispatchEx *iface, BSTR bstrName, DWORD grfdex) { DispatchEx *This = DISPATCHEX_THIS(iface); - FIXME("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex); - return E_NOTIMPL; + dispex_prop_t *prop; + HRESULT hres; + + TRACE("(%p)->(%s %x)\n", This, debugstr_w(bstrName), grfdex); + + if(grfdex & ~(fdexNameCaseSensitive|fdexNameEnsure|fdexNameImplicit)) + FIXME("Unsupported grfdex %x\n", grfdex); + + hres = find_prop_name(This, bstrName, &prop); + if(FAILED(hres)) + return hres; + if(!prop) { + TRACE("not found\n"); + return S_OK; + } + + heap_free(prop->name); + prop->name = NULL; + prop->type = PROP_DELETED; + return S_OK; } static HRESULT WINAPI DispatchEx_DeleteMemberByDispID(IDispatchEx *iface, DISPID id) @@ -163,15 +624,51 @@ static HRESULT WINAPI DispatchEx_GetMemberProperties(IDispatchEx *iface, DISPID static HRESULT WINAPI DispatchEx_GetMemberName(IDispatchEx *iface, DISPID id, BSTR *pbstrName) { DispatchEx *This = DISPATCHEX_THIS(iface); - FIXME("(%p)->(%x %p)\n", This, id, pbstrName); - return E_NOTIMPL; + dispex_prop_t *prop; + + TRACE("(%p)->(%x %p)\n", This, id, pbstrName); + + prop = get_prop(This, id); + if(!prop || !prop->name || prop->type == PROP_DELETED) + return DISP_E_MEMBERNOTFOUND; + + *pbstrName = SysAllocString(prop->name); + if(!*pbstrName) + return E_OUTOFMEMORY; + + return S_OK; } static HRESULT WINAPI DispatchEx_GetNextDispID(IDispatchEx *iface, DWORD grfdex, DISPID id, DISPID *pid) { DispatchEx *This = DISPATCHEX_THIS(iface); - FIXME("(%p)->(%x %x %p)\n", This, grfdex, id, pid); - return E_NOTIMPL; + dispex_prop_t *iter; + HRESULT hres; + + TRACE("(%p)->(%x %x %p)\n", This, grfdex, id, pid); + + if(id == DISPID_STARTENUM) { + hres = fill_protrefs(This); + if(FAILED(hres)) + return hres; + } + + iter = get_prop(This, id+1); + if(!iter) { + *pid = DISPID_STARTENUM; + return S_FALSE; + } + + while(iter < This->props + This->prop_cnt) { + if(iter->name && (get_flags(This, iter) & PROPF_ENUM)) { + *pid = prop_to_id(This, iter); + return S_OK; + } + iter++; + } + + *pid = DISPID_STARTENUM; + return S_FALSE; } static HRESULT WINAPI DispatchEx_GetNameSpaceParent(IDispatchEx *iface, IUnknown **ppunk) @@ -201,10 +698,48 @@ static IDispatchExVtbl DispatchExVtbl = { DispatchEx_GetNameSpaceParent }; -static HRESULT init_dispex(DispatchEx *dispex, script_ctx_t *ctx) +HRESULT jsdisp_set_prototype(DispatchEx *dispex, DispatchEx *prototype) { + VARIANT *var; + + if(!dispex->props[1].name) + return E_OUTOFMEMORY; + + dispex->props[1].type = PROP_VARIANT; + dispex->props[1].flags = 0; + + var = &dispex->props[1].u.var; + V_VT(var) = VT_DISPATCH; + V_DISPATCH(var) = (IDispatch*)_IDispatchEx_(prototype); + + return S_OK; +} + +HRESULT init_dispex(DispatchEx *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, DispatchEx *prototype) +{ + TRACE("%p (%p)\n", dispex, prototype); + dispex->lpIDispatchExVtbl = &DispatchExVtbl; dispex->ref = 1; + dispex->builtin_info = builtin_info; + + dispex->props = heap_alloc((dispex->buf_size=4) * sizeof(dispex_prop_t)); + if(!dispex->props) + return E_OUTOFMEMORY; + + dispex->prototype = prototype; + if(prototype) + IDispatchEx_AddRef(_IDispatchEx_(prototype)); + + dispex->prop_cnt = 1; + dispex->props[0].name = NULL; + dispex->props[0].flags = 0; + if(builtin_info->value_prop.invoke) { + dispex->props[0].type = PROP_BUILTIN; + dispex->props[0].u.p = &builtin_info->value_prop; + }else { + dispex->props[0].type = PROP_DELETED; + } script_addref(ctx); dispex->ctx = ctx; @@ -212,7 +747,15 @@ static HRESULT init_dispex(DispatchEx *dispex, script_ctx_t *ctx) return S_OK; } -HRESULT create_dispex(script_ctx_t *ctx, DispatchEx **dispex) +static const builtin_info_t dispex_info = { + JSCLASS_NONE, + {NULL, NULL, 0}, + 0, NULL, + NULL, + NULL +}; + +HRESULT create_dispex(script_ctx_t *ctx, const builtin_info_t *builtin_info, DispatchEx *prototype, DispatchEx **dispex) { DispatchEx *ret; HRESULT hres; @@ -221,10 +764,204 @@ HRESULT create_dispex(script_ctx_t *ctx, DispatchEx **dispex) if(!ret) return E_OUTOFMEMORY; - hres = init_dispex(ret, ctx); + hres = init_dispex(ret, ctx, builtin_info ? builtin_info : &dispex_info, prototype); if(FAILED(hres)) return hres; *dispex = ret; return S_OK; } + +HRESULT init_dispex_from_constr(DispatchEx *dispex, script_ctx_t *ctx, const builtin_info_t *builtin_info, DispatchEx *constr) +{ + DispatchEx *prot = NULL; + dispex_prop_t *prop; + HRESULT hres; + + static const WCHAR prototypeW[] = {'p','r','o','t','o','t','y','p','e',0}; + + hres = find_prop_name_prot(constr, prototypeW, FALSE, &prop); + if(SUCCEEDED(hres) && prop) { + jsexcept_t jsexcept; + VARIANT var; + + V_VT(&var) = VT_EMPTY; + memset(&jsexcept, 0, sizeof(jsexcept)); + hres = prop_get(constr, prop, ctx->lcid, NULL, &var, &jsexcept, NULL/*FIXME*/); + if(FAILED(hres)) { + ERR("Could not get prototype\n"); + return hres; + } + + if(V_VT(&var) == VT_DISPATCH) + prot = iface_to_jsdisp((IUnknown*)V_DISPATCH(&var)); + VariantClear(&var); + } + + hres = init_dispex(dispex, ctx, builtin_info, prot); + + if(prot) + IDispatchEx_Release(_IDispatchEx_(prot)); + return hres; +} + +DispatchEx *iface_to_jsdisp(IUnknown *iface) +{ + DispatchEx *ret; + HRESULT hres; + + hres = IUnknown_QueryInterface(iface, &IID_IDispatchJS, (void**)&ret); + if(FAILED(hres)) + return NULL; + + return ret; +} + +HRESULT jsdisp_call_value(DispatchEx *disp, LCID lcid, WORD flags, DISPPARAMS *dp, VARIANT *retv, + jsexcept_t *ei, IServiceProvider *caller) +{ + return disp->builtin_info->value_prop.invoke(disp, lcid, flags, dp, retv, ei, caller); +} + +HRESULT jsdisp_call(DispatchEx *disp, DISPID id, LCID lcid, WORD flags, DISPPARAMS *dp, VARIANT *retv, + jsexcept_t *ei, IServiceProvider *caller) +{ + dispex_prop_t *prop; + + memset(ei, 0, sizeof(*ei)); + if(retv) + V_VT(retv) = VT_EMPTY; + + prop = get_prop(disp, id); + if(!prop) + return DISP_E_MEMBERNOTFOUND; + + return invoke_prop_func(disp, disp, prop, lcid, flags, dp, retv, ei, caller); +} + +HRESULT disp_call(IDispatch *disp, DISPID id, LCID lcid, WORD flags, DISPPARAMS *dp, VARIANT *retv, + jsexcept_t *ei, IServiceProvider *caller) +{ + DispatchEx *jsdisp; + IDispatchEx *dispex; + HRESULT hres; + + jsdisp = iface_to_jsdisp((IUnknown*)disp); + if(jsdisp) { + hres = jsdisp_call(jsdisp, id, lcid, flags, dp, retv, ei, caller); + IDispatchEx_Release(_IDispatchEx_(jsdisp)); + return 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, lcid, flags, dp, retv, &ei->ei, &err); + } + + hres = IDispatchEx_InvokeEx(dispex, id, lcid, flags, dp, retv, &ei->ei, caller); + IDispatchEx_Release(dispex); + + return hres; +} + +HRESULT jsdisp_propput_name(DispatchEx *obj, const WCHAR *name, LCID lcid, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller) +{ + DISPID named_arg = DISPID_PROPERTYPUT; + DISPPARAMS dp = {val, &named_arg, 1, 1}; + dispex_prop_t *prop; + HRESULT hres; + + hres = find_prop_name_prot(obj, name, TRUE, &prop); + if(FAILED(hres)) + return hres; + + return prop_put(obj, prop, lcid, &dp, ei, caller); +} + +HRESULT jsdisp_propput_idx(DispatchEx *obj, DWORD idx, LCID lcid, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller) +{ + WCHAR buf[12]; + + static const WCHAR formatW[] = {'%','d',0}; + + sprintfW(buf, formatW, idx); + return jsdisp_propput_name(obj, buf, lcid, val, ei, caller); +} + +HRESULT disp_propput(IDispatch *disp, DISPID id, LCID lcid, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller) +{ + DISPID dispid = DISPID_PROPERTYPUT; + DISPPARAMS dp = {val, &dispid, 1, 1}; + IDispatchEx *dispex; + DispatchEx *jsdisp; + HRESULT hres; + + jsdisp = iface_to_jsdisp((IUnknown*)disp); + if(jsdisp) { + dispex_prop_t *prop; + + prop = get_prop(jsdisp, id); + if(prop) + hres = prop_put(jsdisp, prop, lcid, &dp, ei, caller); + else + hres = DISP_E_MEMBERNOTFOUND; + + IDispatchEx_Release(_IDispatchEx_(jsdisp)); + return hres; + } + + hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); + if(FAILED(hres)) { + ULONG err = 0; + + TRACE("using IDispatch\n"); + return IDispatch_Invoke(disp, id, &IID_NULL, DISPATCH_PROPERTYPUT, lcid, &dp, NULL, &ei->ei, &err); + } + + hres = IDispatchEx_InvokeEx(dispex, id, lcid, DISPATCH_PROPERTYPUT, &dp, NULL, &ei->ei, caller); + + IDispatchEx_Release(dispex); + return hres; +} + +HRESULT disp_propget(IDispatch *disp, DISPID id, LCID lcid, VARIANT *val, jsexcept_t *ei, IServiceProvider *caller) +{ + DISPPARAMS dp = {NULL,NULL,0,0}; + IDispatchEx *dispex; + DispatchEx *jsdisp; + HRESULT hres; + + jsdisp = iface_to_jsdisp((IUnknown*)disp); + if(jsdisp) { + dispex_prop_t *prop; + + prop = get_prop(jsdisp, id); + if(prop) + hres = prop_get(jsdisp, prop, lcid, &dp, val, ei, caller); + else + hres = DISP_E_MEMBERNOTFOUND; + + IDispatchEx_Release(_IDispatchEx_(jsdisp)); + return hres; + } + + hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); + if(FAILED(hres)) { + ULONG err = 0; + + TRACE("uding IDispatch\n"); + return IDispatchEx_Invoke(dispex, id, &IID_NULL, lcid, INVOKE_PROPERTYGET, &dp, val, &ei->ei, &err); + } + + hres = IDispatchEx_InvokeEx(dispex, id, lcid, INVOKE_PROPERTYGET, &dp, val, &ei->ei, caller); + IDispatchEx_Release(dispex); + + return hres; +} diff --git a/reactos/dll/win32/jscript/engine.c b/reactos/dll/win32/jscript/engine.c index ef66c9767a1..f0f5062e4e1 100644 --- a/reactos/dll/win32/jscript/engine.c +++ b/reactos/dll/win32/jscript/engine.c @@ -23,12 +23,150 @@ WINE_DEFAULT_DEBUG_CHANNEL(jscript); +#define EXPR_NOVAL 0x0001 +#define EXPR_NEWREF 0x0002 +#define EXPR_STRREF 0x0004 + static inline HRESULT stat_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) { return stat->eval(ctx, stat, rt, ret); } -HRESULT create_exec_ctx(exec_ctx_t **ret) +static inline HRESULT expr_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + return _expr->eval(ctx, _expr, flags, ei, ret); +} + +static void exprval_release(exprval_t *val) +{ + switch(val->type) { + case EXPRVAL_VARIANT: + if(V_VT(&val->u.var) != VT_EMPTY) + VariantClear(&val->u.var); + return; + case EXPRVAL_IDREF: + if(val->u.idref.disp) + IDispatch_Release(val->u.idref.disp); + return; + case EXPRVAL_NAMEREF: + if(val->u.nameref.disp) + IDispatch_Release(val->u.nameref.disp); + SysFreeString(val->u.nameref.name); + } +} + +/* ECMA-262 3rd Edition 8.7.1 */ +static HRESULT exprval_value(script_ctx_t *ctx, exprval_t *val, jsexcept_t *ei, VARIANT *ret) +{ + V_VT(ret) = VT_EMPTY; + + switch(val->type) { + case EXPRVAL_VARIANT: + return VariantCopy(ret, &val->u.var); + case EXPRVAL_IDREF: + if(!val->u.idref.disp) { + FIXME("throw ReferenceError\n"); + return E_FAIL; + } + + return disp_propget(val->u.idref.disp, val->u.idref.id, ctx->lcid, ret, ei, NULL/*FIXME*/); + default: + ERR("type %d\n", val->type); + return E_FAIL; + } +} + +static HRESULT exprval_to_value(script_ctx_t *ctx, exprval_t *val, jsexcept_t *ei, VARIANT *ret) +{ + if(val->type == EXPRVAL_VARIANT) { + *ret = val->u.var; + V_VT(&val->u.var) = VT_EMPTY; + return S_OK; + } + + return exprval_value(ctx, val, ei, ret); +} + +static HRESULT exprval_to_boolean(script_ctx_t *ctx, exprval_t *exprval, jsexcept_t *ei, VARIANT_BOOL *b) +{ + if(exprval->type != EXPRVAL_VARIANT) { + VARIANT val; + HRESULT hres; + + hres = exprval_to_value(ctx, exprval, ei, &val); + if(FAILED(hres)) + return hres; + + hres = to_boolean(&val, b); + VariantClear(&val); + return hres; + } + + return to_boolean(&exprval->u.var, b); +} + +static void exprval_init(exprval_t *val) +{ + val->type = EXPRVAL_VARIANT; + V_VT(&val->u.var) = VT_EMPTY; +} + +static void exprval_set_idref(exprval_t *val, IDispatch *disp, DISPID id) +{ + val->type = EXPRVAL_IDREF; + val->u.idref.disp = disp; + val->u.idref.id = id; + + if(disp) + IDispatch_AddRef(disp); +} + +HRESULT scope_push(scope_chain_t *scope, DispatchEx *obj, scope_chain_t **ret) +{ + scope_chain_t *new_scope; + + new_scope = heap_alloc(sizeof(scope_chain_t)); + if(!new_scope) + return E_OUTOFMEMORY; + + new_scope->ref = 1; + + IDispatchEx_AddRef(_IDispatchEx_(obj)); + new_scope->obj = obj; + + if(scope) { + scope_addref(scope); + new_scope->next = scope; + }else { + new_scope->next = NULL; + } + + *ret = new_scope; + return S_OK; +} + +static void scope_pop(scope_chain_t **scope) +{ + scope_chain_t *tmp; + + tmp = *scope; + *scope = tmp->next; + scope_release(tmp); +} + +void scope_release(scope_chain_t *scope) +{ + if(--scope->ref) + return; + + if(scope->next) + scope_release(scope->next); + + IDispatchEx_Release(_IDispatchEx_(scope->obj)); + heap_free(scope); +} + +HRESULT create_exec_ctx(IDispatch *this_obj, DispatchEx *var_disp, scope_chain_t *scope, exec_ctx_t **ret) { exec_ctx_t *ctx; @@ -36,6 +174,17 @@ HRESULT create_exec_ctx(exec_ctx_t **ret) if(!ctx) return E_OUTOFMEMORY; + IDispatch_AddRef(this_obj); + ctx->this_obj = this_obj; + + IDispatchEx_AddRef(_IDispatchEx_(var_disp)); + ctx->var_disp = var_disp; + + if(scope) { + scope_addref(scope); + ctx->scope_chain = scope; + } + *ret = ctx; return S_OK; } @@ -45,12 +194,178 @@ void exec_release(exec_ctx_t *ctx) if(--ctx->ref) return; + if(ctx->scope_chain) + scope_release(ctx->scope_chain); + if(ctx->var_disp) + IDispatchEx_Release(_IDispatchEx_(ctx->var_disp)); + if(ctx->this_obj) + IDispatch_Release(ctx->this_obj); heap_free(ctx); } +static HRESULT dispex_get_id(IDispatchEx *dispex, BSTR name, DWORD flags, DISPID *id) +{ + *id = 0; + + return IDispatchEx_GetDispID(dispex, name, flags|fdexNameCaseSensitive, id); +} + +static HRESULT disp_get_id(IDispatch *disp, BSTR name, DWORD flags, DISPID *id) +{ + IDispatchEx *dispex; + HRESULT hres; + + hres = IDispatch_QueryInterface(disp, &IID_IDispatchEx, (void**)&dispex); + if(FAILED(hres)) { + TRACE("unsing IDispatch\n"); + + *id = 0; + return IDispatch_GetIDsOfNames(disp, &IID_NULL, &name, 1, 0, id); + } + + hres = dispex_get_id(dispex, name, flags, id); + IDispatchEx_Release(dispex); + return hres; +} + +/* ECMA-262 3rd Edition 8.7.2 */ +static HRESULT put_value(script_ctx_t *ctx, exprval_t *ref, VARIANT *v, jsexcept_t *ei) +{ + if(ref->type != EXPRVAL_IDREF) { + FIXME("throw ReferemceError\n"); + return E_FAIL; + } + + return disp_propput(ref->u.idref.disp, ref->u.idref.id, ctx->lcid, v, ei, NULL/*FIXME*/); +} + +static HRESULT disp_cmp(IDispatch *disp1, IDispatch *disp2, BOOL *ret) +{ + IObjectIdentity *identity; + IUnknown *unk1, *unk2; + HRESULT hres; + + if(disp1 == disp2) { + *ret = TRUE; + 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 = 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; + }else { + *ret = FALSE; + } + } + + IUnknown_Release(unk1); + IUnknown_Release(unk2); + return S_OK; +} + +static inline BOOL is_num_vt(enum VARENUM vt) +{ + return vt == VT_I4 || vt == VT_R8; +} + +static inline DOUBLE num_val(const VARIANT *v) +{ + return V_VT(v) == VT_I4 ? V_I4(v) : V_R8(v); +} + +/* ECMA-262 3rd Edition 11.9.6 */ +HRESULT equal2_values(VARIANT *lval, VARIANT *rval, BOOL *ret) +{ + TRACE("\n"); + + if(V_VT(lval) != V_VT(rval)) { + if(is_num_vt(V_VT(lval)) && is_num_vt(V_VT(rval))) { + *ret = num_val(lval) == num_val(rval); + return S_OK; + } + + *ret = FALSE; + return S_OK; + } + + switch(V_VT(lval)) { + case VT_EMPTY: + case VT_NULL: + *ret = VARIANT_TRUE; + break; + case VT_I4: + *ret = V_I4(lval) == V_I4(rval); + break; + case VT_R8: + *ret = V_R8(lval) == V_R8(rval); + break; + case VT_BSTR: + *ret = !strcmpW(V_BSTR(lval), V_BSTR(rval)); + break; + case VT_DISPATCH: + return disp_cmp(V_DISPATCH(lval), V_DISPATCH(rval), ret); + case VT_BOOL: + *ret = !V_BOOL(lval) == !V_BOOL(rval); + break; + default: + FIXME("unimplemented vt %d\n", V_VT(lval)); + return E_NOTIMPL; + } + + return S_OK; +} + +static HRESULT literal_to_var(literal_t *literal, VARIANT *v) +{ + V_VT(v) = literal->vt; + + switch(V_VT(v)) { + case VT_EMPTY: + case VT_NULL: + break; + case VT_I4: + V_I4(v) = literal->u.lval; + break; + case VT_R8: + V_R8(v) = literal->u.dval; + break; + case VT_BSTR: + V_BSTR(v) = SysAllocString(literal->u.wstr); + break; + case VT_BOOL: + V_BOOL(v) = literal->u.bval; + break; + case VT_DISPATCH: + IDispatch_AddRef(literal->u.disp); + V_DISPATCH(v) = literal->u.disp; + break; + default: + ERR("wrong type %d\n", V_VT(v)); + return E_NOTIMPL; + } + + return S_OK; +} + HRESULT exec_source(exec_ctx_t *ctx, parser_ctx_t *parser, source_elements_t *source, jsexcept_t *ei, VARIANT *retv) { script_ctx_t *script = parser->script; + function_declaration_t *func; parser_ctx_t *prev_parser; VARIANT val, tmp; statement_t *stat; @@ -58,6 +373,22 @@ HRESULT exec_source(exec_ctx_t *ctx, parser_ctx_t *parser, source_elements_t *so return_type_t rt; HRESULT hres = S_OK; + for(func = source->functions; func; func = func->next) { + DispatchEx *func_obj; + VARIANT var; + + hres = create_source_function(parser, func->parameter_list, func->source_elements, ctx->scope_chain, &func_obj); + if(FAILED(hres)) + return hres; + + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = (IDispatch*)_IDispatchEx_(func_obj); + hres = jsdisp_propput_name(ctx->var_disp, func->identifier, script->lcid, &var, ei, NULL); + IDispatchEx_Release(_IDispatchEx_(func_obj)); + if(FAILED(hres)) + return hres; + } + prev_ctx = script->exec_ctx; script->exec_ctx = ctx; @@ -100,18 +431,144 @@ HRESULT exec_source(exec_ctx_t *ctx, parser_ctx_t *parser, source_elements_t *so return S_OK; } -HRESULT block_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 10.1.4 */ +static HRESULT identifier_eval(exec_ctx_t *ctx, BSTR identifier, DWORD flags, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + scope_chain_t *scope; + named_item_t *item; + DISPID id = 0; + HRESULT hres; + + TRACE("%s\n", debugstr_w(identifier)); + + for(scope = ctx->scope_chain; scope; scope = scope->next) { + hres = dispex_get_id(_IDispatchEx_(scope->obj), identifier, 0, &id); + if(SUCCEEDED(hres)) + break; + } + + if(scope) { + exprval_set_idref(ret, (IDispatch*)_IDispatchEx_(scope->obj), id); + return S_OK; + } + + hres = dispex_get_id(_IDispatchEx_(ctx->parser->script->global), identifier, 0, &id); + if(SUCCEEDED(hres)) { + exprval_set_idref(ret, (IDispatch*)_IDispatchEx_(ctx->parser->script->global), id); + return S_OK; + } + + for(item = ctx->parser->script->named_items; item; item = item->next) { + hres = disp_get_id(item->disp, identifier, 0, &id); + if(SUCCEEDED(hres)) + break; + } + + if(item) { + exprval_set_idref(ret, (IDispatch*)item->disp, id); + return S_OK; + } + + hres = dispex_get_id(_IDispatchEx_(ctx->parser->script->script_disp), identifier, 0, &id); + if(SUCCEEDED(hres)) { + exprval_set_idref(ret, (IDispatch*)_IDispatchEx_(ctx->parser->script->script_disp), id); + return S_OK; + } + + if(flags & EXPR_NEWREF) { + hres = dispex_get_id(_IDispatchEx_(ctx->var_disp), identifier, fdexNameEnsure, &id); + if(FAILED(hres)) + return hres; + + exprval_set_idref(ret, (IDispatch*)_IDispatchEx_(ctx->var_disp), id); + return S_OK; + } + + WARN("Could not find identifier %s\n", debugstr_w(identifier)); + return E_FAIL; } -HRESULT var_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.1 */ +HRESULT block_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + block_statement_t *stat = (block_statement_t*)_stat; + VARIANT val, tmp; + statement_t *iter; + HRESULT hres = S_OK; + + TRACE("\n"); + + V_VT(&val) = VT_EMPTY; + for(iter = stat->stat_list; iter; iter = iter->next) { + hres = stat_eval(ctx, iter, rt, &tmp); + if(FAILED(hres)) + break; + + VariantClear(&val); + val = tmp; + if(rt->type != RT_NORMAL) + break; + } + + if(FAILED(hres)) { + VariantClear(&val); + return hres; + } + + *ret = val; + return S_OK; } +/* ECMA-262 3rd Edition 12.2 */ +static HRESULT variable_list_eval(exec_ctx_t *ctx, variable_declaration_t *var_list, jsexcept_t *ei) +{ + variable_declaration_t *iter; + HRESULT hres = E_FAIL; + + for(iter = var_list; iter; iter = iter->next) { + VARIANT val; + + if(iter->expr) { + exprval_t exprval; + + hres = expr_eval(ctx, iter->expr, 0, ei, &exprval); + if(FAILED(hres)) + break; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + break; + }else { + V_VT(&val) = VT_EMPTY; + } + + hres = jsdisp_propput_name(ctx->var_disp, iter->identifier, ctx->parser->script->lcid, &val, ei, NULL/*FIXME*/); + VariantClear(&val); + if(FAILED(hres)) + break; + } + + return hres; +} + +/* ECMA-262 3rd Edition 12.2 */ +HRESULT var_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) +{ + var_statement_t *stat = (var_statement_t*)_stat; + HRESULT hres; + + TRACE("\n"); + + hres = variable_list_eval(ctx, stat->variable_list, &rt->ei); + if(FAILED(hres)) + return hres; + + V_VT(ret) = VT_EMPTY; + return S_OK; +} + +/* ECMA-262 3rd Edition 12.3 */ HRESULT empty_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) { TRACE("\n"); @@ -120,441 +577,2407 @@ HRESULT empty_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t * return S_OK; } -HRESULT expression_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.4 */ +HRESULT expression_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + expression_statement_t *stat = (expression_statement_t*)_stat; + exprval_t exprval; + VARIANT val; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, stat->expr, EXPR_NOVAL, &rt->ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, &rt->ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + *ret = val; + TRACE("= %s\n", debugstr_variant(ret)); + return S_OK; } -HRESULT if_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.5 */ +HRESULT if_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + if_statement_t *stat = (if_statement_t*)_stat; + exprval_t exprval; + VARIANT_BOOL b; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, stat->expr, 0, &rt->ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_boolean(ctx->parser->script, &exprval, &rt->ei, &b); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + if(b) + hres = stat_eval(ctx, stat->if_stat, rt, ret); + else if(stat->else_stat) + hres = stat_eval(ctx, stat->else_stat, rt, ret); + else + V_VT(ret) = VT_EMPTY; + + return hres; } -HRESULT dowhile_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.6.2 */ +HRESULT while_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + while_statement_t *stat = (while_statement_t*)_stat; + exprval_t exprval; + VARIANT val, tmp; + VARIANT_BOOL b; + BOOL test_expr; + HRESULT hres; + + TRACE("\n"); + + V_VT(&val) = VT_EMPTY; + test_expr = !stat->do_while; + + while(1) { + if(test_expr) { + hres = expr_eval(ctx, stat->expr, 0, &rt->ei, &exprval); + if(FAILED(hres)) + break; + + hres = exprval_to_boolean(ctx->parser->script, &exprval, &rt->ei, &b); + exprval_release(&exprval); + if(FAILED(hres) || !b) + break; + }else { + test_expr = TRUE; + } + + hres = stat_eval(ctx, stat->statement, rt, &tmp); + if(FAILED(hres)) + break; + + VariantClear(&val); + val = tmp; + + if(rt->type == RT_CONTINUE) + rt->type = RT_NORMAL; + if(rt->type != RT_NORMAL) + break; + } + + if(FAILED(hres)) { + VariantClear(&val); + return hres; + } + + if(rt->type == RT_BREAK) + rt->type = RT_NORMAL; + + *ret = val; + return S_OK; } -HRESULT while_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.6.3 */ +HRESULT for_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + for_statement_t *stat = (for_statement_t*)_stat; + VARIANT val, tmp, retv; + exprval_t exprval; + VARIANT_BOOL b; + HRESULT hres; + + TRACE("\n"); + + if(stat->variable_list) { + hres = variable_list_eval(ctx, stat->variable_list, &rt->ei); + if(FAILED(hres)) + return hres; + }else if(stat->begin_expr) { + hres = expr_eval(ctx, stat->begin_expr, EXPR_NEWREF, &rt->ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, &rt->ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + VariantClear(&val); + } + + V_VT(&retv) = VT_EMPTY; + + while(1) { + if(stat->expr) { + hres = expr_eval(ctx, stat->expr, 0, &rt->ei, &exprval); + if(FAILED(hres)) + break; + + hres = exprval_to_boolean(ctx->parser->script, &exprval, &rt->ei, &b); + exprval_release(&exprval); + if(FAILED(hres) || !b) + break; + } + + hres = stat_eval(ctx, stat->statement, rt, &tmp); + if(FAILED(hres)) + break; + + VariantClear(&retv); + retv = tmp; + + if(rt->type == RT_CONTINUE) + rt->type = RT_NORMAL; + else if(rt->type != RT_NORMAL) + break; + + if(stat->end_expr) { + hres = expr_eval(ctx, stat->end_expr, 0, &rt->ei, &exprval); + if(FAILED(hres)) + break; + + hres = exprval_to_value(ctx->parser->script, &exprval, &rt->ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + break; + + VariantClear(&val); + } + } + + if(FAILED(hres)) { + VariantClear(&retv); + return hres; + } + + if(rt->type == RT_BREAK) + rt->type = RT_NORMAL; + + *ret = retv; + return S_OK; } -HRESULT for_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.6.4 */ +HRESULT forin_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + forin_statement_t *stat = (forin_statement_t*)_stat; + VARIANT val, name, retv, tmp; + DISPID id = DISPID_STARTENUM; + BSTR str, identifier = NULL; + IDispatchEx *in_obj; + exprval_t exprval; + HRESULT hres; + + TRACE("\n"); + + if(stat->variable) { + hres = variable_list_eval(ctx, stat->variable, &rt->ei); + if(FAILED(hres)) + return hres; + } + + hres = expr_eval(ctx, stat->in_expr, EXPR_NEWREF, &rt->ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, &rt->ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + if(V_VT(&val) != VT_DISPATCH) { + FIXME("in vt %d\n", V_VT(&val)); + VariantClear(&val); + return E_NOTIMPL; + } + + hres = IDispatch_QueryInterface(V_DISPATCH(&val), &IID_IDispatchEx, (void**)&in_obj); + IDispatch_Release(V_DISPATCH(&val)); + if(FAILED(hres)) { + FIXME("Object doesn't support IDispatchEx\n"); + return E_NOTIMPL; + } + + V_VT(&retv) = VT_EMPTY; + + if(stat->variable) + identifier = SysAllocString(stat->variable->identifier); + + while(1) { + hres = IDispatchEx_GetNextDispID(in_obj, fdexEnumDefault, id, &id); + if(FAILED(hres) || hres == S_FALSE) + break; + + hres = IDispatchEx_GetMemberName(in_obj, id, &str); + if(FAILED(hres)) + break; + + TRACE("iter %s\n", debugstr_w(str)); + + if(stat->variable) + hres = identifier_eval(ctx, identifier, 0, &exprval); + else + hres = expr_eval(ctx, stat->expr, EXPR_NEWREF, &rt->ei, &exprval); + if(SUCCEEDED(hres)) { + V_VT(&name) = VT_BSTR; + V_BSTR(&name) = str; + hres = put_value(ctx->parser->script, &exprval, &name, &rt->ei); + exprval_release(&exprval); + } + SysFreeString(str); + if(FAILED(hres)) + break; + + hres = stat_eval(ctx, stat->statement, rt, &tmp); + if(FAILED(hres)) + break; + + VariantClear(&retv); + retv = tmp; + + if(rt->type == RT_CONTINUE) + rt->type = RT_NORMAL; + else if(rt->type != RT_NORMAL) + break; + } + + SysFreeString(identifier); + IDispatchEx_Release(in_obj); + if(FAILED(hres)) { + VariantClear(&retv); + return hres; + } + + if(rt->type == RT_BREAK) + rt->type = RT_NORMAL; + + *ret = retv; + return S_OK; } -HRESULT forin_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.7 */ +HRESULT continue_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + branch_statement_t *stat = (branch_statement_t*)_stat; + + TRACE("\n"); + + if(stat->identifier) { + FIXME("indentifier not implemented\n"); + return E_NOTIMPL; + } + + rt->type = RT_CONTINUE; + V_VT(ret) = VT_EMPTY; + return S_OK; } -HRESULT continue_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.8 */ +HRESULT break_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + branch_statement_t *stat = (branch_statement_t*)_stat; + + TRACE("\n"); + + if(stat->identifier) { + FIXME("indentifier not implemented\n"); + return E_NOTIMPL; + } + + rt->type = RT_BREAK; + V_VT(ret) = VT_EMPTY; + return S_OK; } -HRESULT break_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.9 */ +HRESULT return_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + expression_statement_t *stat = (expression_statement_t*)_stat; + HRESULT hres; + + TRACE("\n"); + + if(stat->expr) { + exprval_t exprval; + + hres = expr_eval(ctx, stat->expr, 0, &rt->ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, &rt->ei, ret); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + }else { + V_VT(ret) = VT_EMPTY; + } + + TRACE("= %s\n", debugstr_variant(ret)); + rt->type = RT_RETURN; + return S_OK; } -HRESULT return_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.10 */ +HRESULT with_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; -} - -HRESULT with_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) -{ - FIXME("\n"); - return E_NOTIMPL; + with_statement_t *stat = (with_statement_t*)_stat; + exprval_t exprval; + IDispatch *disp; + DispatchEx *obj; + VARIANT val; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, stat->expr, 0, &rt->ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, &rt->ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + hres = to_object(ctx, &val, &disp); + VariantClear(&val); + if(FAILED(hres)) + return hres; + + obj = iface_to_jsdisp((IUnknown*)disp); + IDispatch_Release(disp); + if(!obj) { + FIXME("disp id not jsdisp\n"); + return E_NOTIMPL; + } + + hres = scope_push(ctx->scope_chain, obj, &ctx->scope_chain); + jsdisp_release(obj); + if(FAILED(hres)); + + hres = stat_eval(ctx, stat->statement, rt, ret); + + scope_pop(&ctx->scope_chain); + return hres; } +/* ECMA-262 3rd Edition 12.12 */ HRESULT labelled_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) { FIXME("\n"); return E_NOTIMPL; } -HRESULT switch_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.13 */ +HRESULT switch_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + switch_statement_t *stat = (switch_statement_t*)_stat; + case_clausule_t *iter, *default_clausule = NULL; + statement_t *stat_iter; + VARIANT val, cval; + exprval_t exprval; + BOOL b; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, stat->expr, 0, &rt->ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, &rt->ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + for(iter = stat->case_list; iter; iter = iter->next) { + if(!iter->expr) { + default_clausule = iter; + continue; + } + + hres = expr_eval(ctx, iter->expr, 0, &rt->ei, &exprval); + if(FAILED(hres)) + break; + + hres = exprval_to_value(ctx->parser->script, &exprval, &rt->ei, &cval); + exprval_release(&exprval); + if(FAILED(hres)) + break; + + hres = equal2_values(&val, &cval, &b); + VariantClear(&cval); + if(FAILED(hres) || b) + break; + } + + VariantClear(&val); + if(FAILED(hres)) + return hres; + + if(!iter) + iter = default_clausule; + + V_VT(&val) = VT_EMPTY; + if(iter) { + VARIANT tmp; + + for(stat_iter = iter->stat; stat_iter; stat_iter = stat_iter->next) { + hres = stat_eval(ctx, stat_iter, rt, &tmp); + if(FAILED(hres)) + break; + + VariantClear(&val); + val = tmp; + + if(rt->type != RT_NORMAL) + break; + } + } + + if(FAILED(hres)) { + VariantClear(&val); + return hres; + } + + if(rt->type == RT_BREAK) + rt->type = RT_NORMAL; + + *ret = val; + return S_OK; } -HRESULT throw_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.13 */ +HRESULT throw_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + expression_statement_t *stat = (expression_statement_t*)_stat; + exprval_t exprval; + VARIANT val; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, stat->expr, 0, &rt->ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, &rt->ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + rt->ei.var = val; + return DISP_E_EXCEPTION; } -HRESULT try_statement_eval(exec_ctx_t *ctx, statement_t *stat, return_type_t *rt, VARIANT *ret) +/* ECMA-262 3rd Edition 12.14 */ +static HRESULT catch_eval(exec_ctx_t *ctx, catch_block_t *block, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + DispatchEx *var_disp; + VARIANT ex, val; + HRESULT hres; + + ex = rt->ei.var; + memset(&rt->ei, 0, sizeof(jsexcept_t)); + + hres = create_dispex(ctx->parser->script, NULL, NULL, &var_disp); + if(SUCCEEDED(hres)) { + hres = jsdisp_propput_name(var_disp, block->identifier, ctx->parser->script->lcid, + &ex, &rt->ei, NULL/*FIXME*/); + if(SUCCEEDED(hres)) { + hres = scope_push(ctx->scope_chain, var_disp, &ctx->scope_chain); + if(SUCCEEDED(hres)) { + hres = stat_eval(ctx, block->statement, rt, &val); + scope_pop(&ctx->scope_chain); + } + } + + jsdisp_release(var_disp); + } + + VariantClear(&ex); + if(FAILED(hres)) + return hres; + + *ret = val; + return S_OK; } -HRESULT function_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 12.14 */ +HRESULT try_statement_eval(exec_ctx_t *ctx, statement_t *_stat, return_type_t *rt, VARIANT *ret) { - FIXME("\n"); - return E_NOTIMPL; + try_statement_t *stat = (try_statement_t*)_stat; + VARIANT val; + HRESULT hres; + + TRACE("\n"); + + hres = stat_eval(ctx, stat->try_statement, rt, &val); + if(FAILED(hres)) { + TRACE("EXCEPTION\n"); + if(!stat->catch_block) + return hres; + + hres = catch_eval(ctx, stat->catch_block, rt, &val); + if(FAILED(hres)) + return hres; + } + + if(stat->finally_statement) { + VariantClear(&val); + hres = stat_eval(ctx, stat->finally_statement, rt, &val); + if(FAILED(hres)) + return hres; + } + + *ret = val; + return S_OK; } -HRESULT conditional_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +static HRESULT return_bool(exprval_t *ret, DWORD b) { - FIXME("\n"); - return E_NOTIMPL; + ret->type = EXPRVAL_VARIANT; + V_VT(&ret->u.var) = VT_BOOL; + V_BOOL(&ret->u.var) = b ? VARIANT_TRUE : VARIANT_FALSE; + + return S_OK; } -HRESULT array_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +static HRESULT get_binary_expr_values(exec_ctx_t *ctx, binary_expression_t *expr, jsexcept_t *ei, VARIANT *lval, VARIANT *rval) { - FIXME("\n"); - return E_NOTIMPL; + exprval_t exprval; + HRESULT hres; + + hres = expr_eval(ctx, expr->expression1, 0, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, lval); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + hres = expr_eval(ctx, expr->expression2, 0, ei, &exprval); + if(SUCCEEDED(hres)) { + hres = exprval_to_value(ctx->parser->script, &exprval, ei, rval); + exprval_release(&exprval); + } + + if(FAILED(hres)) { + VariantClear(lval); + return hres; + } + + return S_OK; } -HRESULT member_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +typedef HRESULT (*oper_t)(exec_ctx_t*,VARIANT*,VARIANT*,jsexcept_t*,VARIANT*); + +static HRESULT binary_expr_eval(exec_ctx_t *ctx, binary_expression_t *expr, oper_t oper, jsexcept_t *ei, + exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + VARIANT lval, rval, retv; + HRESULT hres; + + hres = get_binary_expr_values(ctx, expr, ei, &lval, &rval); + if(FAILED(hres)) + return hres; + + hres = oper(ctx, &lval, &rval, ei, &retv); + VariantClear(&lval); + VariantClear(&rval); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + ret->u.var = retv; + return S_OK; } -HRESULT member_new_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.13.2 */ +static HRESULT assign_oper_eval(exec_ctx_t *ctx, expression_t *lexpr, expression_t *rexpr, oper_t oper, + jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + VARIANT retv, lval, rval; + exprval_t exprval, exprvalr; + HRESULT hres; + + hres = expr_eval(ctx, lexpr, EXPR_NEWREF, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_value(ctx->parser->script, &exprval, ei, &lval); + if(SUCCEEDED(hres)) { + hres = expr_eval(ctx, rexpr, 0, ei, &exprvalr); + if(SUCCEEDED(hres)) { + hres = exprval_value(ctx->parser->script, &exprvalr, ei, &rval); + exprval_release(&exprvalr); + } + if(SUCCEEDED(hres)) { + hres = oper(ctx, &lval, &rval, ei, &retv); + VariantClear(&rval); + } + VariantClear(&lval); + } + + if(SUCCEEDED(hres)) { + hres = put_value(ctx->parser->script, &exprval, &retv, ei); + if(FAILED(hres)) + VariantClear(&retv); + } + exprval_release(&exprval); + + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + ret->u.var = retv; + return S_OK; } -HRESULT call_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 13 */ +HRESULT function_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + function_expression_t *expr = (function_expression_t*)_expr; + DispatchEx *dispex; + VARIANT var; + HRESULT hres; + + TRACE("\n"); + + hres = create_source_function(ctx->parser, expr->parameter_list, expr->source_elements, ctx->scope_chain, &dispex); + if(FAILED(hres)) + return hres; + + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = (IDispatch*)_IDispatchEx_(dispex); + + if(expr->identifier) { + hres = jsdisp_propput_name(ctx->var_disp, expr->identifier, ctx->parser->script->lcid, &var, ei, NULL/*FIXME*/); + if(FAILED(hres)) { + jsdisp_release(dispex); + return hres; + } + } + + ret->type = EXPRVAL_VARIANT; + ret->u.var = var; + return S_OK; } +/* ECMA-262 3rd Edition 11.12 */ +HRESULT conditional_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + conditional_expression_t *expr = (conditional_expression_t*)_expr; + exprval_t exprval; + VARIANT_BOOL b; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, 0, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_boolean(ctx->parser->script, &exprval, ei, &b); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + return expr_eval(ctx, b ? expr->true_expression : expr->false_expression, flags, ei, ret); +} + +/* ECMA-262 3rd Edition 11.2.1 */ +HRESULT array_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + array_expression_t *expr = (array_expression_t*)_expr; + exprval_t exprval; + VARIANT member, val; + DISPID id; + BSTR str; + IDispatch *obj = NULL; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->member_expr, EXPR_NEWREF, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &member); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + hres = expr_eval(ctx, expr->expression, EXPR_NEWREF, ei, &exprval); + if(SUCCEEDED(hres)) { + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &val); + exprval_release(&exprval); + } + + if(SUCCEEDED(hres)) + hres = to_object(ctx, &member, &obj); + VariantClear(&member); + if(SUCCEEDED(hres)) { + hres = to_string(ctx->parser->script, &val, ei, &str); + if(SUCCEEDED(hres)) { + if(flags & EXPR_STRREF) { + ret->type = EXPRVAL_NAMEREF; + ret->u.nameref.disp = obj; + ret->u.nameref.name = str; + return S_OK; + } + + hres = disp_get_id(obj, str, flags & EXPR_NEWREF ? fdexNameEnsure : 0, &id); + } + + if(SUCCEEDED(hres)) { + exprval_set_idref(ret, obj, id); + }else if(!(flags & EXPR_NEWREF) && hres == DISP_E_UNKNOWNNAME) { + exprval_init(ret); + hres = S_OK; + } + + IDispatch_Release(obj); + } + + return hres; +} + +/* ECMA-262 3rd Edition 11.2.1 */ +HRESULT member_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + member_expression_t *expr = (member_expression_t*)_expr; + IDispatch *obj = NULL; + exprval_t exprval; + VARIANT member; + DISPID id; + BSTR str; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, 0, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &member); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + hres = to_object(ctx, &member, &obj); + VariantClear(&member); + if(FAILED(hres)) + return hres; + + str = SysAllocString(expr->identifier); + if(flags & EXPR_STRREF) { + ret->type = EXPRVAL_NAMEREF; + ret->u.nameref.disp = obj; + ret->u.nameref.name = str; + return S_OK; + } + + hres = disp_get_id(obj, str, flags & EXPR_NEWREF ? fdexNameEnsure : 0, &id); + SysFreeString(str); + if(SUCCEEDED(hres)) { + exprval_set_idref(ret, obj, id); + }else if(!(flags & EXPR_NEWREF) && hres == DISP_E_UNKNOWNNAME) { + exprval_init(ret); + hres = S_OK; + } + + IDispatch_Release(obj); + return hres; +} + +static void free_dp(DISPPARAMS *dp) +{ + DWORD i; + + for(i=0; i < dp->cArgs; i++) + VariantClear(dp->rgvarg+i); + heap_free(dp->rgvarg); +} + +static HRESULT args_to_param(exec_ctx_t *ctx, argument_t *args, jsexcept_t *ei, DISPPARAMS *dp) +{ + VARIANTARG *vargs; + exprval_t exprval; + argument_t *iter; + DWORD cnt = 0, i; + HRESULT hres = S_OK; + + memset(dp, 0, sizeof(*dp)); + if(!args) + return S_OK; + + for(iter = args; iter; iter = iter->next) + cnt++; + + vargs = heap_alloc_zero(cnt * sizeof(*vargs)); + if(!vargs) + return E_OUTOFMEMORY; + + for(i = cnt, iter = args; iter; iter = iter->next) { + hres = expr_eval(ctx, iter->expr, 0, ei, &exprval); + if(FAILED(hres)) + break; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, vargs + (--i)); + exprval_release(&exprval); + if(FAILED(hres)) + break; + } + + if(FAILED(hres)) { + free_dp(dp); + return hres; + } + + dp->rgvarg = vargs; + dp->cArgs = cnt; + return S_OK; +} + +/* ECMA-262 3rd Edition 11.2.2 */ +HRESULT new_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + call_expression_t *expr = (call_expression_t*)_expr; + exprval_t exprval; + VARIANT constr, var; + DISPPARAMS dp; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, 0, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = args_to_param(ctx, expr->argument_list, ei, &dp); + if(SUCCEEDED(hres)) + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &constr); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + if(V_VT(&constr) != VT_DISPATCH) { + FIXME("throw TypeError\n"); + VariantClear(&constr); + return E_FAIL; + } + + hres = disp_call(V_DISPATCH(&constr), DISPID_VALUE, ctx->parser->script->lcid, + DISPATCH_CONSTRUCT, &dp, &var, ei, NULL/*FIXME*/); + IDispatch_Release(V_DISPATCH(&constr)); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + ret->u.var = var; + return S_OK; +} + +/* ECMA-262 3rd Edition 11.2.3 */ +HRESULT call_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + call_expression_t *expr = (call_expression_t*)_expr; + VARIANT func, var; + exprval_t exprval; + DISPPARAMS dp; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, 0, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = args_to_param(ctx, expr->argument_list, ei, &dp); + if(SUCCEEDED(hres)) { + switch(exprval.type) { + case EXPRVAL_IDREF: + hres = disp_call(exprval.u.idref.disp, exprval.u.idref.id, ctx->parser->script->lcid, DISPATCH_METHOD, + &dp, flags & EXPR_NOVAL ? NULL : &var, ei, NULL/*FIXME*/); + if(flags & EXPR_NOVAL) + V_VT(&var) = VT_EMPTY; + break; + default: + FIXME("unimplemented type %d\n", V_VT(&func)); + hres = E_NOTIMPL; + } + + free_dp(&dp); + } + + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + TRACE("= %s\n", debugstr_variant(&var)); + ret->type = EXPRVAL_VARIANT; + ret->u.var = var; + return S_OK; +} + +/* ECMA-262 3rd Edition 11.1.1 */ HRESULT this_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + TRACE("\n"); + + ret->type = EXPRVAL_VARIANT; + V_VT(&ret->u.var) = VT_DISPATCH; + V_DISPATCH(&ret->u.var) = ctx->this_obj; + IDispatch_AddRef(ctx->this_obj); + return S_OK; } -HRESULT identifier_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 10.1.4 */ +HRESULT identifier_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + identifier_expression_t *expr = (identifier_expression_t*)_expr; + BSTR identifier; + HRESULT hres; + + TRACE("\n"); + + identifier = SysAllocString(expr->identifier); + if(!identifier) + return E_OUTOFMEMORY; + + hres = identifier_eval(ctx, identifier, flags, ret); + + SysFreeString(identifier); + return hres; } -HRESULT literal_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 7.8 */ +HRESULT literal_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + literal_expression_t *expr = (literal_expression_t*)_expr; + VARIANT var; + HRESULT hres; + + TRACE("\n"); + + hres = literal_to_var(expr->literal, &var); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + ret->u.var = var; + return S_OK; } -HRESULT array_literal_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.1.4 */ +HRESULT array_literal_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + array_literal_expression_t *expr = (array_literal_expression_t*)_expr; + DWORD length = 0, i = 0; + array_element_t *elem; + DispatchEx *array; + exprval_t exprval; + VARIANT val; + HRESULT hres; + + TRACE("\n"); + + for(elem = expr->element_list; elem; elem = elem->next) + length += elem->elision+1; + length += expr->length; + + hres = create_array(ctx->parser->script, length, &array); + if(FAILED(hres)) + return hres; + + for(elem = expr->element_list; elem; elem = elem->next) { + i += elem->elision; + + hres = expr_eval(ctx, elem->expr, 0, ei, &exprval); + if(FAILED(hres)) + break; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + break; + + hres = jsdisp_propput_idx(array, i, ctx->parser->script->lcid, &val, ei, NULL/*FIXME*/); + VariantClear(&val); + if(FAILED(hres)) + break; + + i++; + } + + if(FAILED(hres)) { + jsdisp_release(array); + return hres; + } + + ret->type = EXPRVAL_VARIANT; + V_VT(&ret->u.var) = VT_DISPATCH; + V_DISPATCH(&ret->u.var) = (IDispatch*)_IDispatchEx_(array); + return S_OK; } -HRESULT property_value_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.1.5 */ +HRESULT property_value_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + property_value_expression_t *expr = (property_value_expression_t*)_expr; + VARIANT val, tmp; + DispatchEx *obj; + prop_val_t *iter; + exprval_t exprval; + BSTR name; + HRESULT hres; + + TRACE("\n"); + + hres = create_object(ctx->parser->script, NULL, &obj); + if(FAILED(hres)) + return hres; + + for(iter = expr->property_list; iter; iter = iter->next) { + hres = literal_to_var(iter->name, &tmp); + if(FAILED(hres)) + break; + + hres = to_string(ctx->parser->script, &tmp, ei, &name); + VariantClear(&tmp); + if(FAILED(hres)) + break; + + hres = expr_eval(ctx, iter->value, 0, ei, &exprval); + if(SUCCEEDED(hres)) { + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &val); + exprval_release(&exprval); + if(SUCCEEDED(hres)) { + hres = jsdisp_propput_name(obj, name, ctx->parser->script->lcid, &val, ei, NULL/*FIXME*/); + VariantClear(&val); + } + } + + SysFreeString(name); + if(FAILED(hres)) + break; + } + + if(FAILED(hres)) { + jsdisp_release(obj); + return hres; + } + + ret->type = EXPRVAL_VARIANT; + V_VT(&ret->u.var) = VT_DISPATCH; + V_DISPATCH(&ret->u.var) = (IDispatch*)_IDispatchEx_(obj); + return S_OK; } -HRESULT comma_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.14 */ +HRESULT comma_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + VARIANT lval, rval; + HRESULT hres; + + TRACE("\n"); + + hres = get_binary_expr_values(ctx, expr, ei, &lval, &rval); + if(FAILED(hres)) + return hres; + + VariantClear(&lval); + + ret->type = EXPRVAL_VARIANT; + ret->u.var = rval; + return S_OK; } -HRESULT logical_or_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.11 */ +HRESULT logical_or_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + exprval_t exprval; + VARIANT_BOOL b; + VARIANT val; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression1, 0, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + hres = to_boolean(&val, &b); + if(SUCCEEDED(hres) && b) { + ret->type = EXPRVAL_VARIANT; + ret->u.var = val; + return S_OK; + } + + VariantClear(&val); + if(FAILED(hres)) + return hres; + + hres = expr_eval(ctx, expr->expression2, 0, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + ret->u.var = val; + return S_OK; } -HRESULT logical_and_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.11 */ +HRESULT logical_and_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + exprval_t exprval; + VARIANT_BOOL b; + VARIANT val; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression1, 0, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + hres = to_boolean(&val, &b); + if(SUCCEEDED(hres) && !b) { + ret->type = EXPRVAL_VARIANT; + ret->u.var = val; + return S_OK; + } + + VariantClear(&val); + if(FAILED(hres)) + return hres; + + hres = expr_eval(ctx, expr->expression2, 0, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + ret->u.var = val; + return S_OK; } -HRESULT binary_or_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.10 */ +static HRESULT bitor_eval(exec_ctx_t *ctx, VARIANT *lval, VARIANT *rval, jsexcept_t *ei, VARIANT *retv) { - FIXME("\n"); - return E_NOTIMPL; + INT li, ri; + HRESULT hres; + + hres = to_int32(ctx->parser->script, lval, ei, &li); + if(FAILED(hres)) + return hres; + + hres = to_int32(ctx->parser->script, rval, ei, &ri); + if(FAILED(hres)) + return hres; + + V_VT(retv) = VT_I4; + V_I4(retv) = li|ri; + return S_OK; } -HRESULT binary_xor_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.10 */ +HRESULT binary_or_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return binary_expr_eval(ctx, expr, bitor_eval, ei, ret); } -HRESULT binary_and_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.10 */ +static HRESULT xor_eval(exec_ctx_t *ctx, VARIANT *lval, VARIANT *rval, jsexcept_t *ei, VARIANT *retv) { - FIXME("\n"); - return E_NOTIMPL; + INT li, ri; + HRESULT hres; + + hres = to_int32(ctx->parser->script, lval, ei, &li); + if(FAILED(hres)) + return hres; + + hres = to_int32(ctx->parser->script, rval, ei, &ri); + if(FAILED(hres)) + return hres; + + V_VT(retv) = VT_I4; + V_I4(retv) = li^ri; + return S_OK; } +/* ECMA-262 3rd Edition 11.10 */ +HRESULT binary_xor_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return binary_expr_eval(ctx, expr, xor_eval, ei, ret); +} + +/* ECMA-262 3rd Edition 11.10 */ +static HRESULT bitand_eval(exec_ctx_t *ctx, VARIANT *lval, VARIANT *rval, jsexcept_t *ei, VARIANT *retv) +{ + INT li, ri; + HRESULT hres; + + hres = to_int32(ctx->parser->script, lval, ei, &li); + if(FAILED(hres)) + return hres; + + hres = to_int32(ctx->parser->script, rval, ei, &ri); + if(FAILED(hres)) + return hres; + + V_VT(retv) = VT_I4; + V_I4(retv) = li&ri; + return S_OK; +} + +/* ECMA-262 3rd Edition 11.10 */ +HRESULT binary_and_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return binary_expr_eval(ctx, expr, bitand_eval, ei, ret); +} + +/* ECMA-262 3rd Edition 11.8.6 */ HRESULT instanceof_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { FIXME("\n"); return E_NOTIMPL; } +/* ECMA-262 3rd Edition 11.8.7 */ HRESULT in_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { FIXME("\n"); return E_NOTIMPL; } -HRESULT add_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.6.1 */ +static HRESULT add_eval(exec_ctx_t *ctx, VARIANT *lval, VARIANT *rval, jsexcept_t *ei, VARIANT *retv) { - FIXME("\n"); - return E_NOTIMPL; + VARIANT r, l; + HRESULT hres; + + hres = to_primitive(ctx->parser->script, lval, ei, &l); + if(FAILED(hres)) + return hres; + + hres = to_primitive(ctx->parser->script, rval, ei, &r); + if(FAILED(hres)) { + VariantClear(&l); + return hres; + } + + if(V_VT(&l) == VT_BSTR || V_VT(&r) == VT_BSTR) { + BSTR lstr = NULL, rstr = NULL; + + if(V_VT(&l) == VT_BSTR) + lstr = V_BSTR(&l); + else + hres = to_string(ctx->parser->script, &l, ei, &lstr); + + if(SUCCEEDED(hres)) { + if(V_VT(&r) == VT_BSTR) + rstr = V_BSTR(&r); + else + hres = to_string(ctx->parser->script, &r, ei, &rstr); + } + + if(SUCCEEDED(hres)) { + int len1, len2; + + len1 = SysStringLen(lstr); + len2 = SysStringLen(rstr); + + V_VT(retv) = VT_BSTR; + V_BSTR(retv) = SysAllocStringLen(NULL, len1+len2); + memcpy(V_BSTR(retv), lstr, len1*sizeof(WCHAR)); + memcpy(V_BSTR(retv)+len1, rstr, (len2+1)*sizeof(WCHAR)); + } + + if(lstr && V_VT(&l) != VT_BSTR) + SysFreeString(lstr); + if(rstr && V_VT(&r) != VT_BSTR) + SysFreeString(rstr); + }else { + VARIANT nl, nr; + + hres = to_number(ctx->parser->script, &l, ei, &nl); + if(SUCCEEDED(hres)) { + hres = to_number(ctx->parser->script, &r, ei, &nr); + if(SUCCEEDED(hres)) + num_set_val(retv, num_val(&nl) + num_val(&nr)); + } + } + + VariantClear(&r); + VariantClear(&l); + return hres; } -HRESULT sub_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.6.1 */ +HRESULT add_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return binary_expr_eval(ctx, expr, add_eval, ei, ret); } -HRESULT mul_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.6.2 */ +static HRESULT sub_eval(exec_ctx_t *ctx, VARIANT *lval, VARIANT *rval, jsexcept_t *ei, VARIANT *retv) { - FIXME("\n"); - return E_NOTIMPL; + VARIANT lnum, rnum; + HRESULT hres; + + hres = to_number(ctx->parser->script, lval, ei, &lnum); + if(FAILED(hres)) + return hres; + + hres = to_number(ctx->parser->script, rval, ei, &rnum); + if(FAILED(hres)) + return hres; + + num_set_val(retv, num_val(&lnum) - num_val(&rnum)); + return S_OK; } -HRESULT div_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.6.2 */ +HRESULT sub_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return binary_expr_eval(ctx, expr, sub_eval, ei, ret); } +/* ECMA-262 3rd Edition 11.5.1 */ +static HRESULT mul_eval(exec_ctx_t *ctx, VARIANT *lval, VARIANT *rval, jsexcept_t *ei, VARIANT *retv) +{ + VARIANT lnum, rnum; + HRESULT hres; + + hres = to_number(ctx->parser->script, lval, ei, &lnum); + if(FAILED(hres)) + return hres; + + hres = to_number(ctx->parser->script, rval, ei, &rnum); + if(FAILED(hres)) + return hres; + + num_set_val(retv, num_val(&lnum) * num_val(&rnum)); + return S_OK; +} + +/* ECMA-262 3rd Edition 11.5.1 */ +HRESULT mul_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return binary_expr_eval(ctx, expr, mul_eval, ei, ret); +} + +/* ECMA-262 3rd Edition 11.5.2 */ +static HRESULT div_eval(exec_ctx_t *ctx, VARIANT *lval, VARIANT *rval, jsexcept_t *ei, VARIANT *retv) +{ + VARIANT lnum, rnum; + HRESULT hres; + + hres = to_number(ctx->parser->script, lval, ei, &lnum); + if(FAILED(hres)) + return hres; + + hres = to_number(ctx->parser->script, rval, ei, &rnum); + if(FAILED(hres)) + return hres; + + num_set_val(retv, num_val(&lnum) / num_val(&rnum)); + return S_OK; +} + +/* ECMA-262 3rd Edition 11.5.2 */ +HRESULT div_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return binary_expr_eval(ctx, expr, div_eval, ei, ret); +} + +/* ECMA-262 3rd Edition 11.5.3 */ HRESULT mod_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { FIXME("\n"); return E_NOTIMPL; } -HRESULT delete_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.4.2 */ +HRESULT delete_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + unary_expression_t *expr = (unary_expression_t*)_expr; + VARIANT_BOOL b = VARIANT_FALSE; + exprval_t exprval; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, EXPR_STRREF, ei, &exprval); + if(FAILED(hres)) + return hres; + + switch(exprval.type) { + case EXPRVAL_NAMEREF: { + IDispatchEx *dispex; + + hres = IDispatch_QueryInterface(exprval.u.nameref.disp, &IID_IDispatchEx, (void**)&dispex); + if(SUCCEEDED(hres)) { + hres = IDispatchEx_DeleteMemberByName(dispex, exprval.u.nameref.name, fdexNameCaseSensitive); + b = VARIANT_TRUE; + IDispatchEx_Release(dispex); + } + break; + } + default: + FIXME("unsupported type %d\n", exprval.type); + hres = E_NOTIMPL; + } + + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + return return_bool(ret, b); } -HRESULT void_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.4.2 */ +HRESULT void_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + unary_expression_t *expr = (unary_expression_t*)_expr; + exprval_t exprval; + VARIANT tmp; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, 0, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &tmp); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + VariantClear(&tmp); + + ret->type = EXPRVAL_VARIANT; + V_VT(&ret->u.var) = VT_EMPTY; + return S_OK; } -HRESULT typeof_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.4.3 */ +HRESULT typeof_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + unary_expression_t *expr = (unary_expression_t*)_expr; + const WCHAR *str; + exprval_t exprval; + VARIANT val; + HRESULT hres; + + static const WCHAR booleanW[] = {'b','o','o','l','e','a','n',0}; + static const WCHAR functionW[] = {'f','u','n','c','t','i','o','n',0}; + static const WCHAR numberW[] = {'n','u','m','b','e','r',0}; + static const WCHAR objectW[] = {'o','b','j','e','c','t',0}; + static const WCHAR stringW[] = {'s','t','r','i','n','g',0}; + static const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d',0}; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, 0, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + switch(V_VT(&val)) { + case VT_EMPTY: + str = undefinedW; + break; + case VT_NULL: + str = objectW; + break; + case VT_BOOL: + str = booleanW; + break; + case VT_I4: + case VT_R8: + str = numberW; + break; + case VT_BSTR: + str = stringW; + break; + case VT_DISPATCH: { + DispatchEx *dispex; + + dispex = iface_to_jsdisp((IUnknown*)V_DISPATCH(&val)); + if(dispex) { + str = dispex->builtin_info->class == JSCLASS_FUNCTION ? functionW : objectW; + IDispatchEx_Release(_IDispatchEx_(dispex)); + }else { + str = objectW; + } + break; + } + default: + FIXME("unhandled vt %d\n", V_VT(&val)); + hres = E_NOTIMPL; + } + + VariantClear(&val); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + V_VT(&ret->u.var) = VT_BSTR; + V_BSTR(&ret->u.var) = SysAllocString(str); + return S_OK; } -HRESULT minus_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.4.7 */ +HRESULT minus_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + unary_expression_t *expr = (unary_expression_t*)_expr; + exprval_t exprval; + VARIANT val, num; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, 0, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + hres = to_number(ctx->parser->script, &val, ei, &num); + VariantClear(&val); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + num_set_val(&ret->u.var, -num_val(&num)); + return S_OK; } -HRESULT plus_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.4.6 */ +HRESULT plus_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + unary_expression_t *expr = (unary_expression_t*)_expr; + exprval_t exprval; + VARIANT val, num; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, EXPR_NEWREF, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + hres = to_number(ctx->parser->script, &val, ei, &num); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + ret->u.var = num; + return S_OK; } -HRESULT post_increment_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.3.1 */ +HRESULT post_increment_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + unary_expression_t *expr = (unary_expression_t*)_expr; + VARIANT val, num; + exprval_t exprval; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, EXPR_NEWREF, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_value(ctx->parser->script, &exprval, ei, &val); + if(SUCCEEDED(hres)) { + hres = to_number(ctx->parser->script, &val, ei, &num); + VariantClear(&val); + } + + if(SUCCEEDED(hres)) { + VARIANT inc; + num_set_val(&inc, num_val(&num)+1.0); + hres = put_value(ctx->parser->script, &exprval, &inc, ei); + } + + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + ret->u.var = num; + return S_OK; } -HRESULT post_decrement_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.3.2 */ +HRESULT post_decrement_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + unary_expression_t *expr = (unary_expression_t*)_expr; + VARIANT val, num; + exprval_t exprval; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, EXPR_NEWREF, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_value(ctx->parser->script, &exprval, ei, &val); + if(SUCCEEDED(hres)) { + hres = to_number(ctx->parser->script, &val, ei, &num); + VariantClear(&val); + } + + if(SUCCEEDED(hres)) { + VARIANT dec; + num_set_val(&dec, num_val(&num)-1.0); + hres = put_value(ctx->parser->script, &exprval, &dec, ei); + } + + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + ret->u.var = num; + return S_OK; } -HRESULT pre_increment_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.4.4 */ +HRESULT pre_increment_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + unary_expression_t *expr = (unary_expression_t*)_expr; + VARIANT val, num; + exprval_t exprval; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, EXPR_NEWREF, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_value(ctx->parser->script, &exprval, ei, &val); + if(SUCCEEDED(hres)) { + hres = to_number(ctx->parser->script, &val, ei, &num); + VariantClear(&val); + } + + if(SUCCEEDED(hres)) { + num_set_val(&val, num_val(&num)+1.0); + hres = put_value(ctx->parser->script, &exprval, &val, ei); + } + + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + ret->u.var = val; + return S_OK; } -HRESULT pre_decrement_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.4.5 */ +HRESULT pre_decrement_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + unary_expression_t *expr = (unary_expression_t*)_expr; + VARIANT val, num; + exprval_t exprval; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, EXPR_NEWREF, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_value(ctx->parser->script, &exprval, ei, &val); + if(SUCCEEDED(hres)) { + hres = to_number(ctx->parser->script, &val, ei, &num); + VariantClear(&val); + } + + if(SUCCEEDED(hres)) { + num_set_val(&val, num_val(&num)-1.0); + hres = put_value(ctx->parser->script, &exprval, &val, ei); + } + + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + ret->u.var = val; + return S_OK; } -HRESULT new_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.9.3 */ +static HRESULT equal_values(exec_ctx_t *ctx, VARIANT *lval, VARIANT *rval, jsexcept_t *ei, BOOL *ret) { - FIXME("\n"); - return E_NOTIMPL; + if(V_VT(lval) == V_VT(rval) || (is_num_vt(V_VT(lval)) && is_num_vt(V_VT(rval)))) + return equal2_values(lval, rval, ret); + + /* FIXME: NULL disps should be handled in more general way */ + if(V_VT(lval) == VT_DISPATCH && !V_DISPATCH(lval)) { + VARIANT v; + V_VT(&v) = VT_NULL; + return equal_values(ctx, &v, rval, ei, ret); + } + + if(V_VT(rval) == VT_DISPATCH && !V_DISPATCH(rval)) { + VARIANT v; + V_VT(&v) = VT_NULL; + return equal_values(ctx, lval, &v, ei, ret); + } + + if((V_VT(lval) == VT_NULL && V_VT(rval) == VT_EMPTY) || + (V_VT(lval) == VT_EMPTY && V_VT(rval) == VT_NULL)) { + *ret = TRUE; + return S_OK; + } + + if(V_VT(lval) == VT_BSTR && is_num_vt(V_VT(rval))) { + VARIANT v; + HRESULT hres; + + hres = to_number(ctx->parser->script, lval, ei, &v); + if(FAILED(hres)) + return hres; + + return equal_values(ctx, &v, rval, ei, ret); + } + + if(V_VT(rval) == VT_BSTR && is_num_vt(V_VT(lval))) { + VARIANT v; + HRESULT hres; + + hres = to_number(ctx->parser->script, rval, ei, &v); + if(FAILED(hres)) + return hres; + + return equal_values(ctx, lval, &v, ei, ret); + } + + if(V_VT(rval) == VT_BOOL) { + VARIANT v; + + V_VT(&v) = VT_I4; + V_I4(&v) = V_BOOL(rval) ? 1 : 0; + return equal_values(ctx, lval, &v, ei, ret); + } + + if(V_VT(lval) == VT_BOOL) { + VARIANT v; + + V_VT(&v) = VT_I4; + V_I4(&v) = V_BOOL(lval) ? 1 : 0; + return equal_values(ctx, &v, rval, ei, ret); + } + + + if(V_VT(rval) == VT_DISPATCH && (V_VT(lval) == VT_BSTR || is_num_vt(V_VT(lval)))) { + VARIANT v; + HRESULT hres; + + hres = to_primitive(ctx->parser->script, rval, ei, &v); + if(FAILED(hres)) + return hres; + + hres = equal_values(ctx, lval, &v, ei, ret); + + VariantClear(&v); + return hres; + } + + + if(V_VT(lval) == VT_DISPATCH && (V_VT(rval) == VT_BSTR || is_num_vt(V_VT(rval)))) { + VARIANT v; + HRESULT hres; + + hres = to_primitive(ctx->parser->script, lval, ei, &v); + if(FAILED(hres)) + return hres; + + hres = equal_values(ctx, &v, rval, ei, ret); + + VariantClear(&v); + return hres; + } + + + *ret = FALSE; + return S_OK; } -HRESULT equal_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.9.1 */ +HRESULT equal_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + VARIANT rval, lval; + BOOL b; + HRESULT hres; + + TRACE("\n"); + + hres = get_binary_expr_values(ctx, expr, ei, &rval, &lval); + if(FAILED(hres)) + return hres; + + hres = equal_values(ctx, &rval, &lval, ei, &b); + if(FAILED(hres)) + return hres; + + return return_bool(ret, b); } -HRESULT equal2_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.9.4 */ +HRESULT equal2_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + VARIANT rval, lval; + BOOL b; + HRESULT hres; + + TRACE("\n"); + + hres = get_binary_expr_values(ctx, expr, ei, &rval, &lval); + if(FAILED(hres)) + return hres; + + hres = equal2_values(&rval, &lval, &b); + if(FAILED(hres)) + return hres; + + return return_bool(ret, b); } +/* ECMA-262 3rd Edition 11.9.2 */ HRESULT not_equal_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { + binary_expression_t *expr = (binary_expression_t*)_expr; + VARIANT rval, lval; + BOOL b; + HRESULT hres; - FIXME("\n"); - return E_NOTIMPL; + TRACE("\n"); + + hres = get_binary_expr_values(ctx, expr, ei, &lval, &rval); + if(FAILED(hres)) + return hres; + + hres = equal_values(ctx, &lval, &rval, ei, &b); + if(FAILED(hres)) + return hres; + + return return_bool(ret, !b); } -HRESULT not_equal2_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.9.5 */ +HRESULT not_equal2_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + VARIANT rval, lval; + BOOL b; + HRESULT hres; + + TRACE("\n"); + + hres = get_binary_expr_values(ctx, expr, ei, &lval, &rval); + if(FAILED(hres)) + return hres; + + hres = equal2_values(&lval, &rval, &b); + if(FAILED(hres)) + return hres; + + return return_bool(ret, !b); } -HRESULT less_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.8.5 */ +static HRESULT less_eval(exec_ctx_t *ctx, VARIANT *lval, VARIANT *rval, jsexcept_t *ei, BOOL *ret) { - FIXME("\n"); - return E_NOTIMPL; + VARIANT l, r, ln, rn; + HRESULT hres; + + hres = to_primitive(ctx->parser->script, lval, ei, &l); + if(FAILED(hres)) + return hres; + + hres = to_primitive(ctx->parser->script, rval, ei, &r); + if(FAILED(hres)) { + VariantClear(&l); + return hres; + } + + if(V_VT(&l) == VT_BSTR && V_VT(&r) == VT_BSTR) { + *ret = strcmpW(V_BSTR(&l), V_BSTR(&r)) < 0; + SysFreeString(V_BSTR(&l)); + SysFreeString(V_BSTR(&r)); + return S_OK; + } + + hres = to_number(ctx->parser->script, &l, ei, &ln); + VariantClear(&l); + if(SUCCEEDED(hres)) + hres = to_number(ctx->parser->script, &r, ei, &rn); + VariantClear(&r); + if(FAILED(hres)) + return hres; + + if(V_VT(&ln) == VT_I4 && V_VT(&rn) == VT_I4) + *ret = V_I4(&ln) < V_I4(&rn); + else + *ret = num_val(&ln) < num_val(&rn); + + return S_OK; } -HRESULT lesseq_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.8.1 */ +HRESULT less_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + VARIANT rval, lval; + BOOL b; + HRESULT hres; + + TRACE("\n"); + + hres = get_binary_expr_values(ctx, expr, ei, &lval, &rval); + if(FAILED(hres)) + return hres; + + hres = less_eval(ctx, &lval, &rval, ei, &b); + VariantClear(&lval); + VariantClear(&rval); + if(FAILED(hres)) + return hres; + + return return_bool(ret, b); } -HRESULT greater_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.8.3 */ +HRESULT lesseq_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + VARIANT rval, lval; + BOOL b; + HRESULT hres; + + TRACE("\n"); + + hres = get_binary_expr_values(ctx, expr, ei, &lval, &rval); + if(FAILED(hres)) + return hres; + + hres = less_eval(ctx, &rval, &lval, ei, &b); + VariantClear(&lval); + VariantClear(&rval); + if(FAILED(hres)) + return hres; + + return return_bool(ret, !b); } -HRESULT greatereq_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.8.2 */ +HRESULT greater_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + VARIANT rval, lval; + BOOL b; + HRESULT hres; + + TRACE("\n"); + + hres = get_binary_expr_values(ctx, expr, ei, &lval, &rval); + if(FAILED(hres)) + return hres; + + hres = less_eval(ctx, &rval, &lval, ei, &b); + VariantClear(&lval); + VariantClear(&rval); + if(FAILED(hres)) + return hres; + + return return_bool(ret, b); } -HRESULT binary_negation_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.8.4 */ +HRESULT greatereq_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + VARIANT rval, lval; + BOOL b; + HRESULT hres; + + TRACE("\n"); + + hres = get_binary_expr_values(ctx, expr, ei, &lval, &rval); + if(FAILED(hres)) + return hres; + + hres = less_eval(ctx, &lval, &rval, ei, &b); + VariantClear(&lval); + VariantClear(&rval); + if(FAILED(hres)) + return hres; + + return return_bool(ret, !b); } -HRESULT logical_negation_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.4.8 */ +HRESULT binary_negation_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + unary_expression_t *expr = (unary_expression_t*)_expr; + exprval_t exprval; + VARIANT val; + INT i; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, EXPR_NEWREF, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_value(ctx->parser->script, &exprval, ei, &val); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + hres = to_int32(ctx->parser->script, &val, ei, &i); + if(FAILED(hres)) + return hres; + + ret->type = EXPRVAL_VARIANT; + V_VT(&ret->u.var) = VT_I4; + V_I4(&ret->u.var) = ~i; + return S_OK; } +/* ECMA-262 3rd Edition 11.4.9 */ +HRESULT logical_negation_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +{ + unary_expression_t *expr = (unary_expression_t*)_expr; + exprval_t exprval; + VARIANT_BOOL b; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression, EXPR_NEWREF, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = exprval_to_boolean(ctx->parser->script, &exprval, ei, &b); + exprval_release(&exprval); + if(FAILED(hres)) + return hres; + + return return_bool(ret, !b); +} + +/* ECMA-262 3rd Edition 11.7.1 */ +static HRESULT lshift_eval(exec_ctx_t *ctx, VARIANT *lval, VARIANT *rval, jsexcept_t *ei, VARIANT *retv) +{ + DWORD ri; + INT li; + HRESULT hres; + + hres = to_int32(ctx->parser->script, lval, ei, &li); + if(FAILED(hres)) + return hres; + + hres = to_uint32(ctx->parser->script, rval, ei, &ri); + if(FAILED(hres)) + return hres; + + V_VT(retv) = VT_I4; + V_I4(retv) = li << (ri&0x1f); + return S_OK; +} + +/* ECMA-262 3rd Edition 11.7.1 */ HRESULT left_shift_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return binary_expr_eval(ctx, expr, lshift_eval, ei, ret); } +/* ECMA-262 3rd Edition 11.7.2 */ +static HRESULT rshift_eval(exec_ctx_t *ctx, VARIANT *lval, VARIANT *rval, jsexcept_t *ei, VARIANT *retv) +{ + DWORD ri; + INT li; + HRESULT hres; + + hres = to_int32(ctx->parser->script, lval, ei, &li); + if(FAILED(hres)) + return hres; + + hres = to_uint32(ctx->parser->script, rval, ei, &ri); + if(FAILED(hres)) + return hres; + + V_VT(retv) = VT_I4; + V_I4(retv) = li >> (ri&0x1f); + return S_OK; +} + +/* ECMA-262 3rd Edition 11.7.2 */ HRESULT right_shift_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return binary_expr_eval(ctx, expr, rshift_eval, ei, ret); } +/* ECMA-262 3rd Edition 11.7.3 */ +static HRESULT rshift2_eval(exec_ctx_t *ctx, VARIANT *lval, VARIANT *rval, jsexcept_t *ei, VARIANT *retv) +{ + DWORD li, ri; + HRESULT hres; + + hres = to_uint32(ctx->parser->script, lval, ei, &li); + if(FAILED(hres)) + return hres; + + hres = to_uint32(ctx->parser->script, rval, ei, &ri); + if(FAILED(hres)) + return hres; + + V_VT(retv) = VT_I4; + V_I4(retv) = li >> (ri&0x1f); + return S_OK; +} + +/* ECMA-262 3rd Edition 11.7.3 */ HRESULT right2_shift_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return binary_expr_eval(ctx, expr, rshift2_eval, ei, ret); } -HRESULT assign_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.13.1 */ +HRESULT assign_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + exprval_t exprval, exprvalr; + VARIANT rval; + HRESULT hres; + + TRACE("\n"); + + hres = expr_eval(ctx, expr->expression1, EXPR_NEWREF, ei, &exprval); + if(FAILED(hres)) + return hres; + + hres = expr_eval(ctx, expr->expression2, 0, ei, &exprvalr); + if(SUCCEEDED(hres)) { + hres = exprval_to_value(ctx->parser->script, &exprvalr, ei, &rval); + exprval_release(&exprvalr); + } + + if(SUCCEEDED(hres)) + hres = put_value(ctx->parser->script, &exprval, &rval, ei); + + exprval_release(&exprval); + if(FAILED(hres)) { + VariantClear(&rval); + return hres; + } + + ret->type = EXPRVAL_VARIANT; + ret->u.var = rval; + return S_OK; } -HRESULT assign_lshift_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.13.2 */ +HRESULT assign_lshift_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return assign_oper_eval(ctx, expr->expression1, expr->expression2, lshift_eval, ei, ret); } -HRESULT assign_rshift_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.13.2 */ +HRESULT assign_rshift_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return assign_oper_eval(ctx, expr->expression1, expr->expression2, rshift_eval, ei, ret); } -HRESULT assign_rrshift_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.13.2 */ +HRESULT assign_rrshift_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return assign_oper_eval(ctx, expr->expression1, expr->expression2, rshift2_eval, ei, ret); } -HRESULT assign_add_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.13.2 */ +HRESULT assign_add_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return assign_oper_eval(ctx, expr->expression1, expr->expression2, add_eval, ei, ret); } -HRESULT assign_sub_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.13.2 */ +HRESULT assign_sub_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return assign_oper_eval(ctx, expr->expression1, expr->expression2, sub_eval, ei, ret); } -HRESULT assign_mul_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.13.2 */ +HRESULT assign_mul_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return assign_oper_eval(ctx, expr->expression1, expr->expression2, mul_eval, ei, ret); } -HRESULT assign_div_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.13.2 */ +HRESULT assign_div_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return assign_oper_eval(ctx, expr->expression1, expr->expression2, div_eval, ei, ret); } +/* ECMA-262 3rd Edition 11.13.2 */ HRESULT assign_mod_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { FIXME("\n"); return E_NOTIMPL; } -HRESULT assign_and_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.13.2 */ +HRESULT assign_and_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return assign_oper_eval(ctx, expr->expression1, expr->expression2, bitand_eval, ei, ret); } -HRESULT assign_or_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.13.2 */ +HRESULT assign_or_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return assign_oper_eval(ctx, expr->expression1, expr->expression2, bitor_eval, ei, ret); } -HRESULT assign_xor_expression_eval(exec_ctx_t *ctx, expression_t *expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) +/* ECMA-262 3rd Edition 11.13.2 */ +HRESULT assign_xor_expression_eval(exec_ctx_t *ctx, expression_t *_expr, DWORD flags, jsexcept_t *ei, exprval_t *ret) { - FIXME("\n"); - return E_NOTIMPL; + binary_expression_t *expr = (binary_expression_t*)_expr; + + TRACE("\n"); + + return assign_oper_eval(ctx, expr->expression1, expr->expression2, xor_eval, ei, ret); } diff --git a/reactos/dll/win32/jscript/engine.h b/reactos/dll/win32/jscript/engine.h index 6e91178b431..3f7fda86c9a 100644 --- a/reactos/dll/win32/jscript/engine.h +++ b/reactos/dll/win32/jscript/engine.h @@ -18,6 +18,11 @@ typedef struct _source_elements_t source_elements_t; +typedef struct _obj_literal_t { + DispatchEx *obj; + struct _obj_literal_t *next; +} obj_literal_t; + typedef struct _parser_ctx_t { LONG ref; @@ -30,9 +35,10 @@ typedef struct _parser_ctx_t { BOOL nl; HRESULT hres; - jsheap_t tmp_heap; jsheap_t heap; + obj_literal_t *obj_literals; + struct _parser_ctx_t *next; } parser_ctx_t; @@ -53,13 +59,30 @@ static inline void *parser_alloc(parser_ctx_t *ctx, DWORD size) static inline void *parser_alloc_tmp(parser_ctx_t *ctx, DWORD size) { - return jsheap_alloc(&ctx->tmp_heap, size); + return jsheap_alloc(&ctx->script->tmp_heap, size); +} + +typedef struct _scope_chain_t { + LONG ref; + DispatchEx *obj; + struct _scope_chain_t *next; +} scope_chain_t; + +HRESULT scope_push(scope_chain_t*,DispatchEx*,scope_chain_t**); +void scope_release(scope_chain_t*); + +static inline void scope_addref(scope_chain_t *scope) +{ + scope->ref++; } struct _exec_ctx_t { LONG ref; parser_ctx_t *parser; + scope_chain_t *scope_chain; + DispatchEx *var_disp; + IDispatch *this_obj; }; static inline void exec_addref(exec_ctx_t *ctx) @@ -68,13 +91,15 @@ static inline void exec_addref(exec_ctx_t *ctx) } void exec_release(exec_ctx_t*); -HRESULT create_exec_ctx(exec_ctx_t**); +HRESULT create_exec_ctx(IDispatch*,DispatchEx*,scope_chain_t*,exec_ctx_t**); HRESULT exec_source(exec_ctx_t*,parser_ctx_t*,source_elements_t*,jsexcept_t*,VARIANT*); typedef struct _statement_t statement_t; typedef struct _expression_t expression_t; typedef struct _parameter_t parameter_t; +HRESULT create_source_function(parser_ctx_t*,parameter_t*,source_elements_t*,scope_chain_t*,DispatchEx**); + typedef struct { VARTYPE vt; union { @@ -86,6 +111,8 @@ typedef struct { } u; } literal_t; +literal_t *parse_regexp(parser_ctx_t*); + typedef struct _variable_declaration_t { const WCHAR *identifier; expression_t *expr; @@ -134,6 +161,7 @@ typedef struct { typedef struct { statement_t stat; + BOOL do_while; expression_t *expr; statement_t *statement; } while_statement_t; @@ -202,7 +230,6 @@ HRESULT var_statement_eval(exec_ctx_t*,statement_t*,return_type_t*,VARIANT*); HRESULT empty_statement_eval(exec_ctx_t*,statement_t*,return_type_t*,VARIANT*); HRESULT expression_statement_eval(exec_ctx_t*,statement_t*,return_type_t*,VARIANT*); HRESULT if_statement_eval(exec_ctx_t*,statement_t*,return_type_t*,VARIANT*); -HRESULT dowhile_statement_eval(exec_ctx_t*,statement_t*,return_type_t*,VARIANT*); HRESULT while_statement_eval(exec_ctx_t*,statement_t*,return_type_t*,VARIANT*); HRESULT for_statement_eval(exec_ctx_t*,statement_t*,return_type_t*,VARIANT*); HRESULT forin_statement_eval(exec_ctx_t*,statement_t*,return_type_t*,VARIANT*); @@ -215,7 +242,24 @@ HRESULT switch_statement_eval(exec_ctx_t*,statement_t*,return_type_t*,VARIANT*); HRESULT throw_statement_eval(exec_ctx_t*,statement_t*,return_type_t*,VARIANT*); HRESULT try_statement_eval(exec_ctx_t*,statement_t*,return_type_t*,VARIANT*); -typedef struct exprval_t exprval_t; +typedef struct { + enum { + EXPRVAL_VARIANT, + EXPRVAL_IDREF, + EXPRVAL_NAMEREF + } type; + union { + VARIANT var; + struct { + IDispatch *disp; + DISPID id; + } idref; + struct { + IDispatch *disp; + BSTR name; + } nameref; + } u; +} exprval_t; typedef HRESULT (*expression_eval_t)(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); @@ -351,7 +395,6 @@ typedef enum { EXPR_POSTDEC, EXPR_PREINC, EXPR_PREDEC, - EXPR_NEW, EXPR_EQ, EXPR_EQEQ, EXPR_NOTEQ, @@ -383,7 +426,7 @@ HRESULT function_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exp HRESULT conditional_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); HRESULT array_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); HRESULT member_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); -HRESULT member_new_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); +HRESULT new_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); HRESULT call_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); HRESULT this_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); HRESULT identifier_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); @@ -413,7 +456,6 @@ HRESULT post_increment_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_ HRESULT post_decrement_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); HRESULT pre_increment_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); HRESULT pre_decrement_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); -HRESULT new_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); HRESULT equal_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); HRESULT equal2_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); HRESULT not_equal_expression_eval(exec_ctx_t*,expression_t*,DWORD,jsexcept_t*,exprval_t*); diff --git a/reactos/dll/win32/jscript/function.c b/reactos/dll/win32/jscript/function.c new file mode 100644 index 00000000000..7d39ad05893 --- /dev/null +++ b/reactos/dll/win32/jscript/function.c @@ -0,0 +1,496 @@ +/* + * 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 + */ + +#include "jscript.h" +#include "engine.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(jscript); + +typedef struct { + DispatchEx dispex; + builtin_invoke_t value_proc; + DWORD flags; + source_elements_t *source; + parameter_t *parameters; + scope_chain_t *scope_chain; + parser_ctx_t *parser; + DWORD length; +} FunctionInstance; + +static const WCHAR prototypeW[] = {'p','r','o','t','o','t', 'y', 'p','e',0}; + +static const WCHAR lengthW[] = {'l','e','n','g','t','h',0}; +static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0}; +static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0}; +static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0}; +static const WCHAR applyW[] = {'a','p','p','l','y',0}; +static const WCHAR callW[] = {'c','a','l','l',0}; +static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0}; +static const WCHAR propertyIsEnumerableW[] = {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0}; +static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0}; + +static IDispatch *get_this(DISPPARAMS *dp) +{ + DWORD i; + + for(i=0; i < dp->cNamedArgs; i++) { + if(dp->rgdispidNamedArgs[i] == DISPID_THIS) { + if(V_VT(dp->rgvarg+i) == VT_DISPATCH) + return V_DISPATCH(dp->rgvarg+i); + + WARN("This is not VT_DISPATCH\n"); + return NULL; + } + } + + TRACE("no this passed\n"); + return NULL; +} + +static HRESULT init_parameters(DispatchEx *var_disp, FunctionInstance *function, LCID lcid, DISPPARAMS *dp, + jsexcept_t *ei, IServiceProvider *caller) +{ + parameter_t *param; + VARIANT var_empty; + DWORD cargs, i=0; + HRESULT hres; + + V_VT(&var_empty) = VT_EMPTY; + cargs = dp->cArgs - dp->cNamedArgs; + + for(param = function->parameters; param; param = param->next) { + hres = jsdisp_propput_name(var_disp, param->identifier, lcid, + i < cargs ? dp->rgvarg + dp->cArgs-1 - i : &var_empty, + ei, caller); + if(FAILED(hres)) + return hres; + + i++; + } + + return S_OK; +} + +static HRESULT init_arguments(DispatchEx *arg_disp, FunctionInstance *function, LCID lcid, DISPPARAMS *dp, + jsexcept_t *ei, IServiceProvider *caller) +{ + VARIANT var; + DWORD i; + HRESULT hres; + + for(i=0; i < dp->cArgs-dp->cNamedArgs; i++) { + hres = jsdisp_propput_idx(arg_disp, i, lcid, dp->rgvarg+dp->cArgs-1-i, ei, caller); + if(FAILED(hres)) + return hres; + } + + V_VT(&var) = VT_I4; + V_I4(&var) = dp->cArgs - dp->cNamedArgs; + return jsdisp_propput_name(arg_disp, lengthW, lcid, &var, ei, caller); +} + +static HRESULT create_var_disp(FunctionInstance *function, LCID lcid, DISPPARAMS *dp, jsexcept_t *ei, + IServiceProvider *caller, DispatchEx **ret) +{ + DispatchEx *var_disp, *arg_disp; + HRESULT hres; + + static const WCHAR argumentsW[] = {'a','r','g','u','m','e','n','t','s',0}; + + hres = create_dispex(function->dispex.ctx, NULL, NULL, &var_disp); + if(FAILED(hres)) + return hres; + + hres = create_dispex(function->dispex.ctx, NULL, NULL, &arg_disp); + if(SUCCEEDED(hres)) { + hres = init_arguments(arg_disp, function, lcid, dp, ei, caller); + if(SUCCEEDED(hres)) { + VARIANT var; + + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = (IDispatch*)_IDispatchEx_(arg_disp); + hres = jsdisp_propput_name(var_disp, argumentsW, lcid, &var, ei, caller); + } + + jsdisp_release(arg_disp); + } + + if(SUCCEEDED(hres)) + hres = init_parameters(var_disp, function, lcid, dp, ei, caller); + if(FAILED(hres)) { + jsdisp_release(var_disp); + return hres; + } + + *ret = var_disp; + return S_OK; +} + +static HRESULT invoke_source(FunctionInstance *function, IDispatch *this_obj, LCID lcid, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller) +{ + DispatchEx *var_disp; + exec_ctx_t *exec_ctx; + scope_chain_t *scope; + HRESULT hres; + + if(!function->source) { + FIXME("no source\n"); + return E_FAIL; + } + + hres = create_var_disp(function, lcid, dp, ei, caller, &var_disp); + if(FAILED(hres)) + return hres; + + hres = scope_push(function->scope_chain, var_disp, &scope); + if(SUCCEEDED(hres)) { + hres = create_exec_ctx(this_obj, var_disp, scope, &exec_ctx); + scope_release(scope); + } + if(FAILED(hres)) + return hres; + + hres = exec_source(exec_ctx, function->parser, function->source, ei, retv); + exec_release(exec_ctx); + + return hres; +} + +static HRESULT invoke_function(FunctionInstance *function, LCID lcid, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller) +{ + IDispatch *this_obj; + + if(!(this_obj = get_this(dp))) + this_obj = (IDispatch*)_IDispatchEx_(function->dispex.ctx->script_disp); + + return invoke_source(function, this_obj, lcid, dp, retv, ei, caller); +} + +static HRESULT invoke_constructor(FunctionInstance *function, LCID lcid, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller) +{ + DispatchEx *this_obj; + VARIANT var; + HRESULT hres; + + hres = create_object(function->dispex.ctx, &function->dispex, &this_obj); + if(FAILED(hres)) + return hres; + + hres = invoke_source(function, (IDispatch*)_IDispatchEx_(this_obj), lcid, dp, retv, ei, caller); + jsdisp_release(this_obj); + if(FAILED(hres)) + return hres; + + VariantClear(&var); + V_VT(retv) = VT_DISPATCH; + V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(this_obj); + return S_OK; +} + +static HRESULT invoke_value_proc(FunctionInstance *function, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller) +{ + DispatchEx *this_obj = NULL; + IDispatch *this_disp; + HRESULT hres; + + this_disp = get_this(dp); + if(this_disp) + this_obj = iface_to_jsdisp((IUnknown*)this_disp); + + hres = function->value_proc(this_obj ? this_obj : function->dispex.ctx->script_disp, lcid, + flags, dp, retv, ei, caller); + + if(this_obj) + jsdisp_release(this_obj); + return hres; +} + +static HRESULT Function_length(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FunctionInstance *This = (FunctionInstance*)dispex; + + TRACE("%p %d\n", This, This->length); + + switch(flags) { + case DISPATCH_PROPERTYGET: + V_VT(retv) = VT_I4; + V_I4(retv) = This->length; + break; + default: + FIXME("unimplemented flags %x\n", flags); + return E_NOTIMPL; + } + + return S_OK; +} + +static HRESULT Function_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Function_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Function_valueOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Function_apply(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Function_call(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Function_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Function_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Function_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Function_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *caller) +{ + FunctionInstance *function; + + TRACE("\n"); + + if(dispex->builtin_info->class != JSCLASS_FUNCTION) { + ERR("dispex is not a function\n"); + return E_FAIL; + } + + function = (FunctionInstance*)dispex; + + switch(flags) { + case DISPATCH_METHOD: + if(function->value_proc) + return invoke_value_proc(function, lcid, flags, dp, retv, ei, caller); + + return invoke_function(function, lcid, dp, retv, ei, caller); + + case DISPATCH_CONSTRUCT: + if(function->value_proc) + return invoke_value_proc(function, lcid, flags, dp, retv, ei, caller); + + return invoke_constructor(function, lcid, dp, retv, ei, caller); + + default: + FIXME("not implemented flags %x\n", flags); + return E_NOTIMPL; + } + + return S_OK; +} + +static void Function_destructor(DispatchEx *dispex) +{ + FunctionInstance *This = (FunctionInstance*)dispex; + + if(This->parser) + parser_release(This->parser); + if(This->scope_chain) + scope_release(This->scope_chain); + heap_free(This); +} + +static const builtin_prop_t Function_props[] = { + {applyW, Function_apply, PROPF_METHOD}, + {callW, Function_call, PROPF_METHOD}, + {hasOwnPropertyW, Function_hasOwnProperty, PROPF_METHOD}, + {isPrototypeOfW, Function_isPrototypeOf, PROPF_METHOD}, + {lengthW, Function_length, 0}, + {propertyIsEnumerableW, Function_propertyIsEnumerable, PROPF_METHOD}, + {toLocaleStringW, Function_toLocaleString, PROPF_METHOD}, + {toStringW, Function_toString, PROPF_METHOD}, + {valueOfW, Function_valueOf, PROPF_METHOD} +}; + +static const builtin_info_t Function_info = { + JSCLASS_FUNCTION, + {NULL, Function_value, 0}, + sizeof(Function_props)/sizeof(*Function_props), + Function_props, + Function_destructor, + NULL +}; + +static HRESULT FunctionConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT FunctionProt_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT create_function(script_ctx_t *ctx, DWORD flags, BOOL funcprot, DispatchEx *prototype, FunctionInstance **ret) +{ + FunctionInstance *function; + HRESULT hres; + + function = heap_alloc_zero(sizeof(FunctionInstance)); + if(!function) + return E_OUTOFMEMORY; + + if(funcprot) + hres = init_dispex(&function->dispex, ctx, &Function_info, prototype); + else + hres = init_dispex_from_constr(&function->dispex, ctx, &Function_info, ctx->function_constr); + if(FAILED(hres)) + return hres; + + function->flags = flags; + function->length = flags & PROPF_ARGMASK; + + if(prototype) { + jsexcept_t jsexcept; + VARIANT var; + + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = (IDispatch*)_IDispatchEx_(prototype); + memset(&jsexcept, 0, sizeof(jsexcept)); + + hres = jsdisp_propput_name(&function->dispex, prototypeW, ctx->lcid, &var, &jsexcept, NULL/*FIXME*/); + if(FAILED(hres)) { + IDispatchEx_Release(_IDispatchEx_(&function->dispex)); + return hres; + } + } + + *ret = function; + return S_OK; +} + +HRESULT create_builtin_function(script_ctx_t *ctx, builtin_invoke_t value_proc, DWORD flags, + DispatchEx *prototype, DispatchEx **ret) +{ + FunctionInstance *function; + HRESULT hres; + + hres = create_function(ctx, flags, FALSE, prototype, &function); + if(FAILED(hres)) + return hres; + + function->value_proc = value_proc; + + *ret = &function->dispex; + return S_OK; +} + +HRESULT create_source_function(parser_ctx_t *ctx, parameter_t *parameters, source_elements_t *source, + scope_chain_t *scope_chain, DispatchEx **ret) +{ + FunctionInstance *function; + DispatchEx *prototype; + parameter_t *iter; + DWORD length = 0; + HRESULT hres; + + hres = create_object(ctx->script, NULL, &prototype); + if(FAILED(hres)) + return hres; + + hres = create_function(ctx->script, PROPF_CONSTR, FALSE, prototype, &function); + jsdisp_release(prototype); + if(FAILED(hres)) + return hres; + + function->source = source; + function->parameters = parameters; + + if(scope_chain) { + scope_addref(scope_chain); + function->scope_chain = scope_chain; + } + + parser_addref(ctx); + function->parser = ctx; + + for(iter = parameters; iter; iter = iter->next) + length++; + function->length = length; + + *ret = &function->dispex; + return S_OK; +} + +HRESULT init_function_constr(script_ctx_t *ctx) +{ + FunctionInstance *prot, *constr; + HRESULT hres; + + hres = create_function(ctx, PROPF_CONSTR, TRUE, NULL, &prot); + if(FAILED(hres)) + return hres; + + prot->value_proc = FunctionProt_value; + + hres = create_function(ctx, PROPF_CONSTR, TRUE, &prot->dispex, &constr); + jsdisp_release(&prot->dispex); + if(FAILED(hres)) + return hres; + + constr->value_proc = FunctionConstr_value; + ctx->function_constr = &constr->dispex; + return hres; +} diff --git a/reactos/dll/win32/jscript/global.c b/reactos/dll/win32/jscript/global.c new file mode 100644 index 00000000000..340df52ba5d --- /dev/null +++ b/reactos/dll/win32/jscript/global.c @@ -0,0 +1,394 @@ +/* + * 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 + */ + +#include "jscript.h" +#include "engine.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(jscript); + +static const WCHAR NaNW[] = {'N','a','N',0}; +static const WCHAR InfinityW[] = {'I','n','f','i','n','i','t','y',0}; +static const WCHAR ArrayW[] = {'A','r','r','a','y',0}; +static const WCHAR BooleanW[] = {'B','o','o','l','e','a','n',0}; +static const WCHAR DateW[] = {'D','a','t','e',0}; +static const WCHAR FunctionW[] = {'F','u','n','c','t','i','o','n',0}; +static const WCHAR NumberW[] = {'N','u','m','b','e','r',0}; +static const WCHAR ObjectW[] = {'O','b','j','e','c','t',0}; +static const WCHAR StringW[] = {'S','t','r','i','n','g',0}; +static const WCHAR RegExpW[] = {'R','e','g','E','x','p',0}; +static const WCHAR ActiveXObjectW[] = {'A','c','t','i','v','e','X','O','b','j','e','c','t',0}; +static const WCHAR VBArrayW[] = {'V','B','A','r','r','a','y',0}; +static const WCHAR EnumeratorW[] = {'E','n','u','m','e','r','a','t','o','r',0}; +static const WCHAR escapeW[] = {'e','s','c','a','p','e',0}; +static const WCHAR evalW[] = {'e','v','a','l',0}; +static const WCHAR isNaNW[] = {'i','s','N','a','N',0}; +static const WCHAR isFiniteW[] = {'i','s','F','i','n','i','t','e',0}; +static const WCHAR parseIntW[] = {'p','a','r','s','e','I','n','t',0}; +static const WCHAR parseFloatW[] = {'p','a','r','s','e','F','l','o','a','t',0}; +static const WCHAR unescapeW[] = {'u','n','e','s','c','a','p','e',0}; +static const WCHAR getObjectW[] = {'G','e','t','O','b','j','e','c','t',0}; +static const WCHAR ScriptEngineW[] = {'S','c','r','i','p','t','E','n','g','i','n','e',0}; +static const WCHAR ScriptEngineMajorVersionW[] = + {'S','c','r','i','p','t','E','n','g','i','n','e','M','a','j','o','r','V','e','r','s','i','o','n',0}; +static const WCHAR ScriptEngineMinorVersionW[] = + {'S','c','r','i','p','t','E','n','g','i','n','e','M','i','n','o','r','V','e','r','s','i','o','n',0}; +static const WCHAR ScriptEngineBuildVersionW[] = + {'S','c','r','i','p','t','E','n','g','i','n','e','B','u','i','l','d','V','e','r','s','i','o','n',0}; +static const WCHAR CollectGarbageW[] = {'C','o','l','l','e','c','t','G','a','r','b','a','g','e',0}; +static const WCHAR MathW[] = {'M','a','t','h',0}; + +static HRESULT constructor_call(DispatchEx *constr, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + if(flags != DISPATCH_PROPERTYGET) + return jsdisp_call_value(constr, lcid, flags, dp, retv, ei, sp); + + V_VT(retv) = VT_DISPATCH; + V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(constr); + IDispatchEx_AddRef(_IDispatchEx_(constr)); + return S_OK; +} + +static HRESULT JSGlobal_NaN(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_Infinity(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_Array(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + TRACE("\n"); + + return constructor_call(dispex->ctx->array_constr, lcid, flags, dp, retv, ei, sp); +} + +static HRESULT JSGlobal_Boolean(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + TRACE("\n"); + + return constructor_call(dispex->ctx->bool_constr, lcid, flags, dp, retv, ei, sp); +} + +static HRESULT JSGlobal_Date(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_Function(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + TRACE("\n"); + + return constructor_call(dispex->ctx->function_constr, lcid, flags, dp, retv, ei, sp); +} + +static HRESULT JSGlobal_Number(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + TRACE("\n"); + + return constructor_call(dispex->ctx->number_constr, lcid, flags, dp, retv, ei, sp); +} + +static HRESULT JSGlobal_Object(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + TRACE("\n"); + + return constructor_call(dispex->ctx->object_constr, lcid, flags, dp, retv, ei, sp); +} + +static HRESULT JSGlobal_String(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + TRACE("\n"); + + return constructor_call(dispex->ctx->string_constr, lcid, flags, dp, retv, ei, sp); +} + +static HRESULT JSGlobal_RegExp(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + TRACE("\n"); + + return constructor_call(dispex->ctx->regexp_constr, lcid, flags, dp, retv, ei, sp); +} + +static HRESULT JSGlobal_ActiveXObject(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_VBArray(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_Enumerator(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_escape(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +/* ECMA-262 3rd Edition 15.1.2.1 */ +static HRESULT JSGlobal_eval(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + parser_ctx_t *parser_ctx; + VARIANT *arg; + HRESULT hres; + + TRACE("\n"); + + if(!arg_cnt(dp)) { + if(retv) + V_VT(retv) = VT_EMPTY; + return S_OK; + } + + arg = get_arg(dp, 0); + if(V_VT(arg) != VT_BSTR) { + if(retv) { + V_VT(retv) = VT_EMPTY; + return VariantCopy(retv, arg); + } + return S_OK; + } + + if(!dispex->ctx->exec_ctx) { + FIXME("No active exec_ctx\n"); + return E_UNEXPECTED; + } + + TRACE("parsing %s\n", debugstr_w(V_BSTR(arg))); + hres = script_parse(dispex->ctx, V_BSTR(arg), &parser_ctx); + if(FAILED(hres)) { + FIXME("parse failed: %08x\n", hres); + return hres; + } + + hres = exec_source(dispex->ctx->exec_ctx, parser_ctx, parser_ctx->source, ei, retv); + parser_release(parser_ctx); + + return hres; +} + +static HRESULT JSGlobal_isNaN(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_isFinite(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_parseInt(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + return E_NOTIMPL; +} + +static HRESULT JSGlobal_parseFloat(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_unescape(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_GetObject(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_ScriptEngine(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_ScriptEngineMajorVersion(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_ScriptEngineMinorVersion(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_ScriptEngineBuildVersion(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT JSGlobal_CollectGarbage(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static const builtin_prop_t JSGlobal_props[] = { + {ActiveXObjectW, JSGlobal_ActiveXObject, PROPF_METHOD}, + {ArrayW, JSGlobal_Array, PROPF_CONSTR}, + {BooleanW, JSGlobal_Boolean, PROPF_CONSTR}, + {CollectGarbageW, JSGlobal_CollectGarbage, PROPF_METHOD}, + {DateW, JSGlobal_Date, PROPF_CONSTR}, + {EnumeratorW, JSGlobal_Enumerator, PROPF_METHOD}, + {FunctionW, JSGlobal_Function, PROPF_CONSTR}, + {getObjectW, JSGlobal_GetObject, PROPF_METHOD}, + {InfinityW, JSGlobal_Infinity, 0}, +/* {MathW, JSGlobal_Math, 0}, */ + {NaNW, JSGlobal_NaN, 0}, + {NumberW, JSGlobal_Number, PROPF_CONSTR}, + {ObjectW, JSGlobal_Object, PROPF_CONSTR}, + {RegExpW, JSGlobal_RegExp, PROPF_CONSTR}, + {ScriptEngineW, JSGlobal_ScriptEngine, PROPF_METHOD}, + {ScriptEngineBuildVersionW, JSGlobal_ScriptEngineBuildVersion, PROPF_METHOD}, + {ScriptEngineMajorVersionW, JSGlobal_ScriptEngineMajorVersion, PROPF_METHOD}, + {ScriptEngineMinorVersionW, JSGlobal_ScriptEngineMinorVersion, PROPF_METHOD}, + {StringW, JSGlobal_String, PROPF_CONSTR}, + {VBArrayW, JSGlobal_VBArray, PROPF_METHOD}, + {escapeW, JSGlobal_escape, PROPF_METHOD}, + {evalW, JSGlobal_eval, PROPF_METHOD|1}, + {isFiniteW, JSGlobal_isFinite, PROPF_METHOD}, + {isNaNW, JSGlobal_isNaN, PROPF_METHOD}, + {parseFloatW, JSGlobal_parseFloat, PROPF_METHOD}, + {parseIntW, JSGlobal_parseInt, PROPF_METHOD|2}, + {unescapeW, JSGlobal_unescape, PROPF_METHOD} +}; + +static const builtin_info_t JSGlobal_info = { + JSCLASS_GLOBAL, + {NULL, NULL, 0}, + sizeof(JSGlobal_props)/sizeof(*JSGlobal_props), + JSGlobal_props, + NULL, + NULL +}; + +static HRESULT init_constructors(script_ctx_t *ctx) +{ + HRESULT hres; + + hres = init_function_constr(ctx); + if(FAILED(hres)) + return hres; + + hres = create_array_constr(ctx, &ctx->array_constr); + if(FAILED(hres)) + return hres; + + hres = create_bool_constr(ctx, &ctx->bool_constr); + if(FAILED(hres)) + return hres; + + hres = create_number_constr(ctx, &ctx->number_constr); + if(FAILED(hres)) + return hres; + + hres = create_object_constr(ctx, &ctx->object_constr); + if(FAILED(hres)) + return hres; + + hres = create_object_constr(ctx, &ctx->regexp_constr); + if(FAILED(hres)) + return hres; + + hres = create_string_constr(ctx, &ctx->string_constr); + if(FAILED(hres)) + return hres; + + return S_OK; +} + +HRESULT init_global(script_ctx_t *ctx) +{ + DispatchEx *math; + VARIANT var; + HRESULT hres; + + if(ctx->global) + return S_OK; + + hres = init_constructors(ctx); + if(FAILED(hres)) + return hres; + + hres = create_dispex(ctx, &JSGlobal_info, NULL, &ctx->global); + if(FAILED(hres)) + return hres; + + hres = create_math(ctx, &math); + if(FAILED(hres)) + return hres; + + V_VT(&var) = VT_DISPATCH; + V_DISPATCH(&var) = (IDispatch*)_IDispatchEx_(math); + hres = jsdisp_propput_name(ctx->global, MathW, ctx->lcid, &var, NULL/*FIXME*/, NULL/*FIXME*/); + jsdisp_release(math); + + return hres; +} diff --git a/reactos/dll/win32/jscript/jscript.c b/reactos/dll/win32/jscript/jscript.c index 2bd9f12f40a..7c7def07468 100644 --- a/reactos/dll/win32/jscript/jscript.c +++ b/reactos/dll/win32/jscript/jscript.c @@ -54,6 +54,7 @@ void script_release(script_ctx_t *ctx) if(--ctx->ref) return; + jsheap_free(&ctx->tmp_heap); heap_free(ctx); } @@ -80,7 +81,7 @@ static HRESULT exec_global_code(JScript *This, parser_ctx_t *parser_ctx) VARIANT var; HRESULT hres; - hres = create_exec_ctx(&exec_ctx); + hres = create_exec_ctx((IDispatch*)_IDispatchEx_(This->ctx->script_disp), This->ctx->script_disp, NULL, &exec_ctx); if(FAILED(hres)) return hres; @@ -216,7 +217,13 @@ static HRESULT WINAPI JScript_SetScriptSite(IActiveScript *iface, return hres; } - hres = create_dispex(This->ctx, &This->ctx->script_disp); + if(!This->ctx->script_disp) { + hres = create_dispex(This->ctx, NULL, NULL, &This->ctx->script_disp); + if(FAILED(hres)) + return hres; + } + + hres = init_global(This->ctx); if(FAILED(hres)) return hres; @@ -299,6 +306,21 @@ static HRESULT WINAPI JScript_Close(IActiveScript *iface) clear_script_queue(This); + if(This->ctx->named_items) { + named_item_t *iter, *iter2; + + iter = This->ctx->named_items; + while(iter) { + iter2 = iter->next; + + IDispatch_Release(iter->disp); + heap_free(iter); + iter = iter2; + } + + This->ctx->named_items = NULL; + } + if(This->ctx) { change_state(This, SCRIPTSTATE_CLOSED); @@ -306,6 +328,11 @@ static HRESULT WINAPI JScript_Close(IActiveScript *iface) IDispatchEx_Release(_IDispatchEx_(This->ctx->script_disp)); This->ctx->script_disp = NULL; } + + if(This->ctx->global) { + IDispatchEx_Release(_IDispatchEx_(This->ctx->global)); + This->ctx->global = NULL; + } } if(This->site) { @@ -320,8 +347,41 @@ static HRESULT WINAPI JScript_AddNamedItem(IActiveScript *iface, LPCOLESTR pstrName, DWORD dwFlags) { JScript *This = ACTSCRIPT_THIS(iface); - FIXME("(%p)->(%s %x)\n", This, debugstr_w(pstrName), dwFlags); - return E_NOTIMPL; + named_item_t *item; + IDispatch *disp; + IUnknown *unk; + HRESULT hres; + + TRACE("(%p)->(%s %x)\n", This, debugstr_w(pstrName), dwFlags); + + if(This->thread_id != GetCurrentThreadId() || !This->ctx || This->ctx->state == SCRIPTSTATE_CLOSED) + return E_UNEXPECTED; + + 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; + } + + item = heap_alloc(sizeof(*item)); + if(!item) { + IDispatch_Release(disp); + return E_OUTOFMEMORY; + } + + item->disp = disp; + item->flags = dwFlags; + item->next = This->ctx->named_items; + This->ctx->named_items = item; + + return S_OK; } static HRESULT WINAPI JScript_AddTypeLib(IActiveScript *iface, REFGUID rguidTypeLib, @@ -448,6 +508,7 @@ static HRESULT WINAPI JScriptParse_InitNew(IActiveScriptParse *iface) ctx->ref = 1; ctx->state = SCRIPTSTATE_UNINITIALIZED; + jsheap_init(&ctx->tmp_heap); ctx = InterlockedCompareExchangePointer((void**)&This->ctx, ctx, NULL); if(ctx) { @@ -517,7 +578,7 @@ static const IActiveScriptParseVtbl JScriptParseVtbl = { JScriptParse_ParseScriptText }; -#define ASPARSEPROC_THIS(iface) DEFINE_THIS(JScript, IActiveScriptParse, iface) +#define ASPARSEPROC_THIS(iface) DEFINE_THIS(JScript, IActiveScriptParseProcedure2, iface) static HRESULT WINAPI JScriptParseProcedure_QueryInterface(IActiveScriptParseProcedure2 *iface, REFIID riid, void **ppv) { @@ -543,8 +604,30 @@ static HRESULT WINAPI JScriptParseProcedure_ParseProcedureText(IActiveScriptPars DWORD dwSourceContextCookie, ULONG ulStartingLineNumber, DWORD dwFlags, IDispatch **ppdisp) { JScript *This = ASPARSEPROC_THIS(iface); - FIXME("(%p)->()\n", This); - return E_NOTIMPL; + parser_ctx_t *parser_ctx; + DispatchEx *dispex; + HRESULT hres; + + TRACE("(%p)->(%s %s %s %s %p %s %x %u %x %p)\n", This, debugstr_w(pstrCode), debugstr_w(pstrFormalParams), + debugstr_w(pstrProcedureName), debugstr_w(pstrItemName), punkContext, debugstr_w(pstrDelimiter), + dwSourceContextCookie, ulStartingLineNumber, dwFlags, ppdisp); + + if(This->thread_id != GetCurrentThreadId() || This->ctx->state == SCRIPTSTATE_CLOSED) + return E_UNEXPECTED; + + hres = script_parse(This->ctx, pstrCode, &parser_ctx); + if(FAILED(hres)) { + WARN("Parse failed %08x\n", hres); + return hres; + } + + hres = create_source_function(parser_ctx, NULL, parser_ctx->source, NULL, &dispex); + parser_release(parser_ctx); + if(FAILED(hres)) + return hres; + + *ppdisp = (IDispatch*)_IDispatchEx_(dispex); + return S_OK; } #undef ASPARSEPROC_THIS diff --git a/reactos/dll/win32/jscript/jscript.h b/reactos/dll/win32/jscript/jscript.h index 0fff0e89bab..6c4e2723b80 100644 --- a/reactos/dll/win32/jscript/jscript.h +++ b/reactos/dll/win32/jscript/jscript.h @@ -33,32 +33,145 @@ typedef struct _script_ctx_t script_ctx_t; typedef struct _exec_ctx_t exec_ctx_t; +typedef struct _dispex_prop_t dispex_prop_t; typedef struct { EXCEPINFO ei; VARIANT var; } jsexcept_t; -typedef struct DispatchEx { +typedef struct { + void **blocks; + DWORD block_cnt; + DWORD last_block; + DWORD offset; + BOOL mark; + struct list custom_blocks; +} jsheap_t; + +void jsheap_init(jsheap_t*); +void *jsheap_alloc(jsheap_t*,DWORD); +void *jsheap_grow(jsheap_t*,void*,DWORD,DWORD); +void jsheap_clear(jsheap_t*); +void jsheap_free(jsheap_t*); +jsheap_t *jsheap_mark(jsheap_t*); + +typedef struct DispatchEx DispatchEx; + +#define PROPF_ARGMASK 0x00ff +#define PROPF_METHOD 0x0100 +#define PROPF_ENUM 0x0200 +#define PROPF_CONSTR 0x0400 + +typedef enum { + JSCLASS_NONE, + JSCLASS_ARRAY, + JSCLASS_BOOLEAN, + JSCLASS_FUNCTION, + JSCLASS_GLOBAL, + JSCLASS_MATH, + JSCLASS_NUMBER, + JSCLASS_OBJECT, + JSCLASS_REGEXP, + JSCLASS_STRING +} jsclass_t; + +typedef HRESULT (*builtin_invoke_t)(DispatchEx*,LCID,WORD,DISPPARAMS*,VARIANT*,jsexcept_t*,IServiceProvider*); + +typedef struct { + const WCHAR *name; + builtin_invoke_t invoke; + DWORD flags; +} builtin_prop_t; + +typedef struct { + jsclass_t class; + builtin_prop_t value_prop; + DWORD props_cnt; + const builtin_prop_t *props; + void (*destructor)(DispatchEx*); + void (*on_put)(DispatchEx*,const WCHAR*); +} builtin_info_t; + +struct DispatchEx { const IDispatchExVtbl *lpIDispatchExVtbl; LONG ref; + DWORD buf_size; + DWORD prop_cnt; + dispex_prop_t *props; script_ctx_t *ctx; -} DispatchEx; + + DispatchEx *prototype; + + const builtin_info_t *builtin_info; +}; #define _IDispatchEx_(x) ((IDispatchEx*) &(x)->lpIDispatchExVtbl) -HRESULT create_dispex(script_ctx_t*,DispatchEx**); +static inline void jsdisp_release(DispatchEx *jsdisp) +{ + IDispatchEx_Release(_IDispatchEx_(jsdisp)); +} + +HRESULT create_dispex(script_ctx_t*,const builtin_info_t*,DispatchEx*,DispatchEx**); +HRESULT init_dispex(DispatchEx*,script_ctx_t*,const builtin_info_t*,DispatchEx*); +HRESULT init_dispex_from_constr(DispatchEx*,script_ctx_t*,const builtin_info_t*,DispatchEx*); +DispatchEx *iface_to_jsdisp(IUnknown*); + +HRESULT disp_call(IDispatch*,DISPID,LCID,WORD,DISPPARAMS*,VARIANT*,jsexcept_t*,IServiceProvider*); +HRESULT jsdisp_call_value(DispatchEx*,LCID,WORD,DISPPARAMS*,VARIANT*,jsexcept_t*,IServiceProvider*); +HRESULT disp_propget(IDispatch*,DISPID,LCID,VARIANT*,jsexcept_t*,IServiceProvider*); +HRESULT disp_propput(IDispatch*,DISPID,LCID,VARIANT*,jsexcept_t*,IServiceProvider*); +HRESULT jsdisp_propput_name(DispatchEx*,const WCHAR*,LCID,VARIANT*,jsexcept_t*,IServiceProvider*); +HRESULT jsdisp_propput_idx(DispatchEx*,DWORD,LCID,VARIANT*,jsexcept_t*,IServiceProvider*); + +HRESULT create_builtin_function(script_ctx_t*,builtin_invoke_t,DWORD,DispatchEx*,DispatchEx**); + +HRESULT create_object(script_ctx_t*,DispatchEx*,DispatchEx**); +HRESULT create_math(script_ctx_t*,DispatchEx**); +HRESULT create_array(script_ctx_t*,DWORD,DispatchEx**); +HRESULT create_regexp_str(script_ctx_t*,const WCHAR*,DWORD,const WCHAR*,DWORD,DispatchEx**); +HRESULT create_string(script_ctx_t*,const WCHAR*,DWORD,DispatchEx**); +HRESULT create_bool(script_ctx_t*,VARIANT_BOOL,DispatchEx**); +HRESULT create_number(script_ctx_t*,VARIANT*,DispatchEx**); + +HRESULT to_primitive(script_ctx_t*,VARIANT*,jsexcept_t*,VARIANT*); +HRESULT to_boolean(VARIANT*,VARIANT_BOOL*); +HRESULT to_number(script_ctx_t*,VARIANT*,jsexcept_t*,VARIANT*); +HRESULT to_integer(script_ctx_t*,VARIANT*,jsexcept_t*,VARIANT*); +HRESULT to_int32(script_ctx_t*,VARIANT*,jsexcept_t*,INT*); +HRESULT to_uint32(script_ctx_t*,VARIANT*,jsexcept_t*,DWORD*); +HRESULT to_string(script_ctx_t*,VARIANT*,jsexcept_t*,BSTR*); +HRESULT to_object(exec_ctx_t*,VARIANT*,IDispatch**); + +typedef struct named_item_t { + IDispatch *disp; + DWORD flags; + + struct named_item_t *next; +} named_item_t; struct _script_ctx_t { LONG ref; SCRIPTSTATE state; exec_ctx_t *exec_ctx; + named_item_t *named_items; LCID lcid; + jsheap_t tmp_heap; + DispatchEx *script_disp; + DispatchEx *global; + DispatchEx *function_constr; + DispatchEx *array_constr; + DispatchEx *bool_constr; + DispatchEx *number_constr; + DispatchEx *object_constr; + DispatchEx *regexp_constr; + DispatchEx *string_constr; }; void script_release(script_ctx_t*); @@ -68,20 +181,47 @@ static inline void script_addref(script_ctx_t *ctx) ctx->ref++; } -HRESULT WINAPI JScriptFactory_CreateInstance(IClassFactory*,IUnknown*,REFIID,void**); +HRESULT init_global(script_ctx_t*); +HRESULT init_function_constr(script_ctx_t*); + +HRESULT create_array_constr(script_ctx_t*,DispatchEx**); +HRESULT create_bool_constr(script_ctx_t*,DispatchEx**); +HRESULT create_number_constr(script_ctx_t*,DispatchEx**); +HRESULT create_object_constr(script_ctx_t*,DispatchEx**); +HRESULT create_regexp_constr(script_ctx_t*,DispatchEx**); +HRESULT create_string_constr(script_ctx_t*,DispatchEx**); typedef struct { - void **blocks; - DWORD block_cnt; - DWORD last_block; - DWORD offset; - struct list custom_blocks; -} jsheap_t; + const WCHAR *str; + DWORD len; +} match_result_t; -void jsheap_init(jsheap_t*); -void *jsheap_alloc(jsheap_t*,DWORD); -void jsheap_clear(jsheap_t*); -void jsheap_free(jsheap_t*); +HRESULT regexp_match(DispatchEx*,const WCHAR*,DWORD,BOOL,match_result_t**,DWORD*); + +static inline VARIANT *get_arg(DISPPARAMS *dp, DWORD i) +{ + return dp->rgvarg + dp->cArgs-i-1; +} + +static inline DWORD arg_cnt(const DISPPARAMS *dp) +{ + return dp->cArgs - dp->cNamedArgs; +} + +static inline void num_set_val(VARIANT *v, DOUBLE d) +{ + if(d == (DOUBLE)(INT)d) { + V_VT(v) = VT_I4; + V_I4(v) = d; + }else { + V_VT(v) = VT_R8; + V_R8(v) = d; + } +} + +const char *debugstr_variant(const VARIANT*); + +HRESULT WINAPI JScriptFactory_CreateInstance(IClassFactory*,IUnknown*,REFIID,void**); extern LONG module_ref; @@ -115,4 +255,19 @@ 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); + memcpy(ret, str, size); + } + + return ret; +} + #define DEFINE_THIS(cls,ifc,iface) ((cls*)((BYTE*)(iface)-offsetof(cls,lp ## ifc ## Vtbl))) diff --git a/reactos/dll/win32/jscript/jscript.rbuild b/reactos/dll/win32/jscript/jscript.rbuild index cc383c89896..b9308743f38 100644 --- a/reactos/dll/win32/jscript/jscript.rbuild +++ b/reactos/dll/win32/jscript/jscript.rbuild @@ -1,5 +1,5 @@ - + . @@ -21,6 +21,15 @@ jsutils.c lex.c parser.tab.c + math.c + number.c + object.c + regexp.c + string.c + array.c + bool.c + function.c + global.c rsrc.rc jscript.spec diff --git a/reactos/dll/win32/jscript/jscript_main.c b/reactos/dll/win32/jscript/jscript_main.c index 045c96ef0c4..37266e5773c 100644 --- a/reactos/dll/win32/jscript/jscript_main.c +++ b/reactos/dll/win32/jscript/jscript_main.c @@ -38,6 +38,8 @@ static const CLSID CLSID_JScriptAuthor = static const CLSID CLSID_JScriptEncode = {0xf414c262,0x6ac0,0x11cf,{0xb6,0xd1,0x00,0xaa,0x00,0xbb,0xbb,0x58}}; +DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); + static HINSTANCE jscript_hinstance; static HRESULT WINAPI ClassFactory_QueryInterface(IClassFactory *iface, REFIID riid, void **ppv) diff --git a/reactos/dll/win32/jscript/jsutils.c b/reactos/dll/win32/jscript/jsutils.c index 4f84e886b98..f368c3efcaa 100644 --- a/reactos/dll/win32/jscript/jsutils.c +++ b/reactos/dll/win32/jscript/jsutils.c @@ -16,12 +16,37 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ +#include + #include "jscript.h" +#include "engine.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(jscript); +const char *debugstr_variant(const VARIANT *v) +{ + switch(V_VT(v)) { + case VT_EMPTY: + return wine_dbg_sprintf("{VT_EMPTY}"); + case VT_NULL: + return wine_dbg_sprintf("{VT_NULL}"); + case VT_I4: + return wine_dbg_sprintf("{VT_I4: %d}", V_I4(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 static inline DWORD block_size(DWORD block) @@ -88,14 +113,30 @@ void *jsheap_alloc(jsheap_t *heap, DWORD size) return list+1; } +void *jsheap_grow(jsheap_t *heap, void *mem, DWORD size, DWORD inc) +{ + if(mem == (BYTE*)heap->blocks[heap->last_block] + heap->offset-size + && heap->offset+inc < block_size(heap->last_block)) { + heap->offset += inc; + return mem; + } + + return jsheap_alloc(heap, size+inc); +} + void jsheap_clear(jsheap_t *heap) { struct list *tmp; + if(!heap) + return; + while((tmp = list_next(&heap->custom_blocks, &heap->custom_blocks))) { list_remove(tmp); heap_free(tmp); } + + heap->last_block = heap->offset = 0; } void jsheap_free(jsheap_t *heap) @@ -110,3 +151,253 @@ void jsheap_free(jsheap_t *heap) jsheap_init(heap); } + +jsheap_t *jsheap_mark(jsheap_t *heap) +{ + if(heap->mark) + return NULL; + + heap->mark = TRUE; + return heap; +} + +/* ECMA-262 3rd Edition 9.1 */ +HRESULT to_primitive(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret) +{ + switch(V_VT(v)) { + case VT_EMPTY: + case VT_NULL: + case VT_BOOL: + case VT_I4: + case VT_R8: + *ret = *v; + break; + case VT_BSTR: + V_VT(ret) = VT_BSTR; + V_BSTR(ret) = SysAllocString(V_BSTR(v)); + break; + case VT_DISPATCH: + return disp_propget(V_DISPATCH(v), DISPID_VALUE, ctx->lcid, ret, ei, NULL /*FIXME*/); + default: + FIXME("Unimplemented for vt %d\n", V_VT(v)); + return E_NOTIMPL; + } + + return S_OK; +} + +/* ECMA-262 3rd Edition 9.2 */ +HRESULT to_boolean(VARIANT *v, VARIANT_BOOL *b) +{ + switch(V_VT(v)) { + case VT_EMPTY: + case VT_NULL: + *b = VARIANT_FALSE; + break; + case VT_I4: + *b = V_I4(v) ? VARIANT_TRUE : VARIANT_FALSE; + break; + case VT_R8: + *b = V_R8(v) ? VARIANT_TRUE : VARIANT_FALSE; + break; + case VT_BSTR: + *b = V_BSTR(v) && *V_BSTR(v) ? VARIANT_TRUE : VARIANT_FALSE; + break; + case VT_DISPATCH: + *b = V_DISPATCH(v) ? VARIANT_TRUE : VARIANT_FALSE; + break; + case VT_BOOL: + *b = V_BOOL(v); + break; + default: + FIXME("unimplemented for vt %d\n", V_VT(v)); + return E_NOTIMPL; + } + + return S_OK; +} + +/* ECMA-262 3rd Edition 9.3 */ +HRESULT to_number(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret) +{ + switch(V_VT(v)) { + case VT_NULL: + V_VT(ret) = VT_I4; + V_I4(ret) = 0; + break; + case VT_I4: + case VT_R8: + *ret = *v; + break; + case VT_BOOL: + V_VT(ret) = VT_I4; + V_I4(ret) = V_BOOL(v) ? 1 : 0; + break; + default: + FIXME("unimplemented for vt %d\n", V_VT(v)); + return E_NOTIMPL; + } + + return S_OK; +} + +/* ECMA-262 3rd Edition 9.4 */ +HRESULT to_integer(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, VARIANT *ret) +{ + VARIANT num; + HRESULT hres; + + hres = to_number(ctx, v, ei, &num); + if(FAILED(hres)) + return hres; + + if(V_VT(&num) == VT_I4) + *ret = *v; + else + num_set_val(ret, V_R8(&num) >= 0.0 ? floor(V_R8(&num)) : -floor(-V_R8(&num))); + + return S_OK; +} + +/* ECMA-262 3rd Edition 9.5 */ +HRESULT to_int32(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, INT *ret) +{ + VARIANT num; + HRESULT hres; + + hres = to_number(ctx, v, ei, &num); + if(FAILED(hres)) + return hres; + + *ret = V_VT(&num) == VT_I4 ? V_I4(&num) : (INT)V_R8(&num); + return S_OK; +} + +/* ECMA-262 3rd Edition 9.6 */ +HRESULT to_uint32(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, DWORD *ret) +{ + VARIANT num; + HRESULT hres; + + hres = to_number(ctx, v, ei, &num); + if(FAILED(hres)) + return hres; + + *ret = V_VT(&num) == VT_I4 ? V_I4(&num) : (DWORD)V_R8(&num); + return S_OK; +} + +static BSTR int_to_bstr(INT i) +{ + WCHAR buf[12], *p; + BOOL neg = FALSE; + + if(!i) { + static const WCHAR zeroW[] = {'0',0}; + return SysAllocString(zeroW); + } + + if(i < 0) { + neg = TRUE; + i = -i; + } + + p = buf + sizeof(buf)/sizeof(*buf)-1; + *p-- = 0; + while(i) { + *p-- = i%10 + '0'; + i /= 10; + } + + if(neg) + *p = '-'; + else + p++; + + return SysAllocString(p); +} + +/* ECMA-262 3rd Edition 9.8 */ +HRESULT to_string(script_ctx_t *ctx, VARIANT *v, jsexcept_t *ei, BSTR *str) +{ + const WCHAR undefinedW[] = {'u','n','d','e','f','i','n','e','d',0}; + const WCHAR nullW[] = {'n','u','l','l',0}; + const WCHAR trueW[] = {'t','r','u','e',0}; + const WCHAR falseW[] = {'f','a','l','s','e',0}; + + switch(V_VT(v)) { + case VT_EMPTY: + *str = SysAllocString(undefinedW); + break; + case VT_NULL: + *str = SysAllocString(nullW); + break; + case VT_I4: + *str = int_to_bstr(V_I4(v)); + break; + case VT_BSTR: + *str = SysAllocString(V_BSTR(v)); + break; + case VT_DISPATCH: { + VARIANT prim; + HRESULT hres; + + hres = to_primitive(ctx, v, ei, &prim); + if(FAILED(hres)) + return hres; + + hres = to_string(ctx, &prim, ei, str); + VariantClear(&prim); + return hres; + } + case VT_BOOL: + *str = SysAllocString(V_BOOL(v) ? trueW : falseW); + break; + default: + FIXME("unsupported vt %d\n", V_VT(v)); + return E_NOTIMPL; + } + + return *str ? S_OK : E_OUTOFMEMORY; +} + +/* ECMA-262 3rd Edition 9.9 */ +HRESULT to_object(exec_ctx_t *ctx, VARIANT *v, IDispatch **disp) +{ + DispatchEx *dispex; + HRESULT hres; + + switch(V_VT(v)) { + case VT_BSTR: + hres = create_string(ctx->parser->script, V_BSTR(v), SysStringLen(V_BSTR(v)), &dispex); + if(FAILED(hres)) + return hres; + + *disp = (IDispatch*)_IDispatchEx_(dispex); + break; + case VT_I4: + case VT_R8: + hres = create_number(ctx->parser->script, v, &dispex); + if(FAILED(hres)) + return hres; + + *disp = (IDispatch*)_IDispatchEx_(dispex); + break; + case VT_DISPATCH: + IDispatch_AddRef(V_DISPATCH(v)); + *disp = V_DISPATCH(v); + break; + case VT_BOOL: + hres = create_bool(ctx->parser->script, V_BOOL(v), &dispex); + if(FAILED(hres)) + return hres; + + *disp = (IDispatch*)_IDispatchEx_(dispex); + break; + default: + FIXME("unsupported vt %d\n", V_VT(v)); + return E_NOTIMPL; + } + + return S_OK; +} diff --git a/reactos/dll/win32/jscript/lex.c b/reactos/dll/win32/jscript/lex.c index d482999bc3a..ed73202a5ab 100644 --- a/reactos/dll/win32/jscript/lex.c +++ b/reactos/dll/win32/jscript/lex.c @@ -23,7 +23,6 @@ #include "objsafe.h" #include "engine.h" -#define YYSTYPE #include "parser.tab.h" #include "wine/debug.h" @@ -667,7 +666,7 @@ int parser_lex(void *lval, parser_ctx_t *ctx) if(++ctx->ptr < ctx->end) { if(*ctx->ptr == '=') { /* /= */ ctx->ptr++; - *(int*)lval = EXPR_ASSIGNMUL; + *(int*)lval = EXPR_ASSIGNDIV; return tAssignOper; } } @@ -685,3 +684,49 @@ int parser_lex(void *lval, parser_ctx_t *ctx) WARN("unexpected char '%c' %d\n", *ctx->ptr, *ctx->ptr); return 0; } + +static void add_object_literal(parser_ctx_t *ctx, DispatchEx *obj) +{ + obj_literal_t *literal = parser_alloc(ctx, sizeof(obj_literal_t)); + + literal->obj = obj; + literal->next = ctx->obj_literals; + ctx->obj_literals = literal; +} + +literal_t *parse_regexp(parser_ctx_t *ctx) +{ + const WCHAR *re, *flags; + DispatchEx *regexp; + literal_t *ret; + DWORD re_len; + HRESULT hres; + + TRACE("\n"); + + re = ctx->ptr; + while(ctx->ptr < ctx->end && (*ctx->ptr != '/' || *(ctx->ptr-1) == '\\')) + ctx->ptr++; + + if(ctx->ptr == ctx->end) { + WARN("unexpected end of file\n"); + return NULL; + } + + re_len = ctx->ptr-re; + + flags = ++ctx->ptr; + while(ctx->ptr < ctx->end && isalnumW(*ctx->ptr)) + ctx->ptr++; + + hres = create_regexp_str(ctx->script, re, re_len, flags, ctx->ptr-flags, ®exp); + if(FAILED(hres)) + return NULL; + + add_object_literal(ctx, regexp); + + ret = parser_alloc(ctx, sizeof(literal_t)); + ret->vt = VT_DISPATCH; + ret->u.disp = (IDispatch*)_IDispatchEx_(regexp); + return ret; +} diff --git a/reactos/dll/win32/jscript/math.c b/reactos/dll/win32/jscript/math.c new file mode 100644 index 00000000000..8454cfa87e3 --- /dev/null +++ b/reactos/dll/win32/jscript/math.c @@ -0,0 +1,275 @@ +/* + * 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 + */ + +#include "jscript.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(jscript); + +static const WCHAR EW[] = {'E',0}; +static const WCHAR LOG2EW[] = {'L','O','G','2','E',0}; +static const WCHAR LOG10EW[] = {'L','O','G','1','0',0}; +static const WCHAR LN2W[] = {'L','N','2',0}; +static const WCHAR LN10W[] = {'L','N','1','0',0}; +static const WCHAR PIW[] = {'P','I',0}; +static const WCHAR SQRT2W[] = {'S','Q','R','T','2',0}; +static const WCHAR SQRT1_2W[] = {'S','Q','R','T','1','_','2',0}; +static const WCHAR absW[] = {'a','b','s',0}; +static const WCHAR acosW[] = {'a','c','o','s',0}; +static const WCHAR asinW[] = {'a','s','i','n',0}; +static const WCHAR atanW[] = {'a','t','a','n',0}; +static const WCHAR atan2W[] = {'a','t','a','n','2',0}; +static const WCHAR ceilW[] = {'c','e','i','l',0}; +static const WCHAR cosW[] = {'c','o','s',0}; +static const WCHAR expW[] = {'e','x','p',0}; +static const WCHAR floorW[] = {'f','l','o','o','r',0}; +static const WCHAR logW[] = {'l','o','g',0}; +static const WCHAR maxW[] = {'m','a','x',0}; +static const WCHAR minW[] = {'m','i','n',0}; +static const WCHAR powW[] = {'p','o','w',0}; +static const WCHAR randomW[] = {'r','a','n','d','o','m',0}; +static const WCHAR roundW[] = {'r','o','u','n','d',0}; +static const WCHAR sinW[] = {'s','i','n',0}; +static const WCHAR sqrtW[] = {'s','q','r','t',0}; +static const WCHAR tanW[] = {'t','a','n',0}; + +static HRESULT Math_E(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_LOG2E(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_LOG10E(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_LN2(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_LN10(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_PI(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_SQRT2(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_SQRT1_2(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_abs(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_acos(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_asin(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_atan(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_atan2(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_ceil(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_cos(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_exp(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_floor(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_log(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_max(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_min(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_pow(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_random(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_round(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_sin(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_sqrt(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Math_tan(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static const builtin_prop_t Math_props[] = { + {EW, Math_E, 0}, + {LOG2EW, Math_LOG2E, 0}, + {LOG10EW, Math_LOG10E, 0}, + {LN2W, Math_LN2, 0}, + {LN10W, Math_LN10, 0}, + {PIW, Math_PI, 0}, + {SQRT1_2W, Math_SQRT1_2, 0}, + {SQRT2W, Math_SQRT2, 0}, + {absW, Math_abs, PROPF_METHOD}, + {acosW, Math_acos, PROPF_METHOD}, + {asinW, Math_asin, PROPF_METHOD}, + {atanW, Math_atan, PROPF_METHOD}, + {atan2W, Math_atan2, PROPF_METHOD}, + {ceilW, Math_ceil, PROPF_METHOD}, + {cosW, Math_cos, PROPF_METHOD}, + {expW, Math_exp, PROPF_METHOD}, + {floorW, Math_floor, PROPF_METHOD}, + {logW, Math_log, PROPF_METHOD}, + {maxW, Math_max, PROPF_METHOD}, + {minW, Math_min, PROPF_METHOD}, + {powW, Math_pow, PROPF_METHOD}, + {randomW, Math_random, PROPF_METHOD}, + {roundW, Math_round, PROPF_METHOD}, + {sinW, Math_sin, PROPF_METHOD}, + {sqrtW, Math_sqrt, PROPF_METHOD}, + {tanW, Math_tan, PROPF_METHOD} +}; + +static const builtin_info_t Math_info = { + JSCLASS_MATH, + {NULL, NULL, 0}, + sizeof(Math_props)/sizeof(*Math_props), + Math_props, + NULL, + NULL +}; + +HRESULT create_math(script_ctx_t *ctx, DispatchEx **ret) +{ + return create_dispex(ctx, &Math_info, NULL, ret); +} diff --git a/reactos/dll/win32/jscript/number.c b/reactos/dll/win32/jscript/number.c new file mode 100644 index 00000000000..108cfff1a14 --- /dev/null +++ b/reactos/dll/win32/jscript/number.c @@ -0,0 +1,184 @@ +/* + * 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 + */ + +#include "jscript.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(jscript); + +typedef struct { + DispatchEx dispex; + + VARIANT num; +} NumberInstance; + +static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0}; +static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0}; +static const WCHAR toFixedW[] = {'t','o','F','i','x','e','d',0}; +static const WCHAR toExponentialW[] = {'t','o','E','x','p','o','n','e','n','t','i','a','l',0}; +static const WCHAR toPrecisionW[] = {'t','o','P','r','e','c','i','s','i','o','n',0}; +static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0}; +static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0}; +static const WCHAR propertyIsEnumerableW[] = + {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0}; +static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0}; + +static HRESULT Number_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Number_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Number_toFixed(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Number_toExponential(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Number_toPrecision(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Number_valueOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Number_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Number_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Number_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Number_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static const builtin_prop_t Number_props[] = { + {hasOwnPropertyW, Number_hasOwnProperty, PROPF_METHOD}, + {isPrototypeOfW, Number_isPrototypeOf, PROPF_METHOD}, + {propertyIsEnumerableW, Number_propertyIsEnumerable, PROPF_METHOD}, + {toExponentialW, Number_toExponential, PROPF_METHOD}, + {toFixedW, Number_toFixed, PROPF_METHOD}, + {toLocaleStringW, Number_toLocaleString, PROPF_METHOD}, + {toPrecisionW, Number_toPrecision, PROPF_METHOD}, + {toStringW, Number_toString, PROPF_METHOD}, + {valueOfW, Number_valueOf, PROPF_METHOD} +}; + +static const builtin_info_t Number_info = { + JSCLASS_NUMBER, + {NULL, Number_value, 0}, + sizeof(Number_props)/sizeof(*Number_props), + Number_props, + NULL, + NULL +}; + +static HRESULT NumberConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT alloc_number(script_ctx_t *ctx, BOOL use_constr, NumberInstance **ret) +{ + NumberInstance *number = heap_alloc_zero(sizeof(NumberInstance)); + HRESULT hres; + + if(use_constr) + hres = init_dispex_from_constr(&number->dispex, ctx, &Number_info, ctx->number_constr); + else + hres = init_dispex(&number->dispex, ctx, &Number_info, NULL); + if(FAILED(hres)) + return hres; + + *ret = number; + return S_OK; +} + +HRESULT create_number_constr(script_ctx_t *ctx, DispatchEx **ret) +{ + NumberInstance *number; + HRESULT hres; + + hres = alloc_number(ctx, FALSE, &number); + if(FAILED(hres)) + return hres; + + hres = create_builtin_function(ctx, NumberConstr_value, PROPF_CONSTR, &number->dispex, ret); + + jsdisp_release(&number->dispex); + return hres; +} + +HRESULT create_number(script_ctx_t *ctx, VARIANT *num, DispatchEx **ret) +{ + NumberInstance *number; + HRESULT hres; + + hres = alloc_number(ctx, TRUE, &number); + if(FAILED(hres)) + return hres; + + number->num = *num; + + *ret = &number->dispex; + return S_OK; +} diff --git a/reactos/dll/win32/jscript/object.c b/reactos/dll/win32/jscript/object.c new file mode 100644 index 00000000000..2f021f7efa6 --- /dev/null +++ b/reactos/dll/win32/jscript/object.c @@ -0,0 +1,165 @@ +/* + * 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 + */ + +#include "jscript.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(jscript); + +static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0}; +static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0}; +static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0}; +static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0}; +static const WCHAR propertyIsEnumerableW[] = + {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0}; +static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0}; + +static HRESULT Object_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Object_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Object_valueOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Object_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Object_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Object_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT Object_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static void Object_destructor(DispatchEx *dispex) +{ + heap_free(dispex); +} + +static const builtin_prop_t Object_props[] = { + {hasOwnPropertyW, Object_hasOwnProperty, PROPF_METHOD}, + {isPrototypeOfW, Object_isPrototypeOf, PROPF_METHOD}, + {propertyIsEnumerableW, Object_propertyIsEnumerable, PROPF_METHOD}, + {toLocaleStringW, Object_toLocaleString, PROPF_METHOD}, + {toStringW, Object_toString, PROPF_METHOD}, + {valueOfW, Object_valueOf, PROPF_METHOD} +}; + +static const builtin_info_t Object_info = { + JSCLASS_OBJECT, + {NULL, Object_value, 0}, + sizeof(Object_props)/sizeof(*Object_props), + Object_props, + Object_destructor, + NULL +}; + +static HRESULT ObjectConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + HRESULT hres; + + TRACE("\n"); + + switch(flags) { + case DISPATCH_CONSTRUCT: { + DispatchEx *obj; + + hres = create_object(dispex->ctx, NULL, &obj); + if(FAILED(hres)) + return hres; + + V_VT(retv) = VT_DISPATCH; + V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(obj); + break; + } + + default: + FIXME("unimplemented flags: %x\n", flags); + return E_NOTIMPL; + } + + return S_OK; +} + +HRESULT create_object_constr(script_ctx_t *ctx, DispatchEx **ret) +{ + DispatchEx *object; + HRESULT hres; + + hres = create_dispex(ctx, &Object_info, NULL, &object); + if(FAILED(hres)) + return hres; + + hres = create_builtin_function(ctx, ObjectConstr_value, PROPF_CONSTR, object, ret); + + jsdisp_release(object); + return hres; +} + +HRESULT create_object(script_ctx_t *ctx, DispatchEx *constr, DispatchEx **ret) +{ + DispatchEx *object; + HRESULT hres; + + object = heap_alloc_zero(sizeof(DispatchEx)); + if(!object) + return E_OUTOFMEMORY; + + hres = init_dispex_from_constr(object, ctx, &Object_info, constr ? constr : ctx->object_constr); + if(FAILED(hres)) { + heap_free(object); + return hres; + } + + *ret = object; + return S_OK; +} diff --git a/reactos/dll/win32/jscript/parser.tab.c b/reactos/dll/win32/jscript/parser.tab.c index b1a12624f6b..f946a69ef88 100644 --- a/reactos/dll/win32/jscript/parser.tab.c +++ b/reactos/dll/win32/jscript/parser.tab.c @@ -169,7 +169,7 @@ static int parser_error(const char*); static BOOL allow_auto_semicolon(parser_ctx_t*); static void program_parsed(parser_ctx_t*,source_elements_t*); -typedef struct { +typedef struct _statement_list_t { statement_t *head; statement_t *tail; } statement_list_t; @@ -179,7 +179,7 @@ static literal_t *new_null_literal(parser_ctx_t*); static literal_t *new_undefined_literal(parser_ctx_t*); static literal_t *new_boolean_literal(parser_ctx_t*,VARIANT_BOOL); -typedef struct { +typedef struct _property_list_t { prop_val_t *head; prop_val_t *tail; } property_list_t; @@ -187,7 +187,7 @@ typedef struct { static property_list_t *new_property_list(parser_ctx_t*,literal_t*,expression_t*); static property_list_t *property_list_add(parser_ctx_t*,property_list_t*,literal_t*,expression_t*); -typedef struct { +typedef struct _element_list_t { array_element_t *head; array_element_t *tail; } element_list_t; @@ -195,7 +195,7 @@ typedef struct { static element_list_t *new_element_list(parser_ctx_t*,int,expression_t*); static element_list_t *element_list_add(parser_ctx_t*,element_list_t*,int,expression_t*); -typedef struct { +typedef struct _argument_list_t { argument_t *head; argument_t *tail; } argument_list_t; @@ -203,7 +203,7 @@ typedef struct { static argument_list_t *new_argument_list(parser_ctx_t*,expression_t*); static argument_list_t *argument_list_add(parser_ctx_t*,argument_list_t*,expression_t*); -typedef struct { +typedef struct _case_list_t { case_clausule_t *head; case_clausule_t *tail; } case_list_t; @@ -214,7 +214,7 @@ static case_list_t *new_case_list(parser_ctx_t*,case_clausule_t*); static case_list_t *case_list_add(parser_ctx_t*,case_list_t*,case_clausule_t*); static case_clausule_t *new_case_block(parser_ctx_t*,case_list_t*,case_clausule_t*,case_list_t*); -typedef struct { +typedef struct _variable_list_t { variable_declaration_t *head; variable_declaration_t *tail; } variable_list_t; @@ -249,7 +249,7 @@ struct statement_list_t { statement_list_t *new_statement_list(parser_ctx_t*,statement_t*); statement_list_t *statement_list_add(statement_list_t*,statement_t*); -typedef struct { +typedef struct _parameter_list_t { parameter_t *head; parameter_t *tail; } parameter_list_t; @@ -263,7 +263,7 @@ static expression_t *new_unary_expression(parser_ctx_t*,expression_type_t,expres static expression_t *new_conditional_expression(parser_ctx_t*,expression_t*,expression_t*,expression_t*); static expression_t *new_array_expression(parser_ctx_t*,expression_t*,expression_t*); static expression_t *new_member_expression(parser_ctx_t*,expression_t*,const WCHAR*); -static expression_t *new_member_new_expression(parser_ctx_t*,expression_t*,argument_list_t*); +static expression_t *new_new_expression(parser_ctx_t*,expression_t*,argument_list_t*); static expression_t *new_call_expression(parser_ctx_t*,expression_t*,argument_list_t*); static expression_t *new_this_expression(parser_ctx_t*); static expression_t *new_identifier_expression(parser_ctx_t*,const WCHAR*); @@ -302,20 +302,20 @@ typedef union YYSTYPE { int ival; LPCWSTR wstr; literal_t *literal; - argument_list_t *argument_list; + struct _argument_list_t *argument_list; case_clausule_t *case_clausule; - case_list_t *case_list; + struct _case_list_t *case_list; catch_block_t *catch_block; - element_list_t *element_list; + struct _element_list_t *element_list; expression_t *expr; const WCHAR *identifier; function_declaration_t *function_declaration; - parameter_list_t *parameter_list; - property_list_t *property_list; + struct _parameter_list_t *parameter_list; + struct _property_list_t *property_list; source_elements_t *source_elements; statement_t *statement; - statement_list_t *statement_list; - variable_list_t *variable_list; + struct _statement_list_t *statement_list; + struct _variable_list_t *variable_list; variable_declaration_t *variable_declaration; } YYSTYPE; /* Line 196 of yacc.c. */ @@ -652,7 +652,7 @@ static const unsigned short int yyrline[] = 706, 710, 711, 716, 717, 718, 719, 720, 721, 725, 726, 727, 732, 734, 739, 740, 744, 745, 749, 750, 755, 757, 762, 763, 764, 768, 769, 773, 774, 775, - 776, 777, 778, 782, 783, 786, 787 + 776, 777, 778, 783, 784, 787, 788 }; #endif @@ -2279,7 +2279,7 @@ yyreduce: case 106: #line 569 "parser.y" - { new_binary_expression(ctx, EXPR_BXOR, (yyvsp[-2].expr), (yyvsp[0].expr)); ;} + { (yyval.expr) = new_binary_expression(ctx, EXPR_BXOR, (yyvsp[-2].expr), (yyvsp[0].expr)); ;} break; case 107: @@ -2289,7 +2289,7 @@ yyreduce: case 108: #line 576 "parser.y" - { new_binary_expression(ctx, EXPR_BXOR, (yyvsp[-2].expr), (yyvsp[0].expr)); ;} + { (yyval.expr) = new_binary_expression(ctx, EXPR_BXOR, (yyvsp[-2].expr), (yyvsp[0].expr)); ;} break; case 109: @@ -2299,7 +2299,7 @@ yyreduce: case 110: #line 582 "parser.y" - { new_binary_expression(ctx, EXPR_BAND, (yyvsp[-2].expr), (yyvsp[0].expr)); ;} + { (yyval.expr) = new_binary_expression(ctx, EXPR_BAND, (yyvsp[-2].expr), (yyvsp[0].expr)); ;} break; case 111: @@ -2309,7 +2309,7 @@ yyreduce: case 112: #line 589 "parser.y" - { new_binary_expression(ctx, EXPR_BAND, (yyvsp[-2].expr), (yyvsp[0].expr)); ;} + { (yyval.expr) = new_binary_expression(ctx, EXPR_BAND, (yyvsp[-2].expr), (yyvsp[0].expr)); ;} break; case 113: @@ -2494,7 +2494,7 @@ yyreduce: case 149: #line 679 "parser.y" - { (yyval.expr) = new_unary_expression(ctx, EXPR_NEW, (yyvsp[0].expr)); ;} + { (yyval.expr) = new_new_expression(ctx, (yyvsp[0].expr), NULL); ;} break; case 150: @@ -2519,7 +2519,7 @@ yyreduce: case 154: #line 690 "parser.y" - { (yyval.expr) = new_member_new_expression(ctx, (yyvsp[-1].expr), (yyvsp[0].argument_list)); ;} + { (yyval.expr) = new_new_expression(ctx, (yyvsp[-1].expr), (yyvsp[0].argument_list)); ;} break; case 155: @@ -2709,21 +2709,22 @@ yyreduce: case 192: #line 778 "parser.y" - { FIXME("RegExp literal\n"); YYABORT; ;} + { (yyval.literal) = parse_regexp(ctx); + if(!(yyval.literal)) YYABORT; ;} break; case 193: -#line 782 "parser.y" +#line 783 "parser.y" { (yyval.literal) = new_boolean_literal(ctx, TRUE); ;} break; case 194: -#line 783 "parser.y" +#line 784 "parser.y" { (yyval.literal) = new_boolean_literal(ctx, FALSE); ;} break; case 196: -#line 787 "parser.y" +#line 788 "parser.y" { if(!allow_auto_semicolon(ctx)) {YYABORT;} ;} break; @@ -2732,7 +2733,7 @@ yyreduce: } /* Line 1126 of yacc.c. */ -#line 2736 "parser.tab.c" +#line 2737 "parser.tab.c" yyvsp -= yylen; yyssp -= yylen; @@ -3000,7 +3001,7 @@ yyreturn: } -#line 789 "parser.y" +#line 790 "parser.y" static BOOL allow_auto_semicolon(parser_ctx_t *ctx) @@ -3191,14 +3192,14 @@ static case_clausule_t *new_case_block(parser_ctx_t *ctx, case_list_t *case_list if(!ret) return NULL; - for(iter = ret->next; iter->next; iter = iter->next) { - for(iter2 = iter; iter2 && !iter2->expr; iter2 = iter2->next); + for(iter = ret; iter; iter = iter->next) { + for(iter2 = iter; iter2 && !iter2->stat; iter2 = iter2->next); if(!iter2) break; while(iter != iter2) { iter->stat = iter2->stat; - iter2 = iter2->next; + iter = iter->next; } if(stat) { @@ -3300,8 +3301,9 @@ static statement_t *new_while_statement(parser_ctx_t *ctx, BOOL dowhile, express { while_statement_t *ret = parser_alloc(ctx, sizeof(while_statement_t)); - ret->stat.eval = dowhile ? dowhile_statement_eval : while_statement_eval; + ret->stat.eval = while_statement_eval; ret->stat.next = NULL; + ret->do_while = dowhile; ret->expr = expr; ret->statement = stat; @@ -3495,7 +3497,6 @@ static const expression_eval_t expression_eval_table[] = { post_decrement_expression_eval, pre_increment_expression_eval, pre_decrement_expression_eval, - new_expression_eval, equal_expression_eval, equal2_expression_eval, not_equal_expression_eval, @@ -3580,11 +3581,11 @@ static expression_t *new_member_expression(parser_ctx_t *ctx, expression_t *expr return &ret->expr; } -static expression_t *new_member_new_expression(parser_ctx_t *ctx, expression_t *expression, argument_list_t *argument_list) +static expression_t *new_new_expression(parser_ctx_t *ctx, expression_t *expression, argument_list_t *argument_list) { call_expression_t *ret = parser_alloc(ctx, sizeof(call_expression_t)); - ret->expr.eval = member_new_expression_eval; + ret->expr.eval = new_expression_eval; ret->expression = expression; ret->argument_list = argument_list ? argument_list->head : NULL; @@ -3724,9 +3725,14 @@ static void program_parsed(parser_ctx_t *ctx, source_elements_t *source) void parser_release(parser_ctx_t *ctx) { + obj_literal_t *iter; + if(--ctx->ref) return; + for(iter = ctx->obj_literals; iter; iter = iter->next) + jsdisp_release(iter->obj); + jsheap_free(&ctx->heap); heap_free(ctx); } @@ -3734,6 +3740,7 @@ void parser_release(parser_ctx_t *ctx) HRESULT script_parse(script_ctx_t *ctx, const WCHAR *code, parser_ctx_t **ret) { parser_ctx_t *parser_ctx; + jsheap_t *mark; HRESULT hres; parser_ctx = heap_alloc_zero(sizeof(parser_ctx_t)); @@ -3749,11 +3756,11 @@ HRESULT script_parse(script_ctx_t *ctx, const WCHAR *code, parser_ctx_t **ret) script_addref(ctx); parser_ctx->script = ctx; - jsheap_init(&parser_ctx->tmp_heap); + mark = jsheap_mark(&ctx->tmp_heap); jsheap_init(&parser_ctx->heap); parser_parse(parser_ctx); - jsheap_free(&parser_ctx->tmp_heap); + jsheap_clear(mark); if(FAILED(parser_ctx->hres)) { hres = parser_ctx->hres; parser_release(parser_ctx); diff --git a/reactos/dll/win32/jscript/parser.tab.h b/reactos/dll/win32/jscript/parser.tab.h index b3c7aac8f87..4cbdd7e1929 100644 --- a/reactos/dll/win32/jscript/parser.tab.h +++ b/reactos/dll/win32/jscript/parser.tab.h @@ -122,20 +122,20 @@ typedef union YYSTYPE { int ival; LPCWSTR wstr; literal_t *literal; - argument_list_t *argument_list; + struct _argument_list_t *argument_list; case_clausule_t *case_clausule; - case_list_t *case_list; + struct _case_list_t *case_list; catch_block_t *catch_block; - element_list_t *element_list; + struct _element_list_t *element_list; expression_t *expr; const WCHAR *identifier; function_declaration_t *function_declaration; - parameter_list_t *parameter_list; - property_list_t *property_list; + struct _parameter_list_t *parameter_list; + struct _property_list_t *property_list; source_elements_t *source_elements; statement_t *statement; - statement_list_t *statement_list; - variable_list_t *variable_list; + struct _statement_list_t *statement_list; + struct _variable_list_t *variable_list; variable_declaration_t *variable_declaration; } YYSTYPE; /* Line 1447 of yacc.c. */ diff --git a/reactos/dll/win32/jscript/parser.y b/reactos/dll/win32/jscript/parser.y index a9e2063ac80..a6cb5ebf32c 100644 --- a/reactos/dll/win32/jscript/parser.y +++ b/reactos/dll/win32/jscript/parser.y @@ -32,7 +32,7 @@ static int parser_error(const char*); static BOOL allow_auto_semicolon(parser_ctx_t*); static void program_parsed(parser_ctx_t*,source_elements_t*); -typedef struct { +typedef struct _statement_list_t { statement_t *head; statement_t *tail; } statement_list_t; @@ -42,7 +42,7 @@ static literal_t *new_null_literal(parser_ctx_t*); static literal_t *new_undefined_literal(parser_ctx_t*); static literal_t *new_boolean_literal(parser_ctx_t*,VARIANT_BOOL); -typedef struct { +typedef struct _property_list_t { prop_val_t *head; prop_val_t *tail; } property_list_t; @@ -50,7 +50,7 @@ typedef struct { static property_list_t *new_property_list(parser_ctx_t*,literal_t*,expression_t*); static property_list_t *property_list_add(parser_ctx_t*,property_list_t*,literal_t*,expression_t*); -typedef struct { +typedef struct _element_list_t { array_element_t *head; array_element_t *tail; } element_list_t; @@ -58,7 +58,7 @@ typedef struct { static element_list_t *new_element_list(parser_ctx_t*,int,expression_t*); static element_list_t *element_list_add(parser_ctx_t*,element_list_t*,int,expression_t*); -typedef struct { +typedef struct _argument_list_t { argument_t *head; argument_t *tail; } argument_list_t; @@ -66,7 +66,7 @@ typedef struct { static argument_list_t *new_argument_list(parser_ctx_t*,expression_t*); static argument_list_t *argument_list_add(parser_ctx_t*,argument_list_t*,expression_t*); -typedef struct { +typedef struct _case_list_t { case_clausule_t *head; case_clausule_t *tail; } case_list_t; @@ -77,7 +77,7 @@ static case_list_t *new_case_list(parser_ctx_t*,case_clausule_t*); static case_list_t *case_list_add(parser_ctx_t*,case_list_t*,case_clausule_t*); static case_clausule_t *new_case_block(parser_ctx_t*,case_list_t*,case_clausule_t*,case_list_t*); -typedef struct { +typedef struct _variable_list_t { variable_declaration_t *head; variable_declaration_t *tail; } variable_list_t; @@ -112,7 +112,7 @@ struct statement_list_t { statement_list_t *new_statement_list(parser_ctx_t*,statement_t*); statement_list_t *statement_list_add(statement_list_t*,statement_t*); -typedef struct { +typedef struct _parameter_list_t { parameter_t *head; parameter_t *tail; } parameter_list_t; @@ -126,7 +126,7 @@ static expression_t *new_unary_expression(parser_ctx_t*,expression_type_t,expres static expression_t *new_conditional_expression(parser_ctx_t*,expression_t*,expression_t*,expression_t*); static expression_t *new_array_expression(parser_ctx_t*,expression_t*,expression_t*); static expression_t *new_member_expression(parser_ctx_t*,expression_t*,const WCHAR*); -static expression_t *new_member_new_expression(parser_ctx_t*,expression_t*,argument_list_t*); +static expression_t *new_new_expression(parser_ctx_t*,expression_t*,argument_list_t*); static expression_t *new_call_expression(parser_ctx_t*,expression_t*,argument_list_t*); static expression_t *new_this_expression(parser_ctx_t*); static expression_t *new_identifier_expression(parser_ctx_t*,const WCHAR*); @@ -148,20 +148,20 @@ static source_elements_t *source_elements_add_function(source_elements_t*,functi int ival; LPCWSTR wstr; literal_t *literal; - argument_list_t *argument_list; + struct _argument_list_t *argument_list; case_clausule_t *case_clausule; - case_list_t *case_list; + struct _case_list_t *case_list; catch_block_t *catch_block; - element_list_t *element_list; + struct _element_list_t *element_list; expression_t *expr; const WCHAR *identifier; function_declaration_t *function_declaration; - parameter_list_t *parameter_list; - property_list_t *property_list; + struct _parameter_list_t *parameter_list; + struct _property_list_t *property_list; source_elements_t *source_elements; statement_t *statement; - statement_list_t *statement_list; - variable_list_t *variable_list; + struct _statement_list_t *statement_list; + struct _variable_list_t *variable_list; variable_declaration_t *variable_declaration; } @@ -566,27 +566,27 @@ BitwiseORExpressionNoIn BitwiseXORExpression : BitwiseANDExpression { $$ = $1; } | BitwiseXORExpression '^' BitwiseANDExpression - { new_binary_expression(ctx, EXPR_BXOR, $1, $3); } + { $$ = new_binary_expression(ctx, EXPR_BXOR, $1, $3); } /* ECMA-262 3rd Edition 11.10 */ BitwiseXORExpressionNoIn : BitwiseANDExpressionNoIn { $$ = $1; } | BitwiseXORExpressionNoIn '^' BitwiseANDExpressionNoIn - { new_binary_expression(ctx, EXPR_BXOR, $1, $3); } + { $$ = new_binary_expression(ctx, EXPR_BXOR, $1, $3); } /* ECMA-262 3rd Edition 11.10 */ BitwiseANDExpression : EqualityExpression { $$ = $1; } | BitwiseANDExpression '&' EqualityExpression - { new_binary_expression(ctx, EXPR_BAND, $1, $3); } + { $$ = new_binary_expression(ctx, EXPR_BAND, $1, $3); } /* ECMA-262 3rd Edition 11.10 */ BitwiseANDExpressionNoIn : EqualityExpressionNoIn { $$ = $1; } | BitwiseANDExpressionNoIn '&' EqualityExpressionNoIn - { new_binary_expression(ctx, EXPR_BAND, $1, $3); } + { $$ = new_binary_expression(ctx, EXPR_BAND, $1, $3); } /* ECMA-262 3rd Edition 11.9 */ EqualityExpression @@ -676,7 +676,7 @@ LeftHandSideExpression /* ECMA-262 3rd Edition 11.2 */ NewExpression : MemberExpression { $$ = $1; } - | kNEW NewExpression { $$ = new_unary_expression(ctx, EXPR_NEW, $2); } + | kNEW NewExpression { $$ = new_new_expression(ctx, $2, NULL); } /* ECMA-262 3rd Edition 11.2 */ MemberExpression @@ -687,7 +687,7 @@ MemberExpression | MemberExpression '.' tIdentifier { $$ = new_member_expression(ctx, $1, $3); } | kNEW MemberExpression Arguments - { $$ = new_member_new_expression(ctx, $2, $3); } + { $$ = new_new_expression(ctx, $2, $3); } /* ECMA-262 3rd Edition 11.2 */ CallExpression @@ -775,7 +775,8 @@ Literal | BooleanLiteral { $$ = $1; } | tNumericLiteral { $$ = $1; } | tStringLiteral { $$ = new_string_literal(ctx, $1); } - | '/' { FIXME("RegExp literal\n"); YYABORT; } + | '/' { $$ = parse_regexp(ctx); + if(!$$) YYABORT; } /* ECMA-262 3rd Edition 7.8.2 */ BooleanLiteral @@ -976,14 +977,14 @@ static case_clausule_t *new_case_block(parser_ctx_t *ctx, case_list_t *case_list if(!ret) return NULL; - for(iter = ret->next; iter->next; iter = iter->next) { - for(iter2 = iter; iter2 && !iter2->expr; iter2 = iter2->next); + for(iter = ret; iter; iter = iter->next) { + for(iter2 = iter; iter2 && !iter2->stat; iter2 = iter2->next); if(!iter2) break; while(iter != iter2) { iter->stat = iter2->stat; - iter2 = iter2->next; + iter = iter->next; } if(stat) { @@ -1085,8 +1086,9 @@ static statement_t *new_while_statement(parser_ctx_t *ctx, BOOL dowhile, express { while_statement_t *ret = parser_alloc(ctx, sizeof(while_statement_t)); - ret->stat.eval = dowhile ? dowhile_statement_eval : while_statement_eval; + ret->stat.eval = while_statement_eval; ret->stat.next = NULL; + ret->do_while = dowhile; ret->expr = expr; ret->statement = stat; @@ -1280,7 +1282,6 @@ static const expression_eval_t expression_eval_table[] = { post_decrement_expression_eval, pre_increment_expression_eval, pre_decrement_expression_eval, - new_expression_eval, equal_expression_eval, equal2_expression_eval, not_equal_expression_eval, @@ -1365,11 +1366,11 @@ static expression_t *new_member_expression(parser_ctx_t *ctx, expression_t *expr return &ret->expr; } -static expression_t *new_member_new_expression(parser_ctx_t *ctx, expression_t *expression, argument_list_t *argument_list) +static expression_t *new_new_expression(parser_ctx_t *ctx, expression_t *expression, argument_list_t *argument_list) { call_expression_t *ret = parser_alloc(ctx, sizeof(call_expression_t)); - ret->expr.eval = member_new_expression_eval; + ret->expr.eval = new_expression_eval; ret->expression = expression; ret->argument_list = argument_list ? argument_list->head : NULL; @@ -1509,9 +1510,14 @@ static void program_parsed(parser_ctx_t *ctx, source_elements_t *source) void parser_release(parser_ctx_t *ctx) { + obj_literal_t *iter; + if(--ctx->ref) return; + for(iter = ctx->obj_literals; iter; iter = iter->next) + jsdisp_release(iter->obj); + jsheap_free(&ctx->heap); heap_free(ctx); } @@ -1519,6 +1525,7 @@ void parser_release(parser_ctx_t *ctx) HRESULT script_parse(script_ctx_t *ctx, const WCHAR *code, parser_ctx_t **ret) { parser_ctx_t *parser_ctx; + jsheap_t *mark; HRESULT hres; parser_ctx = heap_alloc_zero(sizeof(parser_ctx_t)); @@ -1534,11 +1541,11 @@ HRESULT script_parse(script_ctx_t *ctx, const WCHAR *code, parser_ctx_t **ret) script_addref(ctx); parser_ctx->script = ctx; - jsheap_init(&parser_ctx->tmp_heap); + mark = jsheap_mark(&ctx->tmp_heap); jsheap_init(&parser_ctx->heap); parser_parse(parser_ctx); - jsheap_free(&parser_ctx->tmp_heap); + jsheap_clear(mark); if(FAILED(parser_ctx->hres)) { hres = parser_ctx->hres; parser_release(parser_ctx); diff --git a/reactos/dll/win32/jscript/regexp.c b/reactos/dll/win32/jscript/regexp.c new file mode 100644 index 00000000000..e8e9bea3618 --- /dev/null +++ b/reactos/dll/win32/jscript/regexp.c @@ -0,0 +1,3600 @@ +/* + * 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 "jscript.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(jscript); + +#define JSREG_FOLD 0x01 /* fold uppercase to lowercase */ +#define JSREG_GLOB 0x02 /* global exec, creates array of matches */ +#define JSREG_MULTILINE 0x04 /* treat ^ and $ as begin and end of line */ +#define JSREG_STICKY 0x08 /* only match starting at lastIndex */ + +typedef BYTE JSPackedBool; +typedef BYTE jsbytecode; + +/* + * 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 JSRegExp + * 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; + +typedef struct { + WORD flags; /* flags, see jsapi.h's JSREG_* defines */ + size_t parenCount; /* number of parenthesized submatches */ + size_t classCount; /* count [...] bitmaps */ + RECharSet *classList; /* list of [...] bitmaps */ + BSTR source; /* locked source string, sans // */ + jsbytecode program[1]; /* regular expression bytecode */ +} JSRegExp; + +typedef struct { + DispatchEx dispex; + + JSRegExp *jsregexp; + BSTR str; +} RegExpInstance; + +static const WCHAR sourceW[] = {'s','o','u','r','c','e',0}; +static const WCHAR globalW[] = {'g','l','o','b','a','l',0}; +static const WCHAR ignoreCaseW[] = {'i','g','n','o','r','e','C','a','s','e',0}; +static const WCHAR multilineW[] = {'m','u','l','t','i','l','i','n','e',0}; +static const WCHAR lastIndexW[] = {'l','a','s','t','I','n','d','e','x',0}; +static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0}; +static const WCHAR toLocaleStringW[] = {'t','o','L','o','c','a','l','e','S','t','r','i','n','g',0}; +static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0}; +static const WCHAR propertyIsEnumerableW[] = + {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0}; +static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0}; +static const WCHAR execW[] = {'e','x','e','c',0}; + +static const WCHAR emptyW[] = {0}; + +/* 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) + +#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) + +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 RECapture { + ptrdiff_t index; /* start of contents, -1 for empty */ + size_t length; /* length of capture */ +} RECapture; + +typedef struct REMatchState { + const WCHAR *cp; + RECapture parens[1]; /* first of 're->parenCount' captures, + allocated at end of this struct */ +} REMatchState; + +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 { + script_ctx_t *cx; + JSRegExp *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 */ + + jsheap_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 { + script_ctx_t *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; +} 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 = jsheap_alloc(&state->context->tmp_heap, 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, JSRegExp *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 = (RENode *) 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 = (RENode *) 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 = (RENode *) 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 == + (WCHAR*)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 & JSREG_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 & JSREG_FOLD) ? REOP_FLAT1i : REOP_FLAT1; + *pc++ = (jsbytecode) t->u.flat.chr; + } else { + pc[-1] = (state->flags & JSREG_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 = (RENode *) 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 = (RENode *) 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 = (RENode *) 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 = (RENode *) 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 & JSREG_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 & JSREG_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 & JSREG_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 & JSREG_FOLD) + rangeStart = localMax; /* one run of the uc/dc loop below */ + } + + if (state->flags & JSREG_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, REMatchState *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", (unsigned long) parenIndex, (unsigned long) 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 = jsheap_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 REMatchState * +FlatNIMatcher(REGlobalData *gData, REMatchState *x, 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 REMatchState * +BackrefMatcher(REGlobalData *gData, REMatchState *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 & JSREG_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 + < SysStringLen(gData->regexp->source)); + assert(charSet->u.src.length <= SysStringLen(gData->regexp->source) + - 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 & JSREG_FOLD) { + int i; + + 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 & JSREG_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 = jsheap_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 REMatchState * +SimpleMatch(REGlobalData *gData, REMatchState *x, REOp op, + jsbytecode **startpc, BOOL updatecp) +{ + REMatchState *result = NULL; + WCHAR matchCh; + size_t parenIndex; + size_t offset, length, index; + jsbytecode *pc = *startpc; /* pc has already been incremented past op */ + WCHAR *source; + const WCHAR *startcp = x->cp; + WCHAR ch; + RECharSet *charSet; + + const char *opname = reop_names[op]; + TRACE("\n%06d: %*s%s\n", pc - gData->regexp->program, + 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 & JSREG_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 & JSREG_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 < SysStringLen(gData->regexp->source)); + pc = ReadCompactIndex(pc, &length); + assert(1 <= length); + assert(length <= SysStringLen(gData->regexp->source) - 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 < SysStringLen(gData->regexp->source)); + pc = ReadCompactIndex(pc, &length); + assert(1 <= length); + assert(length <= SysStringLen(gData->regexp->source) - 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 REMatchState * +ExecuteREBytecode(REGlobalData *gData, REMatchState *x) +{ + REMatchState *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 & JSREG_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", pc - gData->regexp->program, + 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 false thru... */ + + 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", (unsigned long) 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", + (unsigned long) backTrackData->parenIndex, + (unsigned long) 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 REMatchState *MatchRegExp(REGlobalData *gData, REMatchState *x) +{ + REMatchState *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 & JSREG_STICKY)) + return result; + gData->backTrackSP = gData->backTrackStack; + gData->cursz = 0; + gData->stateStackTop = 0; + cp2 = cp + gData->skipped; + } + return NULL; +} + +#define MIN_BACKTRACK_LIMIT 400000 + +static REMatchState *InitMatch(script_ctx_t *cx, REGlobalData *gData, JSRegExp *re, size_t length) +{ + REMatchState *result; + UINT i; + + gData->backTrackStackSize = INITIAL_BACKTRACK; + gData->backTrackStack = jsheap_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 = jsheap_alloc(gData->pool, sizeof(REProgState) * INITIAL_STATESTACK); + if (!gData->stateStack) + goto bad; + + gData->stateStackTop = 0; + gData->cx = cx; + gData->regexp = re; + gData->ok = TRUE; + + result = jsheap_alloc(gData->pool, offsetof(REMatchState, parens) + re->parenCount * sizeof(RECapture)); + if (!result) + goto bad; + + for (i = 0; i < re->classCount; i++) { + if (!re->classList[i].converted && + !ProcessCharSet(gData, &re->classList[i])) { + return NULL; + } + } + + return result; + +bad: + js_ReportOutOfScriptQuota(cx); + gData->ok = FALSE; + return NULL; +} + +static void +js_DestroyRegExp(JSRegExp *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); +} + +static JSRegExp * +js_NewRegExp(script_ctx_t *cx, BSTR str, UINT flags, BOOL flat) +{ + JSRegExp *re; + jsheap_t *mark; + CompilerState state; + size_t resize; + jsbytecode *endPC; + UINT i; + size_t len; + + re = NULL; + mark = jsheap_mark(&cx->tmp_heap); + len = SysStringLen(str); + + state.context = cx; + 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(JSRegExp, 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) { + js_DestroyRegExp(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) { + js_DestroyRegExp(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) { + JSRegExp *tmp; + assert((size_t)(endPC - re->program) < state.progLength + 1); + resize = offsetof(JSRegExp, program) + (endPC - re->program); + tmp = heap_realloc(re, resize); + if (tmp) + re = tmp; + } + + re->flags = flags; + re->parenCount = state.parenCount; + re->source = str; + +out: + jsheap_clear(mark); + return re; +} + +HRESULT regexp_match(DispatchEx *dispex, const WCHAR *str, DWORD len, BOOL gflag, match_result_t **match_result, + DWORD *result_cnt) +{ + RegExpInstance *This = (RegExpInstance*)dispex; + match_result_t *ret = NULL; + const WCHAR *cp = str; + REGlobalData gData; + REMatchState *x, *result; + DWORD matchlen; + DWORD i=0, ret_size = 0; + jsheap_t *mark; + size_t length; + HRESULT hres = E_FAIL; + + length = len; + + mark = jsheap_mark(&This->dispex.ctx->tmp_heap); + gData.pool = &This->dispex.ctx->tmp_heap; + + while(1) { + gData.cpbegin = cp; + gData.cpend = str + len; + gData.start = cp-str; + gData.skipped = 0; + + x = InitMatch(NULL, &gData, This->jsregexp, length); + if(!x) { + WARN("InitMatch failed\n"); + break; + } + + x->cp = cp; + result = MatchRegExp(&gData, x); + if(!gData.ok) { + WARN("MatchRegExp failed\n"); + break; + } + + if(!result) { + hres = S_OK; + break; + } + + matchlen = (result->cp-cp) - gData.skipped; + + if(ret) + ret = heap_realloc(ret, (ret_size <<= 1) * sizeof(match_result_t)); + else if(ret_size == i) + ret = heap_alloc((ret_size=4) * sizeof(match_result_t)); + if(!ret) { + hres = E_OUTOFMEMORY; + break; + } + + ret[i].str = result->cp-matchlen; + ret[i].len = matchlen; + + length -= result->cp-cp; + cp = result->cp; + i++; + + if(!gflag && !(This->jsregexp->flags & JSREG_GLOB)) { + hres = S_OK; + break; + } + } + + jsheap_clear(mark); + if(FAILED(hres)) { + heap_free(ret); + return hres; + } + + *match_result = ret; + *result_cnt = i; + return S_OK; +} + +static HRESULT RegExp_source(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_global(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_ignoreCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_multiline(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_lastIndex(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_toLocaleString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_exec(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT RegExp_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static void RegExp_destructor(DispatchEx *dispex) +{ + RegExpInstance *This = (RegExpInstance*)dispex; + + if(This->jsregexp) + js_DestroyRegExp(This->jsregexp); + SysFreeString(This->str); + heap_free(This); +} + +static const builtin_prop_t RegExp_props[] = { + {execW, RegExp_exec, PROPF_METHOD}, + {globalW, RegExp_global, 0}, + {hasOwnPropertyW, RegExp_hasOwnProperty, PROPF_METHOD}, + {ignoreCaseW, RegExp_ignoreCase, 0}, + {isPrototypeOfW, RegExp_isPrototypeOf, PROPF_METHOD}, + {lastIndexW, RegExp_lastIndex, 0}, + {multilineW, RegExp_multiline, 0}, + {propertyIsEnumerableW, RegExp_propertyIsEnumerable, PROPF_METHOD}, + {sourceW, RegExp_source, 0}, + {toLocaleStringW, RegExp_toLocaleString, PROPF_METHOD}, + {toStringW, RegExp_toString, PROPF_METHOD} +}; + +static const builtin_info_t RegExp_info = { + JSCLASS_REGEXP, + {NULL, RegExp_value, 0}, + sizeof(RegExp_props)/sizeof(*RegExp_props), + RegExp_props, + RegExp_destructor, + NULL +}; + +static HRESULT alloc_regexp(script_ctx_t *ctx, BOOL use_constr, RegExpInstance **ret) +{ + RegExpInstance *regexp; + HRESULT hres; + + regexp = heap_alloc_zero(sizeof(RegExpInstance)); + if(!regexp) + return E_OUTOFMEMORY; + + if(use_constr) + hres = init_dispex_from_constr(®exp->dispex, ctx, &RegExp_info, ctx->regexp_constr); + else + hres = init_dispex(®exp->dispex, ctx, &RegExp_info, NULL); + + if(FAILED(hres)) { + heap_free(regexp); + return hres; + } + + *ret = regexp; + return S_OK; +} + +static HRESULT create_regexp(script_ctx_t *ctx, const WCHAR *exp, int len, DWORD flags, DispatchEx **ret) +{ + RegExpInstance *regexp; + HRESULT hres; + + TRACE("%s %x\n", debugstr_w(exp), flags); + + hres = alloc_regexp(ctx, TRUE, ®exp); + if(FAILED(hres)) + return hres; + + if(len == -1) + regexp->str = SysAllocString(exp); + else + regexp->str = SysAllocStringLen(exp, len); + if(!regexp->str) { + jsdisp_release(®exp->dispex); + return E_OUTOFMEMORY; + } + + regexp->jsregexp = js_NewRegExp(ctx, regexp->str, flags, FALSE); + if(!regexp->jsregexp) { + WARN("js_NewRegExp failed\n"); + jsdisp_release(®exp->dispex); + return E_FAIL; + } + + *ret = ®exp->dispex; + return S_OK; +} + +static HRESULT RegExpConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +HRESULT create_regexp_constr(script_ctx_t *ctx, DispatchEx **ret) +{ + RegExpInstance *regexp; + HRESULT hres; + + hres = alloc_regexp(ctx, FALSE, ®exp); + if(FAILED(hres)) + return hres; + + hres = create_builtin_function(ctx, RegExpConstr_value, PROPF_CONSTR, ®exp->dispex, ret); + + jsdisp_release(®exp->dispex); + return hres; +} + +HRESULT create_regexp_str(script_ctx_t *ctx, const WCHAR *exp, DWORD exp_len, const WCHAR *opt, + DWORD opt_len, DispatchEx **ret) +{ + const WCHAR *p; + DWORD flags = 0; + + if(opt) { + for (p = opt; p < opt+opt_len; p++) { + switch (*p) { + case 'g': + flags |= JSREG_GLOB; + break; + case 'i': + flags |= JSREG_FOLD; + break; + case 'm': + flags |= JSREG_MULTILINE; + break; + case 'y': + flags |= JSREG_STICKY; + break; + default: + WARN("wrong flag %c\n", *p); + return E_FAIL; + } + } + } + + return create_regexp(ctx, exp, exp_len, flags, ret); +} diff --git a/reactos/dll/win32/jscript/string.c b/reactos/dll/win32/jscript/string.c new file mode 100644 index 00000000000..12140d58603 --- /dev/null +++ b/reactos/dll/win32/jscript/string.c @@ -0,0 +1,579 @@ +/* + * 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 + */ + +#include "jscript.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(jscript); + +typedef struct { + DispatchEx dispex; + + WCHAR *str; + DWORD length; +} StringInstance; + +static const WCHAR lengthW[] = {'l','e','n','g','t','h',0}; +static const WCHAR toStringW[] = {'t','o','S','t','r','i','n','g',0}; +static const WCHAR valueOfW[] = {'v','a','l','u','e','O','f',0}; +static const WCHAR anchorW[] = {'a','n','c','h','o','r',0}; +static const WCHAR bigW[] = {'b','i','g',0}; +static const WCHAR blinkW[] = {'b','l','i','n','k',0}; +static const WCHAR boldW[] = {'b','o','l','d',0}; +static const WCHAR charAtW[] = {'c','h','a','r','A','t',0}; +static const WCHAR charCodeAtW[] = {'c','h','a','r','C','o','d','e','A','t',0}; +static const WCHAR concatW[] = {'c','o','n','c','a','t',0}; +static const WCHAR fixedW[] = {'f','i','x','e','d',0}; +static const WCHAR fontcolorW[] = {'f','o','n','t','c','o','l','o','r',0}; +static const WCHAR fontsizeW[] = {'f','o','n','t','s','i','z','e',0}; +static const WCHAR indexOfW[] = {'i','n','d','e','x','O','f',0}; +static const WCHAR italicsW[] = {'i','t','a','l','i','c','s',0}; +static const WCHAR lastIndexOfW[] = {'l','a','s','t','I','n','d','e','x','O','f',0}; +static const WCHAR linkW[] = {'l','i','n','k',0}; +static const WCHAR matchW[] = {'m','a','t','c','h',0}; +static const WCHAR replaceW[] = {'r','e','p','l','a','c','e',0}; +static const WCHAR searchW[] = {'s','e','a','r','c','h',0}; +static const WCHAR sliceW[] = {'s','l','i','c','e',0}; +static const WCHAR smallW[] = {'s','m','a','l','l',0}; +static const WCHAR splitW[] = {'s','p','l','i','t',0}; +static const WCHAR strikeW[] = {'s','t','r','i','k','e',0}; +static const WCHAR subW[] = {'s','u','b',0}; +static const WCHAR substringW[] = {'s','u','b','s','t','r','i','n','g',0}; +static const WCHAR substrW[] = {'s','u','b','s','t','r',0}; +static const WCHAR supW[] = {'s','u','p',0}; +static const WCHAR toLowerCaseW[] = {'t','o','L','o','w','e','r','C','a','s','e',0}; +static const WCHAR toUpperCaseW[] = {'t','o','U','p','p','e','r','C','a','s','e',0}; +static const WCHAR toLocaleLowerCaseW[] = {'t','o','L','o','c','a','l','e','L','o','w','e','r','C','a','s','e',0}; +static const WCHAR toLocaleUpperCaseW[] = {'t','o','L','o','c','a','l','e','U','p','p','e','r','C','a','s','e',0}; +static const WCHAR localeCompareW[] = {'l','o','c','a','l','e','C','o','m','p','a','r','e',0}; +static const WCHAR hasOwnPropertyW[] = {'h','a','s','O','w','n','P','r','o','p','e','r','t','y',0}; +static const WCHAR propertyIsEnumerableW[] = + {'p','r','o','p','e','r','t','y','I','s','E','n','u','m','e','r','a','b','l','e',0}; +static const WCHAR isPrototypeOfW[] = {'i','s','P','r','o','t','o','t','y','p','e','O','f',0}; + +static HRESULT String_length(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + TRACE("%p\n", dispex); + + switch(flags) { + case DISPATCH_PROPERTYGET: { + StringInstance *jsthis = (StringInstance*)dispex; + + V_VT(retv) = VT_I4; + V_I4(retv) = jsthis->length; + break; + } + default: + FIXME("unimplemented flags %x\n", flags); + return E_NOTIMPL; + } + + return S_OK; +} + +static HRESULT String_toString(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_valueOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_anchor(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_big(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_blink(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_bold(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +/* ECMA-262 3rd Edition 15.5.4.5 */ +static HRESULT String_charAt(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + StringInstance *strobj; + BSTR str; + INT pos = 0; + HRESULT hres; + + TRACE("\n"); + + if(dispex->builtin_info->class != JSCLASS_STRING) { + FIXME("not string this not supported\n"); + return E_NOTIMPL; + } + + strobj = (StringInstance*)dispex; + + if(arg_cnt(dp)) { + VARIANT num; + + hres = to_integer(dispex->ctx, get_arg(dp, 0), ei, &num); + if(FAILED(hres)) + return hres; + + if(V_VT(&num) == VT_I4) { + pos = V_I4(&num); + }else { + WARN("pos = %lf\n", V_R8(&num)); + pos = -1; + } + } + + if(!retv) + return S_OK; + + if(0 <= pos && pos < strobj->length) + str = SysAllocStringLen(strobj->str+pos, 1); + else + str = SysAllocStringLen(NULL, 0); + if(!str) + return E_OUTOFMEMORY; + + V_VT(retv) = VT_BSTR; + V_BSTR(retv) = str; + return S_OK; +} + +static HRESULT String_charCodeAt(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_concat(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_fixed(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_fontcolor(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_fontsize(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_indexOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_italics(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_lastIndexOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_link(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +/* ECMA-262 3rd Edition 15.5.4.10 */ +static HRESULT String_match(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + StringInstance *This = (StringInstance*)dispex; + match_result_t *match_result; + DispatchEx *array; + VARIANT var, *arg_var; + DWORD match_cnt, i; + HRESULT hres = S_OK; + + TRACE("\n"); + + if(dp->cArgs - dp->cNamedArgs != 1) { + FIXME("unsupported args\n"); + return E_NOTIMPL; + } + + arg_var = get_arg(dp, 0); + switch(V_VT(arg_var)) { + case VT_DISPATCH: { + DispatchEx *regexp; + + regexp = iface_to_jsdisp((IUnknown*)V_DISPATCH(arg_var)); + if(regexp) { + if(regexp->builtin_info->class == JSCLASS_REGEXP) { + hres = regexp_match(regexp, This->str, This->length, FALSE, &match_result, &match_cnt); + jsdisp_release(regexp); + if(FAILED(hres)) + return hres; + break; + } + jsdisp_release(regexp); + } + } + default: + FIXME("implemented only for regexp args\n"); + return E_NOTIMPL; + } + + if(!match_cnt) { + TRACE("no match\n"); + + if(retv) + V_VT(retv) = VT_NULL; + return S_OK; + } + + hres = create_array(dispex->ctx, match_cnt, &array); + if(FAILED(hres)) + return hres; + + V_VT(&var) = VT_BSTR; + + for(i=0; i < match_cnt; i++) { + V_BSTR(&var) = SysAllocStringLen(match_result[i].str, match_result[i].len); + if(!V_BSTR(&var)) { + hres = E_OUTOFMEMORY; + break; + } + + hres = jsdisp_propput_idx(array, i, lcid, &var, ei, NULL/*FIXME*/); + SysFreeString(V_BSTR(&var)); + if(FAILED(hres)) + break; + } + + if(FAILED(hres)) { + jsdisp_release(array); + return hres; + } + + V_VT(retv) = VT_DISPATCH; + V_DISPATCH(retv) = (IDispatch*)_IDispatchEx_(array); + return S_OK; +} + +static HRESULT String_replace(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_search(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_slice(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_small(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_split(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_strike(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_sub(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_substring(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_substr(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_sup(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_toLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_toUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_toLocaleLowerCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_toLocaleUpperCase(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_localeCompare(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_hasOwnProperty(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_propertyIsEnumerable(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_isPrototypeOf(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT String_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static void String_destructor(DispatchEx *dispex) +{ + StringInstance *This = (StringInstance*)dispex; + + heap_free(This->str); + heap_free(This); +} + +static const builtin_prop_t String_props[] = { + {anchorW, String_anchor, PROPF_METHOD}, + {bigW, String_big, PROPF_METHOD}, + {blinkW, String_blink, PROPF_METHOD}, + {boldW, String_bold, PROPF_METHOD}, + {charAtW, String_charAt, PROPF_METHOD}, + {charCodeAtW, String_charCodeAt, PROPF_METHOD}, + {concatW, String_concat, PROPF_METHOD}, + {fixedW, String_fixed, PROPF_METHOD}, + {fontcolorW, String_fontcolor, PROPF_METHOD}, + {fontsizeW, String_fontsize, PROPF_METHOD}, + {hasOwnPropertyW, String_hasOwnProperty, PROPF_METHOD}, + {indexOfW, String_indexOf, PROPF_METHOD}, + {isPrototypeOfW, String_isPrototypeOf, PROPF_METHOD}, + {italicsW, String_italics, PROPF_METHOD}, + {lastIndexOfW, String_lastIndexOf, PROPF_METHOD}, + {lengthW, String_length, 0}, + {linkW, String_link, PROPF_METHOD}, + {localeCompareW, String_localeCompare, PROPF_METHOD}, + {matchW, String_match, PROPF_METHOD}, + {propertyIsEnumerableW, String_propertyIsEnumerable, PROPF_METHOD}, + {replaceW, String_replace, PROPF_METHOD}, + {searchW, String_search, PROPF_METHOD}, + {sliceW, String_slice, PROPF_METHOD}, + {smallW, String_small, PROPF_METHOD}, + {splitW, String_split, PROPF_METHOD}, + {strikeW, String_strike, PROPF_METHOD}, + {substringW, String_substring, PROPF_METHOD}, + {substrW, String_substr, PROPF_METHOD}, + {subW, String_sub, PROPF_METHOD}, + {supW, String_sup, PROPF_METHOD}, + {toLocaleLowerCaseW, String_toLocaleLowerCase, PROPF_METHOD}, + {toLocaleUpperCaseW, String_toLocaleUpperCase, PROPF_METHOD}, + {toLowerCaseW, String_toLowerCase, PROPF_METHOD}, + {toStringW, String_toString, PROPF_METHOD}, + {toUpperCaseW, String_toUpperCase, PROPF_METHOD}, + {valueOfW, String_valueOf, PROPF_METHOD} +}; + +static const builtin_info_t String_info = { + JSCLASS_STRING, + {NULL, String_value, 0}, + sizeof(String_props)/sizeof(*String_props), + String_props, + String_destructor, + NULL +}; + +static HRESULT StringConstr_value(DispatchEx *dispex, LCID lcid, WORD flags, DISPPARAMS *dp, + VARIANT *retv, jsexcept_t *ei, IServiceProvider *sp) +{ + FIXME("\n"); + return E_NOTIMPL; +} + +static HRESULT string_alloc(script_ctx_t *ctx, BOOL use_constr, StringInstance **ret) +{ + StringInstance *string; + HRESULT hres; + + string = heap_alloc_zero(sizeof(StringInstance)); + if(!string) + return E_OUTOFMEMORY; + + if(use_constr) + hres = init_dispex_from_constr(&string->dispex, ctx, &String_info, ctx->string_constr); + else + hres = init_dispex(&string->dispex, ctx, &String_info, NULL); + if(FAILED(hres)) { + heap_free(string); + return hres; + } + + *ret = string; + return S_OK; +} + +HRESULT create_string_constr(script_ctx_t *ctx, DispatchEx **ret) +{ + StringInstance *string; + HRESULT hres; + + hres = string_alloc(ctx, FALSE, &string); + if(FAILED(hres)) + return hres; + + hres = create_builtin_function(ctx, StringConstr_value, PROPF_CONSTR, &string->dispex, ret); + + jsdisp_release(&string->dispex); + return hres; +} + +HRESULT create_string(script_ctx_t *ctx, const WCHAR *str, DWORD len, DispatchEx **ret) +{ + StringInstance *string; + HRESULT hres; + + hres = string_alloc(ctx, TRUE, &string); + if(FAILED(hres)) + return hres; + + if(len == -1) + len = strlenW(str); + + string->length = len; + string->str = heap_alloc((len+1)*sizeof(WCHAR)); + if(!string->str) { + jsdisp_release(&string->dispex); + return E_OUTOFMEMORY; + } + + memcpy(string->str, str, len*sizeof(WCHAR)); + string->str[len] = 0; + + *ret = &string->dispex; + return S_OK; + +}