[JSCRIPT] Sync with Wine Staging 1.9.4. CORE-10912
authorAmine Khaldi <amine.khaldi@reactos.org>
Thu, 3 Mar 2016 13:24:43 +0000 (13:24 +0000)
committerAmine Khaldi <amine.khaldi@reactos.org>
Thu, 3 Mar 2016 13:24:43 +0000 (13:24 +0000)
svn path=/trunk/; revision=70871

13 files changed:
reactos/dll/win32/jscript/CMakeLists.txt
reactos/dll/win32/jscript/array.c
reactos/dll/win32/jscript/bool.c
reactos/dll/win32/jscript/function.c
reactos/dll/win32/jscript/global.c
reactos/dll/win32/jscript/jscript.h
reactos/dll/win32/jscript/json.c [new file with mode: 0644]
reactos/dll/win32/jscript/jsutils.c
reactos/dll/win32/jscript/lex.c
reactos/dll/win32/jscript/number.c
reactos/dll/win32/jscript/object.c
reactos/dll/win32/jscript/parser.h
reactos/media/doc/README.WINE

index e7dde3c..fd4d91e 100644 (file)
@@ -21,6 +21,7 @@ list(APPEND SOURCE
     global.c
     jscript.c
     jscript_main.c
+    json.c
     jsregexp.c
     jsstr.c
     jsutils.c
index 41204b0..315d5d4 100644 (file)
@@ -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;
index dffaecb..1a6fee5 100644 (file)
@@ -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)
 {
index 52481c1..9186796 100644 (file)
@@ -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;
 
index f7caf11..35aa694 100644 (file)
@@ -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;
index ac6db25..02517fa 100644 (file)
@@ -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 (file)
index 0000000..648a8e6
--- /dev/null
@@ -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;
+}
index ca12592..0edb3cf 100644 (file)
@@ -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;
 }
 
index 98c5e40..b4244ab 100644 (file)
@@ -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;
         }
index cebcc9f..a5f6e65 100644 (file)
@@ -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;
index 457bc85..86cbebb 100644 (file)
@@ -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");
 
index 9b7f996..2bc8ff6 100644 (file)
@@ -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,
index 6c53783..6f42be0 100644 (file)
@@ -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