Ben Murdoch | 61f157c | 2016-09-16 13:49:30 +0100 | [diff] [blame] | 1 | // Copyright 2016 the V8 project authors. All rights reserved. |
| 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "src/json-stringifier.h" |
| 6 | |
| 7 | #include "src/conversions.h" |
| 8 | #include "src/lookup.h" |
| 9 | #include "src/messages.h" |
| 10 | #include "src/objects-inl.h" |
| 11 | #include "src/utils.h" |
| 12 | |
| 13 | namespace v8 { |
| 14 | namespace internal { |
| 15 | |
| 16 | // Translation table to escape Latin1 characters. |
| 17 | // Table entries start at a multiple of 8 and are null-terminated. |
| 18 | const char* const JsonStringifier::JsonEscapeTable = |
| 19 | "\\u0000\0 \\u0001\0 \\u0002\0 \\u0003\0 " |
| 20 | "\\u0004\0 \\u0005\0 \\u0006\0 \\u0007\0 " |
| 21 | "\\b\0 \\t\0 \\n\0 \\u000b\0 " |
| 22 | "\\f\0 \\r\0 \\u000e\0 \\u000f\0 " |
| 23 | "\\u0010\0 \\u0011\0 \\u0012\0 \\u0013\0 " |
| 24 | "\\u0014\0 \\u0015\0 \\u0016\0 \\u0017\0 " |
| 25 | "\\u0018\0 \\u0019\0 \\u001a\0 \\u001b\0 " |
| 26 | "\\u001c\0 \\u001d\0 \\u001e\0 \\u001f\0 " |
| 27 | " \0 !\0 \\\"\0 #\0 " |
| 28 | "$\0 %\0 &\0 '\0 " |
| 29 | "(\0 )\0 *\0 +\0 " |
| 30 | ",\0 -\0 .\0 /\0 " |
| 31 | "0\0 1\0 2\0 3\0 " |
| 32 | "4\0 5\0 6\0 7\0 " |
| 33 | "8\0 9\0 :\0 ;\0 " |
| 34 | "<\0 =\0 >\0 ?\0 " |
| 35 | "@\0 A\0 B\0 C\0 " |
| 36 | "D\0 E\0 F\0 G\0 " |
| 37 | "H\0 I\0 J\0 K\0 " |
| 38 | "L\0 M\0 N\0 O\0 " |
| 39 | "P\0 Q\0 R\0 S\0 " |
| 40 | "T\0 U\0 V\0 W\0 " |
| 41 | "X\0 Y\0 Z\0 [\0 " |
| 42 | "\\\\\0 ]\0 ^\0 _\0 " |
| 43 | "`\0 a\0 b\0 c\0 " |
| 44 | "d\0 e\0 f\0 g\0 " |
| 45 | "h\0 i\0 j\0 k\0 " |
| 46 | "l\0 m\0 n\0 o\0 " |
| 47 | "p\0 q\0 r\0 s\0 " |
| 48 | "t\0 u\0 v\0 w\0 " |
| 49 | "x\0 y\0 z\0 {\0 " |
| 50 | "|\0 }\0 ~\0 \177\0 " |
| 51 | "\200\0 \201\0 \202\0 \203\0 " |
| 52 | "\204\0 \205\0 \206\0 \207\0 " |
| 53 | "\210\0 \211\0 \212\0 \213\0 " |
| 54 | "\214\0 \215\0 \216\0 \217\0 " |
| 55 | "\220\0 \221\0 \222\0 \223\0 " |
| 56 | "\224\0 \225\0 \226\0 \227\0 " |
| 57 | "\230\0 \231\0 \232\0 \233\0 " |
| 58 | "\234\0 \235\0 \236\0 \237\0 " |
| 59 | "\240\0 \241\0 \242\0 \243\0 " |
| 60 | "\244\0 \245\0 \246\0 \247\0 " |
| 61 | "\250\0 \251\0 \252\0 \253\0 " |
| 62 | "\254\0 \255\0 \256\0 \257\0 " |
| 63 | "\260\0 \261\0 \262\0 \263\0 " |
| 64 | "\264\0 \265\0 \266\0 \267\0 " |
| 65 | "\270\0 \271\0 \272\0 \273\0 " |
| 66 | "\274\0 \275\0 \276\0 \277\0 " |
| 67 | "\300\0 \301\0 \302\0 \303\0 " |
| 68 | "\304\0 \305\0 \306\0 \307\0 " |
| 69 | "\310\0 \311\0 \312\0 \313\0 " |
| 70 | "\314\0 \315\0 \316\0 \317\0 " |
| 71 | "\320\0 \321\0 \322\0 \323\0 " |
| 72 | "\324\0 \325\0 \326\0 \327\0 " |
| 73 | "\330\0 \331\0 \332\0 \333\0 " |
| 74 | "\334\0 \335\0 \336\0 \337\0 " |
| 75 | "\340\0 \341\0 \342\0 \343\0 " |
| 76 | "\344\0 \345\0 \346\0 \347\0 " |
| 77 | "\350\0 \351\0 \352\0 \353\0 " |
| 78 | "\354\0 \355\0 \356\0 \357\0 " |
| 79 | "\360\0 \361\0 \362\0 \363\0 " |
| 80 | "\364\0 \365\0 \366\0 \367\0 " |
| 81 | "\370\0 \371\0 \372\0 \373\0 " |
| 82 | "\374\0 \375\0 \376\0 \377\0 "; |
| 83 | |
| 84 | JsonStringifier::JsonStringifier(Isolate* isolate) |
| 85 | : isolate_(isolate), builder_(isolate), gap_(nullptr), indent_(0) { |
| 86 | tojson_string_ = factory()->toJSON_string(); |
| 87 | stack_ = factory()->NewJSArray(8); |
| 88 | } |
| 89 | |
| 90 | MaybeHandle<Object> JsonStringifier::Stringify(Handle<Object> object, |
| 91 | Handle<Object> replacer, |
| 92 | Handle<Object> gap) { |
| 93 | if (!InitializeReplacer(replacer)) return MaybeHandle<Object>(); |
| 94 | if (!gap->IsUndefined(isolate_) && !InitializeGap(gap)) { |
| 95 | return MaybeHandle<Object>(); |
| 96 | } |
| 97 | Result result = SerializeObject(object); |
| 98 | if (result == UNCHANGED) return factory()->undefined_value(); |
| 99 | if (result == SUCCESS) return builder_.Finish(); |
| 100 | DCHECK(result == EXCEPTION); |
| 101 | return MaybeHandle<Object>(); |
| 102 | } |
| 103 | |
| 104 | bool IsInList(Handle<String> key, List<Handle<String> >* list) { |
| 105 | // TODO(yangguo): This is O(n^2) for n properties in the list. Deal with this |
| 106 | // if this becomes an issue. |
| 107 | for (const Handle<String>& existing : *list) { |
| 108 | if (String::Equals(existing, key)) return true; |
| 109 | } |
| 110 | return false; |
| 111 | } |
| 112 | |
| 113 | bool JsonStringifier::InitializeReplacer(Handle<Object> replacer) { |
| 114 | DCHECK(property_list_.is_null()); |
| 115 | DCHECK(replacer_function_.is_null()); |
| 116 | Maybe<bool> is_array = Object::IsArray(replacer); |
| 117 | if (is_array.IsNothing()) return false; |
| 118 | if (is_array.FromJust()) { |
| 119 | HandleScope handle_scope(isolate_); |
| 120 | List<Handle<String> > list; |
| 121 | Handle<Object> length_obj; |
| 122 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 123 | isolate_, length_obj, |
| 124 | Object::GetLengthFromArrayLike(isolate_, replacer), false); |
| 125 | uint32_t length; |
| 126 | if (!length_obj->ToUint32(&length)) length = kMaxUInt32; |
| 127 | for (uint32_t i = 0; i < length; i++) { |
| 128 | Handle<Object> element; |
| 129 | Handle<String> key; |
| 130 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 131 | isolate_, element, Object::GetElement(isolate_, replacer, i), false); |
| 132 | if (element->IsNumber() || element->IsString()) { |
| 133 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 134 | isolate_, key, Object::ToString(isolate_, element), false); |
| 135 | } else if (element->IsJSValue()) { |
| 136 | Handle<Object> value(Handle<JSValue>::cast(element)->value(), isolate_); |
| 137 | if (value->IsNumber() || value->IsString()) { |
| 138 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 139 | isolate_, key, Object::ToString(isolate_, element), false); |
| 140 | } |
| 141 | } |
| 142 | if (key.is_null()) continue; |
| 143 | if (!IsInList(key, &list)) list.Add(key); |
| 144 | } |
| 145 | property_list_ = factory()->NewUninitializedFixedArray(list.length()); |
| 146 | for (int i = 0; i < list.length(); i++) { |
| 147 | property_list_->set(i, *list[i]); |
| 148 | } |
| 149 | property_list_ = handle_scope.CloseAndEscape(property_list_); |
| 150 | } else if (replacer->IsCallable()) { |
| 151 | replacer_function_ = Handle<JSReceiver>::cast(replacer); |
| 152 | } |
| 153 | return true; |
| 154 | } |
| 155 | |
| 156 | bool JsonStringifier::InitializeGap(Handle<Object> gap) { |
| 157 | DCHECK_NULL(gap_); |
| 158 | HandleScope scope(isolate_); |
| 159 | if (gap->IsJSValue()) { |
| 160 | Handle<Object> value(Handle<JSValue>::cast(gap)->value(), isolate_); |
| 161 | if (value->IsString()) { |
| 162 | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap, |
| 163 | Object::ToString(isolate_, gap), false); |
| 164 | } else if (value->IsNumber()) { |
| 165 | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, gap, Object::ToNumber(gap), |
| 166 | false); |
| 167 | } |
| 168 | } |
| 169 | |
| 170 | if (gap->IsString()) { |
| 171 | Handle<String> gap_string = Handle<String>::cast(gap); |
| 172 | if (gap_string->length() > 0) { |
| 173 | int gap_length = std::min(gap_string->length(), 10); |
| 174 | gap_ = NewArray<uc16>(gap_length + 1); |
| 175 | String::WriteToFlat(*gap_string, gap_, 0, gap_length); |
| 176 | for (int i = 0; i < gap_length; i++) { |
| 177 | if (gap_[i] > String::kMaxOneByteCharCode) { |
| 178 | builder_.ChangeEncoding(); |
| 179 | break; |
| 180 | } |
| 181 | } |
| 182 | gap_[gap_length] = '\0'; |
| 183 | } |
| 184 | } else if (gap->IsNumber()) { |
| 185 | int num_value = DoubleToInt32(gap->Number()); |
| 186 | if (num_value > 0) { |
| 187 | int gap_length = std::min(num_value, 10); |
| 188 | gap_ = NewArray<uc16>(gap_length + 1); |
| 189 | for (int i = 0; i < gap_length; i++) gap_[i] = ' '; |
| 190 | gap_[gap_length] = '\0'; |
| 191 | } |
| 192 | } |
| 193 | return true; |
| 194 | } |
| 195 | |
| 196 | MaybeHandle<Object> JsonStringifier::ApplyToJsonFunction(Handle<Object> object, |
| 197 | Handle<Object> key) { |
| 198 | HandleScope scope(isolate_); |
| 199 | LookupIterator it(object, tojson_string_, |
| 200 | LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR); |
| 201 | Handle<Object> fun; |
| 202 | ASSIGN_RETURN_ON_EXCEPTION(isolate_, fun, Object::GetProperty(&it), Object); |
| 203 | if (!fun->IsCallable()) return object; |
| 204 | |
| 205 | // Call toJSON function. |
| 206 | if (key->IsSmi()) key = factory()->NumberToString(key); |
| 207 | Handle<Object> argv[] = {key}; |
| 208 | ASSIGN_RETURN_ON_EXCEPTION(isolate_, object, |
| 209 | Execution::Call(isolate_, fun, object, 1, argv), |
| 210 | Object); |
| 211 | return scope.CloseAndEscape(object); |
| 212 | } |
| 213 | |
| 214 | MaybeHandle<Object> JsonStringifier::ApplyReplacerFunction( |
| 215 | Handle<Object> value, Handle<Object> key, Handle<Object> initial_holder) { |
| 216 | HandleScope scope(isolate_); |
| 217 | if (key->IsSmi()) key = factory()->NumberToString(key); |
| 218 | Handle<Object> argv[] = {key, value}; |
| 219 | Handle<JSReceiver> holder = CurrentHolder(value, initial_holder); |
| 220 | ASSIGN_RETURN_ON_EXCEPTION( |
| 221 | isolate_, value, |
| 222 | Execution::Call(isolate_, replacer_function_, holder, 2, argv), Object); |
| 223 | return scope.CloseAndEscape(value); |
| 224 | } |
| 225 | |
| 226 | Handle<JSReceiver> JsonStringifier::CurrentHolder( |
| 227 | Handle<Object> value, Handle<Object> initial_holder) { |
| 228 | int length = Smi::cast(stack_->length())->value(); |
| 229 | if (length == 0) { |
| 230 | Handle<JSObject> holder = |
| 231 | factory()->NewJSObject(isolate_->object_function()); |
| 232 | JSObject::AddProperty(holder, factory()->empty_string(), initial_holder, |
| 233 | NONE); |
| 234 | return holder; |
| 235 | } else { |
| 236 | FixedArray* elements = FixedArray::cast(stack_->elements()); |
| 237 | return Handle<JSReceiver>(JSReceiver::cast(elements->get(length - 1)), |
| 238 | isolate_); |
| 239 | } |
| 240 | } |
| 241 | |
| 242 | JsonStringifier::Result JsonStringifier::StackPush(Handle<Object> object) { |
| 243 | StackLimitCheck check(isolate_); |
| 244 | if (check.HasOverflowed()) { |
| 245 | isolate_->StackOverflow(); |
| 246 | return EXCEPTION; |
| 247 | } |
| 248 | |
| 249 | int length = Smi::cast(stack_->length())->value(); |
| 250 | { |
| 251 | DisallowHeapAllocation no_allocation; |
| 252 | FixedArray* elements = FixedArray::cast(stack_->elements()); |
| 253 | for (int i = 0; i < length; i++) { |
| 254 | if (elements->get(i) == *object) { |
| 255 | AllowHeapAllocation allow_to_return_error; |
| 256 | Handle<Object> error = |
| 257 | factory()->NewTypeError(MessageTemplate::kCircularStructure); |
| 258 | isolate_->Throw(*error); |
| 259 | return EXCEPTION; |
| 260 | } |
| 261 | } |
| 262 | } |
| 263 | JSArray::SetLength(stack_, length + 1); |
| 264 | FixedArray::cast(stack_->elements())->set(length, *object); |
| 265 | return SUCCESS; |
| 266 | } |
| 267 | |
| 268 | void JsonStringifier::StackPop() { |
| 269 | int length = Smi::cast(stack_->length())->value(); |
| 270 | stack_->set_length(Smi::FromInt(length - 1)); |
| 271 | } |
| 272 | |
| 273 | template <bool deferred_string_key> |
| 274 | JsonStringifier::Result JsonStringifier::Serialize_(Handle<Object> object, |
| 275 | bool comma, |
| 276 | Handle<Object> key) { |
| 277 | StackLimitCheck interrupt_check(isolate_); |
| 278 | Handle<Object> initial_value = object; |
| 279 | if (interrupt_check.InterruptRequested() && |
| 280 | isolate_->stack_guard()->HandleInterrupts()->IsException(isolate_)) { |
| 281 | return EXCEPTION; |
| 282 | } |
| 283 | if (object->IsJSReceiver()) { |
| 284 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 285 | isolate_, object, ApplyToJsonFunction(object, key), EXCEPTION); |
| 286 | } |
| 287 | if (!replacer_function_.is_null()) { |
| 288 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 289 | isolate_, object, ApplyReplacerFunction(object, key, initial_value), |
| 290 | EXCEPTION); |
| 291 | } |
| 292 | |
| 293 | if (object->IsSmi()) { |
| 294 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
| 295 | return SerializeSmi(Smi::cast(*object)); |
| 296 | } |
| 297 | |
| 298 | switch (HeapObject::cast(*object)->map()->instance_type()) { |
| 299 | case HEAP_NUMBER_TYPE: |
| 300 | case MUTABLE_HEAP_NUMBER_TYPE: |
| 301 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
| 302 | return SerializeHeapNumber(Handle<HeapNumber>::cast(object)); |
| 303 | case ODDBALL_TYPE: |
| 304 | switch (Oddball::cast(*object)->kind()) { |
| 305 | case Oddball::kFalse: |
| 306 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
| 307 | builder_.AppendCString("false"); |
| 308 | return SUCCESS; |
| 309 | case Oddball::kTrue: |
| 310 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
| 311 | builder_.AppendCString("true"); |
| 312 | return SUCCESS; |
| 313 | case Oddball::kNull: |
| 314 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
| 315 | builder_.AppendCString("null"); |
| 316 | return SUCCESS; |
| 317 | default: |
| 318 | return UNCHANGED; |
| 319 | } |
| 320 | case JS_ARRAY_TYPE: |
| 321 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
| 322 | return SerializeJSArray(Handle<JSArray>::cast(object)); |
| 323 | case JS_VALUE_TYPE: |
| 324 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
| 325 | return SerializeJSValue(Handle<JSValue>::cast(object)); |
| 326 | case SIMD128_VALUE_TYPE: |
| 327 | case SYMBOL_TYPE: |
| 328 | return UNCHANGED; |
| 329 | default: |
| 330 | if (object->IsString()) { |
| 331 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
| 332 | SerializeString(Handle<String>::cast(object)); |
| 333 | return SUCCESS; |
| 334 | } else { |
| 335 | DCHECK(object->IsJSReceiver()); |
| 336 | if (object->IsCallable()) return UNCHANGED; |
| 337 | // Go to slow path for global proxy and objects requiring access checks. |
| 338 | if (deferred_string_key) SerializeDeferredKey(comma, key); |
| 339 | if (object->IsJSProxy()) { |
| 340 | return SerializeJSProxy(Handle<JSProxy>::cast(object)); |
| 341 | } |
| 342 | return SerializeJSObject(Handle<JSObject>::cast(object)); |
| 343 | } |
| 344 | } |
| 345 | |
| 346 | UNREACHABLE(); |
| 347 | return UNCHANGED; |
| 348 | } |
| 349 | |
| 350 | JsonStringifier::Result JsonStringifier::SerializeJSValue( |
| 351 | Handle<JSValue> object) { |
| 352 | String* class_name = object->class_name(); |
| 353 | if (class_name == isolate_->heap()->String_string()) { |
| 354 | Handle<Object> value; |
| 355 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 356 | isolate_, value, Object::ToString(isolate_, object), EXCEPTION); |
| 357 | SerializeString(Handle<String>::cast(value)); |
| 358 | } else if (class_name == isolate_->heap()->Number_string()) { |
| 359 | Handle<Object> value; |
| 360 | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, value, Object::ToNumber(object), |
| 361 | EXCEPTION); |
| 362 | if (value->IsSmi()) return SerializeSmi(Smi::cast(*value)); |
| 363 | SerializeHeapNumber(Handle<HeapNumber>::cast(value)); |
| 364 | } else if (class_name == isolate_->heap()->Boolean_string()) { |
| 365 | Object* value = JSValue::cast(*object)->value(); |
| 366 | DCHECK(value->IsBoolean()); |
| 367 | builder_.AppendCString(value->IsTrue(isolate_) ? "true" : "false"); |
| 368 | } else { |
| 369 | // ES6 24.3.2.1 step 10.c, serialize as an ordinary JSObject. |
| 370 | return SerializeJSObject(object); |
| 371 | } |
| 372 | return SUCCESS; |
| 373 | } |
| 374 | |
| 375 | JsonStringifier::Result JsonStringifier::SerializeSmi(Smi* object) { |
| 376 | static const int kBufferSize = 100; |
| 377 | char chars[kBufferSize]; |
| 378 | Vector<char> buffer(chars, kBufferSize); |
| 379 | builder_.AppendCString(IntToCString(object->value(), buffer)); |
| 380 | return SUCCESS; |
| 381 | } |
| 382 | |
| 383 | JsonStringifier::Result JsonStringifier::SerializeDouble(double number) { |
| 384 | if (std::isinf(number) || std::isnan(number)) { |
| 385 | builder_.AppendCString("null"); |
| 386 | return SUCCESS; |
| 387 | } |
| 388 | static const int kBufferSize = 100; |
| 389 | char chars[kBufferSize]; |
| 390 | Vector<char> buffer(chars, kBufferSize); |
| 391 | builder_.AppendCString(DoubleToCString(number, buffer)); |
| 392 | return SUCCESS; |
| 393 | } |
| 394 | |
| 395 | JsonStringifier::Result JsonStringifier::SerializeJSArray( |
| 396 | Handle<JSArray> object) { |
| 397 | HandleScope handle_scope(isolate_); |
| 398 | Result stack_push = StackPush(object); |
| 399 | if (stack_push != SUCCESS) return stack_push; |
| 400 | uint32_t length = 0; |
| 401 | CHECK(object->length()->ToArrayLength(&length)); |
| 402 | DCHECK(!object->IsAccessCheckNeeded()); |
| 403 | builder_.AppendCharacter('['); |
| 404 | Indent(); |
| 405 | uint32_t i = 0; |
| 406 | if (replacer_function_.is_null()) { |
| 407 | switch (object->GetElementsKind()) { |
| 408 | case FAST_SMI_ELEMENTS: { |
| 409 | Handle<FixedArray> elements(FixedArray::cast(object->elements()), |
| 410 | isolate_); |
| 411 | StackLimitCheck interrupt_check(isolate_); |
| 412 | while (i < length) { |
| 413 | if (interrupt_check.InterruptRequested() && |
| 414 | isolate_->stack_guard()->HandleInterrupts()->IsException( |
| 415 | isolate_)) { |
| 416 | return EXCEPTION; |
| 417 | } |
| 418 | Separator(i == 0); |
| 419 | SerializeSmi(Smi::cast(elements->get(i))); |
| 420 | i++; |
| 421 | } |
| 422 | break; |
| 423 | } |
| 424 | case FAST_DOUBLE_ELEMENTS: { |
| 425 | // Empty array is FixedArray but not FixedDoubleArray. |
| 426 | if (length == 0) break; |
| 427 | Handle<FixedDoubleArray> elements( |
| 428 | FixedDoubleArray::cast(object->elements()), isolate_); |
| 429 | StackLimitCheck interrupt_check(isolate_); |
| 430 | while (i < length) { |
| 431 | if (interrupt_check.InterruptRequested() && |
| 432 | isolate_->stack_guard()->HandleInterrupts()->IsException( |
| 433 | isolate_)) { |
| 434 | return EXCEPTION; |
| 435 | } |
| 436 | Separator(i == 0); |
| 437 | SerializeDouble(elements->get_scalar(i)); |
| 438 | i++; |
| 439 | } |
| 440 | break; |
| 441 | } |
| 442 | case FAST_ELEMENTS: { |
| 443 | Handle<Object> old_length(object->length(), isolate_); |
| 444 | while (i < length) { |
| 445 | if (object->length() != *old_length || |
| 446 | object->GetElementsKind() != FAST_ELEMENTS) { |
| 447 | // Fall back to slow path. |
| 448 | break; |
| 449 | } |
| 450 | Separator(i == 0); |
| 451 | Result result = SerializeElement( |
| 452 | isolate_, |
| 453 | Handle<Object>(FixedArray::cast(object->elements())->get(i), |
| 454 | isolate_), |
| 455 | i); |
| 456 | if (result == UNCHANGED) { |
| 457 | builder_.AppendCString("null"); |
| 458 | } else if (result != SUCCESS) { |
| 459 | return result; |
| 460 | } |
| 461 | i++; |
| 462 | } |
| 463 | break; |
| 464 | } |
| 465 | // The FAST_HOLEY_* cases could be handled in a faster way. They resemble |
| 466 | // the non-holey cases except that a lookup is necessary for holes. |
| 467 | default: |
| 468 | break; |
| 469 | } |
| 470 | } |
| 471 | if (i < length) { |
| 472 | // Slow path for non-fast elements and fall-back in edge case. |
| 473 | Result result = SerializeArrayLikeSlow(object, i, length); |
| 474 | if (result != SUCCESS) return result; |
| 475 | } |
| 476 | Unindent(); |
| 477 | if (length > 0) NewLine(); |
| 478 | builder_.AppendCharacter(']'); |
| 479 | StackPop(); |
| 480 | return SUCCESS; |
| 481 | } |
| 482 | |
| 483 | JsonStringifier::Result JsonStringifier::SerializeArrayLikeSlow( |
| 484 | Handle<JSReceiver> object, uint32_t start, uint32_t length) { |
| 485 | // We need to write out at least two characters per array element. |
| 486 | static const int kMaxSerializableArrayLength = String::kMaxLength / 2; |
| 487 | if (length > kMaxSerializableArrayLength) { |
| 488 | isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError()); |
| 489 | return EXCEPTION; |
| 490 | } |
| 491 | for (uint32_t i = start; i < length; i++) { |
| 492 | Separator(i == 0); |
| 493 | Handle<Object> element; |
| 494 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 495 | isolate_, element, JSReceiver::GetElement(isolate_, object, i), |
| 496 | EXCEPTION); |
| 497 | Result result = SerializeElement(isolate_, element, i); |
| 498 | if (result == SUCCESS) continue; |
| 499 | if (result == UNCHANGED) { |
| 500 | // Detect overflow sooner for large sparse arrays. |
| 501 | if (builder_.HasOverflowed()) return EXCEPTION; |
| 502 | builder_.AppendCString("null"); |
| 503 | } else { |
| 504 | return result; |
| 505 | } |
| 506 | } |
| 507 | return SUCCESS; |
| 508 | } |
| 509 | |
| 510 | JsonStringifier::Result JsonStringifier::SerializeJSObject( |
| 511 | Handle<JSObject> object) { |
| 512 | HandleScope handle_scope(isolate_); |
| 513 | Result stack_push = StackPush(object); |
| 514 | if (stack_push != SUCCESS) return stack_push; |
| 515 | |
| 516 | if (property_list_.is_null() && |
| 517 | object->map()->instance_type() > LAST_CUSTOM_ELEMENTS_RECEIVER && |
| 518 | object->HasFastProperties() && |
| 519 | Handle<JSObject>::cast(object)->elements()->length() == 0) { |
| 520 | DCHECK(object->IsJSObject()); |
| 521 | DCHECK(!object->IsJSGlobalProxy()); |
| 522 | Handle<JSObject> js_obj = Handle<JSObject>::cast(object); |
| 523 | DCHECK(!js_obj->HasIndexedInterceptor()); |
| 524 | DCHECK(!js_obj->HasNamedInterceptor()); |
| 525 | Handle<Map> map(js_obj->map()); |
| 526 | builder_.AppendCharacter('{'); |
| 527 | Indent(); |
| 528 | bool comma = false; |
| 529 | for (int i = 0; i < map->NumberOfOwnDescriptors(); i++) { |
| 530 | Handle<Name> name(map->instance_descriptors()->GetKey(i), isolate_); |
| 531 | // TODO(rossberg): Should this throw? |
| 532 | if (!name->IsString()) continue; |
| 533 | Handle<String> key = Handle<String>::cast(name); |
| 534 | PropertyDetails details = map->instance_descriptors()->GetDetails(i); |
| 535 | if (details.IsDontEnum()) continue; |
| 536 | Handle<Object> property; |
| 537 | if (details.type() == DATA && *map == js_obj->map()) { |
| 538 | FieldIndex field_index = FieldIndex::ForDescriptor(*map, i); |
| 539 | property = JSObject::FastPropertyAt(js_obj, details.representation(), |
| 540 | field_index); |
| 541 | } else { |
| 542 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 543 | isolate_, property, Object::GetPropertyOrElement(js_obj, key), |
| 544 | EXCEPTION); |
| 545 | } |
| 546 | Result result = SerializeProperty(property, comma, key); |
| 547 | if (!comma && result == SUCCESS) comma = true; |
| 548 | if (result == EXCEPTION) return result; |
| 549 | } |
| 550 | Unindent(); |
| 551 | if (comma) NewLine(); |
| 552 | builder_.AppendCharacter('}'); |
| 553 | } else { |
| 554 | Result result = SerializeJSReceiverSlow(object); |
| 555 | if (result != SUCCESS) return result; |
| 556 | } |
| 557 | StackPop(); |
| 558 | return SUCCESS; |
| 559 | } |
| 560 | |
| 561 | JsonStringifier::Result JsonStringifier::SerializeJSReceiverSlow( |
| 562 | Handle<JSReceiver> object) { |
| 563 | Handle<FixedArray> contents = property_list_; |
| 564 | if (contents.is_null()) { |
| 565 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 566 | isolate_, contents, |
| 567 | KeyAccumulator::GetKeys(object, KeyCollectionMode::kOwnOnly, |
| 568 | ENUMERABLE_STRINGS, |
| 569 | GetKeysConversion::kConvertToString), |
| 570 | EXCEPTION); |
| 571 | } |
| 572 | builder_.AppendCharacter('{'); |
| 573 | Indent(); |
| 574 | bool comma = false; |
| 575 | for (int i = 0; i < contents->length(); i++) { |
| 576 | Handle<String> key(String::cast(contents->get(i)), isolate_); |
| 577 | Handle<Object> property; |
| 578 | ASSIGN_RETURN_ON_EXCEPTION_VALUE(isolate_, property, |
| 579 | Object::GetPropertyOrElement(object, key), |
| 580 | EXCEPTION); |
| 581 | Result result = SerializeProperty(property, comma, key); |
| 582 | if (!comma && result == SUCCESS) comma = true; |
| 583 | if (result == EXCEPTION) return result; |
| 584 | } |
| 585 | Unindent(); |
| 586 | if (comma) NewLine(); |
| 587 | builder_.AppendCharacter('}'); |
| 588 | return SUCCESS; |
| 589 | } |
| 590 | |
| 591 | JsonStringifier::Result JsonStringifier::SerializeJSProxy( |
| 592 | Handle<JSProxy> object) { |
| 593 | HandleScope scope(isolate_); |
| 594 | Result stack_push = StackPush(object); |
| 595 | if (stack_push != SUCCESS) return stack_push; |
| 596 | Maybe<bool> is_array = Object::IsArray(object); |
| 597 | if (is_array.IsNothing()) return EXCEPTION; |
| 598 | if (is_array.FromJust()) { |
| 599 | Handle<Object> length_object; |
| 600 | ASSIGN_RETURN_ON_EXCEPTION_VALUE( |
| 601 | isolate_, length_object, |
| 602 | Object::GetLengthFromArrayLike(isolate_, object), EXCEPTION); |
| 603 | uint32_t length; |
| 604 | if (!length_object->ToUint32(&length)) { |
| 605 | // Technically, we need to be able to handle lengths outside the |
| 606 | // uint32_t range. However, we would run into string size overflow |
| 607 | // if we tried to stringify such an array. |
| 608 | isolate_->Throw(*isolate_->factory()->NewInvalidStringLengthError()); |
| 609 | return EXCEPTION; |
| 610 | } |
| 611 | builder_.AppendCharacter('['); |
| 612 | Indent(); |
| 613 | Result result = SerializeArrayLikeSlow(object, 0, length); |
| 614 | if (result != SUCCESS) return result; |
| 615 | Unindent(); |
| 616 | if (length > 0) NewLine(); |
| 617 | builder_.AppendCharacter(']'); |
| 618 | } else { |
| 619 | Result result = SerializeJSReceiverSlow(object); |
| 620 | if (result != SUCCESS) return result; |
| 621 | } |
| 622 | StackPop(); |
| 623 | return SUCCESS; |
| 624 | } |
| 625 | |
| 626 | template <typename SrcChar, typename DestChar> |
| 627 | void JsonStringifier::SerializeStringUnchecked_( |
| 628 | Vector<const SrcChar> src, |
| 629 | IncrementalStringBuilder::NoExtend<DestChar>* dest) { |
| 630 | // Assert that uc16 character is not truncated down to 8 bit. |
| 631 | // The <uc16, char> version of this method must not be called. |
| 632 | DCHECK(sizeof(DestChar) >= sizeof(SrcChar)); |
| 633 | |
| 634 | for (int i = 0; i < src.length(); i++) { |
| 635 | SrcChar c = src[i]; |
| 636 | if (DoNotEscape(c)) { |
| 637 | dest->Append(c); |
| 638 | } else { |
| 639 | dest->AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]); |
| 640 | } |
| 641 | } |
| 642 | } |
| 643 | |
| 644 | template <typename SrcChar, typename DestChar> |
| 645 | void JsonStringifier::SerializeString_(Handle<String> string) { |
| 646 | int length = string->length(); |
| 647 | builder_.Append<uint8_t, DestChar>('"'); |
| 648 | // We make a rough estimate to find out if the current string can be |
| 649 | // serialized without allocating a new string part. The worst case length of |
| 650 | // an escaped character is 6. Shifting the remainin string length right by 3 |
| 651 | // is a more pessimistic estimate, but faster to calculate. |
| 652 | int worst_case_length = length << 3; |
| 653 | if (builder_.CurrentPartCanFit(worst_case_length)) { |
| 654 | DisallowHeapAllocation no_gc; |
| 655 | Vector<const SrcChar> vector = string->GetCharVector<SrcChar>(); |
| 656 | IncrementalStringBuilder::NoExtendBuilder<DestChar> no_extend( |
| 657 | &builder_, worst_case_length); |
| 658 | SerializeStringUnchecked_(vector, &no_extend); |
| 659 | } else { |
| 660 | FlatStringReader reader(isolate_, string); |
| 661 | for (int i = 0; i < reader.length(); i++) { |
| 662 | SrcChar c = reader.Get<SrcChar>(i); |
| 663 | if (DoNotEscape(c)) { |
| 664 | builder_.Append<SrcChar, DestChar>(c); |
| 665 | } else { |
| 666 | builder_.AppendCString(&JsonEscapeTable[c * kJsonEscapeTableEntrySize]); |
| 667 | } |
| 668 | } |
| 669 | } |
| 670 | |
| 671 | builder_.Append<uint8_t, DestChar>('"'); |
| 672 | } |
| 673 | |
| 674 | template <> |
| 675 | bool JsonStringifier::DoNotEscape(uint8_t c) { |
| 676 | return c >= '#' && c <= '~' && c != '\\'; |
| 677 | } |
| 678 | |
| 679 | template <> |
| 680 | bool JsonStringifier::DoNotEscape(uint16_t c) { |
| 681 | return c >= '#' && c != '\\' && c != 0x7f; |
| 682 | } |
| 683 | |
| 684 | void JsonStringifier::NewLine() { |
| 685 | if (gap_ == nullptr) return; |
| 686 | builder_.AppendCharacter('\n'); |
| 687 | for (int i = 0; i < indent_; i++) builder_.AppendCString(gap_); |
| 688 | } |
| 689 | |
| 690 | void JsonStringifier::Separator(bool first) { |
| 691 | if (!first) builder_.AppendCharacter(','); |
| 692 | NewLine(); |
| 693 | } |
| 694 | |
| 695 | void JsonStringifier::SerializeDeferredKey(bool deferred_comma, |
| 696 | Handle<Object> deferred_key) { |
| 697 | Separator(!deferred_comma); |
| 698 | SerializeString(Handle<String>::cast(deferred_key)); |
| 699 | builder_.AppendCharacter(':'); |
| 700 | if (gap_ != nullptr) builder_.AppendCharacter(' '); |
| 701 | } |
| 702 | |
| 703 | void JsonStringifier::SerializeString(Handle<String> object) { |
| 704 | object = String::Flatten(object); |
| 705 | if (builder_.CurrentEncoding() == String::ONE_BYTE_ENCODING) { |
| 706 | if (object->IsOneByteRepresentationUnderneath()) { |
| 707 | SerializeString_<uint8_t, uint8_t>(object); |
| 708 | } else { |
| 709 | builder_.ChangeEncoding(); |
| 710 | SerializeString(object); |
| 711 | } |
| 712 | } else { |
| 713 | if (object->IsOneByteRepresentationUnderneath()) { |
| 714 | SerializeString_<uint8_t, uc16>(object); |
| 715 | } else { |
| 716 | SerializeString_<uc16, uc16>(object); |
| 717 | } |
| 718 | } |
| 719 | } |
| 720 | |
| 721 | } // namespace internal |
| 722 | } // namespace v8 |