Implement get trap for proxies.

TODO: reflective Object methods not handled yet.

BUG=
TEST=

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@7902 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
rossberg@chromium.org 2011-05-16 16:33:58 +00:00
parent daa1be1226
commit 05fd779dd3
16 changed files with 147 additions and 48 deletions

View File

@ -2006,8 +2006,9 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from,
break;
case NORMAL:
// Do not occur since the from object has fast properties.
case HANDLER:
case INTERCEPTOR:
// No element in instance descriptors have interceptor type.
// No element in instance descriptors have proxy or interceptor type.
UNREACHABLE();
break;
}

View File

@ -237,7 +237,8 @@ enum BuiltinExtraArguments {
V(STRING_ADD_LEFT, 1) \
V(STRING_ADD_RIGHT, 1) \
V(APPLY_PREPARE, 1) \
V(APPLY_OVERFLOW, 1)
V(APPLY_OVERFLOW, 1) \
V(DERIVED_GET_TRAP, 2)
class BuiltinFunctionTable;

View File

@ -145,11 +145,16 @@ static Handle<Object> Invoke(bool construct,
}
Handle<Object> Execution::Call(Handle<JSFunction> func,
Handle<Object> Execution::Call(Handle<Object> callable,
Handle<Object> receiver,
int argc,
Object*** args,
bool* pending_exception) {
if (!callable->IsJSFunction()) {
callable = TryGetFunctionDelegate(callable, pending_exception);
if (*pending_exception) return callable;
}
Handle<JSFunction> func = Handle<JSFunction>::cast(callable);
return Invoke(false, func, receiver, argc, args, pending_exception);
}

View File

@ -53,7 +53,7 @@ class Execution : public AllStatic {
// *pending_exception tells whether the invoke resulted in
// a pending exception.
//
static Handle<Object> Call(Handle<JSFunction> func,
static Handle<Object> Call(Handle<Object> callable,
Handle<Object> receiver,
int argc,
Object*** args,

View File

@ -361,6 +361,17 @@ Handle<Object> GetProperty(Handle<JSObject> obj,
}
Handle<Object> GetProperty(Handle<Object> obj,
const char* name,
LookupResult* result) {
Isolate* isolate = Isolate::Current();
Handle<String> str = isolate->factory()->LookupAsciiSymbol(name);
PropertyAttributes attributes;
CALL_HEAP_FUNCTION(
isolate, obj->GetProperty(*obj, result, *str, &attributes), Object);
}
Handle<Object> GetProperty(Handle<Object> obj,
Handle<Object> key) {
Isolate* isolate = Isolate::Current();

View File

@ -242,6 +242,10 @@ Handle<Object> SetOwnElement(Handle<JSObject> object,
Handle<Object> GetProperty(Handle<JSObject> obj,
const char* name);
Handle<Object> GetProperty(Handle<Object> obj,
const char* name,
LookupResult* result);
Handle<Object> GetProperty(Handle<Object> obj,
Handle<Object> key);

View File

@ -903,7 +903,8 @@ MaybeObject* LoadIC::Load(State state,
}
PropertyAttributes attr;
if (lookup.IsProperty() && lookup.type() == INTERCEPTOR) {
if (lookup.IsProperty() &&
(lookup.type() == INTERCEPTOR || lookup.type() == HANDLER)) {
// Get the property.
Object* result;
{ MaybeObject* maybe_result =

View File

@ -192,6 +192,7 @@ function FormatMessage(message) {
redefine_disallowed: ["Cannot redefine property: ", "%0"],
define_disallowed: ["Cannot define property, object is not extensible: ", "%0"],
non_extensible_proto: ["%0", " is not extensible"],
handler_trap_missing: ["Proxy handler ", "%0", " has no '", "%1", "' trap"],
// RangeError
invalid_array_length: ["Invalid array length"],
stack_overflow: ["Maximum call stack size exceeded"],

View File

@ -174,11 +174,12 @@ PropertyType.Normal = 0;
PropertyType.Field = 1;
PropertyType.ConstantFunction = 2;
PropertyType.Callbacks = 3;
PropertyType.Interceptor = 4;
PropertyType.MapTransition = 5;
PropertyType.ExternalArrayTransition = 6;
PropertyType.ConstantTransition = 7;
PropertyType.NullDescriptor = 8;
PropertyType.Handler = 4;
PropertyType.Interceptor = 5;
PropertyType.MapTransition = 6;
PropertyType.ExternalArrayTransition = 7;
PropertyType.ConstantTransition = 8;
PropertyType.NullDescriptor = 9;
// Different attributes for a property.

View File

@ -149,7 +149,7 @@ void Object::Lookup(String* name, LookupResult* result) {
} else if (heap_object->IsBoolean()) {
holder = global_context->boolean_function()->instance_prototype();
} else if (heap_object->IsJSProxy()) {
return result->NotFound(); // For now...
return result->HandlerResult();
}
}
ASSERT(holder != NULL); // Cannot handle null or undefined.
@ -225,6 +225,36 @@ MaybeObject* Object::GetPropertyWithCallback(Object* receiver,
}
MaybeObject* Object::GetPropertyWithHandler(Object* receiver_raw,
String* name_raw,
Object* handler_raw) {
Isolate* isolate = name_raw->GetIsolate();
HandleScope scope;
Handle<Object> receiver(receiver_raw);
Handle<Object> name(name_raw);
Handle<Object> handler(handler_raw);
// Extract trap function.
LookupResult lookup;
Handle<Object> trap(v8::internal::GetProperty(handler, "get", &lookup));
if (!lookup.IsFound()) {
// Get the derived `get' property.
Object* derived = isolate->global_context()->builtins()->javascript_builtin(
Builtins::DERIVED_GET_TRAP);
trap = Handle<JSFunction>(JSFunction::cast(derived));
}
// Call trap function.
Object** args[] = { receiver.location(), name.location() };
bool has_exception;
Handle<Object> result =
Execution::Call(trap, handler, ARRAY_SIZE(args), args, &has_exception);
if (has_exception) return Failure::Exception();
return *result;
}
MaybeObject* Object::GetPropertyWithDefinedGetter(Object* receiver,
JSFunction* getter) {
HandleScope scope;
@ -497,26 +527,29 @@ MaybeObject* Object::GetProperty(Object* receiver,
// holder will always be the interceptor holder and the search may
// only continue with a current object just after the interceptor
// holder in the prototype chain.
Object* last = result->IsProperty() ? result->holder() : heap->null_value();
ASSERT(this != this->GetPrototype());
for (Object* current = this; true; current = current->GetPrototype()) {
if (current->IsAccessCheckNeeded()) {
// Check if we're allowed to read from the current object. Note
// that even though we may not actually end up loading the named
// property from the current object, we still check that we have
// access to it.
JSObject* checked = JSObject::cast(current);
if (!heap->isolate()->MayNamedAccess(checked, name, v8::ACCESS_GET)) {
return checked->GetPropertyWithFailedAccessCheck(receiver,
result,
name,
attributes);
// Proxy handlers do not use the proxy's prototype, so we can skip this.
if (!result->IsHandler()) {
Object* last = result->IsProperty() ? result->holder() : heap->null_value();
ASSERT(this != this->GetPrototype());
for (Object* current = this; true; current = current->GetPrototype()) {
if (current->IsAccessCheckNeeded()) {
// Check if we're allowed to read from the current object. Note
// that even though we may not actually end up loading the named
// property from the current object, we still check that we have
// access to it.
JSObject* checked = JSObject::cast(current);
if (!heap->isolate()->MayNamedAccess(checked, name, v8::ACCESS_GET)) {
return checked->GetPropertyWithFailedAccessCheck(receiver,
result,
name,
attributes);
}
}
// Stop traversing the chain once we reach the last object in the
// chain; either the holder of the result or null in case of an
// absent property.
if (current == last) break;
}
// Stop traversing the chain once we reach the last object in the
// chain; either the holder of the result or null in case of an
// absent property.
if (current == last) break;
}
if (!result->IsProperty()) {
@ -542,14 +575,22 @@ MaybeObject* Object::GetProperty(Object* receiver,
result->GetCallbackObject(),
name,
holder);
case HANDLER: {
JSProxy* proxy = JSProxy::cast(this);
return GetPropertyWithHandler(receiver, name, proxy->handler());
}
case INTERCEPTOR: {
JSObject* recvr = JSObject::cast(receiver);
return holder->GetPropertyWithInterceptor(recvr, name, attributes);
}
default:
UNREACHABLE();
return NULL;
case MAP_TRANSITION:
case EXTERNAL_ARRAY_TRANSITION:
case CONSTANT_TRANSITION:
case NULL_DESCRIPTOR:
break;
}
UNREACHABLE();
return NULL;
}
@ -6550,6 +6591,7 @@ const char* Code::PropertyType2String(PropertyType type) {
case FIELD: return "FIELD";
case CONSTANT_FUNCTION: return "CONSTANT_FUNCTION";
case CALLBACKS: return "CALLBACKS";
case HANDLER: return "HANDLER";
case INTERCEPTOR: return "INTERCEPTOR";
case MAP_TRANSITION: return "MAP_TRANSITION";
case EXTERNAL_ARRAY_TRANSITION: return "EXTERNAL_ARRAY_TRANSITION";

View File

@ -808,6 +808,9 @@ class Object : public MaybeObject {
Object* structure,
String* name,
Object* holder);
MUST_USE_RESULT MaybeObject* GetPropertyWithHandler(Object* receiver,
String* name,
Object* handler);
MUST_USE_RESULT MaybeObject* GetPropertyWithDefinedGetter(Object* receiver,
JSFunction* getter);

View File

@ -74,6 +74,9 @@ void LookupResult::Print(FILE* out) {
PrintF(out, " -callback object:\n");
GetCallbackObject()->Print(out);
break;
case HANDLER:
PrintF(out, " -type = lookup proxy\n");
break;
case INTERCEPTOR:
PrintF(out, " -type = lookup interceptor\n");
break;

View File

@ -166,15 +166,6 @@ class CallbacksDescriptor: public Descriptor {
class LookupResult BASE_EMBEDDED {
public:
// Where did we find the result;
enum {
NOT_FOUND,
DESCRIPTOR_TYPE,
DICTIONARY_TYPE,
INTERCEPTOR_TYPE,
CONSTANT_TYPE
} lookup_type_;
LookupResult()
: lookup_type_(NOT_FOUND),
cacheable_(true),
@ -211,6 +202,12 @@ class LookupResult BASE_EMBEDDED {
number_ = entry;
}
void HandlerResult() {
lookup_type_ = HANDLER_TYPE;
holder_ = NULL;
details_ = PropertyDetails(NONE, HANDLER);
}
void InterceptorResult(JSObject* holder) {
lookup_type_ = INTERCEPTOR_TYPE;
holder_ = holder;
@ -245,6 +242,7 @@ class LookupResult BASE_EMBEDDED {
bool IsDontEnum() { return details_.IsDontEnum(); }
bool IsDeleted() { return details_.IsDeleted(); }
bool IsFound() { return lookup_type_ != NOT_FOUND; }
bool IsHandler() { return lookup_type_ == HANDLER_TYPE; }
// Is the result is a property excluding transitions and the null
// descriptor?
@ -345,6 +343,16 @@ class LookupResult BASE_EMBEDDED {
}
private:
// Where did we find the result;
enum {
NOT_FOUND,
DESCRIPTOR_TYPE,
DICTIONARY_TYPE,
HANDLER_TYPE,
INTERCEPTOR_TYPE,
CONSTANT_TYPE
} lookup_type_;
JSObject* holder_;
int number_;
bool cacheable_;

View File

@ -647,3 +647,20 @@ function DefaultString(x) {
// that is cloned when running the code. It is essential that the
// boilerplate gets the right prototype.
%FunctionSetPrototype($Array, new $Array(0));
/* ------------------------------------------
- - - H a r m o n y P r o x i e s - - -
---------------------------------------------
*/
function DERIVED_GET_TRAP(receiver, name) {
var desc = this.getPropertyDescriptor(name);
if (IS_UNDEFINED(desc)) { return desc; }
if ('value' in desc) {
return desc.value;
} else {
if (IS_UNDEFINED(desc.get)) { return desc.get; }
return desc.get().call(receiver); // The proposal says so...
}
}

View File

@ -322,11 +322,12 @@ enum PropertyType {
FIELD = 1, // only in fast mode
CONSTANT_FUNCTION = 2, // only in fast mode
CALLBACKS = 3,
INTERCEPTOR = 4, // only in lookup results, not in descriptors.
MAP_TRANSITION = 5, // only in fast mode
EXTERNAL_ARRAY_TRANSITION = 6,
CONSTANT_TRANSITION = 7, // only in fast mode
NULL_DESCRIPTOR = 8, // only in fast mode
HANDLER = 4, // only in lookup results, not in descriptors
INTERCEPTOR = 5, // only in lookup results, not in descriptors
MAP_TRANSITION = 6, // only in fast mode
EXTERNAL_ARRAY_TRANSITION = 7,
CONSTANT_TRANSITION = 8, // only in fast mode
NULL_DESCRIPTOR = 9, // only in fast mode
// All properties before MAP_TRANSITION are real.
FIRST_PHANTOM_PROPERTY_TYPE = MAP_TRANSITION,
// There are no IC stubs for NULL_DESCRIPTORS. Therefore,

View File

@ -4264,9 +4264,9 @@ TEST(InterceptorPropertyMirror) {
"named_values[%d] instanceof debug.PropertyMirror", i);
CHECK(CompileRun(buffer.start())->BooleanValue());
// 4 is PropertyType.Interceptor
// 5 is PropertyType.Interceptor
OS::SNPrintF(buffer, "named_values[%d].propertyType()", i);
CHECK_EQ(4, CompileRun(buffer.start())->Int32Value());
CHECK_EQ(5, CompileRun(buffer.start())->Int32Value());
OS::SNPrintF(buffer, "named_values[%d].isNative()", i);
CHECK(CompileRun(buffer.start())->BooleanValue());