if (slot != NULL && slot->type() == Slot::LOOKUP) {
// Variables with a "LOOKUP" slot were introduced as non-locals
// during variable resolution and must have mode DYNAMIC.
- ASSERT(var->mode() == Variable::DYNAMIC);
+ ASSERT(var->is_dynamic());
// For now, just do a runtime call.
frame_->Push(cp);
__ mov(r0, Operand(var->name()));
void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
if (slot->type() == Slot::LOOKUP) {
- ASSERT(slot->var()->mode() == Variable::DYNAMIC);
+ ASSERT(slot->var()->is_dynamic());
// For now, just do a runtime call.
frame_->Push(cp);
} else {
// Note: We would like to keep the assert below, but it fires because of
// some nasty code in LoadTypeofExpression() which should be removed...
- // ASSERT(slot->var()->mode() != Variable::DYNAMIC);
+ // ASSERT(!slot->var()->is_dynamic());
// Special handling for locals allocated in registers.
__ ldr(r0, SlotOperand(slot, r2));
Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
ASSERT(slot != NULL);
if (slot->type() == Slot::LOOKUP) {
- ASSERT(slot->var()->mode() == Variable::DYNAMIC);
+ ASSERT(slot->var()->is_dynamic());
// For now, just do a runtime call.
frame->Push(cp);
frame->Push(r0);
} else {
- ASSERT(slot->var()->mode() != Variable::DYNAMIC);
+ ASSERT(!slot->var()->is_dynamic());
Label exit;
if (init_state == CONST_INIT) {
ASSERT(!tmp.is(esi)); // do not overwrite context register
Register context = esi;
int chain_length = scope()->ContextChainLength(slot->var()->scope());
- for (int i = chain_length; i-- > 0;) {
+ for (int i = 0; i < chain_length; i++) {
// Load the closure.
// (All contexts, even 'with' contexts, have a closure,
// and it is the same for all contexts inside a function.
}
+Operand CodeGenerator::ContextSlotOperandCheckExtensions(Slot* slot,
+ Register tmp,
+ Label* slow) {
+ ASSERT(slot->type() == Slot::CONTEXT);
+ int index = slot->index();
+ __ mov(tmp, Operand(esi));
+ for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_eval()) {
+ // Check that extension is NULL.
+ __ cmp(ContextOperand(tmp, Context::EXTENSION_INDEX), Immediate(0));
+ __ j(not_equal, slow, not_taken);
+ }
+ __ mov(tmp, ContextOperand(tmp, Context::CLOSURE_INDEX));
+ __ mov(tmp, FieldOperand(tmp, JSFunction::kContextOffset));
+ }
+ }
+ // Check that last extension is NULL.
+ __ cmp(ContextOperand(tmp, Context::EXTENSION_INDEX), Immediate(0));
+ __ j(not_equal, slow, not_taken);
+ __ mov(tmp, ContextOperand(tmp, Context::FCONTEXT_INDEX));
+ return ContextOperand(tmp, index);
+}
+
+
+
// Loads a value on TOS. If it is a boolean value, the result may have been
// (partially) translated into branches, or it may have set the condition
// code register. If force_cc is set, the value is forced to set the
if (slot != NULL && slot->type() == Slot::LOOKUP) {
// Variables with a "LOOKUP" slot were introduced as non-locals
// during variable resolution and must have mode DYNAMIC.
- ASSERT(var->mode() == Variable::DYNAMIC);
+ ASSERT(var->is_dynamic());
// For now, just do a runtime call.
frame_->Push(esi);
frame_->Push(Immediate(var->name()));
void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
if (slot->type() == Slot::LOOKUP) {
- ASSERT(slot->var()->mode() == Variable::DYNAMIC);
+ ASSERT(slot->var()->is_dynamic());
+
+ Label slow, 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 (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) {
+ LoadFromGlobalSlotCheckExtensions(slot, typeof_state, ebx, &slow);
+ __ jmp(&done);
+
+ } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) {
+ Slot* potential_slot = slot->var()->local_if_not_shadowed()->slot();
+ __ mov(eax,
+ ContextSlotOperandCheckExtensions(potential_slot,
+ ebx,
+ &slow));
+ __ jmp(&done);
+ }
- // For now, just do a runtime call.
+ __ bind(&slow);
frame_->Push(esi);
frame_->Push(Immediate(slot->var()->name()));
-
if (typeof_state == INSIDE_TYPEOF) {
__ CallRuntime(Runtime::kLoadContextSlotNoReferenceError, 2);
} else {
__ CallRuntime(Runtime::kLoadContextSlot, 2);
}
+
+ __ bind(&done);
frame_->Push(eax);
} else {
// Note: We would like to keep the assert below, but it fires because of
// some nasty code in LoadTypeofExpression() which should be removed...
- // ASSERT(slot->var()->mode() != Variable::DYNAMIC);
+ // ASSERT(!slot->var()->is_dynamic());
if (slot->var()->mode() == Variable::CONST) {
// Const slots may contain 'the hole' value (the constant hasn't been
// initialized yet) which needs to be converted into the 'undefined'
}
+void CodeGenerator::LoadFromGlobalSlotCheckExtensions(Slot* slot,
+ TypeofState typeof_state,
+ Register tmp,
+ Label* slow) {
+ // Check that no extension objects have been created by calls to
+ // eval from the current scope to the global scope.
+ __ mov(tmp, Operand(esi));
+ for (Scope* s = scope(); s != NULL; s = s->outer_scope()) {
+ if (s->num_heap_slots() > 0) {
+ if (s->calls_eval()) {
+ // Check that extension is NULL.
+ __ cmp(ContextOperand(tmp, Context::EXTENSION_INDEX), Immediate(0));
+ __ j(not_equal, slow, not_taken);
+ }
+ // Load next context in chain.
+ __ mov(tmp, ContextOperand(tmp, Context::CLOSURE_INDEX));
+ __ mov(tmp, FieldOperand(tmp, JSFunction::kContextOffset));
+ }
+ // If no outer scope calls eval, we do not need to check more
+ // context extensions.
+ if (!s->outer_scope_calls_eval()) break;
+ }
+
+ // All extension objects were empty and it is safe to use a global
+ // load IC call.
+ Handle<Code> ic(Builtins::builtin(Builtins::LoadIC_Initialize));
+ // Load the global object.
+ LoadGlobal();
+ // Setup the name register.
+ __ mov(ecx, slot->var()->name());
+ // Call IC stub.
+ if (typeof_state == INSIDE_TYPEOF) {
+ __ call(ic, RelocInfo::CODE_TARGET);
+ } else {
+ __ call(ic, RelocInfo::CODE_TARGET_CONTEXT);
+ }
+
+ // Pop the global object. The result is in eax.
+ frame_->Pop();
+}
+
+
void CodeGenerator::VisitSlot(Slot* node) {
Comment cmnt(masm_, "[ Slot");
LoadFromSlot(node, typeof_state());
Slot* slot = expression_->AsVariableProxy()->AsVariable()->slot();
ASSERT(slot != NULL);
if (slot->type() == Slot::LOOKUP) {
- ASSERT(slot->var()->mode() == Variable::DYNAMIC);
+ ASSERT(slot->var()->is_dynamic());
// For now, just do a runtime call.
frame->Push(esi);
frame->Push(eax);
} else {
- ASSERT(slot->var()->mode() != Variable::DYNAMIC);
+ ASSERT(!slot->var()->is_dynamic());
Label exit;
if (init_state == CONST_INIT) {
Operand SlotOperand(Slot* slot, Register tmp);
+ Operand ContextSlotOperandCheckExtensions(Slot* slot,
+ Register tmp,
+ Label* slow);
// Expressions
Operand GlobalObject() const {
// Read a value from a slot and leave it on top of the expression stack.
void LoadFromSlot(Slot* slot, TypeofState typeof_state);
+ void LoadFromGlobalSlotCheckExtensions(Slot* slot,
+ TypeofState typeof_state,
+ Register tmp,
+ Label* slow);
// Special code for typeof expressions: Unfortunately, we must
// be careful when loading the expression in 'typeof'
// declared variables that were introduced through declaration nodes)
// must not appear here.
switch (mode) {
- case Variable::INTERNAL : // fall through
- case Variable::VAR : *attributes = NONE; break;
- case Variable::CONST : *attributes = READ_ONLY; break;
- case Variable::DYNAMIC : UNREACHABLE(); break;
+ case Variable::INTERNAL: // fall through
+ case Variable::VAR: *attributes = NONE; break;
+ case Variable::CONST: *attributes = READ_ONLY; break;
+ case Variable::DYNAMIC: UNREACHABLE(); break;
+ case Variable::DYNAMIC_GLOBAL: UNREACHABLE(); break;
+ case Variable::DYNAMIC_LOCAL: UNREACHABLE(); break;
case Variable::TEMPORARY: UNREACHABLE(); break;
}
return context;
scope_calls_eval_(false),
outer_scope_calls_eval_(false),
inner_scope_calls_eval_(false),
+ outer_scope_is_eval_scope_(false),
force_eager_compilation_(false),
num_stack_slots_(0),
num_heap_slots_(0) {
// and assume they may invoke eval themselves. Eventually we could capture
// this information in the ScopeInfo and then use it here (by traversing
// the call chain stack, at compile time).
- PropagateScopeInfo(is_eval_scope());
+ bool eval_scope = is_eval_scope();
+ PropagateScopeInfo(eval_scope, eval_scope);
// 2) Resolve variables.
Scope* global_scope = NULL;
if (scope_calls_eval_) Indent(n1, "// scope calls 'eval'\n");
if (outer_scope_calls_eval_) Indent(n1, "// outer scope calls 'eval'\n");
if (inner_scope_calls_eval_) Indent(n1, "// inner scope calls 'eval'\n");
+ if (outer_scope_is_eval_scope_) Indent(n1, "// outer scope is 'eval' scope\n");
if (num_stack_slots_ > 0) { Indent(n1, "// ");
PrintF("%d stack slots\n", num_stack_slots_); }
if (num_heap_slots_ > 0) { Indent(n1, "// ");
#endif // DEBUG
-Variable* Scope::NonLocal(Handle<String> name) {
- // Space optimization: reuse existing non-local with the same name.
+Variable* Scope::NonLocal(Handle<String> name, Variable::Mode mode) {
+ // Space optimization: reuse existing non-local with the same name
+ // and mode.
for (int i = 0; i < nonlocals_.length(); i++) {
Variable* var = nonlocals_[i];
- if (var->name().is_identical_to(name)) {
- ASSERT(var->mode() == Variable::DYNAMIC);
+ if (var->name().is_identical_to(name) && var->mode() == mode) {
return var;
}
}
- // Otherwise create a new new-local and add it to the list.
- Variable* var = new Variable(
- NULL /* we don't know the scope */,
- name, Variable::DYNAMIC, true, false);
+ // Otherwise create a new non-local and add it to the list.
+ Variable* var = new Variable(NULL, name, mode, true, false);
nonlocals_.Add(var);
// Allocate it by giving it a dynamic lookup.
// because the variable is just a guess (and may be shadowed by another
// variable that is introduced dynamically via an 'eval' call or a 'with'
// statement).
-Variable* Scope::LookupRecursive(Handle<String> name, bool inner_lookup) {
+Variable* Scope::LookupRecursive(Handle<String> name,
+ bool inner_lookup,
+ Variable** invalidated_local) {
// If we find a variable, but the current scope calls 'eval', the found
// variable may not be the correct one (the 'eval' may introduce a
// property with the same name). In that case, remember that the variable
var = function_;
} else if (outer_scope_ != NULL) {
- var = outer_scope_->LookupRecursive(name, true /* inner lookup */);
+ var = outer_scope_->LookupRecursive(name, true, invalidated_local);
// We may have found a variable in an outer scope. However, if
// the current scope is inside a 'with', the actual variable may
// be a property introduced via the 'with' statement. Then, the
var->is_accessed_from_inner_scope_ = true;
// If the variable we have found is just a guess, invalidate the result.
- if (guess)
+ if (guess) {
+ *invalidated_local = var;
var = NULL;
+ }
return var;
}
if (proxy->var() != NULL) return;
// Otherwise, try to resolve the variable.
- Variable* var = LookupRecursive(proxy->name(), false);
+ Variable* invalidated_local = NULL;
+ Variable* var = LookupRecursive(proxy->name(), false, &invalidated_local);
if (proxy->inside_with()) {
// If we are inside a local 'with' statement, all bets are off
// Note that we must do a lookup anyway, because if we find one,
// we must mark that variable as potentially accessed from this
// inner scope (the property may not be in the 'with' object).
- var = NonLocal(proxy->name());
+ var = NonLocal(proxy->name(), Variable::DYNAMIC);
} else {
// We are not inside a local 'with' statement.
// or we don't know about the outer scope (because we are
// in an eval scope).
if (!is_global_scope() &&
- (is_eval_scope() || outer_scope_calls_eval_ ||
- scope_calls_eval_ || scope_inside_with_)) {
- // We must look up the variable at runtime, and we don't
- // know anything else.
- var = NonLocal(proxy->name());
+ (scope_inside_with_ || outer_scope_is_eval_scope_)) {
+ // If we are inside a with statement or the code is executed
+ // using eval, we give up and look up the variable at runtime.
+ var = NonLocal(proxy->name(), Variable::DYNAMIC);
+
+ } else if (!is_global_scope() &&
+ (scope_calls_eval_ || outer_scope_calls_eval_)) {
+ // If the code is not executed using eval and there are no
+ // with scopes, either we have a local or a global variable
+ // that might be shadowed by an eval-introduced variable.
+ if (invalidated_local != NULL) {
+ var = NonLocal(proxy->name(), Variable::DYNAMIC_LOCAL);
+ var->set_local_if_not_shadowed(invalidated_local);
+ } else {
+ var = NonLocal(proxy->name(), Variable::DYNAMIC_GLOBAL);
+ }
} else {
// We must have a global variable.
}
-bool Scope::PropagateScopeInfo(bool outer_scope_calls_eval) {
+bool Scope::PropagateScopeInfo(bool outer_scope_calls_eval,
+ bool outer_scope_is_eval_scope) {
if (outer_scope_calls_eval) {
outer_scope_calls_eval_ = true;
}
- bool b = scope_calls_eval_ || outer_scope_calls_eval_;
+ if (outer_scope_is_eval_scope) {
+ outer_scope_is_eval_scope_ = true;
+ }
+
+ bool calls_eval = scope_calls_eval_ || outer_scope_calls_eval_;
+ bool is_eval = is_eval_scope() || outer_scope_is_eval_scope_;
for (int i = 0; i < inner_scopes_.length(); i++) {
Scope* inner_scope = inner_scopes_[i];
- if (inner_scope->PropagateScopeInfo(b)) {
+ if (inner_scope->PropagateScopeInfo(calls_eval, is_eval)) {
inner_scope_calls_eval_ = true;
}
if (inner_scope->force_eager_compilation_) {
bool is_function_scope() const { return type_ == FUNCTION_SCOPE; }
bool is_global_scope() const { return type_ == GLOBAL_SCOPE; }
+ // Information about which scopes calls eval.
+ bool calls_eval() const { return scope_calls_eval_; }
+ bool outer_scope_calls_eval() const { return outer_scope_calls_eval_; }
+
// The scope immediately surrounding this scope, or NULL.
Scope* outer_scope() const { return outer_scope_; }
-
// ---------------------------------------------------------------------------
// Accessors.
// Computed via PropagateScopeInfo.
bool outer_scope_calls_eval_;
bool inner_scope_calls_eval_;
+ bool outer_scope_is_eval_scope_;
bool force_eager_compilation_;
// Computed via AllocateVariables; function scopes only.
// Create a non-local variable with a given name.
// These variables are looked up dynamically at runtime.
- Variable* NonLocal(Handle<String> name);
+ Variable* NonLocal(Handle<String> name, Variable::Mode mode);
// Variable resolution.
- Variable* LookupRecursive(Handle<String> name, bool inner_lookup);
+ Variable* LookupRecursive(Handle<String> name,
+ bool inner_lookup,
+ Variable** invalidated_local);
void ResolveVariable(Scope* global_scope, VariableProxy* proxy);
void ResolveVariablesRecursively(Scope* global_scope);
// Scope analysis.
- bool PropagateScopeInfo(bool outer_scope_calls_eval);
+ bool PropagateScopeInfo(bool outer_scope_calls_eval,
+ bool outer_scope_is_eval_scope);
bool HasTrivialContext() const;
// Predicates.
case VAR: return "VAR";
case CONST: return "CONST";
case DYNAMIC: return "DYNAMIC";
+ case DYNAMIC_GLOBAL: return "DYNAMIC_GLOBAL";
+ case DYNAMIC_LOCAL: return "DYNAMIC_LOCAL";
case INTERNAL: return "INTERNAL";
case TEMPORARY: return "TEMPORARY";
}
mode_(mode),
is_valid_LHS_(is_valid_LHS),
is_this_(is_this),
+ local_if_not_shadowed_(NULL),
is_accessed_from_inner_scope_(false),
rewrite_(NULL) {
// names must be canonicalized for fast equality checks
return mode_ != TEMPORARY && scope_ != NULL && scope_->is_global_scope();
}
-
} } // namespace v8::internal
enum Mode {
// User declared variables:
VAR, // declared via 'var', and 'function' declarations
+
CONST, // declared via 'const' declarations
// Variables introduced by the compiler:
- DYNAMIC, // always require dynamic lookup (we don't know the declaration)
- INTERNAL, // like VAR, but not user-visible (may or may not be in a
- // context)
- TEMPORARY // temporary variables (not user-visible), never in a context
+ DYNAMIC, // always require dynamic lookup (we don't know
+ // the declaration)
+
+ DYNAMIC_GLOBAL, // requires dynamic lookup, but we know that the
+ // variable is global unless it has been shadowed
+ // by an eval-introduced variable
+
+ DYNAMIC_LOCAL, // requires dynamic lookup, but we know that the
+ // variable is local and where it is unless it
+ // has been shadowed by an eval-introduced
+ // variable
+
+ INTERNAL, // like VAR, but not user-visible (may or may not
+ // be in a context)
+
+ TEMPORARY // temporary variables (not user-visible), never
+ // in a context
};
// Printing support
return !is_this() && name().is_identical_to(n);
}
+ bool is_dynamic() const {
+ return (mode_ == DYNAMIC ||
+ mode_ == DYNAMIC_GLOBAL ||
+ mode_ == DYNAMIC_LOCAL);
+ }
+
bool is_global() const;
bool is_this() const { return is_this_; }
+ Variable* local_if_not_shadowed() const {
+ ASSERT(mode_ == DYNAMIC_LOCAL && local_if_not_shadowed_ != NULL);
+ return local_if_not_shadowed_;
+ }
+
+ void set_local_if_not_shadowed(Variable* local) {
+ local_if_not_shadowed_ = local;
+ }
+
Expression* rewrite() const { return rewrite_; }
Slot* slot() const;
bool is_valid_LHS_;
bool is_this_;
+ Variable* local_if_not_shadowed_;
+
// Usage info.
bool is_accessed_from_inner_scope_; // set by variable resolver
UseCount var_uses_; // uses of the variable value
--- /dev/null
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Tests global loads from eval inside of a with statement.
+
+var x = 27;
+
+function test(obj, source) {
+ with (obj) {
+ eval(source);
+ }
+}
+
+// Test shadowing in eval scope.
+test({ x: 42 }, "assertEquals(42, x)");
+test({ y: 42 }, "assertEquals(27, x)");
+
+// Test shadowing in local scope inside an eval scope.
+test({ x: 42 }, "function f() { assertEquals(42, x) }; f();");
+test({ y: 42 }, "function f() { assertEquals(27, x) }; f();");
+
+// Test shadowing in local scope inside an eval scope. Deeper nesting
+// this time.
+test({ x: 42 }, "function f() { function g() { assertEquals(42, x) }; g() }; f();");
+test({ y: 42 }, "function f() { function g() { assertEquals(27, x) }; g() }; f();");
+
+// Test shadowing in local scope inside an eval scope with eval calls in the eval scopes.
+test({ x: 42 }, "function f() { eval('1'); assertEquals(42, x) }; f();");
+test({ y: 42 }, "function f() { eval('1'); assertEquals(27, x) }; f();");
+
+// Test shadowing in local scope inside an eval scope with eval calls
+// in the eval scopes. Deeper nesting this time.
+test({ x: 42 }, "function f() { function g() { eval('1'); assertEquals(42, x) }; g() }; f();");
+test({ y: 42 }, "function f() { function g() { eval('1'); assertEquals(27, x) }; g() }; f();");
+
--- /dev/null
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Tests loads of local properties from eval.
+
+function test(source) {
+ var x = 27;
+ eval(source);
+}
+
+test("assertEquals(27, x);");
+test("(function() { assertEquals(27, x) })();");
+
--- /dev/null
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// Tests loading of properties across eval calls.
+
+var x = 1;
+
+// Test loading across an eval call that does not shadow variables.
+function testNoShadowing() {
+ var y = 2;
+ function f() {
+ eval('1');
+ assertEquals(1, x);
+ assertEquals(2, y);
+ function g() {
+ assertEquals(1, x);
+ assertEquals(2, y);
+ }
+ g();
+ }
+ f();
+}
+
+testNoShadowing();
+
+// Test loading across eval calls that do not shadow variables.
+function testNoShadowing2() {
+ var y = 2;
+ eval('1');
+ function f() {
+ eval('1');
+ assertEquals(1, x);
+ assertEquals(2, y);
+ function g() {
+ assertEquals(1, x);
+ assertEquals(2, y);
+ }
+ g();
+ }
+ f();
+}
+
+testNoShadowing2();
+
+// Test loading across an eval call that shadows variables.
+function testShadowing() {
+ var y = 2;
+ function f() {
+ eval('var x = 3; var y = 4;');
+ assertEquals(3, x);
+ assertEquals(4, y);
+ function g() {
+ assertEquals(3, x);
+ assertEquals(4, y);
+ }
+ g();
+ }
+ f();
+}
+
+testShadowing();