Commit fb8e0ab3 authored by yangguo's avatar yangguo Committed by Commit bot

[json] support property list argument in BasicJsonStringifier.

R=cbruni@chromium.org

Review-Url: https://codereview.chromium.org/2004413002
Cr-Commit-Position: refs/heads/master@{#36478}
parent 1dace259
......@@ -2774,12 +2774,13 @@ MaybeLocal<String> JSON::Stringify(Local<Context> context,
Local<String> gap) {
PREPARE_FOR_EXECUTION(context, JSON, Stringify, String);
i::Handle<i::Object> object = Utils::OpenHandle(*json_object);
i::Handle<i::Object> replacer = isolate->factory()->undefined_value();
i::Handle<i::String> gap_string = gap.IsEmpty()
? isolate->factory()->empty_string()
: Utils::OpenHandle(*gap);
i::Handle<i::Object> maybe;
has_pending_exception = !i::BasicJsonStringifier(isolate)
.Stringify(object, gap_string)
.Stringify(object, replacer, gap_string)
.ToHandle(&maybe);
RETURN_ON_FAILED_EXECUTION(String);
Local<String> result;
......
......@@ -121,29 +121,15 @@ function SerializeObject(value, replacer, stack, indent, gap) {
var stepback = indent;
indent += gap;
var partial = new InternalArray();
if (IS_ARRAY(replacer)) {
var length = replacer.length;
for (var i = 0; i < length; i++) {
var p = replacer[i];
var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
if (!IS_UNDEFINED(strP)) {
var member = %QuoteJSONString(p) + ":";
if (gap != "") member += " ";
member += strP;
partial.push(member);
}
}
} else {
var keys = %object_keys(value);
for (var i = 0; i < keys.length; i++) {
var p = keys[i];
var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
if (!IS_UNDEFINED(strP)) {
var member = %QuoteJSONString(p) + ":";
if (gap != "") member += " ";
member += strP;
partial.push(member);
}
var keys = %object_keys(value);
for (var i = 0; i < keys.length; i++) {
var p = keys[i];
var strP = JSONSerialize(p, value, replacer, stack, indent, gap);
if (!IS_UNDEFINED(strP)) {
var member = %QuoteJSONString(p) + ":";
if (gap != "") member += " ";
member += strP;
partial.push(member);
}
}
var final;
......@@ -201,29 +187,9 @@ function JSONSerialize(key, holder, replacer, stack, indent, gap) {
function JSONStringify(value, replacer, space) {
if (arguments.length === 1) return %BasicJSONStringify(value, "");
if (!IS_CALLABLE(replacer) && %is_arraylike(replacer)) {
var property_list = new InternalArray();
var seen_properties = new GlobalSet();
var length = TO_LENGTH(replacer.length);
for (var i = 0; i < length; i++) {
var v = replacer[i];
var item;
if (IS_STRING(v)) {
item = v;
} else if (IS_NUMBER(v)) {
item = %_NumberToString(v);
} else if (IS_STRING_WRAPPER(v) || IS_NUMBER_WRAPPER(v)) {
item = TO_STRING(v);
} else {
continue;
}
if (!seen_properties.has(item)) {
property_list.push(item);
seen_properties.add(item);
}
}
replacer = property_list;
if (arguments.length === 1) return %BasicJSONStringify(value, UNDEFINED, "");
if (!IS_CALLABLE(replacer)) {
return %BasicJSONStringify(value, replacer, space);
}
if (IS_OBJECT(space)) {
// Unwrap 'space' if it is wrapped
......@@ -233,9 +199,6 @@ function JSONStringify(value, replacer, space) {
space = TO_STRING(space);
}
}
if (!IS_CALLABLE(replacer) && !property_list) {
return %BasicJSONStringify(value, space);
}
var gap;
if (IS_NUMBER(space)) {
space = MaxSimple(0, MinSimple(TO_INTEGER(space), 10));
......
......@@ -88,7 +88,9 @@ BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate)
}
MaybeHandle<Object> BasicJsonStringifier::Stringify(Handle<Object> object,
Handle<Object> replacer,
Handle<Object> gap) {
if (!InitializeReplacer(replacer)) return MaybeHandle<Object>();
if (!gap->IsUndefined() && !InitializeGap(gap)) return MaybeHandle<Object>();
Result result = SerializeObject(object);
if (result == UNCHANGED) return factory()->undefined_value();
......@@ -97,15 +99,64 @@ MaybeHandle<Object> BasicJsonStringifier::Stringify(Handle<Object> object,
return MaybeHandle<Object>();
}
bool IsInList(Handle<String> key, List<Handle<String> >* list) {
// TODO(yangguo): This is O(n^2) for n properties in the list. Deal with this
// if this becomes an issue.
for (const Handle<String>& existing : *list) {
if (String::Equals(existing, key)) return true;
}
return false;
}
bool BasicJsonStringifier::InitializeReplacer(Handle<Object> replacer) {
DCHECK(property_list_.is_null());
Maybe<bool> is_array = Object::IsArray(replacer);
if (is_array.IsNothing()) return false;
if (is_array.FromJust()) {
HandleScope handle_scope(isolate_);
List<Handle<String> > list;
Handle<Object> length_obj;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, length_obj,
Object::GetLengthFromArrayLike(isolate_, replacer), false);
uint32_t length;
if (!length_obj->ToUint32(&length)) length = kMaxUInt32;
for (uint32_t i = 0; i < length; i++) {
Handle<Object> element;
Handle<String> key;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, element, Object::GetElement(isolate_, replacer, i), false);
if (element->IsNumber() || element->IsString()) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, key, Object::ToString(isolate_, element), false);
} else if (element->IsJSValue()) {
Handle<Object> value(Handle<JSValue>::cast(element)->value(), isolate_);
if (value->IsNumber() || value->IsString()) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, key, Object::ToString(isolate_, element), false);
}
}
if (key.is_null()) continue;
if (!IsInList(key, &list)) list.Add(key);
}
property_list_ = factory()->NewUninitializedFixedArray(list.length());
for (int i = 0; i < list.length(); i++) {
property_list_->set(i, *list[i]);
}
property_list_ = handle_scope.CloseAndEscape(property_list_);
}
return true;
}
bool BasicJsonStringifier::InitializeGap(Handle<Object> gap) {
DCHECK_NULL(gap_);
HandleScope scope(isolate_);
if (gap->IsJSReceiver()) {
Handle<String> class_name(Handle<JSReceiver>::cast(gap)->class_name());
if (class_name.is_identical_to(factory()->String_string())) {
if (gap->IsJSValue()) {
Handle<Object> value(Handle<JSValue>::cast(gap)->value(), isolate_);
if (value->IsString()) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap,
Object::ToString(isolate_, gap), false);
} else if (class_name.is_identical_to(factory()->number_string())) {
} else if (value->IsNumber()) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap, Object::ToNumber(gap),
false);
}
......@@ -146,7 +197,8 @@ MaybeHandle<Object> BasicJsonStringifier::StringifyString(
if (worst_case_length > 32 * KB) { // Slow path if too large.
BasicJsonStringifier stringifier(isolate);
return stringifier.Stringify(object, isolate->factory()->undefined_value());
Handle<Object> undefined = isolate->factory()->undefined_value();
return stringifier.Stringify(object, undefined, undefined);
}
object = String::Flatten(object);
......@@ -437,7 +489,8 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSObject(
Result stack_push = StackPush(object);
if (stack_push != SUCCESS) return stack_push;
if (object->map()->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER &&
if (property_list_.is_null() &&
object->map()->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER &&
object->HasFastProperties() &&
Handle<JSObject>::cast(object)->elements()->length() == 0) {
DCHECK(object->IsJSObject());
......@@ -487,10 +540,12 @@ BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSObject(
BasicJsonStringifier::Result BasicJsonStringifier::SerializeJSReceiverSlow(
Handle<JSReceiver> object) {
Handle<FixedArray> contents;
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, contents,
JSReceiver::GetKeys(object, OWN_ONLY, ENUMERABLE_STRINGS), EXCEPTION);
Handle<FixedArray> contents = property_list_;
if (contents.is_null()) {
ASSIGN_RETURN_ON_EXCEPTION_VALUE(
isolate_, contents,
JSReceiver::GetKeys(object, OWN_ONLY, ENUMERABLE_STRINGS), EXCEPTION);
}
builder_.AppendCharacter('{');
Indent();
......
......@@ -18,6 +18,7 @@ class BasicJsonStringifier BASE_EMBEDDED {
~BasicJsonStringifier() { DeleteArray(gap_); }
MUST_USE_RESULT MaybeHandle<Object> Stringify(Handle<Object> object,
Handle<Object> replacer,
Handle<Object> gap);
MUST_USE_RESULT static MaybeHandle<Object> StringifyString(
......@@ -26,6 +27,7 @@ class BasicJsonStringifier BASE_EMBEDDED {
private:
enum Result { UNCHANGED, SUCCESS, EXCEPTION };
bool InitializeReplacer(Handle<Object> replacer);
bool InitializeGap(Handle<Object> gap);
MUST_USE_RESULT MaybeHandle<Object> ApplyToJsonFunction(
......@@ -107,6 +109,7 @@ class BasicJsonStringifier BASE_EMBEDDED {
IncrementalStringBuilder builder_;
Handle<String> tojson_string_;
Handle<JSArray> stack_;
Handle<FixedArray> property_list_;
uc16* gap_;
int indent_;
......
......@@ -24,11 +24,12 @@ RUNTIME_FUNCTION(Runtime_QuoteJSONString) {
RUNTIME_FUNCTION(Runtime_BasicJSONStringify) {
HandleScope scope(isolate);
DCHECK(args.length() == 2);
DCHECK(args.length() == 3);
CONVERT_ARG_HANDLE_CHECKED(Object, object, 0);
CONVERT_ARG_HANDLE_CHECKED(Object, gap, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, replacer, 1);
CONVERT_ARG_HANDLE_CHECKED(Object, gap, 2);
RETURN_RESULT_OR_FAILURE(
isolate, BasicJsonStringifier(isolate).Stringify(object, gap));
isolate, BasicJsonStringifier(isolate).Stringify(object, replacer, gap));
}
RUNTIME_FUNCTION(Runtime_ParseJson) {
......
......@@ -328,7 +328,7 @@ namespace internal {
#define FOR_EACH_INTRINSIC_JSON(F) \
F(QuoteJSONString, 1, 1) \
F(BasicJSONStringify, 2, 1) \
F(BasicJSONStringify, 3, 1) \
F(ParseJson, 1, 1)
#define FOR_EACH_INTRINSIC_LITERALS(F) \
......
......@@ -20,7 +20,6 @@ var space = Object.defineProperty(new String, 'toString', {
});
JSON.stringify('', replacer, space);
assertEquals(2, log.length);
assertEquals('get 0', log[0]);
assertEquals('toString', log[1]);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment