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

Side by Side Diff: src/preparser.h

Issue 217823003: Make invalid LHSs that are calls late errors (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Comments Created 6 years, 8 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/parser.cc ('k') | src/preparser.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2012 the V8 project authors. All rights reserved. 1 // Copyright 2012 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 407 matching lines...) Expand 10 before | Expand all | Expand 10 after
418 ExpressionT ParseConditionalExpression(bool accept_IN, bool* ok); 418 ExpressionT ParseConditionalExpression(bool accept_IN, bool* ok);
419 ExpressionT ParseBinaryExpression(int prec, bool accept_IN, bool* ok); 419 ExpressionT ParseBinaryExpression(int prec, bool accept_IN, bool* ok);
420 ExpressionT ParseUnaryExpression(bool* ok); 420 ExpressionT ParseUnaryExpression(bool* ok);
421 ExpressionT ParsePostfixExpression(bool* ok); 421 ExpressionT ParsePostfixExpression(bool* ok);
422 ExpressionT ParseLeftHandSideExpression(bool* ok); 422 ExpressionT ParseLeftHandSideExpression(bool* ok);
423 ExpressionT ParseMemberWithNewPrefixesExpression(bool* ok); 423 ExpressionT ParseMemberWithNewPrefixesExpression(bool* ok);
424 ExpressionT ParseMemberExpression(bool* ok); 424 ExpressionT ParseMemberExpression(bool* ok);
425 ExpressionT ParseMemberExpressionContinuation(ExpressionT expression, 425 ExpressionT ParseMemberExpressionContinuation(ExpressionT expression,
426 bool* ok); 426 bool* ok);
427 427
428 // Checks if the expression is a valid reference expression (e.g., on the
429 // left-hand side of assignments). Although ruled out by ECMA as early errors,
430 // we allow calls for web compatibility and rewrite them to a runtime throw.
431 ExpressionT CheckAndRewriteReferenceExpression(
432 ExpressionT expression,
433 Scanner::Location location, const char* message, bool* ok);
434
428 // Used to detect duplicates in object literals. Each of the values 435 // Used to detect duplicates in object literals. Each of the values
429 // kGetterProperty, kSetterProperty and kValueProperty represents 436 // kGetterProperty, kSetterProperty and kValueProperty represents
430 // a type of object literal property. When parsing a property, its 437 // a type of object literal property. When parsing a property, its
431 // type value is stored in the DuplicateFinder for the property name. 438 // type value is stored in the DuplicateFinder for the property name.
432 // Values are chosen so that having intersection bits means the there is 439 // Values are chosen so that having intersection bits means the there is
433 // an incompatibility. 440 // an incompatibility.
434 // I.e., you can add a getter to a property that already has a setter, since 441 // I.e., you can add a getter to a property that already has a setter, since
435 // kGetterProperty and kSetterProperty doesn't intersect, but not if it 442 // kGetterProperty and kSetterProperty doesn't intersect, but not if it
436 // already has a getter or a value. Adding the getter to an existing 443 // already has a getter or a value. Adding the getter to an existing
437 // setter will store the value (kGetterProperty | kSetterProperty), which 444 // setter will store the value (kGetterProperty | kSetterProperty), which
(...skipping 144 matching lines...) Expand 10 before | Expand all | Expand 10 after
582 } 589 }
583 590
584 static PreParserExpression ThisProperty() { 591 static PreParserExpression ThisProperty() {
585 return PreParserExpression(kThisPropertyExpression); 592 return PreParserExpression(kThisPropertyExpression);
586 } 593 }
587 594
588 static PreParserExpression Property() { 595 static PreParserExpression Property() {
589 return PreParserExpression(kPropertyExpression); 596 return PreParserExpression(kPropertyExpression);
590 } 597 }
591 598
599 static PreParserExpression Call() {
600 return PreParserExpression(kCallExpression);
601 }
602
592 bool IsIdentifier() { return (code_ & kIdentifierFlag) != 0; } 603 bool IsIdentifier() { return (code_ & kIdentifierFlag) != 0; }
593 604
594 // Only works corretly if it is actually an identifier expression.
595 PreParserIdentifier AsIdentifier() { 605 PreParserIdentifier AsIdentifier() {
606 ASSERT(IsIdentifier());
596 return PreParserIdentifier( 607 return PreParserIdentifier(
597 static_cast<PreParserIdentifier::Type>(code_ >> kIdentifierShift)); 608 static_cast<PreParserIdentifier::Type>(code_ >> kIdentifierShift));
598 } 609 }
599 610
600 bool IsStringLiteral() { return (code_ & kStringLiteralFlag) != 0; } 611 bool IsStringLiteral() { return (code_ & kStringLiteralFlag) != 0; }
601 612
602 bool IsUseStrictLiteral() { 613 bool IsUseStrictLiteral() {
603 return (code_ & kStringLiteralMask) == kUseStrictString; 614 return (code_ & kStringLiteralMask) == kUseStrictString;
604 } 615 }
605 616
606 bool IsThis() { return code_ == kThisExpression; } 617 bool IsThis() { return code_ == kThisExpression; }
607 618
608 bool IsThisProperty() { return code_ == kThisPropertyExpression; } 619 bool IsThisProperty() { return code_ == kThisPropertyExpression; }
609 620
610 bool IsProperty() { 621 bool IsProperty() {
611 return code_ == kPropertyExpression || code_ == kThisPropertyExpression; 622 return code_ == kPropertyExpression || code_ == kThisPropertyExpression;
612 } 623 }
613 624
614 bool IsValidLeftHandSide() { 625 bool IsCall() { return code_ == kCallExpression; }
626
627 bool IsValidReferenceExpression() {
615 return IsIdentifier() || IsProperty(); 628 return IsIdentifier() || IsProperty();
616 } 629 }
617 630
618 // At the moment PreParser doesn't track these expression types. 631 // At the moment PreParser doesn't track these expression types.
619 bool IsFunctionLiteral() const { return false; } 632 bool IsFunctionLiteral() const { return false; }
620 bool IsCall() const { return false; }
621 bool IsCallNew() const { return false; } 633 bool IsCallNew() const { return false; }
622 634
623 PreParserExpression AsFunctionLiteral() { return *this; } 635 PreParserExpression AsFunctionLiteral() { return *this; }
624 636
625 // Dummy implementation for making expression->somefunc() work in both Parser 637 // Dummy implementation for making expression->somefunc() work in both Parser
626 // and PreParser. 638 // and PreParser.
627 PreParserExpression* operator->() { return this; } 639 PreParserExpression* operator->() { return this; }
628 640
629 // More dummy implementations of things PreParser doesn't need to track: 641 // More dummy implementations of things PreParser doesn't need to track:
630 void set_index(int index) {} // For YieldExpressions 642 void set_index(int index) {} // For YieldExpressions
(...skipping 13 matching lines...) Expand all
644 656
645 kStringLiteralFlag = 2, // Used to detect directive prologue. 657 kStringLiteralFlag = 2, // Used to detect directive prologue.
646 kUnknownStringLiteral = kStringLiteralFlag, 658 kUnknownStringLiteral = kStringLiteralFlag,
647 kUseStrictString = kStringLiteralFlag | 8, 659 kUseStrictString = kStringLiteralFlag | 8,
648 kStringLiteralMask = kUseStrictString, 660 kStringLiteralMask = kUseStrictString,
649 661
650 // Below here applies if neither identifier nor string literal. Reserve the 662 // Below here applies if neither identifier nor string literal. Reserve the
651 // 2 least significant bits for flags. 663 // 2 least significant bits for flags.
652 kThisExpression = 1 << 2, 664 kThisExpression = 1 << 2,
653 kThisPropertyExpression = 2 << 2, 665 kThisPropertyExpression = 2 << 2,
654 kPropertyExpression = 3 << 2 666 kPropertyExpression = 3 << 2,
667 kCallExpression = 4 << 2
655 }; 668 };
656 669
657 explicit PreParserExpression(int expression_code) : code_(expression_code) {} 670 explicit PreParserExpression(int expression_code) : code_(expression_code) {}
658 671
659 int code_; 672 int code_;
660 }; 673 };
661 674
662 675
663 // PreParserExpressionList doesn't actually store the expressions because 676 // PreParserExpressionList doesn't actually store the expressions because
664 // PreParser doesn't need to. 677 // PreParser doesn't need to.
(...skipping 110 matching lines...) Expand 10 before | Expand all | Expand 10 after
775 } 788 }
776 PreParserExpression NewCountOperation(Token::Value op, 789 PreParserExpression NewCountOperation(Token::Value op,
777 bool is_prefix, 790 bool is_prefix,
778 PreParserExpression expression, 791 PreParserExpression expression,
779 int pos) { 792 int pos) {
780 return PreParserExpression::Default(); 793 return PreParserExpression::Default();
781 } 794 }
782 PreParserExpression NewCall(PreParserExpression expression, 795 PreParserExpression NewCall(PreParserExpression expression,
783 PreParserExpressionList arguments, 796 PreParserExpressionList arguments,
784 int pos) { 797 int pos) {
785 return PreParserExpression::Default(); 798 return PreParserExpression::Call();
786 } 799 }
787 PreParserExpression NewCallNew(PreParserExpression expression, 800 PreParserExpression NewCallNew(PreParserExpression expression,
788 PreParserExpressionList arguments, 801 PreParserExpressionList arguments,
789 int pos) { 802 int pos) {
790 return PreParserExpression::Default(); 803 return PreParserExpression::Default();
791 } 804 }
792 }; 805 };
793 806
794 807
795 class PreParser; 808 class PreParser;
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after
838 851
839 // Returns true if the expression is of type "this.foo". 852 // Returns true if the expression is of type "this.foo".
840 static bool IsThisProperty(PreParserExpression expression) { 853 static bool IsThisProperty(PreParserExpression expression) {
841 return expression.IsThisProperty(); 854 return expression.IsThisProperty();
842 } 855 }
843 856
844 static bool IsIdentifier(PreParserExpression expression) { 857 static bool IsIdentifier(PreParserExpression expression) {
845 return expression.IsIdentifier(); 858 return expression.IsIdentifier();
846 } 859 }
847 860
861 static PreParserIdentifier AsIdentifier(PreParserExpression expression) {
862 return expression.AsIdentifier();
863 }
864
848 static bool IsBoilerplateProperty(PreParserExpression property) { 865 static bool IsBoilerplateProperty(PreParserExpression property) {
849 // PreParser doesn't count boilerplate properties. 866 // PreParser doesn't count boilerplate properties.
850 return false; 867 return false;
851 } 868 }
852 869
853 static bool IsArrayIndex(PreParserIdentifier string, uint32_t* index) { 870 static bool IsArrayIndex(PreParserIdentifier string, uint32_t* index) {
854 return false; 871 return false;
855 } 872 }
856 873
857 // Functions for encapsulating the differences between parsing and preparsing; 874 // Functions for encapsulating the differences between parsing and preparsing;
(...skipping 18 matching lines...) Expand all
876 static void CheckPossibleEvalCall(PreParserExpression expression, 893 static void CheckPossibleEvalCall(PreParserExpression expression,
877 PreParserScope* scope) {} 894 PreParserScope* scope) {}
878 895
879 static PreParserExpression MarkExpressionAsLValue( 896 static PreParserExpression MarkExpressionAsLValue(
880 PreParserExpression expression) { 897 PreParserExpression expression) {
881 // TODO(marja): To be able to produce the same errors, the preparser needs 898 // TODO(marja): To be able to produce the same errors, the preparser needs
882 // to start tracking which expressions are variables and which are lvalues. 899 // to start tracking which expressions are variables and which are lvalues.
883 return expression; 900 return expression;
884 } 901 }
885 902
886 // Checks LHS expression for assignment and prefix/postfix increment/decrement
887 // in strict mode.
888 void CheckStrictModeLValue(PreParserExpression expression, bool* ok);
889
890 bool ShortcutNumericLiteralBinaryExpression(PreParserExpression* x, 903 bool ShortcutNumericLiteralBinaryExpression(PreParserExpression* x,
891 PreParserExpression y, 904 PreParserExpression y,
892 Token::Value op, 905 Token::Value op,
893 int pos, 906 int pos,
894 PreParserFactory* factory) { 907 PreParserFactory* factory) {
895 return false; 908 return false;
896 } 909 }
897 910
898 PreParserExpression BuildUnaryExpression(PreParserExpression expression, 911 PreParserExpression BuildUnaryExpression(PreParserExpression expression,
899 Token::Value op, int pos, 912 Token::Value op, int pos,
900 PreParserFactory* factory) { 913 PreParserFactory* factory) {
901 return PreParserExpression::Default(); 914 return PreParserExpression::Default();
902 } 915 }
903 916
917 PreParserExpression NewThrowReferenceError(const char* type, int pos) {
918 return PreParserExpression::Default();
919 }
920 PreParserExpression NewThrowSyntaxError(
921 const char* type, Handle<Object> arg, int pos) {
922 return PreParserExpression::Default();
923 }
924 PreParserExpression NewThrowTypeError(
925 const char* type, Handle<Object> arg1, Handle<Object> arg2, int pos) {
926 return PreParserExpression::Default();
927 }
928
904 // Reporting errors. 929 // Reporting errors.
905 void ReportMessageAt(Scanner::Location location, 930 void ReportMessageAt(Scanner::Location location,
906 const char* message, 931 const char* message,
907 Vector<const char*> args, 932 Vector<const char*> args,
908 bool is_reference_error = false); 933 bool is_reference_error = false);
909 void ReportMessageAt(Scanner::Location location, 934 void ReportMessageAt(Scanner::Location location,
910 const char* type, 935 const char* type,
911 const char* name_opt, 936 const char* name_opt,
912 bool is_reference_error = false); 937 bool is_reference_error = false);
913 void ReportMessageAt(int start_pos, 938 void ReportMessageAt(int start_pos,
(...skipping 774 matching lines...) Expand 10 before | Expand all | Expand 10 after
1688 if (fni_ != NULL) fni_->Enter(); 1713 if (fni_ != NULL) fni_->Enter();
1689 ExpressionT expression = 1714 ExpressionT expression =
1690 this->ParseConditionalExpression(accept_IN, CHECK_OK); 1715 this->ParseConditionalExpression(accept_IN, CHECK_OK);
1691 1716
1692 if (!Token::IsAssignmentOp(peek())) { 1717 if (!Token::IsAssignmentOp(peek())) {
1693 if (fni_ != NULL) fni_->Leave(); 1718 if (fni_ != NULL) fni_->Leave();
1694 // Parsed conditional expression only (no assignment). 1719 // Parsed conditional expression only (no assignment).
1695 return expression; 1720 return expression;
1696 } 1721 }
1697 1722
1698 if (!expression->IsValidLeftHandSide()) { 1723 expression = this->CheckAndRewriteReferenceExpression(
1699 this->ReportMessageAt(lhs_location, "invalid_lhs_in_assignment", true); 1724 expression, lhs_location, "invalid_lhs_in_assignment", CHECK_OK);
1700 *ok = false;
1701 return this->EmptyExpression();
1702 }
1703
1704 if (strict_mode() == STRICT) {
1705 // Assignment to eval or arguments is disallowed in strict mode.
1706 this->CheckStrictModeLValue(expression, CHECK_OK);
1707 }
1708 expression = this->MarkExpressionAsLValue(expression); 1725 expression = this->MarkExpressionAsLValue(expression);
1709 1726
1710 Token::Value op = Next(); // Get assignment operator. 1727 Token::Value op = Next(); // Get assignment operator.
1711 int pos = position(); 1728 int pos = position();
1712 ExpressionT right = this->ParseAssignmentExpression(accept_IN, CHECK_OK); 1729 ExpressionT right = this->ParseAssignmentExpression(accept_IN, CHECK_OK);
1713 1730
1714 // TODO(1231235): We try to estimate the set of properties set by 1731 // TODO(1231235): We try to estimate the set of properties set by
1715 // constructors. We define a new property whenever there is an 1732 // constructors. We define a new property whenever there is an
1716 // assignment to a property of 'this'. We should probably only add 1733 // assignment to a property of 'this'. We should probably only add
1717 // properties if we haven't seen them before. Otherwise we'll 1734 // properties if we haven't seen them before. Otherwise we'll
(...skipping 139 matching lines...) Expand 10 before | Expand all | Expand 10 after
1857 ReportMessage("strict_delete", Vector<const char*>::empty()); 1874 ReportMessage("strict_delete", Vector<const char*>::empty());
1858 *ok = false; 1875 *ok = false;
1859 return this->EmptyExpression(); 1876 return this->EmptyExpression();
1860 } 1877 }
1861 1878
1862 // Allow Traits do rewrite the expression. 1879 // Allow Traits do rewrite the expression.
1863 return this->BuildUnaryExpression(expression, op, pos, factory()); 1880 return this->BuildUnaryExpression(expression, op, pos, factory());
1864 } else if (Token::IsCountOp(op)) { 1881 } else if (Token::IsCountOp(op)) {
1865 op = Next(); 1882 op = Next();
1866 Scanner::Location lhs_location = scanner()->peek_location(); 1883 Scanner::Location lhs_location = scanner()->peek_location();
1867 ExpressionT expression = ParseUnaryExpression(CHECK_OK); 1884 ExpressionT expression = this->ParseUnaryExpression(CHECK_OK);
1868 if (!expression->IsValidLeftHandSide()) { 1885 expression = this->CheckAndRewriteReferenceExpression(
1869 ReportMessageAt(lhs_location, "invalid_lhs_in_prefix_op", true); 1886 expression, lhs_location, "invalid_lhs_in_prefix_op", CHECK_OK);
1870 *ok = false;
1871 return this->EmptyExpression();
1872 }
1873
1874 if (strict_mode() == STRICT) {
1875 // Prefix expression operand in strict mode may not be eval or arguments.
1876 this->CheckStrictModeLValue(expression, CHECK_OK);
1877 }
1878 this->MarkExpressionAsLValue(expression); 1887 this->MarkExpressionAsLValue(expression);
1879 1888
1880 return factory()->NewCountOperation(op, 1889 return factory()->NewCountOperation(op,
1881 true /* prefix */, 1890 true /* prefix */,
1882 expression, 1891 expression,
1883 position()); 1892 position());
1884 1893
1885 } else { 1894 } else {
1886 return this->ParsePostfixExpression(ok); 1895 return this->ParsePostfixExpression(ok);
1887 } 1896 }
1888 } 1897 }
1889 1898
1890 1899
1891 template <class Traits> 1900 template <class Traits>
1892 typename ParserBase<Traits>::ExpressionT 1901 typename ParserBase<Traits>::ExpressionT
1893 ParserBase<Traits>::ParsePostfixExpression(bool* ok) { 1902 ParserBase<Traits>::ParsePostfixExpression(bool* ok) {
1894 // PostfixExpression :: 1903 // PostfixExpression ::
1895 // LeftHandSideExpression ('++' | '--')? 1904 // LeftHandSideExpression ('++' | '--')?
1896 1905
1897 Scanner::Location lhs_location = scanner()->peek_location(); 1906 Scanner::Location lhs_location = scanner()->peek_location();
1898 ExpressionT expression = this->ParseLeftHandSideExpression(CHECK_OK); 1907 ExpressionT expression = this->ParseLeftHandSideExpression(CHECK_OK);
1899 if (!scanner()->HasAnyLineTerminatorBeforeNext() && 1908 if (!scanner()->HasAnyLineTerminatorBeforeNext() &&
1900 Token::IsCountOp(peek())) { 1909 Token::IsCountOp(peek())) {
1901 if (!expression->IsValidLeftHandSide()) { 1910 expression = this->CheckAndRewriteReferenceExpression(
1902 ReportMessageAt(lhs_location, "invalid_lhs_in_postfix_op", true); 1911 expression, lhs_location, "invalid_lhs_in_postfix_op", CHECK_OK);
1903 *ok = false;
1904 return this->EmptyExpression();
1905 }
1906
1907 if (strict_mode() == STRICT) {
1908 // Postfix expression operand in strict mode may not be eval or arguments.
1909 this->CheckStrictModeLValue(expression, CHECK_OK);
1910 }
1911 expression = this->MarkExpressionAsLValue(expression); 1912 expression = this->MarkExpressionAsLValue(expression);
1912 1913
1913 Token::Value next = Next(); 1914 Token::Value next = Next();
1914 expression = 1915 expression =
1915 factory()->NewCountOperation(next, 1916 factory()->NewCountOperation(next,
1916 false /* postfix */, 1917 false /* postfix */,
1917 expression, 1918 expression,
1918 position()); 1919 position());
1919 } 1920 }
1920 return expression; 1921 return expression;
(...skipping 189 matching lines...) Expand 10 before | Expand all | Expand 10 after
2110 } 2111 }
2111 default: 2112 default:
2112 return expression; 2113 return expression;
2113 } 2114 }
2114 } 2115 }
2115 ASSERT(false); 2116 ASSERT(false);
2116 return this->EmptyExpression(); 2117 return this->EmptyExpression();
2117 } 2118 }
2118 2119
2119 2120
2121 template <typename Traits>
2122 typename ParserBase<Traits>::ExpressionT
2123 ParserBase<Traits>::CheckAndRewriteReferenceExpression(
2124 ExpressionT expression,
2125 Scanner::Location location, const char* message, bool* ok) {
2126 if (strict_mode() == STRICT && this->IsIdentifier(expression) &&
2127 this->IsEvalOrArguments(this->AsIdentifier(expression))) {
2128 this->ReportMessageAt(location, "strict_eval_arguments", false);
2129 *ok = false;
2130 return this->EmptyExpression();
2131 } else if (expression->IsValidReferenceExpression()) {
2132 return expression;
2133 } else if (expression->IsCall()) {
2134 // If it is a call, make it a runtime error for legacy web compatibility.
2135 // Rewrite `expr' to `expr[throw ReferenceError]'.
2136 int pos = location.beg_pos;
2137 ExpressionT error = this->NewThrowReferenceError(message, pos);
2138 return factory()->NewProperty(expression, error, pos);
2139 } else {
2140 this->ReportMessageAt(location, message, true);
2141 *ok = false;
2142 return this->EmptyExpression();
2143 }
2144 }
2145
2146
2120 #undef CHECK_OK 2147 #undef CHECK_OK
2121 #undef CHECK_OK_CUSTOM 2148 #undef CHECK_OK_CUSTOM
2122 2149
2123 2150
2124 template <typename Traits> 2151 template <typename Traits>
2125 void ParserBase<Traits>::ObjectLiteralChecker::CheckProperty( 2152 void ParserBase<Traits>::ObjectLiteralChecker::CheckProperty(
2126 Token::Value property, 2153 Token::Value property,
2127 PropertyKind type, 2154 PropertyKind type,
2128 bool* ok) { 2155 bool* ok) {
2129 int old; 2156 int old;
(...skipping 20 matching lines...) Expand all
2150 "accessor_get_set"); 2177 "accessor_get_set");
2151 } 2178 }
2152 *ok = false; 2179 *ok = false;
2153 } 2180 }
2154 } 2181 }
2155 2182
2156 2183
2157 } } // v8::internal 2184 } } // v8::internal
2158 2185
2159 #endif // V8_PREPARSER_H 2186 #endif // V8_PREPARSER_H
OLDNEW
« no previous file with comments | « src/parser.cc ('k') | src/preparser.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698