Refactoring of code generation for declarations, in preparation for modules.
Do proper dispatch on declaration type instead of mingling together different code generation paths. Once we add more declaration forms, this is more scalable. In separate steps, I'd like to (1) clean up the logic for DeclareGlobal, and (2) try to reduce the special handling of the name function var if possible. R=fschneider@chromium.org BUG= TEST= Review URL: https://chromiumcodereview.appspot.com/9704054 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@11331 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
ef29accd6f
commit
43a52c4cee
@ -266,11 +266,11 @@ void FullCodeGenerator::Generate() {
|
||||
// For named function expressions, declare the function name as a
|
||||
// constant.
|
||||
if (scope()->is_function_scope() && scope()->function() != NULL) {
|
||||
VariableProxy* proxy = scope()->function();
|
||||
ASSERT(proxy->var()->mode() == CONST ||
|
||||
proxy->var()->mode() == CONST_HARMONY);
|
||||
ASSERT(proxy->var()->location() != Variable::UNALLOCATED);
|
||||
EmitDeclaration(proxy, proxy->var()->mode(), NULL);
|
||||
VariableDeclaration* function = scope()->function();
|
||||
ASSERT(function->proxy()->var()->mode() == CONST ||
|
||||
function->proxy()->var()->mode() == CONST_HARMONY);
|
||||
ASSERT(function->proxy()->var()->location() != Variable::UNALLOCATED);
|
||||
VisitVariableDeclaration(function);
|
||||
}
|
||||
VisitDeclarations(scope()->declarations());
|
||||
}
|
||||
@ -780,15 +780,30 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr,
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
VariableMode mode,
|
||||
FunctionLiteral* function) {
|
||||
void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) {
|
||||
// The variable in the declaration always resides in the current function
|
||||
// context.
|
||||
ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
|
||||
if (FLAG_debug_code) {
|
||||
// Check that we're not inside a with or catch context.
|
||||
__ ldr(r1, FieldMemOperand(cp, HeapObject::kMapOffset));
|
||||
__ CompareRoot(r1, Heap::kWithContextMapRootIndex);
|
||||
__ Check(ne, "Declaration in with context.");
|
||||
__ CompareRoot(r1, Heap::kCatchContextMapRootIndex);
|
||||
__ Check(ne, "Declaration in catch context.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitVariableDeclaration(
|
||||
VariableDeclaration* declaration) {
|
||||
// If it was not possible to allocate the variable at compile time, we
|
||||
// need to "declare" it at runtime to make sure it actually exists in the
|
||||
// local context.
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
VariableMode mode = declaration->mode();
|
||||
Variable* variable = proxy->var();
|
||||
bool binding_needs_init = (function == NULL) &&
|
||||
(mode == CONST || mode == CONST_HARMONY || mode == LET);
|
||||
bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
@ -796,46 +811,17 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
if (function != NULL) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
VisitForAccumulatorValue(function);
|
||||
__ str(result_register(), StackOperand(variable));
|
||||
} else if (binding_needs_init) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
if (hole_init) {
|
||||
Comment cmnt(masm_, "[ VariableDeclaration");
|
||||
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
|
||||
__ str(ip, StackOperand(variable));
|
||||
}
|
||||
break;
|
||||
|
||||
case Variable::CONTEXT:
|
||||
// The variable in the decl always resides in the current function
|
||||
// context.
|
||||
ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
|
||||
if (FLAG_debug_code) {
|
||||
// Check that we're not inside a with or catch context.
|
||||
__ ldr(r1, FieldMemOperand(cp, HeapObject::kMapOffset));
|
||||
__ CompareRoot(r1, Heap::kWithContextMapRootIndex);
|
||||
__ Check(ne, "Declaration in with context.");
|
||||
__ CompareRoot(r1, Heap::kCatchContextMapRootIndex);
|
||||
__ Check(ne, "Declaration in catch context.");
|
||||
}
|
||||
if (function != NULL) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
VisitForAccumulatorValue(function);
|
||||
__ str(result_register(), ContextOperand(cp, variable->index()));
|
||||
int offset = Context::SlotOffset(variable->index());
|
||||
// We know that we have written a function, which is not a smi.
|
||||
__ RecordWriteContextSlot(cp,
|
||||
offset,
|
||||
result_register(),
|
||||
r2,
|
||||
kLRHasBeenSaved,
|
||||
kDontSaveFPRegs,
|
||||
EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
|
||||
} else if (binding_needs_init) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
if (hole_init) {
|
||||
Comment cmnt(masm_, "[ VariableDeclaration");
|
||||
EmitDebugCheckDeclarationContext(variable);
|
||||
__ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
|
||||
__ str(ip, ContextOperand(cp, variable->index()));
|
||||
// No write barrier since the_hole_value is in old space.
|
||||
@ -844,13 +830,11 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
break;
|
||||
|
||||
case Variable::LOOKUP: {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
Comment cmnt(masm_, "[ VariableDeclaration");
|
||||
__ mov(r2, Operand(variable->name()));
|
||||
// Declaration nodes are always introduced in one of four modes.
|
||||
ASSERT(mode == VAR ||
|
||||
mode == CONST ||
|
||||
mode == CONST_HARMONY ||
|
||||
mode == LET);
|
||||
ASSERT(mode == VAR || mode == LET ||
|
||||
mode == CONST || mode == CONST_HARMONY);
|
||||
PropertyAttributes attr = (mode == CONST || mode == CONST_HARMONY)
|
||||
? READ_ONLY : NONE;
|
||||
__ mov(r1, Operand(Smi::FromInt(attr)));
|
||||
@ -858,11 +842,7 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
// Note: For variables we must not push an initial value (such as
|
||||
// 'undefined') because we may have a (legal) redeclaration and we
|
||||
// must not destroy the current value.
|
||||
if (function != NULL) {
|
||||
__ Push(cp, r2, r1);
|
||||
// Push initial value for function declaration.
|
||||
VisitForStackValue(function);
|
||||
} else if (binding_needs_init) {
|
||||
if (hole_init) {
|
||||
__ LoadRoot(r0, Heap::kTheHoleValueRootIndex);
|
||||
__ Push(cp, r2, r1, r0);
|
||||
} else {
|
||||
@ -876,6 +856,107 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitFunctionDeclaration(
|
||||
FunctionDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
Variable* variable = proxy->var();
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
break;
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL: {
|
||||
Comment cmnt(masm_, "[ FunctionDeclaration");
|
||||
VisitForAccumulatorValue(declaration->fun());
|
||||
__ str(result_register(), StackOperand(variable));
|
||||
break;
|
||||
}
|
||||
|
||||
case Variable::CONTEXT: {
|
||||
Comment cmnt(masm_, "[ FunctionDeclaration");
|
||||
EmitDebugCheckDeclarationContext(variable);
|
||||
VisitForAccumulatorValue(declaration->fun());
|
||||
__ str(result_register(), ContextOperand(cp, variable->index()));
|
||||
int offset = Context::SlotOffset(variable->index());
|
||||
// We know that we have written a function, which is not a smi.
|
||||
__ RecordWriteContextSlot(cp,
|
||||
offset,
|
||||
result_register(),
|
||||
r2,
|
||||
kLRHasBeenSaved,
|
||||
kDontSaveFPRegs,
|
||||
EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
|
||||
break;
|
||||
}
|
||||
|
||||
case Variable::LOOKUP: {
|
||||
Comment cmnt(masm_, "[ FunctionDeclaration");
|
||||
__ mov(r2, Operand(variable->name()));
|
||||
__ mov(r1, Operand(Smi::FromInt(NONE)));
|
||||
__ Push(cp, r2, r1);
|
||||
// Push initial value for function declaration.
|
||||
VisitForStackValue(declaration->fun());
|
||||
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
Variable* variable = proxy->var();
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
break;
|
||||
|
||||
case Variable::CONTEXT: {
|
||||
Comment cmnt(masm_, "[ ModuleDeclaration");
|
||||
EmitDebugCheckDeclarationContext(variable);
|
||||
// TODO(rossberg): initialize module instance object
|
||||
break;
|
||||
}
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
case Variable::LOOKUP:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitImportDeclaration(ImportDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
Variable* variable = proxy->var();
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
break;
|
||||
|
||||
case Variable::CONTEXT: {
|
||||
Comment cmnt(masm_, "[ ImportDeclaration");
|
||||
EmitDebugCheckDeclarationContext(variable);
|
||||
// TODO(rossberg)
|
||||
break;
|
||||
}
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
case Variable::LOOKUP:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* declaration) {
|
||||
// TODO(rossberg)
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
|
||||
// Call the runtime to declare the globals.
|
||||
// The context is the first argument.
|
||||
|
@ -613,31 +613,6 @@ void FullCodeGenerator::VisitDeclarations(
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitVariableDeclaration(VariableDeclaration* decl) {
|
||||
EmitDeclaration(decl->proxy(), decl->mode(), NULL);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitFunctionDeclaration(FunctionDeclaration* decl) {
|
||||
EmitDeclaration(decl->proxy(), decl->mode(), decl->fun());
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* decl) {
|
||||
EmitDeclaration(decl->proxy(), decl->mode(), NULL);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitImportDeclaration(ImportDeclaration* decl) {
|
||||
EmitDeclaration(decl->proxy(), decl->mode(), NULL);
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* decl) {
|
||||
// TODO(rossberg)
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitModuleLiteral(ModuleLiteral* module) {
|
||||
// TODO(rossberg)
|
||||
}
|
||||
|
@ -413,12 +413,9 @@ class FullCodeGenerator: public AstVisitor {
|
||||
Label* if_true,
|
||||
Label* if_false);
|
||||
|
||||
// Platform-specific code for a variable, constant, or function
|
||||
// declaration. Functions have an initial value.
|
||||
// Increments global_count_ for unallocated variables.
|
||||
void EmitDeclaration(VariableProxy* proxy,
|
||||
VariableMode mode,
|
||||
FunctionLiteral* function);
|
||||
// If enabled, emit debug code for checking that the current context is
|
||||
// neither a with nor a catch context.
|
||||
void EmitDebugCheckDeclarationContext(Variable* variable);
|
||||
|
||||
// Platform-specific code for checking the stack limit at the back edge of
|
||||
// a loop.
|
||||
@ -548,12 +545,8 @@ class FullCodeGenerator: public AstVisitor {
|
||||
Handle<Script> script() { return info_->script(); }
|
||||
bool is_eval() { return info_->is_eval(); }
|
||||
bool is_native() { return info_->is_native(); }
|
||||
bool is_classic_mode() {
|
||||
return language_mode() == CLASSIC_MODE;
|
||||
}
|
||||
LanguageMode language_mode() {
|
||||
return function()->language_mode();
|
||||
}
|
||||
bool is_classic_mode() { return language_mode() == CLASSIC_MODE; }
|
||||
LanguageMode language_mode() { return function()->language_mode(); }
|
||||
FunctionLiteral* function() { return info_->function(); }
|
||||
Scope* scope() { return scope_; }
|
||||
|
||||
|
136
src/hydrogen.cc
136
src/hydrogen.cc
@ -2550,7 +2550,7 @@ HGraph* HGraphBuilder::CreateGraph() {
|
||||
// Handle implicit declaration of the function name in named function
|
||||
// expressions before other declarations.
|
||||
if (scope->is_function_scope() && scope->function() != NULL) {
|
||||
HandleDeclaration(scope->function(), CONST, NULL, NULL);
|
||||
VisitVariableDeclaration(scope->function());
|
||||
}
|
||||
VisitDeclarations(scope->declarations());
|
||||
AddSimulate(AstNode::kDeclarationsId);
|
||||
@ -7161,20 +7161,15 @@ void HGraphBuilder::VisitThisFunction(ThisFunction* expr) {
|
||||
|
||||
void HGraphBuilder::VisitDeclarations(ZoneList<Declaration*>* declarations) {
|
||||
int length = declarations->length();
|
||||
int global_count = 0;
|
||||
for (int i = 0; i < declarations->length(); i++) {
|
||||
Declaration* decl = declarations->at(i);
|
||||
FunctionDeclaration* fun_decl = decl->AsFunctionDeclaration();
|
||||
HandleDeclaration(decl->proxy(),
|
||||
decl->mode(),
|
||||
fun_decl != NULL ? fun_decl->fun() : NULL,
|
||||
&global_count);
|
||||
}
|
||||
int save_global_count = global_count_;
|
||||
global_count_ = 0;
|
||||
|
||||
AstVisitor::VisitDeclarations(declarations);
|
||||
|
||||
// Batch declare global functions and variables.
|
||||
if (global_count > 0) {
|
||||
if (global_count_ > 0) {
|
||||
Handle<FixedArray> array =
|
||||
isolate()->factory()->NewFixedArray(2 * global_count, TENURED);
|
||||
isolate()->factory()->NewFixedArray(2 * global_count_, TENURED);
|
||||
for (int j = 0, i = 0; i < length; i++) {
|
||||
Declaration* decl = declarations->at(i);
|
||||
Variable* var = decl->proxy()->var();
|
||||
@ -7210,40 +7205,35 @@ void HGraphBuilder::VisitDeclarations(ZoneList<Declaration*>* declarations) {
|
||||
flags);
|
||||
AddInstruction(result);
|
||||
}
|
||||
|
||||
global_count_ = save_global_count;
|
||||
}
|
||||
|
||||
|
||||
void HGraphBuilder::HandleDeclaration(VariableProxy* proxy,
|
||||
VariableMode mode,
|
||||
FunctionLiteral* function,
|
||||
int* global_count) {
|
||||
void HGraphBuilder::VisitVariableDeclaration(VariableDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
VariableMode mode = declaration->mode();
|
||||
Variable* var = proxy->var();
|
||||
bool binding_needs_init =
|
||||
(mode == CONST || mode == CONST_HARMONY || mode == LET);
|
||||
bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
|
||||
switch (var->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++(*global_count);
|
||||
++global_count_;
|
||||
return;
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
if (hole_init) {
|
||||
HValue* value = graph()->GetConstantHole();
|
||||
environment()->Bind(var, value);
|
||||
}
|
||||
break;
|
||||
case Variable::CONTEXT:
|
||||
if (binding_needs_init || function != NULL) {
|
||||
HValue* value = NULL;
|
||||
if (function != NULL) {
|
||||
CHECK_ALIVE(VisitForValue(function));
|
||||
value = Pop();
|
||||
} else {
|
||||
value = graph()->GetConstantHole();
|
||||
}
|
||||
if (var->IsContextSlot()) {
|
||||
HValue* context = environment()->LookupContext();
|
||||
HStoreContextSlot* store = new HStoreContextSlot(
|
||||
context, var->index(), HStoreContextSlot::kNoCheck, value);
|
||||
AddInstruction(store);
|
||||
if (store->HasObservableSideEffects()) AddSimulate(proxy->id());
|
||||
} else {
|
||||
environment()->Bind(var, value);
|
||||
}
|
||||
if (hole_init) {
|
||||
HValue* value = graph()->GetConstantHole();
|
||||
HValue* context = environment()->LookupContext();
|
||||
HStoreContextSlot* store = new HStoreContextSlot(
|
||||
context, var->index(), HStoreContextSlot::kNoCheck, value);
|
||||
AddInstruction(store);
|
||||
if (store->HasObservableSideEffects()) AddSimulate(proxy->id());
|
||||
}
|
||||
break;
|
||||
case Variable::LOOKUP:
|
||||
@ -7252,28 +7242,76 @@ void HGraphBuilder::HandleDeclaration(VariableProxy* proxy,
|
||||
}
|
||||
|
||||
|
||||
void HGraphBuilder::VisitVariableDeclaration(VariableDeclaration* decl) {
|
||||
UNREACHABLE();
|
||||
void HGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
Variable* var = proxy->var();
|
||||
switch (var->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
return;
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL: {
|
||||
CHECK_ALIVE(VisitForValue(declaration->fun()));
|
||||
HValue* value = Pop();
|
||||
environment()->Bind(var, value);
|
||||
break;
|
||||
}
|
||||
case Variable::CONTEXT: {
|
||||
CHECK_ALIVE(VisitForValue(declaration->fun()));
|
||||
HValue* value = Pop();
|
||||
HValue* context = environment()->LookupContext();
|
||||
HStoreContextSlot* store = new HStoreContextSlot(
|
||||
context, var->index(), HStoreContextSlot::kNoCheck, value);
|
||||
AddInstruction(store);
|
||||
if (store->HasObservableSideEffects()) AddSimulate(proxy->id());
|
||||
break;
|
||||
}
|
||||
case Variable::LOOKUP:
|
||||
return Bailout("unsupported lookup slot in declaration");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HGraphBuilder::VisitFunctionDeclaration(FunctionDeclaration* decl) {
|
||||
UNREACHABLE();
|
||||
void HGraphBuilder::VisitModuleDeclaration(ModuleDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
Variable* var = proxy->var();
|
||||
switch (var->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
return;
|
||||
case Variable::CONTEXT: {
|
||||
// TODO(rossberg)
|
||||
break;
|
||||
}
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
case Variable::LOOKUP:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HGraphBuilder::VisitModuleDeclaration(ModuleDeclaration* decl) {
|
||||
UNREACHABLE();
|
||||
void HGraphBuilder::VisitImportDeclaration(ImportDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
Variable* var = proxy->var();
|
||||
switch (var->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
return;
|
||||
case Variable::CONTEXT: {
|
||||
// TODO(rossberg)
|
||||
break;
|
||||
}
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
case Variable::LOOKUP:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void HGraphBuilder::VisitImportDeclaration(ImportDeclaration* decl) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
void HGraphBuilder::VisitExportDeclaration(ExportDeclaration* decl) {
|
||||
UNREACHABLE();
|
||||
void HGraphBuilder::VisitExportDeclaration(ExportDeclaration* declaration) {
|
||||
// TODO(rossberg)
|
||||
}
|
||||
|
||||
|
||||
|
@ -913,11 +913,6 @@ class HGraphBuilder: public AstVisitor {
|
||||
INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION)
|
||||
#undef INLINE_FUNCTION_GENERATOR_DECLARATION
|
||||
|
||||
void HandleDeclaration(VariableProxy* proxy,
|
||||
VariableMode mode,
|
||||
FunctionLiteral* function,
|
||||
int* global_count);
|
||||
|
||||
void VisitDelete(UnaryOperation* expr);
|
||||
void VisitVoid(UnaryOperation* expr);
|
||||
void VisitTypeof(UnaryOperation* expr);
|
||||
@ -1167,6 +1162,7 @@ class HGraphBuilder: public AstVisitor {
|
||||
HBasicBlock* current_block_;
|
||||
|
||||
int inlined_count_;
|
||||
int global_count_;
|
||||
|
||||
Zone* zone_;
|
||||
|
||||
|
@ -262,11 +262,11 @@ void FullCodeGenerator::Generate() {
|
||||
// For named function expressions, declare the function name as a
|
||||
// constant.
|
||||
if (scope()->is_function_scope() && scope()->function() != NULL) {
|
||||
VariableProxy* proxy = scope()->function();
|
||||
ASSERT(proxy->var()->mode() == CONST ||
|
||||
proxy->var()->mode() == CONST_HARMONY);
|
||||
ASSERT(proxy->var()->location() != Variable::UNALLOCATED);
|
||||
EmitDeclaration(proxy, proxy->var()->mode(), NULL);
|
||||
VariableDeclaration* function = scope()->function();
|
||||
ASSERT(function->proxy()->var()->mode() == CONST ||
|
||||
function->proxy()->var()->mode() == CONST_HARMONY);
|
||||
ASSERT(function->proxy()->var()->location() != Variable::UNALLOCATED);
|
||||
VisitVariableDeclaration(function);
|
||||
}
|
||||
VisitDeclarations(scope()->declarations());
|
||||
}
|
||||
@ -756,15 +756,30 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr,
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
VariableMode mode,
|
||||
FunctionLiteral* function) {
|
||||
void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) {
|
||||
// The variable in the declaration always resides in the current function
|
||||
// context.
|
||||
ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
|
||||
if (FLAG_debug_code) {
|
||||
// Check that we're not inside a with or catch context.
|
||||
__ mov(ebx, FieldOperand(esi, HeapObject::kMapOffset));
|
||||
__ cmp(ebx, isolate()->factory()->with_context_map());
|
||||
__ Check(not_equal, "Declaration in with context.");
|
||||
__ cmp(ebx, isolate()->factory()->catch_context_map());
|
||||
__ Check(not_equal, "Declaration in catch context.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitVariableDeclaration(
|
||||
VariableDeclaration* declaration) {
|
||||
// If it was not possible to allocate the variable at compile time, we
|
||||
// need to "declare" it at runtime to make sure it actually exists in the
|
||||
// local context.
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
VariableMode mode = declaration->mode();
|
||||
Variable* variable = proxy->var();
|
||||
bool binding_needs_init = (function == NULL) &&
|
||||
(mode == CONST || mode == CONST_HARMONY || mode == LET);
|
||||
bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
@ -772,44 +787,17 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
if (function != NULL) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
VisitForAccumulatorValue(function);
|
||||
__ mov(StackOperand(variable), result_register());
|
||||
} else if (binding_needs_init) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
if (hole_init) {
|
||||
Comment cmnt(masm_, "[ VariableDeclaration");
|
||||
__ mov(StackOperand(variable),
|
||||
Immediate(isolate()->factory()->the_hole_value()));
|
||||
}
|
||||
break;
|
||||
|
||||
case Variable::CONTEXT:
|
||||
// The variable in the decl always resides in the current function
|
||||
// context.
|
||||
ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
|
||||
if (FLAG_debug_code) {
|
||||
// Check that we're not inside a with or catch context.
|
||||
__ mov(ebx, FieldOperand(esi, HeapObject::kMapOffset));
|
||||
__ cmp(ebx, isolate()->factory()->with_context_map());
|
||||
__ Check(not_equal, "Declaration in with context.");
|
||||
__ cmp(ebx, isolate()->factory()->catch_context_map());
|
||||
__ Check(not_equal, "Declaration in catch context.");
|
||||
}
|
||||
if (function != NULL) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
VisitForAccumulatorValue(function);
|
||||
__ mov(ContextOperand(esi, variable->index()), result_register());
|
||||
// We know that we have written a function, which is not a smi.
|
||||
__ RecordWriteContextSlot(esi,
|
||||
Context::SlotOffset(variable->index()),
|
||||
result_register(),
|
||||
ecx,
|
||||
kDontSaveFPRegs,
|
||||
EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
|
||||
} else if (binding_needs_init) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
if (hole_init) {
|
||||
Comment cmnt(masm_, "[ VariableDeclaration");
|
||||
EmitDebugCheckDeclarationContext(variable);
|
||||
__ mov(ContextOperand(esi, variable->index()),
|
||||
Immediate(isolate()->factory()->the_hole_value()));
|
||||
// No write barrier since the hole value is in old space.
|
||||
@ -818,14 +806,12 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
break;
|
||||
|
||||
case Variable::LOOKUP: {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
Comment cmnt(masm_, "[ VariableDeclaration");
|
||||
__ push(esi);
|
||||
__ push(Immediate(variable->name()));
|
||||
// Declaration nodes are always introduced in one of four modes.
|
||||
ASSERT(mode == VAR ||
|
||||
mode == CONST ||
|
||||
mode == CONST_HARMONY ||
|
||||
mode == LET);
|
||||
// VariableDeclaration nodes are always introduced in one of four modes.
|
||||
ASSERT(mode == VAR || mode == LET ||
|
||||
mode == CONST || mode == CONST_HARMONY);
|
||||
PropertyAttributes attr = (mode == CONST || mode == CONST_HARMONY)
|
||||
? READ_ONLY : NONE;
|
||||
__ push(Immediate(Smi::FromInt(attr)));
|
||||
@ -833,9 +819,7 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
// Note: For variables we must not push an initial value (such as
|
||||
// 'undefined') because we may have a (legal) redeclaration and we
|
||||
// must not destroy the current value.
|
||||
if (function != NULL) {
|
||||
VisitForStackValue(function);
|
||||
} else if (binding_needs_init) {
|
||||
if (hole_init) {
|
||||
__ push(Immediate(isolate()->factory()->the_hole_value()));
|
||||
} else {
|
||||
__ push(Immediate(Smi::FromInt(0))); // Indicates no initial value.
|
||||
@ -847,6 +831,104 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitFunctionDeclaration(
|
||||
FunctionDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
Variable* variable = proxy->var();
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
break;
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL: {
|
||||
Comment cmnt(masm_, "[ FunctionDeclaration");
|
||||
VisitForAccumulatorValue(declaration->fun());
|
||||
__ mov(StackOperand(variable), result_register());
|
||||
break;
|
||||
}
|
||||
|
||||
case Variable::CONTEXT: {
|
||||
Comment cmnt(masm_, "[ FunctionDeclaration");
|
||||
EmitDebugCheckDeclarationContext(variable);
|
||||
VisitForAccumulatorValue(declaration->fun());
|
||||
__ mov(ContextOperand(esi, variable->index()), result_register());
|
||||
// We know that we have written a function, which is not a smi.
|
||||
__ RecordWriteContextSlot(esi,
|
||||
Context::SlotOffset(variable->index()),
|
||||
result_register(),
|
||||
ecx,
|
||||
kDontSaveFPRegs,
|
||||
EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
|
||||
break;
|
||||
}
|
||||
|
||||
case Variable::LOOKUP: {
|
||||
Comment cmnt(masm_, "[ FunctionDeclaration");
|
||||
__ push(esi);
|
||||
__ push(Immediate(variable->name()));
|
||||
__ push(Immediate(Smi::FromInt(NONE)));
|
||||
VisitForStackValue(declaration->fun());
|
||||
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
Variable* variable = proxy->var();
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
break;
|
||||
|
||||
case Variable::CONTEXT: {
|
||||
Comment cmnt(masm_, "[ ModuleDeclaration");
|
||||
EmitDebugCheckDeclarationContext(variable);
|
||||
// TODO(rossberg): initialize module instance object
|
||||
break;
|
||||
}
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
case Variable::LOOKUP:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitImportDeclaration(ImportDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
Variable* variable = proxy->var();
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
break;
|
||||
|
||||
case Variable::CONTEXT: {
|
||||
Comment cmnt(masm_, "[ ImportDeclaration");
|
||||
EmitDebugCheckDeclarationContext(variable);
|
||||
// TODO(rossberg)
|
||||
break;
|
||||
}
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
case Variable::LOOKUP:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* declaration) {
|
||||
// TODO(rossberg)
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
|
||||
// Call the runtime to declare the globals.
|
||||
__ push(esi); // The context is the first argument.
|
||||
|
@ -4460,15 +4460,15 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name,
|
||||
Variable* fvar = NULL;
|
||||
Token::Value fvar_init_op = Token::INIT_CONST;
|
||||
if (type == FunctionLiteral::NAMED_EXPRESSION) {
|
||||
VariableMode fvar_mode;
|
||||
if (is_extended_mode()) {
|
||||
fvar_mode = CONST_HARMONY;
|
||||
fvar_init_op = Token::INIT_CONST_HARMONY;
|
||||
} else {
|
||||
fvar_mode = CONST;
|
||||
}
|
||||
fvar =
|
||||
top_scope_->DeclareFunctionVar(function_name, fvar_mode, factory());
|
||||
if (is_extended_mode()) fvar_init_op = Token::INIT_CONST_HARMONY;
|
||||
VariableMode fvar_mode = is_extended_mode() ? CONST_HARMONY : CONST;
|
||||
fvar = new(zone()) Variable(top_scope_,
|
||||
function_name, fvar_mode, true /* is valid LHS */,
|
||||
Variable::NORMAL, kCreatedInitialized);
|
||||
VariableProxy* proxy = factory()->NewVariableProxy(fvar);
|
||||
VariableDeclaration* fvar_declaration =
|
||||
factory()->NewVariableDeclaration(proxy, fvar_mode, top_scope_);
|
||||
top_scope_->DeclareFunctionVar(fvar_declaration);
|
||||
}
|
||||
|
||||
// Determine whether the function will be lazily compiled.
|
||||
|
@ -1289,9 +1289,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) {
|
||||
// We have to declare a global const property. To capture we only
|
||||
// assign to it when evaluating the assignment for "const x =
|
||||
// <expr>" the initial value is the hole.
|
||||
bool is_const_property = value->IsTheHole();
|
||||
bool is_function_declaration = false;
|
||||
if (value->IsUndefined() || is_const_property) {
|
||||
bool is_var = value->IsUndefined();
|
||||
bool is_const = value->IsTheHole();
|
||||
bool is_function = value->IsSharedFunctionInfo();
|
||||
bool is_module = value->IsJSObject();
|
||||
ASSERT(is_var + is_const + is_function + is_module == 1);
|
||||
|
||||
if (is_var || is_const) {
|
||||
// Lookup the property in the global object, and don't set the
|
||||
// value of the variable if the property is already there.
|
||||
LookupResult lookup(isolate);
|
||||
@ -1299,41 +1303,34 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) {
|
||||
if (lookup.IsProperty()) {
|
||||
// We found an existing property. Unless it was an interceptor
|
||||
// that claims the property is absent, skip this declaration.
|
||||
if (lookup.type() != INTERCEPTOR) {
|
||||
continue;
|
||||
}
|
||||
if (lookup.type() != INTERCEPTOR) continue;
|
||||
PropertyAttributes attributes = global->GetPropertyAttribute(*name);
|
||||
if (attributes != ABSENT) {
|
||||
continue;
|
||||
}
|
||||
if (attributes != ABSENT) continue;
|
||||
// Fall-through and introduce the absent property by using
|
||||
// SetProperty.
|
||||
}
|
||||
} else {
|
||||
is_function_declaration = true;
|
||||
} else if (is_function) {
|
||||
// Copy the function and update its context. Use it as value.
|
||||
Handle<SharedFunctionInfo> shared =
|
||||
Handle<SharedFunctionInfo>::cast(value);
|
||||
Handle<JSFunction> function =
|
||||
isolate->factory()->NewFunctionFromSharedFunctionInfo(shared,
|
||||
context,
|
||||
TENURED);
|
||||
isolate->factory()->NewFunctionFromSharedFunctionInfo(
|
||||
shared, context, TENURED);
|
||||
value = function;
|
||||
}
|
||||
|
||||
LookupResult lookup(isolate);
|
||||
global->LocalLookup(*name, &lookup);
|
||||
|
||||
// Compute the property attributes. According to ECMA-262, section
|
||||
// 13, page 71, the property must be read-only and
|
||||
// non-deletable. However, neither SpiderMonkey nor KJS creates the
|
||||
// property as read-only, so we don't either.
|
||||
// Compute the property attributes. According to ECMA-262,
|
||||
// the property must be non-configurable except in eval.
|
||||
int attr = NONE;
|
||||
if (!DeclareGlobalsEvalFlag::decode(flags)) {
|
||||
bool is_eval = DeclareGlobalsEvalFlag::decode(flags);
|
||||
if (!is_eval || is_module) {
|
||||
attr |= DONT_DELETE;
|
||||
}
|
||||
bool is_native = DeclareGlobalsNativeFlag::decode(flags);
|
||||
if (is_const_property || (is_native && is_function_declaration)) {
|
||||
if (is_const || is_module || (is_native && is_function)) {
|
||||
attr |= READ_ONLY;
|
||||
}
|
||||
|
||||
@ -1346,7 +1343,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) {
|
||||
// handlers such as "function onload() {}". Firefox does call the
|
||||
// onload setter in those case and Safari does not. We follow
|
||||
// Safari for compatibility.
|
||||
if (is_function_declaration) {
|
||||
if (is_function || is_module) {
|
||||
if (lookup.IsProperty() && (lookup.type() != INTERCEPTOR)) {
|
||||
// Do not overwrite READ_ONLY properties.
|
||||
if (lookup.GetAttributes() & READ_ONLY) {
|
||||
|
@ -53,7 +53,7 @@ Handle<ScopeInfo> ScopeInfo::Create(Scope* scope) {
|
||||
FunctionVariableInfo function_name_info;
|
||||
VariableMode function_variable_mode;
|
||||
if (scope->is_function_scope() && scope->function() != NULL) {
|
||||
Variable* var = scope->function()->var();
|
||||
Variable* var = scope->function()->proxy()->var();
|
||||
if (!var->is_used()) {
|
||||
function_name_info = UNUSED;
|
||||
} else if (var->IsContextSlot()) {
|
||||
@ -129,8 +129,8 @@ Handle<ScopeInfo> ScopeInfo::Create(Scope* scope) {
|
||||
// If present, add the function variable name and its index.
|
||||
ASSERT(index == scope_info->FunctionNameEntryIndex());
|
||||
if (has_function_name) {
|
||||
int var_index = scope->function()->var()->index();
|
||||
scope_info->set(index++, *scope->function()->name());
|
||||
int var_index = scope->function()->proxy()->var()->index();
|
||||
scope_info->set(index++, *scope->function()->proxy()->name());
|
||||
scope_info->set(index++, Smi::FromInt(var_index));
|
||||
ASSERT(function_name_info != STACK ||
|
||||
(var_index == scope_info->StackLocalCount() &&
|
||||
|
@ -415,14 +415,20 @@ Variable* Scope::LocalLookup(Handle<String> name) {
|
||||
|
||||
Variable* Scope::LookupFunctionVar(Handle<String> name,
|
||||
AstNodeFactory<AstNullVisitor>* factory) {
|
||||
if (function_ != NULL && function_->name().is_identical_to(name)) {
|
||||
return function_->var();
|
||||
if (function_ != NULL && function_->proxy()->name().is_identical_to(name)) {
|
||||
return function_->proxy()->var();
|
||||
} else if (!scope_info_.is_null()) {
|
||||
// If we are backed by a scope info, try to lookup the variable there.
|
||||
VariableMode mode;
|
||||
int index = scope_info_->FunctionContextSlotIndex(*name, &mode);
|
||||
if (index < 0) return NULL;
|
||||
Variable* var = DeclareFunctionVar(name, mode, factory);
|
||||
Variable* var = new Variable(
|
||||
this, name, mode, true /* is valid LHS */,
|
||||
Variable::NORMAL, kCreatedInitialized);
|
||||
VariableProxy* proxy = factory->NewVariableProxy(var);
|
||||
VariableDeclaration* declaration =
|
||||
factory->NewVariableDeclaration(proxy, mode, this);
|
||||
DeclareFunctionVar(declaration);
|
||||
var->AllocateTo(Variable::CONTEXT, index);
|
||||
return var;
|
||||
} else {
|
||||
@ -794,7 +800,7 @@ void Scope::Print(int n) {
|
||||
// Function name, if any (named function literals, only).
|
||||
if (function_ != NULL) {
|
||||
Indent(n1, "// (local) function name: ");
|
||||
PrintName(function_->name());
|
||||
PrintName(function_->proxy()->name());
|
||||
PrintF("\n");
|
||||
}
|
||||
|
||||
@ -827,7 +833,7 @@ void Scope::Print(int n) {
|
||||
// Print locals.
|
||||
Indent(n1, "// function var\n");
|
||||
if (function_ != NULL) {
|
||||
PrintVar(n1, function_->var());
|
||||
PrintVar(n1, function_->proxy()->var());
|
||||
}
|
||||
|
||||
Indent(n1, "// temporary vars\n");
|
||||
@ -1211,7 +1217,7 @@ void Scope::AllocateNonParameterLocals() {
|
||||
// because of the current ScopeInfo implementation (see
|
||||
// ScopeInfo::ScopeInfo(FunctionScope* scope) constructor).
|
||||
if (function_ != NULL) {
|
||||
AllocateNonParameterLocal(function_->var());
|
||||
AllocateNonParameterLocal(function_->proxy()->var());
|
||||
}
|
||||
}
|
||||
|
||||
@ -1253,14 +1259,14 @@ void Scope::AllocateVariablesRecursively() {
|
||||
|
||||
int Scope::StackLocalCount() const {
|
||||
return num_stack_slots() -
|
||||
(function_ != NULL && function_->var()->IsStackLocal() ? 1 : 0);
|
||||
(function_ != NULL && function_->proxy()->var()->IsStackLocal() ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
int Scope::ContextLocalCount() const {
|
||||
if (num_heap_slots() == 0) return 0;
|
||||
return num_heap_slots() - Context::MIN_CONTEXT_SLOTS -
|
||||
(function_ != NULL && function_->var()->IsContextSlot() ? 1 : 0);
|
||||
(function_ != NULL && function_->proxy()->var()->IsContextSlot() ? 1 : 0);
|
||||
}
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
19
src/scopes.h
19
src/scopes.h
@ -126,15 +126,9 @@ class Scope: public ZoneObject {
|
||||
// Declare the function variable for a function literal. This variable
|
||||
// is in an intermediate scope between this function scope and the the
|
||||
// outer scope. Only possible for function scopes; at most one variable.
|
||||
template<class Visitor>
|
||||
Variable* DeclareFunctionVar(Handle<String> name,
|
||||
VariableMode mode,
|
||||
AstNodeFactory<Visitor>* factory) {
|
||||
ASSERT(is_function_scope() && function_ == NULL);
|
||||
Variable* function_var = new Variable(
|
||||
this, name, mode, true, Variable::NORMAL, kCreatedInitialized);
|
||||
function_ = factory->NewVariableProxy(function_var);
|
||||
return function_var;
|
||||
void DeclareFunctionVar(VariableDeclaration* declaration) {
|
||||
ASSERT(is_function_scope());
|
||||
function_ = declaration;
|
||||
}
|
||||
|
||||
// Declare a parameter in this scope. When there are duplicated
|
||||
@ -312,9 +306,8 @@ class Scope: public ZoneObject {
|
||||
Variable* receiver() { return receiver_; }
|
||||
|
||||
// The variable holding the function literal for named function
|
||||
// literals, or NULL.
|
||||
// Only valid for function scopes.
|
||||
VariableProxy* function() const {
|
||||
// literals, or NULL. Only valid for function scopes.
|
||||
VariableDeclaration* function() const {
|
||||
ASSERT(is_function_scope());
|
||||
return function_;
|
||||
}
|
||||
@ -446,7 +439,7 @@ class Scope: public ZoneObject {
|
||||
// Convenience variable.
|
||||
Variable* receiver_;
|
||||
// Function variable, if any; function scopes only.
|
||||
VariableProxy* function_;
|
||||
VariableDeclaration* function_;
|
||||
// Convenience variable; function scopes only.
|
||||
Variable* arguments_;
|
||||
// Interface; module scopes only.
|
||||
|
@ -257,11 +257,11 @@ void FullCodeGenerator::Generate() {
|
||||
// For named function expressions, declare the function name as a
|
||||
// constant.
|
||||
if (scope()->is_function_scope() && scope()->function() != NULL) {
|
||||
VariableProxy* proxy = scope()->function();
|
||||
ASSERT(proxy->var()->mode() == CONST ||
|
||||
proxy->var()->mode() == CONST_HARMONY);
|
||||
ASSERT(proxy->var()->location() != Variable::UNALLOCATED);
|
||||
EmitDeclaration(proxy, proxy->var()->mode(), NULL);
|
||||
VariableDeclaration* function = scope()->function();
|
||||
ASSERT(function->proxy()->var()->mode() == CONST ||
|
||||
function->proxy()->var()->mode() == CONST_HARMONY);
|
||||
ASSERT(function->proxy()->var()->location() != Variable::UNALLOCATED);
|
||||
VisitVariableDeclaration(function);
|
||||
}
|
||||
VisitDeclarations(scope()->declarations());
|
||||
}
|
||||
@ -753,15 +753,30 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(Expression* expr,
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
VariableMode mode,
|
||||
FunctionLiteral* function) {
|
||||
void FullCodeGenerator::EmitDebugCheckDeclarationContext(Variable* variable) {
|
||||
// The variable in the declaration always resides in the current function
|
||||
// context.
|
||||
ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
|
||||
if (FLAG_debug_code) {
|
||||
// Check that we're not inside a with or catch context.
|
||||
__ movq(rbx, FieldOperand(rsi, HeapObject::kMapOffset));
|
||||
__ CompareRoot(rbx, Heap::kWithContextMapRootIndex);
|
||||
__ Check(not_equal, "Declaration in with context.");
|
||||
__ CompareRoot(rbx, Heap::kCatchContextMapRootIndex);
|
||||
__ Check(not_equal, "Declaration in catch context.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitVariableDeclaration(
|
||||
VariableDeclaration* declaration) {
|
||||
// If it was not possible to allocate the variable at compile time, we
|
||||
// need to "declare" it at runtime to make sure it actually exists in the
|
||||
// local context.
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
VariableMode mode = declaration->mode();
|
||||
Variable* variable = proxy->var();
|
||||
bool binding_needs_init = (function == NULL) &&
|
||||
(mode == CONST || mode == CONST_HARMONY || mode == LET);
|
||||
bool hole_init = mode == CONST || mode == CONST_HARMONY || mode == LET;
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
@ -769,45 +784,17 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
if (function != NULL) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
VisitForAccumulatorValue(function);
|
||||
__ movq(StackOperand(variable), result_register());
|
||||
} else if (binding_needs_init) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
if (hole_init) {
|
||||
Comment cmnt(masm_, "[ VariableDeclaration");
|
||||
__ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
|
||||
__ movq(StackOperand(variable), kScratchRegister);
|
||||
}
|
||||
break;
|
||||
|
||||
case Variable::CONTEXT:
|
||||
// The variable in the decl always resides in the current function
|
||||
// context.
|
||||
ASSERT_EQ(0, scope()->ContextChainLength(variable->scope()));
|
||||
if (FLAG_debug_code) {
|
||||
// Check that we're not inside a with or catch context.
|
||||
__ movq(rbx, FieldOperand(rsi, HeapObject::kMapOffset));
|
||||
__ CompareRoot(rbx, Heap::kWithContextMapRootIndex);
|
||||
__ Check(not_equal, "Declaration in with context.");
|
||||
__ CompareRoot(rbx, Heap::kCatchContextMapRootIndex);
|
||||
__ Check(not_equal, "Declaration in catch context.");
|
||||
}
|
||||
if (function != NULL) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
VisitForAccumulatorValue(function);
|
||||
__ movq(ContextOperand(rsi, variable->index()), result_register());
|
||||
int offset = Context::SlotOffset(variable->index());
|
||||
// We know that we have written a function, which is not a smi.
|
||||
__ RecordWriteContextSlot(rsi,
|
||||
offset,
|
||||
result_register(),
|
||||
rcx,
|
||||
kDontSaveFPRegs,
|
||||
EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
|
||||
} else if (binding_needs_init) {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
if (hole_init) {
|
||||
Comment cmnt(masm_, "[ VariableDeclaration");
|
||||
EmitDebugCheckDeclarationContext(variable);
|
||||
__ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex);
|
||||
__ movq(ContextOperand(rsi, variable->index()), kScratchRegister);
|
||||
// No write barrier since the hole value is in old space.
|
||||
@ -816,14 +803,12 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
break;
|
||||
|
||||
case Variable::LOOKUP: {
|
||||
Comment cmnt(masm_, "[ Declaration");
|
||||
Comment cmnt(masm_, "[ VariableDeclaration");
|
||||
__ push(rsi);
|
||||
__ Push(variable->name());
|
||||
// Declaration nodes are always introduced in one of four modes.
|
||||
ASSERT(mode == VAR ||
|
||||
mode == CONST ||
|
||||
mode == CONST_HARMONY ||
|
||||
mode == LET);
|
||||
ASSERT(mode == VAR || mode == LET ||
|
||||
mode == CONST || mode == CONST_HARMONY);
|
||||
PropertyAttributes attr =
|
||||
(mode == CONST || mode == CONST_HARMONY) ? READ_ONLY : NONE;
|
||||
__ Push(Smi::FromInt(attr));
|
||||
@ -831,9 +816,7 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
// Note: For variables we must not push an initial value (such as
|
||||
// 'undefined') because we may have a (legal) redeclaration and we
|
||||
// must not destroy the current value.
|
||||
if (function != NULL) {
|
||||
VisitForStackValue(function);
|
||||
} else if (binding_needs_init) {
|
||||
if (hole_init) {
|
||||
__ PushRoot(Heap::kTheHoleValueRootIndex);
|
||||
} else {
|
||||
__ Push(Smi::FromInt(0)); // Indicates no initial value.
|
||||
@ -845,6 +828,105 @@ void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy,
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitFunctionDeclaration(
|
||||
FunctionDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
Variable* variable = proxy->var();
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
break;
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL: {
|
||||
Comment cmnt(masm_, "[ FunctionDeclaration");
|
||||
VisitForAccumulatorValue(declaration->fun());
|
||||
__ movq(StackOperand(variable), result_register());
|
||||
break;
|
||||
}
|
||||
|
||||
case Variable::CONTEXT: {
|
||||
Comment cmnt(masm_, "[ FunctionDeclaration");
|
||||
EmitDebugCheckDeclarationContext(variable);
|
||||
VisitForAccumulatorValue(declaration->fun());
|
||||
__ movq(ContextOperand(rsi, variable->index()), result_register());
|
||||
int offset = Context::SlotOffset(variable->index());
|
||||
// We know that we have written a function, which is not a smi.
|
||||
__ RecordWriteContextSlot(rsi,
|
||||
offset,
|
||||
result_register(),
|
||||
rcx,
|
||||
kDontSaveFPRegs,
|
||||
EMIT_REMEMBERED_SET,
|
||||
OMIT_SMI_CHECK);
|
||||
PrepareForBailoutForId(proxy->id(), NO_REGISTERS);
|
||||
break;
|
||||
}
|
||||
|
||||
case Variable::LOOKUP: {
|
||||
Comment cmnt(masm_, "[ FunctionDeclaration");
|
||||
__ push(rsi);
|
||||
__ Push(variable->name());
|
||||
__ Push(Smi::FromInt(NONE));
|
||||
VisitForStackValue(declaration->fun());
|
||||
__ CallRuntime(Runtime::kDeclareContextSlot, 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitModuleDeclaration(ModuleDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
Variable* variable = proxy->var();
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
break;
|
||||
|
||||
case Variable::CONTEXT: {
|
||||
Comment cmnt(masm_, "[ ModuleDeclaration");
|
||||
EmitDebugCheckDeclarationContext(variable);
|
||||
// TODO(rossberg): initialize module instance object
|
||||
break;
|
||||
}
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
case Variable::LOOKUP:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitImportDeclaration(ImportDeclaration* declaration) {
|
||||
VariableProxy* proxy = declaration->proxy();
|
||||
Variable* variable = proxy->var();
|
||||
switch (variable->location()) {
|
||||
case Variable::UNALLOCATED:
|
||||
++global_count_;
|
||||
break;
|
||||
|
||||
case Variable::CONTEXT: {
|
||||
Comment cmnt(masm_, "[ ImportDeclaration");
|
||||
EmitDebugCheckDeclarationContext(variable);
|
||||
// TODO(rossberg)
|
||||
break;
|
||||
}
|
||||
|
||||
case Variable::PARAMETER:
|
||||
case Variable::LOCAL:
|
||||
case Variable::LOOKUP:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::VisitExportDeclaration(ExportDeclaration* declaration) {
|
||||
// TODO(rossberg)
|
||||
}
|
||||
|
||||
|
||||
void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) {
|
||||
// Call the runtime to declare the globals.
|
||||
__ push(rsi); // The context is the first argument.
|
||||
|
Loading…
Reference in New Issue
Block a user