Bug 947014 - Allow callers of Wrapper::New to specify a prototype. (r=bholley)
authorEric Faust <efaustbmo@gmail.com>
Wed, 29 Jan 2014 17:20:16 -0800
changeset 181931 f6395f80b24fa7235fbfbf64f13d67339e74518a
parent 181930 beb52f820ac567ba0adb91c14f35429d581e64c1
child 181932 d44de38ad97f2aaeb289ca3d90cd956845d39c4c
push id3343
push userffxbld
push dateMon, 17 Mar 2014 21:55:32 +0000
treeherdermozilla-beta@2f7d3415f79f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbholley
bugs947014
milestone29.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 947014 - Allow callers of Wrapper::New to specify a prototype. (r=bholley)
js/src/jsproxy.h
js/src/jswrapper.cpp
js/src/jswrapper.h
js/src/shell/js.cpp
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -443,22 +443,16 @@ class MOZ_STACK_CLASS ProxyOptions {
         return setClass(classp);
     }
 
   private:
     bool singleton_;
     const Class *clasp_;
 };
 
-class MOZ_STACK_CLASS WrapperOptions : public ProxyOptions {
-  public:
-    WrapperOptions() : ProxyOptions(false, nullptr)
-    {}
-};
-
 JS_FRIEND_API(JSObject *)
 NewProxyObject(JSContext *cx, BaseProxyHandler *handler, HandleValue priv,
                JSObject *proto, JSObject *parent, const ProxyOptions &options = ProxyOptions());
 
 JSObject *
 RenewProxyObject(JSContext *cx, JSObject *obj, BaseProxyHandler *handler, Value priv);
 
 class JS_FRIEND_API(AutoEnterPolicy)
--- a/js/src/jswrapper.cpp
+++ b/js/src/jswrapper.cpp
@@ -48,17 +48,17 @@ Wrapper::New(JSContext *cx, JSObject *ob
 
     RootedValue priv(cx, ObjectValue(*obj));
     mozilla::Maybe<WrapperOptions> opts;
     if (!options) {
         opts.construct();
         opts.ref().selectDefaultClass(obj->isCallable());
         options = opts.addr();
     }
-    return NewProxyObject(cx, handler, priv, TaggedProto::LazyProto, parent, *options);
+    return NewProxyObject(cx, handler, priv, options->proto(), parent, *options);
 }
 
 JSObject *
 Wrapper::Renew(JSContext *cx, JSObject *existing, JSObject *obj, Wrapper *handler)
 {
     JS_ASSERT(!obj->isCallable());
     existing->as<ProxyObject>().renew(cx, handler, ObjectValue(*obj));
     return existing;
@@ -137,16 +137,17 @@ Wrapper::Wrapper(unsigned flags, bool ha
 }
 
 Wrapper::~Wrapper()
 {
 }
 
 Wrapper Wrapper::singleton((unsigned)0);
 Wrapper Wrapper::singletonWithPrototype((unsigned)0, true);
+JSObject *Wrapper::defaultProto = TaggedProto::LazyProto;
 
 /* Compartments. */
 
 extern JSObject *
 js::TransparentObjectWrapper(JSContext *cx, HandleObject existing, HandleObject obj,
                              HandleObject wrappedProto, HandleObject parent,
                              unsigned flags)
 {
--- a/js/src/jswrapper.h
+++ b/js/src/jswrapper.h
@@ -11,16 +11,47 @@
 
 #include "jsproxy.h"
 
 namespace js {
 
 class DummyFrameGuard;
 
 /*
+ * Helper for Wrapper::New default options.
+ *
+ * Callers of Wrapper::New() who wish to specify a prototype for the created
+ * Wrapper, *MUST* construct a WrapperOptions with a JSContext.
+ */
+class MOZ_STACK_CLASS WrapperOptions : public ProxyOptions {
+  public:
+    WrapperOptions() : ProxyOptions(false, nullptr),
+                       proto_()
+    {}
+
+    WrapperOptions(JSContext *cx) : ProxyOptions(false, nullptr),
+                                    proto_()
+    {
+        proto_.construct(cx);
+    }
+
+    JSObject *proto() const {
+        return proto_.empty() ? Wrapper::defaultProto : proto_.ref();
+    }
+    WrapperOptions &setProto(JSObject *protoArg) {
+        JS_ASSERT(!proto_.empty());
+        proto_.ref() = protoArg;
+        return *this;
+    }
+
+  private:
+    mozilla::Maybe<JS::RootedObject> proto_;
+};
+
+/*
  * A wrapper is a proxy with a target object to which it generally forwards
  * operations, but may restrict access to certain operations or instrument
  * the trap operations in various ways. A wrapper is distinct from a Direct Proxy
  * Handler in the sense that it can be "unwrapped" in C++, exposing the underlying
  * object (Direct Proxy Handlers have an underlying target object, but don't
  * expect to expose this object via any kind of unwrapping operation). Callers
  * should be careful to avoid unwrapping security wrappers in the wrong context.
  */
@@ -55,16 +86,18 @@ class JS_FRIEND_API(Wrapper) : public Di
     explicit Wrapper(unsigned flags, bool hasPrototype = false);
 
     virtual ~Wrapper();
 
     virtual bool finalizeInBackground(Value priv) MOZ_OVERRIDE;
 
     static Wrapper singleton;
     static Wrapper singletonWithPrototype;
+
+    static JSObject *defaultProto;
 };
 
 /* Base class for all cross compartment wrapper handlers. */
 class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
 {
   public:
     CrossCompartmentWrapper(unsigned flags, bool hasPrototype = false);
 
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3759,44 +3759,16 @@ ThisFilename(JSContext *cx, unsigned arg
     }
     JSString *filename = JS_NewStringCopyZ(cx, script->filename());
     if (!filename)
         return false;
     args.rval().setString(filename);
     return true;
 }
 
-/*
- * Internal class for testing hasPrototype easily.
- * Uses passed in prototype instead of target's.
- */
-class WrapperWithProto : public Wrapper
-{
-  public:
-    explicit WrapperWithProto(unsigned flags)
-      : Wrapper(flags, true)
-    { }
-
-    static JSObject *New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
-                         Wrapper *handler);
-};
-
-/* static */ JSObject *
-WrapperWithProto::New(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent,
-                      Wrapper *handler)
-{
-    JS_ASSERT(parent);
-    AutoMarkInDeadZone amd(cx->zone());
-
-    RootedValue priv(cx, ObjectValue(*obj));
-    ProxyOptions options;
-    options.selectDefaultClass(obj->isCallable());
-    return NewProxyObject(cx, handler, priv, proto, parent, options);
-}
-
 static bool
 Wrap(JSContext *cx, unsigned argc, jsval *vp)
 {
     jsval v = argc > 0 ? JS_ARGV(cx, vp)[0] : UndefinedValue();
     if (JSVAL_IS_PRIMITIVE(v)) {
         JS_SET_RVAL(cx, vp, v);
         return true;
     }
@@ -3820,19 +3792,21 @@ WrapWithProto(JSContext *cx, unsigned ar
         proto = JS_ARGV(cx, vp)[1];
     }
     if (!obj.isObject() || !proto.isObjectOrNull()) {
         JS_ReportErrorNumber(cx, my_GetErrorMessage, nullptr, JSSMSG_INVALID_ARGS,
                              "wrapWithProto");
         return false;
     }
 
-    JSObject *wrapped = WrapperWithProto::New(cx, &obj.toObject(), proto.toObjectOrNull(),
-                                              &obj.toObject().global(),
-                                              &Wrapper::singletonWithPrototype);
+    WrapperOptions options(cx);
+    options.setProto(proto.toObjectOrNull());
+    options.selectDefaultClass(obj.toObject().isCallable());
+    JSObject *wrapped = Wrapper::New(cx, &obj.toObject(), &obj.toObject().global(),
+                                     &Wrapper::singletonWithPrototype, &options);
     if (!wrapped)
         return false;
 
     JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(wrapped));
     return true;
 }
 
 static JSObject *