Ensure that JSProxy::Fix gives the generated JSObject map a constructor
authoradamk@chromium.org <adamk@chromium.org>
Wed, 27 Aug 2014 15:54:23 +0000 (15:54 +0000)
committeradamk@chromium.org <adamk@chromium.org>
Wed, 27 Aug 2014 15:54:23 +0000 (15:54 +0000)
All JSObjects in V8 either have a map()->constructor() field or are
JSFunctions. JSProxy::Fix, however, was not enforcing this, and
Object.observe's use of JSObject::GetCreationContext() exposed this.

Note that this is not Object.observe-specific: the API call
v8::Object::CreationContext() also would have revealed this bug.

This patch chooses Object as a reasonable constructor to put on the
newly-fixed object's map. Note that this has no effect on the "constructor"
property in JS. In doing so, I've also tightened up the code underlying
JSProxy::Fix to only support JSObject and JSFunction as possible output
types.

BUG=405844
LOG=N
R=rossberg@chromium.org, verwaest@chromium.org

Review URL: https://codereview.chromium.org/505303004

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@23466 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/factory.cc
src/factory.h
test/mjsunit/harmony/regress/regress-405844.js [new file with mode: 0644]

index 643329c..5a07708 100644 (file)
@@ -1777,20 +1777,19 @@ Handle<JSProxy> Factory::NewJSFunctionProxy(Handle<Object> handler,
 }
 
 
-void Factory::ReinitializeJSReceiver(Handle<JSReceiver> object,
-                                     InstanceType type,
-                                     int size) {
-  DCHECK(type >= FIRST_JS_OBJECT_TYPE);
+void Factory::ReinitializeJSProxy(Handle<JSProxy> proxy, InstanceType type,
+                                  int size) {
+  DCHECK(type == JS_OBJECT_TYPE || type == JS_FUNCTION_TYPE);
 
   // Allocate fresh map.
   // TODO(rossberg): Once we optimize proxies, cache these maps.
   Handle<Map> map = NewMap(type, size);
 
   // Check that the receiver has at least the size of the fresh object.
-  int size_difference = object->map()->instance_size() - map->instance_size();
+  int size_difference = proxy->map()->instance_size() - map->instance_size();
   DCHECK(size_difference >= 0);
 
-  map->set_prototype(object->map()->prototype());
+  map->set_prototype(proxy->map()->prototype());
 
   // Allocate the backing storage for the properties.
   int prop_size = map->InitialPropertiesLength();
@@ -1811,24 +1810,31 @@ void Factory::ReinitializeJSReceiver(Handle<JSReceiver> object,
 
   // Put in filler if the new object is smaller than the old.
   if (size_difference > 0) {
-    Address address = object->address();
+    Address address = proxy->address();
     heap->CreateFillerObjectAt(address + map->instance_size(), size_difference);
     heap->AdjustLiveBytes(address, -size_difference, Heap::FROM_MUTATOR);
   }
 
   // Reset the map for the object.
-  object->synchronized_set_map(*map);
-  Handle<JSObject> jsobj = Handle<JSObject>::cast(object);
+  proxy->synchronized_set_map(*map);
+  Handle<JSObject> jsobj = Handle<JSObject>::cast(proxy);
 
   // Reinitialize the object from the constructor map.
   heap->InitializeJSObjectFromMap(*jsobj, *properties, *map);
 
+  // The current native context is used to set up certain bits.
+  // TODO(adamk): Using the current context seems wrong, it should be whatever
+  // context the JSProxy originated in. But that context isn't stored anywhere.
+  Handle<Context> context(isolate()->native_context());
+
   // Functions require some minimal initialization.
   if (type == JS_FUNCTION_TYPE) {
     map->set_function_with_prototype(true);
-    Handle<JSFunction> js_function = Handle<JSFunction>::cast(object);
-    Handle<Context> context(isolate()->native_context());
+    Handle<JSFunction> js_function = Handle<JSFunction>::cast(proxy);
     InitializeFunction(js_function, shared.ToHandleChecked(), context);
+  } else {
+    // Provide JSObjects with a constructor.
+    map->set_constructor(context->object_function());
   }
 }
 
@@ -1866,13 +1872,13 @@ void Factory::ReinitializeJSGlobalProxy(Handle<JSGlobalProxy> object,
 }
 
 
-void Factory::BecomeJSObject(Handle<JSReceiver> object) {
-  ReinitializeJSReceiver(object, JS_OBJECT_TYPE, JSObject::kHeaderSize);
+void Factory::BecomeJSObject(Handle<JSProxy> proxy) {
+  ReinitializeJSProxy(proxy, JS_OBJECT_TYPE, JSObject::kHeaderSize);
 }
 
 
-void Factory::BecomeJSFunction(Handle<JSReceiver> object) {
-  ReinitializeJSReceiver(object, JS_FUNCTION_TYPE, JSFunction::kSize);
+void Factory::BecomeJSFunction(Handle<JSProxy> proxy) {
+  ReinitializeJSProxy(proxy, JS_FUNCTION_TYPE, JSFunction::kSize);
 }
 
 
index c3ad0dc..f9329ae 100644 (file)
@@ -449,13 +449,6 @@ class Factory V8_FINAL {
                                      Handle<Object> construct_trap,
                                      Handle<Object> prototype);
 
-  // Reinitialize a JSReceiver into an (empty) JS object of respective type and
-  // size, but keeping the original prototype.  The receiver must have at least
-  // the size of the new object.  The object is reinitialized and behaves as an
-  // object that has been freshly allocated.
-  void ReinitializeJSReceiver(
-      Handle<JSReceiver> object, InstanceType type, int size);
-
   // 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
@@ -464,8 +457,8 @@ class Factory V8_FINAL {
                                  Handle<JSFunction> constructor);
 
   // Change the type of the argument into a JS object/function and reinitialize.
-  void BecomeJSObject(Handle<JSReceiver> object);
-  void BecomeJSFunction(Handle<JSReceiver> object);
+  void BecomeJSObject(Handle<JSProxy> object);
+  void BecomeJSFunction(Handle<JSProxy> object);
 
   Handle<JSFunction> NewFunction(Handle<String> name,
                                  Handle<Code> code,
@@ -707,6 +700,12 @@ class Factory V8_FINAL {
   Handle<JSFunction> NewFunction(Handle<Map> map,
                                  Handle<String> name,
                                  MaybeHandle<Code> maybe_code);
+
+  // Reinitialize a JSProxy into an (empty) JS object of respective type and
+  // size, but keeping the original prototype.  The receiver must have at least
+  // the size of the new object.  The object is reinitialized and behaves as an
+  // object that has been freshly allocated.
+  void ReinitializeJSProxy(Handle<JSProxy> proxy, InstanceType type, int size);
 };
 
 } }  // namespace v8::internal
diff --git a/test/mjsunit/harmony/regress/regress-405844.js b/test/mjsunit/harmony/regress/regress-405844.js
new file mode 100644 (file)
index 0000000..fbe7310
--- /dev/null
@@ -0,0 +1,13 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Flags: --harmony-proxies
+
+var proxy = Proxy.create({ fix: function() { return {}; } });
+Object.preventExtensions(proxy);
+Object.observe(proxy, function(){});
+
+var functionProxy = Proxy.createFunction({ fix: function() { return {}; } }, function(){});
+Object.preventExtensions(functionProxy);
+Object.observe(functionProxy, function(){});