Commit aec46ca5 authored by dslomov's avatar dslomov Committed by Commit bot

Stack allocate lexical locals + hoist stack slots

Review URL: https://codereview.chromium.org/981203003

Cr-Commit-Position: refs/heads/master@{#28008}
parent b3a8f612
...@@ -1022,10 +1022,15 @@ void AstGraphBuilder::VisitBlock(Block* stmt) { ...@@ -1022,10 +1022,15 @@ void AstGraphBuilder::VisitBlock(Block* stmt) {
VisitStatements(stmt->statements()); VisitStatements(stmt->statements());
} else { } else {
// Visit declarations and statements in a block scope. // Visit declarations and statements in a block scope.
Node* context = BuildLocalBlockContext(stmt->scope()); if (stmt->scope()->ContextLocalCount() > 0) {
ContextScope scope(this, stmt->scope(), context); Node* context = BuildLocalBlockContext(stmt->scope());
VisitDeclarations(stmt->scope()->declarations()); ContextScope scope(this, stmt->scope(), context);
VisitStatements(stmt->statements()); VisitDeclarations(stmt->scope()->declarations());
VisitStatements(stmt->statements());
} else {
VisitDeclarations(stmt->scope()->declarations());
VisitStatements(stmt->statements());
}
} }
if (stmt->labels() != NULL) block.EndBlock(); if (stmt->labels() != NULL) block.EndBlock();
} }
...@@ -1480,10 +1485,15 @@ void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) { ...@@ -1480,10 +1485,15 @@ void AstGraphBuilder::VisitClassLiteral(ClassLiteral* expr) {
VisitClassLiteralContents(expr); VisitClassLiteralContents(expr);
} else { } else {
// Visit declarations and class literal in a block scope. // Visit declarations and class literal in a block scope.
Node* context = BuildLocalBlockContext(expr->scope()); if (expr->scope()->ContextLocalCount() > 0) {
ContextScope scope(this, expr->scope(), context); Node* context = BuildLocalBlockContext(expr->scope());
VisitDeclarations(expr->scope()->declarations()); ContextScope scope(this, expr->scope(), context);
VisitClassLiteralContents(expr); VisitDeclarations(expr->scope()->declarations());
VisitClassLiteralContents(expr);
} else {
VisitDeclarations(expr->scope()->declarations());
VisitClassLiteralContents(expr);
}
} }
} }
......
...@@ -1814,22 +1814,27 @@ bool BackEdgeTable::Verify(Isolate* isolate, Code* unoptimized) { ...@@ -1814,22 +1814,27 @@ bool BackEdgeTable::Verify(Isolate* isolate, Code* unoptimized) {
FullCodeGenerator::EnterBlockScopeIfNeeded::EnterBlockScopeIfNeeded( FullCodeGenerator::EnterBlockScopeIfNeeded::EnterBlockScopeIfNeeded(
FullCodeGenerator* codegen, Scope* scope, BailoutId entry_id, FullCodeGenerator* codegen, Scope* scope, BailoutId entry_id,
BailoutId declarations_id, BailoutId exit_id) BailoutId declarations_id, BailoutId exit_id)
: codegen_(codegen), scope_(scope), exit_id_(exit_id) { : codegen_(codegen), exit_id_(exit_id) {
saved_scope_ = codegen_->scope(); saved_scope_ = codegen_->scope();
if (scope == NULL) { if (scope == NULL) {
codegen_->PrepareForBailoutForId(entry_id, NO_REGISTERS); codegen_->PrepareForBailoutForId(entry_id, NO_REGISTERS);
needs_block_context_ = false;
} else { } else {
needs_block_context_ = scope->ContextLocalCount() > 0;
codegen_->scope_ = scope; codegen_->scope_ = scope;
{ {
Comment cmnt(masm(), "[ Extend block context"); if (needs_block_context_) {
__ Push(scope->GetScopeInfo(codegen->isolate())); Comment cmnt(masm(), "[ Extend block context");
codegen_->PushFunctionArgumentForContextAllocation(); __ Push(scope->GetScopeInfo(codegen->isolate()));
__ CallRuntime(Runtime::kPushBlockContext, 2); codegen_->PushFunctionArgumentForContextAllocation();
__ CallRuntime(Runtime::kPushBlockContext, 2);
// Replace the context stored in the frame.
codegen_->StoreToFrameField(StandardFrameConstants::kContextOffset, // Replace the context stored in the frame.
codegen_->context_register()); codegen_->StoreToFrameField(StandardFrameConstants::kContextOffset,
codegen_->context_register());
}
CHECK_EQ(0, scope->num_stack_slots());
codegen_->PrepareForBailoutForId(entry_id, NO_REGISTERS); codegen_->PrepareForBailoutForId(entry_id, NO_REGISTERS);
} }
{ {
...@@ -1842,7 +1847,7 @@ FullCodeGenerator::EnterBlockScopeIfNeeded::EnterBlockScopeIfNeeded( ...@@ -1842,7 +1847,7 @@ FullCodeGenerator::EnterBlockScopeIfNeeded::EnterBlockScopeIfNeeded(
FullCodeGenerator::EnterBlockScopeIfNeeded::~EnterBlockScopeIfNeeded() { FullCodeGenerator::EnterBlockScopeIfNeeded::~EnterBlockScopeIfNeeded() {
if (scope_ != NULL) { if (needs_block_context_) {
codegen_->LoadContextField(codegen_->context_register(), codegen_->LoadContextField(codegen_->context_register(),
Context::PREVIOUS_INDEX); Context::PREVIOUS_INDEX);
// Update local stack frame context field. // Update local stack frame context field.
......
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
#include "src/compiler.h" #include "src/compiler.h"
#include "src/globals.h" #include "src/globals.h"
#include "src/objects.h" #include "src/objects.h"
#include "src/scopes.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -220,8 +221,9 @@ class FullCodeGenerator: public AstVisitor { ...@@ -220,8 +221,9 @@ class FullCodeGenerator: public AstVisitor {
virtual ~NestedBlock() {} virtual ~NestedBlock() {}
virtual NestedStatement* Exit(int* stack_depth, int* context_length) { virtual NestedStatement* Exit(int* stack_depth, int* context_length) {
if (statement()->AsBlock()->scope() != NULL) { auto block_scope = statement()->AsBlock()->scope();
++(*context_length); if (block_scope != nullptr) {
if (block_scope->ContextLocalCount() > 0) ++(*context_length);
} }
return previous_; return previous_;
} }
...@@ -968,9 +970,9 @@ class FullCodeGenerator: public AstVisitor { ...@@ -968,9 +970,9 @@ class FullCodeGenerator: public AstVisitor {
MacroAssembler* masm() const { return codegen_->masm(); } MacroAssembler* masm() const { return codegen_->masm(); }
FullCodeGenerator* codegen_; FullCodeGenerator* codegen_;
Scope* scope_;
Scope* saved_scope_; Scope* saved_scope_;
BailoutId exit_id_; BailoutId exit_id_;
bool needs_block_context_;
}; };
MacroAssembler* masm_; MacroAssembler* masm_;
......
...@@ -4512,26 +4512,29 @@ void HOptimizedGraphBuilder::VisitBlock(Block* stmt) { ...@@ -4512,26 +4512,29 @@ void HOptimizedGraphBuilder::VisitBlock(Block* stmt) {
{ BreakAndContinueScope push(&break_info, this); { BreakAndContinueScope push(&break_info, this);
if (scope != NULL) { if (scope != NULL) {
// Load the function object. if (scope->ContextLocalCount() > 0) {
Scope* declaration_scope = scope->DeclarationScope(); // Load the function object.
HInstruction* function; Scope* declaration_scope = scope->DeclarationScope();
HValue* outer_context = environment()->context(); HInstruction* function;
if (declaration_scope->is_script_scope() || HValue* outer_context = environment()->context();
declaration_scope->is_eval_scope()) { if (declaration_scope->is_script_scope() ||
function = new(zone()) HLoadContextSlot( declaration_scope->is_eval_scope()) {
outer_context, Context::CLOSURE_INDEX, HLoadContextSlot::kNoCheck); function = new (zone())
} else { HLoadContextSlot(outer_context, Context::CLOSURE_INDEX,
function = New<HThisFunction>(); HLoadContextSlot::kNoCheck);
} } else {
AddInstruction(function); function = New<HThisFunction>();
// Allocate a block context and store it to the stack frame. }
HInstruction* inner_context = Add<HAllocateBlockContext>( AddInstruction(function);
outer_context, function, scope->GetScopeInfo(isolate())); // Allocate a block context and store it to the stack frame.
HInstruction* instr = Add<HStoreFrameContext>(inner_context); HInstruction* inner_context = Add<HAllocateBlockContext>(
set_scope(scope); outer_context, function, scope->GetScopeInfo(isolate()));
environment()->BindContext(inner_context); HInstruction* instr = Add<HStoreFrameContext>(inner_context);
if (instr->HasObservableSideEffects()) { set_scope(scope);
AddSimulate(stmt->EntryId(), REMOVABLE_SIMULATE); environment()->BindContext(inner_context);
if (instr->HasObservableSideEffects()) {
AddSimulate(stmt->EntryId(), REMOVABLE_SIMULATE);
}
} }
VisitDeclarations(scope->declarations()); VisitDeclarations(scope->declarations());
AddSimulate(stmt->DeclsId(), REMOVABLE_SIMULATE); AddSimulate(stmt->DeclsId(), REMOVABLE_SIMULATE);
...@@ -4539,7 +4542,8 @@ void HOptimizedGraphBuilder::VisitBlock(Block* stmt) { ...@@ -4539,7 +4542,8 @@ void HOptimizedGraphBuilder::VisitBlock(Block* stmt) {
CHECK_BAILOUT(VisitStatements(stmt->statements())); CHECK_BAILOUT(VisitStatements(stmt->statements()));
} }
set_scope(outer_scope); set_scope(outer_scope);
if (scope != NULL && current_block() != NULL) { if (scope != NULL && current_block() != NULL &&
scope->ContextLocalCount() > 0) {
HValue* inner_context = environment()->context(); HValue* inner_context = environment()->context();
HValue* outer_context = Add<HLoadNamedField>( HValue* outer_context = Add<HLoadNamedField>(
inner_context, nullptr, inner_context, nullptr,
......
...@@ -4288,6 +4288,9 @@ class ScopeInfo : public FixedArray { ...@@ -4288,6 +4288,9 @@ class ScopeInfo : public FixedArray {
// Return the name of the given stack local. // Return the name of the given stack local.
String* StackLocalName(int var); String* StackLocalName(int var);
// Return the name of the given stack local.
int StackLocalIndex(int var);
// Return the name of the given context local. // Return the name of the given context local.
String* ContextLocalName(int var); String* ContextLocalName(int var);
...@@ -4396,33 +4399,38 @@ class ScopeInfo : public FixedArray { ...@@ -4396,33 +4399,38 @@ class ScopeInfo : public FixedArray {
// slot is used per parameter, so in total this part occupies // slot is used per parameter, so in total this part occupies
// ParameterCount() slots in the array. For other scopes than function // ParameterCount() slots in the array. For other scopes than function
// scopes ParameterCount() is 0. // scopes ParameterCount() is 0.
// 2. StackLocalEntries: // 2. StackLocalFirstSlot:
// Index of a first stack slot for stack local. Stack locals belonging to
// this scope are located on a stack at slots starting from this index.
// 3. StackLocalEntries:
// Contains the names of local variables that are allocated on the stack, // Contains the names of local variables that are allocated on the stack,
// in increasing order of the stack slot index. One slot is used per stack // in increasing order of the stack slot index. First local variable has
// local, so in total this part occupies StackLocalCount() slots in the // a stack slot index defined in StackLocalFirstSlot (point 2 above).
// array. // One slot is used per stack local, so in total this part occupies
// 3. ContextLocalNameEntries: // StackLocalCount() slots in the array.
// 4. ContextLocalNameEntries:
// Contains the names of local variables and parameters that are allocated // Contains the names of local variables and parameters that are allocated
// in the context. They are stored in increasing order of the context slot // in the context. They are stored in increasing order of the context slot
// index starting with Context::MIN_CONTEXT_SLOTS. One slot is used per // index starting with Context::MIN_CONTEXT_SLOTS. One slot is used per
// context local, so in total this part occupies ContextLocalCount() slots // context local, so in total this part occupies ContextLocalCount() slots
// in the array. // in the array.
// 4. ContextLocalInfoEntries: // 5. ContextLocalInfoEntries:
// Contains the variable modes and initialization flags corresponding to // Contains the variable modes and initialization flags corresponding to
// the context locals in ContextLocalNameEntries. One slot is used per // the context locals in ContextLocalNameEntries. One slot is used per
// context local, so in total this part occupies ContextLocalCount() // context local, so in total this part occupies ContextLocalCount()
// slots in the array. // slots in the array.
// 5. StrongModeFreeVariableNameEntries: // 6. StrongModeFreeVariableNameEntries:
// Stores the names of strong mode free variables. // Stores the names of strong mode free variables.
// 6. StrongModeFreeVariablePositionEntries: // 7. StrongModeFreeVariablePositionEntries:
// Stores the locations (start and end position) of strong mode free // Stores the locations (start and end position) of strong mode free
// variables. // variables.
// 7. FunctionNameEntryIndex: // 8. FunctionNameEntryIndex:
// If the scope belongs to a named function expression this part contains // If the scope belongs to a named function expression this part contains
// information about the function variable. It always occupies two array // information about the function variable. It always occupies two array
// slots: a. The name of the function variable. // slots: a. The name of the function variable.
// b. The context or stack slot index for the variable. // b. The context or stack slot index for the variable.
int ParameterEntriesIndex(); int ParameterEntriesIndex();
int StackLocalFirstSlotIndex();
int StackLocalEntriesIndex(); int StackLocalEntriesIndex();
int ContextLocalNameEntriesIndex(); int ContextLocalNameEntriesIndex();
int ContextLocalInfoEntriesIndex(); int ContextLocalInfoEntriesIndex();
......
...@@ -519,6 +519,7 @@ void ParserTraits::CheckPossibleEvalCall(Expression* expression, ...@@ -519,6 +519,7 @@ void ParserTraits::CheckPossibleEvalCall(Expression* expression,
if (callee != NULL && if (callee != NULL &&
callee->raw_name() == parser_->ast_value_factory()->eval_string()) { callee->raw_name() == parser_->ast_value_factory()->eval_string()) {
scope->DeclarationScope()->RecordEvalCall(); scope->DeclarationScope()->RecordEvalCall();
scope->RecordEvalCall();
} }
} }
...@@ -3339,8 +3340,8 @@ Statement* Parser::DesugarLexicalBindingsInForStatement( ...@@ -3339,8 +3340,8 @@ Statement* Parser::DesugarLexicalBindingsInForStatement(
clear_first = clear_first =
factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition); factory()->NewExpressionStatement(assignment, RelocInfo::kNoPosition);
} }
Statement* clear_first_or_next = Statement* clear_first_or_next = factory()->NewIfStatement(
factory()->NewIfStatement(compare, clear_first, next, next->position()); compare, clear_first, next, RelocInfo::kNoPosition);
inner_block->AddStatement(clear_first_or_next, zone()); inner_block->AddStatement(clear_first_or_next, zone());
} }
...@@ -4048,6 +4049,10 @@ FunctionLiteral* Parser::ParseFunctionLiteral( ...@@ -4048,6 +4049,10 @@ FunctionLiteral* Parser::ParseFunctionLiteral(
parenthesized_function_ = false; // The bit was set for this function only. parenthesized_function_ = false; // The bit was set for this function only.
if (is_lazily_parsed) { if (is_lazily_parsed) {
for (Scope* s = scope_->outer_scope();
s != nullptr && (s != s->DeclarationScope()); s = s->outer_scope()) {
s->ForceContextAllocation();
}
SkipLazyFunctionBody(&materialized_literal_count, SkipLazyFunctionBody(&materialized_literal_count,
&expected_property_count, CHECK_OK); &expected_property_count, CHECK_OK);
} else { } else {
......
...@@ -686,17 +686,16 @@ static bool ParameterIsShadowedByContextLocal(Handle<ScopeInfo> info, ...@@ -686,17 +686,16 @@ static bool ParameterIsShadowedByContextLocal(Handle<ScopeInfo> info,
// frame. // frame.
MUST_USE_RESULT MUST_USE_RESULT
static MaybeHandle<JSObject> MaterializeStackLocalsWithFrameInspector( static MaybeHandle<JSObject> MaterializeStackLocalsWithFrameInspector(
Isolate* isolate, Handle<JSObject> target, Handle<JSFunction> function, Isolate* isolate, Handle<JSObject> target, Handle<ScopeInfo> scope_info,
FrameInspector* frame_inspector) { FrameInspector* frame_inspector) {
Handle<SharedFunctionInfo> shared(function->shared());
Handle<ScopeInfo> scope_info(shared->scope_info());
// First fill all parameters. // First fill all parameters.
for (int i = 0; i < scope_info->ParameterCount(); ++i) { for (int i = 0; i < scope_info->ParameterCount(); ++i) {
// Do not materialize the parameter if it is shadowed by a context local. // Do not materialize the parameter if it is shadowed by a context local.
Handle<String> name(scope_info->ParameterName(i)); Handle<String> name(scope_info->ParameterName(i));
if (ParameterIsShadowedByContextLocal(scope_info, name)) continue; if (ParameterIsShadowedByContextLocal(scope_info, name)) continue;
DCHECK_NOT_NULL(frame_inspector);
HandleScope scope(isolate); HandleScope scope(isolate);
Handle<Object> value(i < frame_inspector->GetParametersCount() Handle<Object> value(i < frame_inspector->GetParametersCount()
? frame_inspector->GetParameter(i) ? frame_inspector->GetParameter(i)
...@@ -713,8 +712,12 @@ static MaybeHandle<JSObject> MaterializeStackLocalsWithFrameInspector( ...@@ -713,8 +712,12 @@ static MaybeHandle<JSObject> MaterializeStackLocalsWithFrameInspector(
for (int i = 0; i < scope_info->StackLocalCount(); ++i) { for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
if (scope_info->LocalIsSynthetic(i)) continue; if (scope_info->LocalIsSynthetic(i)) continue;
Handle<String> name(scope_info->StackLocalName(i)); Handle<String> name(scope_info->StackLocalName(i));
Handle<Object> value(frame_inspector->GetExpression(i), isolate); Handle<Object> value(
if (value->IsTheHole()) continue; frame_inspector->GetExpression(scope_info->StackLocalIndex(i)),
isolate);
if (value->IsTheHole()) {
value = isolate->factory()->undefined_value();
}
RETURN_ON_EXCEPTION(isolate, Runtime::SetObjectProperty( RETURN_ON_EXCEPTION(isolate, Runtime::SetObjectProperty(
isolate, target, name, value, SLOPPY), isolate, target, name, value, SLOPPY),
...@@ -724,12 +727,21 @@ static MaybeHandle<JSObject> MaterializeStackLocalsWithFrameInspector( ...@@ -724,12 +727,21 @@ static MaybeHandle<JSObject> MaterializeStackLocalsWithFrameInspector(
return target; return target;
} }
MUST_USE_RESULT
static MaybeHandle<JSObject> MaterializeStackLocalsWithFrameInspector(
Isolate* isolate, Handle<JSObject> target, Handle<JSFunction> function,
FrameInspector* frame_inspector) {
Handle<SharedFunctionInfo> shared(function->shared());
Handle<ScopeInfo> scope_info(shared->scope_info());
return MaterializeStackLocalsWithFrameInspector(isolate, target, scope_info,
frame_inspector);
}
static void UpdateStackLocalsFromMaterializedObject(Isolate* isolate, static void UpdateStackLocalsFromMaterializedObject(
Handle<JSObject> target, Isolate* isolate, Handle<JSObject> target, Handle<ScopeInfo> scope_info,
Handle<JSFunction> function, JavaScriptFrame* frame, int inlined_jsframe_index) {
JavaScriptFrame* frame,
int inlined_jsframe_index) {
if (inlined_jsframe_index != 0 || frame->is_optimized()) { if (inlined_jsframe_index != 0 || frame->is_optimized()) {
// Optimized frames are not supported. // Optimized frames are not supported.
// TODO(yangguo): make sure all code deoptimized when debugger is active // TODO(yangguo): make sure all code deoptimized when debugger is active
...@@ -737,9 +749,6 @@ static void UpdateStackLocalsFromMaterializedObject(Isolate* isolate, ...@@ -737,9 +749,6 @@ static void UpdateStackLocalsFromMaterializedObject(Isolate* isolate,
return; return;
} }
Handle<SharedFunctionInfo> shared(function->shared());
Handle<ScopeInfo> scope_info(shared->scope_info());
// Parameters. // Parameters.
for (int i = 0; i < scope_info->ParameterCount(); ++i) { for (int i = 0; i < scope_info->ParameterCount(); ++i) {
// Shadowed parameters were not materialized. // Shadowed parameters were not materialized.
...@@ -756,12 +765,13 @@ static void UpdateStackLocalsFromMaterializedObject(Isolate* isolate, ...@@ -756,12 +765,13 @@ static void UpdateStackLocalsFromMaterializedObject(Isolate* isolate,
// Stack locals. // Stack locals.
for (int i = 0; i < scope_info->StackLocalCount(); ++i) { for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
if (scope_info->LocalIsSynthetic(i)) continue; if (scope_info->LocalIsSynthetic(i)) continue;
if (frame->GetExpression(i)->IsTheHole()) continue; int index = scope_info->StackLocalIndex(i);
if (frame->GetExpression(index)->IsTheHole()) continue;
HandleScope scope(isolate); HandleScope scope(isolate);
Handle<Object> value = Object::GetPropertyOrElement( Handle<Object> value = Object::GetPropertyOrElement(
target, handle(scope_info->StackLocalName(i), target, handle(scope_info->StackLocalName(i),
isolate)).ToHandleChecked(); isolate)).ToHandleChecked();
frame->SetExpression(i, *value); frame->SetExpression(index, *value);
} }
} }
...@@ -903,7 +913,7 @@ static bool SetLocalVariableValue(Isolate* isolate, JavaScriptFrame* frame, ...@@ -903,7 +913,7 @@ static bool SetLocalVariableValue(Isolate* isolate, JavaScriptFrame* frame,
for (int i = 0; i < scope_info->StackLocalCount(); ++i) { for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
HandleScope scope(isolate); HandleScope scope(isolate);
if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) { if (String::Equals(handle(scope_info->StackLocalName(i)), variable_name)) {
frame->SetExpression(i, *new_value); frame->SetExpression(scope_info->StackLocalIndex(i), *new_value);
return true; return true;
} }
} }
...@@ -940,6 +950,30 @@ static bool SetLocalVariableValue(Isolate* isolate, JavaScriptFrame* frame, ...@@ -940,6 +950,30 @@ static bool SetLocalVariableValue(Isolate* isolate, JavaScriptFrame* frame,
} }
static bool SetBlockVariableValue(Isolate* isolate,
Handle<Context> block_context,
Handle<ScopeInfo> scope_info,
JavaScriptFrame* frame,
Handle<String> variable_name,
Handle<Object> new_value) {
if (frame != nullptr) {
for (int i = 0; i < scope_info->StackLocalCount(); ++i) {
HandleScope scope(isolate);
if (String::Equals(handle(scope_info->StackLocalName(i)),
variable_name)) {
frame->SetExpression(scope_info->StackLocalIndex(i), *new_value);
return true;
}
}
}
if (!block_context.is_null()) {
return SetContextLocalValue(block_context->GetIsolate(), scope_info,
block_context, variable_name, new_value);
}
return false;
}
// Create a plain JSObject which materializes the closure content for the // Create a plain JSObject which materializes the closure content for the
// context. // context.
MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeClosure( MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeClosure(
...@@ -1020,17 +1054,6 @@ static bool SetClosureVariableValue(Isolate* isolate, Handle<Context> context, ...@@ -1020,17 +1054,6 @@ static bool SetClosureVariableValue(Isolate* isolate, Handle<Context> context,
} }
static bool SetBlockContextVariableValue(Handle<Context> block_context,
Handle<String> variable_name,
Handle<Object> new_value) {
DCHECK(block_context->IsBlockContext());
Handle<ScopeInfo> scope_info(ScopeInfo::cast(block_context->extension()));
return SetContextLocalValue(block_context->GetIsolate(), scope_info,
block_context, variable_name, new_value);
}
static bool SetScriptVariableValue(Handle<Context> context, static bool SetScriptVariableValue(Handle<Context> context,
Handle<String> variable_name, Handle<String> variable_name,
Handle<Object> new_value) { Handle<Object> new_value) {
...@@ -1082,19 +1105,27 @@ static bool SetCatchVariableValue(Isolate* isolate, Handle<Context> context, ...@@ -1082,19 +1105,27 @@ static bool SetCatchVariableValue(Isolate* isolate, Handle<Context> context,
// Create a plain JSObject which materializes the block scope for the specified // Create a plain JSObject which materializes the block scope for the specified
// block context. // block context.
MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeBlockScope( MUST_USE_RESULT static MaybeHandle<JSObject> MaterializeBlockScope(
Isolate* isolate, Handle<Context> context) { Isolate* isolate, Handle<ScopeInfo> scope_info, Handle<Context> context,
DCHECK(context->IsBlockContext()); JavaScriptFrame* frame, int inlined_jsframe_index) {
Handle<ScopeInfo> scope_info(ScopeInfo::cast(context->extension()));
// Allocate and initialize a JSObject with all the arguments, stack locals
// heap locals and extension properties of the debugged function.
Handle<JSObject> block_scope = Handle<JSObject> block_scope =
isolate->factory()->NewJSObject(isolate->object_function()); isolate->factory()->NewJSObject(isolate->object_function());
// Fill all context locals. if (frame != nullptr) {
if (!ScopeInfo::CopyContextLocalsToScopeObject(scope_info, context, FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
block_scope)) { RETURN_ON_EXCEPTION(isolate,
return MaybeHandle<JSObject>(); MaterializeStackLocalsWithFrameInspector(
isolate, block_scope, scope_info, &frame_inspector),
JSObject);
}
if (!context.is_null()) {
Handle<ScopeInfo> scope_info_from_context(
ScopeInfo::cast(context->extension()));
// Fill all context locals.
if (!ScopeInfo::CopyContextLocalsToScopeObject(scope_info_from_context,
context, block_scope)) {
return MaybeHandle<JSObject>();
}
} }
return block_scope; return block_scope;
...@@ -1361,8 +1392,20 @@ class ScopeIterator { ...@@ -1361,8 +1392,20 @@ class ScopeIterator {
case ScopeIterator::ScopeTypeClosure: case ScopeIterator::ScopeTypeClosure:
// Materialize the content of the closure scope into a JSObject. // Materialize the content of the closure scope into a JSObject.
return MaterializeClosure(isolate_, CurrentContext()); return MaterializeClosure(isolate_, CurrentContext());
case ScopeIterator::ScopeTypeBlock: case ScopeIterator::ScopeTypeBlock: {
return MaterializeBlockScope(isolate_, CurrentContext()); if (!nested_scope_chain_.is_empty()) {
// this is a block scope on the stack.
Handle<ScopeInfo> scope_info = nested_scope_chain_.last();
Handle<Context> context = scope_info->HasContext()
? CurrentContext()
: Handle<Context>::null();
return MaterializeBlockScope(isolate_, scope_info, context, frame_,
inlined_jsframe_index_);
} else {
return MaterializeBlockScope(isolate_, Handle<ScopeInfo>::null(),
CurrentContext(), nullptr, 0);
}
}
case ScopeIterator::ScopeTypeModule: case ScopeIterator::ScopeTypeModule:
return MaterializeModuleScope(isolate_, CurrentContext()); return MaterializeModuleScope(isolate_, CurrentContext());
} }
...@@ -1370,6 +1413,16 @@ class ScopeIterator { ...@@ -1370,6 +1413,16 @@ class ScopeIterator {
return Handle<JSObject>(); return Handle<JSObject>();
} }
bool HasContext() {
ScopeType type = Type();
if (type == ScopeTypeBlock || type == ScopeTypeLocal) {
if (!nested_scope_chain_.is_empty()) {
return nested_scope_chain_.last()->HasContext();
}
}
return true;
}
bool SetVariableValue(Handle<String> variable_name, bool SetVariableValue(Handle<String> variable_name,
Handle<Object> new_value) { Handle<Object> new_value) {
DCHECK(!failed_); DCHECK(!failed_);
...@@ -1391,8 +1444,9 @@ class ScopeIterator { ...@@ -1391,8 +1444,9 @@ class ScopeIterator {
return SetScriptVariableValue(CurrentContext(), variable_name, return SetScriptVariableValue(CurrentContext(), variable_name,
new_value); new_value);
case ScopeIterator::ScopeTypeBlock: case ScopeIterator::ScopeTypeBlock:
return SetBlockContextVariableValue(CurrentContext(), variable_name, return SetBlockVariableValue(
new_value); isolate_, HasContext() ? CurrentContext() : Handle<Context>::null(),
CurrentScopeInfo(), frame_, variable_name, new_value);
case ScopeIterator::ScopeTypeModule: case ScopeIterator::ScopeTypeModule:
// TODO(2399): should we implement it? // TODO(2399): should we implement it?
break; break;
...@@ -2188,6 +2242,179 @@ static Handle<JSObject> NewJSObjectWithNullProto(Isolate* isolate) { ...@@ -2188,6 +2242,179 @@ static Handle<JSObject> NewJSObjectWithNullProto(Isolate* isolate) {
} }
namespace {
// This class builds a context chain for evaluation of expressions
// in debugger.
// The scope chain leading up to a breakpoint where evaluation occurs
// looks like:
// - [a mix of with, catch and block scopes]
// - [function stack + context]
// - [outer context]
// The builder materializes all stack variables into properties of objects;
// the expression is then evaluated as if it is inside a series of 'with'
// statements using those objects. To this end, the builder builds a new
// context chain, based on a scope chain:
// - every With and Catch scope begets a cloned context
// - Block scope begets one or two contexts:
// - if a block has context-allocated varaibles, its context is cloned
// - stack locals are materizalized as a With context
// - Local scope begets a With context for materizalized locals, chained to
// original function context. Original function context is the end of
// the chain.
class EvaluationContextBuilder {
public:
EvaluationContextBuilder(Isolate* isolate, JavaScriptFrame* frame,
int inlined_jsframe_index)
: isolate_(isolate),
frame_(frame),
inlined_jsframe_index_(inlined_jsframe_index) {
FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
Handle<JSFunction> function =
handle(JSFunction::cast(frame_inspector.GetFunction()));
Handle<Context> outer_context = handle(function->context(), isolate);
outer_info_ = handle(function->shared());
Handle<Context> inner_context;
bool stop = false;
for (ScopeIterator it(isolate, frame, inlined_jsframe_index);
!it.Failed() && !it.Done() && !stop; it.Next()) {
ScopeIterator::ScopeType scope_type = it.Type();
if (scope_type == ScopeIterator::ScopeTypeLocal) {
Handle<JSObject> materialized_function =
NewJSObjectWithNullProto(isolate);
if (!MaterializeStackLocalsWithFrameInspector(
isolate, materialized_function, function, &frame_inspector)
.ToHandle(&materialized_function))
return;
if (!MaterializeArgumentsObject(isolate, materialized_function,
function)
.ToHandle(&materialized_function))
return;
Handle<Context> parent_context =
it.HasContext() ? it.CurrentContext() : outer_context;
Handle<Context> with_context = isolate->factory()->NewWithContext(
function, parent_context, materialized_function);
ContextChainElement context_chain_element;
context_chain_element.original_context = it.CurrentContext();
context_chain_element.materialized_object = materialized_function;
context_chain_element.scope_info = it.CurrentScopeInfo();
context_chain_.Add(context_chain_element);
stop = true;
RecordContextsInChain(&inner_context, with_context, with_context);
} else if (scope_type == ScopeIterator::ScopeTypeCatch ||
scope_type == ScopeIterator::ScopeTypeWith) {
Handle<Context> cloned_context =
Handle<Context>::cast(FixedArray::CopySize(
it.CurrentContext(), it.CurrentContext()->length()));
ContextChainElement context_chain_element;
context_chain_element.original_context = it.CurrentContext();
context_chain_element.cloned_context = cloned_context;
context_chain_.Add(context_chain_element);
RecordContextsInChain(&inner_context, cloned_context, cloned_context);
} else if (scope_type == ScopeIterator::ScopeTypeBlock) {
Handle<JSObject> materialized_object =
NewJSObjectWithNullProto(isolate);
if (!MaterializeStackLocalsWithFrameInspector(
isolate, materialized_object, it.CurrentScopeInfo(),
&frame_inspector).ToHandle(&materialized_object))
return;
if (it.HasContext()) {
Handle<Context> cloned_context =
Handle<Context>::cast(FixedArray::CopySize(
it.CurrentContext(), it.CurrentContext()->length()));
Handle<Context> with_context = isolate->factory()->NewWithContext(
function, cloned_context, materialized_object);
ContextChainElement context_chain_element;
context_chain_element.original_context = it.CurrentContext();
context_chain_element.cloned_context = cloned_context;
context_chain_element.materialized_object = materialized_object;
context_chain_element.scope_info = it.CurrentScopeInfo();
context_chain_.Add(context_chain_element);
RecordContextsInChain(&inner_context, cloned_context, with_context);
} else {
Handle<Context> with_context = isolate->factory()->NewWithContext(
function, outer_context, materialized_object);
ContextChainElement context_chain_element;
context_chain_element.materialized_object = materialized_object;
context_chain_element.scope_info = it.CurrentScopeInfo();
context_chain_.Add(context_chain_element);
RecordContextsInChain(&inner_context, with_context, with_context);
}
} else {
stop = true;
}
}
if (innermost_context_.is_null()) {
innermost_context_ = outer_context;
}
DCHECK(!innermost_context_.is_null());
}
void UpdateVariables() {
for (int i = 0; i < context_chain_.length(); i++) {
ContextChainElement element = context_chain_[i];
if (!element.original_context.is_null() &&
!element.cloned_context.is_null()) {
Handle<Context> cloned_context = element.cloned_context;
cloned_context->CopyTo(
Context::MIN_CONTEXT_SLOTS, *element.original_context,
Context::MIN_CONTEXT_SLOTS,
cloned_context->length() - Context::MIN_CONTEXT_SLOTS);
}
if (!element.materialized_object.is_null()) {
// Write back potential changes to materialized stack locals to the
// stack.
UpdateStackLocalsFromMaterializedObject(
isolate_, element.materialized_object, element.scope_info, frame_,
inlined_jsframe_index_);
}
}
}
Handle<Context> innermost_context() const { return innermost_context_; }
Handle<SharedFunctionInfo> outer_info() const { return outer_info_; }
private:
struct ContextChainElement {
Handle<Context> original_context;
Handle<Context> cloned_context;
Handle<JSObject> materialized_object;
Handle<ScopeInfo> scope_info;
};
void RecordContextsInChain(Handle<Context>* inner_context,
Handle<Context> first, Handle<Context> last) {
if (!inner_context->is_null()) {
(*inner_context)->set_previous(*last);
} else {
innermost_context_ = last;
}
*inner_context = first;
}
Handle<SharedFunctionInfo> outer_info_;
Handle<Context> innermost_context_;
List<ContextChainElement> context_chain_;
Isolate* isolate_;
JavaScriptFrame* frame_;
int inlined_jsframe_index_;
};
}
// Evaluate a piece of JavaScript in the context of a stack frame for // Evaluate a piece of JavaScript in the context of a stack frame for
// debugging. Things that need special attention are: // debugging. Things that need special attention are:
// - Parameters and stack-allocated locals need to be materialized. Altered // - Parameters and stack-allocated locals need to be materialized. Altered
...@@ -2224,100 +2451,22 @@ RUNTIME_FUNCTION(Runtime_DebugEvaluate) { ...@@ -2224,100 +2451,22 @@ RUNTIME_FUNCTION(Runtime_DebugEvaluate) {
isolate->set_context(*(save->context())); isolate->set_context(*(save->context()));
// Materialize stack locals and the arguments object. // Materialize stack locals and the arguments object.
Handle<JSObject> materialized;
Handle<JSFunction> function;
Handle<SharedFunctionInfo> outer_info;
Handle<Context> eval_context;
// We need to limit the lifetime of the FrameInspector because evaluation can
// call arbitrary code and only one FrameInspector can be active at a time.
{
FrameInspector frame_inspector(frame, inlined_jsframe_index, isolate);
materialized = NewJSObjectWithNullProto(isolate);
function = handle(JSFunction::cast(frame_inspector.GetFunction()));
outer_info = handle(function->shared());
eval_context = handle(Context::cast(frame_inspector.GetContext()));
ASSIGN_RETURN_FAILURE_ON_EXCEPTION( EvaluationContextBuilder context_builder(isolate, frame,
isolate, materialized, inlined_jsframe_index);
MaterializeStackLocalsWithFrameInspector(isolate, materialized, if (isolate->has_pending_exception()) {
function, &frame_inspector)); return isolate->heap()->exception();
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, materialized,
MaterializeArgumentsObject(isolate, materialized, function));
}
// At this point, the lookup chain may look like this:
// [inner context] -> [function stack]+[function context] -> [outer context]
// The function stack is not an actual context, it complements the function
// context. In order to have the same lookup chain when debug-evaluating,
// we:
// - clone inner context
// - materialize the stack and insert it into the context chain as a
// with-context before the function context.
// [inner context clone] -> [with context] -> [function context] ->
// [outer context]
// Ordering the with-context before the function context forces a dynamic
// lookup instead of a static lookup that could fail as the scope info is
// outdated and may expect variables to still be stack-allocated.
// Afterwards, we write changes to the with-context back to the stack, and
// write changes in cloned contexts back to original contexts.
DCHECK(!eval_context.is_null());
Handle<Context> function_context = eval_context;
Handle<Context> outer_context(function->context(), isolate);
Handle<Context> inner_context;
Handle<Context> innermost_context;
// We iterate to find the function's context, cloning until we hit it.
// If the function has no context-allocated variables, we iterate until
// we hit the outer context.
while (!function_context->IsFunctionContext() &&
!function_context->IsScriptContext() &&
!function_context.is_identical_to(outer_context)) {
Handle<Context> clone = Handle<Context>::cast(
FixedArray::CopySize(function_context, function_context->length()));
if (!inner_context.is_null()) {
inner_context->set_previous(*clone);
} else {
innermost_context = clone;
}
inner_context = clone;
function_context = Handle<Context>(function_context->previous(), isolate);
} }
Handle<Context> materialized_context = isolate->factory()->NewWithContext(
function, function_context, materialized);
if (inner_context.is_null()) {
// No inner context. The with-context is now inner-most.
innermost_context = materialized_context;
} else {
inner_context->set_previous(*materialized_context);
}
Handle<Object> receiver(frame->receiver(), isolate); Handle<Object> receiver(frame->receiver(), isolate);
MaybeHandle<Object> maybe_result = MaybeHandle<Object> maybe_result = DebugEvaluate(
DebugEvaluate(isolate, outer_info, innermost_context, context_extension, isolate, context_builder.outer_info(),
receiver, source); context_builder.innermost_context(), context_extension, receiver, source);
Handle<Object> result; Handle<Object> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, maybe_result); ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, result, maybe_result);
context_builder.UpdateVariables();
// Write back potential changes to materialized stack locals to the stack.
UpdateStackLocalsFromMaterializedObject(isolate, materialized, function,
frame, inlined_jsframe_index);
while (!innermost_context.is_identical_to(materialized_context)) {
DCHECK(eval_context->map() == innermost_context->map());
innermost_context->CopyTo(
Context::MIN_CONTEXT_SLOTS, *eval_context, Context::MIN_CONTEXT_SLOTS,
innermost_context->length() - Context::MIN_CONTEXT_SLOTS);
innermost_context = handle(innermost_context->previous(), isolate);
eval_context = handle(eval_context->previous(), isolate);
}
return *result; return *result;
} }
......
...@@ -27,7 +27,6 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, ...@@ -27,7 +27,6 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
const int strong_mode_free_variable_count = const int strong_mode_free_variable_count =
strong_mode_free_variables.length(); strong_mode_free_variables.length();
// Make sure we allocate the correct amount. // Make sure we allocate the correct amount.
DCHECK(scope->StackLocalCount() == stack_local_count);
DCHECK(scope->ContextLocalCount() == context_local_count); DCHECK(scope->ContextLocalCount() == context_local_count);
bool simple_parameter_list = bool simple_parameter_list =
...@@ -54,8 +53,8 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, ...@@ -54,8 +53,8 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
const bool has_function_name = function_name_info != NONE; const bool has_function_name = function_name_info != NONE;
const int parameter_count = scope->num_parameters(); const int parameter_count = scope->num_parameters();
const int length = kVariablePartIndex + parameter_count + stack_local_count + const int length = kVariablePartIndex + parameter_count +
2 * context_local_count + (1 + stack_local_count) + 2 * context_local_count +
3 * strong_mode_free_variable_count + 3 * strong_mode_free_variable_count +
(has_function_name ? 2 : 0); (has_function_name ? 2 : 0);
...@@ -89,9 +88,17 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, ...@@ -89,9 +88,17 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
// Add stack locals' names. We are assuming that the stack locals' // Add stack locals' names. We are assuming that the stack locals'
// slots are allocated in increasing order, so we can simply add // slots are allocated in increasing order, so we can simply add
// them to the ScopeInfo object. // them to the ScopeInfo object.
int first_slot_index;
if (stack_local_count > 0) {
first_slot_index = stack_locals[0]->index();
} else {
first_slot_index = 0;
}
DCHECK(index == scope_info->StackLocalFirstSlotIndex());
scope_info->set(index++, Smi::FromInt(first_slot_index));
DCHECK(index == scope_info->StackLocalEntriesIndex()); DCHECK(index == scope_info->StackLocalEntriesIndex());
for (int i = 0; i < stack_local_count; ++i) { for (int i = 0; i < stack_local_count; ++i) {
DCHECK(stack_locals[i]->index() == i); DCHECK(stack_locals[i]->index() == first_slot_index + i);
scope_info->set(index++, *stack_locals[i]->name()); scope_info->set(index++, *stack_locals[i]->name());
} }
...@@ -145,16 +152,12 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, ...@@ -145,16 +152,12 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone,
int var_index = scope->function()->proxy()->var()->index(); int var_index = scope->function()->proxy()->var()->index();
scope_info->set(index++, *scope->function()->proxy()->name()); scope_info->set(index++, *scope->function()->proxy()->name());
scope_info->set(index++, Smi::FromInt(var_index)); scope_info->set(index++, Smi::FromInt(var_index));
DCHECK(function_name_info != STACK ||
(var_index == scope_info->StackLocalCount() &&
var_index == scope_info->StackSlotCount() - 1));
DCHECK(function_name_info != CONTEXT || DCHECK(function_name_info != CONTEXT ||
var_index == scope_info->ContextLength() - 1); var_index == scope_info->ContextLength() - 1);
} }
DCHECK(index == scope_info->length()); DCHECK(index == scope_info->length());
DCHECK(scope->num_parameters() == scope_info->ParameterCount()); DCHECK(scope->num_parameters() == scope_info->ParameterCount());
DCHECK(scope->num_stack_slots() == scope_info->StackSlotCount());
DCHECK(scope->num_heap_slots() == scope_info->ContextLength() || DCHECK(scope->num_heap_slots() == scope_info->ContextLength() ||
(scope->num_heap_slots() == kVariablePartIndex && (scope->num_heap_slots() == kVariablePartIndex &&
scope_info->ContextLength() == 0)); scope_info->ContextLength() == 0));
...@@ -269,6 +272,13 @@ String* ScopeInfo::StackLocalName(int var) { ...@@ -269,6 +272,13 @@ String* ScopeInfo::StackLocalName(int var) {
} }
int ScopeInfo::StackLocalIndex(int var) {
DCHECK(0 <= var && var < StackLocalCount());
int first_slot_index = Smi::cast(get(StackLocalFirstSlotIndex()))->value();
return first_slot_index + var;
}
String* ScopeInfo::ContextLocalName(int var) { String* ScopeInfo::ContextLocalName(int var) {
DCHECK(0 <= var && var < ContextLocalCount()); DCHECK(0 <= var && var < ContextLocalCount());
int info_index = ContextLocalNameEntriesIndex() + var; int info_index = ContextLocalNameEntriesIndex() + var;
...@@ -343,11 +353,12 @@ int ScopeInfo::StrongModeFreeVariableEndPosition(int var) { ...@@ -343,11 +353,12 @@ int ScopeInfo::StrongModeFreeVariableEndPosition(int var) {
int ScopeInfo::StackSlotIndex(String* name) { int ScopeInfo::StackSlotIndex(String* name) {
DCHECK(name->IsInternalizedString()); DCHECK(name->IsInternalizedString());
if (length() > 0) { if (length() > 0) {
int first_slot_index = Smi::cast(get(StackLocalFirstSlotIndex()))->value();
int start = StackLocalEntriesIndex(); int start = StackLocalEntriesIndex();
int end = StackLocalEntriesIndex() + StackLocalCount(); int end = StackLocalEntriesIndex() + StackLocalCount();
for (int i = start; i < end; ++i) { for (int i = start; i < end; ++i) {
if (name == get(i)) { if (name == get(i)) {
return i - start; return i - start + first_slot_index;
} }
} }
} }
...@@ -453,7 +464,7 @@ bool ScopeInfo::CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info, ...@@ -453,7 +464,7 @@ bool ScopeInfo::CopyContextLocalsToScopeObject(Handle<ScopeInfo> scope_info,
if (scope_info->LocalIsSynthetic(first_context_var + i)) continue; if (scope_info->LocalIsSynthetic(first_context_var + i)) continue;
int context_index = Context::MIN_CONTEXT_SLOTS + i; int context_index = Context::MIN_CONTEXT_SLOTS + i;
Handle<Object> value = Handle<Object>(context->get(context_index), isolate); Handle<Object> value = Handle<Object>(context->get(context_index), isolate);
// Do not reflect variables under TDZ in scope object. // Reflect variables under TDZ as undefined in scope object.
if (value->IsTheHole()) continue; if (value->IsTheHole()) continue;
RETURN_ON_EXCEPTION_VALUE( RETURN_ON_EXCEPTION_VALUE(
isolate, Runtime::DefineObjectProperty( isolate, Runtime::DefineObjectProperty(
...@@ -472,11 +483,16 @@ int ScopeInfo::ParameterEntriesIndex() { ...@@ -472,11 +483,16 @@ int ScopeInfo::ParameterEntriesIndex() {
} }
int ScopeInfo::StackLocalEntriesIndex() { int ScopeInfo::StackLocalFirstSlotIndex() {
return ParameterEntriesIndex() + ParameterCount(); return ParameterEntriesIndex() + ParameterCount();
} }
int ScopeInfo::StackLocalEntriesIndex() {
return StackLocalFirstSlotIndex() + 1;
}
int ScopeInfo::ContextLocalNameEntriesIndex() { int ScopeInfo::ContextLocalNameEntriesIndex() {
return StackLocalEntriesIndex() + StackLocalCount(); return StackLocalEntriesIndex() + StackLocalCount();
} }
......
...@@ -371,6 +371,7 @@ Scope* Scope::FinalizeBlockScope() { ...@@ -371,6 +371,7 @@ Scope* Scope::FinalizeBlockScope() {
if (uses_arguments()) outer_scope_->RecordArgumentsUsage(); if (uses_arguments()) outer_scope_->RecordArgumentsUsage();
if (uses_super_property()) outer_scope_->RecordSuperPropertyUsage(); if (uses_super_property()) outer_scope_->RecordSuperPropertyUsage();
if (uses_this()) outer_scope_->RecordThisUsage(); if (uses_this()) outer_scope_->RecordThisUsage();
if (scope_calls_eval_) outer_scope_->RecordEvalCall();
return NULL; return NULL;
} }
...@@ -381,13 +382,13 @@ Variable* Scope::LookupLocal(const AstRawString* name) { ...@@ -381,13 +382,13 @@ Variable* Scope::LookupLocal(const AstRawString* name) {
if (result != NULL || scope_info_.is_null()) { if (result != NULL || scope_info_.is_null()) {
return result; return result;
} }
Handle<String> name_handle = name->string();
// The Scope is backed up by ScopeInfo. This means it cannot operate in a // The Scope is backed up by ScopeInfo. This means it cannot operate in a
// heap-independent mode, and all strings must be internalized immediately. So // heap-independent mode, and all strings must be internalized immediately. So
// it's ok to get the Handle<String> here. // it's ok to get the Handle<String> here.
Handle<String> name_handle = name->string();
// If we have a serialized scope info, we might find the variable there. // If we have a serialized scope info, we might find the variable there.
// There should be no local slot with the given name. // There should be no local slot with the given name.
DCHECK(scope_info_->StackSlotIndex(*name_handle) < 0); DCHECK(scope_info_->StackSlotIndex(*name_handle) < 0 || is_block_scope());
// Check context slot lookup. // Check context slot lookup.
VariableMode mode; VariableMode mode;
...@@ -706,6 +707,7 @@ bool Scope::HasLazyCompilableOuterContext() const { ...@@ -706,6 +707,7 @@ bool Scope::HasLazyCompilableOuterContext() const {
bool found_non_trivial_declarations = false; bool found_non_trivial_declarations = false;
for (const Scope* scope = outer; scope != NULL; scope = scope->outer_scope_) { for (const Scope* scope = outer; scope != NULL; scope = scope->outer_scope_) {
if (scope->is_with_scope() && !found_non_trivial_declarations) return false; if (scope->is_with_scope() && !found_non_trivial_declarations) return false;
if (scope->is_block_scope() && !scope->decls_.is_empty()) return false;
if (scope->is_declaration_scope() && scope->num_heap_slots() > 0) { if (scope->is_declaration_scope() && scope->num_heap_slots() > 0) {
found_non_trivial_declarations = true; found_non_trivial_declarations = true;
} }
...@@ -1288,7 +1290,7 @@ bool Scope::MustAllocateInContext(Variable* var) { ...@@ -1288,7 +1290,7 @@ bool Scope::MustAllocateInContext(Variable* var) {
if (has_forced_context_allocation()) return true; if (has_forced_context_allocation()) return true;
if (var->mode() == TEMPORARY) return false; if (var->mode() == TEMPORARY) return false;
if (var->mode() == INTERNAL) return true; if (var->mode() == INTERNAL) return true;
if (is_catch_scope() || is_block_scope() || is_module_scope()) return true; if (is_catch_scope() || is_module_scope()) return true;
if (is_script_scope() && IsLexicalVariableMode(var->mode())) return true; if (is_script_scope() && IsLexicalVariableMode(var->mode())) return true;
return var->has_forced_context_allocation() || return var->has_forced_context_allocation() ||
scope_calls_eval_ || scope_calls_eval_ ||
...@@ -1309,7 +1311,11 @@ bool Scope::HasArgumentsParameter(Isolate* isolate) { ...@@ -1309,7 +1311,11 @@ bool Scope::HasArgumentsParameter(Isolate* isolate) {
void Scope::AllocateStackSlot(Variable* var) { void Scope::AllocateStackSlot(Variable* var) {
var->AllocateTo(Variable::LOCAL, num_stack_slots_++); if (is_block_scope()) {
DeclarationScope()->AllocateStackSlot(var);
} else {
var->AllocateTo(Variable::LOCAL, num_stack_slots_++);
}
} }
...@@ -1434,6 +1440,9 @@ void Scope::AllocateNonParameterLocals(Isolate* isolate) { ...@@ -1434,6 +1440,9 @@ void Scope::AllocateNonParameterLocals(Isolate* isolate) {
void Scope::AllocateVariablesRecursively(Isolate* isolate) { void Scope::AllocateVariablesRecursively(Isolate* isolate) {
if (!already_resolved()) {
num_stack_slots_ = 0;
}
// Allocate variables for inner scopes. // Allocate variables for inner scopes.
for (int i = 0; i < inner_scopes_.length(); i++) { for (int i = 0; i < inner_scopes_.length(); i++) {
inner_scopes_[i]->AllocateVariablesRecursively(isolate); inner_scopes_[i]->AllocateVariablesRecursively(isolate);
...@@ -1443,7 +1452,6 @@ void Scope::AllocateVariablesRecursively(Isolate* isolate) { ...@@ -1443,7 +1452,6 @@ void Scope::AllocateVariablesRecursively(Isolate* isolate) {
// variables in inner scopes which might not had been resolved yet. // variables in inner scopes which might not had been resolved yet.
if (already_resolved()) return; if (already_resolved()) return;
// The number of slots required for variables. // The number of slots required for variables.
num_stack_slots_ = 0;
num_heap_slots_ = Context::MIN_CONTEXT_SLOTS; num_heap_slots_ = Context::MIN_CONTEXT_SLOTS;
// Allocate variables for this scope. // Allocate variables for this scope.
......
...@@ -52,6 +52,7 @@ function listener(event, exec_state, event_data, data) { ...@@ -52,6 +52,7 @@ function listener(event, exec_state, event_data, data) {
} }
} catch (e) { } catch (e) {
exception = e; exception = e;
print(e + e.stack);
} }
} }
......
...@@ -1054,10 +1054,9 @@ BeginTest("Classes and methods 1"); ...@@ -1054,10 +1054,9 @@ BeginTest("Classes and methods 1");
listener_delegate = function(exec_state) { listener_delegate = function(exec_state) {
"use strict" "use strict"
CheckScopeChain([debug.ScopeType.Local, CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Block,
debug.ScopeType.Script, debug.ScopeType.Script,
debug.ScopeType.Global], exec_state); debug.ScopeType.Global], exec_state);
CheckScopeContent({C1: class { m() { debugger; }} }, 1, exec_state); CheckScopeContent({}, 1, exec_state);
}; };
(function() { (function() {
......
// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --min-preparse-length=0
'use strict';
let xxx = 1;
let f = undefined;
{
let inner_x = xxx;
f = function() { return inner_x; };
}
assertSame(1, f());
xxx = 42;
{
f = function() { return inner_x1; };
let inner_x1 = xxx;
}
assertSame(42, f());
xxx = 31;
{
let inner_x1 = xxx;
try {
throw new Error();
} catch (e) {
f = function() { return inner_x1; };
}
}
assertSame(31, f());
...@@ -498,7 +498,47 @@ listener_delegate = function(exec_state) { ...@@ -498,7 +498,47 @@ listener_delegate = function(exec_state) {
debug.ScopeType.Local, debug.ScopeType.Local,
debug.ScopeType.Script, debug.ScopeType.Script,
debug.ScopeType.Global], exec_state); debug.ScopeType.Global], exec_state);
CheckScopeContent({}, 0, exec_state); CheckScopeContent({x:undefined}, 0, exec_state);
}; };
uninitialized_1(); uninitialized_1();
EndTest(); EndTest();
// Block scopes shadowing
BeginTest("Block scopes shadowing 1");
function shadowing_1() {
let i = 0;
{
let i = 5;
debugger;
assertEquals(27, i);
}
assertEquals(0, i);
debugger;
assertEquals(27, i);
}
listener_delegate = function (exec_state) {
exec_state.frame(0).evaluate("i = 27");
}
shadowing_1();
EndTest();
// Block scopes shadowing
BeginTest("Block scopes shadowing 2");
function shadowing_2() {
let i = 0;
{
let j = 5;
debugger;
assertEquals(27, j);
}
assertEquals(0, i);
}
listener_delegate = function (exec_state) {
exec_state.frame(0).evaluate("j = 27");
}
shadowing_2();
EndTest();
...@@ -107,13 +107,10 @@ var f2 = (function() { ...@@ -107,13 +107,10 @@ var f2 = (function() {
var mirror = Debug.MakeMirror(f2); var mirror = Debug.MakeMirror(f2);
assertEquals(6, mirror.scopeCount()); assertEquals(5, mirror.scopeCount());
// Implementation artifact: l4 isn't used in closure, but still it is saved.
CheckScope(mirror.scope(0), { l4: 11 }, ScopeType.Block);
CheckScope(mirror.scope(1), { l3: 9 }, ScopeType.Block); CheckScope(mirror.scope(0), { l3: 9 }, ScopeType.Block);
CheckScope(mirror.scope(2), { l1: 6, l2: 7 }, ScopeType.Block); CheckScope(mirror.scope(1), { l2: 7 }, ScopeType.Block);
CheckScope(mirror.scope(3), { v1:3, l0: 0, v3: 5, v6: 11 }, ScopeType.Closure); CheckScope(mirror.scope(2), { v1:3, l0: 0, v3: 5, v6: 11 }, ScopeType.Closure);
CheckScope(mirror.scope(4), { top_level_let: 255 }, ScopeType.Script); CheckScope(mirror.scope(3), { top_level_let: 255 }, ScopeType.Script);
CheckScope(mirror.scope(5), {}, ScopeType.Global); CheckScope(mirror.scope(4), {}, ScopeType.Global);
...@@ -9,7 +9,7 @@ var exception = null; ...@@ -9,7 +9,7 @@ var exception = null;
var break_count = 0; var break_count = 0;
var expected_values = var expected_values =
[ReferenceError, ReferenceError, 0, 0, 0, 0, 0, 1, ReferenceError, ReferenceError]; [ReferenceError, undefined, 0, 0, 0, 0, 1, ReferenceError, ReferenceError];
function listener(event, exec_state, event_data, data) { function listener(event, exec_state, event_data, data) {
try { try {
...@@ -39,6 +39,7 @@ function listener(event, exec_state, event_data, data) { ...@@ -39,6 +39,7 @@ function listener(event, exec_state, event_data, data) {
assertTrue(v instanceof ReferenceError); assertTrue(v instanceof ReferenceError);
} else { } else {
assertSame(expected_values[break_count], v); assertSame(expected_values[break_count], v);
} }
++break_count; ++break_count;
...@@ -62,14 +63,14 @@ var sum = 0; ...@@ -62,14 +63,14 @@ var sum = 0;
debugger; // Break 0. debugger; // Break 0.
for (let i=0; // Break 1. for (let i=0; // Break 1.
i < 1; // Break 2. // Break 3. // Break 6. // Break 7. i < 1; // Break 2. // Break 5. // Break 6.
i++) { i++) {
let key = i; // Break 4. let key = i; // Break 3.
sum += key; // Break 5. sum += key; // Break 4.
} }
}()); // Break 8. }()); // Break 7.
assertNull(exception); // Break 9. assertNull(exception); // Break 8.
assertEquals(expected_breaks, break_count); assertEquals(expected_breaks, break_count);
Debug.setListener(null); Debug.setListener(null);
...@@ -114,6 +114,14 @@ ...@@ -114,6 +114,14 @@
'debug-listbreakpoints': [PASS, NO_VARIANTS], # arm64 nosnap with turbofan 'debug-listbreakpoints': [PASS, NO_VARIANTS], # arm64 nosnap with turbofan
'debug-enable-disable-breakpoints': [PASS, NO_VARIANTS], #arm64 nosnap with turbofan. 'debug-enable-disable-breakpoints': [PASS, NO_VARIANTS], #arm64 nosnap with turbofan.
# Issue 4035: unexpected frame->context() in debugger
'regress/regress-crbug-107996': [PASS, NO_VARIANTS],
'regress/regress-crbug-171715': [PASS, NO_VARIANTS],
'regress/regress-crbug-222893': [PASS, NO_VARIANTS],
'regress/regress-325676': [PASS, NO_VARIANTS],
'debug-evaluate-closure': [PASS, NO_VARIANTS],
'debug-evaluate-with': [PASS, NO_VARIANTS],
# TODO(mstarzinger): Optimizing top-level code flushed out some correctness # TODO(mstarzinger): Optimizing top-level code flushed out some correctness
# issues on ARM and ARM64. # issues on ARM and ARM64.
'es6/math-log2-log10': [PASS, NO_VARIANTS], # on ARM and ARM64. 'es6/math-log2-log10': [PASS, NO_VARIANTS], # on ARM and ARM64.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment