Commit 22558609 authored by rossberg@chromium.org's avatar rossberg@chromium.org

Make instanceof and Object.getPrototypeOf work for proxies, plus a few other tweaks.

R=kmillikin@chromium.org

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

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@8150 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent d7b7d7f8
......@@ -192,7 +192,11 @@ 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_non_object: ["Proxy.", "%0", " called with non-object as handler"],
handler_trap_missing: ["Proxy handler ", "%0", " has no '", "%1", "' trap"],
proxy_prop_not_configurable: ["Trap ", "%1", " of proxy handler ", "%0", " returned non-configurable descriptor for property ", "%2"],
proxy_non_object_prop_names: ["Trap ", "%1", " returned non-object ", "%0"],
proxy_repeated_prop_name: ["Trap ", "%1", " returned repeated property name ", "%2"],
// RangeError
invalid_array_length: ["Invalid array length"],
stack_overflow: ["Maximum call stack size exceeded"],
......
......@@ -2961,7 +2961,7 @@ Object* Map::prototype() {
void Map::set_prototype(Object* value, WriteBarrierMode mode) {
ASSERT(value->IsNull() || value->IsJSObject());
ASSERT(value->IsNull() || value->IsJSReceiver());
WRITE_FIELD(this, kPrototypeOffset, value);
CONDITIONAL_WRITE_BARRIER(GetHeap(), this, kPrototypeOffset, mode);
}
......
......@@ -638,7 +638,7 @@ Object* Object::GetPrototype() {
// The object is either a number, a string, a boolean,
// a real JS object, or a Harmony proxy.
if (heap_object->IsJSObject() || heap_object->IsJSProxy()) {
if (heap_object->IsJSReceiver()) {
return heap_object->map()->prototype();
}
Heap* heap = heap_object->GetHeap();
......@@ -3345,8 +3345,7 @@ void JSObject::LocalLookup(String* name, LookupResult* result) {
}
// Check __proto__ before interceptor.
if (name->Equals(heap->Proto_symbol()) &&
!IsJSContextExtensionObject()) {
if (name->Equals(heap->Proto_symbol()) && !IsJSContextExtensionObject()) {
result->ConstantResult(this);
return;
}
......
......@@ -60,8 +60,9 @@ $Proxy.createFunction = function(handler, callTrap, constructTrap) {
}
$Proxy.create = function(handler, proto) {
if (!IS_SPEC_OBJECT(handler)) throw TypeError
if (!IS_SPEC_OBJECT(proto)) proto = $Object.prototype
if (!IS_SPEC_OBJECT(handler))
throw MakeTypeError("handler_non_object", ["create"])
if (!IS_SPEC_OBJECT(proto)) proto = null // Mozilla does this...
return %CreateJSProxy(handler, proto)
}
......@@ -130,3 +131,7 @@ function DerivedSetTrap(receiver, name, val) {
configurable: true});
return true;
}
function DerivedHasTrap(name) {
return !!this.getPropertyDescriptor(name)
}
......@@ -594,12 +594,26 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSProxy) {
Object* handler = args[0];
Object* prototype = args[1];
Object* used_prototype =
(prototype->IsJSObject() || prototype->IsJSProxy()) ? prototype
: isolate->heap()->null_value();
prototype->IsJSReceiver() ? prototype : isolate->heap()->null_value();
return isolate->heap()->AllocateJSProxy(handler, used_prototype);
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSProxy) {
ASSERT(args.length() == 1);
Object* obj = args[0];
return obj->IsJSProxy()
? isolate->heap()->true_value() : isolate->heap()->false_value();
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetHandler) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSProxy, proxy, args[0]);
return proxy->handler();
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateCatchExtensionObject) {
ASSERT(args.length() == 2);
CONVERT_CHECKED(String, key, args[0]);
......@@ -634,6 +648,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ClassOf) {
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetPrototype) {
NoHandleAllocation ha;
ASSERT(args.length() == 1);
Object* obj = args[0];
if (obj->IsJSGlobalProxy()) obj = obj->GetPrototype();
return obj->GetPrototype();
}
RUNTIME_FUNCTION(MaybeObject*, Runtime_IsInPrototypeChain) {
NoHandleAllocation ha;
ASSERT(args.length() == 2);
......
......@@ -67,6 +67,7 @@ namespace internal {
F(SpecialArrayFunctions, 1, 1) \
F(GetGlobalReceiver, 0, 1) \
\
F(GetPrototype, 1, 1) \
F(IsInPrototypeChain, 2, 1) \
F(SetHiddenPrototype, 2, 1) \
\
......@@ -278,6 +279,8 @@ namespace internal {
\
/* Harmony proxies */ \
F(CreateJSProxy, 2, 1) \
F(IsJSProxy, 1, 1) \
F(GetHandler, 1, 1) \
\
/* Catch context extension objects */ \
F(CreateCatchExtensionObject, 2, 1) \
......
......@@ -336,6 +336,7 @@ function IsInconsistentDescriptor(desc) {
return IsAccessorDescriptor(desc) && IsDataDescriptor(desc);
}
// ES5 8.10.4
function FromPropertyDescriptor(desc) {
if (IS_UNDEFINED(desc)) return desc;
......@@ -399,6 +400,23 @@ function ToPropertyDescriptor(obj) {
}
// For Harmony proxies.
function ToCompletePropertyDescriptor(obj) {
var desc = ToPropertyDescriptor(obj)
if (IsGenericDescriptor(desc) || IsDataDescriptor(desc)) {
if (!("value" in desc)) desc.value = void 0;
if (!("writable" in desc)) desc.writable = false;
} else {
// Is accessor descriptor.
if (!("get" in desc)) desc.get = void 0;
if (!("set" in desc)) desc.set = void 0;
}
if (!("enumerable" in desc)) desc.enumerable = false;
if (!("configurable" in desc)) desc.configurable = false;
return desc;
}
function PropertyDescriptor() {
// Initialize here so they are all in-object and have the same map.
// Default values from ES5 8.6.1.
......@@ -547,9 +565,25 @@ function ConvertDescriptorArrayToDescriptor(desc_array) {
// ES5 section 8.12.2.
function GetProperty(obj, p) {
if (%IsJSProxy(obj)) {
var handler = %GetHandler(obj);
var getProperty = handler.getPropertyDescriptor;
if (IS_UNDEFINED(getProperty)) {
throw MakeTypeError("handler_trap_missing",
[handler, "getPropertyDescriptor"]);
}
var descriptor = getProperty.call(handler, p);
if (IS_UNDEFINED(descriptor)) return descriptor;
var desc = ToCompletePropertyDescriptor(descriptor);
if (!desc.configurable) {
throw MakeTypeError("proxy_prop_not_configurable",
[handler, "getPropertyDescriptor", p, descriptor]);
}
return desc;
}
var prop = GetOwnProperty(obj);
if (!IS_UNDEFINED(prop)) return prop;
var proto = obj.__proto__;
var proto = %GetPrototype(obj);
if (IS_NULL(proto)) return void 0;
return GetProperty(proto, p);
}
......@@ -557,6 +591,12 @@ function GetProperty(obj, p) {
// ES5 section 8.12.6
function HasProperty(obj, p) {
if (%IsJSProxy(obj)) {
var handler = %GetHandler(obj)
var has = handler.has
if (IS_UNDEFINED(has)) has = DerivedHasTrap
return ToBoolean(has.call(handler, obj, p))
}
var desc = GetProperty(obj, p);
return IS_UNDEFINED(desc) ? false : true;
}
......@@ -745,7 +785,7 @@ function DefineOwnProperty(obj, p, desc, should_throw) {
function ObjectGetPrototypeOf(obj) {
if (!IS_SPEC_OBJECT(obj))
throw MakeTypeError("obj_ctor_property_non_object", ["getPrototypeOf"]);
return obj.__proto__;
return %GetPrototype(obj);
}
......@@ -758,11 +798,43 @@ function ObjectGetOwnPropertyDescriptor(obj, p) {
}
// For Harmony proxies
function ToStringArray(obj, trap) {
if (!IS_SPEC_OBJECT(obj)) {
throw MakeTypeError("proxy_non_object_prop_names", [obj, trap]);
}
var n = ToUint32(obj.length);
var array = new $Array(n);
var names = {}
for (var index = 0; index < n; index++) {
var s = ToString(obj[index]);
if (s in names) {
throw MakeTypeError("proxy_repeated_prop_name", [obj, trap, s])
}
array[index] = s;
names.s = 0;
}
return array;
}
// ES5 section 15.2.3.4.
function ObjectGetOwnPropertyNames(obj) {
if (!IS_SPEC_OBJECT(obj))
throw MakeTypeError("obj_ctor_property_non_object", ["getOwnPropertyNames"]);
// Special handling for proxies.
if (%IsJSProxy(obj)) {
var handler = %GetHandler(obj);
var getOwnPropertyNames = handler.getOwnPropertyNames;
if (IS_UNDEFINED(getOwnPropertyNames)) {
throw MakeTypeError("handler_trap_missing",
[handler, "getOwnPropertyNames"]);
}
var names = getOwnPropertyNames.call(handler);
return ToStringArray(names, "getOwnPropertyNames");
}
// Find all the indexed properties.
// Get the local element names.
......
......@@ -363,6 +363,7 @@ static void Generate_JSConstructStubHelper(MacroAssembler* masm,
// If the type of the result (stored in its map) is less than
// FIRST_SPEC_OBJECT_TYPE, it is not an object in the ECMA sense.
STATIC_ASSERT(LAST_SPEC_OBJECT_TYPE == LAST_TYPE);
__ CmpObjectType(rax, FIRST_SPEC_OBJECT_TYPE, rcx);
__ j(above_equal, &exit);
......
......@@ -38,12 +38,35 @@ function TestGet(handler) {
// assertEquals(Object.getOwnPropertyDescriptor(o, "b").value, 42)
}
TestGet({get: function(r, k) { return 42 }})
TestGet({getPropertyDescriptor: function(k) { return {value: 42} }})
TestGet({getPropertyDescriptor: function(k) { return {get value() { return 42 }} }})
TestGet({get: undefined, getPropertyDescriptor: function(k) { return {value: 42} }})
TestGet({
get: function(r, k) { return 42 }
})
TestGet({
get: function(r, k) { return this.get2(r, k) },
get2: function(r, k) { return 42 }
})
TestGet({
getPropertyDescriptor: function(k) { return {value: 42} }
})
TestGet({
getPropertyDescriptor: function(k) { return this.getPropertyDescriptor2(k) },
getPropertyDescriptor2: function(k) { return {value: 42} }
})
TestGet({
getPropertyDescriptor: function(k) {
return {get value() { return 42 }}
}
})
TestGet({
get: undefined,
getPropertyDescriptor: function(k) { return {value: 42} }
})
TestGet(Proxy.create({get: function(pr, pk) { return function(r, k) { return 42 } }}))
TestGet(Proxy.create({
get: function(pr, pk) {
return function(r, k) { return 42 }
}
}))
......@@ -64,46 +87,86 @@ function TestSet(handler) {
// assertEquals(44, val)
}
TestSet({set: function(r, k, v) { key = k; val = v; return true }})
TestSet({getOwnPropertyDescriptor: function(k) { return {writable: true} },
defineProperty: function(k, desc) { key = k, val = desc.value }})
TestSet({getOwnPropertyDescriptor: function(k) { return {get writable() { return true }} },
defineProperty: function(k, desc) { key = k, val = desc.value }})
TestSet({getOwnPropertyDescriptor: function(k) { return {set: function(v) { key = k, val = v }} }})
TestSet({getOwnPropertyDescriptor: function(k) { return null },
getPropertyDescriptor: function(k) { return {writable: true} },
defineProperty: function(k, desc) { key = k, val = desc.value }})
TestSet({getOwnPropertyDescriptor: function(k) { return null },
getPropertyDescriptor: function(k) { return {get writable() { return true }} },
defineProperty: function(k, desc) { key = k, val = desc.value }})
TestSet({getOwnPropertyDescriptor: function(k) { return null },
getPropertyDescriptor: function(k) { return {set: function(v) { key = k, val = v }} }})
TestSet({getOwnPropertyDescriptor: function(k) { return null },
getPropertyDescriptor: function(k) { return null },
defineProperty: function(k, desc) { key = k, val = desc.value }})
TestSet({
set: function(r, k, v) { key = k; val = v; return true }
})
TestSet({
set: function(r, k, v) { return this.set2(r, k, v) },
set2: function(r, k, v) { key = k; val = v; return true }
})
TestSet({
getOwnPropertyDescriptor: function(k) { return {writable: true} },
defineProperty: function(k, desc) { key = k; val = desc.value }
})
TestSet({
getOwnPropertyDescriptor: function(k) {
return this.getOwnPropertyDescriptor2(k)
},
getOwnPropertyDescriptor2: function(k) { return {writable: true} },
defineProperty: function(k, desc) { this.defineProperty2(k, desc) },
defineProperty2: function(k, desc) { key = k; val = desc.value }
})
TestSet({
getOwnPropertyDescriptor: function(k) {
return {get writable() { return true }}
},
defineProperty: function(k, desc) { key = k; val = desc.value }
})
TestSet({
getOwnPropertyDescriptor: function(k) {
return {set: function(v) { key = k; val = v }}
}
})
TestSet({
getOwnPropertyDescriptor: function(k) { return null },
getPropertyDescriptor: function(k) { return {writable: true} },
defineProperty: function(k, desc) { key = k; val = desc.value }
})
TestSet({
getOwnPropertyDescriptor: function(k) { return null },
getPropertyDescriptor: function(k) {
return {get writable() { return true }}
},
defineProperty: function(k, desc) { key = k; val = desc.value }
})
TestSet({
getOwnPropertyDescriptor: function(k) { return null },
getPropertyDescriptor: function(k) {
return {set: function(v) { key = k; val = v }}
}
})
TestSet({
getOwnPropertyDescriptor: function(k) { return null },
getPropertyDescriptor: function(k) { return null },
defineProperty: function(k, desc) { key = k, val = desc.value }
})
TestSet(Proxy.create({get: function(pr, pk) { return function(r, k, v) { key = k; val = v; return true } }}))
TestSet(Proxy.create({
get: function(pr, pk) {
return function(r, k, v) { key = k; val = v; return true }
}
}))
// Comparison.
var o1 = Proxy.create({})
var o2 = Proxy.create({})
function TestComparison(eq) {
var o1 = Proxy.create({})
var o2 = Proxy.create({})
assertTrue(o1 == o1)
assertTrue(o2 == o2)
assertTrue(!(o1 == o2))
assertTrue(!(o1 == {}))
assertTrue(!({} == o2))
assertTrue(!({} == {}))
assertTrue(eq(o1, o1))
assertTrue(eq(o2, o2))
assertTrue(!eq(o1, o2))
assertTrue(!eq(o1, {}))
assertTrue(!eq({}, o2))
assertTrue(!eq({}, {}))
}
assertTrue(o1 === o1)
assertTrue(o2 === o2)
assertTrue(!(o1 === o2))
assertTrue(!(o1 === {}))
assertTrue(!({} === o2))
assertTrue(!({} === {}))
TestComparison(function(o1, o2) { return o1 == o2 })
TestComparison(function(o1, o2) { return o1 === o2 })
TestComparison(function(o1, o2) { return !(o1 != o2) })
TestComparison(function(o1, o2) { return !(o1 !== o2) })
......@@ -114,3 +177,85 @@ assertTrue(typeof Proxy.create({}) == "object")
assertTrue("object" == typeof Proxy.create({}))
// No function proxies yet.
// Instanceof (instanceof).
function TestInstanceof() {
var o = {}
var p1 = Proxy.create({})
var p2 = Proxy.create({}, o)
var p3 = Proxy.create({}, p2)
var f = function() {}
f.prototype = o
var f1 = function() {}
f1.prototype = p1
var f2 = function() {}
f2.prototype = p2
assertTrue(o instanceof Object)
assertFalse(o instanceof f)
assertFalse(o instanceof f1)
assertFalse(o instanceof f2)
assertFalse(p1 instanceof Object)
assertFalse(p1 instanceof f)
assertFalse(p1 instanceof f1)
assertFalse(p1 instanceof f2)
assertTrue(p2 instanceof Object)
assertTrue(p2 instanceof f)
assertFalse(p2 instanceof f1)
assertFalse(p2 instanceof f2)
assertTrue(p3 instanceof Object)
assertTrue(p3 instanceof f)
assertFalse(p3 instanceof f1)
assertTrue(p3 instanceof f2)
}
TestInstanceof()
// Prototype (Object.getPrototypeOf).
function TestPrototype() {
var o = {}
var p1 = Proxy.create({})
var p2 = Proxy.create({}, o)
var p3 = Proxy.create({}, p2)
var p4 = Proxy.create({}, 666)
assertSame(Object.getPrototypeOf(o), Object.prototype)
assertSame(Object.getPrototypeOf(p1), null)
assertSame(Object.getPrototypeOf(p2), o)
assertSame(Object.getPrototypeOf(p3), p2)
assertSame(Object.getPrototypeOf(p4), null)
}
TestPrototype()
// Property names (Object.getOwnPropertyNames).
function TestPropertyNames(names, handler) {
var p = Proxy.create(handler)
assertArrayEquals(names, Object.getOwnPropertyNames(p))
}
TestPropertyNames([], {
getOwnPropertyNames: function() { return [] }
})
TestPropertyNames(["a", "zz", " ", "0"], {
getOwnPropertyNames: function() { return ["a", "zz", " ", 0] }
})
TestPropertyNames(["throw", "function "], {
getOwnPropertyNames: function() { return this.getOwnPropertyNames2() },
getOwnPropertyNames2: function() { return ["throw", "function "] }
})
TestPropertyNames(["[object Object]"], {
get getOwnPropertyNames() {
return function() { return [{}] }
}
})
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