From 07a14c5c02437760f59d4c66f699c835cd33bf65 Mon Sep 17 00:00:00 2001 From: Amine Khaldi Date: Thu, 3 Mar 2016 13:24:43 +0000 Subject: [PATCH] [JSCRIPT] Sync with Wine Staging 1.9.4. CORE-10912 svn path=/trunk/; revision=70871 --- reactos/dll/win32/jscript/CMakeLists.txt | 1 + reactos/dll/win32/jscript/array.c | 6 + reactos/dll/win32/jscript/bool.c | 6 + reactos/dll/win32/jscript/function.c | 1 + reactos/dll/win32/jscript/global.c | 17 +- reactos/dll/win32/jscript/jscript.h | 8 +- reactos/dll/win32/jscript/json.c | 844 +++++++++++++++++++++++ reactos/dll/win32/jscript/jsutils.c | 7 +- reactos/dll/win32/jscript/lex.c | 90 +-- reactos/dll/win32/jscript/number.c | 8 +- reactos/dll/win32/jscript/object.c | 2 +- reactos/dll/win32/jscript/parser.h | 4 + reactos/media/doc/README.WINE | 2 +- 13 files changed, 945 insertions(+), 51 deletions(-) create mode 100644 reactos/dll/win32/jscript/json.c diff --git a/reactos/dll/win32/jscript/CMakeLists.txt b/reactos/dll/win32/jscript/CMakeLists.txt index e7dde3cd261..fd4d91e80f6 100644 --- a/reactos/dll/win32/jscript/CMakeLists.txt +++ b/reactos/dll/win32/jscript/CMakeLists.txt @@ -21,6 +21,7 @@ list(APPEND SOURCE global.c jscript.c jscript_main.c + json.c jsregexp.c jsstr.c jsutils.c diff --git a/reactos/dll/win32/jscript/array.c b/reactos/dll/win32/jscript/array.c index 41204b0b5a9..315d5d4055a 100644 --- a/reactos/dll/win32/jscript/array.c +++ b/reactos/dll/win32/jscript/array.c @@ -55,6 +55,12 @@ static inline ArrayInstance *array_this(vdisp_t *jsthis) return is_vclass(jsthis, JSCLASS_ARRAY) ? array_from_vdisp(jsthis) : NULL; } +unsigned array_get_length(jsdisp_t *array) +{ + assert(is_class(array, JSCLASS_ARRAY)); + return array_from_jsdisp(array)->length; +} + static HRESULT get_length(script_ctx_t *ctx, vdisp_t *vdisp, jsdisp_t **jsthis, DWORD *ret) { ArrayInstance *array; diff --git a/reactos/dll/win32/jscript/bool.c b/reactos/dll/win32/jscript/bool.c index dffaecb58b3..1a6fee5bc1d 100644 --- a/reactos/dll/win32/jscript/bool.c +++ b/reactos/dll/win32/jscript/bool.c @@ -33,6 +33,12 @@ static inline BoolInstance *bool_this(vdisp_t *jsthis) return is_vclass(jsthis, JSCLASS_BOOLEAN) ? (BoolInstance*)jsthis->u.jsdisp : NULL; } +BOOL bool_obj_value(jsdisp_t *obj) +{ + assert(is_class(obj, JSCLASS_BOOLEAN)); + return ((BoolInstance*)obj)->val; +} + /* ECMA-262 3rd Edition 15.6.4.2 */ static HRESULT Bool_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) { diff --git a/reactos/dll/win32/jscript/function.c b/reactos/dll/win32/jscript/function.c index 52481c11d8b..91867962852 100644 --- a/reactos/dll/win32/jscript/function.c +++ b/reactos/dll/win32/jscript/function.c @@ -814,6 +814,7 @@ static HRESULT FunctionConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD fla TRACE("\n"); switch(flags) { + case DISPATCH_METHOD: case DISPATCH_CONSTRUCT: { IDispatch *ret; diff --git a/reactos/dll/win32/jscript/global.c b/reactos/dll/win32/jscript/global.c index f7caf1117e2..35aa6941424 100644 --- a/reactos/dll/win32/jscript/global.c +++ b/reactos/dll/win32/jscript/global.c @@ -57,6 +57,7 @@ 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 const WCHAR JSONW[] = {'J','S','O','N',0}; static const WCHAR encodeURIW[] = {'e','n','c','o','d','e','U','R','I',0}; static const WCHAR decodeURIW[] = {'d','e','c','o','d','e','U','R','I',0}; static const WCHAR encodeURIComponentW[] = {'e','n','c','o','d','e','U','R','I','C','o','m','p','o','n','e','n','t',0}; @@ -254,8 +255,7 @@ static HRESULT JSGlobal_isFinite(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, if(FAILED(hres)) return hres; - if(!isinf(n) && !isnan(n)) - ret = TRUE; + ret = is_finite(n); } if(r) @@ -1098,6 +1098,19 @@ HRESULT init_global(script_ctx_t *ctx) if(FAILED(hres)) return hres; + if(ctx->version >= 2) { + jsdisp_t *json; + + hres = create_json(ctx, &json); + if(FAILED(hres)) + return hres; + + hres = jsdisp_propput_dontenum(ctx->global, JSONW, jsval_obj(json)); + jsdisp_release(json); + if(FAILED(hres)) + return hres; + } + hres = create_activex_constr(ctx, &constr); if(FAILED(hres)) return hres; diff --git a/reactos/dll/win32/jscript/jscript.h b/reactos/dll/win32/jscript/jscript.h index ac6db25d34c..02517fa8d13 100644 --- a/reactos/dll/win32/jscript/jscript.h +++ b/reactos/dll/win32/jscript/jscript.h @@ -131,7 +131,8 @@ typedef enum { JSCLASS_REGEXP, JSCLASS_STRING, JSCLASS_ARGUMENTS, - JSCLASS_VBARRAY + JSCLASS_VBARRAY, + JSCLASS_JSON } jsclass_t; jsdisp_t *iface_to_jsdisp(IUnknown*) DECLSPEC_HIDDEN; @@ -331,6 +332,7 @@ HRESULT create_string(script_ctx_t*,jsstr_t*,jsdisp_t**) DECLSPEC_HIDDEN; HRESULT create_bool(script_ctx_t*,BOOL,jsdisp_t**) DECLSPEC_HIDDEN; HRESULT create_number(script_ctx_t*,double,jsdisp_t**) DECLSPEC_HIDDEN; HRESULT create_vbarray(script_ctx_t*,SAFEARRAY*,jsdisp_t**) DECLSPEC_HIDDEN; +HRESULT create_json(script_ctx_t*,jsdisp_t**) DECLSPEC_HIDDEN; typedef enum { NO_HINT, @@ -353,6 +355,7 @@ HRESULT variant_change_type(script_ctx_t*,VARIANT*,VARIANT*,VARTYPE) DECLSPEC_HI HRESULT decode_source(WCHAR*) DECLSPEC_HIDDEN; HRESULT double_to_string(double,jsstr_t**) DECLSPEC_HIDDEN; +BOOL is_finite(double) DECLSPEC_HIDDEN; typedef struct named_item_t { IDispatch *disp; @@ -472,6 +475,9 @@ HRESULT regexp_match_next(script_ctx_t*,jsdisp_t*,DWORD,jsstr_t*,struct match_st HRESULT parse_regexp_flags(const WCHAR*,DWORD,DWORD*) DECLSPEC_HIDDEN; HRESULT regexp_string_match(script_ctx_t*,jsdisp_t*,jsstr_t*,jsval_t*) DECLSPEC_HIDDEN; +BOOL bool_obj_value(jsdisp_t*) DECLSPEC_HIDDEN; +unsigned array_get_length(jsdisp_t*) DECLSPEC_HIDDEN; + static inline BOOL is_class(jsdisp_t *jsdisp, jsclass_t class) { return jsdisp->builtin_info->class == class; diff --git a/reactos/dll/win32/jscript/json.c b/reactos/dll/win32/jscript/json.c new file mode 100644 index 00000000000..648a8e6184b --- /dev/null +++ b/reactos/dll/win32/jscript/json.c @@ -0,0 +1,844 @@ +/* + * Copyright 2016 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" + +static const WCHAR parseW[] = {'p','a','r','s','e',0}; +static const WCHAR stringifyW[] = {'s','t','r','i','n','g','i','f','y',0}; + +static const WCHAR nullW[] = {'n','u','l','l',0}; +static const WCHAR trueW[] = {'t','r','u','e',0}; +static const WCHAR falseW[] = {'f','a','l','s','e',0}; + +static const WCHAR toJSONW[] = {'t','o','J','S','O','N',0}; + +typedef struct { + const WCHAR *ptr; + const WCHAR *end; + script_ctx_t *ctx; +} json_parse_ctx_t; + +static BOOL is_json_space(WCHAR c) +{ + return c == ' ' || c == '\t' || c == '\n' || c == '\r'; +} + +static WCHAR skip_spaces(json_parse_ctx_t *ctx) +{ + while(is_json_space(*ctx->ptr)) + ctx->ptr++; + return *ctx->ptr; +} + +static BOOL is_keyword(json_parse_ctx_t *ctx, const WCHAR *keyword) +{ + unsigned i; + for(i=0; keyword[i]; i++) { + if(!ctx->ptr[i] || keyword[i] != ctx->ptr[i]) + return FALSE; + } + if(is_identifier_char(ctx->ptr[i])) + return FALSE; + ctx->ptr += i; + return TRUE; +} + +/* ECMA-262 5.1 Edition 15.12.1.1 */ +static HRESULT parse_json_string(json_parse_ctx_t *ctx, WCHAR **r) +{ + const WCHAR *ptr = ++ctx->ptr; + size_t len; + WCHAR *buf; + + while(*ctx->ptr && *ctx->ptr != '"') { + if(*ctx->ptr++ == '\\') + ctx->ptr++; + } + if(!*ctx->ptr) { + FIXME("unterminated string\n"); + return E_FAIL; + } + + len = ctx->ptr-ptr; + buf = heap_alloc((len+1)*sizeof(WCHAR)); + if(!buf) + return E_OUTOFMEMORY; + if(len) + memcpy(buf, ptr, len*sizeof(WCHAR)); + buf[len] = 0; + + if(!unescape(buf)) { + FIXME("unescape failed\n"); + heap_free(buf); + return E_FAIL; + } + + ctx->ptr++; + *r = buf; + return S_OK; +} + +/* ECMA-262 5.1 Edition 15.12.1.2 */ +static HRESULT parse_json_value(json_parse_ctx_t *ctx, jsval_t *r) +{ + HRESULT hres; + + switch(skip_spaces(ctx)) { + + /* JSONNullLiteral */ + case 'n': + if(!is_keyword(ctx, nullW)) + break; + *r = jsval_null(); + return S_OK; + + /* JSONBooleanLiteral */ + case 't': + if(!is_keyword(ctx, trueW)) + break; + *r = jsval_bool(TRUE); + return S_OK; + case 'f': + if(!is_keyword(ctx, falseW)) + break; + *r = jsval_bool(FALSE); + return S_OK; + + /* JSONObject */ + case '{': { + WCHAR *prop_name; + jsdisp_t *obj; + jsval_t val; + + hres = create_object(ctx->ctx, NULL, &obj); + if(FAILED(hres)) + return hres; + + ctx->ptr++; + if(skip_spaces(ctx) == '}') { + ctx->ptr++; + *r = jsval_obj(obj); + return S_OK; + } + + while(1) { + if(*ctx->ptr != '"') + break; + hres = parse_json_string(ctx, &prop_name); + if(FAILED(hres)) + break; + + if(skip_spaces(ctx) != ':') { + FIXME("missing ':'\n"); + heap_free(prop_name); + break; + } + + ctx->ptr++; + hres = parse_json_value(ctx, &val); + if(SUCCEEDED(hres)) { + hres = jsdisp_propput_name(obj, prop_name, val); + jsval_release(val); + } + heap_free(prop_name); + if(FAILED(hres)) + break; + + if(skip_spaces(ctx) == '}') { + ctx->ptr++; + *r = jsval_obj(obj); + return S_OK; + } + + if(*ctx->ptr++ != ',') { + FIXME("expected ','\n"); + break; + } + skip_spaces(ctx); + } + + jsdisp_release(obj); + break; + } + + /* JSONString */ + case '"': { + WCHAR *string; + jsstr_t *str; + + hres = parse_json_string(ctx, &string); + if(FAILED(hres)) + return hres; + + /* FIXME: avoid reallocation */ + str = jsstr_alloc(string); + heap_free(string); + if(!str) + return E_OUTOFMEMORY; + + *r = jsval_string(str); + return S_OK; + } + + /* JSONArray */ + case '[': { + jsdisp_t *array; + unsigned i = 0; + jsval_t val; + + hres = create_array(ctx->ctx, 0, &array); + if(FAILED(hres)) + return hres; + + ctx->ptr++; + if(skip_spaces(ctx) == ']') { + ctx->ptr++; + *r = jsval_obj(array); + return S_OK; + } + + while(1) { + hres = parse_json_value(ctx, &val); + if(FAILED(hres)) + break; + + hres = jsdisp_propput_idx(array, i, val); + jsval_release(val); + if(FAILED(hres)) + break; + + if(skip_spaces(ctx) == ']') { + ctx->ptr++; + *r = jsval_obj(array); + return S_OK; + } + + if(*ctx->ptr != ',') { + FIXME("expected ','\n"); + break; + } + + ctx->ptr++; + i++; + } + + jsdisp_release(array); + break; + } + + /* JSONNumber */ + default: { + int sign = 1; + double n; + + if(*ctx->ptr == '-') { + sign = -1; + ctx->ptr++; + skip_spaces(ctx); + } + + if(!isdigitW(*ctx->ptr)) + break; + + if(*ctx->ptr == '0') { + ctx->ptr++; + n = 0; + if(is_identifier_char(*ctx->ptr)) + break; + }else { + hres = parse_decimal(&ctx->ptr, ctx->end, &n); + if(FAILED(hres)) + return hres; + } + + *r = jsval_number(sign*n); + return S_OK; + } + } + + FIXME("Syntax error at %s\n", debugstr_w(ctx->ptr)); + return E_FAIL; +} + +/* ECMA-262 5.1 Edition 15.12.2 */ +static HRESULT JSON_parse(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + json_parse_ctx_t parse_ctx; + const WCHAR *buf; + jsstr_t *str; + jsval_t ret; + HRESULT hres; + + if(argc != 1) { + FIXME("Unsupported args\n"); + return E_INVALIDARG; + } + + hres = to_flat_string(ctx, argv[0], &str, &buf); + if(FAILED(hres)) + return hres; + + TRACE("%s\n", debugstr_w(buf)); + + parse_ctx.ptr = buf; + parse_ctx.end = buf + jsstr_length(str); + parse_ctx.ctx = ctx; + hres = parse_json_value(&parse_ctx, &ret); + jsstr_release(str); + if(FAILED(hres)) + return hres; + + if(skip_spaces(&parse_ctx)) { + FIXME("syntax error\n"); + jsval_release(ret); + return E_FAIL; + } + + if(r) + *r = ret; + else + jsval_release(ret); + return S_OK; +} + +typedef struct { + script_ctx_t *ctx; + + WCHAR *buf; + size_t buf_size; + size_t buf_len; + + jsdisp_t **stack; + size_t stack_top; + size_t stack_size; + + WCHAR gap[11]; /* according to the spec, it's no longer than 10 chars */ +} stringify_ctx_t; + +static BOOL stringify_push_obj(stringify_ctx_t *ctx, jsdisp_t *obj) +{ + if(!ctx->stack_size) { + ctx->stack = heap_alloc(4*sizeof(*ctx->stack)); + if(!ctx->stack) + return FALSE; + ctx->stack_size = 4; + }else if(ctx->stack_top == ctx->stack_size) { + jsdisp_t **new_stack; + + new_stack = heap_realloc(ctx->stack, ctx->stack_size*2*sizeof(*ctx->stack)); + if(!new_stack) + return FALSE; + ctx->stack = new_stack; + ctx->stack_size *= 2; + } + + ctx->stack[ctx->stack_top++] = obj; + return TRUE; +} + +static void stringify_pop_obj(stringify_ctx_t *ctx) +{ + ctx->stack_top--; +} + +static BOOL is_on_stack(stringify_ctx_t *ctx, jsdisp_t *obj) +{ + size_t i = ctx->stack_top; + while(i--) { + if(ctx->stack[i] == obj) + return TRUE; + } + return FALSE; +} + +static BOOL append_string_len(stringify_ctx_t *ctx, const WCHAR *str, size_t len) +{ + if(!ctx->buf_size) { + ctx->buf = heap_alloc(len*2*sizeof(WCHAR)); + if(!ctx->buf) + return FALSE; + ctx->buf_size = len*2; + }else if(ctx->buf_len + len > ctx->buf_size) { + WCHAR *new_buf; + size_t new_size; + + new_size = ctx->buf_size * 2 + len; + new_buf = heap_realloc(ctx->buf, new_size*sizeof(WCHAR)); + if(!new_buf) + return FALSE; + ctx->buf = new_buf; + ctx->buf_size = new_size; + } + + if(len) + memcpy(ctx->buf + ctx->buf_len, str, len*sizeof(WCHAR)); + ctx->buf_len += len; + return TRUE; +} + +static inline BOOL append_string(stringify_ctx_t *ctx, const WCHAR *str) +{ + return append_string_len(ctx, str, strlenW(str)); +} + +static inline BOOL append_char(stringify_ctx_t *ctx, WCHAR c) +{ + return append_string_len(ctx, &c, 1); +} + +static inline BOOL append_simple_quote(stringify_ctx_t *ctx, WCHAR c) +{ + WCHAR str[] = {'\\',c}; + return append_string_len(ctx, str, 2); +} + +static HRESULT maybe_to_primitive(script_ctx_t *ctx, jsval_t val, jsval_t *r) +{ + jsdisp_t *obj; + HRESULT hres; + + if(!is_object_instance(val) || !get_object(val) || !(obj = iface_to_jsdisp((IUnknown*)get_object(val)))) + return jsval_copy(val, r); + + if(is_class(obj, JSCLASS_NUMBER)) { + double n; + hres = to_number(ctx, val, &n); + jsdisp_release(obj); + if(SUCCEEDED(hres)) + *r = jsval_number(n); + return hres; + } + + if(is_class(obj, JSCLASS_STRING)) { + jsstr_t *str; + hres = to_string(ctx, val, &str); + jsdisp_release(obj); + if(SUCCEEDED(hres)) + *r = jsval_string(str); + return hres; + } + + if(is_class(obj, JSCLASS_BOOLEAN)) { + *r = jsval_bool(bool_obj_value(obj)); + jsdisp_release(obj); + return S_OK; + } + + *r = jsval_obj(obj); + return S_OK; +} + +/* ECMA-262 5.1 Edition 15.12.3 (abstract operation Quote) */ +static HRESULT json_quote(stringify_ctx_t *ctx, const WCHAR *ptr, size_t len) +{ + if(!ptr || !append_char(ctx, '"')) + return E_OUTOFMEMORY; + + while(len--) { + switch(*ptr) { + case '"': + case '\\': + if(!append_simple_quote(ctx, *ptr)) + return E_OUTOFMEMORY; + break; + case '\b': + if(!append_simple_quote(ctx, 'b')) + return E_OUTOFMEMORY; + break; + case '\f': + if(!append_simple_quote(ctx, 'f')) + return E_OUTOFMEMORY; + break; + case '\n': + if(!append_simple_quote(ctx, 'n')) + return E_OUTOFMEMORY; + break; + case '\r': + if(!append_simple_quote(ctx, 'r')) + return E_OUTOFMEMORY; + break; + case '\t': + if(!append_simple_quote(ctx, 't')) + return E_OUTOFMEMORY; + break; + default: + if(*ptr < ' ') { + const WCHAR formatW[] = {'\\','u','%','0','4','x',0}; + WCHAR buf[7]; + sprintfW(buf, formatW, *ptr); + if(!append_string(ctx, buf)) + return E_OUTOFMEMORY; + }else { + if(!append_char(ctx, *ptr)) + return E_OUTOFMEMORY; + } + } + ptr++; + } + + return append_char(ctx, '"') ? S_OK : E_OUTOFMEMORY; +} + +static inline BOOL is_callable(jsdisp_t *obj) +{ + return is_class(obj, JSCLASS_FUNCTION); +} + +static HRESULT stringify(stringify_ctx_t *ctx, jsval_t val); + +/* ECMA-262 5.1 Edition 15.12.3 (abstract operation JA) */ +static HRESULT stringify_array(stringify_ctx_t *ctx, jsdisp_t *obj) +{ + unsigned length, i, j; + jsval_t val; + HRESULT hres; + + if(is_on_stack(ctx, obj)) { + FIXME("Found a cycle\n"); + return E_FAIL; + } + + if(!stringify_push_obj(ctx, obj)) + return E_OUTOFMEMORY; + + if(!append_char(ctx, '[')) + return E_OUTOFMEMORY; + + length = array_get_length(obj); + + for(i=0; i < length; i++) { + if(i && !append_char(ctx, ',')) + return E_OUTOFMEMORY; + + if(*ctx->gap) { + if(!append_char(ctx, '\n')) + return E_OUTOFMEMORY; + + for(j=0; j < ctx->stack_top; j++) { + if(!append_string(ctx, ctx->gap)) + return E_OUTOFMEMORY; + } + } + + hres = jsdisp_get_idx(obj, i, &val); + if(FAILED(hres)) + return hres; + + hres = stringify(ctx, val); + if(FAILED(hres)) + return hres; + + if(hres == S_FALSE && !append_string(ctx, nullW)) + return E_OUTOFMEMORY; + } + + if((length && *ctx->gap && !append_char(ctx, '\n')) || !append_char(ctx, ']')) + return E_OUTOFMEMORY; + + stringify_pop_obj(ctx); + return S_OK; +} + +/* ECMA-262 5.1 Edition 15.12.3 (abstract operation JO) */ +static HRESULT stringify_object(stringify_ctx_t *ctx, jsdisp_t *obj) +{ + DISPID dispid = DISPID_STARTENUM; + jsval_t val = jsval_undefined(); + unsigned prop_cnt = 0, i; + size_t stepback; + BSTR prop_name; + HRESULT hres; + + if(is_on_stack(ctx, obj)) { + FIXME("Found a cycle\n"); + return E_FAIL; + } + + if(!stringify_push_obj(ctx, obj)) + return E_OUTOFMEMORY; + + if(!append_char(ctx, '{')) + return E_OUTOFMEMORY; + + while((hres = IDispatchEx_GetNextDispID(&obj->IDispatchEx_iface, fdexEnumDefault, dispid, &dispid)) == S_OK) { + jsval_release(val); + hres = jsdisp_propget(obj, dispid, &val); + if(FAILED(hres)) + return hres; + + if(is_undefined(val)) + continue; + + stepback = ctx->buf_len; + + if(prop_cnt && !append_char(ctx, ',')) { + hres = E_OUTOFMEMORY; + break; + } + + if(*ctx->gap) { + if(!append_char(ctx, '\n')) { + hres = E_OUTOFMEMORY; + break; + } + + for(i=0; i < ctx->stack_top; i++) { + if(!append_string(ctx, ctx->gap)) { + hres = E_OUTOFMEMORY; + break; + } + } + } + + hres = IDispatchEx_GetMemberName(&obj->IDispatchEx_iface, dispid, &prop_name); + if(FAILED(hres)) + break; + + hres = json_quote(ctx, prop_name, SysStringLen(prop_name)); + SysFreeString(prop_name); + if(FAILED(hres)) + break; + + if(!append_char(ctx, ':') || (*ctx->gap && !append_char(ctx, ' '))) { + hres = E_OUTOFMEMORY; + break; + } + + hres = stringify(ctx, val); + if(FAILED(hres)) + break; + + if(hres == S_FALSE) { + ctx->buf_len = stepback; + continue; + } + + prop_cnt++; + } + jsval_release(val); + if(FAILED(hres)) + return hres; + + if(prop_cnt && *ctx->gap) { + if(!append_char(ctx, '\n')) + return E_OUTOFMEMORY; + + for(i=1; i < ctx->stack_top; i++) { + if(!append_string(ctx, ctx->gap)) { + hres = E_OUTOFMEMORY; + break; + } + } + } + + if(!append_char(ctx, '}')) + return E_OUTOFMEMORY; + + stringify_pop_obj(ctx); + return S_OK; +} + +/* ECMA-262 5.1 Edition 15.12.3 (abstract operation Str) */ +static HRESULT stringify(stringify_ctx_t *ctx, jsval_t val) +{ + jsval_t value; + HRESULT hres; + + if(is_object_instance(val) && get_object(val)) { + jsdisp_t *obj; + DISPID id; + + obj = iface_to_jsdisp((IUnknown*)get_object(val)); + if(!obj) + return S_FALSE; + + hres = jsdisp_get_id(obj, toJSONW, 0, &id); + jsdisp_release(obj); + if(hres == S_OK) + FIXME("Use toJSON.\n"); + } + + /* FIXME: Support replacer replacer. */ + + hres = maybe_to_primitive(ctx->ctx, val, &value); + if(FAILED(hres)) + return hres; + + switch(jsval_type(value)) { + case JSV_NULL: + if(!append_string(ctx, nullW)) + hres = E_OUTOFMEMORY; + break; + case JSV_BOOL: + if(!append_string(ctx, get_bool(value) ? trueW : falseW)) + hres = E_OUTOFMEMORY; + break; + case JSV_STRING: { + jsstr_t *str = get_string(value); + const WCHAR *ptr = jsstr_flatten(str); + if(ptr) + hres = json_quote(ctx, ptr, jsstr_length(str)); + else + hres = E_OUTOFMEMORY; + break; + } + case JSV_NUMBER: { + double n = get_number(value); + if(is_finite(n)) { + const WCHAR *ptr; + jsstr_t *str; + + /* FIXME: Optimize. There is no need for jsstr_t here. */ + hres = double_to_string(n, &str); + if(FAILED(hres)) + break; + + ptr = jsstr_flatten(str); + assert(ptr != NULL); + hres = ptr && !append_string_len(ctx, ptr, jsstr_length(str)) ? E_OUTOFMEMORY : S_OK; + jsstr_release(str); + }else { + if(!append_string(ctx, nullW)) + hres = E_OUTOFMEMORY; + } + break; + } + case JSV_OBJECT: { + jsdisp_t *obj; + + obj = iface_to_jsdisp((IUnknown*)get_object(value)); + if(!obj) { + hres = S_FALSE; + break; + } + + if(!is_callable(obj)) + hres = is_class(obj, JSCLASS_ARRAY) ? stringify_array(ctx, obj) : stringify_object(ctx, obj); + else + hres = S_FALSE; + + jsdisp_release(obj); + break; + } + case JSV_UNDEFINED: + hres = S_FALSE; + break; + case JSV_VARIANT: + FIXME("VARIANT\n"); + hres = E_NOTIMPL; + break; + } + + jsval_release(value); + return hres; +} + +/* ECMA-262 5.1 Edition 15.12.3 */ +static HRESULT JSON_stringify(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + stringify_ctx_t stringify_ctx = {ctx, NULL,0,0, NULL,0,0, {0}}; + HRESULT hres; + + TRACE("\n"); + + if(argc >= 2 && is_object_instance(argv[1])) { + FIXME("Replacer %s not yet supported\n", debugstr_jsval(argv[1])); + return E_NOTIMPL; + } + + if(argc >= 3) { + jsval_t space_val; + + hres = maybe_to_primitive(ctx, argv[2], &space_val); + if(FAILED(hres)) + return hres; + + if(is_number(space_val)) { + double n = get_number(space_val); + if(n >= 1) { + int i, len; + if(n > 10) + n = 10; + len = floor(n); + for(i=0; i < len; i++) + stringify_ctx.gap[i] = ' '; + stringify_ctx.gap[len] = 0; + } + }else if(is_string(space_val)) { + jsstr_t *space_str = get_string(space_val); + size_t len = jsstr_length(space_str); + if(len > 10) + len = 10; + jsstr_extract(space_str, 0, len, stringify_ctx.gap); + } + + jsval_release(space_val); + } + + hres = stringify(&stringify_ctx, argv[0]); + if(SUCCEEDED(hres) && r) { + assert(!stringify_ctx.stack_top); + + if(hres == S_OK) { + jsstr_t *ret = jsstr_alloc_len(stringify_ctx.buf, stringify_ctx.buf_len); + if(ret) + *r = jsval_string(ret); + else + hres = E_OUTOFMEMORY; + }else { + *r = jsval_undefined(); + } + } + + heap_free(stringify_ctx.buf); + heap_free(stringify_ctx.stack); + return hres; +} + +static const builtin_prop_t JSON_props[] = { + {parseW, JSON_parse, PROPF_METHOD|2}, + {stringifyW, JSON_stringify, PROPF_METHOD|3} +}; + +static const builtin_info_t JSON_info = { + JSCLASS_JSON, + {NULL, NULL, 0}, + sizeof(JSON_props)/sizeof(*JSON_props), + JSON_props, + NULL, + NULL +}; + +HRESULT create_json(script_ctx_t *ctx, jsdisp_t **ret) +{ + jsdisp_t *json; + HRESULT hres; + + json = heap_alloc_zero(sizeof(*json)); + if(!json) + return E_OUTOFMEMORY; + + hres = init_dispex_from_constr(json, ctx, &JSON_info, ctx->object_constr); + if(FAILED(hres)) { + heap_free(json); + return hres; + } + + *ret = json; + return S_OK; +} diff --git a/reactos/dll/win32/jscript/jsutils.c b/reactos/dll/win32/jscript/jsutils.c index ca12592bd96..0edb3cf2e40 100644 --- a/reactos/dll/win32/jscript/jsutils.c +++ b/reactos/dll/win32/jscript/jsutils.c @@ -43,6 +43,11 @@ const char *debugstr_jsval(const jsval_t v) return NULL; } +BOOL is_finite(double n) +{ + return !isnan(n) && !isinf(n); +} + #define MIN_BLOCK_SIZE 128 #define ARENA_FREE_FILLER 0xaa @@ -631,7 +636,7 @@ HRESULT to_int32(script_ctx_t *ctx, jsval_t v, INT *ret) if(FAILED(hres)) return hres; - *ret = isnan(n) || isinf(n) ? 0 : n; + *ret = is_finite(n) ? n : 0; return S_OK; } diff --git a/reactos/dll/win32/jscript/lex.c b/reactos/dll/win32/jscript/lex.c index 98c5e40b97d..b4244ab26f5 100644 --- a/reactos/dll/win32/jscript/lex.c +++ b/reactos/dll/win32/jscript/lex.c @@ -20,8 +20,6 @@ #include "parser.tab.h" -#define LONGLONG_MAX (((LONGLONG)0x7fffffff<<32)|0xffffffff) - static const WCHAR breakW[] = {'b','r','e','a','k',0}; static const WCHAR caseW[] = {'c','a','s','e',0}; static const WCHAR catchW[] = {'c','a','t','c','h',0}; @@ -97,7 +95,7 @@ static int lex_error(parser_ctx_t *ctx, HRESULT hres) } /* ECMA-262 3rd Edition 7.6 */ -static BOOL is_identifier_char(WCHAR c) +BOOL is_identifier_char(WCHAR c) { return isalnumW(c) || c == '$' || c == '_' || c == '\\'; } @@ -237,7 +235,7 @@ static BOOL skip_spaces(parser_ctx_t *ctx) return ctx->ptr != ctx->end; } -static BOOL unescape(WCHAR *str) +BOOL unescape(WCHAR *str) { WCHAR *pd, *p, c; int i; @@ -394,14 +392,14 @@ literal_t *new_boolean_literal(parser_ctx_t *ctx, BOOL bval) return ret; } -static BOOL parse_double_literal(parser_ctx_t *ctx, LONG int_part, double *ret) +HRESULT parse_decimal(const WCHAR **iter, const WCHAR *end, double *ret) { - LONGLONG d, hlp; + const WCHAR *ptr = *iter; + LONGLONG d = 0, hlp; int exp = 0; - d = int_part; - while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) { - hlp = d*10 + *(ctx->ptr++) - '0'; + while(ptr < end && isdigitW(*ptr)) { + hlp = d*10 + *(ptr++) - '0'; if(d>MAXLONGLONG/10 || hlp<0) { exp++; break; @@ -409,51 +407,48 @@ static BOOL parse_double_literal(parser_ctx_t *ctx, LONG int_part, double *ret) else d = hlp; } - while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) { + while(ptr < end && isdigitW(*ptr)) { exp++; - ctx->ptr++; + ptr++; } - if(*ctx->ptr == '.') { - ctx->ptr++; + if(*ptr == '.') { + ptr++; - while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) { - hlp = d*10 + *(ctx->ptr++) - '0'; + while(ptr < end && isdigitW(*ptr)) { + hlp = d*10 + *(ptr++) - '0'; if(d>MAXLONGLONG/10 || hlp<0) break; d = hlp; exp--; } - while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) - ctx->ptr++; + while(ptr < end && isdigitW(*ptr)) + ptr++; } - if(ctx->ptr < ctx->end && (*ctx->ptr == 'e' || *ctx->ptr == 'E')) { + if(ptr < end && (*ptr == 'e' || *ptr == 'E')) { int sign = 1, e = 0; - ctx->ptr++; - if(ctx->ptr < ctx->end) { - if(*ctx->ptr == '+') { - ctx->ptr++; - }else if(*ctx->ptr == '-') { + if(++ptr < end) { + if(*ptr == '+') { + ptr++; + }else if(*ptr == '-') { sign = -1; - ctx->ptr++; - }else if(!isdigitW(*ctx->ptr)) { + ptr++; + }else if(!isdigitW(*ptr)) { WARN("Expected exponent part\n"); - lex_error(ctx, E_FAIL); - return FALSE; + return E_FAIL; } } - if(ctx->ptr == ctx->end) { + if(ptr == end) { WARN("unexpected end of file\n"); - lex_error(ctx, E_FAIL); - return FALSE; + return E_FAIL; } - while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) { - if(e > INT_MAX/10 || (e = e*10 + *ctx->ptr++ - '0')<0) + while(ptr < end && isdigitW(*ptr)) { + if(e > INT_MAX/10 || (e = e*10 + *ptr++ - '0')<0) e = INT_MAX; } e *= sign; @@ -463,22 +458,25 @@ static BOOL parse_double_literal(parser_ctx_t *ctx, LONG int_part, double *ret) else exp += e; } - if(is_identifier_char(*ctx->ptr)) { + if(is_identifier_char(*ptr)) { WARN("wrong char after zero\n"); - lex_error(ctx, JS_E_MISSING_SEMICOLON); - return FALSE; + return JS_E_MISSING_SEMICOLON; } *ret = exp>=0 ? d*pow(10, exp) : d/pow(10, -exp); - return TRUE; + *iter = ptr; + return S_OK; } static BOOL parse_numeric_literal(parser_ctx_t *ctx, double *ret) { - LONG l, d; + HRESULT hres; + + if(*ctx->ptr == '0') { + LONG d, l = 0; + + ctx->ptr++; - l = *ctx->ptr++ - '0'; - if(!l) { if(*ctx->ptr == 'x' || *ctx->ptr == 'X') { if(++ctx->ptr == ctx->end) { ERR("unexpected end of file\n"); @@ -534,7 +532,13 @@ static BOOL parse_numeric_literal(parser_ctx_t *ctx, double *ret) } } - return parse_double_literal(ctx, l, ret); + hres = parse_decimal(&ctx->ptr, ctx->end, ret); + if(FAILED(hres)) { + lex_error(ctx, hres); + return FALSE; + } + + return TRUE; } static int next_token(parser_ctx_t *ctx, void *lval) @@ -587,8 +591,12 @@ static int next_token(parser_ctx_t *ctx, void *lval) case '.': if(++ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) { double n; - if(!parse_double_literal(ctx, 0, &n)) + HRESULT hres; + hres = parse_decimal(&ctx->ptr, ctx->end, &n); + if(FAILED(hres)) { + lex_error(ctx, hres); return -1; + } *(literal_t**)lval = new_double_literal(ctx, n); return tNumericLiteral; } diff --git a/reactos/dll/win32/jscript/number.c b/reactos/dll/win32/jscript/number.c index cebcc9f3ef7..a5f6e650262 100644 --- a/reactos/dll/win32/jscript/number.c +++ b/reactos/dll/win32/jscript/number.c @@ -247,7 +247,7 @@ static HRESULT Number_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, u val = number->value; - if(radix==10 || isnan(val) || isinf(val)) { + if(radix==10 || !is_finite(val)) { hres = to_string(ctx, jsval_number(val), &str); if(FAILED(hres)) return hres; @@ -373,7 +373,7 @@ static HRESULT Number_toFixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, un } val = number->value; - if(isinf(val) || isnan(val)) { + if(!is_finite(val)) { hres = to_string(ctx, jsval_number(val), &str); if(FAILED(hres)) return hres; @@ -414,7 +414,7 @@ static HRESULT Number_toExponential(script_ctx_t *ctx, vdisp_t *jsthis, WORD fla } val = number->value; - if(isinf(val) || isnan(val)) { + if(!is_finite(val)) { hres = to_string(ctx, jsval_number(val), &str); if(FAILED(hres)) return hres; @@ -455,7 +455,7 @@ static HRESULT Number_toPrecision(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags } val = number->value; - if(isinf(val) || isnan(val) || !prec) { + if(!is_finite(val) || !prec) { hres = to_string(ctx, jsval_number(val), &str); if(FAILED(hres)) return hres; diff --git a/reactos/dll/win32/jscript/object.c b/reactos/dll/win32/jscript/object.c index 457bc859bf0..86cbebbaafa 100644 --- a/reactos/dll/win32/jscript/object.c +++ b/reactos/dll/win32/jscript/object.c @@ -48,7 +48,7 @@ static HRESULT Object_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, u static const WCHAR stringW[] = {'S','t','r','i','n','g',0}; /* Keep in sync with jsclass_t enum */ static const WCHAR *names[] = {NULL, arrayW, booleanW, dateW, errorW, - functionW, NULL, mathW, numberW, objectW, regexpW, stringW, objectW, objectW}; + functionW, NULL, mathW, numberW, objectW, regexpW, stringW, objectW, objectW, objectW}; TRACE("\n"); diff --git a/reactos/dll/win32/jscript/parser.h b/reactos/dll/win32/jscript/parser.h index 9b7f9961db4..2bc8ff6c051 100644 --- a/reactos/dll/win32/jscript/parser.h +++ b/reactos/dll/win32/jscript/parser.h @@ -64,6 +64,10 @@ static inline void *parser_alloc_tmp(parser_ctx_t *ctx, DWORD size) return heap_pool_alloc(&ctx->script->tmp_heap, size); } +BOOL is_identifier_char(WCHAR) DECLSPEC_HIDDEN; +BOOL unescape(WCHAR*) DECLSPEC_HIDDEN; +HRESULT parse_decimal(const WCHAR**,const WCHAR*,double*) DECLSPEC_HIDDEN; + typedef enum { LT_DOUBLE, LT_STRING, diff --git a/reactos/media/doc/README.WINE b/reactos/media/doc/README.WINE index 6c537839518..6f42be08fdb 100644 --- a/reactos/media/doc/README.WINE +++ b/reactos/media/doc/README.WINE @@ -85,7 +85,7 @@ reactos/dll/win32/inseng # Synced to WineStaging-1.7.55 reactos/dll/win32/iphlpapi # Out of sync reactos/dll/win32/itircl # Synced to WineStaging-1.7.55 reactos/dll/win32/itss # Synced to WineStaging-1.7.55 -reactos/dll/win32/jscript # Synced to WineStaging-1.7.55 +reactos/dll/win32/jscript # Synced to WineStaging-1.9.4 reactos/dll/win32/jsproxy # Synced to WineStaging-1.7.55 reactos/dll/win32/loadperf # Synced to WineStaging-1.7.55 reactos/dll/win32/localspl # Synced to WineStaging-1.7.55 -- 2.17.1