Revision: 8707
Author: [email protected]
Date: Thu Jul 21 04:20:27 2011
Log: Implement Object.prototype.{hasOwnProperty, propertyIsEnumerable}
for proxies.
Refactor trap invocation.
Test other Object.prototype functionality for proxies.
[email protected]
BUG=v8:1543
TEST=
Review URL: http://codereview.chromium.org/7436004
http://code.google.com/p/v8/source/detail?r=8707
Modified:
/branches/bleeding_edge/src/messages.js
/branches/bleeding_edge/src/proxy.js
/branches/bleeding_edge/src/v8natives.js
/branches/bleeding_edge/test/mjsunit/harmony/proxies.js
=======================================
--- /branches/bleeding_edge/src/messages.js Tue Jul 19 01:19:31 2011
+++ /branches/bleeding_edge/src/messages.js Thu Jul 21 04:20:27 2011
@@ -195,6 +195,7 @@
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"],
+ handler_trap_must_be_callable: ["Proxy handler ", "%0", " has
non-callable '", "%1", "' trap"],
handler_returned_false: ["Proxy handler ", "%0", " returned
false for '", "%1", "' trap"],
handler_returned_undefined: ["Proxy handler ", "%0", " returned
undefined for '", "%1", "' trap"],
proxy_prop_not_configurable: ["Trap ", "%1", " of proxy
handler ", "%0", " returned non-configurable descriptor for
property ", "%2"],
=======================================
--- /branches/bleeding_edge/src/proxy.js Wed Jul 13 04:01:17 2011
+++ /branches/bleeding_edge/src/proxy.js Thu Jul 21 04:20:27 2011
@@ -135,6 +135,10 @@
function DerivedHasTrap(name) {
return !!this.getPropertyDescriptor(name)
}
+
+function DerivedHasOwnTrap(name) {
+ return !!this.getOwnPropertyDescriptor(name)
+}
function DerivedKeysTrap() {
var names = this.getOwnPropertyNames()
=======================================
--- /branches/bleeding_edge/src/v8natives.js Wed Jul 20 03:51:11 2011
+++ /branches/bleeding_edge/src/v8natives.js Thu Jul 21 04:20:27 2011
@@ -232,6 +232,10 @@
// ECMA-262 - 15.2.4.5
function ObjectHasOwnProperty(V) {
+ if (%IsJSProxy(this)) {
+ var handler = %GetHandler(this);
+ return CallTrap1(handler, "hasOwn", DerivedHasOwnTrap,
TO_STRING_INLINE(V));
+ }
return %HasLocalProperty(TO_OBJECT_INLINE(this), TO_STRING_INLINE(V));
}
@@ -249,7 +253,12 @@
// ECMA-262 - 15.2.4.6
function ObjectPropertyIsEnumerable(V) {
- return %IsPropertyEnumerable(ToObject(this), ToString(V));
+ var P = ToString(V);
+ if (%IsJSProxy(this)) {
+ var desc = GetOwnProperty(this, P);
+ return IS_UNDEFINED(desc) ? false : desc.isEnumerable();
+ }
+ return %IsPropertyEnumerable(ToObject(this), P);
}
@@ -310,9 +319,7 @@
throw MakeTypeError("obj_ctor_property_non_object", ["keys"]);
if (%IsJSProxy(obj)) {
var handler = %GetHandler(obj);
- var keys = handler.keys;
- if (IS_UNDEFINED(keys)) keys = DerivedKeysTrap;
- var names = %_CallFunction(handler, keys);
+ var names = CallTrap0(handler, "keys", DerivedKeysTrap);
return ToStringArray(names);
}
return %LocalKeys(obj);
@@ -581,18 +588,43 @@
return desc;
}
+
+
+// For Harmony proxies.
+function GetTrap(handler, name, defaultTrap) {
+ var trap = handler[name];
+ if (IS_UNDEFINED(trap)) {
+ if (IS_UNDEFINED(defaultTrap)) {
+ throw MakeTypeError("handler_trap_missing", [handler, name]);
+ }
+ trap = defaultTrap;
+ } else if (!IS_FUNCTION(trap)) {
+ throw MakeTypeError("handler_trap_must_be_callable", [handler, name]);
+ }
+ return trap;
+}
+
+
+function CallTrap0(handler, name, defaultTrap) {
+ return %_CallFunction(handler, GetTrap(handler, name, defaultTrap));
+}
+
+
+function CallTrap1(handler, name, defaultTrap, x) {
+ return %_CallFunction(handler, x, GetTrap(handler, name, defaultTrap));
+}
+
+
+function CallTrap2(handler, name, defaultTrap, x, y) {
+ return %_CallFunction(handler, x, y, GetTrap(handler, name,
defaultTrap));
+}
// 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 = %_CallFunction(handler, p, getProperty);
+ var descriptor = CallTrap1(obj, "getPropertyDescriptor", void 0, p);
if (IS_UNDEFINED(descriptor)) return descriptor;
var desc = ToCompletePropertyDescriptor(descriptor);
if (!desc.isConfigurable()) {
@@ -613,9 +645,7 @@
function HasProperty(obj, p) {
if (%IsJSProxy(obj)) {
var handler = %GetHandler(obj);
- var has = handler.has;
- if (IS_UNDEFINED(has)) has = DerivedHasTrap;
- return ToBoolean(%_CallFunction(handler, obj, p, has));
+ return ToBoolean(CallTrap1(handler, "has", DerivedHasTrap, p));
}
var desc = GetProperty(obj, p);
return IS_UNDEFINED(desc) ? false : true;
@@ -623,15 +653,11 @@
// ES5 section 8.12.1.
-function GetOwnProperty(obj, p) {
+function GetOwnProperty(obj, v) {
+ var p = ToString(v);
if (%IsJSProxy(obj)) {
var handler = %GetHandler(obj);
- var getOwnProperty = handler.getOwnPropertyDescriptor;
- if (IS_UNDEFINED(getOwnProperty)) {
- throw MakeTypeError("handler_trap_missing",
- [handler, "getOwnPropertyDescriptor"]);
- }
- var descriptor = %_CallFunction(handler, p, getOwnProperty);
+ var descriptor = CallTrap1(handler, "getOwnPropertyDescriptor", void
0, p);
if (IS_UNDEFINED(descriptor)) return descriptor;
var desc = ToCompletePropertyDescriptor(descriptor);
if (!desc.isConfigurable()) {
@@ -644,7 +670,7 @@
// GetOwnProperty returns an array indexed by the constants
// defined in macros.py.
// If p is not a property on obj undefined is returned.
- var props = %GetOwnProperty(ToObject(obj), ToString(p));
+ var props = %GetOwnProperty(ToObject(obj), ToString(v));
// A false value here means that access checks failed.
if (props === false) return void 0;
@@ -656,11 +682,7 @@
// Harmony proxies.
function DefineProxyProperty(obj, p, attributes, should_throw) {
var handler = %GetHandler(obj);
- var defineProperty = handler.defineProperty;
- if (IS_UNDEFINED(defineProperty)) {
- throw MakeTypeError("handler_trap_missing",
[handler, "defineProperty"]);
- }
- var result = %_CallFunction(handler, p, attributes, defineProperty);
+ var result = CallTrap2(handler, "defineProperty", void 0, p, attributes);
if (!ToBoolean(result)) {
if (should_throw) {
throw MakeTypeError("handler_returned_false",
@@ -889,12 +911,7 @@
// 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 = %_CallFunction(handler, getOwnPropertyNames);
+ var names = CallTrap0(handler, "getOwnPropertyNames", void 0);
return ToStringArray(names, "getOwnPropertyNames");
}
@@ -1024,11 +1041,7 @@
// Harmony proxies.
function ProxyFix(obj) {
var handler = %GetHandler(obj);
- var fix = handler.fix;
- if (IS_UNDEFINED(fix)) {
- throw MakeTypeError("handler_trap_missing", [handler, "fix"]);
- }
- var props = %_CallFunction(handler, fix);
+ var props = CallTrap0(handler, "fix", void 0);
if (IS_UNDEFINED(props)) {
throw MakeTypeError("handler_returned_undefined", [handler, "fix"]);
}
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/proxies.js Tue Jul 19
02:38:59 2011
+++ /branches/bleeding_edge/test/mjsunit/harmony/proxies.js Thu Jul 21
04:20:27 2011
@@ -42,22 +42,27 @@
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} }
@@ -83,32 +88,38 @@
TestGetCall({
get: function(r, k) { return function() { return 55 } }
})
+
TestGetCall({
get: function(r, k) { return this.get2(r, k) },
get2: function(r, k) { return function() { return 55 } }
})
+
TestGetCall({
getPropertyDescriptor: function(k) {
return {value: function() { return 55 }}
}
})
+
TestGetCall({
getPropertyDescriptor: function(k) { return
this.getPropertyDescriptor2(k) },
getPropertyDescriptor2: function(k) {
return {value: function() { return 55 }}
}
})
+
TestGetCall({
getPropertyDescriptor: function(k) {
return {get value() { return function() { return 55 } }}
}
})
+
TestGetCall({
get: undefined,
getPropertyDescriptor: function(k) {
return {value: function() { return 55 }}
}
})
+
TestGetCall({
get: function(r, k) {
if (k == "gg") {
@@ -146,14 +157,17 @@
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)
@@ -162,22 +176,26 @@
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) {
@@ -185,12 +203,14 @@
},
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 },
@@ -279,10 +299,12 @@
TestDefine({
defineProperty: function(k, d) { key = k; desc = d; return true }
})
+
TestDefine({
defineProperty: function(k, d) { return this.defineProperty2(k, d) },
defineProperty2: function(k, d) { key = k; desc = d; return true }
})
+
TestDefine(Proxy.create({
get: function(pr, pk) {
return function(k, d) { key = k; desc = d; return true }
@@ -323,10 +345,12 @@
TestDelete({
'delete': function(k) { key = k; return k < "z" }
})
+
TestDelete({
'delete': function(k) { return this.delete2(k) },
delete2: function(k) { key = k; return k < "z" }
})
+
TestDelete(Proxy.create({
get: function(pr, pk) {
return function(k) { key = k; return k < "z" }
@@ -363,6 +387,7 @@
defineProperty: function(k, d) { this["__" + k] = d; return true },
getOwnPropertyDescriptor: function(k) { return this["__" + k] }
})
+
TestDescriptor({
defineProperty: function(k, d) { this["__" + k] = d; return true },
getOwnPropertyDescriptor: function(k) {
@@ -404,7 +429,7 @@
-// Element (in).
+// Membership test (in).
var key
function TestIn(handler) {
@@ -442,26 +467,31 @@
TestIn({
has: function(k) { key = k; return k < "z" }
})
+
TestIn({
has: function(k) { return this.has2(k) },
has2: function(k) { key = k; return k < "z" }
})
+
TestIn({
getPropertyDescriptor: function(k) {
key = k; return k < "z" ? {value: 42} : void 0
}
})
+
TestIn({
getPropertyDescriptor: function(k) { return
this.getPropertyDescriptor2(k) },
getPropertyDescriptor2: function(k) {
key = k; return k < "z" ? {value: 42} : void 0
}
})
+
TestIn({
getPropertyDescriptor: function(k) {
key = k; return k < "z" ? {get value() { return 42 }} : void 0
}
})
+
TestIn({
get: undefined,
getPropertyDescriptor: function(k) {
@@ -477,7 +507,65 @@
-// Instanceof (instanceof).
+// Own Properties (Object.prototype.hasOwnProperty).
+
+var key
+function TestHasOwn(handler) {
+ var o = Proxy.create(handler)
+ assertTrue(Object.prototype.hasOwnProperty.call(o, "a"))
+ assertEquals("a", key)
+ assertTrue(Object.prototype.hasOwnProperty.call(o, 99))
+ assertEquals("99", key)
+ assertFalse(Object.prototype.hasOwnProperty.call(o, "z"))
+ assertEquals("z", key)
+}
+
+TestHasOwn({
+ hasOwn: function(k) { key = k; return k < "z" }
+})
+
+TestHasOwn({
+ hasOwn: function(k) { return this.hasOwn2(k) },
+ hasOwn2: function(k) { key = k; return k < "z" }
+})
+
+TestHasOwn({
+ getOwnPropertyDescriptor: function(k) {
+ key = k; return k < "z" ? {value: 42} : void 0
+ }
+})
+
+TestHasOwn({
+ getOwnPropertyDescriptor: function(k) {
+ return this.getOwnPropertyDescriptor2(k)
+ },
+ getOwnPropertyDescriptor2: function(k) {
+ key = k; return k < "z" ? {value: 42} : void 0
+ }
+})
+
+TestHasOwn({
+ getOwnPropertyDescriptor: function(k) {
+ key = k; return k < "z" ? {get value() { return 42 }} : void 0
+ }
+})
+
+TestHasOwn({
+ hasOwn: undefined,
+ getOwnPropertyDescriptor: function(k) {
+ key = k; return k < "z" ? {value: 42} : void 0
+ }
+})
+
+TestHasOwn(Proxy.create({
+ get: function(pr, pk) {
+ return function(k) { key = k; return k < "z" }
+ }
+}))
+
+
+
+// Instanceof (instanceof)
function TestInstanceof() {
var o = {}
@@ -514,7 +602,7 @@
-// Prototype (Object.getPrototypeOf).
+// Prototype (Object.getPrototypeOf, Object.prototype.isPrototypeOf).
function TestPrototype() {
var o = {}
@@ -528,6 +616,32 @@
assertSame(Object.getPrototypeOf(p2), o)
assertSame(Object.getPrototypeOf(p3), p2)
assertSame(Object.getPrototypeOf(p4), null)
+
+ assertTrue(Object.prototype.isPrototypeOf(o))
+ assertFalse(Object.prototype.isPrototypeOf(p1))
+ assertTrue(Object.prototype.isPrototypeOf(p2))
+ assertTrue(Object.prototype.isPrototypeOf(p3))
+ assertFalse(Object.prototype.isPrototypeOf(p4))
+ assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, o))
+ assertFalse(Object.prototype.isPrototypeOf.call(Object.prototype, p1))
+ assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, p2))
+ assertTrue(Object.prototype.isPrototypeOf.call(Object.prototype, p3))
+ assertFalse(Object.prototype.isPrototypeOf.call(Object.prototype, p4))
+ assertFalse(Object.prototype.isPrototypeOf.call(o, o))
+ assertFalse(Object.prototype.isPrototypeOf.call(o, p1))
+ assertTrue(Object.prototype.isPrototypeOf.call(o, p2))
+ assertTrue(Object.prototype.isPrototypeOf.call(o, p3))
+ assertFalse(Object.prototype.isPrototypeOf.call(o, p4))
+ assertFalse(Object.prototype.isPrototypeOf.call(p1, p1))
+ assertFalse(Object.prototype.isPrototypeOf.call(p1, o))
+ assertFalse(Object.prototype.isPrototypeOf.call(p1, p2))
+ assertFalse(Object.prototype.isPrototypeOf.call(p1, p3))
+ assertFalse(Object.prototype.isPrototypeOf.call(p1, p4))
+ assertFalse(Object.prototype.isPrototypeOf.call(p2, p1))
+ assertFalse(Object.prototype.isPrototypeOf.call(p2, p2))
+ assertTrue(Object.prototype.isPrototypeOf.call(p2, p3))
+ assertFalse(Object.prototype.isPrototypeOf.call(p2, p4))
+ assertFalse(Object.prototype.isPrototypeOf.call(p3, p2))
}
TestPrototype()
@@ -544,13 +658,16 @@
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 [{}] }
@@ -566,22 +683,27 @@
TestKeys([], {
keys: function() { return [] }
})
+
TestKeys(["a", "zz", " ", "0"], {
keys: function() { return ["a", "zz", " ", 0] }
})
+
TestKeys(["throw", "function "], {
keys: function() { return this.keys2() },
keys2: function() { return ["throw", "function "] }
})
+
TestKeys(["[object Object]"], {
get keys() {
return function() { return [{}] }
}
})
+
TestKeys(["a", "0"], {
getOwnPropertyNames: function() { return ["a", 23, "zz", "", 0] },
getOwnPropertyDescriptor: function(k) { return {enumerable: k.length ==
1} }
})
+
TestKeys(["23", "zz", ""], {
getOwnPropertyNames: function() { return this.getOwnPropertyNames2() },
getOwnPropertyNames2: function() { return ["a", 23, "zz", "", 0] },
@@ -590,6 +712,7 @@
},
getOwnPropertyDescriptor2: function(k) { return {enumerable: k.length !=
1} }
})
+
TestKeys(["a", "b", "c", "5"], {
get getOwnPropertyNames() {
return function() { return ["0", 4, "a", "b", "c", 5] }
@@ -598,6 +721,7 @@
return function(k) { return {enumerable: k >= "44"} }
}
})
+
TestKeys([], {
get getOwnPropertyNames() {
return function() { return ["a", "b", "c"] }
@@ -661,6 +785,7 @@
TestFix([], {
fix: function() { return {} }
})
+
TestFix(["a", "b", "c", "d", "zz"], {
fix: function() {
return {
@@ -672,12 +797,14 @@
}
}
})
+
TestFix(["a"], {
fix: function() { return this.fix2() },
fix2: function() {
return {a: {value: 4, writable: true, configurable: true, enumerable:
true}}
}
})
+
TestFix(["b"], {
get fix() {
return function() {
@@ -685,3 +812,87 @@
}
}
})
+
+
+
+// String conversion (Object.prototype.toString,
Object.prototype.toLocaleString)
+
+var key
+function TestToString(handler) {
+ var o = Proxy.create(handler)
+ key = ""
+ assertEquals("[object Object]", Object.prototype.toString.call(o))
+ assertEquals("", key)
+ assertEquals("my_proxy", Object.prototype.toLocaleString.call(o))
+ assertEquals("toString", key)
+}
+
+TestToString({
+ get: function(r, k) { key = k; return function() { return "my_proxy" } }
+})
+
+TestToString({
+ get: function(r, k) { return this.get2(r, k) },
+ get2: function(r, k) { key = k; return function() { return "my_proxy" } }
+})
+
+TestToString(Proxy.create({
+ get: function(pr, pk) {
+ return function(r, k) { key = k; return function() { return "my_proxy"
} }
+ }
+}))
+
+
+
+// Value conversion (Object.prototype.toValue)
+
+function TestValueOf(handler) {
+ var o = Proxy.create(handler)
+ assertSame(o, Object.prototype.valueOf.call(o))
+}
+
+TestValueOf({})
+
+
+
+// Enumerability (Object.prototype.propertyIsEnumerable)
+
+var key
+function TestIsEnumerable(handler) {
+ var o = Proxy.create(handler)
+ assertTrue(Object.prototype.propertyIsEnumerable.call(o, "a"))
+ assertEquals("a", key)
+ assertTrue(Object.prototype.propertyIsEnumerable.call(o, 2))
+ assertEquals("2", key)
+ assertFalse(Object.prototype.propertyIsEnumerable.call(o, "z"))
+ assertEquals("z", key)
+}
+
+TestIsEnumerable({
+ getOwnPropertyDescriptor: function(k) {
+ key = k; return {enumerable: k < "z", configurable: true}
+ },
+})
+
+TestIsEnumerable({
+ getOwnPropertyDescriptor: function(k) {
+ return this.getOwnPropertyDescriptor2(k)
+ },
+ getOwnPropertyDescriptor2: function(k) {
+ key = k; return {enumerable: k < "z", configurable: true}
+ },
+})
+
+TestIsEnumerable({
+ getOwnPropertyDescriptor: function(k) {
+ key = k; return {get enumerable() { return k < "z" }, configurable:
true}
+ },
+})
+
+TestIsEnumerable(Proxy.create({
+ get: function(pr, pk) {
+ return function(k) {
+ key = k; return {enumerable: k < "z", configurable: true}
+ }
+ }
+}))
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev