Commit c128b8d9 authored by kasperl@chromium.org's avatar kasperl@chromium.org

Improve code for looking up in context slots in runtime.cc and

use safe casting operations to slot access on contexts when
possible.


git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@588 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 91b19fcf
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
#include "v8.h" #include "v8.h"
#include "bootstrapper.h"
#include "debug.h" #include "debug.h"
#include "scopeinfo.h" #include "scopeinfo.h"
...@@ -99,19 +100,19 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags, ...@@ -99,19 +100,19 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
} }
// check extension/with object // check extension/with object
Handle<JSObject> context_ext(context->extension()); if (context->has_extension()) {
if (*context_ext != NULL) { Handle<JSObject> extension = Handle<JSObject>(context->extension());
if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0) { if ((flags & FOLLOW_PROTOTYPE_CHAIN) == 0) {
*attributes = context_ext->GetLocalPropertyAttribute(*name); *attributes = extension->GetLocalPropertyAttribute(*name);
} else { } else {
*attributes = context_ext->GetPropertyAttribute(*name); *attributes = extension->GetPropertyAttribute(*name);
} }
if (*attributes != ABSENT) { if (*attributes != ABSENT) {
// property found // property found
if (FLAG_trace_contexts) { if (FLAG_trace_contexts) {
PrintF("=> found property in context object %p\n", *context_ext); PrintF("=> found property in context object %p\n", *extension);
} }
return context_ext; return extension;
} }
} }
...@@ -184,11 +185,10 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags, ...@@ -184,11 +185,10 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
// proceed with enclosing context // proceed with enclosing context
if (context->IsGlobalContext()) { if (context->IsGlobalContext()) {
follow_context_chain = false; follow_context_chain = false;
} else if (context->previous() != NULL) { } else if (context->is_function_context()) {
context = Handle<Context>(context->previous());
} else {
ASSERT(context->is_function_context());
context = Handle<Context>(Context::cast(context->closure()->context())); context = Handle<Context>(Context::cast(context->closure()->context()));
} else {
context = Handle<Context>(context->previous());
} }
} while (follow_context_chain); } while (follow_context_chain);
...@@ -196,8 +196,23 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags, ...@@ -196,8 +196,23 @@ Handle<Object> Context::Lookup(Handle<String> name, ContextLookupFlags flags,
if (FLAG_trace_contexts) { if (FLAG_trace_contexts) {
PrintF("=> no property/slot found\n"); PrintF("=> no property/slot found\n");
} }
return Handle<Object>(reinterpret_cast<JSObject*>(NULL)); return Handle<Object>::null();
} }
#ifdef DEBUG
bool Context::IsBootstrappingOrContext(Object* object) {
// During bootstrapping we allow all objects to pass as
// contexts. This is necessary to fix circular dependencies.
return Bootstrapper::IsActive() || object->IsContext();
}
bool Context::IsBootstrappingOrGlobalObject(Object* object) {
// During bootstrapping we allow all objects to pass as global
// objects. This is necessary to fix circular dependencies.
return Bootstrapper::IsActive() || object->IsGlobalObject();
}
#endif
} } // namespace v8::internal } } // namespace v8::internal
...@@ -220,23 +220,24 @@ class Context: public FixedArray { ...@@ -220,23 +220,24 @@ class Context: public FixedArray {
JSFunction* closure() { return JSFunction::cast(get(CLOSURE_INDEX)); } JSFunction* closure() { return JSFunction::cast(get(CLOSURE_INDEX)); }
void set_closure(JSFunction* closure) { set(CLOSURE_INDEX, closure); } void set_closure(JSFunction* closure) { set(CLOSURE_INDEX, closure); }
Context* fcontext() { Context* fcontext() { return Context::cast(get(FCONTEXT_INDEX)); }
return reinterpret_cast<Context*>(get(FCONTEXT_INDEX));
}
void set_fcontext(Context* context) { set(FCONTEXT_INDEX, context); } void set_fcontext(Context* context) { set(FCONTEXT_INDEX, context); }
Context* previous() { Context* previous() {
return reinterpret_cast<Context*>(get(PREVIOUS_INDEX)); Object* result = unchecked_previous();
ASSERT(IsBootstrappingOrContext(result));
return reinterpret_cast<Context*>(result);
} }
void set_previous(Context* context) { set(PREVIOUS_INDEX, context); } void set_previous(Context* context) { set(PREVIOUS_INDEX, context); }
JSObject* extension() { bool has_extension() { return unchecked_extension() != NULL; }
return reinterpret_cast<JSObject*>(get(EXTENSION_INDEX)); JSObject* extension() { return JSObject::cast(unchecked_extension()); }
}
void set_extension(JSObject* object) { set(EXTENSION_INDEX, object); } void set_extension(JSObject* object) { set(EXTENSION_INDEX, object); }
GlobalObject* global() { GlobalObject* global() {
return reinterpret_cast<GlobalObject*>(get(GLOBAL_INDEX)); Object* result = get(GLOBAL_INDEX);
ASSERT(IsBootstrappingOrGlobalObject(result));
return reinterpret_cast<GlobalObject*>(result);
} }
void set_global(GlobalObject* global) { set(GLOBAL_INDEX, global); } void set_global(GlobalObject* global) { set(GLOBAL_INDEX, global); }
...@@ -251,7 +252,7 @@ class Context: public FixedArray { ...@@ -251,7 +252,7 @@ class Context: public FixedArray {
Context* global_context(); Context* global_context();
// Tells if this is a function context (as opposed to a 'with' context). // Tells if this is a function context (as opposed to a 'with' context).
bool is_function_context() { return previous() == NULL; } bool is_function_context() { return unchecked_previous() == NULL; }
// Tells whether the global context is marked with out of memory. // Tells whether the global context is marked with out of memory.
bool has_out_of_memory() { bool has_out_of_memory() {
...@@ -293,7 +294,7 @@ class Context: public FixedArray { ...@@ -293,7 +294,7 @@ class Context: public FixedArray {
// and the name is the property name, and the property exists. // and the name is the property name, and the property exists.
// attributes != ABSENT. // attributes != ABSENT.
// //
// 4) index_ < 0 && result.deref() == NULL: // 4) index_ < 0 && result.is_null():
// there was no context found with the corresponding property. // there was no context found with the corresponding property.
// attributes == ABSENT. // attributes == ABSENT.
Handle<Object> Lookup(Handle<String> name, ContextLookupFlags flags, Handle<Object> Lookup(Handle<String> name, ContextLookupFlags flags,
...@@ -303,6 +304,17 @@ class Context: public FixedArray { ...@@ -303,6 +304,17 @@ class Context: public FixedArray {
static int SlotOffset(int index) { static int SlotOffset(int index) {
return kHeaderSize + index * kPointerSize - kHeapObjectTag; return kHeaderSize + index * kPointerSize - kHeapObjectTag;
} }
private:
// Unchecked access to the slots.
Object* unchecked_previous() { return get(PREVIOUS_INDEX); }
Object* unchecked_extension() { return get(EXTENSION_INDEX); }
#ifdef DEBUG
// Bootstrapping-aware type checks.
static bool IsBootstrappingOrContext(Object* object);
static bool IsBootstrappingOrGlobalObject(Object* object);
#endif
}; };
} } // namespace v8::internal } } // namespace v8::internal
......
...@@ -2139,7 +2139,7 @@ bool JSObject::ReferencesObject(Object* obj) { ...@@ -2139,7 +2139,7 @@ bool JSObject::ReferencesObject(Object* obj) {
} }
// Check the context extension if any. // Check the context extension if any.
if (context->extension() != NULL) { if (context->has_extension()) {
return context->extension()->ReferencesObject(obj); return context->extension()->ReferencesObject(obj);
} }
} }
......
...@@ -504,7 +504,7 @@ static Object* Runtime_DeclareContextSlot(Arguments args) { ...@@ -504,7 +504,7 @@ static Object* Runtime_DeclareContextSlot(Arguments args) {
// "declared" in the function context's extension context, or in the // "declared" in the function context's extension context, or in the
// global context. // global context.
Handle<JSObject> context_ext; Handle<JSObject> context_ext;
if (context->extension() != NULL) { 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>(context->extension());
} else { } else {
...@@ -3347,7 +3347,7 @@ static Object* Runtime_LookupContext(Arguments args) { ...@@ -3347,7 +3347,7 @@ static Object* Runtime_LookupContext(Arguments args) {
Handle<Object> holder = Handle<Object> holder =
context->Lookup(name, flags, &index, &attributes); context->Lookup(name, flags, &index, &attributes);
if (index < 0 && *holder != NULL) { if (index < 0 && !holder.is_null()) {
ASSERT(holder->IsJSObject()); ASSERT(holder->IsJSObject());
return *holder; return *holder;
} }
...@@ -3364,62 +3364,48 @@ static Object* Runtime_LookupContext(Arguments args) { ...@@ -3364,62 +3364,48 @@ static Object* Runtime_LookupContext(Arguments args) {
// compiler to do the right thing. // compiler to do the right thing.
// //
// TODO(1236026): This is a non-portable hack that should be removed. // TODO(1236026): This is a non-portable hack that should be removed.
typedef uint64_t ObjPair; typedef uint64_t ObjectPair;
ObjPair MakePair(Object* x, Object* y) { static inline ObjectPair MakePair(Object* x, Object* y) {
return reinterpret_cast<uint32_t>(x) | return reinterpret_cast<uint32_t>(x) |
(reinterpret_cast<ObjPair>(y) << 32); (reinterpret_cast<ObjectPair>(y) << 32);
} }
static Object* Unhole(Object* x, PropertyAttributes attributes) { static inline Object* Unhole(Object* x, PropertyAttributes attributes) {
ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0); ASSERT(!x->IsTheHole() || (attributes & READ_ONLY) != 0);
USE(attributes); USE(attributes);
return x->IsTheHole() ? Heap::undefined_value() : x; return x->IsTheHole() ? Heap::undefined_value() : x;
} }
static Object* ComputeContextSlotReceiver(Object* holder) { static JSObject* ComputeReceiverForNonGlobal(JSObject* holder) {
// If the "property" we were looking for is a local variable or an ASSERT(!holder->IsGlobalObject());
// argument in a context, the receiver is the global object; see
// ECMA-262, 3rd., 10.1.6 and 10.2.3.
// Contexts and global objects are most common.
if (holder->IsContext()) {
return Context::cast(holder)->global()->global_receiver();
}
if (holder->IsGlobalObject()) {
// If the holder is a global object, we have to be careful to wrap
// it in its proxy if necessary.
return GlobalObject::cast(holder)->global_receiver();
}
Context* top = Top::context(); Context* top = Top::context();
// TODO(125): Find a better - and faster way - of checking for // Get the context extension function.
// arguments and context extension objects. This kinda sucks.
JSFunction* context_extension_function = JSFunction* context_extension_function =
top->global_context()->context_extension_function(); top->global_context()->context_extension_function();
JSObject* arguments_boilerplate = // If the holder isn't a context extension object, we just return it
top->global_context()->arguments_boilerplate(); // as the receiver. This allows arguments objects to be used as
JSFunction* arguments_function = // receivers, but only if they are put in the context scope chain
JSFunction::cast(arguments_boilerplate->map()->constructor()); // explicitly via a with-statement.
// If the holder is an arguments object or a context extension then the Object* constructor = holder->map()->constructor();
// receiver is also the global object; if (constructor != context_extension_function) return holder;
Object* constructor = HeapObject::cast(holder)->map()->constructor(); // Fall back to using the global object as the receiver if the
if (constructor == context_extension_function || // property turns out to be a local variable allocated in a context
constructor == arguments_function) { // extension object - introduced via eval.
return Top::context()->global()->global_receiver(); return top->global()->global_receiver();
}
return holder;
} }
static ObjPair LoadContextSlotHelper(Arguments args, bool throw_error) { static ObjectPair LoadContextSlotHelper(Arguments args, bool throw_error) {
HandleScope scope; HandleScope scope;
ASSERT(args.length() == 2); ASSERT(args.length() == 2);
if (!args[0]->IsContext()) return MakePair(IllegalOperation(), NULL); if (!args[0]->IsContext() || !args[1]->IsString()) {
return MakePair(IllegalOperation(), NULL);
}
Handle<Context> context = args.at<Context>(0); Handle<Context> context = args.at<Context>(0);
Handle<String> name(String::cast(args[1])); Handle<String> name = args.at<String>(1);
int index; int index;
PropertyAttributes attributes; PropertyAttributes attributes;
...@@ -3427,29 +3413,31 @@ static ObjPair LoadContextSlotHelper(Arguments args, bool throw_error) { ...@@ -3427,29 +3413,31 @@ static ObjPair LoadContextSlotHelper(Arguments args, bool throw_error) {
Handle<Object> holder = Handle<Object> holder =
context->Lookup(name, flags, &index, &attributes); context->Lookup(name, flags, &index, &attributes);
// If the index is non-negative, the slot has been found in a local
// variable or a parameter. Read it from the context object or the
// arguments object.
if (index >= 0) { if (index >= 0) {
Handle<Object> receiver = // If the "property" we were looking for is a local variable or an
Handle<Object>(ComputeContextSlotReceiver(*holder)); // argument in a context, the receiver is the global object; see
Handle<Object> value; // ECMA-262, 3rd., 10.1.6 and 10.2.3.
if (holder->IsContext()) { JSObject* receiver = Top::context()->global()->global_receiver();
value = Handle<Object>(Context::cast(*holder)->get(index)); Object* value = (holder->IsContext())
} else { ? Context::cast(*holder)->get(index)
// Arguments object. : JSObject::cast(*holder)->GetElement(index);
value = Handle<Object>(JSObject::cast(*holder)->GetElement(index)); return MakePair(Unhole(value, attributes), receiver);
} }
return MakePair(Unhole(*value, attributes), *receiver);
} // If the holder is found, we read the property from it.
if (!holder.is_null() && holder->IsJSObject()) {
if (*holder != NULL) { JSObject* object = JSObject::cast(*holder);
ASSERT(Handle<JSObject>::cast(holder)->HasProperty(*name)); ASSERT(object->HasProperty(*name));
// Note: As of 5/29/2008, GetProperty does the "unholing" and so JSObject* receiver = (object->IsGlobalObject())
// this call here is redundant. We left it anyway, to be explicit; ? GlobalObject::cast(object)->global_receiver()
// also it's not clear why GetProperty should do the unholing in : ComputeReceiverForNonGlobal(object);
// the first place. // No need to unhole the value here. This is taken care of by the
return MakePair( // GetProperty function.
Unhole(Handle<JSObject>::cast(holder)->GetProperty(*name), Object* value = object->GetProperty(*name);
attributes), return MakePair(value, receiver);
ComputeContextSlotReceiver(*holder));
} }
if (throw_error) { if (throw_error) {
...@@ -3464,12 +3452,12 @@ static ObjPair LoadContextSlotHelper(Arguments args, bool throw_error) { ...@@ -3464,12 +3452,12 @@ static ObjPair LoadContextSlotHelper(Arguments args, bool throw_error) {
} }
static ObjPair Runtime_LoadContextSlot(Arguments args) { static ObjectPair Runtime_LoadContextSlot(Arguments args) {
return LoadContextSlotHelper(args, true); return LoadContextSlotHelper(args, true);
} }
static ObjPair Runtime_LoadContextSlotNoReferenceError(Arguments args) { static ObjectPair Runtime_LoadContextSlotNoReferenceError(Arguments args) {
return LoadContextSlotHelper(args, false); return LoadContextSlotHelper(args, false);
} }
...@@ -3508,7 +3496,7 @@ static Object* Runtime_StoreContextSlot(Arguments args) { ...@@ -3508,7 +3496,7 @@ static Object* Runtime_StoreContextSlot(Arguments args) {
// It is either in an JSObject extension context or it was not found. // It is either in an JSObject extension context or it was not found.
Handle<JSObject> context_ext; Handle<JSObject> context_ext;
if (*holder != NULL) { if (!holder.is_null()) {
// The property exists in the extension context. // The property exists in the extension context.
context_ext = Handle<JSObject>::cast(holder); context_ext = Handle<JSObject>::cast(holder);
} else { } else {
...@@ -4508,7 +4496,7 @@ static Object* Runtime_GetFrameDetails(Arguments args) { ...@@ -4508,7 +4496,7 @@ static Object* Runtime_GetFrameDetails(Arguments args) {
Handle<String> name = info.LocalName(i); Handle<String> name = info.LocalName(i);
// Traverse the context chain to the function context as all local // Traverse the context chain to the function context as all local
// variables stored in the context will be on the function context. // variables stored in the context will be on the function context.
while (context->previous() != NULL) { while (!context->is_function_context()) {
context = Handle<Context>(context->previous()); context = Handle<Context>(context->previous());
} }
ASSERT(context->is_function_context()); ASSERT(context->is_function_context());
...@@ -5024,7 +5012,7 @@ static Object* Runtime_DebugEvaluate(Arguments args) { ...@@ -5024,7 +5012,7 @@ static Object* Runtime_DebugEvaluate(Arguments args) {
} }
// Finally copy any properties from the function context extension. This will // Finally copy any properties from the function context extension. This will
// be variables introduced by eval. // be variables introduced by eval.
if (function_context->extension() != NULL && if (function_context->has_extension() &&
!function_context->IsGlobalContext()) { !function_context->IsGlobalContext()) {
Handle<JSObject> ext(JSObject::cast(function_context->extension())); Handle<JSObject> ext(JSObject::cast(function_context->extension()));
Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext); Handle<FixedArray> keys = GetKeysInFixedArrayFor(ext);
......
...@@ -49,6 +49,9 @@ function F(f) { ...@@ -49,6 +49,9 @@ function F(f) {
// Receiver should be the arguments object here. // Receiver should be the arguments object here.
assertEquals("[object Object]", eval("arguments[0]()")); assertEquals("[object Object]", eval("arguments[0]()"));
with (arguments) {
assertEquals("[object Object]", toString());
}
} }
F(Object.prototype.toString); F(Object.prototype.toString);
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