Jacek Caban : jscript: Added JSON.parse implementation.

Alexandre Julliard julliard at wine.codeweavers.com
Thu Jan 28 10:06:44 CST 2016


Module: wine
Branch: master
Commit: f0be56e17c39c8b59a7bcb4c3081fae3fa49ded9
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=f0be56e17c39c8b59a7bcb4c3081fae3fa49ded9

Author: Jacek Caban <jacek at codeweavers.com>
Date:   Wed Jan 27 20:43:41 2016 +0100

jscript: Added JSON.parse implementation.

Signed-off-by: Jacek Caban <jacek at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/jscript/json.c   | 292 +++++++++++++++++++++++++++++++++++++++++++++++++-
 dlls/jscript/lex.c    |   6 +-
 dlls/jscript/parser.h |   4 +
 3 files changed, 297 insertions(+), 5 deletions(-)

diff --git a/dlls/jscript/json.c b/dlls/jscript/json.c
index 6e33a88..01c780e 100644
--- a/dlls/jscript/json.c
+++ b/dlls/jscript/json.c
@@ -19,6 +19,7 @@
 #include <math.h>
 
 #include "jscript.h"
+#include "parser.h"
 
 #include "wine/debug.h"
 #include "wine/unicode.h"
@@ -28,10 +29,297 @@ WINE_DEFAULT_DEBUG_CHANNEL(jscript);
 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};
+
+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)
 {
-    FIXME("\n");
-    return E_NOTIMPL;
+    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;
 }
 
 static HRESULT JSON_stringify(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
diff --git a/dlls/jscript/lex.c b/dlls/jscript/lex.c
index dd788f0..65b16a6 100644
--- a/dlls/jscript/lex.c
+++ b/dlls/jscript/lex.c
@@ -109,7 +109,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 == '\\';
 }
@@ -249,7 +249,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;
@@ -406,7 +406,7 @@ literal_t *new_boolean_literal(parser_ctx_t *ctx, BOOL bval)
     return ret;
 }
 
-static HRESULT parse_decimal(const WCHAR **iter, const WCHAR *end, double *ret)
+HRESULT parse_decimal(const WCHAR **iter, const WCHAR *end, double *ret)
 {
     const WCHAR *ptr = *iter;
     LONGLONG d = 0, hlp;
diff --git a/dlls/jscript/parser.h b/dlls/jscript/parser.h
index f8d404f..c3dcb82 100644
--- a/dlls/jscript/parser.h
+++ b/dlls/jscript/parser.h
@@ -62,6 +62,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,




More information about the wine-cvs mailing list