Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(198)

Side by Side Diff: src/json-stringifier.h

Issue 11186025: Reimplement a simpler version of JSON.stringify. (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Created 8 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « src/json.js ('k') | src/runtime.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #ifndef V8_JSON_STRINGIFIER_H_
29 #define V8_JSON_STRINGIFIER_H_
30
31 #include "v8.h"
32 #include "v8utils.h"
33 #include "v8conversions.h"
34
35 namespace v8 {
36 namespace internal {
37
38 class BasicJsonStringifier BASE_EMBEDDED {
39 public:
40 explicit BasicJsonStringifier(Isolate* isolate);
41
42 MaybeObject* Stringify(Handle<Object> object);
43
44 private:
45 static const int kPartLength = 8 * 1024;
46 static const int kStackLimit = 8 * 1024;
47
48 enum Result { UNCHANGED, SUCCESS, BAILOUT, CIRCULAR, STACK_OVERFLOW };
49
50 template <bool is_ascii> void Extend();
51
52 void ChangeEncoding();
53
54 void ShrinkCurrentPart();
55
56 template <bool is_ascii, typename Char>
57 INLINE(void Append_(Char c));
58
59 template <bool is_ascii, typename Char>
60 INLINE(void AppendUnchecked_(Char c));
61
62 template <bool is_ascii, typename Char>
63 INLINE(void Append_(Char* chars));
64
65 template <bool is_ascii, typename Char>
66 INLINE(void Append_(const Char* chars));
67
68 template <bool is_ascii, typename Char>
69 INLINE(void AppendUnchecked_(const Char* chars));
70
71 INLINE(void Append(char c)) {
72 if (is_ascii_) {
73 Append_<true>(c);
74 } else {
75 Append_<false>(c);
76 }
77 }
78
79 INLINE(void Append(const char* chars)) {
80 if (is_ascii_) {
81 Append_<true>(chars);
82 } else {
83 Append_<false>(chars);
84 }
85 }
86
87 INLINE(Handle<Object> GetProperty(Handle<JSObject> object,
88 Handle<String> key));
89
90 INLINE(bool MayHaveToJsonFunction(Handle<JSObject> object));
91
92 INLINE(Result Serialize(Handle<Object> object)) {
93 return Serialize_<false>(object);
94 }
95
96 INLINE(Result SerializeDeferred(Handle<Object> object,
97 bool deferred_comma,
98 Handle<String> deferred_key)) {
99 ASSERT(!deferred_key.is_null());
100 return Serialize_<true>(object, deferred_comma, deferred_key);
101 }
102
103 template <bool deferred_key>
104 Result Serialize_(Handle<Object> object,
105 bool comma = false,
106 Handle<String> key = Handle<String>::null());
107
108 INLINE(void SerializeDeferredKey(bool deferred_comma,
109 Handle<String> deferred_key)) {
110 if (deferred_comma) Append(',');
111 SerializeString(deferred_key);
112 Append(':');
113 }
114
115 INLINE(Result SerializeSmi(Smi* object));
116
117 INLINE(Result SerializeDouble(double number));
118 INLINE(Result SerializeHeapNumber(Handle<HeapNumber> object)) {
119 return SerializeDouble(object->value());
120 }
121
122 Result SerializeArray(Handle<JSArray> object);
123 Result SerializeObject(Handle<JSObject> object);
124
125 void SerializeString(Handle<String> object);
126
127 template <bool is_ascii, typename Char>
128 INLINE(void SerializeString_(Vector<const Char> vector));
129
130 INLINE(Result StackPush(Handle<Object> object));
131 INLINE(void StackPop());
132
133 Isolate* isolate_;
134 Handle<String> accumulator_;
135 Handle<String> current_part_;
136 Handle<String> tojson_symbol_;
137 Handle<JSArray> stack_;
138 int current_index_;
139 bool is_ascii_;
140
141 static const int kJsonQuotesCharactersPerEntry = 8;
142 static const char* const JsonQuotes;
143 };
144
145
146 const char* const BasicJsonStringifier::JsonQuotes =
147 "\\u0000\0 \\u0001\0 \\u0002\0 \\u0003\0 "
148 "\\u0004\0 \\u0005\0 \\u0006\0 \\u0007\0 "
149 "\\b\0 \\t\0 \\n\0 \\u000b\0 "
150 "\\f\0 \\r\0 \\u000e\0 \\u000f\0 "
151 "\\u0010\0 \\u0011\0 \\u0012\0 \\u0013\0 "
152 "\\u0014\0 \\u0015\0 \\u0016\0 \\u0017\0 "
153 "\\u0018\0 \\u0019\0 \\u001a\0 \\u001b\0 "
154 "\\u001c\0 \\u001d\0 \\u001e\0 \\u001f\0 "
155 " \0 !\0 \\\"\0 #\0 "
156 "$\0 %\0 &\0 '\0 "
157 "(\0 )\0 *\0 +\0 "
158 ",\0 -\0 .\0 /\0 "
159 "0\0 1\0 2\0 3\0 "
160 "4\0 5\0 6\0 7\0 "
161 "8\0 9\0 :\0 ;\0 "
162 "<\0 =\0 >\0 ?\0 "
163 "@\0 A\0 B\0 C\0 "
164 "D\0 E\0 F\0 G\0 "
165 "H\0 I\0 J\0 K\0 "
166 "L\0 M\0 N\0 O\0 "
167 "P\0 Q\0 R\0 S\0 "
168 "T\0 U\0 V\0 W\0 "
169 "X\0 Y\0 Z\0 [\0 "
170 "\\\\\0 ]\0 ^\0 _\0 "
171 "`\0 a\0 b\0 c\0 "
172 "d\0 e\0 f\0 g\0 "
173 "h\0 i\0 j\0 k\0 "
174 "l\0 m\0 n\0 o\0 "
175 "p\0 q\0 r\0 s\0 "
176 "t\0 u\0 v\0 w\0 "
177 "x\0 y\0 z\0 {\0 "
178 "|\0 }\0 ~\0 \177\0 ";
179
180
181 BasicJsonStringifier::BasicJsonStringifier(Isolate* isolate)
182 : isolate_(isolate), current_index_(0), is_ascii_(true) {
183 accumulator_ = isolate_->factory()->empty_string();
184 current_part_ =
185 isolate_->factory()->NewRawAsciiString(kPartLength);
186 tojson_symbol_ = isolate_->factory()->LookupAsciiSymbol("toJSON");
187 stack_ = isolate_->factory()->NewJSArray(8);
188 }
189
190
191 MaybeObject* BasicJsonStringifier::Stringify(Handle<Object> object) {
192 switch (Serialize(object)) {
193 case SUCCESS:
194 ShrinkCurrentPart();
195 return *isolate_->factory()->NewConsString(accumulator_, current_part_);
196 case UNCHANGED:
197 return isolate_->heap()->undefined_value();
198 case CIRCULAR:
199 return isolate_->Throw(*isolate_->factory()->NewTypeError(
200 "circular_structure", HandleVector<Object>(NULL, 0)));
201 case STACK_OVERFLOW:
202 return isolate_->StackOverflow();
203 default:
204 return Smi::FromInt(0);
205 }
206 }
207
208
209 template <bool is_ascii, typename Char>
210 void BasicJsonStringifier::Append_(Char c) {
211 if (is_ascii) {
212 SeqAsciiString::cast(*current_part_)->SeqAsciiStringSet(
213 current_index_++, c);
214 } else {
215 SeqTwoByteString::cast(*current_part_)->SeqTwoByteStringSet(
216 current_index_++, c);
217 }
218 if (current_index_ == kPartLength) Extend<is_ascii>();
219 }
220
221
222 template <bool is_ascii, typename Char>
223 void BasicJsonStringifier::AppendUnchecked_(Char c) {
224 if (is_ascii) {
225 SeqAsciiString::cast(*current_part_)->SeqAsciiStringSet(
226 current_index_++, c);
227 } else {
228 SeqTwoByteString::cast(*current_part_)->SeqTwoByteStringSet(
229 current_index_++, c);
230 }
231 ASSERT(current_index_ < kPartLength);
232 }
233
234
235 template <bool is_ascii, typename Char>
236 void BasicJsonStringifier::Append_(Char* chars) {
237 for ( ; *chars != '\0'; chars++) Append_<is_ascii>(*chars);
238 }
239
240
241 template <bool is_ascii, typename Char>
242 void BasicJsonStringifier::Append_(const Char* chars) {
243 for ( ; *chars != '\0'; chars++) Append_<is_ascii, Char>(*chars);
244 }
245
246
247 template <bool is_ascii, typename Char>
248 void BasicJsonStringifier::AppendUnchecked_(const Char* chars) {
249 for ( ; *chars != '\0'; chars++) AppendUnchecked_<is_ascii, Char>(*chars);
250 }
251
252
253 Handle<Object> BasicJsonStringifier::GetProperty(Handle<JSObject> object,
254 Handle<String> key) {
255 LookupResult lookup(isolate_);
256 object->LocalLookupRealNamedProperty(*key, &lookup);
257 if (!lookup.IsProperty()) return isolate_->factory()->undefined_value();
258 switch (lookup.type()) {
259 case NORMAL: {
260 Object* value = lookup.holder()->GetNormalizedProperty(&lookup);
261 ASSERT(!value->IsTheHole());
262 return Handle<Object>(value);
263 }
264 case FIELD: {
265 Object* value = lookup.holder()->FastPropertyAt(lookup.GetFieldIndex());
266 ASSERT(!value->IsTheHole());
267 return Handle<Object>(value);
268 }
269 case CONSTANT_FUNCTION:
270 return Handle<Object>(lookup.GetConstantFunction());
271 case CALLBACKS:
272 case HANDLER:
273 case INTERCEPTOR:
274 return Handle<Object>::null();
275 case TRANSITION:
276 case NONEXISTENT:
277 UNREACHABLE();
278 break;
279 }
280 return Handle<Object>::null();
281 }
282
283
284 bool BasicJsonStringifier::MayHaveToJsonFunction(Handle<JSObject> object) {
285 LookupResult lookup(isolate_);
286 object->LookupRealNamedProperty(*tojson_symbol_, &lookup);
287 if (!lookup.IsProperty()) return false;
288 Object* value;
289 switch (lookup.type()) {
290 case NORMAL:
291 value = lookup.holder()->GetNormalizedProperty(&lookup);
292 break;
293 case FIELD:
294 value = lookup.holder()->FastPropertyAt(lookup.GetFieldIndex());
295 break;
296 default:
297 return true;
298 }
299 ASSERT(!value->IsTheHole());
300 return value->IsSpecFunction();
301 }
302
303
304 BasicJsonStringifier::Result BasicJsonStringifier::StackPush(
305 Handle<Object> object) {
306 int length = Smi::cast(stack_->length())->value();
307 if (length > kStackLimit) return STACK_OVERFLOW;
308 FixedArray* elements = FixedArray::cast(stack_->elements());
309 for (int i = 0; i < length; i++) {
310 if (elements->get(i) == *object) {
311 return CIRCULAR;
312 }
313 }
314 stack_->EnsureSize(length + 1);
315 FixedArray::cast(stack_->elements())->set(length, *object);
316 stack_->set_length(Smi::FromInt(length + 1));
317 return SUCCESS;
318 }
319
320
321 void BasicJsonStringifier::StackPop() {
322 int length = Smi::cast(stack_->length())->value();
323 stack_->set_length(Smi::FromInt(length - 1));
324 }
325
326
327 template <bool deferred_key>
328 BasicJsonStringifier::Result BasicJsonStringifier::Serialize_(
329 Handle<Object> object, bool comma, Handle<String> key) {
330 if (object->IsJSObject()) {
331 // We don't deal with custom toJSON functions.
332 if (MayHaveToJsonFunction(Handle<JSObject>::cast(object))) return BAILOUT;
333
334 if (object->IsJSFunction()) {
335 return UNCHANGED;
336 } else if (object->IsJSArray()) {
337 if (deferred_key) SerializeDeferredKey(comma, key);
338 return SerializeArray(Handle<JSArray>::cast(object));
339 } else if (object->IsJSValue()) {
340 // JSValue with a custom prototype.
341 if (object->GetPrototype()->IsJSReceiver()) return BAILOUT;
342 // Unpack value wrapper and fall through.
343 object = Handle<Object>(JSValue::cast(*object)->value());
344 } else {
345 if (deferred_key) SerializeDeferredKey(comma, key);
346 return SerializeObject(Handle<JSObject>::cast(object));
347 }
348 }
349
350 if (object->IsString()) {
351 if (deferred_key) SerializeDeferredKey(comma, key);
352 SerializeString(Handle<String>::cast(object));
353 return SUCCESS;
354 } else if (object->IsSmi()) {
355 if (deferred_key) SerializeDeferredKey(comma, key);
356 return SerializeSmi(Smi::cast(*object));
357 } else if (object->IsHeapNumber()) {
358 if (deferred_key) SerializeDeferredKey(comma, key);
359 return SerializeHeapNumber(Handle<HeapNumber>::cast(object));
360 } else if (object->IsOddball()) {
361 switch (Oddball::cast(*object)->kind()) {
362 case Oddball::kFalse:
363 if (deferred_key) SerializeDeferredKey(comma, key);
364 Append("false");
365 return SUCCESS;
366 case Oddball::kTrue:
367 if (deferred_key) SerializeDeferredKey(comma, key);
368 Append("true");
369 return SUCCESS;
370 case Oddball::kNull:
371 if (deferred_key) SerializeDeferredKey(comma, key);
372 Append("null");
373 return SUCCESS;
374 }
375 }
376
377 return UNCHANGED;
378 }
379
380
381 BasicJsonStringifier::Result BasicJsonStringifier::SerializeSmi(Smi* object) {
382 static const int kBufferSize = 100;
383 char chars[kBufferSize];
384 Vector<char> buffer(chars, kBufferSize);
385 Append(IntToCString(object->value(), buffer));
386 return SUCCESS;
387 }
388
389
390 BasicJsonStringifier::Result BasicJsonStringifier::SerializeDouble(
391 double number) {
392 if (isinf(number) || isnan(number)) {
393 Append("null");
394 return SUCCESS;
395 }
396 static const int kBufferSize = 100;
397 char chars[kBufferSize];
398 Vector<char> buffer(chars, kBufferSize);
399 Append(DoubleToCString(number, buffer));
400 return SUCCESS;
401 }
402
403
404 BasicJsonStringifier::Result BasicJsonStringifier::SerializeArray(
405 Handle<JSArray> object) {
406 if (StackPush(object) == CIRCULAR) return CIRCULAR;
407 int length = Smi::cast(object->length())->value();
408 Append('[');
409 switch (object->GetElementsKind()) {
410 case FAST_SMI_ELEMENTS: {
411 Handle<FixedArray> elements = Handle<FixedArray>(
412 FixedArray::cast(object->elements()));
413 for (int i = 0; i < length; i++) {
414 if (i > 0) Append(',');
415 SerializeSmi(Smi::cast(elements->get(i)));
416 }
417 break;
418 }
419 case FAST_HOLEY_SMI_ELEMENTS: {
420 Handle<FixedArray> elements = Handle<FixedArray>(
421 FixedArray::cast(object->elements()));
422 for (int i = 0; i < length; i++) {
423 if (i > 0) Append(',');
424 if (elements->is_the_hole(i)) {
425 Append("null");
426 } else {
427 SerializeSmi(Smi::cast(elements->get(i)));
428 }
429 }
430 break;
431 }
432 case FAST_HOLEY_DOUBLE_ELEMENTS:
433 case FAST_DOUBLE_ELEMENTS: {
434 Handle<FixedDoubleArray> elements = Handle<FixedDoubleArray>(
435 FixedDoubleArray::cast(object->elements()));
436 for (int i = 0; i < length; i++) {
437 if (i > 0) Append(',');
438 SerializeDouble(elements->get_scalar(i));
439 }
440 break;
441 }
442 case FAST_HOLEY_ELEMENTS:
443 case FAST_ELEMENTS: {
444 Handle<FixedArray> elements = Handle<FixedArray>(
445 FixedArray::cast(object->elements()));
446 for (int i = 0; i < length; i++) {
447 if (i > 0) Append(',');
448 Result result = Serialize(Handle<Object>(elements->get(i)));
449 if (result == SUCCESS) continue;
450 if (result == UNCHANGED) {
451 Append("null");
452 } else {
453 return result;
454 }
455 }
456 break;
457 }
458 default:
459 return BAILOUT;
460 }
461 Append(']');
462 StackPop();
463 return SUCCESS;
464 }
465
466
467 BasicJsonStringifier::Result BasicJsonStringifier::SerializeObject(
468 Handle<JSObject> object) {
469 Result stack_push = StackPush(object);
470 if (stack_push != SUCCESS) return stack_push;
471 if (object->IsJSGlobalProxy()) return BAILOUT;
472 bool threw = false;
473 Handle<FixedArray> contents =
474 GetKeysInFixedArrayFor(object, LOCAL_ONLY, &threw);
475 if (threw) return BAILOUT;
476 Append('{');
477 int length = contents->length();
478 bool comma = false;
479 for (int i = 0; i < length; i++) {
480 Object* key = contents->get(i);
481 Handle<String> key_handle;
482 Handle<Object> property;
483 if (key->IsString()) {
484 key_handle = Handle<String>(String::cast(key));
485 property = GetProperty(object, key_handle);
486 } else {
487 ASSERT(key->IsNumber());
488 key_handle = isolate_->factory()->NumberToString(Handle<Object>(key));
489 uint32_t index;
490 if (key->IsSmi()) {
491 property = Object::GetElement(object, Smi::cast(key)->value());
492 } else if (key_handle->AsArrayIndex(&index)) {
493 property = Object::GetElement(object, index);
494 } else {
495 property = GetProperty(object, key_handle);
496 }
497 }
498 if (property.is_null()) return BAILOUT;
499 Result result = SerializeDeferred(property, comma, key_handle);
500 if (!comma && result == SUCCESS) comma = true;
501 if (result >= BAILOUT) return result;
502 }
503 Append('}');
504 StackPop();
505 return SUCCESS;
506 }
507
508
509 void BasicJsonStringifier::ShrinkCurrentPart() {
510 ASSERT(current_index_ < kPartLength);
511 if (current_index_ == 0) {
512 current_part_ = isolate_->factory()->empty_string();
513 return;
514 }
515
516 int string_size, allocated_string_size;
517 if (is_ascii_) {
518 allocated_string_size = SeqAsciiString::SizeFor(kPartLength);
519 string_size = SeqAsciiString::SizeFor(current_index_);
520 } else {
521 allocated_string_size = SeqTwoByteString::SizeFor(kPartLength);
522 string_size = SeqTwoByteString::SizeFor(current_index_);
523 }
524
525 int delta = allocated_string_size - string_size;
526 current_part_->set_length(current_index_);
527
528 // String sizes are pointer size aligned, so that we can use filler objects
529 // that are a multiple of pointer size.
530 Address end_of_string = current_part_->address() + string_size;
531 isolate_->heap()->CreateFillerObjectAt(end_of_string, delta);
532 if (Marking::IsBlack(Marking::MarkBitFrom(*current_part_))) {
533 MemoryChunk::IncrementLiveBytesFromMutator(
534 current_part_->address(), -delta);
535 }
536 }
537
538
539 template <bool is_ascii>
540 void BasicJsonStringifier::Extend() {
541 accumulator_ =
542 isolate_->factory()->NewConsString(accumulator_, current_part_);
543 if (is_ascii) {
544 current_part_ =
545 isolate_->factory()->NewRawAsciiString(kPartLength);
546 } else {
547 current_part_ =
548 isolate_->factory()->NewRawTwoByteString(kPartLength);
549 }
550 current_index_ = 0;
551 }
552
553
554 void BasicJsonStringifier::ChangeEncoding() {
555 ShrinkCurrentPart();
556 accumulator_ = isolate_->factory()->NewConsString(accumulator_,
557 current_part_);
558 current_part_ =
559 isolate_->factory()->NewRawTwoByteString(kPartLength);
560 current_index_ = 0;
561 is_ascii_ = false;
562 }
563
564
565 template <bool is_ascii, typename Char>
566 void BasicJsonStringifier::SerializeString_(Vector<const Char> vector) {
567 int length = vector.length();
568 if (current_index_ + (length << 3) < (kPartLength - 2)) {
569 AppendUnchecked_<is_ascii, char>('"');
570 for (int i = 0; i < length; i++) {
571 Char c = vector[i];
572 if ((c >= '#' && c <= '~' && c != '\\') ||
573 (!is_ascii && ((c & 0xFF80) != 0))) {
574 AppendUnchecked_<is_ascii, Char>(c);
575 } else {
576 AppendUnchecked_<is_ascii, char>(
577 &JsonQuotes[c * kJsonQuotesCharactersPerEntry]);
578 }
579 }
580 AppendUnchecked_<is_ascii, char>('"');
581 } else {
582 Append_<is_ascii, char>('"');
583 for (int i = 0; i < length; i++) {
584 Char c = vector[i];
585 if ((c >= '#' && c <= '~' && c != '\\') ||
586 (!is_ascii && ((c & 0xFF80) != 0))) {
587 Append_<is_ascii, Char>(c);
588 } else {
589 Append_<is_ascii, char>(&JsonQuotes[c * kJsonQuotesCharactersPerEntry]);
590 }
591 }
592 Append_<is_ascii, char>('"');
593 }
594 }
595
596
597 void BasicJsonStringifier::SerializeString(Handle<String> object) {
598 FlattenString(object);
599 String::FlatContent flat = object->GetFlatContent();
600 if (is_ascii_) {
601 if (flat.IsAscii()) {
602 SerializeString_<true, char>(flat.ToAsciiVector());
603 } else {
604 ChangeEncoding();
605 SerializeString(object);
606 }
607 } else {
608 if (flat.IsAscii()) {
609 SerializeString_<false, char>(flat.ToAsciiVector());
610 } else {
611 SerializeString_<false, uc16>(flat.ToUC16Vector());
612 }
613 }
614 }
615
616 } } // namespace v8::internal
617
618 #endif // V8_JSON_STRINGIFIER_H_
OLDNEW
« no previous file with comments | « src/json.js ('k') | src/runtime.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698