Optimize loads from variables that might be shadowed by variables
introduced by eval. In the cases where calls to eval have not introduced any variables, we do not need to perform a runtime call. Instead, we verify that the context extension objects have not been created and perform a direct load. Not implemented for ARM yet and the scope resolution code could use some better abstractions. I'd like to do that in a separate changelist. Review URL: http://codereview.chromium.org/20419 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@1298 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
parent
98208125cb
commit
a09832ab76
@ -1105,7 +1105,7 @@ void CodeGenerator::VisitDeclaration(Declaration* node) {
|
||||
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()));
|
||||
@ -1983,7 +1983,7 @@ void CodeGenerator::VisitConditional(Conditional* node) {
|
||||
|
||||
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);
|
||||
@ -2000,7 +2000,7 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
|
||||
} 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));
|
||||
@ -3348,7 +3348,7 @@ void Reference::SetValue(InitState init_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(cp);
|
||||
@ -3380,7 +3380,7 @@ void Reference::SetValue(InitState init_state) {
|
||||
frame->Push(r0);
|
||||
|
||||
} else {
|
||||
ASSERT(slot->var()->mode() != Variable::DYNAMIC);
|
||||
ASSERT(!slot->var()->is_dynamic());
|
||||
|
||||
Label exit;
|
||||
if (init_state == CONST_INIT) {
|
||||
|
@ -422,7 +422,7 @@ Operand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
|
||||
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.
|
||||
@ -450,6 +450,32 @@ Operand CodeGenerator::SlotOperand(Slot* slot, Register tmp) {
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
@ -1404,7 +1430,7 @@ void CodeGenerator::VisitDeclaration(Declaration* node) {
|
||||
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()));
|
||||
@ -2323,23 +2349,44 @@ void CodeGenerator::VisitConditional(Conditional* node) {
|
||||
|
||||
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.
|
||||
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);
|
||||
}
|
||||
|
||||
__ 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'
|
||||
@ -2359,6 +2406,48 @@ void CodeGenerator::LoadFromSlot(Slot* slot, TypeofState typeof_state) {
|
||||
}
|
||||
|
||||
|
||||
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());
|
||||
@ -4013,7 +4102,7 @@ void Reference::SetValue(InitState init_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);
|
||||
@ -4045,7 +4134,7 @@ void Reference::SetValue(InitState init_state) {
|
||||
frame->Push(eax);
|
||||
|
||||
} else {
|
||||
ASSERT(slot->var()->mode() != Variable::DYNAMIC);
|
||||
ASSERT(!slot->var()->is_dynamic());
|
||||
|
||||
Label exit;
|
||||
if (init_state == CONST_INIT) {
|
||||
|
@ -267,6 +267,9 @@ class CodeGenerator: public AstVisitor {
|
||||
|
||||
Operand SlotOperand(Slot* slot, Register tmp);
|
||||
|
||||
Operand ContextSlotOperandCheckExtensions(Slot* slot,
|
||||
Register tmp,
|
||||
Label* slow);
|
||||
|
||||
// Expressions
|
||||
Operand GlobalObject() const {
|
||||
@ -284,6 +287,10 @@ class CodeGenerator: public AstVisitor {
|
||||
|
||||
// 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'
|
||||
|
@ -134,10 +134,12 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
|
||||
// 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;
|
||||
|
@ -139,6 +139,7 @@ Scope::Scope(Scope* outer_scope, Type type)
|
||||
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) {
|
||||
@ -312,7 +313,8 @@ void Scope::AllocateVariables() {
|
||||
// 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;
|
||||
@ -442,6 +444,7 @@ void Scope::Print(int n) {
|
||||
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, "// ");
|
||||
@ -482,20 +485,18 @@ void Scope::Print(int n) {
|
||||
#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.
|
||||
@ -511,7 +512,9 @@ Variable* Scope::NonLocal(Handle<String> name) {
|
||||
// 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
|
||||
@ -542,7 +545,7 @@ Variable* Scope::LookupRecursive(Handle<String> name, bool inner_lookup) {
|
||||
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
|
||||
@ -563,8 +566,10 @@ Variable* Scope::LookupRecursive(Handle<String> name, bool inner_lookup) {
|
||||
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;
|
||||
}
|
||||
@ -578,7 +583,8 @@ void Scope::ResolveVariable(Scope* global_scope, VariableProxy* proxy) {
|
||||
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
|
||||
@ -587,7 +593,7 @@ void Scope::ResolveVariable(Scope* global_scope, VariableProxy* proxy) {
|
||||
// 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.
|
||||
@ -601,11 +607,22 @@ void Scope::ResolveVariable(Scope* global_scope, VariableProxy* proxy) {
|
||||
// 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.
|
||||
@ -643,15 +660,21 @@ void Scope::ResolveVariablesRecursively(Scope* global_scope) {
|
||||
}
|
||||
|
||||
|
||||
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_) {
|
||||
|
15
src/scopes.h
15
src/scopes.h
@ -166,10 +166,13 @@ class Scope: public ZoneObject {
|
||||
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.
|
||||
|
||||
@ -290,6 +293,7 @@ class Scope: public ZoneObject {
|
||||
// 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.
|
||||
@ -298,15 +302,18 @@ class Scope: public ZoneObject {
|
||||
|
||||
// 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.
|
||||
|
@ -110,6 +110,8 @@ const char* Variable::Mode2String(Mode mode) {
|
||||
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";
|
||||
}
|
||||
@ -143,6 +145,7 @@ Variable::Variable(Scope* scope,
|
||||
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
|
||||
@ -156,5 +159,4 @@ bool Variable::is_global() const {
|
||||
return mode_ != TEMPORARY && scope_ != NULL && scope_->is_global_scope();
|
||||
}
|
||||
|
||||
|
||||
} } // namespace v8::internal
|
||||
|
@ -113,13 +113,27 @@ class Variable: public ZoneObject {
|
||||
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
|
||||
@ -150,9 +164,24 @@ class Variable: public ZoneObject {
|
||||
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;
|
||||
|
||||
@ -168,6 +197,8 @@ class Variable: public ZoneObject {
|
||||
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
|
||||
|
59
test/mjsunit/global-load-from-eval-in-with.js
Normal file
59
test/mjsunit/global-load-from-eval-in-with.js
Normal file
@ -0,0 +1,59 @@
|
||||
// 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();");
|
||||
|
37
test/mjsunit/local-load-from-eval.js
Normal file
37
test/mjsunit/local-load-from-eval.js
Normal file
@ -0,0 +1,37 @@
|
||||
// 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) })();");
|
||||
|
85
test/mjsunit/property-load-across-eval.js
Normal file
85
test/mjsunit/property-load-across-eval.js
Normal file
@ -0,0 +1,85 @@
|
||||
// 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();
|
Loading…
Reference in New Issue
Block a user