Change the representation of catch contexts.

Before, they had no extra slots and an extension object with one named
property.  Now, they use the extension slot for the property name and have
an extra slot for the thrown object.  This increases the size of the context
itself, but removes overall allocation and eliminates a level of indirection.

R=ager@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8277 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
kmillikin@chromium.org 2011-06-14 12:16:23 +00:00
parent 38a75cf731
commit 7d527f857f
10 changed files with 103 additions and 101 deletions

View File

@ -98,24 +98,38 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
// Check extension/with/global object. // Check extension/with/global object.
if (context->has_extension()) { if (context->has_extension()) {
Handle<JSObject> extension = Handle<JSObject>(context->extension(), if (context->IsCatchContext()) {
isolate); // Catch contexts have the variable name in the extension slot.
// Context extension objects needs to behave as if they have no if (name->Equals(String::cast(context->extension()))) {
// prototype. So even if we want to follow prototype chains, we if (FLAG_trace_contexts) {
// need to only do a local lookup for context extension objects. PrintF("=> found in catch context\n");
if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 || }
extension->IsJSContextExtensionObject()) { *index_ = Context::THROWN_OBJECT_INDEX;
*attributes = extension->GetLocalPropertyAttribute(*name); *attributes = NONE;
} else { return context;
*attributes = extension->GetPropertyAttribute(*name); }
} } else {
if (*attributes != ABSENT) { // Global, function, and with contexts may have an object in the
// property found // extension slot.
if (FLAG_trace_contexts) { Handle<JSObject> extension(JSObject::cast(context->extension()),
PrintF("=> found property in context object %p\n", isolate);
reinterpret_cast<void*>(*extension)); // Context extension objects needs to behave as if they have no
// prototype. So even if we want to follow prototype chains, we
// need to only do a local lookup for context extension objects.
if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0 ||
extension->IsJSContextExtensionObject()) {
*attributes = extension->GetLocalPropertyAttribute(*name);
} else {
*attributes = extension->GetPropertyAttribute(*name);
}
if (*attributes != ABSENT) {
// property found
if (FLAG_trace_contexts) {
PrintF("=> found property in context object %p\n",
reinterpret_cast<void*>(*extension));
}
return extension;
} }
return extension;
} }
} }

View File

@ -181,10 +181,16 @@ class Context: public FixedArray {
CLOSURE_INDEX, CLOSURE_INDEX,
FCONTEXT_INDEX, FCONTEXT_INDEX,
PREVIOUS_INDEX, PREVIOUS_INDEX,
// The extension slot is used for either the global object (in global
// contexts), eval extension object (function contexts), subject of with
// (with contexts), or the variable name (catch contexts).
EXTENSION_INDEX, EXTENSION_INDEX,
GLOBAL_INDEX, GLOBAL_INDEX,
MIN_CONTEXT_SLOTS, MIN_CONTEXT_SLOTS,
// This slot holds the thrown value in catch contexts.
THROWN_OBJECT_INDEX = MIN_CONTEXT_SLOTS,
// These slots are only in global contexts. // These slots are only in global contexts.
GLOBAL_PROXY_INDEX = MIN_CONTEXT_SLOTS, GLOBAL_PROXY_INDEX = MIN_CONTEXT_SLOTS,
SECURITY_TOKEN_INDEX, SECURITY_TOKEN_INDEX,
@ -265,9 +271,9 @@ class Context: public FixedArray {
} }
void set_previous(Context* context) { set(PREVIOUS_INDEX, context); } void set_previous(Context* context) { set(PREVIOUS_INDEX, context); }
bool has_extension() { return unchecked_extension() != NULL; } bool has_extension() { return extension() != NULL; }
JSObject* extension() { return JSObject::cast(unchecked_extension()); } Object* extension() { return get(EXTENSION_INDEX); }
void set_extension(JSObject* object) { set(EXTENSION_INDEX, object); } void set_extension(Object* object) { set(EXTENSION_INDEX, object); }
GlobalObject* global() { GlobalObject* global() {
Object* result = get(GLOBAL_INDEX); Object* result = get(GLOBAL_INDEX);
@ -385,7 +391,6 @@ class Context: public FixedArray {
private: private:
// Unchecked access to the slots. // Unchecked access to the slots.
Object* unchecked_previous() { return get(PREVIOUS_INDEX); } Object* unchecked_previous() { return get(PREVIOUS_INDEX); }
Object* unchecked_extension() { return get(EXTENSION_INDEX); }
#ifdef DEBUG #ifdef DEBUG
// Bootstrapping-aware type checks. // Bootstrapping-aware type checks.

View File

@ -250,10 +250,11 @@ Handle<Context> Factory::NewFunctionContext(int length,
Handle<Context> Factory::NewCatchContext(Handle<Context> previous, Handle<Context> Factory::NewCatchContext(Handle<Context> previous,
Handle<JSObject> extension) { Handle<String> name,
Handle<Object> thrown_object) {
CALL_HEAP_FUNCTION( CALL_HEAP_FUNCTION(
isolate(), isolate(),
isolate()->heap()->AllocateCatchContext(*previous, *extension), isolate()->heap()->AllocateCatchContext(*previous, *name, *thrown_object),
Context); Context);
} }

View File

@ -151,7 +151,8 @@ class Factory {
// Create a catch context. // Create a catch context.
Handle<Context> NewCatchContext(Handle<Context> previous, Handle<Context> NewCatchContext(Handle<Context> previous,
Handle<JSObject> extension); Handle<String> name,
Handle<Object> thrown_object);
// Create a 'with' context. // Create a 'with' context.
Handle<Context> NewWithContext(Handle<Context> previous, Handle<Context> NewWithContext(Handle<Context> previous,

View File

@ -1107,9 +1107,7 @@ void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) {
{ Comment cmnt(masm_, "[ Extend catch context"); { Comment cmnt(masm_, "[ Extend catch context");
__ Push(stmt->name()); __ Push(stmt->name());
__ push(result_register()); __ push(result_register());
__ CallRuntime(Runtime::kCreateCatchExtensionObject, 2); __ CallRuntime(Runtime::kPushCatchContext, 2);
__ push(result_register());
__ CallRuntime(Runtime::kPushCatchContext, 1);
StoreToFrameField(StandardFrameConstants::kContextOffset, StoreToFrameField(StandardFrameConstants::kContextOffset,
context_register()); context_register());
} }

View File

@ -3936,9 +3936,12 @@ MaybeObject* Heap::AllocateFunctionContext(int length, JSFunction* function) {
MaybeObject* Heap::AllocateCatchContext(Context* previous, MaybeObject* Heap::AllocateCatchContext(Context* previous,
JSObject* extension) { String* name,
Object* thrown_object) {
STATIC_ASSERT(Context::MIN_CONTEXT_SLOTS == Context::THROWN_OBJECT_INDEX);
Object* result; Object* result;
{ MaybeObject* maybe_result = AllocateFixedArray(Context::MIN_CONTEXT_SLOTS); { MaybeObject* maybe_result =
AllocateFixedArray(Context::MIN_CONTEXT_SLOTS + 1);
if (!maybe_result->ToObject(&result)) return maybe_result; if (!maybe_result->ToObject(&result)) return maybe_result;
} }
Context* context = reinterpret_cast<Context*>(result); Context* context = reinterpret_cast<Context*>(result);
@ -3946,8 +3949,9 @@ MaybeObject* Heap::AllocateCatchContext(Context* previous,
context->set_closure(previous->closure()); context->set_closure(previous->closure());
context->set_fcontext(previous->fcontext()); context->set_fcontext(previous->fcontext());
context->set_previous(previous); context->set_previous(previous);
context->set_extension(extension); context->set_extension(name);
context->set_global(previous->global()); context->set_global(previous->global());
context->set(Context::THROWN_OBJECT_INDEX, thrown_object);
return context; return context;
} }

View File

@ -648,7 +648,8 @@ class Heap {
// Allocate a catch context. // Allocate a catch context.
MUST_USE_RESULT MaybeObject* AllocateCatchContext(Context* previous, MUST_USE_RESULT MaybeObject* AllocateCatchContext(Context* previous,
JSObject* extension); String* name,
Object* thrown_object);
// Allocate a 'with' context. // Allocate a 'with' context.
MUST_USE_RESULT MaybeObject* AllocateWithContext(Context* previous, MUST_USE_RESULT MaybeObject* AllocateWithContext(Context* previous,
JSObject* extension); JSObject* extension);

View File

@ -3230,7 +3230,7 @@ bool JSObject::ReferencesObject(Object* obj) {
// Check the context extension if any. // Check the context extension if any.
if (context->has_extension()) { if (context->has_extension()) {
return context->extension()->ReferencesObject(obj); return JSObject::cast(context->extension())->ReferencesObject(obj);
} }
} }

View File

@ -614,31 +614,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetHandler) {
} }
RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateCatchExtensionObject) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(String, key, args[0]);
Object* value = args[1];
ASSERT(!value->IsFailure());
// Create a catch context extension object.
JSFunction* constructor =
isolate->context()->global_context()->
context_extension_function();
Object* object;
{ MaybeObject* maybe_object = isolate->heap()->AllocateJSObject(constructor);
if (!maybe_object->ToObject(&object)) return maybe_object;
}
// Assign the exception value to the catch variable and make sure
// that the catch variable is DontDelete.
{ MaybeObject* maybe_value =
// Passing non-strict per ECMA-262 5th Ed. 12.14. Catch, bullet #4.
JSObject::cast(object)->SetProperty(
key, value, DONT_DELETE, kNonStrictMode);
if (!maybe_value->ToObject(&value)) return maybe_value;
}
return object;
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_ClassOf) { RUNTIME_FUNCTION(MaybeObject*, Runtime_ClassOf) {
NoHandleAllocation ha; NoHandleAllocation ha;
ASSERT(args.length() == 1); ASSERT(args.length() == 1);
@ -1310,7 +1285,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareContextSlot) {
Handle<JSObject> context_ext; Handle<JSObject> context_ext;
if (context->has_extension()) { if (context->has_extension()) {
// The function context's extension context exists - use it. // The function context's extension context exists - use it.
context_ext = Handle<JSObject>(context->extension()); context_ext = Handle<JSObject>(JSObject::cast(context->extension()));
} else { } else {
// The function context's extension context does not exists - allocate // The function context's extension context does not exists - allocate
// it. // it.
@ -7942,12 +7917,14 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_PushWithContext) {
RUNTIME_FUNCTION(MaybeObject*, Runtime_PushCatchContext) { RUNTIME_FUNCTION(MaybeObject*, Runtime_PushCatchContext) {
NoHandleAllocation ha; NoHandleAllocation ha;
ASSERT(args.length() == 1); ASSERT(args.length() == 2);
JSObject* extension_object = JSObject::cast(args[0]); String* name = String::cast(args[0]);
Object* thrown_object = args[1];
Context* context; Context* context;
MaybeObject* maybe_context = MaybeObject* maybe_context =
isolate->heap()->AllocateCatchContext(isolate->context(), isolate->heap()->AllocateCatchContext(isolate->context(),
extension_object); name,
thrown_object);
if (!maybe_context->To(&context)) return maybe_context; if (!maybe_context->To(&context)) return maybe_context;
isolate->set_context(context); isolate->set_context(context);
return context; return context;
@ -10200,6 +10177,23 @@ static Handle<JSObject> MaterializeClosure(Isolate* isolate,
} }
// Create a plain JSObject which materializes the scope for the specified
// catch context.
static Handle<JSObject> MaterializeCatchScope(Isolate* isolate,
Handle<Context> context) {
ASSERT(context->IsCatchContext());
Handle<String> name(String::cast(context->extension()));
Handle<Object> thrown_object(context->get(Context::THROWN_OBJECT_INDEX));
Handle<JSObject> catch_scope =
isolate->factory()->NewJSObject(isolate->object_function());
RETURN_IF_EMPTY_HANDLE_VALUE(
isolate,
SetProperty(catch_scope, name, thrown_object, NONE, kNonStrictMode),
Handle<JSObject>());
return catch_scope;
}
// Iterate over the actual scopes visible from a stack frame. All scopes are // 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 // backed by an actual context except the local scope, which is inserted
// "artifically" in the context chain. // "artifically" in the context chain.
@ -10210,10 +10204,6 @@ class ScopeIterator {
ScopeTypeLocal, ScopeTypeLocal,
ScopeTypeWith, ScopeTypeWith,
ScopeTypeClosure, ScopeTypeClosure,
// Every catch block contains an implicit with block (its parameter is
// a JSContextExtensionObject) that extends current scope with a variable
// holding exception object. Such with blocks are treated as scopes of their
// own type.
ScopeTypeCatch ScopeTypeCatch
}; };
@ -10291,12 +10281,7 @@ class ScopeIterator {
return ScopeTypeClosure; return ScopeTypeClosure;
} }
ASSERT(context_->has_extension()); ASSERT(context_->has_extension());
// Current scope is either an explicit with statement or a with statement if (context_->IsCatchContext()) {
// implicitely generated for a catch block.
// If the extension object here is a JSContextExtensionObject then
// current with statement is one frome a catch block otherwise it's a
// regular with statement.
if (context_->extension()->IsJSContextExtensionObject()) {
return ScopeTypeCatch; return ScopeTypeCatch;
} }
return ScopeTypeWith; return ScopeTypeWith;
@ -10307,20 +10292,17 @@ class ScopeIterator {
switch (Type()) { switch (Type()) {
case ScopeIterator::ScopeTypeGlobal: case ScopeIterator::ScopeTypeGlobal:
return Handle<JSObject>(CurrentContext()->global()); return Handle<JSObject>(CurrentContext()->global());
break;
case ScopeIterator::ScopeTypeLocal: case ScopeIterator::ScopeTypeLocal:
// Materialize the content of the local scope into a JSObject. // Materialize the content of the local scope into a JSObject.
return MaterializeLocalScope(isolate_, frame_); return MaterializeLocalScope(isolate_, frame_);
break;
case ScopeIterator::ScopeTypeWith: case ScopeIterator::ScopeTypeWith:
case ScopeIterator::ScopeTypeCatch:
// Return the with object. // Return the with object.
return Handle<JSObject>(CurrentContext()->extension()); return Handle<JSObject>(JSObject::cast(CurrentContext()->extension()));
break; case ScopeIterator::ScopeTypeCatch:
return MaterializeCatchScope(isolate_, CurrentContext());
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());
break;
} }
UNREACHABLE(); UNREACHABLE();
return Handle<JSObject>(); return Handle<JSObject>();
@ -10351,8 +10333,7 @@ class ScopeIterator {
if (!CurrentContext().is_null()) { if (!CurrentContext().is_null()) {
CurrentContext()->Print(); CurrentContext()->Print();
if (CurrentContext()->has_extension()) { if (CurrentContext()->has_extension()) {
Handle<JSObject> extension = Handle<Object> extension(CurrentContext()->extension());
Handle<JSObject>(CurrentContext()->extension());
if (extension->IsJSContextExtensionObject()) { if (extension->IsJSContextExtensionObject()) {
extension->Print(); extension->Print();
} }
@ -10361,34 +10342,27 @@ class ScopeIterator {
break; break;
} }
case ScopeIterator::ScopeTypeWith: { case ScopeIterator::ScopeTypeWith:
PrintF("With:\n"); PrintF("With:\n");
Handle<JSObject> extension = CurrentContext()->extension()->Print();
Handle<JSObject>(CurrentContext()->extension());
extension->Print();
break; break;
}
case ScopeIterator::ScopeTypeCatch: { case ScopeIterator::ScopeTypeCatch:
PrintF("Catch:\n"); PrintF("Catch:\n");
Handle<JSObject> extension = CurrentContext()->extension()->Print();
Handle<JSObject>(CurrentContext()->extension()); CurrentContext()->get(Context::THROWN_OBJECT_INDEX)->Print();
extension->Print();
break; break;
}
case ScopeIterator::ScopeTypeClosure: { case ScopeIterator::ScopeTypeClosure:
PrintF("Closure:\n"); PrintF("Closure:\n");
CurrentContext()->Print(); CurrentContext()->Print();
if (CurrentContext()->has_extension()) { if (CurrentContext()->has_extension()) {
Handle<JSObject> extension = Handle<Object> extension(CurrentContext()->extension());
Handle<JSObject>(CurrentContext()->extension());
if (extension->IsJSContextExtensionObject()) { if (extension->IsJSContextExtensionObject()) {
extension->Print(); extension->Print();
} }
} }
break; break;
}
default: default:
UNREACHABLE(); UNREACHABLE();
@ -10867,10 +10841,17 @@ static Handle<Context> CopyWithContextChain(Isolate* isolate,
HandleScope scope(isolate); HandleScope scope(isolate);
Handle<Context> previous(current->previous()); Handle<Context> previous(current->previous());
Handle<Context> new_previous = CopyWithContextChain(isolate, previous, base); Handle<Context> new_previous = CopyWithContextChain(isolate, previous, base);
Handle<JSObject> extension(JSObject::cast(current->extension())); Handle<Context> new_current;
Handle<Context> new_current = current->IsCatchContext() if (current->IsCatchContext()) {
? isolate->factory()->NewCatchContext(new_previous, extension) Handle<String> name(String::cast(current->extension()));
: isolate->factory()->NewWithContext(new_previous, extension); Handle<Object> thrown_object(current->get(Context::THROWN_OBJECT_INDEX));
new_current =
isolate->factory()->NewCatchContext(new_previous, name, thrown_object);
} else {
Handle<JSObject> extension(JSObject::cast(current->extension()));
new_current =
isolate->factory()->NewWithContext(new_previous, extension);
}
return scope.CloseAndEscape(new_current); return scope.CloseAndEscape(new_current);
} }

View File

@ -283,9 +283,6 @@ namespace internal {
F(IsJSProxy, 1, 1) \ F(IsJSProxy, 1, 1) \
F(GetHandler, 1, 1) \ F(GetHandler, 1, 1) \
\ \
/* Catch context extension objects */ \
F(CreateCatchExtensionObject, 2, 1) \
\
/* Statements */ \ /* Statements */ \
F(NewClosure, 3, 1) \ F(NewClosure, 3, 1) \
F(NewObject, 1, 1) \ F(NewObject, 1, 1) \
@ -300,7 +297,7 @@ namespace internal {
/* Contexts */ \ /* Contexts */ \
F(NewFunctionContext, 1, 1) \ F(NewFunctionContext, 1, 1) \
F(PushWithContext, 1, 1) \ F(PushWithContext, 1, 1) \
F(PushCatchContext, 1, 1) \ F(PushCatchContext, 2, 1) \
F(DeleteContextSlot, 2, 1) \ F(DeleteContextSlot, 2, 1) \
F(LoadContextSlot, 2, 2) \ F(LoadContextSlot, 2, 2) \
F(LoadContextSlotNoReferenceError, 2, 2) \ F(LoadContextSlotNoReferenceError, 2, 2) \