Commit 3c7ca304 authored by keuchel@chromium.org's avatar keuchel@chromium.org

Preliminary code for block scopes and block contexts.

BUG=
TEST=

Review URL: http://codereview.chromium.org/7549008

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8911 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 431b51ac
......@@ -50,7 +50,8 @@ Block::Block(Isolate* isolate,
bool is_initializer_block)
: BreakableStatement(isolate, labels, TARGET_FOR_NAMED_ONLY),
statements_(capacity),
is_initializer_block_(is_initializer_block) {
is_initializer_block_(is_initializer_block),
block_scope_(NULL) {
}
......
......@@ -359,9 +359,13 @@ class Block: public BreakableStatement {
ZoneList<Statement*>* statements() { return &statements_; }
bool is_initializer_block() const { return is_initializer_block_; }
Scope* block_scope() const { return block_scope_; }
void set_block_scope(Scope* block_scope) { block_scope_ = block_scope; }
private:
ZoneList<Statement*> statements_;
bool is_initializer_block_;
Scope* block_scope_;
};
......
......@@ -109,7 +109,7 @@ Handle<Object> Context::Lookup(Handle<String> name,
}
// Check extension/with/global object.
if (context->has_extension()) {
if (!context->IsBlockContext() && context->has_extension()) {
if (context->IsCatchContext()) {
// Catch contexts have the variable name in the extension slot.
if (name->Equals(String::cast(context->extension()))) {
......@@ -121,6 +121,9 @@ Handle<Object> Context::Lookup(Handle<String> name,
return context;
}
} else {
ASSERT(context->IsGlobalContext() ||
context->IsFunctionContext() ||
context->IsWithContext());
// Global, function, and with contexts may have an object in the
// extension slot.
Handle<JSObject> extension(JSObject::cast(context->extension()),
......@@ -145,11 +148,20 @@ Handle<Object> Context::Lookup(Handle<String> name,
}
}
// Only functions can have locals, parameters, and a function name.
if (context->IsFunctionContext()) {
// Check serialized scope information of functions and blocks. Only
// functions can have parameters, and a function name.
if (context->IsFunctionContext() || context->IsBlockContext()) {
// We may have context-local slots. Check locals in the context.
Handle<SerializedScopeInfo> scope_info(
context->closure()->shared()->scope_info(), isolate);
Handle<SerializedScopeInfo> scope_info;
if (context->IsFunctionContext()) {
scope_info = Handle<SerializedScopeInfo>(
context->closure()->shared()->scope_info(), isolate);
} else {
ASSERT(context->IsBlockContext());
scope_info = Handle<SerializedScopeInfo>(
SerializedScopeInfo::cast(context->extension()), isolate);
}
Variable::Mode mode;
int index = scope_info->ContextSlotIndex(*name, &mode);
ASSERT(index < 0 || index >= MIN_CONTEXT_SLOTS);
......
......@@ -295,6 +295,10 @@ class Context: public FixedArray {
Map* map = this->map();
return map == map->GetHeap()->with_context_map();
}
bool IsBlockContext() {
Map* map = this->map();
return map == map->GetHeap()->block_context_map();
}
// Tells whether the global context is marked with out of memory.
inline bool has_out_of_memory();
......
......@@ -740,6 +740,7 @@ Persistent<Context> Shell::CreateEvaluationContext() {
// Initialize the global objects
Handle<ObjectTemplate> global_template = CreateGlobalTemplate();
Persistent<Context> context = Context::New(NULL, global_template);
ASSERT(!context.IsEmpty());
Context::Scope scope(context);
#ifndef V8_SHARED
......
......@@ -103,7 +103,8 @@ Debug.ScopeType = { Global: 0,
Local: 1,
With: 2,
Closure: 3,
Catch: 4 };
Catch: 4,
Block: 5 };
// Current debug state.
......
......@@ -34,6 +34,7 @@
#include "macro-assembler.h"
#include "objects.h"
#include "objects-visiting.h"
#include "scopeinfo.h"
namespace v8 {
namespace internal {
......@@ -291,6 +292,19 @@ Handle<Context> Factory::NewWithContext(Handle<JSFunction> function,
}
Handle<Context> Factory::NewBlockContext(
Handle<JSFunction> function,
Handle<Context> previous,
Handle<SerializedScopeInfo> scope_info) {
CALL_HEAP_FUNCTION(
isolate(),
isolate()->heap()->AllocateBlockContext(*function,
*previous,
*scope_info),
Context);
}
Handle<Struct> Factory::NewStruct(InstanceType type) {
CALL_HEAP_FUNCTION(
isolate(),
......@@ -734,6 +748,14 @@ Handle<JSFunction> Factory::NewFunctionWithoutPrototype(Handle<String> name,
}
Handle<SerializedScopeInfo> Factory::NewSerializedScopeInfo(int length) {
CALL_HEAP_FUNCTION(
isolate(),
isolate()->heap()->AllocateSerializedScopeInfo(length),
SerializedScopeInfo);
}
Handle<Code> Factory::NewCode(const CodeDesc& desc,
Code::Flags flags,
Handle<Object> self_ref,
......
......@@ -167,6 +167,11 @@ class Factory {
Handle<Context> previous,
Handle<JSObject> extension);
// Create a 'block' context.
Handle<Context> NewBlockContext(Handle<JSFunction> function,
Handle<Context> previous,
Handle<SerializedScopeInfo> scope_info);
// Return the Symbol matching the passed in string.
Handle<String> SymbolFromString(Handle<String> value);
......@@ -277,6 +282,8 @@ class Factory {
Handle<Context> context,
PretenureFlag pretenure = TENURED);
Handle<SerializedScopeInfo> NewSerializedScopeInfo(int length);
Handle<Code> NewCode(const CodeDesc& desc,
Code::Flags flags,
Handle<Object> self_reference,
......
......@@ -100,6 +100,7 @@ private:
DEFINE_bool(harmony_typeof, false, "enable harmony semantics for typeof")
DEFINE_bool(harmony_proxies, false, "enable harmony proxies")
DEFINE_bool(harmony_weakmaps, false, "enable harmony weak maps")
DEFINE_bool(harmony_block_scoping, false, "enable harmony block scoping")
// Flags for experimental implementation features.
DEFINE_bool(unbox_double_arrays, true, "automatically unbox arrays of doubles")
......
......@@ -35,6 +35,7 @@
#include "macro-assembler.h"
#include "prettyprinter.h"
#include "scopes.h"
#include "scopeinfo.h"
#include "stub-cache.h"
namespace v8 {
......@@ -845,8 +846,23 @@ void FullCodeGenerator::VisitBlock(Block* stmt) {
Breakable nested_statement(this, stmt);
SetStatementPosition(stmt);
Scope* saved_scope = scope();
if (stmt->block_scope() != NULL) {
{ Comment cmnt(masm_, "[ Extend block context");
scope_ = stmt->block_scope();
__ Push(scope_->GetSerializedScopeInfo());
PushFunctionArgumentForContextAllocation();
__ CallRuntime(Runtime::kPushBlockContext, 2);
StoreToFrameField(StandardFrameConstants::kContextOffset,
context_register());
}
{ Comment cmnt(masm_, "[ Declarations");
VisitDeclarations(scope_->declarations());
}
}
PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS);
VisitStatements(stmt->statements());
scope_ = saved_scope;
__ bind(nested_statement.break_target());
PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS);
}
......
......@@ -1745,6 +1745,12 @@ bool Heap::CreateInitialMaps() {
set_fixed_cow_array_map(Map::cast(obj));
ASSERT(fixed_array_map() != fixed_cow_array_map());
{ MaybeObject* maybe_obj =
AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
if (!maybe_obj->ToObject(&obj)) return false;
}
set_serialized_scope_info_map(Map::cast(obj));
{ MaybeObject* maybe_obj = AllocateMap(HEAP_NUMBER_TYPE, HeapNumber::kSize);
if (!maybe_obj->ToObject(&obj)) return false;
}
......@@ -1906,6 +1912,12 @@ bool Heap::CreateInitialMaps() {
}
set_with_context_map(Map::cast(obj));
{ MaybeObject* maybe_obj =
AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
if (!maybe_obj->ToObject(&obj)) return false;
}
set_block_context_map(Map::cast(obj));
{ MaybeObject* maybe_obj =
AllocateMap(FIXED_ARRAY_TYPE, kVariableSizeSentinel);
if (!maybe_obj->ToObject(&obj)) return false;
......@@ -4017,6 +4029,37 @@ MaybeObject* Heap::AllocateWithContext(JSFunction* function,
}
MaybeObject* Heap::AllocateBlockContext(JSFunction* function,
Context* previous,
SerializedScopeInfo* scope_info) {
Object* result;
{ MaybeObject* maybe_result =
AllocateFixedArray(scope_info->NumberOfContextSlots());
if (!maybe_result->ToObject(&result)) return maybe_result;
}
// TODO(keuchel): properly initialize context slots.
Context* context = reinterpret_cast<Context*>(result);
context->set_map(block_context_map());
context->set_closure(function);
context->set_previous(previous);
context->set_extension(scope_info);
context->set_global(previous->global());
return context;
}
MaybeObject* Heap::AllocateSerializedScopeInfo(int length) {
Object* result;
{ MaybeObject* maybe_result = AllocateFixedArray(length, TENURED);
if (!maybe_result->ToObject(&result)) return maybe_result;
}
SerializedScopeInfo* scope_info =
reinterpret_cast<SerializedScopeInfo*>(result);
scope_info->set_map(serialized_scope_info_map());
return scope_info;
}
MaybeObject* Heap::AllocateStruct(InstanceType type) {
Map* map;
switch (type) {
......
......@@ -65,6 +65,7 @@ inline Heap* _inline_get_heap_();
V(Map, heap_number_map, HeapNumberMap) \
V(Map, global_context_map, GlobalContextMap) \
V(Map, fixed_array_map, FixedArrayMap) \
V(Map, serialized_scope_info_map, SerializedScopeInfoMap) \
V(Map, fixed_cow_array_map, FixedCOWArrayMap) \
V(Map, fixed_double_array_map, FixedDoubleArrayMap) \
V(Object, no_interceptor_result_sentinel, NoInterceptorResultSentinel) \
......@@ -111,6 +112,7 @@ inline Heap* _inline_get_heap_();
V(Map, function_context_map, FunctionContextMap) \
V(Map, catch_context_map, CatchContextMap) \
V(Map, with_context_map, WithContextMap) \
V(Map, block_context_map, BlockContextMap) \
V(Map, code_map, CodeMap) \
V(Map, oddball_map, OddballMap) \
V(Map, global_property_cell_map, GlobalPropertyCellMap) \
......@@ -221,7 +223,8 @@ inline Heap* _inline_get_heap_();
V(closure_symbol, "(closure)") \
V(use_strict, "use strict") \
V(dot_symbol, ".") \
V(anonymous_function_symbol, "(anonymous function)")
V(anonymous_function_symbol, "(anonymous function)") \
V(block_scope_symbol, ".block")
// Forward declarations.
class GCTracer;
......@@ -484,6 +487,9 @@ class Heap {
// Allocates an empty code cache.
MUST_USE_RESULT MaybeObject* AllocateCodeCache();
// Allocates a serialized scope info.
MUST_USE_RESULT MaybeObject* AllocateSerializedScopeInfo(int length);
// Allocates an empty PolymorphicCodeCache.
MUST_USE_RESULT MaybeObject* AllocatePolymorphicCodeCache();
......@@ -669,6 +675,11 @@ class Heap {
Context* previous,
JSObject* extension);
// Allocate a block context.
MUST_USE_RESULT MaybeObject* AllocateBlockContext(JSFunction* function,
Context* previous,
SerializedScopeInfo* info);
// Allocates a new utility object in the old generation.
MUST_USE_RESULT MaybeObject* AllocateStruct(InstanceType type);
......
......@@ -2480,6 +2480,9 @@ void HGraphBuilder::VisitBlock(Block* stmt) {
ASSERT(!HasStackOverflow());
ASSERT(current_block() != NULL);
ASSERT(current_block()->HasPredecessor());
if (stmt->block_scope() != NULL) {
return Bailout("ScopedBlock");
}
BreakAndContinueInfo break_info(stmt);
{ BreakAndContinueScope push(&break_info, this);
CHECK_BAILOUT(VisitStatements(stmt->statements()));
......
......@@ -195,7 +195,8 @@ ScopeType = { Global: 0,
Local: 1,
With: 2,
Closure: 3,
Catch: 4 };
Catch: 4,
Block: 5 };
// Mirror hierarchy:
......
......@@ -552,7 +552,8 @@ bool Object::IsContext() {
return (map == heap->function_context_map() ||
map == heap->catch_context_map() ||
map == heap->with_context_map() ||
map == heap->global_context_map());
map == heap->global_context_map() ||
map == heap->block_context_map());
}
return false;
}
......@@ -565,6 +566,13 @@ bool Object::IsGlobalContext() {
}
bool Object::IsSerializedScopeInfo() {
return Object::IsHeapObject() &&
HeapObject::cast(this)->map() ==
HeapObject::cast(this)->GetHeap()->serialized_scope_info_map();
}
bool Object::IsJSFunction() {
return Object::IsHeapObject()
&& HeapObject::cast(this)->map()->instance_type() == JS_FUNCTION_TYPE;
......
......@@ -743,6 +743,7 @@ class MaybeObject BASE_EMBEDDED {
V(FixedDoubleArray) \
V(Context) \
V(GlobalContext) \
V(SerializedScopeInfo) \
V(JSFunction) \
V(Code) \
V(Oddball) \
......
......@@ -584,7 +584,8 @@ Parser::Parser(Handle<Script> script,
pre_data_(pre_data),
fni_(NULL),
stack_overflow_(false),
parenthesized_function_(false) {
parenthesized_function_(false),
harmony_block_scoping_(false) {
AstNode::ResetIds();
}
......@@ -809,6 +810,9 @@ void Parser::ReportMessageAt(Scanner::Location source_location,
isolate()->Throw(*result, &location);
}
void Parser::SetHarmonyBlockScoping(bool block_scoping) {
harmony_block_scoping_ = block_scoping;
}
// Base class containing common code for the different finder classes used by
// the parser.
......@@ -1487,6 +1491,8 @@ Statement* Parser::ParseFunctionDeclaration(bool* ok) {
Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) {
if (harmony_block_scoping_) return ParseScopedBlock(labels, ok);
// Block ::
// '{' Statement* '}'
......@@ -1510,6 +1516,56 @@ Block* Parser::ParseBlock(ZoneStringList* labels, bool* ok) {
}
Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) {
// Construct block expecting 16 statements.
Block* body = new(zone()) Block(isolate(), labels, 16, false);
Scope* saved_scope = top_scope_;
Scope* block_scope = NewScope(top_scope_,
Scope::BLOCK_SCOPE,
inside_with());
body->set_block_scope(block_scope);
block_scope->DeclareLocal(isolate()->factory()->block_scope_symbol(),
Variable::VAR);
if (top_scope_->is_strict_mode()) {
block_scope->EnableStrictMode();
}
top_scope_ = block_scope;
// Parse the statements and collect escaping labels.
TargetCollector collector;
Target target(&this->target_stack_, &collector);
Expect(Token::LBRACE, CHECK_OK);
{
Target target_body(&this->target_stack_, body);
InitializationBlockFinder block_finder(top_scope_, target_stack_);
while (peek() != Token::RBRACE) {
Statement* stat = ParseStatement(NULL, CHECK_OK);
if (stat && !stat->IsEmpty()) {
body->AddStatement(stat);
block_finder.Update(stat);
}
}
}
Expect(Token::RBRACE, CHECK_OK);
// Create exit block.
Block* exit = new(zone()) Block(isolate(), NULL, 1, false);
exit->AddStatement(new(zone()) ExitContextStatement());
// Create a try-finally statement.
TryFinallyStatement* try_finally =
new(zone()) TryFinallyStatement(body, exit);
try_finally->set_escaping_targets(collector.targets());
top_scope_ = saved_scope;
// Create a result block.
Block* result = new(zone()) Block(isolate(), NULL, 1, false);
result->AddStatement(try_finally);
return result;
}
Block* Parser::ParseVariableStatement(bool* ok) {
// VariableStatement ::
// VariableDeclarations ';'
......@@ -5105,6 +5161,8 @@ bool ParserApi::Parse(CompilationInfo* info) {
Handle<Script> script = info->script();
if (info->is_lazy()) {
Parser parser(script, true, NULL, NULL);
parser.SetHarmonyBlockScoping(!info->is_native() &&
FLAG_harmony_block_scoping);
result = parser.ParseLazy(info);
} else {
// Whether we allow %identifier(..) syntax.
......@@ -5112,6 +5170,8 @@ bool ParserApi::Parse(CompilationInfo* info) {
info->allows_natives_syntax() || FLAG_allow_natives_syntax;
ScriptDataImpl* pre_data = info->pre_parse_data();
Parser parser(script, allow_natives_syntax, info->extension(), pre_data);
parser.SetHarmonyBlockScoping(!info->is_native() &&
FLAG_harmony_block_scoping);
if (pre_data != NULL && pre_data->has_error()) {
Scanner::Location loc = pre_data->MessageLocation();
const char* message = pre_data->BuildMessage();
......@@ -5130,7 +5190,6 @@ bool ParserApi::Parse(CompilationInfo* info) {
info->StrictMode());
}
}
info->SetFunction(result);
return (result != NULL);
}
......
......@@ -435,6 +435,7 @@ class Parser {
void ReportMessageAt(Scanner::Location loc,
const char* message,
Vector<Handle<String> > args);
void SetHarmonyBlockScoping(bool block_scoping);
private:
// Limit on number of function parameters is chosen arbitrarily.
......@@ -483,6 +484,7 @@ class Parser {
Statement* ParseFunctionDeclaration(bool* ok);
Statement* ParseNativeDeclaration(bool* ok);
Block* ParseBlock(ZoneStringList* labels, bool* ok);
Block* ParseScopedBlock(ZoneStringList* labels, bool* ok);
Block* ParseVariableStatement(bool* ok);
Block* ParseVariableDeclarations(bool accept_IN,
Handle<String>* out,
......@@ -715,6 +717,7 @@ class Parser {
// Heuristically that means that the function will be called immediately,
// so never lazily compile it.
bool parenthesized_function_;
bool harmony_block_scoping_;
friend class LexicalScope;
};
......
......@@ -8316,6 +8316,30 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PushCatchContext) {
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_PushBlockContext) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
SerializedScopeInfo* scope_info = SerializedScopeInfo::cast(args[0]);
JSFunction* function;
if (args[1]->IsSmi()) {
// A smi sentinel indicates a context nested inside global code rather
// than some function. There is a canonical empty function that can be
// gotten from the global context.
function = isolate->context()->global_context()->closure();
} else {
function = JSFunction::cast(args[1]);
}
Context* context;
MaybeObject* maybe_context =
isolate->heap()->AllocateBlockContext(function,
isolate->context(),
scope_info);
if (!maybe_context->To(&context)) return maybe_context;
isolate->set_context(context);
return context;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_DeleteContextSlot) {
HandleScope scope(isolate);
ASSERT(args.length() == 2);
......@@ -10641,6 +10665,34 @@ static Handle<JSObject> MaterializeCatchScope(Isolate* isolate,
}
// Create a plain JSObject which materializes the block scope for the specified
// block context.
static Handle<JSObject> MaterializeBlockScope(
Isolate* isolate,
Handle<Context> context) {
ASSERT(context->IsBlockContext());
Handle<SerializedScopeInfo> serialized_scope_info(
SerializedScopeInfo::cast(context->extension()));
ScopeInfo<> scope_info(*serialized_scope_info);
// Allocate and initialize a JSObject with all the arguments, stack locals
// heap locals and extension properties of the debugged function.
Handle<JSObject> block_scope =
isolate->factory()->NewJSObject(isolate->object_function());
// Fill all context locals.
if (scope_info.number_of_context_slots() > Context::MIN_CONTEXT_SLOTS) {
if (!CopyContextLocalsToScopeObject(isolate,
serialized_scope_info, scope_info,
context, block_scope)) {
return Handle<JSObject>();
}
}
return block_scope;
}
// Iterate over the actual scopes visible from a stack frame. All scopes are
// backed by an actual context except the local scope, which is inserted
// "artifically" in the context chain.
......@@ -10651,7 +10703,8 @@ class ScopeIterator {
ScopeTypeLocal,
ScopeTypeWith,
ScopeTypeClosure,
ScopeTypeCatch
ScopeTypeCatch,
ScopeTypeBlock
};
ScopeIterator(Isolate* isolate,
......@@ -10677,8 +10730,10 @@ class ScopeIterator {
} else if (context_->IsFunctionContext()) {
at_local_ = true;
} else if (context_->closure() != *function_) {
// The context_ is a with or catch block from the outer function.
ASSERT(context_->IsWithContext() || context_->IsCatchContext());
// The context_ is a block or with or catch block from the outer function.
ASSERT(context_->IsWithContext() ||
context_->IsCatchContext() ||
context_->IsBlockContext());
at_local_ = true;
}
}
......@@ -10733,6 +10788,9 @@ class ScopeIterator {
if (context_->IsCatchContext()) {
return ScopeTypeCatch;
}
if (context_->IsBlockContext()) {
return ScopeTypeBlock;
}
ASSERT(context_->IsWithContext());
return ScopeTypeWith;
}
......@@ -10753,6 +10811,8 @@ class ScopeIterator {
case ScopeIterator::ScopeTypeClosure:
// Materialize the content of the closure scope into a JSObject.
return MaterializeClosure(isolate_, CurrentContext());
case ScopeIterator::ScopeTypeBlock:
return MaterializeBlockScope(isolate_, CurrentContext());
}
UNREACHABLE();
return Handle<JSObject>();
......@@ -11309,7 +11369,13 @@ static Handle<Context> CopyWithContextChain(Isolate* isolate,
new_previous,
name,
thrown_object);
} else if (current->IsBlockContext()) {
Handle<SerializedScopeInfo> scope_info(
SerializedScopeInfo::cast(current->extension()));
new_current =
isolate->factory()->NewBlockContext(function, new_previous, scope_info);
} else {
ASSERT(current->IsWithContext());
Handle<JSObject> extension(JSObject::cast(current->extension()));
new_current =
isolate->factory()->NewWithContext(function, new_previous, extension);
......
......@@ -310,6 +310,7 @@ namespace internal {
F(NewFunctionContext, 1, 1) \
F(PushWithContext, 2, 1) \
F(PushCatchContext, 3, 1) \
F(PushBlockContext, 2, 1) \
F(DeleteContextSlot, 2, 1) \
F(LoadContextSlot, 2, 2) \
F(LoadContextSlotNoReferenceError, 2, 2) \
......
......@@ -313,7 +313,7 @@ Handle<SerializedScopeInfo> ScopeInfo<Allocator>::Serialize() {
stack_slots_.length();
Handle<SerializedScopeInfo> data(
SerializedScopeInfo::cast(*FACTORY->NewFixedArray(length, TENURED)));
SerializedScopeInfo::cast(*FACTORY->NewSerializedScopeInfo(length)));
AssertNoAllocation nogc;
Object** p0 = data->data_start();
......
......@@ -107,7 +107,7 @@ class SerializedScopeInfo : public FixedArray {
public :
static SerializedScopeInfo* cast(Object* object) {
ASSERT(object->IsFixedArray());
ASSERT(object->IsSerializedScopeInfo());
return reinterpret_cast<SerializedScopeInfo*>(object);
}
......
......@@ -146,7 +146,9 @@ Scope::Scope(Scope* outer_scope, Type type)
}
Scope::Scope(Scope* inner_scope, Handle<SerializedScopeInfo> scope_info)
Scope::Scope(Scope* inner_scope,
Type type,
Handle<SerializedScopeInfo> scope_info)
: isolate_(Isolate::Current()),
inner_scopes_(4),
variables_(),
......@@ -156,7 +158,7 @@ Scope::Scope(Scope* inner_scope, Handle<SerializedScopeInfo> scope_info)
decls_(4),
already_resolved_(true) {
ASSERT(!scope_info.is_null());
SetDefaults(FUNCTION_SCOPE, NULL, scope_info);
SetDefaults(type, NULL, scope_info);
if (scope_info->HasHeapAllocatedLocals()) {
num_heap_slots_ = scope_info_->NumberOfContextSlots();
}
......@@ -232,8 +234,13 @@ Scope* Scope::DeserializeScopeChain(CompilationInfo* info,
if (context->IsFunctionContext()) {
SerializedScopeInfo* scope_info =
context->closure()->shared()->scope_info();
current_scope =
new Scope(current_scope, Handle<SerializedScopeInfo>(scope_info));
current_scope = new Scope(current_scope, FUNCTION_SCOPE,
Handle<SerializedScopeInfo>(scope_info));
} else if (context->IsBlockContext()) {
SerializedScopeInfo* scope_info =
SerializedScopeInfo::cast(context->extension());
current_scope = new Scope(current_scope, BLOCK_SCOPE,
Handle<SerializedScopeInfo>(scope_info));
} else {
ASSERT(context->IsCatchContext());
String* name = String::cast(context->extension());
......@@ -294,10 +301,13 @@ void Scope::Initialize(bool inside_with) {
// instead load them directly from the stack. Currently, the only
// such parameter is 'this' which is passed on the stack when
// invoking scripts
if (is_catch_scope()) {
if (is_catch_scope() || is_block_scope()) {
ASSERT(outer_scope() != NULL);
receiver_ = outer_scope()->receiver();
} else {
ASSERT(is_function_scope() ||
is_global_scope() ||
is_eval_scope());
Variable* var =
variables_.Declare(this,
isolate_->factory()->this_symbol(),
......@@ -559,13 +569,22 @@ int Scope::ContextChainLength(Scope* scope) {
Scope* Scope::DeclarationScope() {
Scope* scope = this;
while (scope->is_catch_scope()) {
while (scope->is_catch_scope() ||
scope->is_block_scope()) {
scope = scope->outer_scope();
}
return scope;
}
Handle<SerializedScopeInfo> Scope::GetSerializedScopeInfo() {
if (scope_info_.is_null()) {
scope_info_ = SerializedScopeInfo::Create(this);
}
return scope_info_;
}
#ifdef DEBUG
static const char* Header(Scope::Type type) {
switch (type) {
......@@ -573,6 +592,7 @@ static const char* Header(Scope::Type type) {
case Scope::FUNCTION_SCOPE: return "function";
case Scope::GLOBAL_SCOPE: return "global";
case Scope::CATCH_SCOPE: return "catch";
case Scope::BLOCK_SCOPE: return "block";
}
UNREACHABLE();
return NULL;
......@@ -598,9 +618,11 @@ static void PrintVar(PrettyPrinter* printer, int indent, Variable* var) {
PrintF("; // ");
if (var->rewrite() != NULL) {
PrintF("%s, ", printer->Print(var->rewrite()));
if (var->is_accessed_from_inner_scope()) PrintF(", ");
if (var->is_accessed_from_inner_function_scope()) PrintF(", ");
}
if (var->is_accessed_from_inner_function_scope()) {
PrintF("inner scope access");
}
if (var->is_accessed_from_inner_scope()) PrintF("inner scope access");
PrintF("\n");
}
}
......@@ -721,7 +743,7 @@ Variable* Scope::NonLocal(Handle<String> name, Variable::Mode mode) {
// another variable that is introduced dynamically via an 'eval' call
// or a 'with' statement).
Variable* Scope::LookupRecursive(Handle<String> name,
bool inner_lookup,
bool from_inner_function,
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
......@@ -737,7 +759,7 @@ Variable* Scope::LookupRecursive(Handle<String> name,
// (Even if there is an 'eval' in this scope which introduces the
// same variable again, the resulting variable remains the same.
// Note that enclosing 'with' statements are handled at the call site.)
if (!inner_lookup)
if (!from_inner_function)
return var;
} else {
......@@ -753,7 +775,10 @@ Variable* Scope::LookupRecursive(Handle<String> name,
var = function_;
} else if (outer_scope_ != NULL) {
var = outer_scope_->LookupRecursive(name, true, invalidated_local);
var = outer_scope_->LookupRecursive(
name,
is_function_scope() || from_inner_function,
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
......@@ -770,8 +795,8 @@ Variable* Scope::LookupRecursive(Handle<String> name,
ASSERT(var != NULL);
// If this is a lookup from an inner scope, mark the variable.
if (inner_lookup) {
var->MarkAsAccessedFromInnerScope();
if (from_inner_function) {
var->MarkAsAccessedFromInnerFunctionScope();
}
// If the variable we have found is just a guess, invalidate the
......@@ -922,11 +947,12 @@ bool Scope::MustAllocate(Variable* var) {
// via an eval() call. This is only possible if the variable has a
// visible name.
if ((var->is_this() || var->name()->length() > 0) &&
(var->is_accessed_from_inner_scope() ||
(var->is_accessed_from_inner_function_scope() ||
scope_calls_eval_ ||
inner_scope_calls_eval_ ||
scope_contains_with_ ||
is_catch_scope())) {
is_catch_scope() ||
is_block_scope())) {
var->set_is_used(true);
}
// Global variables do not need to be allocated.
......@@ -943,8 +969,8 @@ bool Scope::MustAllocateInContext(Variable* var) {
// Exceptions: temporary variables are never allocated in a context;
// catch-bound variables are always allocated in a context.
if (var->mode() == Variable::TEMPORARY) return false;
if (is_catch_scope()) return true;
return var->is_accessed_from_inner_scope() ||
if (is_catch_scope() || is_block_scope()) return true;
return var->is_accessed_from_inner_function_scope() ||
scope_calls_eval_ ||
inner_scope_calls_eval_ ||
scope_contains_with_ ||
......@@ -1010,7 +1036,7 @@ void Scope::AllocateParameterLocals() {
if (uses_nonstrict_arguments) {
// Give the parameter a use from an inner scope, to force allocation
// to the context.
var->MarkAsAccessedFromInnerScope();
var->MarkAsAccessedFromInnerFunctionScope();
}
if (MustAllocate(var)) {
......
......@@ -93,7 +93,8 @@ class Scope: public ZoneObject {
EVAL_SCOPE, // The top-level scope for an eval source.
FUNCTION_SCOPE, // The top-level scope for a function.
GLOBAL_SCOPE, // The top-level scope for a program or a top-level eval.
CATCH_SCOPE // The scope introduced by catch.
CATCH_SCOPE, // The scope introduced by catch.
BLOCK_SCOPE // The scope introduced by a new block.
};
Scope(Scope* outer_scope, Type type);
......@@ -204,6 +205,7 @@ class Scope: public ZoneObject {
bool is_function_scope() const { return type_ == FUNCTION_SCOPE; }
bool is_global_scope() const { return type_ == GLOBAL_SCOPE; }
bool is_catch_scope() const { return type_ == CATCH_SCOPE; }
bool is_block_scope() const { return type_ == BLOCK_SCOPE; }
bool is_strict_mode() const { return strict_mode_; }
bool is_strict_mode_eval_scope() const {
return is_eval_scope() && is_strict_mode();
......@@ -294,6 +296,8 @@ class Scope: public ZoneObject {
// where var declarations will be hoisted to in the implementation.
Scope* DeclarationScope();
Handle<SerializedScopeInfo> GetSerializedScopeInfo();
// ---------------------------------------------------------------------------
// Strict mode support.
bool IsDeclared(Handle<String> name) {
......@@ -397,7 +401,7 @@ class Scope: public ZoneObject {
// Variable resolution.
Variable* LookupRecursive(Handle<String> name,
bool inner_lookup,
bool from_inner_function,
Variable** invalidated_local);
void ResolveVariable(Scope* global_scope,
Handle<Context> context,
......@@ -425,8 +429,8 @@ class Scope: public ZoneObject {
void AllocateVariablesRecursively();
private:
// Construct a function scope based on the scope info.
Scope(Scope* inner_scope, Handle<SerializedScopeInfo> scope_info);
// Construct a function or block scope based on the scope info.
Scope(Scope* inner_scope, Type type, Handle<SerializedScopeInfo> scope_info);
// Construct a catch scope with a binding for the name.
Scope(Scope* inner_scope, Handle<String> catch_variable_name);
......
......@@ -544,6 +544,7 @@ class PartialSerializer : public Serializer {
ASSERT(!o->IsScript());
return o->IsString() || o->IsSharedFunctionInfo() ||
o->IsHeapNumber() || o->IsCode() ||
o->IsSerializedScopeInfo() ||
o->map() == HEAP->fixed_cow_array_map();
}
......
......@@ -92,7 +92,7 @@ Variable::Variable(Scope* scope,
local_if_not_shadowed_(NULL),
rewrite_(NULL),
is_valid_LHS_(is_valid_LHS),
is_accessed_from_inner_scope_(false),
is_accessed_from_inner_function_scope_(false),
is_used_(false) {
// names must be canonicalized for fast equality checks
ASSERT(name->IsSymbol());
......
......@@ -95,11 +95,12 @@ class Variable: public ZoneObject {
Handle<String> name() const { return name_; }
Mode mode() const { return mode_; }
bool is_accessed_from_inner_scope() const {
return is_accessed_from_inner_scope_;
bool is_accessed_from_inner_function_scope() const {
return is_accessed_from_inner_function_scope_;
}
void MarkAsAccessedFromInnerScope() {
is_accessed_from_inner_scope_ = true;
void MarkAsAccessedFromInnerFunctionScope() {
ASSERT(mode_ != TEMPORARY);
is_accessed_from_inner_function_scope_ = true;
}
bool is_used() { return is_used_; }
void set_is_used(bool flag) { is_used_ = flag; }
......@@ -156,7 +157,7 @@ class Variable: public ZoneObject {
bool is_valid_LHS_;
// Usage info.
bool is_accessed_from_inner_scope_; // set by variable resolver
bool is_accessed_from_inner_function_scope_; // set by variable resolver
bool is_used_;
};
......
......@@ -146,6 +146,7 @@ var knownProblems = {
"NewStrictArgumentsFast": true,
"PushWithContext": true,
"PushCatchContext": true,
"PushBlockContext": true,
"LazyCompile": true,
"LazyRecompile": true,
"NotifyDeoptimized": true,
......
// Copyright 2011 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.
// Flags: --allow-natives-syntax
// Test deserialization of block contexts during lazy compilation
// of closures.
function f() {
var g;
{
// TODO(keuchel): introduce let
var x = 0;
g = function () {
x = x + 1;
return x;
}
}
return g;
}
var o = f();
assertEquals(1, o());
assertEquals(2, o());
assertEquals(3, o());
%OptimizeFunctionOnNextCall(o);
assertEquals(4, o());
// Copyright 2011 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.
// Flags: --allow-natives-syntax --harmony-block-scoping
// Test functionality of block scopes.
// Hoisting of var declarations.
function f1() {
{
var x = 1;
var y;
}
assertEquals(1, x)
assertEquals(undefined, y)
}
f1();
// Dynamic lookup through block scopes.
function f2(one) {
var x = one + 1;
// TODO(keuchel): introduce let
// let y = one + 2;
if (one == 1) {
// Parameter
assertEquals(1, eval('one'));
// Function local var variable
assertEquals(2, eval('x'));
// Function local let variable
// TODO(keuchel): introduce let
// assertEquals(3, eval('y'));
}
}
f2(1);
This diff is collapsed.
// Copyright 2011 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.
// Flags: --expose-debug-as debug
// Test debug evaluation for functions without local context, but with
// nested catch contexts.
function f() {
{ // Line 1.
var i = 1; // Line 2. // TODO(keuchel): introduce let
try { // Line 3.
throw 'stuff'; // Line 4.
} catch (e) { // Line 5.
x = 2; // Line 6.
}
}
};
// Get the Debug object exposed from the debug context global object.
Debug = debug.Debug
// Set breakpoint on line 6.
var bp = Debug.setBreakPoint(f, 6);
function listener(event, exec_state, event_data, data) {
if (event == Debug.DebugEvent.Break) {
result = exec_state.frame().evaluate("i").value();
}
};
// Add the debug event listener.
Debug.setListener(listener);
result = -1;
f();
assertEquals(1, result);
// Clear breakpoint.
Debug.clearBreakPoint(bp);
// Get rid of the debug event listener.
Debug.setListener(null);
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