frame_->SpillAll();
Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
// Only generate the fast case for locals that rewrite to slots.
- // This rules out argument loads.
+ // This rules out argument loads because eval forces arguments
+ // access to be through the arguments object.
if (potential_slot != NULL) {
__ ldr(r0,
ContextSlotOperandCheckExtensions(potential_slot,
} else if (var != NULL && var->slot() != NULL &&
var->slot()->type() == Slot::LOOKUP) {
// ----------------------------------
- // JavaScript example: 'with (obj) foo(1, 2, 3)' // foo is in obj
+ // JavaScript examples:
+ //
+ // with (obj) foo(1, 2, 3) // foo is in obj
+ //
+ // function f() {};
+ // function g() {
+ // eval(...);
+ // f(); // f could be in extension object
+ // }
// ----------------------------------
+ // JumpTargets do not yet support merging frames so the frame must be
+ // spilled when jumping to these targets.
+ JumpTarget slow;
+ JumpTarget done;
+
+ // Generate fast-case code for variables that might be shadowed by
+ // eval-introduced variables. Eval is used a lot without
+ // introducing variables. In those cases, we do not want to
+ // perform a runtime call for all variables in the scope
+ // containing the eval.
+ if (var->mode() == Variable::DYNAMIC_GLOBAL) {
+ LoadFromGlobalSlotCheckExtensions(var->slot(), NOT_INSIDE_TYPEOF, &slow);
+ frame_->EmitPush(r0);
+ LoadGlobalReceiver(r1);
+ done.Jump();
+
+ } else if (var->mode() == Variable::DYNAMIC_LOCAL) {
+ Slot* potential_slot = var->local_if_not_shadowed()->slot();
+ // Only generate the fast case for locals that rewrite to slots.
+ // This rules out argument loads because eval forces arguments
+ // access to be through the arguments object.
+ if (potential_slot != NULL) {
+ __ ldr(r0,
+ ContextSlotOperandCheckExtensions(potential_slot,
+ r1,
+ r2,
+ &slow));
+ if (potential_slot->var()->mode() == Variable::CONST) {
+ __ LoadRoot(ip, Heap::kTheHoleValueRootIndex);
+ __ cmp(r0, ip);
+ __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq);
+ }
+ frame_->EmitPush(r0);
+ LoadGlobalReceiver(r1);
+ done.Jump();
+ }
+ }
+
+ slow.Bind();
// Load the function
frame_->EmitPush(cp);
__ mov(r0, Operand(var->name()));
frame_->EmitPush(r0); // function
frame_->EmitPush(r1); // receiver
- // Call the function.
+ done.Bind();
+ // Call the function. At this point, everything is spilled but the
+ // function and receiver are in r0 and r1.
CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
frame_->EmitPush(r0);
} else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
// Only generate the fast case for locals that rewrite to slots.
- // This rules out argument loads.
+ // This rules out argument loads because eval forces arguments
+ // access to be through the arguments object.
if (potential_slot != NULL) {
// Allocate a fresh register to use as a temp in
// ContextSlotOperandCheckExtensions and to hold the result
} else if (var != NULL && var->slot() != NULL &&
var->slot()->type() == Slot::LOOKUP) {
// ----------------------------------
- // JavaScript example: 'with (obj) foo(1, 2, 3)' // foo is in obj
+ // JavaScript examples:
+ //
+ // with (obj) foo(1, 2, 3) // foo is in obj
+ //
+ // function f() {};
+ // function g() {
+ // eval(...);
+ // f(); // f could be in extension object
+ // }
// ----------------------------------
- // Load the function from the context. Sync the frame so we can
- // push the arguments directly into place.
+ JumpTarget slow;
+ JumpTarget done;
+
+ // Generate fast-case code for variables that might be shadowed by
+ // eval-introduced variables. Eval is used a lot without
+ // introducing variables. In those cases, we do not want to
+ // perform a runtime call for all variables in the scope
+ // containing the eval.
+ Result function;
+ if (var->mode() == Variable::DYNAMIC_GLOBAL) {
+ function = LoadFromGlobalSlotCheckExtensions(var->slot(),
+ NOT_INSIDE_TYPEOF,
+ &slow);
+ frame_->Push(&function);
+ LoadGlobalReceiver();
+ done.Jump();
+
+ } else if (var->mode() == Variable::DYNAMIC_LOCAL) {
+ Slot* potential_slot = var->local_if_not_shadowed()->slot();
+ // Only generate the fast case for locals that rewrite to slots.
+ // This rules out argument loads because eval forces arguments
+ // access to be through the arguments object.
+ if (potential_slot != NULL) {
+ // Allocate a fresh register to use as a temp in
+ // ContextSlotOperandCheckExtensions and to hold the result
+ // value.
+ function = allocator()->Allocate();
+ ASSERT(function.is_valid());
+ __ mov(function.reg(),
+ ContextSlotOperandCheckExtensions(potential_slot,
+ function,
+ &slow));
+ JumpTarget push_function_and_receiver;
+ if (potential_slot->var()->mode() == Variable::CONST) {
+ __ cmp(function.reg(), Factory::the_hole_value());
+ push_function_and_receiver.Branch(not_equal, &function);
+ __ mov(function.reg(), Factory::undefined_value());
+ }
+ push_function_and_receiver.Bind(&function);
+ frame_->Push(&function);
+ LoadGlobalReceiver();
+ done.Jump();
+ }
+ }
+
+ slow.Bind();
+ // Enter the runtime system to load the function from the context.
+ // Sync the frame so we can push the arguments directly into
+ // place.
frame_->SyncRange(0, frame_->element_count() - 1);
frame_->EmitPush(esi);
frame_->EmitPush(Immediate(var->name()));
ASSERT(!allocator()->is_used(edx));
frame_->EmitPush(edx);
+ done.Bind();
// Call the function.
CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
} else if (var != NULL && var->slot() != NULL &&
var->slot()->type() == Slot::LOOKUP) {
// ----------------------------------
- // JavaScript example: 'with (obj) foo(1, 2, 3)' // foo is in obj
+ // JavaScript examples:
+ //
+ // with (obj) foo(1, 2, 3) // foo is in obj
+ //
+ // function f() {};
+ // function g() {
+ // eval(...);
+ // f(); // f could be in extension object
+ // }
// ----------------------------------
+ JumpTarget slow;
+ JumpTarget done;
+
+ // Generate fast-case code for variables that might be shadowed by
+ // eval-introduced variables. Eval is used a lot without
+ // introducing variables. In those cases, we do not want to
+ // perform a runtime call for all variables in the scope
+ // containing the eval.
+ Result function;
+ if (var->mode() == Variable::DYNAMIC_GLOBAL) {
+ function = LoadFromGlobalSlotCheckExtensions(var->slot(),
+ NOT_INSIDE_TYPEOF,
+ &slow);
+ frame_->Push(&function);
+ LoadGlobalReceiver();
+ done.Jump();
+
+ } else if (var->mode() == Variable::DYNAMIC_LOCAL) {
+ Slot* potential_slot = var->local_if_not_shadowed()->slot();
+ // Only generate the fast case for locals that rewrite to slots.
+ // This rules out argument loads because eval forces arguments
+ // access to be through the arguments object.
+ if (potential_slot != NULL) {
+ // Allocate a fresh register to use as a temp in
+ // ContextSlotOperandCheckExtensions and to hold the result
+ // value.
+ function = allocator()->Allocate();
+ ASSERT(function.is_valid());
+ __ movq(function.reg(),
+ ContextSlotOperandCheckExtensions(potential_slot,
+ function,
+ &slow));
+ JumpTarget push_function_and_receiver;
+ if (potential_slot->var()->mode() == Variable::CONST) {
+ __ CompareRoot(function.reg(), Heap::kTheHoleValueRootIndex);
+ push_function_and_receiver.Branch(not_equal, &function);
+ __ LoadRoot(function.reg(), Heap::kUndefinedValueRootIndex);
+ }
+ push_function_and_receiver.Bind(&function);
+ frame_->Push(&function);
+ LoadGlobalReceiver();
+ done.Jump();
+ }
+ }
+
+ slow.Bind();
// Load the function from the context. Sync the frame so we can
// push the arguments directly into place.
frame_->SyncRange(0, frame_->element_count() - 1);
ASSERT(!allocator()->is_used(rdx));
frame_->EmitPush(rdx);
+ done.Bind();
// Call the function.
CallWithArguments(args, NO_CALL_FUNCTION_FLAGS, node->position());
} else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
// Only generate the fast case for locals that rewrite to slots.
- // This rules out argument loads.
+ // This rules out argument loads because eval forces arguments
+ // access to be through the arguments object.
if (potential_slot != NULL) {
// Allocate a fresh register to use as a temp in
// ContextSlotOperandCheckExtensions and to hold the result
// Tests loading of properties across eval calls.
var x = 1;
+function global_function() { return 'global'; }
+const const_uninitialized;
+const const_initialized = function() { return "const_global"; }
// Test loading across an eval call that does not shadow variables.
function testNoShadowing() {
var y = 2;
+ function local_function() { return 'local'; }
+ const local_const_uninitialized;
+ const local_const_initialized = function() { return "const_local"; }
function f() {
eval('1');
assertEquals(1, x);
assertEquals(2, y);
+ assertEquals('global', global_function());
+ assertEquals('local', local_function());
+ try {
+ const_uninitialized();
+ assertUnreachable();
+ } catch(e) {
+ // Ignore.
+ }
+ assertEquals('const_global', const_initialized());
+ try {
+ local_const_uninitialized();
+ assertUnreachable();
+ } catch(e) {
+ // Ignore.
+ }
+ assertEquals('const_local', local_const_initialized());
function g() {
assertEquals(1, x);
assertEquals(2, y);
+ assertEquals('global', global_function());
+ assertEquals('local', local_function());
+ try {
+ const_uninitialized();
+ assertUnreachable();
+ } catch(e) {
+ // Ignore.
+ }
+ assertEquals('const_global', const_initialized());
+ try {
+ local_const_uninitialized();
+ assertUnreachable();
+ } catch(e) {
+ // Ignore.
+ }
+ assertEquals('const_local', local_const_initialized());
}
g();
}
// Test loading across eval calls that do not shadow variables.
function testNoShadowing2() {
var y = 2;
+ function local_function() { return 'local'; }
eval('1');
function f() {
eval('1');
assertEquals(1, x);
assertEquals(2, y);
+ assertEquals('global', global_function());
+ assertEquals('local', local_function());
function g() {
assertEquals(1, x);
assertEquals(2, y);
+ assertEquals('global', global_function());
+ assertEquals('local', local_function());
}
g();
}
// Test loading across an eval call that shadows variables.
function testShadowing() {
var y = 2;
+ function local_function() { return 'local'; }
function f() {
eval('var x = 3; var y = 4;');
assertEquals(3, x);
assertEquals(4, y);
+ eval('function local_function() { return "new_local"; }');
+ eval('function global_function() { return "new_nonglobal"; }');
+ assertEquals('new_nonglobal', global_function());
+ assertEquals('new_local', local_function());
function g() {
assertEquals(3, x);
assertEquals(4, y);
+ assertEquals('new_nonglobal', global_function());
+ assertEquals('new_local', local_function());
}
g();
}