Index: src/arm/full-codegen-arm.cc |
=================================================================== |
--- src/arm/full-codegen-arm.cc (revision 6881) |
+++ src/arm/full-codegen-arm.cc (working copy) |
@@ -219,46 +219,47 @@ |
Move(dot_arguments_slot, r3, r1, r2); |
} |
- { Comment cmnt(masm_, "[ Declarations"); |
- // For named function expressions, declare the function name as a |
- // constant. |
- if (scope()->is_function_scope() && scope()->function() != NULL) { |
- EmitDeclaration(scope()->function(), Variable::CONST, NULL); |
- } |
- // Visit all the explicit declarations unless there is an illegal |
- // redeclaration. |
- if (scope()->HasIllegalRedeclaration()) { |
- scope()->VisitIllegalRedeclaration(this); |
- } else { |
- VisitDeclarations(scope()->declarations()); |
- } |
- } |
- |
if (FLAG_trace) { |
__ CallRuntime(Runtime::kTraceEnter, 0); |
} |
- // Check the stack for overflow or break request. |
- { Comment cmnt(masm_, "[ Stack check"); |
- PrepareForBailout(info->function(), NO_REGISTERS); |
- Label ok; |
- __ LoadRoot(ip, Heap::kStackLimitRootIndex); |
- __ cmp(sp, Operand(ip)); |
- __ b(hs, &ok); |
- StackCheckStub stub; |
- __ CallStub(&stub); |
- __ bind(&ok); |
- } |
+ // Visit the declarations and body unless there is an illegal |
+ // redeclaration. |
+ if (scope()->HasIllegalRedeclaration()) { |
+ Comment cmnt(masm_, "[ Declarations"); |
+ scope()->VisitIllegalRedeclaration(this); |
- { Comment cmnt(masm_, "[ Body"); |
- ASSERT(loop_depth() == 0); |
- VisitStatements(function()->body()); |
- ASSERT(loop_depth() == 0); |
+ } else { |
+ { Comment cmnt(masm_, "[ Declarations"); |
+ // For named function expressions, declare the function name as a |
+ // constant. |
+ if (scope()->is_function_scope() && scope()->function() != NULL) { |
+ EmitDeclaration(scope()->function(), Variable::CONST, NULL); |
+ } |
+ VisitDeclarations(scope()->declarations()); |
+ } |
+ |
+ { Comment cmnt(masm_, "[ Stack check"); |
+ PrepareForBailout(info->function(), NO_REGISTERS); |
+ Label ok; |
+ __ LoadRoot(ip, Heap::kStackLimitRootIndex); |
+ __ cmp(sp, Operand(ip)); |
+ __ b(hs, &ok); |
+ StackCheckStub stub; |
+ __ CallStub(&stub); |
+ __ bind(&ok); |
+ } |
+ |
+ { Comment cmnt(masm_, "[ Body"); |
+ ASSERT(loop_depth() == 0); |
+ VisitStatements(function()->body()); |
+ ASSERT(loop_depth() == 0); |
+ } |
} |
+ // Always emit a 'return undefined' in case control fell off the end of |
+ // the body. |
{ Comment cmnt(masm_, "[ return <undefined>;"); |
- // Emit a 'return undefined' in case control fell off the end of the |
- // body. |
__ LoadRoot(r0, Heap::kUndefinedValueRootIndex); |
} |
EmitReturnSequence(); |
@@ -694,10 +695,11 @@ |
// We bypass the general EmitSlotSearch because we know more about |
// this specific context. |
- // The variable in the decl always resides in the current 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 if we have the correct context pointer. |
+ // Check that we're not inside a 'with'. |
__ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX)); |
__ cmp(r1, cp); |
__ Check(eq, "Unexpected declaration in current context."); |
@@ -1037,7 +1039,7 @@ |
Slot* slot, |
Label* slow) { |
ASSERT(slot->type() == Slot::CONTEXT); |
- Register current = cp; |
+ Register context = cp; |
Register next = r3; |
Register temp = r4; |
@@ -1045,22 +1047,25 @@ |
if (s->num_heap_slots() > 0) { |
if (s->calls_eval()) { |
// Check that extension is NULL. |
- __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX)); |
+ __ ldr(temp, ContextOperand(context, Context::EXTENSION_INDEX)); |
__ tst(temp, temp); |
__ b(ne, slow); |
} |
- __ ldr(next, ContextOperand(current, Context::CLOSURE_INDEX)); |
+ __ ldr(next, ContextOperand(context, Context::CLOSURE_INDEX)); |
__ ldr(next, FieldMemOperand(next, JSFunction::kContextOffset)); |
// Walk the rest of the chain without clobbering cp. |
- current = next; |
+ context = next; |
} |
} |
// Check that last extension is NULL. |
- __ ldr(temp, ContextOperand(current, Context::EXTENSION_INDEX)); |
+ __ ldr(temp, ContextOperand(context, Context::EXTENSION_INDEX)); |
__ tst(temp, temp); |
__ b(ne, slow); |
- __ ldr(temp, ContextOperand(current, Context::FCONTEXT_INDEX)); |
- return ContextOperand(temp, slot->index()); |
+ |
+ // This function is used only for loads, not stores, so it's safe to |
+ // return an cp-based operand (the write barrier cannot be allowed to |
+ // destroy the cp register). |
+ return ContextOperand(context, slot->index()); |
} |
@@ -2004,34 +2009,60 @@ |
: Builtins::StoreIC_Initialize)); |
EmitCallIC(ic, RelocInfo::CODE_TARGET_CONTEXT); |
- } else if (var->mode() != Variable::CONST || op == Token::INIT_CONST) { |
- // Perform the assignment for non-const variables and for initialization |
- // of const variables. Const assignments are simply skipped. |
- Label done; |
+ } else if (op == Token::INIT_CONST) { |
+ // Like var declarations, const declarations are hoisted to function |
+ // scope. However, unlike var initializers, const initializers are able |
+ // to drill a hole to that function context, even from inside a 'with' |
+ // context. We thus bypass the normal static scope lookup. |
Slot* slot = var->AsSlot(); |
+ Label skip; |
switch (slot->type()) { |
case Slot::PARAMETER: |
+ // No const parameters. |
+ UNREACHABLE(); |
+ break; |
case Slot::LOCAL: |
- if (op == Token::INIT_CONST) { |
- // Detect const reinitialization by checking for the hole value. |
- __ ldr(r1, MemOperand(fp, SlotOffset(slot))); |
- __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
- __ cmp(r1, ip); |
- __ b(ne, &done); |
- } |
+ // Detect const reinitialization by checking for the hole value. |
+ __ ldr(r1, MemOperand(fp, SlotOffset(slot))); |
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
+ __ cmp(r1, ip); |
+ __ b(ne, &skip); |
+ __ str(result_register(), MemOperand(fp, SlotOffset(slot))); |
+ break; |
+ case Slot::CONTEXT: { |
+ __ ldr(r1, ContextOperand(cp, Context::FCONTEXT_INDEX)); |
+ __ ldr(r2, ContextOperand(r1, slot->index())); |
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
+ __ cmp(r2, ip); |
+ __ b(ne, &skip); |
+ __ str(r0, ContextOperand(r1, slot->index())); |
+ int offset = Context::SlotOffset(slot->index()); |
+ __ mov(r3, r0); // Preserve the stored value in r0. |
+ __ RecordWrite(r1, Operand(offset), r3, r2); |
+ break; |
+ } |
+ case Slot::LOOKUP: |
+ __ push(r0); |
+ __ mov(r0, Operand(slot->var()->name())); |
+ __ Push(cp, r0); // Context and name. |
+ __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); |
+ break; |
+ } |
+ __ bind(&skip); |
+ |
+ } else if (var->mode() != Variable::CONST) { |
+ // Perform the assignment for non-const variables. Const assignments |
+ // are simply skipped. |
+ Slot* slot = var->AsSlot(); |
+ switch (slot->type()) { |
+ case Slot::PARAMETER: |
+ case Slot::LOCAL: |
// Perform the assignment. |
__ str(result_register(), MemOperand(fp, SlotOffset(slot))); |
break; |
case Slot::CONTEXT: { |
MemOperand target = EmitSlotSearch(slot, r1); |
- if (op == Token::INIT_CONST) { |
- // Detect const reinitialization by checking for the hole value. |
- __ ldr(r2, target); |
- __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); |
- __ cmp(r2, ip); |
- __ b(ne, &done); |
- } |
// Perform the assignment and issue the write barrier. |
__ str(result_register(), target); |
// RecordWrite may destroy all its register arguments. |
@@ -2042,20 +2073,13 @@ |
} |
case Slot::LOOKUP: |
- // Call the runtime for the assignment. The runtime will ignore |
- // const reinitialization. |
+ // Call the runtime for the assignment. |
__ push(r0); // Value. |
__ mov(r0, Operand(slot->var()->name())); |
__ Push(cp, r0); // Context and name. |
- if (op == Token::INIT_CONST) { |
- // The runtime will ignore const redeclaration. |
- __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); |
- } else { |
- __ CallRuntime(Runtime::kStoreContextSlot, 3); |
- } |
+ __ CallRuntime(Runtime::kStoreContextSlot, 3); |
break; |
} |
- __ bind(&done); |
} |
} |