ES6 computed property names
This adds support for computed property names, under the flag
--harmony-computed-property-names, for both object literals and
classes.
This is a revert of the revert, a76419f0f4
.
This changes to do an early bailout in
HOptimizedGraphBuilder::VisitObjectLiteral instead of doing that in the later
loop.
BUG=v8:3754
LOG=Y
TBR=dslomov@chromium.org
Review URL: https://codereview.chromium.org/792233008
Cr-Commit-Position: refs/heads/master@{#25868}
This commit is contained in:
parent
44e2dd535e
commit
cc568d1b7a
@ -1686,11 +1686,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
expr->CalculateEmitStore(zone());
|
||||
|
||||
AccessorTable accessor_table(zone());
|
||||
for (int i = 0; i < expr->properties()->length(); i++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(i);
|
||||
int property_index = 0;
|
||||
for (; property_index < expr->properties()->length(); property_index++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(property_index);
|
||||
if (property->is_computed_name()) break;
|
||||
if (property->IsCompileTimeValue()) continue;
|
||||
|
||||
Literal* key = property->key();
|
||||
Literal* key = property->key()->AsLiteral();
|
||||
Expression* value = property->value();
|
||||
if (!result_saved) {
|
||||
__ push(r0); // Save result on stack
|
||||
@ -1778,6 +1780,67 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
|
||||
}
|
||||
|
||||
// Object literals have two parts. The "static" part on the left contains no
|
||||
// computed property names, and so we can compute its map ahead of time; see
|
||||
// runtime.cc::CreateObjectLiteralBoilerplate. The second "dynamic" part
|
||||
// starts with the first computed property name, and continues with all
|
||||
// properties to its right. All the code from above initializes the static
|
||||
// component of the object literal, and arranges for the map of the result to
|
||||
// reflect the static order in which the keys appear. For the dynamic
|
||||
// properties, we compile them into a series of "SetOwnProperty" runtime
|
||||
// calls. This will preserve insertion order.
|
||||
for (; property_index < expr->properties()->length(); property_index++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(property_index);
|
||||
|
||||
Expression* value = property->value();
|
||||
if (!result_saved) {
|
||||
__ push(r0); // Save result on the stack
|
||||
result_saved = true;
|
||||
}
|
||||
|
||||
__ ldr(r0, MemOperand(sp)); // Duplicate receiver.
|
||||
__ push(r0);
|
||||
|
||||
if (property->kind() == ObjectLiteral::Property::PROTOTYPE) {
|
||||
DCHECK(!property->is_computed_name());
|
||||
VisitForStackValue(value);
|
||||
if (property->emit_store()) {
|
||||
__ CallRuntime(Runtime::kInternalSetPrototype, 2);
|
||||
} else {
|
||||
__ Drop(2);
|
||||
}
|
||||
} else {
|
||||
EmitPropertyKey(property);
|
||||
VisitForStackValue(value);
|
||||
|
||||
switch (property->kind()) {
|
||||
case ObjectLiteral::Property::CONSTANT:
|
||||
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
|
||||
case ObjectLiteral::Property::COMPUTED:
|
||||
if (property->emit_store()) {
|
||||
__ mov(r0, Operand(Smi::FromInt(NONE)));
|
||||
__ push(r0);
|
||||
__ CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
|
||||
} else {
|
||||
__ Drop(3);
|
||||
}
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::PROTOTYPE:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::GETTER:
|
||||
__ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::SETTER:
|
||||
__ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (expr->has_function()) {
|
||||
DCHECK(result_saved);
|
||||
__ ldr(r0, MemOperand(sp));
|
||||
@ -2478,9 +2541,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
|
||||
for (int i = 0; i < lit->properties()->length(); i++) {
|
||||
ObjectLiteral::Property* property = lit->properties()->at(i);
|
||||
Literal* key = property->key()->AsLiteral();
|
||||
Expression* value = property->value();
|
||||
DCHECK(key != NULL);
|
||||
|
||||
if (property->is_static()) {
|
||||
__ ldr(scratch, MemOperand(sp, kPointerSize)); // constructor
|
||||
@ -2488,7 +2549,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
__ ldr(scratch, MemOperand(sp, 0)); // prototype
|
||||
}
|
||||
__ push(scratch);
|
||||
VisitForStackValue(key);
|
||||
EmitPropertyKey(property);
|
||||
VisitForStackValue(value);
|
||||
EmitSetHomeObjectIfNeeded(value, 2);
|
||||
|
||||
@ -2501,11 +2562,11 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::GETTER:
|
||||
__ CallRuntime(Runtime::kDefineClassGetter, 3);
|
||||
__ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::SETTER:
|
||||
__ CallRuntime(Runtime::kDefineClassSetter, 3);
|
||||
__ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1667,11 +1667,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
expr->CalculateEmitStore(zone());
|
||||
|
||||
AccessorTable accessor_table(zone());
|
||||
for (int i = 0; i < expr->properties()->length(); i++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(i);
|
||||
int property_index = 0;
|
||||
for (; property_index < expr->properties()->length(); property_index++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(property_index);
|
||||
if (property->is_computed_name()) break;
|
||||
if (property->IsCompileTimeValue()) continue;
|
||||
|
||||
Literal* key = property->key();
|
||||
Literal* key = property->key()->AsLiteral();
|
||||
Expression* value = property->value();
|
||||
if (!result_saved) {
|
||||
__ Push(x0); // Save result on stack
|
||||
@ -1759,6 +1761,67 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
|
||||
}
|
||||
|
||||
// Object literals have two parts. The "static" part on the left contains no
|
||||
// computed property names, and so we can compute its map ahead of time; see
|
||||
// runtime.cc::CreateObjectLiteralBoilerplate. The second "dynamic" part
|
||||
// starts with the first computed property name, and continues with all
|
||||
// properties to its right. All the code from above initializes the static
|
||||
// component of the object literal, and arranges for the map of the result to
|
||||
// reflect the static order in which the keys appear. For the dynamic
|
||||
// properties, we compile them into a series of "SetOwnProperty" runtime
|
||||
// calls. This will preserve insertion order.
|
||||
for (; property_index < expr->properties()->length(); property_index++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(property_index);
|
||||
|
||||
Expression* value = property->value();
|
||||
if (!result_saved) {
|
||||
__ Push(x0); // Save result on stack
|
||||
result_saved = true;
|
||||
}
|
||||
|
||||
__ Peek(x10, 0); // Duplicate receiver.
|
||||
__ Push(x10);
|
||||
|
||||
if (property->kind() == ObjectLiteral::Property::PROTOTYPE) {
|
||||
DCHECK(!property->is_computed_name());
|
||||
VisitForStackValue(value);
|
||||
if (property->emit_store()) {
|
||||
__ CallRuntime(Runtime::kInternalSetPrototype, 2);
|
||||
} else {
|
||||
__ Drop(2);
|
||||
}
|
||||
} else {
|
||||
EmitPropertyKey(property);
|
||||
VisitForStackValue(value);
|
||||
|
||||
switch (property->kind()) {
|
||||
case ObjectLiteral::Property::CONSTANT:
|
||||
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
|
||||
case ObjectLiteral::Property::COMPUTED:
|
||||
if (property->emit_store()) {
|
||||
__ Mov(x0, Smi::FromInt(NONE));
|
||||
__ Push(x0);
|
||||
__ CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
|
||||
} else {
|
||||
__ Drop(3);
|
||||
}
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::PROTOTYPE:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::GETTER:
|
||||
__ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::SETTER:
|
||||
__ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (expr->has_function()) {
|
||||
DCHECK(result_saved);
|
||||
__ Peek(x0, 0);
|
||||
@ -2175,9 +2238,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
|
||||
for (int i = 0; i < lit->properties()->length(); i++) {
|
||||
ObjectLiteral::Property* property = lit->properties()->at(i);
|
||||
Literal* key = property->key()->AsLiteral();
|
||||
Expression* value = property->value();
|
||||
DCHECK(key != NULL);
|
||||
|
||||
if (property->is_static()) {
|
||||
__ Peek(scratch, kPointerSize); // constructor
|
||||
@ -2185,7 +2246,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
__ Peek(scratch, 0); // prototype
|
||||
}
|
||||
__ Push(scratch);
|
||||
VisitForStackValue(key);
|
||||
EmitPropertyKey(property);
|
||||
VisitForStackValue(value);
|
||||
EmitSetHomeObjectIfNeeded(value, 2);
|
||||
|
||||
@ -2198,11 +2259,11 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::GETTER:
|
||||
__ CallRuntime(Runtime::kDefineClassGetter, 3);
|
||||
__ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::SETTER:
|
||||
__ CallRuntime(Runtime::kDefineClassSetter, 3);
|
||||
__ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -476,6 +476,7 @@ void AstNumberingVisitor::VisitObjectLiteral(ObjectLiteral* node) {
|
||||
|
||||
void AstNumberingVisitor::VisitObjectLiteralProperty(
|
||||
ObjectLiteralProperty* node) {
|
||||
if (node->is_computed_name()) DisableTurbofan(kComputedPropertyName);
|
||||
Visit(node->key());
|
||||
Visit(node->value());
|
||||
}
|
||||
|
50
src/ast.cc
50
src/ast.cc
@ -185,13 +185,17 @@ void FunctionLiteral::InitializeSharedInfo(
|
||||
|
||||
ObjectLiteralProperty::ObjectLiteralProperty(Zone* zone,
|
||||
AstValueFactory* ast_value_factory,
|
||||
Literal* key, Expression* value,
|
||||
bool is_static) {
|
||||
emit_store_ = true;
|
||||
key_ = key;
|
||||
value_ = value;
|
||||
is_static_ = is_static;
|
||||
if (key->raw_value()->EqualsString(ast_value_factory->proto_string())) {
|
||||
Expression* key, Expression* value,
|
||||
bool is_static,
|
||||
bool is_computed_name)
|
||||
: key_(key),
|
||||
value_(value),
|
||||
emit_store_(true),
|
||||
is_static_(is_static),
|
||||
is_computed_name_(is_computed_name) {
|
||||
if (!is_computed_name &&
|
||||
key->AsLiteral()->raw_value()->EqualsString(
|
||||
ast_value_factory->proto_string())) {
|
||||
kind_ = PROTOTYPE;
|
||||
} else if (value_->AsMaterializedLiteral() != NULL) {
|
||||
kind_ = MATERIALIZED_LITERAL;
|
||||
@ -204,13 +208,16 @@ ObjectLiteralProperty::ObjectLiteralProperty(Zone* zone,
|
||||
|
||||
|
||||
ObjectLiteralProperty::ObjectLiteralProperty(Zone* zone, bool is_getter,
|
||||
Expression* key,
|
||||
FunctionLiteral* value,
|
||||
bool is_static) {
|
||||
emit_store_ = true;
|
||||
value_ = value;
|
||||
kind_ = is_getter ? GETTER : SETTER;
|
||||
is_static_ = is_static;
|
||||
}
|
||||
bool is_static,
|
||||
bool is_computed_name)
|
||||
: key_(key),
|
||||
value_(value),
|
||||
kind_(is_getter ? GETTER : SETTER),
|
||||
emit_store_(true),
|
||||
is_static_(is_static),
|
||||
is_computed_name_(is_computed_name) {}
|
||||
|
||||
|
||||
bool ObjectLiteral::Property::IsCompileTimeValue() {
|
||||
@ -237,10 +244,11 @@ void ObjectLiteral::CalculateEmitStore(Zone* zone) {
|
||||
allocator);
|
||||
for (int i = properties()->length() - 1; i >= 0; i--) {
|
||||
ObjectLiteral::Property* property = properties()->at(i);
|
||||
Literal* literal = property->key();
|
||||
if (property->is_computed_name()) continue;
|
||||
Literal* literal = property->key()->AsLiteral();
|
||||
if (literal->value()->IsNull()) continue;
|
||||
uint32_t hash = literal->Hash();
|
||||
// If the key of a computed property is in the table, do not emit
|
||||
// If the key of a computed property value is in the table, do not emit
|
||||
// a store for the property later.
|
||||
if ((property->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL ||
|
||||
property->kind() == ObjectLiteral::Property::COMPUTED) &&
|
||||
@ -279,6 +287,13 @@ void ObjectLiteral::BuildConstantProperties(Isolate* isolate) {
|
||||
is_simple = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (position == boilerplate_properties_ * 2) {
|
||||
DCHECK(property->is_computed_name());
|
||||
break;
|
||||
}
|
||||
DCHECK(!property->is_computed_name());
|
||||
|
||||
MaterializedLiteral* m_literal = property->value()->AsMaterializedLiteral();
|
||||
if (m_literal != NULL) {
|
||||
m_literal->BuildConstants(isolate);
|
||||
@ -288,7 +303,7 @@ void ObjectLiteral::BuildConstantProperties(Isolate* isolate) {
|
||||
// Add CONSTANT and COMPUTED properties to boilerplate. Use undefined
|
||||
// value for COMPUTED properties, the real value is filled in at
|
||||
// runtime. The enumeration order is maintained.
|
||||
Handle<Object> key = property->key()->value();
|
||||
Handle<Object> key = property->key()->AsLiteral()->value();
|
||||
Handle<Object> value = GetBoilerplateValue(property->value(), isolate);
|
||||
|
||||
// Ensure objects that may, at any point in time, contain fields with double
|
||||
@ -640,7 +655,8 @@ void CallNew::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
|
||||
|
||||
|
||||
void ObjectLiteral::Property::RecordTypeFeedback(TypeFeedbackOracle* oracle) {
|
||||
TypeFeedbackId id = key()->LiteralFeedbackId();
|
||||
DCHECK(!is_computed_name());
|
||||
TypeFeedbackId id = key()->AsLiteral()->LiteralFeedbackId();
|
||||
SmallMapList maps;
|
||||
oracle->CollectReceiverTypes(id, &maps);
|
||||
receiver_type_ = maps.length() == 1 ? maps.at(0)
|
||||
|
38
src/ast.h
38
src/ast.h
@ -1467,10 +1467,7 @@ class ObjectLiteralProperty FINAL : public ZoneObject {
|
||||
PROTOTYPE // Property is __proto__.
|
||||
};
|
||||
|
||||
ObjectLiteralProperty(Zone* zone, AstValueFactory* ast_value_factory,
|
||||
Literal* key, Expression* value, bool is_static);
|
||||
|
||||
Literal* key() { return key_; }
|
||||
Expression* key() { return key_; }
|
||||
Expression* value() { return value_; }
|
||||
Kind kind() { return kind_; }
|
||||
|
||||
@ -1485,20 +1482,26 @@ class ObjectLiteralProperty FINAL : public ZoneObject {
|
||||
bool emit_store();
|
||||
|
||||
bool is_static() const { return is_static_; }
|
||||
bool is_computed_name() const { return is_computed_name_; }
|
||||
|
||||
protected:
|
||||
friend class AstNodeFactory;
|
||||
|
||||
ObjectLiteralProperty(Zone* zone, bool is_getter, FunctionLiteral* value,
|
||||
bool is_static);
|
||||
void set_key(Literal* key) { key_ = key; }
|
||||
ObjectLiteralProperty(Zone* zone, AstValueFactory* ast_value_factory,
|
||||
Expression* key, Expression* value, bool is_static,
|
||||
bool is_computed_name);
|
||||
|
||||
ObjectLiteralProperty(Zone* zone, bool is_getter, Expression* key,
|
||||
FunctionLiteral* value, bool is_static,
|
||||
bool is_computed_name);
|
||||
|
||||
private:
|
||||
Literal* key_;
|
||||
Expression* key_;
|
||||
Expression* value_;
|
||||
Kind kind_;
|
||||
bool emit_store_;
|
||||
bool is_static_;
|
||||
bool is_computed_name_;
|
||||
Handle<Map> receiver_type_;
|
||||
};
|
||||
|
||||
@ -3377,20 +3380,21 @@ class AstNodeFactory FINAL BASE_EMBEDDED {
|
||||
boilerplate_properties, has_function, pos);
|
||||
}
|
||||
|
||||
ObjectLiteral::Property* NewObjectLiteralProperty(Literal* key,
|
||||
ObjectLiteral::Property* NewObjectLiteralProperty(Expression* key,
|
||||
Expression* value,
|
||||
bool is_static) {
|
||||
return new (zone_) ObjectLiteral::Property(zone_, ast_value_factory_, key,
|
||||
value, is_static);
|
||||
bool is_static,
|
||||
bool is_computed_name) {
|
||||
return new (zone_) ObjectLiteral::Property(
|
||||
zone_, ast_value_factory_, key, value, is_static, is_computed_name);
|
||||
}
|
||||
|
||||
ObjectLiteral::Property* NewObjectLiteralProperty(bool is_getter,
|
||||
Expression* key,
|
||||
FunctionLiteral* value,
|
||||
int pos, bool is_static) {
|
||||
ObjectLiteral::Property* prop =
|
||||
new (zone_) ObjectLiteral::Property(zone_, is_getter, value, is_static);
|
||||
prop->set_key(NewStringLiteral(value->raw_name(), pos));
|
||||
return prop;
|
||||
int pos, bool is_static,
|
||||
bool is_computed_name) {
|
||||
return new (zone_) ObjectLiteral::Property(zone_, is_getter, key, value,
|
||||
is_static, is_computed_name);
|
||||
}
|
||||
|
||||
RegExpLiteral* NewRegExpLiteral(const AstRawString* pattern,
|
||||
|
@ -47,6 +47,7 @@ namespace internal {
|
||||
V(kCodeGenerationFailed, "Code generation failed") \
|
||||
V(kCodeObjectNotProperlyPatched, "Code object not properly patched") \
|
||||
V(kCompoundAssignmentToLookupSlot, "Compound assignment to lookup slot") \
|
||||
V(kComputedPropertyName, "Computed property name") \
|
||||
V(kContextAllocatedArguments, "Context-allocated arguments") \
|
||||
V(kCopyBuffersOverlap, "Copy buffers overlap") \
|
||||
V(kCouldNotGenerateZero, "Could not generate +0.0") \
|
||||
|
@ -1596,6 +1596,7 @@ EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_tostring)
|
||||
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_templates)
|
||||
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_sloppy)
|
||||
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_unicode)
|
||||
EMPTY_NATIVE_FUNCTIONS_FOR_FEATURE(harmony_computed_property_names)
|
||||
|
||||
|
||||
void Genesis::InstallNativeFunctions_harmony_proxies() {
|
||||
@ -1626,6 +1627,7 @@ EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_proxies)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_templates)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_sloppy)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_unicode)
|
||||
EMPTY_INITIALIZE_GLOBAL_FOR_FEATURE(harmony_computed_property_names)
|
||||
|
||||
void Genesis::InitializeGlobal_harmony_regexps() {
|
||||
Handle<JSObject> builtins(native_context()->builtins());
|
||||
@ -2180,6 +2182,7 @@ bool Genesis::InstallExperimentalNatives() {
|
||||
"native harmony-templates.js", NULL};
|
||||
static const char* harmony_sloppy_natives[] = {NULL};
|
||||
static const char* harmony_unicode_natives[] = {NULL};
|
||||
static const char* harmony_computed_property_names_natives[] = {NULL};
|
||||
|
||||
for (int i = ExperimentalNatives::GetDebuggerCount();
|
||||
i < ExperimentalNatives::GetBuiltinsCount(); i++) {
|
||||
|
@ -165,35 +165,36 @@ enum BuiltinExtraArguments {
|
||||
DEBUG_BREAK)
|
||||
|
||||
// Define list of builtins implemented in JavaScript.
|
||||
#define BUILTINS_LIST_JS(V) \
|
||||
V(EQUALS, 1) \
|
||||
V(STRICT_EQUALS, 1) \
|
||||
V(COMPARE, 2) \
|
||||
V(ADD, 1) \
|
||||
V(SUB, 1) \
|
||||
V(MUL, 1) \
|
||||
V(DIV, 1) \
|
||||
V(MOD, 1) \
|
||||
V(BIT_OR, 1) \
|
||||
V(BIT_AND, 1) \
|
||||
V(BIT_XOR, 1) \
|
||||
V(SHL, 1) \
|
||||
V(SAR, 1) \
|
||||
V(SHR, 1) \
|
||||
V(DELETE, 2) \
|
||||
V(IN, 1) \
|
||||
V(INSTANCE_OF, 1) \
|
||||
V(FILTER_KEY, 1) \
|
||||
V(CALL_NON_FUNCTION, 0) \
|
||||
V(CALL_NON_FUNCTION_AS_CONSTRUCTOR, 0) \
|
||||
#define BUILTINS_LIST_JS(V) \
|
||||
V(EQUALS, 1) \
|
||||
V(STRICT_EQUALS, 1) \
|
||||
V(COMPARE, 2) \
|
||||
V(ADD, 1) \
|
||||
V(SUB, 1) \
|
||||
V(MUL, 1) \
|
||||
V(DIV, 1) \
|
||||
V(MOD, 1) \
|
||||
V(BIT_OR, 1) \
|
||||
V(BIT_AND, 1) \
|
||||
V(BIT_XOR, 1) \
|
||||
V(SHL, 1) \
|
||||
V(SAR, 1) \
|
||||
V(SHR, 1) \
|
||||
V(DELETE, 2) \
|
||||
V(IN, 1) \
|
||||
V(INSTANCE_OF, 1) \
|
||||
V(FILTER_KEY, 1) \
|
||||
V(CALL_NON_FUNCTION, 0) \
|
||||
V(CALL_NON_FUNCTION_AS_CONSTRUCTOR, 0) \
|
||||
V(CALL_FUNCTION_PROXY, 1) \
|
||||
V(CALL_FUNCTION_PROXY_AS_CONSTRUCTOR, 1) \
|
||||
V(TO_OBJECT, 0) \
|
||||
V(TO_NUMBER, 0) \
|
||||
V(TO_STRING, 0) \
|
||||
V(STRING_ADD_LEFT, 1) \
|
||||
V(STRING_ADD_RIGHT, 1) \
|
||||
V(APPLY_PREPARE, 1) \
|
||||
V(TO_OBJECT, 0) \
|
||||
V(TO_NUMBER, 0) \
|
||||
V(TO_STRING, 0) \
|
||||
V(TO_NAME, 0) \
|
||||
V(STRING_ADD_LEFT, 1) \
|
||||
V(STRING_ADD_RIGHT, 1) \
|
||||
V(APPLY_PREPARE, 1) \
|
||||
V(STACK_OVERFLOW, 1)
|
||||
|
||||
class BuiltinFunctionTable;
|
||||
|
@ -910,7 +910,7 @@ void AstGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(i);
|
||||
if (property->IsCompileTimeValue()) continue;
|
||||
|
||||
Literal* key = property->key();
|
||||
Literal* key = property->key()->AsLiteral();
|
||||
switch (property->kind()) {
|
||||
case ObjectLiteral::Property::CONSTANT:
|
||||
UNREACHABLE();
|
||||
|
@ -172,15 +172,16 @@ DEFINE_IMPLICATION(harmony, es_staging)
|
||||
DEFINE_IMPLICATION(es_staging, harmony)
|
||||
|
||||
// Features that are still work in progress (behind individual flags).
|
||||
#define HARMONY_INPROGRESS(V) \
|
||||
V(harmony_modules, "harmony modules (implies block scoping)") \
|
||||
V(harmony_arrays, "harmony array methods") \
|
||||
V(harmony_array_includes, "harmony Array.prototype.includes") \
|
||||
V(harmony_regexps, "harmony regular expression extensions") \
|
||||
V(harmony_arrow_functions, "harmony arrow functions") \
|
||||
V(harmony_proxies, "harmony proxies") \
|
||||
V(harmony_sloppy, "harmony features in sloppy mode") \
|
||||
V(harmony_unicode, "harmony unicode escapes")
|
||||
#define HARMONY_INPROGRESS(V) \
|
||||
V(harmony_modules, "harmony modules (implies block scoping)") \
|
||||
V(harmony_arrays, "harmony array methods") \
|
||||
V(harmony_array_includes, "harmony Array.prototype.includes") \
|
||||
V(harmony_regexps, "harmony regular expression extensions") \
|
||||
V(harmony_arrow_functions, "harmony arrow functions") \
|
||||
V(harmony_proxies, "harmony proxies") \
|
||||
V(harmony_sloppy, "harmony features in sloppy mode") \
|
||||
V(harmony_unicode, "harmony unicode escapes") \
|
||||
V(harmony_computed_property_names, "harmony computed property names")
|
||||
|
||||
// Features that are complete (but still behind --harmony/es-staging flag).
|
||||
#define HARMONY_STAGED(V) \
|
||||
|
@ -1211,6 +1211,13 @@ void FullCodeGenerator::EmitUnwindBeforeReturn() {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitPropertyKey(ObjectLiteralProperty* property) {
|
||||
VisitForStackValue(property->key());
|
||||
__ InvokeBuiltin(Builtins::TO_NAME, CALL_FUNCTION);
|
||||
__ Push(result_register());
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitReturnStatement(ReturnStatement* stmt) {
|
||||
Comment cmnt(masm_, "[ ReturnStatement");
|
||||
SetStatementPosition(stmt);
|
||||
|
@ -568,6 +568,9 @@ class FullCodeGenerator: public AstVisitor {
|
||||
// in the accumulator after installing all the properties.
|
||||
void EmitClassDefineProperties(ClassLiteral* lit);
|
||||
|
||||
// Pushes the property key as a Name on the stack.
|
||||
void EmitPropertyKey(ObjectLiteralProperty* property);
|
||||
|
||||
// Apply the compound assignment operator. Expects the left operand on top
|
||||
// of the stack and the right one in the accumulator.
|
||||
void EmitBinaryOp(BinaryOperation* expr,
|
||||
|
@ -5552,6 +5552,13 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
DCHECK(!HasStackOverflow());
|
||||
DCHECK(current_block() != NULL);
|
||||
DCHECK(current_block()->HasPredecessor());
|
||||
|
||||
for (int i = 0; i < expr->properties()->length(); i++) {
|
||||
if (expr->properties()->at(i)->is_computed_name()) {
|
||||
return Bailout(kComputedPropertyName);
|
||||
}
|
||||
}
|
||||
|
||||
expr->BuildConstantProperties(isolate());
|
||||
Handle<JSFunction> closure = function_state()->compilation_info()->closure();
|
||||
HInstruction* literal;
|
||||
@ -5607,9 +5614,10 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
|
||||
for (int i = 0; i < expr->properties()->length(); i++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(i);
|
||||
DCHECK(!property->is_computed_name());
|
||||
if (property->IsCompileTimeValue()) continue;
|
||||
|
||||
Literal* key = property->key();
|
||||
Literal* key = property->key()->AsLiteral();
|
||||
Expression* value = property->value();
|
||||
|
||||
switch (property->kind()) {
|
||||
@ -5635,7 +5643,7 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
}
|
||||
|
||||
Handle<Map> map = property->GetReceiverType();
|
||||
Handle<String> name = property->key()->AsPropertyName();
|
||||
Handle<String> name = key->AsPropertyName();
|
||||
HInstruction* store;
|
||||
if (map.is_null()) {
|
||||
// If we don't know the monomorphic type, do a generic store.
|
||||
|
@ -1617,11 +1617,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
expr->CalculateEmitStore(zone());
|
||||
|
||||
AccessorTable accessor_table(zone());
|
||||
for (int i = 0; i < expr->properties()->length(); i++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(i);
|
||||
int property_index = 0;
|
||||
for (; property_index < expr->properties()->length(); property_index++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(property_index);
|
||||
if (property->is_computed_name()) break;
|
||||
if (property->IsCompileTimeValue()) continue;
|
||||
|
||||
Literal* key = property->key();
|
||||
Literal* key = property->key()->AsLiteral();
|
||||
Expression* value = property->value();
|
||||
if (!result_saved) {
|
||||
__ push(eax); // Save result on the stack
|
||||
@ -1701,6 +1703,65 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
|
||||
}
|
||||
|
||||
// Object literals have two parts. The "static" part on the left contains no
|
||||
// computed property names, and so we can compute its map ahead of time; see
|
||||
// runtime.cc::CreateObjectLiteralBoilerplate. The second "dynamic" part
|
||||
// starts with the first computed property name, and continues with all
|
||||
// properties to its right. All the code from above initializes the static
|
||||
// component of the object literal, and arranges for the map of the result to
|
||||
// reflect the static order in which the keys appear. For the dynamic
|
||||
// properties, we compile them into a series of "SetOwnProperty" runtime
|
||||
// calls. This will preserve insertion order.
|
||||
for (; property_index < expr->properties()->length(); property_index++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(property_index);
|
||||
|
||||
Expression* value = property->value();
|
||||
if (!result_saved) {
|
||||
__ push(eax); // Save result on the stack
|
||||
result_saved = true;
|
||||
}
|
||||
|
||||
__ push(Operand(esp, 0)); // Duplicate receiver.
|
||||
|
||||
if (property->kind() == ObjectLiteral::Property::PROTOTYPE) {
|
||||
DCHECK(!property->is_computed_name());
|
||||
VisitForStackValue(value);
|
||||
if (property->emit_store()) {
|
||||
__ CallRuntime(Runtime::kInternalSetPrototype, 2);
|
||||
} else {
|
||||
__ Drop(2);
|
||||
}
|
||||
} else {
|
||||
EmitPropertyKey(property);
|
||||
VisitForStackValue(value);
|
||||
|
||||
switch (property->kind()) {
|
||||
case ObjectLiteral::Property::CONSTANT:
|
||||
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
|
||||
case ObjectLiteral::Property::COMPUTED:
|
||||
if (property->emit_store()) {
|
||||
__ push(Immediate(Smi::FromInt(NONE)));
|
||||
__ CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
|
||||
} else {
|
||||
__ Drop(3);
|
||||
}
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::PROTOTYPE:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::GETTER:
|
||||
__ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::SETTER:
|
||||
__ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (expr->has_function()) {
|
||||
DCHECK(result_saved);
|
||||
__ push(Operand(esp, 0));
|
||||
@ -2394,16 +2455,14 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
|
||||
for (int i = 0; i < lit->properties()->length(); i++) {
|
||||
ObjectLiteral::Property* property = lit->properties()->at(i);
|
||||
Literal* key = property->key()->AsLiteral();
|
||||
Expression* value = property->value();
|
||||
DCHECK(key != NULL);
|
||||
|
||||
if (property->is_static()) {
|
||||
__ push(Operand(esp, kPointerSize)); // constructor
|
||||
} else {
|
||||
__ push(Operand(esp, 0)); // prototype
|
||||
}
|
||||
VisitForStackValue(key);
|
||||
EmitPropertyKey(property);
|
||||
VisitForStackValue(value);
|
||||
EmitSetHomeObjectIfNeeded(value, 2);
|
||||
|
||||
@ -2416,11 +2475,11 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::GETTER:
|
||||
__ CallRuntime(Runtime::kDefineClassGetter, 3);
|
||||
__ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::SETTER:
|
||||
__ CallRuntime(Runtime::kDefineClassSetter, 3);
|
||||
__ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -407,9 +407,8 @@ bool ParserTraits::IsConstructor(const AstRawString* identifier) const {
|
||||
bool ParserTraits::IsThisProperty(Expression* expression) {
|
||||
DCHECK(expression != NULL);
|
||||
Property* property = expression->AsProperty();
|
||||
return property != NULL &&
|
||||
property->obj()->AsVariableProxy() != NULL &&
|
||||
property->obj()->AsVariableProxy()->is_this();
|
||||
return property != NULL && property->obj()->IsVariableProxy() &&
|
||||
property->obj()->AsVariableProxy()->is_this();
|
||||
}
|
||||
|
||||
|
||||
@ -433,8 +432,7 @@ void ParserTraits::PushPropertyName(FuncNameInferrer* fni,
|
||||
void ParserTraits::CheckAssigningFunctionLiteralToProperty(Expression* left,
|
||||
Expression* right) {
|
||||
DCHECK(left != NULL);
|
||||
if (left->AsProperty() != NULL &&
|
||||
right->AsFunctionLiteral() != NULL) {
|
||||
if (left->IsProperty() && right->IsFunctionLiteral()) {
|
||||
right->AsFunctionLiteral()->set_pretenure();
|
||||
}
|
||||
}
|
||||
@ -806,6 +804,8 @@ Parser::Parser(CompilationInfo* info, ParseInfo* parse_info)
|
||||
set_allow_harmony_templates(FLAG_harmony_templates);
|
||||
set_allow_harmony_sloppy(FLAG_harmony_sloppy);
|
||||
set_allow_harmony_unicode(FLAG_harmony_unicode);
|
||||
set_allow_harmony_computed_property_names(
|
||||
FLAG_harmony_computed_property_names);
|
||||
for (int feature = 0; feature < v8::Isolate::kUseCounterFeatureCount;
|
||||
++feature) {
|
||||
use_counts_[feature] = 0;
|
||||
@ -3976,6 +3976,8 @@ PreParser::PreParseResult Parser::ParseLazyFunctionBodyWithPreParser(
|
||||
reusable_preparser_->set_allow_harmony_templates(allow_harmony_templates());
|
||||
reusable_preparser_->set_allow_harmony_sloppy(allow_harmony_sloppy());
|
||||
reusable_preparser_->set_allow_harmony_unicode(allow_harmony_unicode());
|
||||
reusable_preparser_->set_allow_harmony_computed_property_names(
|
||||
allow_harmony_computed_property_names());
|
||||
}
|
||||
PreParser::PreParseResult result =
|
||||
reusable_preparser_->PreParseLazyFunction(strict_mode(),
|
||||
@ -4035,8 +4037,11 @@ ClassLiteral* Parser::ParseClassLiteral(const AstRawString* name,
|
||||
if (fni_ != NULL) fni_->Enter();
|
||||
const bool in_class = true;
|
||||
const bool is_static = false;
|
||||
ObjectLiteral::Property* property = ParsePropertyDefinition(
|
||||
NULL, in_class, is_static, &has_seen_constructor, CHECK_OK);
|
||||
bool is_computed_name = false; // Classes do not care about computed
|
||||
// property names here.
|
||||
ObjectLiteral::Property* property =
|
||||
ParsePropertyDefinition(NULL, in_class, is_static, &is_computed_name,
|
||||
&has_seen_constructor, CHECK_OK);
|
||||
|
||||
if (has_seen_constructor && constructor == NULL) {
|
||||
constructor = GetPropertyValue(property);
|
||||
|
@ -412,11 +412,6 @@ class ParserTraits {
|
||||
return string->AsArrayIndex(index);
|
||||
}
|
||||
|
||||
bool IsConstructorProperty(ObjectLiteral::Property* property) {
|
||||
return property->key()->raw_value()->EqualsString(
|
||||
ast_value_factory()->constructor_string());
|
||||
}
|
||||
|
||||
static Expression* GetPropertyValue(ObjectLiteral::Property* property) {
|
||||
return property->value();
|
||||
}
|
||||
@ -426,7 +421,9 @@ class ParserTraits {
|
||||
static void PushLiteralName(FuncNameInferrer* fni, const AstRawString* id) {
|
||||
fni->PushLiteralName(id);
|
||||
}
|
||||
|
||||
void PushPropertyName(FuncNameInferrer* fni, Expression* expression);
|
||||
|
||||
static void InferFunctionName(FuncNameInferrer* fni,
|
||||
FunctionLiteral* func_to_infer) {
|
||||
fni->AddFunction(func_to_infer);
|
||||
|
@ -990,8 +990,10 @@ PreParserExpression PreParser::ParseClassLiteral(
|
||||
if (Check(Token::SEMICOLON)) continue;
|
||||
const bool in_class = true;
|
||||
const bool is_static = false;
|
||||
ParsePropertyDefinition(NULL, in_class, is_static, &has_seen_constructor,
|
||||
CHECK_OK);
|
||||
bool is_computed_name = false; // Classes do not care about computed
|
||||
// property names here.
|
||||
ParsePropertyDefinition(NULL, in_class, is_static, &is_computed_name,
|
||||
&has_seen_constructor, CHECK_OK);
|
||||
}
|
||||
|
||||
Expect(Token::RBRACE, CHECK_OK);
|
||||
|
131
src/preparser.h
131
src/preparser.h
@ -87,6 +87,7 @@ class ParserBase : public Traits {
|
||||
allow_harmony_arrow_functions_(false),
|
||||
allow_harmony_object_literals_(false),
|
||||
allow_harmony_sloppy_(false),
|
||||
allow_harmony_computed_property_names_(false),
|
||||
zone_(zone) {}
|
||||
|
||||
// Getters that indicate whether certain syntactical constructs are
|
||||
@ -108,6 +109,9 @@ class ParserBase : public Traits {
|
||||
bool allow_harmony_templates() const { return scanner()->HarmonyTemplates(); }
|
||||
bool allow_harmony_sloppy() const { return allow_harmony_sloppy_; }
|
||||
bool allow_harmony_unicode() const { return scanner()->HarmonyUnicode(); }
|
||||
bool allow_harmony_computed_property_names() const {
|
||||
return allow_harmony_computed_property_names_;
|
||||
}
|
||||
|
||||
// Setters that determine whether certain syntactical constructs are
|
||||
// allowed to be parsed by this instance of the parser.
|
||||
@ -140,6 +144,9 @@ class ParserBase : public Traits {
|
||||
void set_allow_harmony_unicode(bool allow) {
|
||||
scanner()->SetHarmonyUnicode(allow);
|
||||
}
|
||||
void set_allow_harmony_computed_property_names(bool allow) {
|
||||
allow_harmony_computed_property_names_ = allow;
|
||||
}
|
||||
|
||||
protected:
|
||||
enum AllowEvalOrArgumentsAsIdentifier {
|
||||
@ -490,11 +497,13 @@ class ParserBase : public Traits {
|
||||
ExpressionT ParsePrimaryExpression(bool* ok);
|
||||
ExpressionT ParseExpression(bool accept_IN, bool* ok);
|
||||
ExpressionT ParseArrayLiteral(bool* ok);
|
||||
IdentifierT ParsePropertyName(bool* is_get, bool* is_set, bool* is_static,
|
||||
ExpressionT ParsePropertyName(IdentifierT* name, bool* is_get, bool* is_set,
|
||||
bool* is_static, bool* is_computed_name,
|
||||
bool* ok);
|
||||
ExpressionT ParseObjectLiteral(bool* ok);
|
||||
ObjectLiteralPropertyT ParsePropertyDefinition(ObjectLiteralChecker* checker,
|
||||
bool in_class, bool is_static,
|
||||
bool* is_computed_name,
|
||||
bool* has_seen_constructor,
|
||||
bool* ok);
|
||||
typename Traits::Type::ExpressionList ParseArguments(bool* ok);
|
||||
@ -598,6 +607,7 @@ class ParserBase : public Traits {
|
||||
bool allow_harmony_arrow_functions_;
|
||||
bool allow_harmony_object_literals_;
|
||||
bool allow_harmony_sloppy_;
|
||||
bool allow_harmony_computed_property_names_;
|
||||
|
||||
typename Traits::Type::Zone* zone_; // Only used by Parser.
|
||||
};
|
||||
@ -1031,13 +1041,16 @@ class PreParserFactory {
|
||||
return PreParserExpression::Default();
|
||||
}
|
||||
PreParserExpression NewObjectLiteralProperty(bool is_getter,
|
||||
PreParserExpression key,
|
||||
PreParserExpression value,
|
||||
int pos, bool is_static) {
|
||||
int pos, bool is_static,
|
||||
bool is_computed_name) {
|
||||
return PreParserExpression::Default();
|
||||
}
|
||||
PreParserExpression NewObjectLiteralProperty(PreParserExpression key,
|
||||
PreParserExpression value,
|
||||
bool is_static) {
|
||||
bool is_static,
|
||||
bool is_computed_name) {
|
||||
return PreParserExpression::Default();
|
||||
}
|
||||
PreParserExpression NewObjectLiteral(PreParserExpressionList properties,
|
||||
@ -1223,11 +1236,13 @@ class PreParserTraits {
|
||||
// PreParser should not use FuncNameInferrer.
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
static void PushPropertyName(FuncNameInferrer* fni,
|
||||
PreParserExpression expression) {
|
||||
// PreParser should not use FuncNameInferrer.
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
static void InferFunctionName(FuncNameInferrer* fni,
|
||||
PreParserExpression expression) {
|
||||
// PreParser should not use FuncNameInferrer.
|
||||
@ -1987,24 +2002,55 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseArrayLiteral(
|
||||
|
||||
|
||||
template <class Traits>
|
||||
typename ParserBase<Traits>::IdentifierT ParserBase<Traits>::ParsePropertyName(
|
||||
bool* is_get, bool* is_set, bool* is_static, bool* ok) {
|
||||
Token::Value next = peek();
|
||||
switch (next) {
|
||||
typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParsePropertyName(
|
||||
IdentifierT* name, bool* is_get, bool* is_set, bool* is_static,
|
||||
bool* is_computed_name, bool* ok) {
|
||||
Token::Value token = peek();
|
||||
int pos = peek_position();
|
||||
|
||||
// For non computed property names we normalize the name a bit:
|
||||
//
|
||||
// "12" -> 12
|
||||
// 12.3 -> "12.3"
|
||||
// 12.30 -> "12.3"
|
||||
// identifier -> "identifier"
|
||||
//
|
||||
// This is important because we use the property name as a key in a hash
|
||||
// table when we compute constant properties.
|
||||
switch (token) {
|
||||
case Token::STRING:
|
||||
Consume(Token::STRING);
|
||||
return this->GetSymbol(scanner_);
|
||||
*name = this->GetSymbol(scanner());
|
||||
break;
|
||||
|
||||
case Token::NUMBER:
|
||||
Consume(Token::NUMBER);
|
||||
return this->GetNumberAsSymbol(scanner_);
|
||||
*name = this->GetNumberAsSymbol(scanner());
|
||||
break;
|
||||
|
||||
case Token::LBRACK:
|
||||
if (allow_harmony_computed_property_names_) {
|
||||
*is_computed_name = true;
|
||||
Consume(Token::LBRACK);
|
||||
ExpressionT expression = ParseAssignmentExpression(true, CHECK_OK);
|
||||
Expect(Token::RBRACK, CHECK_OK);
|
||||
return expression;
|
||||
}
|
||||
|
||||
// Fall through.
|
||||
case Token::STATIC:
|
||||
*is_static = true;
|
||||
// Fall through.
|
||||
|
||||
// Fall through.
|
||||
default:
|
||||
return ParseIdentifierNameOrGetOrSet(is_get, is_set, ok);
|
||||
*name = ParseIdentifierNameOrGetOrSet(is_get, is_set, CHECK_OK);
|
||||
break;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return this->EmptyIdentifier();
|
||||
|
||||
uint32_t index;
|
||||
return this->IsArrayIndex(*name, &index)
|
||||
? factory()->NewNumberLiteral(index, pos)
|
||||
: factory()->NewStringLiteral(*name, pos);
|
||||
}
|
||||
|
||||
|
||||
@ -2012,9 +2058,11 @@ template <class Traits>
|
||||
typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
|
||||
Traits>::ParsePropertyDefinition(ObjectLiteralChecker* checker,
|
||||
bool in_class, bool is_static,
|
||||
bool* is_computed_name,
|
||||
bool* has_seen_constructor, bool* ok) {
|
||||
DCHECK(!in_class || is_static || has_seen_constructor != NULL);
|
||||
ExpressionT value = this->EmptyExpression();
|
||||
IdentifierT name = this->EmptyIdentifier();
|
||||
bool is_get = false;
|
||||
bool is_set = false;
|
||||
bool name_is_static = false;
|
||||
@ -2022,15 +2070,17 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
|
||||
|
||||
Token::Value name_token = peek();
|
||||
int next_pos = peek_position();
|
||||
IdentifierT name =
|
||||
ParsePropertyName(&is_get, &is_set, &name_is_static,
|
||||
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
||||
ExpressionT name_expression = ParsePropertyName(
|
||||
&name, &is_get, &is_set, &name_is_static, is_computed_name,
|
||||
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
||||
|
||||
if (fni_ != NULL) this->PushLiteralName(fni_, name);
|
||||
if (fni_ != NULL && !*is_computed_name) {
|
||||
this->PushLiteralName(fni_, name);
|
||||
}
|
||||
|
||||
if (!in_class && !is_generator && peek() == Token::COLON) {
|
||||
// PropertyDefinition : PropertyName ':' AssignmentExpression
|
||||
if (checker != NULL) {
|
||||
if (!*is_computed_name && checker != NULL) {
|
||||
checker->CheckProperty(name_token, kValueProperty,
|
||||
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
||||
}
|
||||
@ -2068,7 +2118,7 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
|
||||
kind = FunctionKind::kNormalFunction;
|
||||
}
|
||||
|
||||
if (checker != NULL) {
|
||||
if (!*is_computed_name && checker != NULL) {
|
||||
checker->CheckProperty(name_token, kValueProperty,
|
||||
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
||||
}
|
||||
@ -2082,14 +2132,18 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
|
||||
|
||||
} else if (in_class && name_is_static && !is_static) {
|
||||
// static MethodDefinition
|
||||
return ParsePropertyDefinition(checker, true, true, NULL, ok);
|
||||
return ParsePropertyDefinition(checker, true, true, is_computed_name, NULL,
|
||||
ok);
|
||||
|
||||
} else if (is_get || is_set) {
|
||||
// Accessor
|
||||
name = this->EmptyIdentifier();
|
||||
bool dont_care = false;
|
||||
name_token = peek();
|
||||
name = ParsePropertyName(&dont_care, &dont_care, &dont_care,
|
||||
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
||||
|
||||
name_expression = ParsePropertyName(
|
||||
&name, &dont_care, &dont_care, &dont_care, is_computed_name,
|
||||
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
||||
|
||||
// Validate the property.
|
||||
if (is_static && this->IsPrototype(name)) {
|
||||
@ -2101,7 +2155,7 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
|
||||
*ok = false;
|
||||
return this->EmptyObjectLiteralProperty();
|
||||
}
|
||||
if (checker != NULL) {
|
||||
if (!*is_computed_name && checker != NULL) {
|
||||
checker->CheckProperty(name_token,
|
||||
is_get ? kGetterProperty : kSetterProperty,
|
||||
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
||||
@ -2114,8 +2168,17 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
|
||||
FunctionLiteral::ANONYMOUS_EXPRESSION,
|
||||
is_get ? FunctionLiteral::GETTER_ARITY : FunctionLiteral::SETTER_ARITY,
|
||||
CHECK_OK_CUSTOM(EmptyObjectLiteralProperty));
|
||||
return factory()->NewObjectLiteralProperty(is_get, value, next_pos,
|
||||
is_static);
|
||||
|
||||
// Make sure the name expression is a string since we need a Name for
|
||||
// Runtime_DefineAccessorPropertyUnchecked and since we can determine this
|
||||
// statically we can skip the extra runtime check.
|
||||
if (!*is_computed_name) {
|
||||
name_expression =
|
||||
factory()->NewStringLiteral(name, name_expression->position());
|
||||
}
|
||||
|
||||
return factory()->NewObjectLiteralProperty(
|
||||
is_get, name_expression, value, next_pos, is_static, *is_computed_name);
|
||||
|
||||
} else if (!in_class && allow_harmony_object_literals_ &&
|
||||
Token::IsIdentifier(name_token, strict_mode(),
|
||||
@ -2129,12 +2192,8 @@ typename ParserBase<Traits>::ObjectLiteralPropertyT ParserBase<
|
||||
return this->EmptyObjectLiteralProperty();
|
||||
}
|
||||
|
||||
uint32_t index;
|
||||
LiteralT key = this->IsArrayIndex(name, &index)
|
||||
? factory()->NewNumberLiteral(index, next_pos)
|
||||
: factory()->NewStringLiteral(name, next_pos);
|
||||
|
||||
return factory()->NewObjectLiteralProperty(key, value, is_static);
|
||||
return factory()->NewObjectLiteralProperty(name_expression, value, is_static,
|
||||
*is_computed_name);
|
||||
}
|
||||
|
||||
|
||||
@ -2149,6 +2208,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
|
||||
this->NewPropertyList(4, zone_);
|
||||
int number_of_boilerplate_properties = 0;
|
||||
bool has_function = false;
|
||||
bool has_computed_names = false;
|
||||
|
||||
ObjectLiteralChecker checker(this, strict_mode());
|
||||
|
||||
@ -2159,8 +2219,13 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
|
||||
|
||||
const bool in_class = false;
|
||||
const bool is_static = false;
|
||||
bool is_computed_name = false;
|
||||
ObjectLiteralPropertyT property = this->ParsePropertyDefinition(
|
||||
&checker, in_class, is_static, NULL, CHECK_OK);
|
||||
&checker, in_class, is_static, &is_computed_name, NULL, CHECK_OK);
|
||||
|
||||
if (is_computed_name) {
|
||||
has_computed_names = true;
|
||||
}
|
||||
|
||||
// Mark top-level object literals that contain function literals and
|
||||
// pretenure the literal so it can be added as a constant function
|
||||
@ -2169,7 +2234,7 @@ typename ParserBase<Traits>::ExpressionT ParserBase<Traits>::ParseObjectLiteral(
|
||||
&has_function);
|
||||
|
||||
// Count CONSTANT or COMPUTED properties to maintain the enumeration order.
|
||||
if (this->IsBoilerplateProperty(property)) {
|
||||
if (!has_computed_names && this->IsBoilerplateProperty(property)) {
|
||||
number_of_boilerplate_properties++;
|
||||
}
|
||||
properties->Add(property, zone());
|
||||
|
@ -468,6 +468,12 @@ function TO_STRING() {
|
||||
}
|
||||
|
||||
|
||||
// Convert the receiver to a string or symbol - forward to ToName.
|
||||
function TO_NAME() {
|
||||
return %ToName(this);
|
||||
}
|
||||
|
||||
|
||||
/* -------------------------------------
|
||||
- - - C o n v e r s i o n s - - -
|
||||
-------------------------------------
|
||||
|
@ -153,18 +153,10 @@ RUNTIME_FUNCTION(Runtime_DefineClassMethod) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 2);
|
||||
|
||||
uint32_t index;
|
||||
if (key->ToArrayIndex(&index)) {
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, JSObject::SetOwnElement(object, index, function, STRICT));
|
||||
}
|
||||
|
||||
Handle<Name> name;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name,
|
||||
Runtime::ToName(isolate, key));
|
||||
if (name->AsArrayIndex(&index)) {
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, JSObject::SetOwnElement(object, index, function, STRICT));
|
||||
@ -177,42 +169,6 @@ RUNTIME_FUNCTION(Runtime_DefineClassMethod) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DefineClassGetter) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, getter, 2);
|
||||
|
||||
Handle<Name> name;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name,
|
||||
Runtime::ToName(isolate, key));
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSObject::DefineAccessor(object, name, getter,
|
||||
isolate->factory()->null_value(), NONE));
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DefineClassSetter) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, setter, 2);
|
||||
|
||||
Handle<Name> name;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name,
|
||||
Runtime::ToName(isolate, key));
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSObject::DefineAccessor(object, name, isolate->factory()->null_value(),
|
||||
setter, NONE));
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_ClassGetSourceCode) {
|
||||
HandleScope shs(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
|
@ -1606,5 +1606,35 @@ RUNTIME_FUNCTION(RuntimeReference_ClassOf) {
|
||||
if (!obj->IsJSReceiver()) return isolate->heap()->null_value();
|
||||
return JSReceiver::cast(obj)->class_name();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DefineGetterPropertyUnchecked) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, getter, 2);
|
||||
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSObject::DefineAccessor(object, name, getter,
|
||||
isolate->factory()->null_value(), NONE));
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_DefineSetterPropertyUnchecked) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 3);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSObject, object, 0);
|
||||
CONVERT_ARG_HANDLE_CHECKED(Name, name, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, setter, 2);
|
||||
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSObject::DefineAccessor(object, name, isolate->factory()->null_value(),
|
||||
setter, NONE));
|
||||
return isolate->heap()->undefined_value();
|
||||
}
|
||||
}
|
||||
} // namespace v8::internal
|
||||
|
@ -187,8 +187,6 @@ namespace internal {
|
||||
F(HomeObjectSymbol, 0, 1) \
|
||||
F(DefineClass, 6, 1) \
|
||||
F(DefineClassMethod, 3, 1) \
|
||||
F(DefineClassGetter, 3, 1) \
|
||||
F(DefineClassSetter, 3, 1) \
|
||||
F(ClassGetSourceCode, 1, 1) \
|
||||
F(ThrowNonMethodError, 0, 1) \
|
||||
F(ThrowUnsupportedSuperError, 0, 1) \
|
||||
@ -262,6 +260,8 @@ namespace internal {
|
||||
F(DefineDataPropertyUnchecked, 4, 1) \
|
||||
F(DefineAccessorPropertyUnchecked, 5, 1) \
|
||||
F(GetDataProperty, 2, 1) \
|
||||
F(DefineGetterPropertyUnchecked, 3, 1) \
|
||||
F(DefineSetterPropertyUnchecked, 3, 1) \
|
||||
\
|
||||
/* Arrays */ \
|
||||
F(RemoveArrayHoles, 2, 1) \
|
||||
|
@ -408,7 +408,9 @@ void AstTyper::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
if ((prop->kind() == ObjectLiteral::Property::MATERIALIZED_LITERAL &&
|
||||
!CompileTimeValue::IsCompileTimeValue(prop->value())) ||
|
||||
prop->kind() == ObjectLiteral::Property::COMPUTED) {
|
||||
if (prop->key()->value()->IsInternalizedString() && prop->emit_store()) {
|
||||
if (!prop->is_computed_name() &&
|
||||
prop->key()->AsLiteral()->value()->IsInternalizedString() &&
|
||||
prop->emit_store()) {
|
||||
prop->RecordTypeFeedback(oracle());
|
||||
}
|
||||
}
|
||||
|
@ -1651,11 +1651,13 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
expr->CalculateEmitStore(zone());
|
||||
|
||||
AccessorTable accessor_table(zone());
|
||||
for (int i = 0; i < expr->properties()->length(); i++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(i);
|
||||
int property_index = 0;
|
||||
for (; property_index < expr->properties()->length(); property_index++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(property_index);
|
||||
if (property->is_computed_name()) break;
|
||||
if (property->IsCompileTimeValue()) continue;
|
||||
|
||||
Literal* key = property->key();
|
||||
Literal* key = property->key()->AsLiteral();
|
||||
Expression* value = property->value();
|
||||
if (!result_saved) {
|
||||
__ Push(rax); // Save result on the stack
|
||||
@ -1735,6 +1737,65 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
|
||||
}
|
||||
|
||||
// Object literals have two parts. The "static" part on the left contains no
|
||||
// computed property names, and so we can compute its map ahead of time; see
|
||||
// runtime.cc::CreateObjectLiteralBoilerplate. The second "dynamic" part
|
||||
// starts with the first computed property name, and continues with all
|
||||
// properties to its right. All the code from above initializes the static
|
||||
// component of the object literal, and arranges for the map of the result to
|
||||
// reflect the static order in which the keys appear. For the dynamic
|
||||
// properties, we compile them into a series of "SetOwnProperty" runtime
|
||||
// calls. This will preserve insertion order.
|
||||
for (; property_index < expr->properties()->length(); property_index++) {
|
||||
ObjectLiteral::Property* property = expr->properties()->at(property_index);
|
||||
|
||||
Expression* value = property->value();
|
||||
if (!result_saved) {
|
||||
__ Push(rax); // Save result on the stack
|
||||
result_saved = true;
|
||||
}
|
||||
|
||||
__ Push(Operand(rsp, 0)); // Duplicate receiver.
|
||||
|
||||
if (property->kind() == ObjectLiteral::Property::PROTOTYPE) {
|
||||
DCHECK(!property->is_computed_name());
|
||||
VisitForStackValue(value);
|
||||
if (property->emit_store()) {
|
||||
__ CallRuntime(Runtime::kInternalSetPrototype, 2);
|
||||
} else {
|
||||
__ Drop(2);
|
||||
}
|
||||
} else {
|
||||
EmitPropertyKey(property);
|
||||
VisitForStackValue(value);
|
||||
|
||||
switch (property->kind()) {
|
||||
case ObjectLiteral::Property::CONSTANT:
|
||||
case ObjectLiteral::Property::MATERIALIZED_LITERAL:
|
||||
case ObjectLiteral::Property::COMPUTED:
|
||||
if (property->emit_store()) {
|
||||
__ Push(Smi::FromInt(NONE));
|
||||
__ CallRuntime(Runtime::kDefineDataPropertyUnchecked, 4);
|
||||
} else {
|
||||
__ Drop(3);
|
||||
}
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::PROTOTYPE:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::GETTER:
|
||||
__ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::SETTER:
|
||||
__ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (expr->has_function()) {
|
||||
DCHECK(result_saved);
|
||||
__ Push(Operand(rsp, 0));
|
||||
@ -2393,16 +2454,14 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
|
||||
for (int i = 0; i < lit->properties()->length(); i++) {
|
||||
ObjectLiteral::Property* property = lit->properties()->at(i);
|
||||
Literal* key = property->key()->AsLiteral();
|
||||
Expression* value = property->value();
|
||||
DCHECK(key != NULL);
|
||||
|
||||
if (property->is_static()) {
|
||||
__ Push(Operand(rsp, kPointerSize)); // constructor
|
||||
} else {
|
||||
__ Push(Operand(rsp, 0)); // prototype
|
||||
}
|
||||
VisitForStackValue(key);
|
||||
EmitPropertyKey(property);
|
||||
VisitForStackValue(value);
|
||||
EmitSetHomeObjectIfNeeded(value, 2);
|
||||
|
||||
@ -2415,11 +2474,11 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::GETTER:
|
||||
__ CallRuntime(Runtime::kDefineClassGetter, 3);
|
||||
__ CallRuntime(Runtime::kDefineGetterPropertyUnchecked, 3);
|
||||
break;
|
||||
|
||||
case ObjectLiteral::Property::SETTER:
|
||||
__ CallRuntime(Runtime::kDefineClassSetter, 3);
|
||||
__ CallRuntime(Runtime::kDefineSetterPropertyUnchecked, 3);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1358,7 +1358,8 @@ enum ParserFlag {
|
||||
kAllowHarmonyObjectLiterals,
|
||||
kAllowHarmonyTemplates,
|
||||
kAllowHarmonySloppy,
|
||||
kAllowHarmonyUnicode
|
||||
kAllowHarmonyUnicode,
|
||||
kAllowHarmonyComputedPropertyNames
|
||||
};
|
||||
|
||||
|
||||
@ -1385,6 +1386,8 @@ void SetParserFlags(i::ParserBase<Traits>* parser,
|
||||
parser->set_allow_harmony_templates(flags.Contains(kAllowHarmonyTemplates));
|
||||
parser->set_allow_harmony_sloppy(flags.Contains(kAllowHarmonySloppy));
|
||||
parser->set_allow_harmony_unicode(flags.Contains(kAllowHarmonyUnicode));
|
||||
parser->set_allow_harmony_computed_property_names(
|
||||
flags.Contains(kAllowHarmonyComputedPropertyNames));
|
||||
}
|
||||
|
||||
|
||||
@ -4582,3 +4585,61 @@ TEST(LexicalScopingSloppyMode) {
|
||||
always_true_flags, arraysize(always_true_flags),
|
||||
always_false_flags, arraysize(always_false_flags));
|
||||
}
|
||||
|
||||
|
||||
TEST(ComputedPropertyName) {
|
||||
const char* context_data[][2] = {{"({[", "]: 1});"},
|
||||
{"({get [", "]() {}});"},
|
||||
{"({set [", "](_) {}});"},
|
||||
{"({[", "]() {}});"},
|
||||
{"({*[", "]() {}});"},
|
||||
{"(class {get [", "]() {}});"},
|
||||
{"(class {set [", "](_) {}});"},
|
||||
{"(class {[", "]() {}});"},
|
||||
{"(class {*[", "]() {}});"},
|
||||
{NULL, NULL}};
|
||||
const char* error_data[] = {
|
||||
"1, 2",
|
||||
"var name",
|
||||
NULL};
|
||||
|
||||
static const ParserFlag always_flags[] = {
|
||||
kAllowHarmonyClasses,
|
||||
kAllowHarmonyComputedPropertyNames,
|
||||
kAllowHarmonyObjectLiterals,
|
||||
kAllowHarmonySloppy,
|
||||
};
|
||||
RunParserSyncTest(context_data, error_data, kError, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
|
||||
const char* name_data[] = {
|
||||
"1",
|
||||
"1 + 2",
|
||||
"'name'",
|
||||
"\"name\"",
|
||||
"[]",
|
||||
"{}",
|
||||
NULL};
|
||||
|
||||
RunParserSyncTest(context_data, name_data, kSuccess, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
}
|
||||
|
||||
|
||||
TEST(ComputedPropertyNameShorthandError) {
|
||||
const char* context_data[][2] = {{"({", "});"},
|
||||
{NULL, NULL}};
|
||||
const char* error_data[] = {
|
||||
"a: 1, [2]",
|
||||
"[1], a: 1",
|
||||
NULL};
|
||||
|
||||
static const ParserFlag always_flags[] = {
|
||||
kAllowHarmonyClasses,
|
||||
kAllowHarmonyComputedPropertyNames,
|
||||
kAllowHarmonyObjectLiterals,
|
||||
kAllowHarmonySloppy,
|
||||
};
|
||||
RunParserSyncTest(context_data, error_data, kError, NULL, 0,
|
||||
always_flags, arraysize(always_flags));
|
||||
}
|
||||
|
367
test/mjsunit/harmony/computed-property-names-classes.js
Normal file
367
test/mjsunit/harmony/computed-property-names-classes.js
Normal file
@ -0,0 +1,367 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
'use strict';
|
||||
|
||||
// Flags: --harmony-computed-property-names --harmony-classes
|
||||
|
||||
|
||||
function ID(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
(function TestClassMethodString() {
|
||||
class C {
|
||||
a() { return 'A'}
|
||||
['b']() { return 'B'; }
|
||||
c() { return 'C'; }
|
||||
[ID('d')]() { return 'D'; }
|
||||
}
|
||||
assertEquals('A', new C().a());
|
||||
assertEquals('B', new C().b());
|
||||
assertEquals('C', new C().c());
|
||||
assertEquals('D', new C().d());
|
||||
assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(C.prototype));
|
||||
})();
|
||||
|
||||
|
||||
(function TestClassMethodNumber() {
|
||||
class C {
|
||||
a() { return 'A'; }
|
||||
[1]() { return 'B'; }
|
||||
c() { return 'C'; }
|
||||
[ID(2)]() { return 'D'; }
|
||||
}
|
||||
assertEquals('A', new C().a());
|
||||
assertEquals('B', new C()[1]());
|
||||
assertEquals('C', new C().c());
|
||||
assertEquals('D', new C()[2]());
|
||||
// Array indexes first.
|
||||
assertArrayEquals(['1', '2', 'a', 'c'], Object.keys(C.prototype));
|
||||
})();
|
||||
|
||||
|
||||
(function TestClassMethodSymbol() {
|
||||
var sym1 = Symbol();
|
||||
var sym2 = Symbol();
|
||||
class C {
|
||||
a() { return 'A'; }
|
||||
[sym1]() { return 'B'; }
|
||||
c() { return 'C'; }
|
||||
[ID(sym2)]() { return 'D'; }
|
||||
}
|
||||
assertEquals('A', new C().a());
|
||||
assertEquals('B', new C()[sym1]());
|
||||
assertEquals('C', new C().c());
|
||||
assertEquals('D', new C()[sym2]());
|
||||
assertArrayEquals(['a', 'c'], Object.keys(C.prototype));
|
||||
assertArrayEquals([sym1, sym2], Object.getOwnPropertySymbols(C.prototype));
|
||||
})();
|
||||
|
||||
|
||||
|
||||
(function TestStaticClassMethodString() {
|
||||
class C {
|
||||
static a() { return 'A'}
|
||||
static ['b']() { return 'B'; }
|
||||
static c() { return 'C'; }
|
||||
static ['d']() { return 'D'; }
|
||||
}
|
||||
assertEquals('A', C.a());
|
||||
assertEquals('B', C.b());
|
||||
assertEquals('C', C.c());
|
||||
assertEquals('D', C.d());
|
||||
assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(C));
|
||||
})();
|
||||
|
||||
|
||||
(function TestStaticClassMethodNumber() {
|
||||
class C {
|
||||
static a() { return 'A'; }
|
||||
static [1]() { return 'B'; }
|
||||
static c() { return 'C'; }
|
||||
static [2]() { return 'D'; }
|
||||
}
|
||||
assertEquals('A', C.a());
|
||||
assertEquals('B', C[1]());
|
||||
assertEquals('C', C.c());
|
||||
assertEquals('D', C[2]());
|
||||
// Array indexes first.
|
||||
assertArrayEquals(['1', '2', 'a', 'c'], Object.keys(C));
|
||||
})();
|
||||
|
||||
|
||||
(function TestStaticClassMethodSymbol() {
|
||||
var sym1 = Symbol();
|
||||
var sym2 = Symbol();
|
||||
class C {
|
||||
static a() { return 'A'; }
|
||||
static [sym1]() { return 'B'; }
|
||||
static c() { return 'C'; }
|
||||
static [sym2]() { return 'D'; }
|
||||
}
|
||||
assertEquals('A', C.a());
|
||||
assertEquals('B', C[sym1]());
|
||||
assertEquals('C', C.c());
|
||||
assertEquals('D', C[sym2]());
|
||||
assertArrayEquals(['a', 'c'], Object.keys(C));
|
||||
assertArrayEquals([sym1, sym2], Object.getOwnPropertySymbols(C));
|
||||
})();
|
||||
|
||||
|
||||
|
||||
function assertIteratorResult(value, done, result) {
|
||||
assertEquals({ value: value, done: done}, result);
|
||||
}
|
||||
|
||||
|
||||
(function TestGeneratorComputedName() {
|
||||
class C {
|
||||
*['a']() {
|
||||
yield 1;
|
||||
yield 2;
|
||||
}
|
||||
}
|
||||
var iter = new C().a();
|
||||
assertIteratorResult(1, false, iter.next());
|
||||
assertIteratorResult(2, false, iter.next());
|
||||
assertIteratorResult(undefined, true, iter.next());
|
||||
assertArrayEquals(['a'], Object.keys(C.prototype));
|
||||
})();
|
||||
|
||||
|
||||
(function TestToNameSideEffects() {
|
||||
var counter = 0;
|
||||
var key1 = {
|
||||
toString: function() {
|
||||
assertEquals(0, counter++);
|
||||
return 'b';
|
||||
}
|
||||
};
|
||||
var key2 = {
|
||||
toString: function() {
|
||||
assertEquals(1, counter++);
|
||||
return 'd';
|
||||
}
|
||||
};
|
||||
class C {
|
||||
a() { return 'A'; }
|
||||
[key1]() { return 'B'; }
|
||||
c() { return 'C'; }
|
||||
[key2]() { return 'D'; }
|
||||
}
|
||||
assertEquals(2, counter);
|
||||
assertEquals('A', new C().a());
|
||||
assertEquals('B', new C().b());
|
||||
assertEquals('C', new C().c());
|
||||
assertEquals('D', new C().d());
|
||||
assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(C.prototype));
|
||||
})();
|
||||
|
||||
|
||||
(function TestToNameSideEffectsNumbers() {
|
||||
var counter = 0;
|
||||
var key1 = {
|
||||
valueOf: function() {
|
||||
assertEquals(0, counter++);
|
||||
return 1;
|
||||
},
|
||||
toString: null
|
||||
};
|
||||
var key2 = {
|
||||
valueOf: function() {
|
||||
assertEquals(1, counter++);
|
||||
return 2;
|
||||
},
|
||||
toString: null
|
||||
};
|
||||
|
||||
class C {
|
||||
a() { return 'A'; }
|
||||
[key1]() { return 'B'; }
|
||||
c() { return 'C'; }
|
||||
[key2]() { return 'D'; }
|
||||
}
|
||||
assertEquals(2, counter);
|
||||
assertEquals('A', new C().a());
|
||||
assertEquals('B', new C()[1]());
|
||||
assertEquals('C', new C().c());
|
||||
assertEquals('D', new C()[2]());
|
||||
// Array indexes first.
|
||||
assertArrayEquals(['1', '2', 'a', 'c'], Object.keys(C.prototype));
|
||||
})();
|
||||
|
||||
|
||||
(function TestGetter() {
|
||||
class C {
|
||||
get ['a']() {
|
||||
return 'A';
|
||||
}
|
||||
}
|
||||
assertEquals('A', new C().a);
|
||||
|
||||
class C2 {
|
||||
get b() {
|
||||
assertUnreachable();
|
||||
}
|
||||
get ['b']() {
|
||||
return 'B';
|
||||
}
|
||||
}
|
||||
assertEquals('B', new C2().b);
|
||||
|
||||
class C3 {
|
||||
get c() {
|
||||
assertUnreachable();
|
||||
}
|
||||
get ['c']() {
|
||||
assertUnreachable();
|
||||
}
|
||||
get ['c']() {
|
||||
return 'C';
|
||||
}
|
||||
}
|
||||
assertEquals('C', new C3().c);
|
||||
|
||||
class C4 {
|
||||
get ['d']() {
|
||||
assertUnreachable();
|
||||
}
|
||||
get d() {
|
||||
return 'D';
|
||||
}
|
||||
}
|
||||
assertEquals('D', new C4().d);
|
||||
})();
|
||||
|
||||
|
||||
(function TestSetter() {
|
||||
var calls = 0;
|
||||
class C {
|
||||
set ['a'](_) {
|
||||
calls++;
|
||||
}
|
||||
}
|
||||
new C().a = 'A';
|
||||
assertEquals(1, calls);
|
||||
|
||||
calls = 0;
|
||||
class C2 {
|
||||
set b(_) {
|
||||
assertUnreachable();
|
||||
}
|
||||
set ['b'](_) {
|
||||
calls++;
|
||||
}
|
||||
}
|
||||
new C2().b = 'B';
|
||||
assertEquals(1, calls);
|
||||
|
||||
calls = 0;
|
||||
class C3 {
|
||||
set c(_) {
|
||||
assertUnreachable()
|
||||
}
|
||||
set ['c'](_) {
|
||||
assertUnreachable()
|
||||
}
|
||||
set ['c'](_) {
|
||||
calls++
|
||||
}
|
||||
}
|
||||
new C3().c = 'C';
|
||||
assertEquals(1, calls);
|
||||
|
||||
calls = 0;
|
||||
class C4 {
|
||||
set ['d'](_) {
|
||||
assertUnreachable()
|
||||
}
|
||||
set d(_) {
|
||||
calls++
|
||||
}
|
||||
}
|
||||
new C4().d = 'D';
|
||||
assertEquals(1, calls);
|
||||
})();
|
||||
|
||||
|
||||
(function TestPrototype() {
|
||||
// Normally a static prototype property is not allowed.
|
||||
class C {
|
||||
static ['prototype']() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
assertEquals(1, C.prototype());
|
||||
|
||||
class C2 {
|
||||
static get ['prototype']() {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
assertEquals(2, C2.prototype);
|
||||
|
||||
var calls = 0;
|
||||
class C3 {
|
||||
static set ['prototype'](x) {
|
||||
assertEquals(3, x);
|
||||
calls++;
|
||||
}
|
||||
}
|
||||
C3.prototype = 3;
|
||||
assertEquals(1, calls);
|
||||
|
||||
class C4 {
|
||||
static *['prototype']() {
|
||||
yield 1;
|
||||
yield 2;
|
||||
}
|
||||
}
|
||||
var iter = C4.prototype();
|
||||
assertIteratorResult(1, false, iter.next());
|
||||
assertIteratorResult(2, false, iter.next());
|
||||
assertIteratorResult(undefined, true, iter.next());
|
||||
})();
|
||||
|
||||
|
||||
(function TestConstructor() {
|
||||
// Normally a constructor property is not allowed.
|
||||
class C {
|
||||
['constructor']() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
assertTrue(C !== C.prototype.constructor);
|
||||
assertEquals(1, new C().constructor());
|
||||
|
||||
class C2 {
|
||||
get ['constructor']() {
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
assertEquals(2, new C2().constructor);
|
||||
|
||||
var calls = 0;
|
||||
class C3 {
|
||||
set ['constructor'](x) {
|
||||
assertEquals(3, x);
|
||||
calls++;
|
||||
}
|
||||
}
|
||||
new C3().constructor = 3;
|
||||
assertEquals(1, calls);
|
||||
|
||||
class C4 {
|
||||
*['constructor']() {
|
||||
yield 1;
|
||||
yield 2;
|
||||
}
|
||||
}
|
||||
var iter = new C4().constructor();
|
||||
assertIteratorResult(1, false, iter.next());
|
||||
assertIteratorResult(2, false, iter.next());
|
||||
assertIteratorResult(undefined, true, iter.next());
|
||||
})();
|
@ -0,0 +1,121 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-computed-property-names --harmony-object-literals
|
||||
|
||||
|
||||
function ID(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
(function TestMethodComputedNameString() {
|
||||
var object = {
|
||||
a() { return 'A'},
|
||||
['b']() { return 'B'; },
|
||||
c() { return 'C'; },
|
||||
[ID('d')]() { return 'D'; },
|
||||
};
|
||||
assertEquals('A', object.a());
|
||||
assertEquals('B', object.b());
|
||||
assertEquals('C', object.c());
|
||||
assertEquals('D', object.d());
|
||||
assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(object));
|
||||
})();
|
||||
|
||||
|
||||
(function TestMethodComputedNameNumber() {
|
||||
var object = {
|
||||
a() { return 'A'; },
|
||||
[1]() { return 'B'; },
|
||||
c() { return 'C'; },
|
||||
[ID(2)]() { return 'D'; },
|
||||
};
|
||||
assertEquals('A', object.a());
|
||||
assertEquals('B', object[1]());
|
||||
assertEquals('C', object.c());
|
||||
assertEquals('D', object[2]());
|
||||
// Array indexes first.
|
||||
assertArrayEquals(['1', '2', 'a', 'c'], Object.keys(object));
|
||||
})();
|
||||
|
||||
|
||||
(function TestMethodComputedNameSymbol() {
|
||||
var sym1 = Symbol();
|
||||
var sym2 = Symbol();
|
||||
var object = {
|
||||
a() { return 'A'; },
|
||||
[sym1]() { return 'B'; },
|
||||
c() { return 'C'; },
|
||||
[ID(sym2)]() { return 'D'; },
|
||||
};
|
||||
assertEquals('A', object.a());
|
||||
assertEquals('B', object[sym1]());
|
||||
assertEquals('C', object.c());
|
||||
assertEquals('D', object[sym2]());
|
||||
assertArrayEquals(['a', 'c'], Object.keys(object));
|
||||
assertArrayEquals([sym1, sym2], Object.getOwnPropertySymbols(object));
|
||||
})();
|
||||
|
||||
|
||||
function assertIteratorResult(value, done, result) {
|
||||
assertEquals({ value: value, done: done}, result);
|
||||
}
|
||||
|
||||
|
||||
(function TestGeneratorComputedName() {
|
||||
var object = {
|
||||
*['a']() {
|
||||
yield 1;
|
||||
yield 2;
|
||||
}
|
||||
};
|
||||
var iter = object.a();
|
||||
assertIteratorResult(1, false, iter.next());
|
||||
assertIteratorResult(2, false, iter.next());
|
||||
assertIteratorResult(undefined, true, iter.next());
|
||||
assertArrayEquals(['a'], Object.keys(object));
|
||||
})();
|
||||
|
||||
|
||||
(function TestToNameSideEffects() {
|
||||
var counter = 0;
|
||||
var key1 = {
|
||||
toString: function() {
|
||||
assertEquals(0, counter++);
|
||||
return 'b';
|
||||
}
|
||||
};
|
||||
var key2 = {
|
||||
toString: function() {
|
||||
assertEquals(1, counter++);
|
||||
return 'd';
|
||||
}
|
||||
};
|
||||
var object = {
|
||||
a() { return 'A'; },
|
||||
[key1]() { return 'B'; },
|
||||
c() { return 'C'; },
|
||||
[key2]() { return 'D'; },
|
||||
};
|
||||
assertEquals(2, counter);
|
||||
assertEquals('A', object.a());
|
||||
assertEquals('B', object.b());
|
||||
assertEquals('C', object.c());
|
||||
assertEquals('D', object.d());
|
||||
assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(object));
|
||||
})();
|
||||
|
||||
|
||||
(function TestDuplicateKeys() {
|
||||
'use strict';
|
||||
// ES5 does not allow duplicate keys.
|
||||
// ES6 does but we haven't changed our code yet.
|
||||
|
||||
var object = {
|
||||
a() { return 1; },
|
||||
['a']() { return 2; },
|
||||
};
|
||||
assertEquals(2, object.a());
|
||||
})();
|
270
test/mjsunit/harmony/computed-property-names.js
Normal file
270
test/mjsunit/harmony/computed-property-names.js
Normal file
@ -0,0 +1,270 @@
|
||||
// Copyright 2014 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --harmony-computed-property-names
|
||||
|
||||
|
||||
function ID(x) {
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
|
||||
(function TestBasicsString() {
|
||||
var object = {
|
||||
a: 'A',
|
||||
['b']: 'B',
|
||||
c: 'C',
|
||||
[ID('d')]: 'D',
|
||||
};
|
||||
assertEquals('A', object.a);
|
||||
assertEquals('B', object.b);
|
||||
assertEquals('C', object.c);
|
||||
assertEquals('D', object.d);
|
||||
assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(object));
|
||||
})();
|
||||
|
||||
|
||||
(function TestBasicsNumber() {
|
||||
var object = {
|
||||
a: 'A',
|
||||
[1]: 'B',
|
||||
c: 'C',
|
||||
[ID(2)]: 'D',
|
||||
};
|
||||
assertEquals('A', object.a);
|
||||
assertEquals('B', object[1]);
|
||||
assertEquals('C', object.c);
|
||||
assertEquals('D', object[2]);
|
||||
// Array indexes first.
|
||||
assertArrayEquals(['1', '2', 'a', 'c'], Object.keys(object));
|
||||
})();
|
||||
|
||||
|
||||
(function TestBasicsSymbol() {
|
||||
var sym1 = Symbol();
|
||||
var sym2 = Symbol();
|
||||
var object = {
|
||||
a: 'A',
|
||||
[sym1]: 'B',
|
||||
c: 'C',
|
||||
[ID(sym2)]: 'D',
|
||||
};
|
||||
assertEquals('A', object.a);
|
||||
assertEquals('B', object[sym1]);
|
||||
assertEquals('C', object.c);
|
||||
assertEquals('D', object[sym2]);
|
||||
assertArrayEquals(['a', 'c'], Object.keys(object));
|
||||
assertArrayEquals([sym1, sym2], Object.getOwnPropertySymbols(object));
|
||||
})();
|
||||
|
||||
|
||||
(function TestToNameSideEffects() {
|
||||
var counter = 0;
|
||||
var key1 = {
|
||||
toString: function() {
|
||||
assertEquals(0, counter++);
|
||||
return 'b';
|
||||
}
|
||||
};
|
||||
var key2 = {
|
||||
toString: function() {
|
||||
assertEquals(1, counter++);
|
||||
return 'd';
|
||||
}
|
||||
};
|
||||
var object = {
|
||||
a: 'A',
|
||||
[key1]: 'B',
|
||||
c: 'C',
|
||||
[key2]: 'D',
|
||||
};
|
||||
assertEquals(2, counter);
|
||||
assertEquals('A', object.a);
|
||||
assertEquals('B', object.b);
|
||||
assertEquals('C', object.c);
|
||||
assertEquals('D', object.d);
|
||||
assertArrayEquals(['a', 'b', 'c', 'd'], Object.keys(object));
|
||||
})();
|
||||
|
||||
|
||||
(function TestToNameSideEffectsNumbers() {
|
||||
var counter = 0;
|
||||
var key1 = {
|
||||
valueOf: function() {
|
||||
assertEquals(0, counter++);
|
||||
return 1;
|
||||
},
|
||||
toString: null
|
||||
};
|
||||
var key2 = {
|
||||
valueOf: function() {
|
||||
assertEquals(1, counter++);
|
||||
return 2;
|
||||
},
|
||||
toString: null
|
||||
};
|
||||
|
||||
var object = {
|
||||
a: 'A',
|
||||
[key1]: 'B',
|
||||
c: 'C',
|
||||
[key2]: 'D',
|
||||
};
|
||||
assertEquals(2, counter);
|
||||
assertEquals('A', object.a);
|
||||
assertEquals('B', object[1]);
|
||||
assertEquals('C', object.c);
|
||||
assertEquals('D', object[2]);
|
||||
// Array indexes first.
|
||||
assertArrayEquals(['1', '2', 'a', 'c'], Object.keys(object));
|
||||
})();
|
||||
|
||||
|
||||
(function TestDoubleName() {
|
||||
var object = {
|
||||
[1.2]: 'A',
|
||||
[1e55]: 'B',
|
||||
[0.000001]: 'C',
|
||||
[-0]: 'D',
|
||||
[Infinity]: 'E',
|
||||
[-Infinity]: 'F',
|
||||
[NaN]: 'G',
|
||||
};
|
||||
assertEquals('A', object['1.2']);
|
||||
assertEquals('B', object['1e+55']);
|
||||
assertEquals('C', object['0.000001']);
|
||||
assertEquals('D', object[0]);
|
||||
assertEquals('E', object[Infinity]);
|
||||
assertEquals('F', object[-Infinity]);
|
||||
assertEquals('G', object[NaN]);
|
||||
})();
|
||||
|
||||
|
||||
(function TestGetter() {
|
||||
var object = {
|
||||
get ['a']() {
|
||||
return 'A';
|
||||
}
|
||||
};
|
||||
assertEquals('A', object.a);
|
||||
|
||||
object = {
|
||||
get b() {
|
||||
assertUnreachable();
|
||||
},
|
||||
get ['b']() {
|
||||
return 'B';
|
||||
}
|
||||
};
|
||||
assertEquals('B', object.b);
|
||||
|
||||
object = {
|
||||
get c() {
|
||||
assertUnreachable();
|
||||
},
|
||||
get ['c']() {
|
||||
assertUnreachable();
|
||||
},
|
||||
get ['c']() {
|
||||
return 'C';
|
||||
}
|
||||
};
|
||||
assertEquals('C', object.c);
|
||||
|
||||
object = {
|
||||
get ['d']() {
|
||||
assertUnreachable();
|
||||
},
|
||||
get d() {
|
||||
return 'D';
|
||||
}
|
||||
};
|
||||
assertEquals('D', object.d);
|
||||
})();
|
||||
|
||||
|
||||
(function TestSetter() {
|
||||
var calls = 0;
|
||||
var object = {
|
||||
set ['a'](_) {
|
||||
calls++;
|
||||
}
|
||||
};
|
||||
object.a = 'A';
|
||||
assertEquals(1, calls);
|
||||
|
||||
calls = 0;
|
||||
object = {
|
||||
set b(_) {
|
||||
assertUnreachable();
|
||||
},
|
||||
set ['b'](_) {
|
||||
calls++;
|
||||
}
|
||||
};
|
||||
object.b = 'B';
|
||||
assertEquals(1, calls);
|
||||
|
||||
calls = 0;
|
||||
object = {
|
||||
set c(_) {
|
||||
assertUnreachable()
|
||||
},
|
||||
set ['c'](_) {
|
||||
assertUnreachable()
|
||||
},
|
||||
set ['c'](_) {
|
||||
calls++
|
||||
}
|
||||
};
|
||||
object.c = 'C';
|
||||
assertEquals(1, calls);
|
||||
|
||||
calls = 0;
|
||||
object = {
|
||||
set ['d'](_) {
|
||||
assertUnreachable()
|
||||
},
|
||||
set d(_) {
|
||||
calls++
|
||||
}
|
||||
};
|
||||
object.d = 'D';
|
||||
assertEquals(1, calls);
|
||||
})();
|
||||
|
||||
|
||||
(function TestDuplicateKeys() {
|
||||
'use strict';
|
||||
// ES5 does not allow duplicate keys.
|
||||
// ES6 does but we haven't changed our code yet.
|
||||
|
||||
var object = {
|
||||
a: 1,
|
||||
['a']: 2,
|
||||
};
|
||||
assertEquals(2, object.a);
|
||||
})();
|
||||
|
||||
|
||||
(function TestProto() {
|
||||
var proto = {};
|
||||
var object = {
|
||||
__proto__: proto
|
||||
};
|
||||
assertEquals(proto, Object.getPrototypeOf(object));
|
||||
|
||||
object = {
|
||||
'__proto__': proto
|
||||
};
|
||||
assertEquals(proto, Object.getPrototypeOf(object));
|
||||
|
||||
var object = {
|
||||
['__proto__']: proto
|
||||
};
|
||||
assertEquals(Object.prototype, Object.getPrototypeOf(object));
|
||||
assertEquals(proto, object.__proto__);
|
||||
assertTrue(object.hasOwnProperty('__proto__'));
|
||||
})();
|
Loading…
Reference in New Issue
Block a user