| /* |
| * 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 <math.h> |
| #include <assert.h> |
| |
| #include "jscript.h" |
| #include "parser.h" |
| |
| #include "wine/debug.h" |
| #include "wine/unicode.h" |
| |
| 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}; |
| |
| 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(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(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(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; |
| } |