mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-06 16:45:48 +00:00
jscript: Implement reviver
argument for JSON.parse.
Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com>
This commit is contained in:
parent
a4697901cc
commit
2dcc5a7026
|
@ -93,7 +93,7 @@ static HRESULT set_length(jsdisp_t *obj, DWORD length)
|
|||
return jsdisp_propput_name(obj, L"length", jsval_number(length));
|
||||
}
|
||||
|
||||
static WCHAR *idx_to_str(DWORD idx, WCHAR *ptr)
|
||||
WCHAR *idx_to_str(DWORD idx, WCHAR *ptr)
|
||||
{
|
||||
if(!idx) {
|
||||
*ptr = '0';
|
||||
|
|
|
@ -318,6 +318,7 @@ HRESULT variant_date_to_string(script_ctx_t*,double,jsstr_t**) DECLSPEC_HIDDEN;
|
|||
HRESULT decode_source(WCHAR*) DECLSPEC_HIDDEN;
|
||||
|
||||
HRESULT double_to_string(double,jsstr_t**) DECLSPEC_HIDDEN;
|
||||
WCHAR *idx_to_str(DWORD,WCHAR*) DECLSPEC_HIDDEN;
|
||||
|
||||
static inline BOOL is_digit(WCHAR c)
|
||||
{
|
||||
|
|
|
@ -267,20 +267,104 @@ static HRESULT parse_json_value(json_parse_ctx_t *ctx, jsval_t *r)
|
|||
return E_FAIL;
|
||||
}
|
||||
|
||||
struct transform_json_object_ctx
|
||||
{
|
||||
script_ctx_t *ctx;
|
||||
IDispatch *reviver;
|
||||
HRESULT hres;
|
||||
};
|
||||
|
||||
static jsval_t transform_json_object(struct transform_json_object_ctx *proc_ctx, jsdisp_t *holder, jsstr_t *name)
|
||||
{
|
||||
jsval_t res, args[2];
|
||||
const WCHAR *str;
|
||||
|
||||
if(!(str = jsstr_flatten(name)))
|
||||
proc_ctx->hres = E_OUTOFMEMORY;
|
||||
else
|
||||
proc_ctx->hres = jsdisp_propget_name(holder, str, &args[1]);
|
||||
if(FAILED(proc_ctx->hres))
|
||||
return jsval_undefined();
|
||||
|
||||
if(is_object_instance(args[1])) {
|
||||
jsdisp_t *obj = to_jsdisp(get_object(args[1]));
|
||||
jsstr_t *jsstr;
|
||||
DISPID id;
|
||||
BOOL b;
|
||||
|
||||
if(!obj) {
|
||||
FIXME("non-JS obj in JSON object: %p\n", get_object(args[1]));
|
||||
proc_ctx->hres = E_NOTIMPL;
|
||||
return jsval_undefined();
|
||||
}else if(is_class(obj, JSCLASS_ARRAY)) {
|
||||
unsigned i, length = array_get_length(obj);
|
||||
WCHAR buf[14], *buf_end;
|
||||
|
||||
buf_end = buf + ARRAY_SIZE(buf) - 1;
|
||||
*buf_end-- = 0;
|
||||
for(i = 0; i < length; i++) {
|
||||
str = idx_to_str(i, buf_end);
|
||||
if(!(jsstr = jsstr_alloc(str))) {
|
||||
proc_ctx->hres = E_OUTOFMEMORY;
|
||||
return jsval_undefined();
|
||||
}
|
||||
res = transform_json_object(proc_ctx, obj, jsstr);
|
||||
jsstr_release(jsstr);
|
||||
if(is_undefined(res)) {
|
||||
if(FAILED(proc_ctx->hres))
|
||||
return jsval_undefined();
|
||||
if(FAILED(jsdisp_get_id(obj, str, 0, &id)))
|
||||
continue;
|
||||
proc_ctx->hres = disp_delete((IDispatch*)&obj->IDispatchEx_iface, id, &b);
|
||||
}else {
|
||||
proc_ctx->hres = jsdisp_define_data_property(obj, str, PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE, res);
|
||||
jsval_release(res);
|
||||
}
|
||||
if(FAILED(proc_ctx->hres))
|
||||
return jsval_undefined();
|
||||
}
|
||||
}else {
|
||||
id = DISPID_STARTENUM;
|
||||
for(;;) {
|
||||
proc_ctx->hres = jsdisp_next_prop(obj, id, JSDISP_ENUM_OWN_ENUMERABLE, &id);
|
||||
if(proc_ctx->hres == S_FALSE)
|
||||
break;
|
||||
if(FAILED(proc_ctx->hres) || FAILED(proc_ctx->hres = jsdisp_get_prop_name(obj, id, &jsstr)))
|
||||
return jsval_undefined();
|
||||
res = transform_json_object(proc_ctx, obj, jsstr);
|
||||
if(is_undefined(res)) {
|
||||
if(SUCCEEDED(proc_ctx->hres))
|
||||
proc_ctx->hres = disp_delete((IDispatch*)&obj->IDispatchEx_iface, id, &b);
|
||||
}else {
|
||||
if(!(str = jsstr_flatten(jsstr)))
|
||||
proc_ctx->hres = E_OUTOFMEMORY;
|
||||
else
|
||||
proc_ctx->hres = jsdisp_define_data_property(obj, str, PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE, res);
|
||||
jsval_release(res);
|
||||
}
|
||||
jsstr_release(jsstr);
|
||||
if(FAILED(proc_ctx->hres))
|
||||
return jsval_undefined();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
args[0] = jsval_string(name);
|
||||
proc_ctx->hres = disp_call_value(proc_ctx->ctx, proc_ctx->reviver, (IDispatch*)&holder->IDispatchEx_iface,
|
||||
DISPATCH_METHOD, ARRAY_SIZE(args), args, &res);
|
||||
return FAILED(proc_ctx->hres) ? jsval_undefined() : res;
|
||||
}
|
||||
|
||||
/* ECMA-262 5.1 Edition 15.12.2 */
|
||||
static HRESULT JSON_parse(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
|
||||
{
|
||||
json_parse_ctx_t parse_ctx;
|
||||
const WCHAR *buf;
|
||||
jsdisp_t *root;
|
||||
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;
|
||||
|
@ -293,12 +377,38 @@ static HRESULT JSON_parse(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned
|
|||
hres = parse_json_value(&parse_ctx, &ret);
|
||||
if(SUCCEEDED(hres) && skip_spaces(&parse_ctx)) {
|
||||
FIXME("syntax error\n");
|
||||
jsval_release(ret);
|
||||
hres = E_FAIL;
|
||||
}
|
||||
jsstr_release(str);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
|
||||
/* FIXME: check IsCallable */
|
||||
if(argc > 1 && is_object_instance(argv[1])) {
|
||||
hres = create_object(ctx, NULL, &root);
|
||||
if(FAILED(hres)) {
|
||||
jsval_release(ret);
|
||||
return hres;
|
||||
}
|
||||
hres = jsdisp_define_data_property(root, L"", PROPF_WRITABLE | PROPF_ENUMERABLE | PROPF_CONFIGURABLE, ret);
|
||||
jsval_release(ret);
|
||||
|
||||
if(SUCCEEDED(hres)) {
|
||||
struct transform_json_object_ctx proc_ctx = { ctx, get_object(argv[1]), S_OK };
|
||||
if(!(str = jsstr_alloc(L"")))
|
||||
hres = E_OUTOFMEMORY;
|
||||
else {
|
||||
ret = transform_json_object(&proc_ctx, root, str);
|
||||
jsstr_release(str);
|
||||
hres = proc_ctx.hres;
|
||||
}
|
||||
}
|
||||
jsdisp_release(root);
|
||||
if(FAILED(hres))
|
||||
return hres;
|
||||
}
|
||||
|
||||
if(r)
|
||||
*r = ret;
|
||||
else
|
||||
|
|
|
@ -1960,7 +1960,7 @@ ok(isNaN(tmp), "Math.tan(-Infinity) is not NaN");
|
|||
[[[,2,undefined,3,{prop:0},],undefined," "],"[\n null,\n 2,\n null,\n 3,\n {\n \"prop\": 0\n },\n null\n]"]
|
||||
];
|
||||
|
||||
var i, s, v;
|
||||
var i, s, v, t;
|
||||
|
||||
for(i=0; i < stringify_tests.length; i++) {
|
||||
s = JSON.stringify.apply(null, stringify_tests[i][0]);
|
||||
|
@ -2043,6 +2043,61 @@ ok(isNaN(tmp), "Math.tan(-Infinity) is not NaN");
|
|||
v = JSON.parse(parse_tests[i][0]);
|
||||
ok(json_cmp(v, parse_tests[i][1]), "parse[" + i + "] returned " + v + ", expected " + parse_tests[i][1]);
|
||||
}
|
||||
|
||||
v = [ [-1, "b"], {"length": -2, "0": -4, "1": -5}, [{}], [{"x": [null]}] ];
|
||||
s =
|
||||
'{' +
|
||||
'"foo": true,' +
|
||||
'"bar": [],' +
|
||||
'"baz": "remove_me",' +
|
||||
'"obj": {' +
|
||||
' "arr": [ [1, "b"], {"length": 2, "0": 4, "1": 5}, [{}], [{"x": [null]}] ],' +
|
||||
' "": "empty"' +
|
||||
'},' +
|
||||
'"last": false' +
|
||||
'}';
|
||||
o = JSON.parse(s), t = JSON.parse(s), i = new Object();
|
||||
i[""] = t;
|
||||
delete t.baz; /* baz gets removed */
|
||||
t.obj.arr = v; /* has negative values */
|
||||
|
||||
var walk_expect = [
|
||||
[ o, "foo", true ],
|
||||
[ o, "bar", [] ],
|
||||
[ o, "baz", "remove_me" ],
|
||||
[ [1, "b"], "0", 1 ],
|
||||
[ [-1, "b"], "1", "b" ],
|
||||
[ [ [-1, "b"], {"length": 2, "0": 4, "1": 5}, [{}], [{"x": [null]}] ], "0", [-1, "b"] ],
|
||||
[ {"length": 2, "0": 4, "1": 5}, "length", 2 ],
|
||||
[ {"length": -2, "0": 4, "1": 5}, "0", 4 ],
|
||||
[ {"length": -2, "0": -4, "1": 5}, "1", 5 ],
|
||||
[ v, "1", {"length": -2, "0": -4, "1": -5} ],
|
||||
[ [{}], "0", {} ],
|
||||
[ v, "2", [{}] ],
|
||||
[ [null], "0", null ],
|
||||
[ {"x": [null]}, "x", [null] ],
|
||||
[ [{"x": [null]}], "0", {"x": [null]} ],
|
||||
[ v, "3", [{"x": [null]}] ],
|
||||
[ { "arr": v, "": "empty" }, "arr", v ],
|
||||
[ { "arr": v, "": "empty" }, "", "empty" ],
|
||||
[ t, "obj", { "arr": v, "": "empty" } ],
|
||||
[ t, "last", false ],
|
||||
[ i, "", t ]
|
||||
];
|
||||
i = 0;
|
||||
v = JSON.parse(s, function(prop, value) {
|
||||
var a = [this, prop, value];
|
||||
ok(json_cmp(a, walk_expect[i]), "[walk step " + i + "] got [" + a + "], expected [" + walk_expect[i] + "]");
|
||||
i++;
|
||||
return (typeof value === 'number') ? -value : (value === "remove_me" ? undefined : value);
|
||||
});
|
||||
ok(i === walk_expect.length, "parse with reviver walked " + i + " steps, expected " + walk_expect.length);
|
||||
ok(json_cmp(v, t), "parse with reviver returned wrong object");
|
||||
|
||||
v = JSON.parse('true', function(prop, value) { return prop === "" ? undefined : value; });
|
||||
ok(v === undefined, "parse with reviver removing last prop returned " + v);
|
||||
v = JSON.parse('true', function(prop, value) { return prop === "" ? false : value; });
|
||||
ok(v === false, "parse with reviver setting last prop to false returned " + v);
|
||||
})();
|
||||
|
||||
var func = function (a) {
|
||||
|
|
Loading…
Reference in a new issue