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:
arv 2014-12-17 10:38:38 -08:00 committed by Commit bot
parent 44e2dd535e
commit cc568d1b7a
28 changed files with 1368 additions and 201 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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());
}

View File

@ -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)

View File

@ -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,

View File

@ -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") \

View File

@ -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++) {

View File

@ -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;

View File

@ -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();

View File

@ -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) \

View File

@ -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);

View File

@ -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,

View File

@ -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.

View File

@ -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:

View File

@ -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);

View File

@ -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);

View File

@ -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);

View File

@ -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());

View File

@ -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 - - -
-------------------------------------

View File

@ -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);

View File

@ -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

View File

@ -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) \

View File

@ -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());
}
}

View File

@ -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:

View File

@ -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));
}

View 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());
})();

View File

@ -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());
})();

View 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__'));
})();