[JSCRIPT] Sync with Wine Staging 1.9.4. CORE-10912

svn path=/trunk/; revision=70871
This commit is contained in:
Amine Khaldi 2016-03-03 13:24:43 +00:00
parent 832c97fe3b
commit 07a14c5c02
13 changed files with 945 additions and 51 deletions

View file

@ -21,6 +21,7 @@ list(APPEND SOURCE
global.c
jscript.c
jscript_main.c
json.c
jsregexp.c
jsstr.c
jsutils.c

View file

@ -55,6 +55,12 @@ static inline ArrayInstance *array_this(vdisp_t *jsthis)
return is_vclass(jsthis, JSCLASS_ARRAY) ? array_from_vdisp(jsthis) : NULL;
}
unsigned array_get_length(jsdisp_t *array)
{
assert(is_class(array, JSCLASS_ARRAY));
return array_from_jsdisp(array)->length;
}
static HRESULT get_length(script_ctx_t *ctx, vdisp_t *vdisp, jsdisp_t **jsthis, DWORD *ret)
{
ArrayInstance *array;

View file

@ -33,6 +33,12 @@ static inline BoolInstance *bool_this(vdisp_t *jsthis)
return is_vclass(jsthis, JSCLASS_BOOLEAN) ? (BoolInstance*)jsthis->u.jsdisp : NULL;
}
BOOL bool_obj_value(jsdisp_t *obj)
{
assert(is_class(obj, JSCLASS_BOOLEAN));
return ((BoolInstance*)obj)->val;
}
/* ECMA-262 3rd Edition 15.6.4.2 */
static HRESULT Bool_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r)
{

View file

@ -814,6 +814,7 @@ static HRESULT FunctionConstr_value(script_ctx_t *ctx, vdisp_t *jsthis, WORD fla
TRACE("\n");
switch(flags) {
case DISPATCH_METHOD:
case DISPATCH_CONSTRUCT: {
IDispatch *ret;

View file

@ -57,6 +57,7 @@ static const WCHAR ScriptEngineBuildVersionW[] =
{'S','c','r','i','p','t','E','n','g','i','n','e','B','u','i','l','d','V','e','r','s','i','o','n',0};
static const WCHAR CollectGarbageW[] = {'C','o','l','l','e','c','t','G','a','r','b','a','g','e',0};
static const WCHAR MathW[] = {'M','a','t','h',0};
static const WCHAR JSONW[] = {'J','S','O','N',0};
static const WCHAR encodeURIW[] = {'e','n','c','o','d','e','U','R','I',0};
static const WCHAR decodeURIW[] = {'d','e','c','o','d','e','U','R','I',0};
static const WCHAR encodeURIComponentW[] = {'e','n','c','o','d','e','U','R','I','C','o','m','p','o','n','e','n','t',0};
@ -254,8 +255,7 @@ static HRESULT JSGlobal_isFinite(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags,
if(FAILED(hres))
return hres;
if(!isinf(n) && !isnan(n))
ret = TRUE;
ret = is_finite(n);
}
if(r)
@ -1098,6 +1098,19 @@ HRESULT init_global(script_ctx_t *ctx)
if(FAILED(hres))
return hres;
if(ctx->version >= 2) {
jsdisp_t *json;
hres = create_json(ctx, &json);
if(FAILED(hres))
return hres;
hres = jsdisp_propput_dontenum(ctx->global, JSONW, jsval_obj(json));
jsdisp_release(json);
if(FAILED(hres))
return hres;
}
hres = create_activex_constr(ctx, &constr);
if(FAILED(hres))
return hres;

View file

@ -131,7 +131,8 @@ typedef enum {
JSCLASS_REGEXP,
JSCLASS_STRING,
JSCLASS_ARGUMENTS,
JSCLASS_VBARRAY
JSCLASS_VBARRAY,
JSCLASS_JSON
} jsclass_t;
jsdisp_t *iface_to_jsdisp(IUnknown*) DECLSPEC_HIDDEN;
@ -331,6 +332,7 @@ HRESULT create_string(script_ctx_t*,jsstr_t*,jsdisp_t**) DECLSPEC_HIDDEN;
HRESULT create_bool(script_ctx_t*,BOOL,jsdisp_t**) DECLSPEC_HIDDEN;
HRESULT create_number(script_ctx_t*,double,jsdisp_t**) DECLSPEC_HIDDEN;
HRESULT create_vbarray(script_ctx_t*,SAFEARRAY*,jsdisp_t**) DECLSPEC_HIDDEN;
HRESULT create_json(script_ctx_t*,jsdisp_t**) DECLSPEC_HIDDEN;
typedef enum {
NO_HINT,
@ -353,6 +355,7 @@ HRESULT variant_change_type(script_ctx_t*,VARIANT*,VARIANT*,VARTYPE) DECLSPEC_HI
HRESULT decode_source(WCHAR*) DECLSPEC_HIDDEN;
HRESULT double_to_string(double,jsstr_t**) DECLSPEC_HIDDEN;
BOOL is_finite(double) DECLSPEC_HIDDEN;
typedef struct named_item_t {
IDispatch *disp;
@ -472,6 +475,9 @@ HRESULT regexp_match_next(script_ctx_t*,jsdisp_t*,DWORD,jsstr_t*,struct match_st
HRESULT parse_regexp_flags(const WCHAR*,DWORD,DWORD*) DECLSPEC_HIDDEN;
HRESULT regexp_string_match(script_ctx_t*,jsdisp_t*,jsstr_t*,jsval_t*) DECLSPEC_HIDDEN;
BOOL bool_obj_value(jsdisp_t*) DECLSPEC_HIDDEN;
unsigned array_get_length(jsdisp_t*) DECLSPEC_HIDDEN;
static inline BOOL is_class(jsdisp_t *jsdisp, jsclass_t class)
{
return jsdisp->builtin_info->class == class;

View file

@ -0,0 +1,844 @@
/*
* 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 "jscript.h"
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((IUnknown*)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((IUnknown*)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((IUnknown*)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;
}

View file

@ -43,6 +43,11 @@ const char *debugstr_jsval(const jsval_t v)
return NULL;
}
BOOL is_finite(double n)
{
return !isnan(n) && !isinf(n);
}
#define MIN_BLOCK_SIZE 128
#define ARENA_FREE_FILLER 0xaa
@ -631,7 +636,7 @@ HRESULT to_int32(script_ctx_t *ctx, jsval_t v, INT *ret)
if(FAILED(hres))
return hres;
*ret = isnan(n) || isinf(n) ? 0 : n;
*ret = is_finite(n) ? n : 0;
return S_OK;
}

View file

@ -20,8 +20,6 @@
#include "parser.tab.h"
#define LONGLONG_MAX (((LONGLONG)0x7fffffff<<32)|0xffffffff)
static const WCHAR breakW[] = {'b','r','e','a','k',0};
static const WCHAR caseW[] = {'c','a','s','e',0};
static const WCHAR catchW[] = {'c','a','t','c','h',0};
@ -97,7 +95,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 == '\\';
}
@ -237,7 +235,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;
@ -394,14 +392,14 @@ literal_t *new_boolean_literal(parser_ctx_t *ctx, BOOL bval)
return ret;
}
static BOOL parse_double_literal(parser_ctx_t *ctx, LONG int_part, double *ret)
HRESULT parse_decimal(const WCHAR **iter, const WCHAR *end, double *ret)
{
LONGLONG d, hlp;
const WCHAR *ptr = *iter;
LONGLONG d = 0, hlp;
int exp = 0;
d = int_part;
while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
hlp = d*10 + *(ctx->ptr++) - '0';
while(ptr < end && isdigitW(*ptr)) {
hlp = d*10 + *(ptr++) - '0';
if(d>MAXLONGLONG/10 || hlp<0) {
exp++;
break;
@ -409,51 +407,48 @@ static BOOL parse_double_literal(parser_ctx_t *ctx, LONG int_part, double *ret)
else
d = hlp;
}
while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
while(ptr < end && isdigitW(*ptr)) {
exp++;
ctx->ptr++;
ptr++;
}
if(*ctx->ptr == '.') {
ctx->ptr++;
if(*ptr == '.') {
ptr++;
while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
hlp = d*10 + *(ctx->ptr++) - '0';
while(ptr < end && isdigitW(*ptr)) {
hlp = d*10 + *(ptr++) - '0';
if(d>MAXLONGLONG/10 || hlp<0)
break;
d = hlp;
exp--;
}
while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr))
ctx->ptr++;
while(ptr < end && isdigitW(*ptr))
ptr++;
}
if(ctx->ptr < ctx->end && (*ctx->ptr == 'e' || *ctx->ptr == 'E')) {
if(ptr < end && (*ptr == 'e' || *ptr == 'E')) {
int sign = 1, e = 0;
ctx->ptr++;
if(ctx->ptr < ctx->end) {
if(*ctx->ptr == '+') {
ctx->ptr++;
}else if(*ctx->ptr == '-') {
if(++ptr < end) {
if(*ptr == '+') {
ptr++;
}else if(*ptr == '-') {
sign = -1;
ctx->ptr++;
}else if(!isdigitW(*ctx->ptr)) {
ptr++;
}else if(!isdigitW(*ptr)) {
WARN("Expected exponent part\n");
lex_error(ctx, E_FAIL);
return FALSE;
return E_FAIL;
}
}
if(ctx->ptr == ctx->end) {
if(ptr == end) {
WARN("unexpected end of file\n");
lex_error(ctx, E_FAIL);
return FALSE;
return E_FAIL;
}
while(ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
if(e > INT_MAX/10 || (e = e*10 + *ctx->ptr++ - '0')<0)
while(ptr < end && isdigitW(*ptr)) {
if(e > INT_MAX/10 || (e = e*10 + *ptr++ - '0')<0)
e = INT_MAX;
}
e *= sign;
@ -463,22 +458,25 @@ static BOOL parse_double_literal(parser_ctx_t *ctx, LONG int_part, double *ret)
else exp += e;
}
if(is_identifier_char(*ctx->ptr)) {
if(is_identifier_char(*ptr)) {
WARN("wrong char after zero\n");
lex_error(ctx, JS_E_MISSING_SEMICOLON);
return FALSE;
return JS_E_MISSING_SEMICOLON;
}
*ret = exp>=0 ? d*pow(10, exp) : d/pow(10, -exp);
return TRUE;
*iter = ptr;
return S_OK;
}
static BOOL parse_numeric_literal(parser_ctx_t *ctx, double *ret)
{
LONG l, d;
HRESULT hres;
if(*ctx->ptr == '0') {
LONG d, l = 0;
ctx->ptr++;
l = *ctx->ptr++ - '0';
if(!l) {
if(*ctx->ptr == 'x' || *ctx->ptr == 'X') {
if(++ctx->ptr == ctx->end) {
ERR("unexpected end of file\n");
@ -534,7 +532,13 @@ static BOOL parse_numeric_literal(parser_ctx_t *ctx, double *ret)
}
}
return parse_double_literal(ctx, l, ret);
hres = parse_decimal(&ctx->ptr, ctx->end, ret);
if(FAILED(hres)) {
lex_error(ctx, hres);
return FALSE;
}
return TRUE;
}
static int next_token(parser_ctx_t *ctx, void *lval)
@ -587,8 +591,12 @@ static int next_token(parser_ctx_t *ctx, void *lval)
case '.':
if(++ctx->ptr < ctx->end && isdigitW(*ctx->ptr)) {
double n;
if(!parse_double_literal(ctx, 0, &n))
HRESULT hres;
hres = parse_decimal(&ctx->ptr, ctx->end, &n);
if(FAILED(hres)) {
lex_error(ctx, hres);
return -1;
}
*(literal_t**)lval = new_double_literal(ctx, n);
return tNumericLiteral;
}

View file

@ -247,7 +247,7 @@ static HRESULT Number_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, u
val = number->value;
if(radix==10 || isnan(val) || isinf(val)) {
if(radix==10 || !is_finite(val)) {
hres = to_string(ctx, jsval_number(val), &str);
if(FAILED(hres))
return hres;
@ -373,7 +373,7 @@ static HRESULT Number_toFixed(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, un
}
val = number->value;
if(isinf(val) || isnan(val)) {
if(!is_finite(val)) {
hres = to_string(ctx, jsval_number(val), &str);
if(FAILED(hres))
return hres;
@ -414,7 +414,7 @@ static HRESULT Number_toExponential(script_ctx_t *ctx, vdisp_t *jsthis, WORD fla
}
val = number->value;
if(isinf(val) || isnan(val)) {
if(!is_finite(val)) {
hres = to_string(ctx, jsval_number(val), &str);
if(FAILED(hres))
return hres;
@ -455,7 +455,7 @@ static HRESULT Number_toPrecision(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags
}
val = number->value;
if(isinf(val) || isnan(val) || !prec) {
if(!is_finite(val) || !prec) {
hres = to_string(ctx, jsval_number(val), &str);
if(FAILED(hres))
return hres;

View file

@ -48,7 +48,7 @@ static HRESULT Object_toString(script_ctx_t *ctx, vdisp_t *jsthis, WORD flags, u
static const WCHAR stringW[] = {'S','t','r','i','n','g',0};
/* Keep in sync with jsclass_t enum */
static const WCHAR *names[] = {NULL, arrayW, booleanW, dateW, errorW,
functionW, NULL, mathW, numberW, objectW, regexpW, stringW, objectW, objectW};
functionW, NULL, mathW, numberW, objectW, regexpW, stringW, objectW, objectW, objectW};
TRACE("\n");

View file

@ -64,6 +64,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,

View file

@ -85,7 +85,7 @@ reactos/dll/win32/inseng # Synced to WineStaging-1.7.55
reactos/dll/win32/iphlpapi # Out of sync
reactos/dll/win32/itircl # Synced to WineStaging-1.7.55
reactos/dll/win32/itss # Synced to WineStaging-1.7.55
reactos/dll/win32/jscript # Synced to WineStaging-1.7.55
reactos/dll/win32/jscript # Synced to WineStaging-1.9.4
reactos/dll/win32/jsproxy # Synced to WineStaging-1.7.55
reactos/dll/win32/loadperf # Synced to WineStaging-1.7.55
reactos/dll/win32/localspl # Synced to WineStaging-1.7.55