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