Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Optimize json string-based parameter checking and object ha… #887

Closed
wants to merge 1 commit into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
258 changes: 129 additions & 129 deletions quickjs.c
Original file line number Diff line number Diff line change
Expand Up @@ -45531,180 +45531,180 @@ static int js_json_to_str(JSContext *ctx, JSONStringifyContext *jsc,
JSValue holder, JSValue val,
JSValue indent)
{
JSValue indent1, sep, sep1, tab, v, prop;
JSObject *p;
int64_t i, len;
int cl, ret;
bool has_content;

indent1 = JS_UNDEFINED;
sep = JS_UNDEFINED;
sep1 = JS_UNDEFINED;
tab = JS_UNDEFINED;
prop = JS_UNDEFINED;
if (!ctx || !jsc || !jsc->b) {
return -1;
}

if (JS_IsObject(val)) {
p = JS_VALUE_GET_OBJ(val);
cl = p->class_id;
if (cl == JS_CLASS_STRING) {
val = JS_ToStringFree(ctx, val);
if (JS_IsException(val))
goto exception;
goto concat_primitive;
} else if (cl == JS_CLASS_NUMBER) {
val = JS_ToNumberFree(ctx, val);
if (JS_IsException(val))
goto exception;
goto concat_primitive;
} else if (cl == JS_CLASS_BOOLEAN || cl == JS_CLASS_BIG_INT) {
set_value(ctx, &val, js_dup(p->u.object_data));
goto concat_primitive;
}
Comment on lines -45547 to -45562
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this code has some side effects:

  • JSON.stringify(new String()) -> '{}' instead of '""' (as in v8)
  • JSON.stringify(new Boolean()) -> '{}' instead of 'false' (as in v8)
  • JSON.stringify(new Number()) -> '{}' instead of '0' (as in v8)
    I am not sure if tc39 has tests for these, but since node has consistent behavior for boxed values, we should implement these the same way.

v = js_array_includes(ctx, jsc->stack, 1, &val);
JSValue indent1 = JS_UNDEFINED;
JSValue sep = JS_UNDEFINED;
JSValue sep1 = JS_UNDEFINED;
JSValue tab = JS_UNDEFINED;
JSValue prop = JS_UNDEFINED;
int ret = -1;

JSValue v = js_array_includes(ctx, jsc->stack, 1, &val);
if (JS_IsException(v))
goto exception;

if (JS_ToBoolFree(ctx, v)) {
JS_ThrowTypeError(ctx, "circular reference");
goto exception;
}
indent1 = JS_ConcatString(ctx, js_dup(indent), js_dup(jsc->gap));
if (JS_IsException(indent1))
goto exception;
if (!JS_IsEmptyString(jsc->gap)) {

if (!JS_IsUndefined(jsc->gap)) {
indent1 = JS_ConcatString(ctx, js_dup(indent), js_dup(jsc->gap));
if (JS_IsException(indent1))
goto exception;

sep = JS_ConcatString3(ctx, "\n", js_dup(indent1), "");
if (JS_IsException(sep))
goto exception;

sep1 = js_new_string8(ctx, " ");
if (JS_IsException(sep1))
goto exception;
} else {
sep = js_dup(jsc->empty);
sep1 = js_dup(jsc->empty);
}
v = js_array_push(ctx, jsc->stack, 1, &val, 0);
if (check_exception_free(ctx, v))
goto exception;
Comment on lines -45584 to -45586
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removing this seems problematic: if val is an array that contains a self reference, the cycle will not be detected.
Same problem for non-Array objects with self references.


ret = JS_IsArray(ctx, val);
if (ret < 0)
goto exception;

if (ret) {
int64_t len;
if (js_get_length64(ctx, &len, val))
goto exception;
string_buffer_putc8(jsc->b, '[');
for(i = 0; i < len; i++) {
if (i > 0)
string_buffer_putc8(jsc->b, ',');
string_buffer_concat_value(jsc->b, sep);
v = JS_GetPropertyInt64(ctx, val, i);
if (JS_IsException(v))
goto exception;
/* XXX: could do this string conversion only when needed */
prop = JS_ToStringFree(ctx, js_int64(i));
if (JS_IsException(prop))

if (len < 0 || len > INT32_MAX) {
JS_ThrowRangeError(ctx, "array length out of range");
goto exception;
}

if (string_buffer_putc8(jsc->b, '[') < 0)
goto exception;

for(int64_t i = 0; i < len; i++) {
if (i > 0) {
if (string_buffer_putc8(jsc->b, ',') < 0)
goto exception;
}

JSValue elem = JS_GetPropertyInt64(ctx, val, i);
if (JS_IsException(elem))
goto exception;
v = js_json_check(ctx, jsc, val, v, prop);
JS_FreeValue(ctx, prop);
prop = JS_UNDEFINED;
if (JS_IsException(v))

elem = js_json_check(ctx, jsc, val, elem, JS_NewInt64(ctx, i));
if (JS_IsException(elem))
goto exception;
if (JS_IsUndefined(v))
v = JS_NULL;
if (js_json_to_str(ctx, jsc, val, v, indent1))

if (js_json_to_str(ctx, jsc, val, elem, indent1) < 0)
goto exception;
}
if (len > 0 && !JS_IsEmptyString(jsc->gap)) {
string_buffer_putc8(jsc->b, '\n');
string_buffer_concat_value(jsc->b, indent);
}
string_buffer_putc8(jsc->b, ']');

if (string_buffer_putc8(jsc->b, ']') < 0)
goto exception;
} else {
if (!JS_IsUndefined(jsc->property_list))
tab = js_dup(jsc->property_list);
else
tab = js_object_keys(ctx, JS_UNDEFINED, 1, &val, JS_ITERATOR_KIND_KEY);
if (JS_IsException(tab))
JSPropertyEnum *props = NULL;
uint32_t prop_count = 0;

Comment on lines +45610 to +45612
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This array should be defined at the outer scope so the objects can be freed in case of exception.

if (JS_GetOwnPropertyNames(ctx, &props, &prop_count, val,
JS_GPN_STRING_MASK | JS_GPN_ENUM_ONLY) < 0)
goto exception;
if (js_get_length64(ctx, &len, tab))

if (string_buffer_putc8(jsc->b, '{') < 0)
goto exception;
string_buffer_putc8(jsc->b, '{');
has_content = false;
for(i = 0; i < len; i++) {
JS_FreeValue(ctx, prop);
prop = JS_GetPropertyInt64(ctx, tab, i);
if (JS_IsException(prop))

for(uint32_t i = 0; i < prop_count; i++) {
if (i > 0) {
if (string_buffer_putc8(jsc->b, ',') < 0)
goto exception;
}

JSValue prop_val = JS_GetProperty(ctx, val, props[i].atom);
if (JS_IsException(prop_val))
goto exception;
v = JS_GetPropertyValue(ctx, val, js_dup(prop));
if (JS_IsException(v))

JSValue prop_str = JS_AtomToString(ctx, props[i].atom);
if (JS_IsException(prop_str)) {
JS_FreeValue(ctx, prop_val);
goto exception;
v = js_json_check(ctx, jsc, val, v, prop);
if (JS_IsException(v))
}

if (string_buffer_putc8(jsc->b, '"') < 0 ||
string_buffer_concat_value(jsc->b, prop_str) < 0 ||
string_buffer_putc8(jsc->b, '"') < 0 ||
string_buffer_putc8(jsc->b, ':') < 0) {
JS_FreeValue(ctx, prop_val);
JS_FreeValue(ctx, prop_str);
goto exception;
if (!JS_IsUndefined(v)) {
if (has_content)
string_buffer_putc8(jsc->b, ',');
prop = JS_ToQuotedStringFree(ctx, prop);
if (JS_IsException(prop)) {
JS_FreeValue(ctx, v);
goto exception;
}
string_buffer_concat_value(jsc->b, sep);
string_buffer_concat_value(jsc->b, prop);
string_buffer_putc8(jsc->b, ':');
string_buffer_concat_value(jsc->b, sep1);
if (js_json_to_str(ctx, jsc, val, v, indent1))
goto exception;
has_content = true;
}

JS_FreeValue(ctx, prop_str);

if (js_json_to_str(ctx, jsc, val, prop_val, indent1) < 0)
goto exception;
}
if (has_content && JS_VALUE_GET_STRING(jsc->gap)->len != 0) {
string_buffer_putc8(jsc->b, '\n');
string_buffer_concat_value(jsc->b, indent);
}
string_buffer_putc8(jsc->b, '}');

if (string_buffer_putc8(jsc->b, '}') < 0)
goto exception;

js_free_prop_enum(ctx, props, prop_count);
}
if (check_exception_free(ctx, js_array_pop(ctx, jsc->stack, 0, NULL, 0)))
goto exception;
JS_FreeValue(ctx, val);
JS_FreeValue(ctx, tab);

JS_FreeValue(ctx, indent1);
JS_FreeValue(ctx, sep);
JS_FreeValue(ctx, sep1);
JS_FreeValue(ctx, indent1);
JS_FreeValue(ctx, tab);
JS_FreeValue(ctx, prop);
return 0;

exception:
JS_FreeValue(ctx, indent1);
JS_FreeValue(ctx, sep);
JS_FreeValue(ctx, sep1);
JS_FreeValue(ctx, tab);
JS_FreeValue(ctx, prop);
Comment on lines +45664 to +45669
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Move the definitions to the outer scope and add js_free_prop_enum(ctx, props, prop_count);

return -1;
}
concat_primitive:

switch (JS_VALUE_GET_NORM_TAG(val)) {
case JS_TAG_STRING:
val = JS_ToQuotedStringFree(ctx, val);
if (JS_IsException(val))
goto exception;
goto concat_value;
case JS_TAG_FLOAT64:
if (!isfinite(JS_VALUE_GET_FLOAT64(val))) {
val = JS_NULL;
case JS_TAG_STRING: {
JSValue quoted = JS_ToQuotedStringFree(ctx, js_dup(val));
if (JS_IsException(quoted))
return -1;
return string_buffer_concat_value(jsc->b, quoted);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a memory leak: should be return string_buffer_concat_value_free(jsc->b, quoted)

}
goto concat_value;
case JS_TAG_INT:
case JS_TAG_BOOL:
case JS_TAG_NULL:
concat_value:
return string_buffer_concat_value_free(jsc->b, val);
case JS_TAG_BIG_INT:
JS_ThrowTypeError(ctx, "BigInt are forbidden in JSON.stringify");
goto exception;
default:
JS_FreeValue(ctx, val);
return 0;

case JS_TAG_FLOAT64: {
double d = JS_VALUE_GET_FLOAT64(val);
if (!isfinite(d)) {
return string_buffer_concat_value(jsc->b, JS_NULL);
}
JSValue num_str = JS_ToString(ctx, val);
if (JS_IsException(num_str))
return -1;
return string_buffer_concat_value(jsc->b, num_str);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a memory leak: should be return string_buffer_concat_value_free(jsc->b, num_str)

}

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have many instances of trailing spaces in the patch, please remove these.

case JS_TAG_INT:
case JS_TAG_BOOL:
case JS_TAG_NULL: {
JSValue str = JS_ToString(ctx, val);
if (JS_IsException(str))
return -1;
return string_buffer_concat_value(jsc->b, str);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a memory leak: should be return string_buffer_concat_value_free(jsc->b, str)

}

case JS_TAG_BIG_INT:
JS_ThrowTypeError(ctx, "BigInt are forbidden in JSON.stringify");
return -1;

default:
return 0;
}

exception:
JS_FreeValue(ctx, val);
JS_FreeValue(ctx, tab);
JS_FreeValue(ctx, sep);
JS_FreeValue(ctx, sep1);
JS_FreeValue(ctx, indent1);
JS_FreeValue(ctx, prop);
return -1;
}

JSValue JS_JSONStringify(JSContext *ctx, JSValue obj,
Expand Down
Loading