Commit 98f7b7e1 authored by arv@chromium.org's avatar arv@chromium.org

Add back the duplicate property checker

We're not quite ready to make this change.

BUG=v8:3498
LOG=Y
R=rossberg@chromium.org

Review URL: https://codereview.chromium.org/491053002

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23284 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 65c9c2a2
...@@ -130,6 +130,7 @@ class ParserBase : public Traits { ...@@ -130,6 +130,7 @@ class ParserBase : public Traits {
}; };
class CheckpointBase; class CheckpointBase;
class ObjectLiteralChecker;
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// FunctionState and BlockState together implement the parser's scope stack. // FunctionState and BlockState together implement the parser's scope stack.
...@@ -473,7 +474,8 @@ class ParserBase : public Traits { ...@@ -473,7 +474,8 @@ class ParserBase : public Traits {
ExpressionT ParseExpression(bool accept_IN, bool* ok); ExpressionT ParseExpression(bool accept_IN, bool* ok);
ExpressionT ParseArrayLiteral(bool* ok); ExpressionT ParseArrayLiteral(bool* ok);
ExpressionT ParseObjectLiteral(bool* ok); ExpressionT ParseObjectLiteral(bool* ok);
ObjectLiteralPropertyT ParsePropertyDefinition(bool* ok); ObjectLiteralPropertyT ParsePropertyDefinition(ObjectLiteralChecker* checker,
bool* ok);
typename Traits::Type::ExpressionList ParseArguments(bool* ok); typename Traits::Type::ExpressionList ParseArguments(bool* ok);
ExpressionT ParseAssignmentExpression(bool accept_IN, bool* ok); ExpressionT ParseAssignmentExpression(bool accept_IN, bool* ok);
ExpressionT ParseYieldExpression(bool* ok); ExpressionT ParseYieldExpression(bool* ok);
...@@ -496,6 +498,60 @@ class ParserBase : public Traits { ...@@ -496,6 +498,60 @@ class ParserBase : public Traits {
ExpressionT expression, ExpressionT expression,
Scanner::Location location, const char* message, bool* ok); Scanner::Location location, const char* message, bool* ok);
// Used to detect duplicates in object literals. Each of the values
// kGetterProperty, kSetterProperty and kValueProperty represents
// a type of object literal property. When parsing a property, its
// type value is stored in the DuplicateFinder for the property name.
// Values are chosen so that having intersection bits means the there is
// an incompatibility.
// I.e., you can add a getter to a property that already has a setter, since
// kGetterProperty and kSetterProperty doesn't intersect, but not if it
// already has a getter or a value. Adding the getter to an existing
// setter will store the value (kGetterProperty | kSetterProperty), which
// is incompatible with adding any further properties.
enum PropertyKind {
kNone = 0,
// Bit patterns representing different object literal property types.
kGetterProperty = 1,
kSetterProperty = 2,
kValueProperty = 7,
// Helper constants.
kValueFlag = 4
};
// Validation per ECMA 262 - 11.1.5 "Object Initializer".
class ObjectLiteralChecker {
public:
ObjectLiteralChecker(ParserBase* parser, StrictMode strict_mode)
: parser_(parser),
finder_(scanner()->unicode_cache()),
strict_mode_(strict_mode) {}
void CheckProperty(Token::Value property, PropertyKind type, bool* ok);
private:
ParserBase* parser() const { return parser_; }
Scanner* scanner() const { return parser_->scanner(); }
// Checks the type of conflict based on values coming from PropertyType.
bool HasConflict(PropertyKind type1, PropertyKind type2) {
return (type1 & type2) != 0;
}
bool IsDataDataConflict(PropertyKind type1, PropertyKind type2) {
return ((type1 & type2) & kValueFlag) != 0;
}
bool IsDataAccessorConflict(PropertyKind type1, PropertyKind type2) {
return ((type1 ^ type2) & kValueFlag) != 0;
}
bool IsAccessorAccessorConflict(PropertyKind type1, PropertyKind type2) {
return ((type1 | type2) & kValueFlag) == 0;
}
ParserBase* parser_;
DuplicateFinder finder_;
StrictMode strict_mode_;
};
// If true, the next (and immediately following) function literal is // If true, the next (and immediately following) function literal is
// preceded by a parenthesis. // preceded by a parenthesis.
// Heuristically that means that the function will be called immediately, // Heuristically that means that the function will be called immediately,
...@@ -1793,8 +1849,8 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral( ...@@ -1793,8 +1849,8 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral(
template <class Traits> template <class Traits>
typename ParserBase<Traits>::ObjectLiteralPropertyT typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
ParserBase<Traits>::ParsePropertyDefinition(bool* ok) { Traits>::ParsePropertyDefinition(ObjectLiteralChecker* checker, bool* ok) {
LiteralT key = this->EmptyLiteral(); LiteralT key = this->EmptyLiteral();
Token::Value next = peek(); Token::Value next = peek();
int next_pos = peek_position(); int next_pos = peek_position();
...@@ -1846,6 +1902,10 @@ ParserBase<Traits>::ParsePropertyDefinition(bool* ok) { ...@@ -1846,6 +1902,10 @@ ParserBase<Traits>::ParsePropertyDefinition(bool* ok) {
name = ParseIdentifierName( name = ParseIdentifierName(
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
} }
// Validate the property.
PropertyKind type = is_getter ? kGetterProperty : kSetterProperty;
checker->CheckProperty(next, type,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
typename Traits::Type::FunctionLiteral value = typename Traits::Type::FunctionLiteral value =
this->ParseFunctionLiteral( this->ParseFunctionLiteral(
name, scanner()->location(), name, scanner()->location(),
...@@ -1863,6 +1923,10 @@ ParserBase<Traits>::ParsePropertyDefinition(bool* ok) { ...@@ -1863,6 +1923,10 @@ ParserBase<Traits>::ParsePropertyDefinition(bool* ok) {
} }
} }
// Validate the property
checker->CheckProperty(next, kValueProperty,
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
Expect(Token::COLON, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); Expect(Token::COLON, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
ExpressionT value = this->ParseAssignmentExpression( ExpressionT value = this->ParseAssignmentExpression(
true, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty)); true, CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
...@@ -1883,12 +1947,15 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral( ...@@ -1883,12 +1947,15 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
int number_of_boilerplate_properties = 0; int number_of_boilerplate_properties = 0;
bool has_function = false; bool has_function = false;
ObjectLiteralChecker checker(this, strict_mode());
Expect(Token::LBRACE, CHECK_OK); Expect(Token::LBRACE, CHECK_OK);
while (peek() != Token::RBRACE) { while (peek() != Token::RBRACE) {
if (fni_ != NULL) fni_->Enter(); if (fni_ != NULL) fni_->Enter();
ObjectLiteralPropertyT property = this->ParsePropertyDefinition(CHECK_OK); ObjectLiteralPropertyT property =
this->ParsePropertyDefinition(&checker, CHECK_OK);
// Mark top-level object literals that contain function literals and // Mark top-level object literals that contain function literals and
// pretenure the literal so it can be added as a constant function // pretenure the literal so it can be added as a constant function
...@@ -2572,6 +2639,32 @@ ParserBase<Traits>::CheckAndRewriteReferenceExpression( ...@@ -2572,6 +2639,32 @@ ParserBase<Traits>::CheckAndRewriteReferenceExpression(
#undef CHECK_OK_CUSTOM #undef CHECK_OK_CUSTOM
template <typename Traits>
void ParserBase<Traits>::ObjectLiteralChecker::CheckProperty(
Token::Value property, PropertyKind type, bool* ok) {
int old;
if (property == Token::NUMBER) {
old = scanner()->FindNumber(&finder_, type);
} else {
old = scanner()->FindSymbol(&finder_, type);
}
PropertyKind old_type = static_cast<PropertyKind>(old);
if (HasConflict(old_type, type)) {
if (IsDataDataConflict(old_type, type)) {
// Both are data properties.
if (strict_mode_ == SLOPPY) return;
parser()->ReportMessage("strict_duplicate_property");
} else if (IsDataAccessorConflict(old_type, type)) {
// Both a data and an accessor property with the same name.
parser()->ReportMessage("accessor_data_property");
} else {
DCHECK(IsAccessorAccessorConflict(old_type, type));
// Both accessors of the same type.
parser()->ReportMessage("accessor_get_set");
}
*ok = false;
}
}
} } // v8::internal } } // v8::internal
#endif // V8_PREPARSER_H #endif // V8_PREPARSER_H
...@@ -2486,7 +2486,7 @@ TEST(StrictObjectLiteralChecking) { ...@@ -2486,7 +2486,7 @@ TEST(StrictObjectLiteralChecking) {
{ NULL, NULL } { NULL, NULL }
}; };
// ES6 allows duplicate properties even in strict mode. // These are only errors in strict mode.
const char* statement_data[] = { const char* statement_data[] = {
"foo: 1, foo: 2", "foo: 1, foo: 2",
"\"foo\": 1, \"foo\": 2", "\"foo\": 1, \"foo\": 2",
...@@ -2499,7 +2499,7 @@ TEST(StrictObjectLiteralChecking) { ...@@ -2499,7 +2499,7 @@ TEST(StrictObjectLiteralChecking) {
}; };
RunParserSyncTest(non_strict_context_data, statement_data, kSuccess); RunParserSyncTest(non_strict_context_data, statement_data, kSuccess);
RunParserSyncTest(strict_context_data, statement_data, kSuccess); RunParserSyncTest(strict_context_data, statement_data, kError);
} }
...@@ -2511,17 +2511,23 @@ TEST(ErrorsObjectLiteralChecking) { ...@@ -2511,17 +2511,23 @@ TEST(ErrorsObjectLiteralChecking) {
}; };
const char* statement_data[] = { const char* statement_data[] = {
",", ",", "foo: 1, get foo() {}", "foo: 1, set foo(v) {}",
// Wrong number of parameters "\"foo\": 1, get \"foo\"() {}", "\"foo\": 1, set \"foo\"(v) {}",
"get bar(x) {}", "1: 1, get 1() {}", "1: 1, set 1() {}",
"get bar(x, y) {}", // It's counter-intuitive, but these collide too (even in classic
"set bar() {}", // mode). Note that we can have "foo" and foo as properties in classic
"set bar(x, y) {}", // mode,
// Parsing FunctionLiteral for getter or setter fails // but we cannot have "foo" and get foo, or foo and get "foo".
"get foo( +", "foo: 1, get \"foo\"() {}", "foo: 1, set \"foo\"(v) {}",
"get foo() \"error\"", "\"foo\": 1, get foo() {}", "\"foo\": 1, set foo(v) {}",
NULL "1: 1, get \"1\"() {}", "1: 1, set \"1\"() {}",
}; "\"1\": 1, get 1() {}"
"\"1\": 1, set 1(v) {}"
// Wrong number of parameters
"get bar(x) {}",
"get bar(x, y) {}", "set bar() {}", "set bar(x, y) {}",
// Parsing FunctionLiteral for getter or setter fails
"get foo( +", "get foo() \"error\"", NULL};
RunParserSyncTest(context_data, statement_data, kError); RunParserSyncTest(context_data, statement_data, kError);
} }
...@@ -2565,24 +2571,6 @@ TEST(NoErrorsObjectLiteralChecking) { ...@@ -2565,24 +2571,6 @@ TEST(NoErrorsObjectLiteralChecking) {
"super: 6", "super: 6",
"eval: 7", "eval: 7",
"arguments: 8", "arguments: 8",
// Duplicate property names are allowed in ES6.
"foo: 1, get foo() {}",
"foo: 1, set foo(v) {}",
"\"foo\": 1, get \"foo\"() {}",
"\"foo\": 1, set \"foo\"(v) {}",
"1: 1, get 1() {}",
"1: 1, set 1(v) {}",
// It's counter-intuitive, but these collide too (even in classic
// mode). Note that we can have "foo" and foo as properties in classic mode,
// but we cannot have "foo" and get foo, or foo and get "foo".
"foo: 1, get \"foo\"() {}",
"foo: 1, set \"foo\"(v) {}",
"\"foo\": 1, get foo() {}",
"\"foo\": 1, set foo(v) {}",
"1: 1, get \"1\"() {}",
"1: 1, set \"1\"(v) {}",
"\"1\": 1, get 1() {}",
"\"1\": 1, set 1(v) {}",
NULL NULL
}; };
......
...@@ -166,6 +166,17 @@ assertThrows('\ ...@@ -166,6 +166,17 @@ assertThrows('\
"use strict";\ "use strict";\
}', SyntaxError); }', SyntaxError);
// Duplicate data properties.
CheckStrictMode("var x = { dupe : 1, nondupe: 3, dupe : 2 };", SyntaxError);
CheckStrictMode("var x = { '1234' : 1, '2345' : 2, '1234' : 3 };", SyntaxError);
CheckStrictMode("var x = { '1234' : 1, '2345' : 2, 1234 : 3 };", SyntaxError);
CheckStrictMode("var x = { 3.14 : 1, 2.71 : 2, 3.14 : 3 };", SyntaxError);
CheckStrictMode("var x = { 3.14 : 1, '3.14' : 2 };", SyntaxError);
CheckStrictMode("var x = { \
123: 1, \
123.00000000000000000000000000000000000000000000000000000000000000000001: 2 \
}", SyntaxError);
// Non-conflicting data properties. // Non-conflicting data properties.
(function StrictModeNonDuplicate() { (function StrictModeNonDuplicate() {
"use strict"; "use strict";
...@@ -177,52 +188,37 @@ assertThrows('\ ...@@ -177,52 +188,37 @@ assertThrows('\
}; };
})(); })();
// Duplicate properties are no longer errors in ES6. // Two getters (non-strict)
(function Duplicates() { assertThrows("var x = { get foo() { }, get foo() { } };", SyntaxError);
"use strict"; assertThrows("var x = { get foo(){}, get 'foo'(){}};", SyntaxError);
assertThrows("var x = { get 12(){}, get '12'(){}};", SyntaxError);
({ dupe : 1, nondupe: 3, dupe : 2 });
({ '1234' : 1, '2345' : 2, '1234' : 3 }); // Two setters (non-strict)
({ '1234' : 1, '2345' : 2, 1234 : 3 }); assertThrows("var x = { set foo(v) { }, set foo(v) { } };", SyntaxError);
({ 3.14 : 1, 2.71 : 2, 3.14 : 3 }); assertThrows("var x = { set foo(v) { }, set 'foo'(v) { } };", SyntaxError);
({ 3.14 : 1, '3.14' : 2 }); assertThrows("var x = { set 13(v) { }, set '13'(v) { } };", SyntaxError);
({
123: 1, // Setter and data (non-strict)
123.00000000000000000000000000000000000000000000000000000000000000000001: 2 assertThrows("var x = { foo: 'data', set foo(v) { } };", SyntaxError);
}); assertThrows("var x = { set foo(v) { }, foo: 'data' };", SyntaxError);
assertThrows("var x = { foo: 'data', set 'foo'(v) { } };", SyntaxError);
// Two getters assertThrows("var x = { set foo(v) { }, 'foo': 'data' };", SyntaxError);
({ get foo() { }, get foo() { } }); assertThrows("var x = { 'foo': 'data', set foo(v) { } };", SyntaxError);
({ get foo(){}, get 'foo'(){}}); assertThrows("var x = { set 'foo'(v) { }, foo: 'data' };", SyntaxError);
({ get 12(){}, get '12'(){}}); assertThrows("var x = { 'foo': 'data', set 'foo'(v) { } };", SyntaxError);
assertThrows("var x = { set 'foo'(v) { }, 'foo': 'data' };", SyntaxError);
// Two setters assertThrows("var x = { 12: 1, set '12'(v){}};", SyntaxError);
({ set foo(v) { }, set foo(v) { } }); assertThrows("var x = { 12: 1, set 12(v){}};", SyntaxError);
({ set foo(v) { }, set 'foo'(v) { } }); assertThrows("var x = { '12': 1, set '12'(v){}};", SyntaxError);
({ set 13(v) { }, set '13'(v) { } }); assertThrows("var x = { '12': 1, set 12(v){}};", SyntaxError);
// Setter and data // Getter and data (non-strict)
({ foo: 'data', set foo(v) { } }); assertThrows("var x = { foo: 'data', get foo() { } };", SyntaxError);
({ set foo(v) { }, foo: 'data' }); assertThrows("var x = { get foo() { }, foo: 'data' };", SyntaxError);
({ foo: 'data', set 'foo'(v) { } }); assertThrows("var x = { 'foo': 'data', get foo() { } };", SyntaxError);
({ set foo(v) { }, 'foo': 'data' }); assertThrows("var x = { get 'foo'() { }, 'foo': 'data' };", SyntaxError);
({ 'foo': 'data', set foo(v) { } }); assertThrows("var x = { '12': 1, get '12'(){}};", SyntaxError);
({ set 'foo'(v) { }, foo: 'data' }); assertThrows("var x = { '12': 1, get 12(){}};", SyntaxError);
({ 'foo': 'data', set 'foo'(v) { } });
({ set 'foo'(v) { }, 'foo': 'data' });
({ 12: 1, set '12'(v){}});
({ 12: 1, set 12(v){}});
({ '12': 1, set '12'(v){}});
({ '12': 1, set 12(v){}});
// Getter and data
({ foo: 'data', get foo() { } });
({ get foo() { }, foo: 'data' });
({ 'foo': 'data', get foo() { } });
({ get 'foo'() { }, 'foo': 'data' });
({ '12': 1, get '12'(){}});
({ '12': 1, get 12(){}});
})();
// Assignment to eval or arguments // Assignment to eval or arguments
CheckStrictMode("function strict() { eval = undefined; }", SyntaxError); CheckStrictMode("function strict() { eval = undefined; }", SyntaxError);
......
...@@ -77,17 +77,6 @@ ...@@ -77,17 +77,6 @@
'S8.5_A2.1': [PASS, FAIL_OK], 'S8.5_A2.1': [PASS, FAIL_OK],
'S8.5_A2.2': [PASS, FAIL_OK], 'S8.5_A2.2': [PASS, FAIL_OK],
# ES6 allows duplicate properties
'11.1.5-4-4-a-1-s': [FAIL],
'11.1.5_4-4-b-1': [FAIL],
'11.1.5_4-4-b-2': [FAIL],
'11.1.5_4-4-c-1': [FAIL],
'11.1.5_4-4-c-2': [FAIL],
'11.1.5_4-4-d-1': [FAIL],
'11.1.5_4-4-d-2': [FAIL],
'11.1.5_4-4-d-3': [FAIL],
'11.1.5_4-4-d-4': [FAIL],
############################ INVALID TESTS ############################# ############################ INVALID TESTS #############################
# The reference value calculated by Test262 is incorrect if you run these # The reference value calculated by Test262 is incorrect if you run these
......
...@@ -26,20 +26,20 @@ Make sure that we correctly identify parse errors in object literals ...@@ -26,20 +26,20 @@ Make sure that we correctly identify parse errors in object literals
On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE". On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
PASS ({a:1, get a(){}}), true is true PASS ({a:1, get a(){}}) threw exception SyntaxError: Object literal may not have data and accessor property with the same name.
PASS ({a:1, set a(v){}}), true is true PASS ({a:1, set a(v){}}) threw exception SyntaxError: Object literal may not have data and accessor property with the same name.
PASS ({get a(){}, a:1}), true is true PASS ({get a(){}, a:1}) threw exception SyntaxError: Object literal may not have data and accessor property with the same name.
PASS ({set a(v){}, a:1}), true is true PASS ({set a(v){}, a:1}) threw exception SyntaxError: Object literal may not have data and accessor property with the same name.
PASS ({get a(){}, get a(){}}), true is true PASS ({get a(){}, get a(){}}) threw exception SyntaxError: Object literal may not have multiple get/set accessors with the same name.
PASS ({set a(v){}, set a(v){}}), true is true PASS ({set a(v){}, set a(v){}}) threw exception SyntaxError: Object literal may not have multiple get/set accessors with the same name.
PASS ({set a(v){}, get a(){}, set a(v){}}), true is true PASS ({set a(v){}, get a(){}, set a(v){}}) threw exception SyntaxError: Object literal may not have multiple get/set accessors with the same name.
PASS (function(){({a:1, get a(){}})}), true is true PASS (function(){({a:1, get a(){}})}) threw exception SyntaxError: Object literal may not have data and accessor property with the same name.
PASS (function(){({a:1, set a(v){}})}), true is true PASS (function(){({a:1, set a(v){}})}) threw exception SyntaxError: Object literal may not have data and accessor property with the same name.
PASS (function(){({get a(){}, a:1})}), true is true PASS (function(){({get a(){}, a:1})}) threw exception SyntaxError: Object literal may not have data and accessor property with the same name.
PASS (function(){({set a(v){}, a:1})}), true is true PASS (function(){({set a(v){}, a:1})}) threw exception SyntaxError: Object literal may not have data and accessor property with the same name.
PASS (function(){({get a(){}, get a(){}})}), true is true PASS (function(){({get a(){}, get a(){}})}) threw exception SyntaxError: Object literal may not have multiple get/set accessors with the same name.
PASS (function(){({set a(v){}, set a(v){}})}), true is true PASS (function(){({set a(v){}, set a(v){}})}) threw exception SyntaxError: Object literal may not have multiple get/set accessors with the same name.
PASS (function(){({set a(v){}, get a(){}, set a(v){}})}), true is true PASS (function(){({set a(v){}, get a(){}, set a(v){}})}) threw exception SyntaxError: Object literal may not have multiple get/set accessors with the same name.
PASS ({a:1, a:1, a:1}), true is true PASS ({a:1, a:1, a:1}), true is true
PASS ({get a(){}, set a(v){}}), true is true PASS ({get a(){}, set a(v){}}), true is true
PASS ({set a(v){}, get a(){}}), true is true PASS ({set a(v){}, get a(){}}), true is true
...@@ -48,6 +48,5 @@ PASS (function(){({get a(){}, set a(v){}})}), true is true ...@@ -48,6 +48,5 @@ PASS (function(){({get a(){}, set a(v){}})}), true is true
PASS (function(){({set a(v){}, get a(){}})}), true is true PASS (function(){({set a(v){}, get a(){}})}), true is true
PASS successfullyParsed is true PASS successfullyParsed is true
TEST COMPLETE TEST COMPLETE
...@@ -23,20 +23,20 @@ ...@@ -23,20 +23,20 @@
description("Make sure that we correctly identify parse errors in object literals"); description("Make sure that we correctly identify parse errors in object literals");
shouldBeTrue("({a:1, get a(){}}), true"); shouldThrow("({a:1, get a(){}})");
shouldBeTrue("({a:1, set a(v){}}), true"); shouldThrow("({a:1, set a(v){}})");
shouldBeTrue("({get a(){}, a:1}), true"); shouldThrow("({get a(){}, a:1})");
shouldBeTrue("({set a(v){}, a:1}), true"); shouldThrow("({set a(v){}, a:1})");
shouldBeTrue("({get a(){}, get a(){}}), true"); shouldThrow("({get a(){}, get a(){}})");
shouldBeTrue("({set a(v){}, set a(v){}}), true"); shouldThrow("({set a(v){}, set a(v){}})");
shouldBeTrue("({set a(v){}, get a(){}, set a(v){}}), true"); shouldThrow("({set a(v){}, get a(){}, set a(v){}})");
shouldBeTrue("(function(){({a:1, get a(){}})}), true"); shouldThrow("(function(){({a:1, get a(){}})})");
shouldBeTrue("(function(){({a:1, set a(v){}})}), true"); shouldThrow("(function(){({a:1, set a(v){}})})");
shouldBeTrue("(function(){({get a(){}, a:1})}), true"); shouldThrow("(function(){({get a(){}, a:1})})");
shouldBeTrue("(function(){({set a(v){}, a:1})}), true"); shouldThrow("(function(){({set a(v){}, a:1})})");
shouldBeTrue("(function(){({get a(){}, get a(){}})}), true"); shouldThrow("(function(){({get a(){}, get a(){}})})");
shouldBeTrue("(function(){({set a(v){}, set a(v){}})}), true"); shouldThrow("(function(){({set a(v){}, set a(v){}})})");
shouldBeTrue("(function(){({set a(v){}, get a(){}, set a(v){}})}), true"); shouldThrow("(function(){({set a(v){}, get a(){}, set a(v){}})})");
shouldBeTrue("({a:1, a:1, a:1}), true"); shouldBeTrue("({a:1, a:1, a:1}), true");
shouldBeTrue("({get a(){}, set a(v){}}), true"); shouldBeTrue("({get a(){}, set a(v){}}), true");
shouldBeTrue("({set a(v){}, get a(){}}), true"); shouldBeTrue("({set a(v){}, get a(){}}), true");
......
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