ES6: Add support for super in object literals
This only available under --harmony-classes BUG=v8:3571 LOG=Y R=dslomov@chromium.org Review URL: https://codereview.chromium.org/718473002 Cr-Commit-Position: refs/heads/master@{#25271} git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@25271 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
1386257c55
commit
84741e76a3
@ -1383,6 +1383,19 @@ void FullCodeGenerator::EmitLoadHomeObject(SuperReference* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitSetHomeObjectIfNeeded(Expression* initializer,
|
||||
int offset) {
|
||||
if (NeedsHomeObject(initializer)) {
|
||||
__ ldr(StoreDescriptor::ReceiverRegister(), MemOperand(sp));
|
||||
__ mov(StoreDescriptor::NameRegister(),
|
||||
Operand(isolate()->factory()->home_object_symbol()));
|
||||
__ ldr(StoreDescriptor::ValueRegister(),
|
||||
MemOperand(sp, offset * kPointerSize));
|
||||
CallStoreIC();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitLoadGlobalCheckExtensions(VariableProxy* proxy,
|
||||
TypeofState typeof_state,
|
||||
Label* slow) {
|
||||
@ -1739,6 +1752,14 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ ldr(StoreDescriptor::ReceiverRegister(), MemOperand(sp));
|
||||
CallStoreIC(key->LiteralFeedbackId());
|
||||
PrepareForBailoutForId(key->id(), NO_REGISTERS);
|
||||
|
||||
if (NeedsHomeObject(value)) {
|
||||
__ Move(StoreDescriptor::ReceiverRegister(), r0);
|
||||
__ mov(StoreDescriptor::NameRegister(),
|
||||
Operand(isolate()->factory()->home_object_symbol()));
|
||||
__ ldr(StoreDescriptor::ValueRegister(), MemOperand(sp));
|
||||
CallStoreIC();
|
||||
}
|
||||
} else {
|
||||
VisitForEffect(value);
|
||||
}
|
||||
@ -1750,6 +1771,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
VisitForStackValue(key);
|
||||
VisitForStackValue(value);
|
||||
if (property->emit_store()) {
|
||||
EmitSetHomeObjectIfNeeded(value, 2);
|
||||
__ mov(r0, Operand(Smi::FromInt(SLOPPY))); // PropertyAttributes
|
||||
__ push(r0);
|
||||
__ CallRuntime(Runtime::kSetProperty, 4);
|
||||
@ -1787,7 +1809,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ push(r0);
|
||||
VisitForStackValue(it->first);
|
||||
EmitAccessor(it->second->getter);
|
||||
EmitSetHomeObjectIfNeeded(it->second->getter, 2);
|
||||
EmitAccessor(it->second->setter);
|
||||
EmitSetHomeObjectIfNeeded(it->second->setter, 3);
|
||||
__ mov(r0, Operand(Smi::FromInt(NONE)));
|
||||
__ push(r0);
|
||||
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
|
||||
@ -2534,6 +2558,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
__ push(scratch);
|
||||
VisitForStackValue(key);
|
||||
VisitForStackValue(value);
|
||||
EmitSetHomeObjectIfNeeded(value, 2);
|
||||
|
||||
switch (property->kind()) {
|
||||
case ObjectLiteral::Property::CONSTANT:
|
||||
|
@ -1372,6 +1372,18 @@ void FullCodeGenerator::EmitLoadHomeObject(SuperReference* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitSetHomeObjectIfNeeded(Expression* initializer,
|
||||
int offset) {
|
||||
if (NeedsHomeObject(initializer)) {
|
||||
__ Peek(StoreDescriptor::ReceiverRegister(), 0);
|
||||
__ Mov(StoreDescriptor::NameRegister(),
|
||||
Operand(isolate()->factory()->home_object_symbol()));
|
||||
__ Peek(StoreDescriptor::ValueRegister(), offset * kPointerSize);
|
||||
CallStoreIC();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitLoadGlobalCheckExtensions(VariableProxy* proxy,
|
||||
TypeofState typeof_state,
|
||||
Label* slow) {
|
||||
@ -1721,6 +1733,14 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ Peek(StoreDescriptor::ReceiverRegister(), 0);
|
||||
CallStoreIC(key->LiteralFeedbackId());
|
||||
PrepareForBailoutForId(key->id(), NO_REGISTERS);
|
||||
|
||||
if (NeedsHomeObject(value)) {
|
||||
__ Mov(StoreDescriptor::ReceiverRegister(), x0);
|
||||
__ Mov(StoreDescriptor::NameRegister(),
|
||||
Operand(isolate()->factory()->home_object_symbol()));
|
||||
__ Peek(StoreDescriptor::ValueRegister(), 0);
|
||||
CallStoreIC();
|
||||
}
|
||||
} else {
|
||||
VisitForEffect(value);
|
||||
}
|
||||
@ -1732,6 +1752,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ Push(x0);
|
||||
VisitForStackValue(key);
|
||||
VisitForStackValue(value);
|
||||
EmitSetHomeObjectIfNeeded(value, 2);
|
||||
__ Mov(x0, Smi::FromInt(SLOPPY)); // Strict mode
|
||||
__ Push(x0);
|
||||
__ CallRuntime(Runtime::kSetProperty, 4);
|
||||
@ -1769,7 +1790,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ Push(x10);
|
||||
VisitForStackValue(it->first);
|
||||
EmitAccessor(it->second->getter);
|
||||
EmitSetHomeObjectIfNeeded(it->second->getter, 2);
|
||||
EmitAccessor(it->second->setter);
|
||||
EmitSetHomeObjectIfNeeded(it->second->setter, 3);
|
||||
__ Mov(x10, Smi::FromInt(NONE));
|
||||
__ Push(x10);
|
||||
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
|
||||
@ -2203,6 +2226,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
__ Push(scratch);
|
||||
VisitForStackValue(key);
|
||||
VisitForStackValue(value);
|
||||
EmitSetHomeObjectIfNeeded(value, 2);
|
||||
|
||||
switch (property->kind()) {
|
||||
case ObjectLiteral::Property::CONSTANT:
|
||||
|
@ -151,6 +151,12 @@ StrictMode FunctionLiteral::strict_mode() const {
|
||||
}
|
||||
|
||||
|
||||
bool FunctionLiteral::needs_super_binding() const {
|
||||
DCHECK_NOT_NULL(scope());
|
||||
return scope()->uses_super() || scope()->inner_uses_super();
|
||||
}
|
||||
|
||||
|
||||
void FunctionLiteral::InitializeSharedInfo(
|
||||
Handle<Code> unoptimized_code) {
|
||||
for (RelocIterator it(*unoptimized_code); !it.done(); it.next()) {
|
||||
|
@ -2497,6 +2497,12 @@ class FunctionLiteral FINAL : public Expression {
|
||||
bool is_expression() const { return IsExpression::decode(bitfield_); }
|
||||
bool is_anonymous() const { return IsAnonymous::decode(bitfield_); }
|
||||
StrictMode strict_mode() const;
|
||||
bool needs_super_binding() const;
|
||||
|
||||
static bool NeedsHomeObject(Expression* literal) {
|
||||
return literal != NULL && literal->IsFunctionLiteral() &&
|
||||
literal->AsFunctionLiteral()->needs_super_binding();
|
||||
}
|
||||
|
||||
int materialized_literal_count() { return materialized_literal_count_; }
|
||||
int expected_property_count() { return expected_property_count_; }
|
||||
|
@ -614,6 +614,15 @@ class FullCodeGenerator: public AstVisitor {
|
||||
|
||||
void EmitLoadHomeObject(SuperReference* expr);
|
||||
|
||||
static bool NeedsHomeObject(Expression* expr) {
|
||||
return FunctionLiteral::NeedsHomeObject(expr);
|
||||
}
|
||||
|
||||
// Adds the [[HomeObject]] to |initializer| if it is a FunctionLiteral.
|
||||
// The value of the initializer is expected to be at the top of the stack.
|
||||
// |offset| is the offset in the stack where the home object can be found.
|
||||
void EmitSetHomeObjectIfNeeded(Expression* initializer, int offset);
|
||||
|
||||
void EmitLoadSuperConstructor(SuperReference* expr);
|
||||
|
||||
void CallIC(Handle<Code> code,
|
||||
|
@ -5653,6 +5653,17 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
if (property->emit_store()) {
|
||||
CHECK_ALIVE(VisitForValue(value));
|
||||
HValue* value = Pop();
|
||||
|
||||
// Add [[HomeObject]] to function literals.
|
||||
if (FunctionLiteral::NeedsHomeObject(property->value())) {
|
||||
Handle<Symbol> sym = isolate()->factory()->home_object_symbol();
|
||||
HInstruction* store_home = BuildKeyedGeneric(
|
||||
STORE, NULL, value, Add<HConstant>(sym), literal);
|
||||
AddInstruction(store_home);
|
||||
DCHECK(store_home->HasObservableSideEffects());
|
||||
Add<HSimulate>(property->value()->id(), REMOVABLE_SIMULATE);
|
||||
}
|
||||
|
||||
Handle<Map> map = property->GetReceiverType();
|
||||
Handle<String> name = property->key()->AsPropertyName();
|
||||
HInstruction* store;
|
||||
@ -5674,9 +5685,8 @@ void HOptimizedGraphBuilder::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
}
|
||||
}
|
||||
AddInstruction(store);
|
||||
if (store->HasObservableSideEffects()) {
|
||||
Add<HSimulate>(key->id(), REMOVABLE_SIMULATE);
|
||||
}
|
||||
DCHECK(store->HasObservableSideEffects());
|
||||
Add<HSimulate>(key->id(), REMOVABLE_SIMULATE);
|
||||
} else {
|
||||
CHECK_ALIVE(VisitForEffect(value));
|
||||
}
|
||||
|
@ -1306,6 +1306,19 @@ void FullCodeGenerator::EmitLoadHomeObject(SuperReference* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitSetHomeObjectIfNeeded(Expression* initializer,
|
||||
int offset) {
|
||||
if (NeedsHomeObject(initializer)) {
|
||||
__ mov(StoreDescriptor::ReceiverRegister(), Operand(esp, 0));
|
||||
__ mov(StoreDescriptor::NameRegister(),
|
||||
Immediate(isolate()->factory()->home_object_symbol()));
|
||||
__ mov(StoreDescriptor::ValueRegister(),
|
||||
Operand(esp, offset * kPointerSize));
|
||||
CallStoreIC();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitLoadGlobalCheckExtensions(VariableProxy* proxy,
|
||||
TypeofState typeof_state,
|
||||
Label* slow) {
|
||||
@ -1670,6 +1683,14 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ mov(StoreDescriptor::ReceiverRegister(), Operand(esp, 0));
|
||||
CallStoreIC(key->LiteralFeedbackId());
|
||||
PrepareForBailoutForId(key->id(), NO_REGISTERS);
|
||||
|
||||
if (NeedsHomeObject(value)) {
|
||||
__ mov(StoreDescriptor::ReceiverRegister(), eax);
|
||||
__ mov(StoreDescriptor::NameRegister(),
|
||||
Immediate(isolate()->factory()->home_object_symbol()));
|
||||
__ mov(StoreDescriptor::ValueRegister(), Operand(esp, 0));
|
||||
CallStoreIC();
|
||||
}
|
||||
} else {
|
||||
VisitForEffect(value);
|
||||
}
|
||||
@ -1679,6 +1700,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
VisitForStackValue(key);
|
||||
VisitForStackValue(value);
|
||||
if (property->emit_store()) {
|
||||
EmitSetHomeObjectIfNeeded(value, 2);
|
||||
__ push(Immediate(Smi::FromInt(SLOPPY))); // Strict mode
|
||||
__ CallRuntime(Runtime::kSetProperty, 4);
|
||||
} else {
|
||||
@ -1711,7 +1733,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ push(Operand(esp, 0)); // Duplicate receiver.
|
||||
VisitForStackValue(it->first);
|
||||
EmitAccessor(it->second->getter);
|
||||
EmitSetHomeObjectIfNeeded(it->second->getter, 2);
|
||||
EmitAccessor(it->second->setter);
|
||||
EmitSetHomeObjectIfNeeded(it->second->setter, 3);
|
||||
__ push(Immediate(Smi::FromInt(NONE)));
|
||||
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
|
||||
}
|
||||
@ -2448,6 +2472,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
}
|
||||
VisitForStackValue(key);
|
||||
VisitForStackValue(value);
|
||||
EmitSetHomeObjectIfNeeded(value, 2);
|
||||
|
||||
switch (property->kind()) {
|
||||
case ObjectLiteral::Property::CONSTANT:
|
||||
|
@ -965,6 +965,7 @@ class PreParserScope {
|
||||
bool IsDeclared(const PreParserIdentifier& identifier) const { return false; }
|
||||
void DeclareParameter(const PreParserIdentifier& identifier, VariableMode) {}
|
||||
void RecordArgumentsUsage() {}
|
||||
void RecordSuperUsage() {}
|
||||
void RecordThisUsage() {}
|
||||
|
||||
// Allow scope->Foo() to work.
|
||||
@ -2508,6 +2509,7 @@ ParserBase<Traits>::ParseMemberWithNewPrefixesExpression(bool* ok) {
|
||||
int new_pos = position();
|
||||
ExpressionT result = this->EmptyExpression();
|
||||
if (Check(Token::SUPER)) {
|
||||
scope_->RecordSuperUsage();
|
||||
result = this->SuperReference(scope_, factory());
|
||||
} else {
|
||||
result = this->ParseMemberWithNewPrefixesExpression(CHECK_OK);
|
||||
@ -2567,6 +2569,7 @@ ParserBase<Traits>::ParseMemberExpression(bool* ok) {
|
||||
} else if (peek() == Token::SUPER) {
|
||||
int beg_pos = position();
|
||||
Consume(Token::SUPER);
|
||||
scope_->RecordSuperUsage();
|
||||
Token::Value next = peek();
|
||||
if (next == Token::PERIOD || next == Token::LBRACK ||
|
||||
next == Token::LPAREN) {
|
||||
|
@ -113,6 +113,8 @@ RUNTIME_FUNCTION(Runtime_DefineClass) {
|
||||
isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
constructor, isolate->factory()->prototype_string(),
|
||||
prototype, attribs));
|
||||
|
||||
// TODO(arv): Only do this conditionally.
|
||||
Handle<Symbol> home_object_symbol(isolate->heap()->home_object_symbol());
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
@ -153,11 +155,6 @@ RUNTIME_FUNCTION(Runtime_DefineClassMethod) {
|
||||
CONVERT_ARG_HANDLE_CHECKED(Object, key, 1);
|
||||
CONVERT_ARG_HANDLE_CHECKED(JSFunction, function, 2);
|
||||
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate, JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
function, isolate->factory()->home_object_symbol(), object,
|
||||
DONT_ENUM));
|
||||
|
||||
uint32_t index;
|
||||
if (key->ToArrayIndex(&index)) {
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
@ -189,11 +186,6 @@ RUNTIME_FUNCTION(Runtime_DefineClassGetter) {
|
||||
Handle<Name> name;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name,
|
||||
Runtime::ToName(isolate, key));
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
getter, isolate->factory()->home_object_symbol(), object, DONT_ENUM));
|
||||
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSObject::DefineAccessor(object, name, getter,
|
||||
@ -212,10 +204,6 @@ RUNTIME_FUNCTION(Runtime_DefineClassSetter) {
|
||||
Handle<Name> name;
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, name,
|
||||
Runtime::ToName(isolate, key));
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSObject::SetOwnPropertyIgnoreAttributes(
|
||||
setter, isolate->factory()->home_object_symbol(), object, DONT_ENUM));
|
||||
RETURN_FAILURE_ON_EXCEPTION(
|
||||
isolate,
|
||||
JSObject::DefineAccessor(object, name, isolate->factory()->null_value(),
|
||||
|
@ -160,16 +160,18 @@ void Scope::SetDefaults(ScopeType scope_type,
|
||||
scope_inside_with_ = false;
|
||||
scope_contains_with_ = false;
|
||||
scope_calls_eval_ = false;
|
||||
scope_uses_this_ = false;
|
||||
scope_uses_arguments_ = false;
|
||||
scope_uses_super_ = false;
|
||||
scope_uses_this_ = false;
|
||||
asm_module_ = false;
|
||||
asm_function_ = outer_scope != NULL && outer_scope->asm_module_;
|
||||
// Inherit the strict mode from the parent scope.
|
||||
strict_mode_ = outer_scope != NULL ? outer_scope->strict_mode_ : SLOPPY;
|
||||
outer_scope_calls_sloppy_eval_ = false;
|
||||
inner_scope_calls_eval_ = false;
|
||||
inner_scope_uses_this_ = false;
|
||||
inner_scope_uses_arguments_ = false;
|
||||
inner_scope_uses_this_ = false;
|
||||
inner_scope_uses_super_ = false;
|
||||
force_eager_compilation_ = false;
|
||||
force_context_allocation_ = (outer_scope != NULL && !is_function_scope())
|
||||
? outer_scope->has_forced_context_allocation() : false;
|
||||
@ -889,12 +891,14 @@ void Scope::Print(int n) {
|
||||
if (scope_inside_with_) Indent(n1, "// scope inside 'with'\n");
|
||||
if (scope_contains_with_) Indent(n1, "// scope contains 'with'\n");
|
||||
if (scope_calls_eval_) Indent(n1, "// scope calls 'eval'\n");
|
||||
if (scope_uses_this_) Indent(n1, "// scope uses 'this'\n");
|
||||
if (scope_uses_arguments_) Indent(n1, "// scope uses 'arguments'\n");
|
||||
if (inner_scope_uses_this_) Indent(n1, "// inner scope uses 'this'\n");
|
||||
if (scope_uses_super_) Indent(n1, "// scope uses 'super'\n");
|
||||
if (scope_uses_this_) Indent(n1, "// scope uses 'this'\n");
|
||||
if (inner_scope_uses_arguments_) {
|
||||
Indent(n1, "// inner scope uses 'arguments'\n");
|
||||
}
|
||||
if (inner_scope_uses_super_) Indent(n1, "// inner scope uses 'super'\n");
|
||||
if (inner_scope_uses_this_) Indent(n1, "// inner scope uses 'this'\n");
|
||||
if (outer_scope_calls_sloppy_eval_) {
|
||||
Indent(n1, "// outer scope calls 'eval' in sloppy context\n");
|
||||
}
|
||||
@ -1177,15 +1181,18 @@ void Scope::PropagateScopeInfo(bool outer_scope_calls_sloppy_eval ) {
|
||||
inner_scope_calls_eval_ = true;
|
||||
}
|
||||
// If the inner scope is an arrow function, propagate the flags tracking
|
||||
// usage of this/arguments, but do not propagate them out from normal
|
||||
// usage of arguments/super/this, but do not propagate them out from normal
|
||||
// functions.
|
||||
if (!inner->is_function_scope() || inner->is_arrow_scope()) {
|
||||
if (inner->scope_uses_this_ || inner->inner_scope_uses_this_) {
|
||||
inner_scope_uses_this_ = true;
|
||||
}
|
||||
if (inner->scope_uses_arguments_ || inner->inner_scope_uses_arguments_) {
|
||||
inner_scope_uses_arguments_ = true;
|
||||
}
|
||||
if (inner->scope_uses_super_ || inner->inner_scope_uses_super_) {
|
||||
inner_scope_uses_super_ = true;
|
||||
}
|
||||
if (inner->scope_uses_this_ || inner->inner_scope_uses_this_) {
|
||||
inner_scope_uses_this_ = true;
|
||||
}
|
||||
}
|
||||
if (inner->force_eager_compilation_) {
|
||||
force_eager_compilation_ = true;
|
||||
|
30
src/scopes.h
30
src/scopes.h
@ -211,12 +211,15 @@ class Scope: public ZoneObject {
|
||||
// Inform the scope that the corresponding code contains an eval call.
|
||||
void RecordEvalCall() { if (!is_global_scope()) scope_calls_eval_ = true; }
|
||||
|
||||
// Inform the scope that the corresponding code uses "this".
|
||||
void RecordThisUsage() { scope_uses_this_ = true; }
|
||||
|
||||
// Inform the scope that the corresponding code uses "arguments".
|
||||
void RecordArgumentsUsage() { scope_uses_arguments_ = true; }
|
||||
|
||||
// Inform the scope that the corresponding code uses "super".
|
||||
void RecordSuperUsage() { scope_uses_super_ = true; }
|
||||
|
||||
// Inform the scope that the corresponding code uses "this".
|
||||
void RecordThisUsage() { scope_uses_this_ = true; }
|
||||
|
||||
// Set the strict mode flag (unless disabled by a global flag).
|
||||
void SetStrictMode(StrictMode strict_mode) { strict_mode_ = strict_mode; }
|
||||
|
||||
@ -301,14 +304,18 @@ class Scope: public ZoneObject {
|
||||
// Does this scope contain a with statement.
|
||||
bool contains_with() const { return scope_contains_with_; }
|
||||
|
||||
// Does this scope access "this".
|
||||
bool uses_this() const { return scope_uses_this_; }
|
||||
// Does any inner scope access "this".
|
||||
bool inner_uses_this() const { return inner_scope_uses_this_; }
|
||||
// Does this scope access "arguments".
|
||||
bool uses_arguments() const { return scope_uses_arguments_; }
|
||||
// Does any inner scope access "arguments".
|
||||
bool inner_uses_arguments() const { return inner_scope_uses_arguments_; }
|
||||
// Does this scope access "super".
|
||||
bool uses_super() const { return scope_uses_super_; }
|
||||
// Does any inner scope access "super".
|
||||
bool inner_uses_super() const { return inner_scope_uses_super_; }
|
||||
// Does this scope access "this".
|
||||
bool uses_this() const { return scope_uses_this_; }
|
||||
// Does any inner scope access "this".
|
||||
bool inner_uses_this() const { return inner_scope_uses_this_; }
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Accessors.
|
||||
@ -486,10 +493,12 @@ class Scope: public ZoneObject {
|
||||
// This scope or a nested catch scope or with scope contain an 'eval' call. At
|
||||
// the 'eval' call site this scope is the declaration scope.
|
||||
bool scope_calls_eval_;
|
||||
// This scope uses "this".
|
||||
bool scope_uses_this_;
|
||||
// This scope uses "arguments".
|
||||
bool scope_uses_arguments_;
|
||||
// This scope uses "super".
|
||||
bool scope_uses_super_;
|
||||
// This scope uses "this".
|
||||
bool scope_uses_this_;
|
||||
// This scope contains an "use asm" annotation.
|
||||
bool asm_module_;
|
||||
// This scope's outer context is an asm module.
|
||||
@ -503,8 +512,9 @@ class Scope: public ZoneObject {
|
||||
// Computed via PropagateScopeInfo.
|
||||
bool outer_scope_calls_sloppy_eval_;
|
||||
bool inner_scope_calls_eval_;
|
||||
bool inner_scope_uses_this_;
|
||||
bool inner_scope_uses_arguments_;
|
||||
bool inner_scope_uses_super_;
|
||||
bool inner_scope_uses_this_;
|
||||
bool force_eager_compilation_;
|
||||
bool force_context_allocation_;
|
||||
|
||||
|
@ -1341,6 +1341,19 @@ void FullCodeGenerator::EmitLoadHomeObject(SuperReference* expr) {
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitSetHomeObjectIfNeeded(Expression* initializer,
|
||||
int offset) {
|
||||
if (NeedsHomeObject(initializer)) {
|
||||
__ movp(StoreDescriptor::ReceiverRegister(), Operand(rsp, 0));
|
||||
__ Move(StoreDescriptor::NameRegister(),
|
||||
isolate()->factory()->home_object_symbol());
|
||||
__ movp(StoreDescriptor::ValueRegister(),
|
||||
Operand(rsp, offset * kPointerSize));
|
||||
CallStoreIC();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitLoadGlobalCheckExtensions(VariableProxy* proxy,
|
||||
TypeofState typeof_state,
|
||||
Label* slow) {
|
||||
@ -1704,6 +1717,14 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ movp(StoreDescriptor::ReceiverRegister(), Operand(rsp, 0));
|
||||
CallStoreIC(key->LiteralFeedbackId());
|
||||
PrepareForBailoutForId(key->id(), NO_REGISTERS);
|
||||
|
||||
if (NeedsHomeObject(value)) {
|
||||
__ movp(StoreDescriptor::ReceiverRegister(), rax);
|
||||
__ Move(StoreDescriptor::NameRegister(),
|
||||
isolate()->factory()->home_object_symbol());
|
||||
__ movp(StoreDescriptor::ValueRegister(), Operand(rsp, 0));
|
||||
CallStoreIC();
|
||||
}
|
||||
} else {
|
||||
VisitForEffect(value);
|
||||
}
|
||||
@ -1713,6 +1734,7 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
VisitForStackValue(key);
|
||||
VisitForStackValue(value);
|
||||
if (property->emit_store()) {
|
||||
EmitSetHomeObjectIfNeeded(value, 2);
|
||||
__ Push(Smi::FromInt(SLOPPY)); // Strict mode
|
||||
__ CallRuntime(Runtime::kSetProperty, 4);
|
||||
} else {
|
||||
@ -1745,7 +1767,9 @@ void FullCodeGenerator::VisitObjectLiteral(ObjectLiteral* expr) {
|
||||
__ Push(Operand(rsp, 0)); // Duplicate receiver.
|
||||
VisitForStackValue(it->first);
|
||||
EmitAccessor(it->second->getter);
|
||||
EmitSetHomeObjectIfNeeded(it->second->getter, 2);
|
||||
EmitAccessor(it->second->setter);
|
||||
EmitSetHomeObjectIfNeeded(it->second->setter, 3);
|
||||
__ Push(Smi::FromInt(NONE));
|
||||
__ CallRuntime(Runtime::kDefineAccessorPropertyUnchecked, 5);
|
||||
}
|
||||
@ -2447,6 +2471,7 @@ void FullCodeGenerator::EmitClassDefineProperties(ClassLiteral* lit) {
|
||||
}
|
||||
VisitForStackValue(key);
|
||||
VisitForStackValue(value);
|
||||
EmitSetHomeObjectIfNeeded(value, 2);
|
||||
|
||||
switch (property->kind()) {
|
||||
case ObjectLiteral::Property::CONSTANT:
|
||||
|
@ -919,7 +919,7 @@ static int Utf8LengthHelper(const char* s) {
|
||||
}
|
||||
|
||||
|
||||
TEST(ScopeUsesThisAndArguments) {
|
||||
TEST(ScopeUsesArgumentsSuperThis) {
|
||||
static const struct {
|
||||
const char* prefix;
|
||||
const char* suffix;
|
||||
@ -928,62 +928,66 @@ TEST(ScopeUsesThisAndArguments) {
|
||||
{ "var f = () => {", "}" },
|
||||
};
|
||||
|
||||
enum Expected {
|
||||
NONE = 0,
|
||||
ARGUMENTS = 1,
|
||||
SUPER = 2,
|
||||
THIS = 4,
|
||||
INNER_ARGUMENTS = 8,
|
||||
INNER_SUPER = 16,
|
||||
INNER_THIS = 32
|
||||
};
|
||||
|
||||
static const struct {
|
||||
const char* body;
|
||||
bool uses_this;
|
||||
bool uses_arguments;
|
||||
bool inner_uses_this;
|
||||
bool inner_uses_arguments;
|
||||
int expected;
|
||||
} source_data[] = {
|
||||
{ "",
|
||||
false, false, false, false },
|
||||
{ "return this",
|
||||
true, false, false, false },
|
||||
{ "return arguments",
|
||||
false, true, false, false },
|
||||
{ "return arguments[0]",
|
||||
false, true, false, false },
|
||||
{ "return this + arguments[0]",
|
||||
true, true, false, false },
|
||||
{ "return x => this + x",
|
||||
false, false, true, false },
|
||||
{ "this.foo = 42;",
|
||||
true, false, false, false },
|
||||
{ "this.foo();",
|
||||
true, false, false, false },
|
||||
{ "if (foo()) { this.f() }",
|
||||
true, false, false, false },
|
||||
{ "if (arguments.length) { this.f() }",
|
||||
true, true, false, false },
|
||||
{ "while (true) { this.f() }",
|
||||
true, false, false, false },
|
||||
{ "if (true) { while (true) this.foo(arguments) }",
|
||||
true, true, false, false },
|
||||
// Multiple nesting levels must work as well.
|
||||
{ "while (true) { while (true) { while (true) return this } }",
|
||||
true, false, false, false },
|
||||
{ "if (1) { return () => { while (true) new this() } }",
|
||||
false, false, true, false },
|
||||
// Note that propagation of the inner_uses_this() value does not
|
||||
// cross boundaries of normal functions onto parent scopes.
|
||||
{ "return function (x) { return this + x }",
|
||||
false, false, false, false },
|
||||
{ "var x = function () { this.foo = 42 };",
|
||||
false, false, false, false },
|
||||
{ "if (1) { return function () { while (true) new this() } }",
|
||||
false, false, false, false },
|
||||
{ "return function (x) { return () => this }",
|
||||
false, false, false, false },
|
||||
// Flags must be correctly set when using block scoping.
|
||||
{ "\"use strict\"; while (true) { let x; this, arguments; }",
|
||||
false, false, true, true },
|
||||
{ "\"use strict\"; if (foo()) { let x; this.f() }",
|
||||
false, false, true, false },
|
||||
{ "\"use strict\"; if (1) {"
|
||||
" let x; return function () { return this + arguments }"
|
||||
"}",
|
||||
false, false, false, false },
|
||||
};
|
||||
{"", NONE},
|
||||
{"return this", THIS},
|
||||
{"return arguments", ARGUMENTS},
|
||||
{"return super()", SUPER},
|
||||
{"return super.x", SUPER},
|
||||
{"return arguments[0]", ARGUMENTS},
|
||||
{"return this + arguments[0]", ARGUMENTS | THIS},
|
||||
{"return this + arguments[0] + super.x", ARGUMENTS | SUPER | THIS},
|
||||
{"return x => this + x", INNER_THIS},
|
||||
{"return x => super() + x", INNER_SUPER},
|
||||
{"this.foo = 42;", THIS},
|
||||
{"this.foo();", THIS},
|
||||
{"if (foo()) { this.f() }", THIS},
|
||||
{"if (foo()) { super.f() }", SUPER},
|
||||
{"if (arguments.length) { this.f() }", ARGUMENTS | THIS},
|
||||
{"while (true) { this.f() }", THIS},
|
||||
{"while (true) { super.f() }", SUPER},
|
||||
{"if (true) { while (true) this.foo(arguments) }", ARGUMENTS | THIS},
|
||||
// Multiple nesting levels must work as well.
|
||||
{"while (true) { while (true) { while (true) return this } }", THIS},
|
||||
{"while (true) { while (true) { while (true) return super() } }",
|
||||
SUPER},
|
||||
{"if (1) { return () => { while (true) new this() } }", INNER_THIS},
|
||||
{"if (1) { return () => { while (true) new super() } }", INNER_SUPER},
|
||||
// Note that propagation of the inner_uses_this() value does not
|
||||
// cross boundaries of normal functions onto parent scopes.
|
||||
{"return function (x) { return this + x }", NONE},
|
||||
{"return function (x) { return super() + x }", NONE},
|
||||
{"var x = function () { this.foo = 42 };", NONE},
|
||||
{"var x = function () { super.foo = 42 };", NONE},
|
||||
{"if (1) { return function () { while (true) new this() } }", NONE},
|
||||
{"if (1) { return function () { while (true) new super() } }", NONE},
|
||||
{"return function (x) { return () => this }", NONE},
|
||||
{"return function (x) { return () => super() }", NONE},
|
||||
// Flags must be correctly set when using block scoping.
|
||||
{"\"use strict\"; while (true) { let x; this, arguments; }",
|
||||
INNER_ARGUMENTS | INNER_THIS},
|
||||
{"\"use strict\"; while (true) { let x; this, super(), arguments; }",
|
||||
INNER_ARGUMENTS | INNER_SUPER | INNER_THIS},
|
||||
{"\"use strict\"; if (foo()) { let x; this.f() }", INNER_THIS},
|
||||
{"\"use strict\"; if (foo()) { let x; super.f() }", INNER_SUPER},
|
||||
{"\"use strict\"; if (1) {"
|
||||
" let x; return function () { return this + super() + arguments }"
|
||||
"}",
|
||||
NONE},
|
||||
};
|
||||
|
||||
i::Isolate* isolate = CcTest::i_isolate();
|
||||
i::Factory* factory = isolate->factory();
|
||||
@ -1013,6 +1017,7 @@ TEST(ScopeUsesThisAndArguments) {
|
||||
isolate->unicode_cache()};
|
||||
i::Parser parser(&info, &parse_info);
|
||||
parser.set_allow_arrow_functions(true);
|
||||
parser.set_allow_classes(true);
|
||||
parser.set_allow_harmony_scoping(true);
|
||||
info.MarkAsGlobal();
|
||||
parser.Parse();
|
||||
@ -1025,11 +1030,16 @@ TEST(ScopeUsesThisAndArguments) {
|
||||
CHECK_EQ(1, global_scope->inner_scopes()->length());
|
||||
|
||||
i::Scope* scope = global_scope->inner_scopes()->at(0);
|
||||
CHECK_EQ(source_data[i].uses_this, scope->uses_this());
|
||||
CHECK_EQ(source_data[i].uses_arguments, scope->uses_arguments());
|
||||
CHECK_EQ(source_data[i].inner_uses_this, scope->inner_uses_this());
|
||||
CHECK_EQ(source_data[i].inner_uses_arguments,
|
||||
CHECK_EQ((source_data[i].expected & ARGUMENTS) != 0,
|
||||
scope->uses_arguments());
|
||||
CHECK_EQ((source_data[i].expected & SUPER) != 0, scope->uses_super());
|
||||
CHECK_EQ((source_data[i].expected & THIS) != 0, scope->uses_this());
|
||||
CHECK_EQ((source_data[i].expected & INNER_ARGUMENTS) != 0,
|
||||
scope->inner_uses_arguments());
|
||||
CHECK_EQ((source_data[i].expected & INNER_SUPER) != 0,
|
||||
scope->inner_uses_super());
|
||||
CHECK_EQ((source_data[i].expected & INNER_THIS) != 0,
|
||||
scope->inner_uses_this());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
168
test/mjsunit/harmony/object-literals-super.js
Normal file
168
test/mjsunit/harmony/object-literals-super.js
Normal file
@ -0,0 +1,168 @@
|
||||
// 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-classes --allow-natives-syntax
|
||||
|
||||
|
||||
(function TestHomeObject() {
|
||||
var object = {
|
||||
method() {
|
||||
return super.method();
|
||||
},
|
||||
get getter() {
|
||||
return super.getter;
|
||||
},
|
||||
set setter(v) {
|
||||
super.setter = v;
|
||||
},
|
||||
get accessor() {
|
||||
return super.accessor;
|
||||
},
|
||||
set accessor(v) {
|
||||
super.accessor = v;
|
||||
},
|
||||
property: function() {
|
||||
super.property();
|
||||
},
|
||||
propertyWithParen: (function() {
|
||||
super.property();
|
||||
}),
|
||||
propertyWithParens: ((function() {
|
||||
super.property();
|
||||
})),
|
||||
|
||||
methodNoSuper() {},
|
||||
get getterNoSuper() {},
|
||||
set setterNoSuper(v) {},
|
||||
get accessorNoSuper() {},
|
||||
set accessorNoSuper(v) {},
|
||||
propertyNoSuper: function() {},
|
||||
propertyWithParenNoSuper: (function() {}),
|
||||
propertyWithParensNoSuper: ((function() {}))
|
||||
};
|
||||
|
||||
assertEquals(object, object.method[%HomeObjectSymbol()]);
|
||||
var desc = Object.getOwnPropertyDescriptor(object, 'getter');
|
||||
assertEquals(object, desc.get[%HomeObjectSymbol()]);
|
||||
desc = Object.getOwnPropertyDescriptor(object, 'setter');
|
||||
assertEquals(object, desc.set[%HomeObjectSymbol()]);
|
||||
desc = Object.getOwnPropertyDescriptor(object, 'accessor');
|
||||
assertEquals(object, desc.get[%HomeObjectSymbol()]);
|
||||
assertEquals(object, desc.set[%HomeObjectSymbol()]);
|
||||
assertEquals(object, object.property[%HomeObjectSymbol()]);
|
||||
assertEquals(object, object.propertyWithParen[%HomeObjectSymbol()]);
|
||||
assertEquals(object, object.propertyWithParens[%HomeObjectSymbol()]);
|
||||
|
||||
assertEquals(undefined, object.methodNoSuper[%HomeObjectSymbol()]);
|
||||
desc = Object.getOwnPropertyDescriptor(object, 'getterNoSuper');
|
||||
assertEquals(undefined, desc.get[%HomeObjectSymbol()]);
|
||||
desc = Object.getOwnPropertyDescriptor(object, 'setterNoSuper');
|
||||
assertEquals(undefined, desc.set[%HomeObjectSymbol()]);
|
||||
desc = Object.getOwnPropertyDescriptor(object, 'accessorNoSuper');
|
||||
assertEquals(undefined, desc.get[%HomeObjectSymbol()]);
|
||||
assertEquals(undefined, desc.set[%HomeObjectSymbol()]);
|
||||
assertEquals(undefined, object.propertyNoSuper[%HomeObjectSymbol()]);
|
||||
assertEquals(undefined, object.propertyWithParenNoSuper[%HomeObjectSymbol()]);
|
||||
assertEquals(undefined,
|
||||
object.propertyWithParensNoSuper[%HomeObjectSymbol()]);
|
||||
})();
|
||||
|
||||
|
||||
(function TestMethod() {
|
||||
var object = {
|
||||
__proto__: {
|
||||
method(x) {
|
||||
return 'proto' + x;
|
||||
}
|
||||
},
|
||||
method(x) {
|
||||
return super.method(x);
|
||||
}
|
||||
};
|
||||
assertEquals('proto42', object.method(42));
|
||||
})();
|
||||
|
||||
|
||||
(function TestGetter() {
|
||||
var object = {
|
||||
__proto__: {
|
||||
_x: 42,
|
||||
get x() {
|
||||
return 'proto' + this._x;
|
||||
}
|
||||
},
|
||||
get x() {
|
||||
return super.x;
|
||||
}
|
||||
};
|
||||
assertEquals('proto42', object.x);
|
||||
})();
|
||||
|
||||
|
||||
(function TestSetter() {
|
||||
var object = {
|
||||
__proto__: {
|
||||
_x: 0,
|
||||
set x(v) {
|
||||
return this._x = v;
|
||||
}
|
||||
},
|
||||
set x(v) {
|
||||
super.x = v;
|
||||
}
|
||||
};
|
||||
assertEquals(1, object.x = 1);
|
||||
assertEquals(1, object._x);
|
||||
assertEquals(0, Object.getPrototypeOf(object)._x);
|
||||
})();
|
||||
|
||||
|
||||
(function TestMethodAsProperty() {
|
||||
var object = {
|
||||
__proto__: {
|
||||
method: function(x) {
|
||||
return 'proto' + x;
|
||||
}
|
||||
},
|
||||
method: function(x) {
|
||||
return super.method(x);
|
||||
}
|
||||
};
|
||||
assertEquals('proto42', object.method(42));
|
||||
})();
|
||||
|
||||
|
||||
(function TestOptimized() {
|
||||
// Object literals without any accessors get optimized.
|
||||
var object = {
|
||||
method() {
|
||||
return super.toString;
|
||||
}
|
||||
};
|
||||
assertEquals(Object.prototype.toString, object.method());
|
||||
})();
|
||||
|
||||
|
||||
(function TestConciseGenerator() {
|
||||
var o = {
|
||||
__proto__: {
|
||||
m() {
|
||||
return 42;
|
||||
}
|
||||
},
|
||||
*g() {
|
||||
yield super.m();
|
||||
},
|
||||
g2: function*() {
|
||||
yield super.m() + 1;
|
||||
},
|
||||
g3: (function*() {
|
||||
yield super.m() + 2;
|
||||
})
|
||||
};
|
||||
|
||||
assertEquals(42, o.g().next().value);
|
||||
assertEquals(43, o.g2().next().value);
|
||||
assertEquals(44, o.g3().next().value);
|
||||
})();
|
@ -124,6 +124,9 @@
|
||||
'regress/regress-crbug-259300': [PASS, NO_VARIANTS],
|
||||
'regress/regress-frame-details-null-receiver': [PASS, NO_VARIANTS],
|
||||
|
||||
# TODO(arv): TurboFan does not yet add [[HomeObject]] as needed.
|
||||
'harmony/object-literals-super': [PASS, NO_VARIANTS],
|
||||
|
||||
##############################################################################
|
||||
# Too slow in debug mode with --stress-opt mode.
|
||||
'compiler/regress-stacktrace-methods': [PASS, ['mode == debug', SKIP]],
|
||||
|
Loading…
Reference in New Issue
Block a user