jscript: Added JSON.parse implementation.

Signed-off-by: Jacek Caban <jacek@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Jacek Caban 2016-01-27 20:43:41 +01:00 committed by Alexandre Julliard
parent 4e1e2ee451
commit f0be56e17c
3 changed files with 297 additions and 5 deletions

View File

@ -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)

View File

@ -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;

View File

@ -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,