Bug 911864 - Expose an API to copy a single property across compartments. r=jorendorff
authorBobby Holley <bobbyholley@gmail.com>
Fri, 01 Nov 2013 15:31:56 +0100
changeset 153060 48d02e98287b1ad8b89066323f8d08c40a3f1862
parent 153059 2efa2d9b1ea2a3cdcdd11aea5a21b93dddd28fb7
child 153061 aad5b909924dbd9d069c1b91c9613c100284af1a
push id25566
push userryanvm@gmail.com
push dateFri, 01 Nov 2013 18:40:05 +0000
treeherdermozilla-central@5bb07c1ae9f5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjorendorff
bugs911864
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 911864 - Expose an API to copy a single property across compartments. r=jorendorff
js/src/jsfriendapi.h
js/src/jsobj.cpp
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -168,19 +168,33 @@ js_DumpAtom(JSAtom *atom);
 
 extern JS_FRIEND_API(void)
 js_DumpObject(JSObject *obj);
 
 extern JS_FRIEND_API(void)
 js_DumpChars(const jschar *s, size_t n);
 #endif
 
+/*
+ * Copies all own properties from |obj| to |target|. |obj| must be a "native"
+ * object (that is to say, normal-ish - not an Array or a Proxy).
+ *
+ * On entry, |cx| must be in the compartment of |target|.
+ */
 extern JS_FRIEND_API(bool)
 JS_CopyPropertiesFrom(JSContext *cx, JSObject *target, JSObject *obj);
 
+/*
+ * Single-property version of the above. This function asserts that an |own|
+ * property of the given name exists on |obj|.
+ */
+extern JS_FRIEND_API(bool)
+JS_CopyPropertyFrom(JSContext *cx, JS::HandleId id, JS::HandleObject target,
+                    JS::HandleObject obj);
+
 extern JS_FRIEND_API(bool)
 JS_WrapPropertyDescriptor(JSContext *cx, JS::MutableHandle<JSPropertyDescriptor> desc);
 
 extern JS_FRIEND_API(bool)
 JS_WrapAutoIdVector(JSContext *cx, JS::AutoIdVector &props);
 
 extern JS_FRIEND_API(bool)
 JS_EnumerateState(JSContext *cx, JS::HandleObject obj, JSIterateOp enum_op,
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -1751,52 +1751,81 @@ JSObject::deleteByValue(JSContext *cx, H
 
     if (name->isIndex(&index))
         return deleteElement(cx, obj, index, succeeded);
 
     Rooted<PropertyName*> propname(cx, name->asPropertyName());
     return deleteProperty(cx, obj, propname, succeeded);
 }
 
+static bool
+CopyProperty(JSContext *cx, HandleObject target, HandleObject obj,
+             HandleShape shape)
+{
+    // |shape| and |obj| are generally not same-compartment with |target| and
+    // |cx| here.
+    assertSameCompartment(cx, target);
+
+    unsigned attrs = shape->attributes();
+    PropertyOp getter = shape->getter();
+    StrictPropertyOp setter = shape->setter();
+    AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
+    if ((attrs & JSPROP_GETTER) && !cx->compartment()->wrap(cx, &getter))
+        return false;
+    if ((attrs & JSPROP_SETTER) && !cx->compartment()->wrap(cx, &setter))
+        return false;
+    RootedValue v(cx, shape->hasSlot() ? obj->getSlot(shape->slot())
+                                       : UndefinedValue());
+    if (!cx->compartment()->wrap(cx, &v))
+        return false;
+    RootedId id(cx, shape->propid());
+    return JSObject::defineGeneric(cx, target, id, v, getter,
+                                   setter, attrs);
+}
+
+JS_FRIEND_API(bool)
+JS_CopyPropertyFrom(JSContext *cx, HandleId id, HandleObject target,
+                    HandleObject obj)
+{
+    assertSameCompartment(cx, target);
+    MOZ_ASSERT(obj->isNative());
+    RootedObject obj2(cx);
+    RootedShape shape(cx);
+    {
+        AutoCompartment ac(cx, obj);
+        if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &shape))
+            return false;
+    }
+    MOZ_ASSERT(shape && obj == obj2);
+    return CopyProperty(cx, target, obj, shape);
+}
+
 JS_FRIEND_API(bool)
 JS_CopyPropertiesFrom(JSContext *cx, JSObject *targetArg, JSObject *objArg)
 {
     RootedObject target(cx, targetArg);
     RootedObject obj(cx, objArg);
+    assertSameCompartment(cx, target);
 
     // If we're not native, then we cannot copy properties.
     JS_ASSERT(target->isNative() == obj->isNative());
     if (!target->isNative())
         return true;
 
     AutoShapeVector shapes(cx);
     for (Shape::Range<NoGC> r(obj->lastProperty()); !r.empty(); r.popFront()) {
         if (!shapes.append(&r.front()))
             return false;
     }
 
+    size_t n = shapes.length();
     RootedShape shape(cx);
-    RootedValue v(cx);
-    RootedId id(cx);
-    size_t n = shapes.length();
     while (n > 0) {
         shape = shapes[--n];
-        unsigned attrs = shape->attributes();
-        PropertyOp getter = shape->getter();
-        StrictPropertyOp setter = shape->setter();
-        AutoRooterGetterSetter gsRoot(cx, attrs, &getter, &setter);
-        if ((attrs & JSPROP_GETTER) && !cx->compartment()->wrap(cx, &getter))
-            return false;
-        if ((attrs & JSPROP_SETTER) && !cx->compartment()->wrap(cx, &setter))
-            return false;
-        v = shape->hasSlot() ? obj->getSlot(shape->slot()) : UndefinedValue();
-        if (!cx->compartment()->wrap(cx, &v))
-            return false;
-        id = shape->propid();
-        if (!JSObject::defineGeneric(cx, target, id, v, getter, setter, attrs))
+        if (!CopyProperty(cx, target, obj, shape))
             return false;
     }
     return true;
 }
 
 static bool
 CopySlots(JSContext *cx, HandleObject from, HandleObject to)
 {