Revision: 8673
Author: [email protected]
Date: Mon Jul 18 06:04:52 2011
Log: Implement sealing, freezing, and related functions for proxies.
[email protected]
BUG=v8:1543
TEST=
Review URL: http://codereview.chromium.org/7391001
http://code.google.com/p/v8/source/detail?r=8673
Modified:
/branches/bleeding_edge/src/factory.cc
/branches/bleeding_edge/src/factory.h
/branches/bleeding_edge/src/heap.cc
/branches/bleeding_edge/src/heap.h
/branches/bleeding_edge/src/messages.js
/branches/bleeding_edge/src/objects-inl.h
/branches/bleeding_edge/src/objects.cc
/branches/bleeding_edge/src/objects.h
/branches/bleeding_edge/src/runtime.cc
/branches/bleeding_edge/src/runtime.h
/branches/bleeding_edge/src/v8natives.js
/branches/bleeding_edge/test/mjsunit/harmony/proxies.js
=======================================
--- /branches/bleeding_edge/src/factory.cc Mon Jul 4 23:19:53 2011
+++ /branches/bleeding_edge/src/factory.cc Mon Jul 18 06:04:52 2011
@@ -890,6 +890,13 @@
isolate()->heap()->AllocateJSProxy(*handler, *prototype),
JSProxy);
}
+
+
+void Factory::BecomeJSObject(Handle<JSProxy> object) {
+ CALL_HEAP_FUNCTION_VOID(
+ isolate(),
+ isolate()->heap()->ReinitializeJSProxyAsJSObject(*object));
+}
Handle<SharedFunctionInfo> Factory::NewSharedFunctionInfo(
=======================================
--- /branches/bleeding_edge/src/factory.h Wed Jun 29 00:41:42 2011
+++ /branches/bleeding_edge/src/factory.h Mon Jul 18 06:04:52 2011
@@ -253,6 +253,9 @@
Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object>
prototype);
+ // Change the type of the argument into a regular JS object and
reinitialize.
+ void BecomeJSObject(Handle<JSProxy> object);
+
Handle<JSFunction> NewFunction(Handle<String> name,
Handle<Object> prototype);
=======================================
--- /branches/bleeding_edge/src/heap.cc Wed Jul 13 02:09:04 2011
+++ /branches/bleeding_edge/src/heap.cc Mon Jul 18 06:04:52 2011
@@ -3267,14 +3267,13 @@
MaybeObject* maybe_map_obj = AllocateMap(JS_PROXY_TYPE, JSProxy::kSize);
if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj;
map->set_prototype(prototype);
- map->set_pre_allocated_property_fields(1);
- map->set_inobject_properties(1);
// Allocate the proxy object.
Object* result;
MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
if (!maybe_result->ToObject(&result)) return maybe_result;
JSProxy::cast(result)->set_handler(handler);
+ JSProxy::cast(result)->set_padding(Smi::FromInt(0));
return result;
}
@@ -3412,6 +3411,36 @@
// Return the new clone.
return clone;
}
+
+
+MaybeObject* Heap::ReinitializeJSProxyAsJSObject(JSProxy* object) {
+ // Allocate fresh map.
+ // TODO(rossberg): Once we optimize proxies, cache these maps.
+ Map* map;
+ MaybeObject* maybe_map_obj =
+ AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+ if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj;
+
+ // Check that the receiver has the same size as a fresh object.
+ ASSERT(map->instance_size() == object->map()->instance_size());
+
+ map->set_prototype(object->map()->prototype());
+
+ // Allocate the backing storage for the properties.
+ int prop_size = map->unused_property_fields() -
map->inobject_properties();
+ Object* properties;
+ { MaybeObject* maybe_properties = AllocateFixedArray(prop_size, TENURED);
+ if (!maybe_properties->ToObject(&properties)) return maybe_properties;
+ }
+
+ // Reset the map for the object.
+ object->set_map(map);
+
+ // Reinitialize the object from the constructor map.
+ InitializeJSObjectFromMap(JSObject::cast(object),
+ FixedArray::cast(properties), map);
+ return object;
+}
MaybeObject* Heap::ReinitializeJSGlobalProxy(JSFunction* constructor,
=======================================
--- /branches/bleeding_edge/src/heap.h Wed Jul 13 02:09:04 2011
+++ /branches/bleeding_edge/src/heap.h Mon Jul 18 06:04:52 2011
@@ -441,6 +441,11 @@
MUST_USE_RESULT MaybeObject* AllocateJSProxy(Object* handler,
Object* prototype);
+ // Reinitialize a JSProxy into an (empty) JSObject. The receiver
+ // must have the same size as an empty object. The object is
reinitialized
+ // and behaves as an object that has been freshly allocated.
+ MUST_USE_RESULT MaybeObject* ReinitializeJSProxyAsJSObject(JSProxy*
object);
+
// Reinitialize an JSGlobalProxy based on a constructor. The object
// must have the same size as objects allocated using the
// constructor. The object is reinitialized and behaves as an
=======================================
--- /branches/bleeding_edge/src/messages.js Thu Jul 7 05:41:20 2011
+++ /branches/bleeding_edge/src/messages.js Mon Jul 18 06:04:52 2011
@@ -195,7 +195,8 @@
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_failed: ["Proxy handler ", "%0", " returned
false for '", "%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"],
proxy_non_object_prop_names: ["Trap ", "%1", " returned
non-object ", "%0"],
proxy_repeated_prop_name: ["Trap ", "%1", " returned repeated
property name ", "%2"],
=======================================
--- /branches/bleeding_edge/src/objects-inl.h Wed Jul 13 09:36:26 2011
+++ /branches/bleeding_edge/src/objects-inl.h Mon Jul 18 06:04:52 2011
@@ -3761,6 +3761,7 @@
ACCESSORS(JSProxy, handler, Object, kHandlerOffset)
+ACCESSORS(JSProxy, padding, Object, kPaddingOffset)
Address Foreign::address() {
=======================================
--- /branches/bleeding_edge/src/objects.cc Fri Jul 15 02:10:20 2011
+++ /branches/bleeding_edge/src/objects.cc Mon Jul 18 06:04:52 2011
@@ -2301,6 +2301,18 @@
}
+void JSProxy::Fix() {
+ Isolate* isolate = GetIsolate();
+ HandleScope scope(isolate);
+ Handle<JSProxy> self(this);
+
+ isolate->factory()->BecomeJSObject(self);
+ ASSERT(IsJSObject());
+ // TODO(rossberg): recognize function proxies.
+}
+
+
+
MaybeObject* JSObject::SetPropertyForResult(LookupResult* result,
String* name,
Object* value,
=======================================
--- /branches/bleeding_edge/src/objects.h Fri Jul 15 02:10:20 2011
+++ /branches/bleeding_edge/src/objects.h Mon Jul 18 06:04:52 2011
@@ -6475,6 +6475,9 @@
// [handler]: The handler property.
DECL_ACCESSORS(handler, Object)
+ // [padding]: The padding slot (unused, see below).
+ DECL_ACCESSORS(padding, Object)
+
// Casting.
static inline JSProxy* cast(Object* obj);
@@ -6493,6 +6496,9 @@
String* name,
bool* has_exception);
+ // Turn this into an (empty) JSObject.
+ void Fix();
+
// Dispatched behavior.
#ifdef OBJECT_PRINT
inline void JSProxyPrint() {
@@ -6504,9 +6510,14 @@
void JSProxyVerify();
#endif
- // Layout description.
+ // Layout description. We add padding so that a proxy has the same
+ // size as a virgin JSObject. This is essential for becoming a JSObject
+ // upon freeze.
static const int kHandlerOffset = HeapObject::kHeaderSize;
- static const int kSize = kHandlerOffset + kPointerSize;
+ static const int kPaddingOffset = kHandlerOffset + kPointerSize;
+ static const int kSize = kPaddingOffset + kPointerSize;
+
+ STATIC_CHECK(kSize == JSObject::kHeaderSize);
typedef FixedBodyDescriptor<kHandlerOffset,
kHandlerOffset + kPointerSize,
=======================================
--- /branches/bleeding_edge/src/runtime.cc Fri Jul 15 02:10:20 2011
+++ /branches/bleeding_edge/src/runtime.cc Mon Jul 18 06:04:52 2011
@@ -613,6 +613,14 @@
CONVERT_CHECKED(JSProxy, proxy, args[0]);
return proxy->handler();
}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Fix) {
+ ASSERT(args.length() == 1);
+ CONVERT_CHECKED(JSProxy, proxy, args[0]);
+ proxy->Fix();
+ return proxy;
+}
RUNTIME_FUNCTION(MaybeObject*, Runtime_ClassOf) {
=======================================
--- /branches/bleeding_edge/src/runtime.h Fri Jul 15 02:10:20 2011
+++ /branches/bleeding_edge/src/runtime.h Mon Jul 18 06:04:52 2011
@@ -284,6 +284,7 @@
F(CreateJSProxy, 2, 1) \
F(IsJSProxy, 1, 1) \
F(GetHandler, 1, 1) \
+ F(Fix, 1, 1) \
\
/* Statements */ \
F(NewClosure, 3, 1) \
=======================================
--- /branches/bleeding_edge/src/v8natives.js Wed Jul 13 04:57:15 2011
+++ /branches/bleeding_edge/src/v8natives.js Mon Jul 18 06:04:52 2011
@@ -663,7 +663,8 @@
var result = %_CallFunction(handler, p, attributes, defineProperty);
if (!ToBoolean(result)) {
if (should_throw) {
- throw MakeTypeError("handler_failed", [handler, "defineProperty"]);
+ throw MakeTypeError("handler_returned_false",
+ [handler, "defineProperty"]);
} else {
return false;
}
@@ -1018,6 +1019,22 @@
}
return obj;
}
+
+
+// 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);
+ if (IS_UNDEFINED(props)) {
+ throw MakeTypeError("handler_returned_undefined", [handler, "fix"]);
+ }
+ %Fix(obj);
+ ObjectDefineProperties(obj, props);
+}
// ES5 section 15.2.3.8.
@@ -1025,6 +1042,9 @@
if (!IS_SPEC_OBJECT(obj)) {
throw MakeTypeError("obj_ctor_property_non_object", ["seal"]);
}
+ if (%IsJSProxy(obj)) {
+ ProxyFix(obj);
+ }
var names = ObjectGetOwnPropertyNames(obj);
for (var i = 0; i < names.length; i++) {
var name = names[i];
@@ -1034,7 +1054,8 @@
DefineOwnProperty(obj, name, desc, true);
}
}
- return ObjectPreventExtension(obj);
+ %PreventExtensions(obj);
+ return obj;
}
@@ -1043,6 +1064,9 @@
if (!IS_SPEC_OBJECT(obj)) {
throw MakeTypeError("obj_ctor_property_non_object", ["freeze"]);
}
+ if (%IsJSProxy(obj)) {
+ ProxyFix(obj);
+ }
var names = ObjectGetOwnPropertyNames(obj);
for (var i = 0; i < names.length; i++) {
var name = names[i];
@@ -1053,7 +1077,8 @@
DefineOwnProperty(obj, name, desc, true);
}
}
- return ObjectPreventExtension(obj);
+ %PreventExtensions(obj);
+ return obj;
}
@@ -1062,6 +1087,9 @@
if (!IS_SPEC_OBJECT(obj)) {
throw MakeTypeError("obj_ctor_property_non_object",
["preventExtension"]);
}
+ if (%IsJSProxy(obj)) {
+ ProxyFix(obj);
+ }
%PreventExtensions(obj);
return obj;
}
@@ -1072,6 +1100,9 @@
if (!IS_SPEC_OBJECT(obj)) {
throw MakeTypeError("obj_ctor_property_non_object", ["isSealed"]);
}
+ if (%IsJSProxy(obj)) {
+ return false;
+ }
var names = ObjectGetOwnPropertyNames(obj);
for (var i = 0; i < names.length; i++) {
var name = names[i];
@@ -1090,6 +1121,9 @@
if (!IS_SPEC_OBJECT(obj)) {
throw MakeTypeError("obj_ctor_property_non_object", ["isFrozen"]);
}
+ if (%IsJSProxy(obj)) {
+ return false;
+ }
var names = ObjectGetOwnPropertyNames(obj);
for (var i = 0; i < names.length; i++) {
var name = names[i];
@@ -1109,6 +1143,9 @@
if (!IS_SPEC_OBJECT(obj)) {
throw MakeTypeError("obj_ctor_property_non_object", ["isExtensible"]);
}
+ if (%IsJSProxy(obj)) {
+ return true;
+ }
return %IsExtensible(obj);
}
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/proxies.js Fri Jul 15
02:10:20 2011
+++ /branches/bleeding_edge/test/mjsunit/harmony/proxies.js Mon Jul 18
06:04:52 2011
@@ -529,3 +529,84 @@
},
getOwnPropertyDescriptor: function(k) { return {} }
})
+
+
+
+// Fixing (Object.freeze, Object.seal, Object.preventExtensions,
+// Object.isFrozen, Object.isSealed, Object.isExtensible)
+
+function TestFix(names, handler) {
+ var proto = {p: 77}
+ var assertFixing = function(o, s, f, e) {
+ assertEquals(s, Object.isSealed(o))
+ assertEquals(f, Object.isFrozen(o))
+ assertEquals(e, Object.isExtensible(o))
+ }
+
+ var o1 = Proxy.create(handler, proto)
+ assertFixing(o1, false, false, true)
+ Object.seal(o1)
+ assertFixing(o1, true, names.length === 0, false)
+ assertArrayEquals(names.sort(), Object.getOwnPropertyNames(o1).sort())
+ assertArrayEquals(names.filter(function(x) {return x < "z"}).sort(),
+ Object.keys(o1).sort())
+ assertEquals(proto, Object.getPrototypeOf(o1))
+ assertEquals(77, o1.p)
+ for (var n in o1) {
+ var desc = Object.getOwnPropertyDescriptor(o1, n)
+ if (desc !== undefined) assertFalse(desc.configurable)
+ }
+
+ var o2 = Proxy.create(handler, proto)
+ assertFixing(o2, false, false, true)
+ Object.freeze(o2)
+ assertFixing(o2, true, true, false)
+ assertArrayEquals(names.sort(), Object.getOwnPropertyNames(o2).sort())
+ assertArrayEquals(names.filter(function(x) {return x < "z"}).sort(),
+ Object.keys(o2).sort())
+ assertEquals(proto, Object.getPrototypeOf(o2))
+ assertEquals(77, o2.p)
+ for (var n in o2) {
+ var desc = Object.getOwnPropertyDescriptor(o2, n)
+ if (desc !== undefined) assertFalse(desc.writable)
+ if (desc !== undefined) assertFalse(desc.configurable)
+ }
+
+ var o3 = Proxy.create(handler, proto)
+ assertFixing(o3, false, false, true)
+ Object.preventExtensions(o3)
+ assertFixing(o3, names.length === 0, names.length === 0, false)
+ assertArrayEquals(names.sort(), Object.getOwnPropertyNames(o3).sort())
+ assertArrayEquals(names.filter(function(x) {return x < "z"}).sort(),
+ Object.keys(o3).sort())
+ assertEquals(proto, Object.getPrototypeOf(o3))
+ assertEquals(77, o3.p)
+}
+
+TestFix([], {
+ fix: function() { return {} }
+})
+TestFix(["a", "b", "c", "d", "zz"], {
+ fix: function() {
+ return {
+ a: {value: "a", writable: true, configurable: false, enumerable:
true},
+ b: {value: 33, writable: false, configurable: false, enumerable:
true},
+ c: {value: 0, writable: true, configurable: true, enumerable: true},
+ d: {value: true, writable: false, configurable: true, enumerable:
true},
+ zz: {value: 0, enumerable: false}
+ }
+ }
+})
+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() {
+ return {b: {configurable: true, writable: true, enumerable: true}}
+ }
+ }
+})
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev