Make proxy API internal, allow JSObject::swap on functions (566818, r=brendan).
authorAndreas Gal <gal@mozilla.com>
Mon, 24 May 2010 14:33:03 -0700
changeset 42832 060a7e1a5635ce3e7e25661682e080b0da4907d5
parent 42831 299580074c1fd89358162b726624634c8a55ac2d
child 42833 8bc3bb17cb43d85234b3d49d0b13780891705705
push idunknown
push userunknown
push dateunknown
reviewersbrendan
bugs566818
milestone1.9.3a5pre
Make proxy API internal, allow JSObject::swap on functions (566818, r=brendan).
js/src/jsobj.cpp
js/src/jsproxy.cpp
js/src/jsproxy.h
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -868,17 +868,17 @@ obj_toString(JSContext *cx, uintN argc, 
     size_t nchars;
     const char *clazz, *prefix;
     JSString *str;
 
     obj = JS_THIS_OBJECT(cx, vp);
     if (!obj)
         return JS_FALSE;
     if (obj->isProxy()) {
-        if (!JS_GetProxyObjectClass(cx, obj, &clazz))
+        if (!GetProxyObjectClass(cx, obj, &clazz))
             return false;
     } else {
         obj = js_GetWrappedObject(cx, obj);
         clazz = obj->getClass()->name;
     }
     nchars = 9 + strlen(clazz);         /* 9 for "[object ]" */
     chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
     if (!chars)
@@ -3194,34 +3194,48 @@ js_DefineBlockVariable(JSContext *cx, JS
 
     /* Use JSPROP_ENUMERATE to aid the disassembler. */
     return js_DefineNativeProperty(cx, obj, id, JSVAL_VOID,
                                    block_getProperty, block_setProperty,
                                    JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED,
                                    JSScopeProperty::HAS_SHORTID, index, NULL);
 }
 
+static size_t
+GetObjectSize(JSObject *obj)
+{
+    return (obj->isFunction() && !obj->getPrivate())
+           ? sizeof(JSFunction)
+           : sizeof(JSObject);
+}
+
 /*
  * Use this method with extreme caution. It trades the guts of two objects and updates
  * scope ownership. This operation is not thread-safe, just as fast array to slow array
  * transitions are inherently not thread-safe. Don't perform a swap operation on objects
  * shared across threads or, or bad things will happen. You have been warned.
  */
 void
 JSObject::swap(JSObject *other)
 {
     /* For both objects determine whether they own their respective scopes. */
     bool thisOwns = this->isNative() && scope()->object == this;
     bool otherOwns = other->isNative() && other->scope()->object == other;
 
+    size_t size = GetObjectSize(this);
+    JS_ASSERT(size == GetObjectSize(other));
+
     /* Trade the guts of the objects. */
-    JSObject tmp;
-    memcpy(&tmp, this, sizeof(JSObject));
-    memcpy(this, other, sizeof(JSObject));
-    memcpy(other, &tmp, sizeof(JSObject));
+    union {
+        JSFunction fun;
+        JSObject obj;
+    } tmp;
+    memcpy(&tmp, this, size);
+    memcpy(this, other, size);
+    memcpy(other, &tmp, size);
 
     /* Fixup scope ownerships. */
     if (otherOwns)
         scope()->object = this;
     if (thisOwns)
         other->scope()->object = other;
 }
 
--- a/js/src/jsproxy.cpp
+++ b/js/src/jsproxy.cpp
@@ -47,16 +47,18 @@
 #include "jsobj.h"
 #include "jsproxy.h"
 #include "jsscope.h"
 
 #include "jsobjinlines.h"
 
 using namespace js;
 
+namespace js {
+
 JSProxyHandler::~JSProxyHandler()
 {
 }
 
 bool
 JSProxyHandler::has(JSContext *cx, JSObject *proxy, jsid id, bool *bp)
 {
     AutoDescriptor desc(cx);
@@ -598,18 +600,18 @@ JSProxy::enumerateOwn(JSContext *cx, JSO
         AutoValueRooter rval(cx);
         if (!TryHandlerTrap(cx, proxy, Trap(cx, JSVAL_TO_OBJECT(handler), ATOM(enumerateOwn), 0, NULL, rval.addr())))
             return false;
         return ArrayToJSIDArray(cx, rval.value(), idap);
     }
     return TryHandlerTrap(cx, proxy, ((JSProxyHandler *) JSVAL_TO_PRIVATE(handler))->enumerateOwn(cx, proxy, idap));
 }
 
-JS_PUBLIC_API(JSBool)
-JS_GetProxyObjectClass(JSContext *cx, JSObject *proxy, const char **namep)
+JS_FRIEND_API(JSBool)
+GetProxyObjectClass(JSContext *cx, JSObject *proxy, const char **namep)
 {
     if (!proxy->isProxy()) {
         char *bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK,
                                                  OBJECT_TO_JSVAL(proxy), NULL);
         if (!bytes)
             return JS_FALSE;
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                              JSMSG_UNEXPECTED_TYPE, bytes, "not a proxy");
@@ -767,17 +769,17 @@ JSObjectOps js_ObjectProxyObjectOps = {
 };
 
 static JSObjectOps *
 obj_proxy_getObjectOps(JSContext *cx, JSClass *clasp)
 {
     return &js_ObjectProxyObjectOps;
 }
 
-JS_FRIEND_API(JSClass) js_ObjectProxyClass = {
+JS_FRIEND_API(JSClass) ObjectProxyClass = {
     "ObjectProxy",
     JSCLASS_HAS_RESERVED_SLOTS(3) |
     JSCLASS_NEW_ENUMERATE,
     JS_PropertyStub,        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
     JS_EnumerateStub,       JS_ResolveStub,  JS_ConvertStub,  NULL,
     obj_proxy_getObjectOps, NULL,            NULL,            NULL,
     NULL,                   NULL,            NULL,            NULL
 };
@@ -846,43 +848,43 @@ JSObjectOps js_FunctionProxyObjectOps = 
 };
 
 static JSObjectOps *
 fun_proxy_getObjectOps(JSContext *cx, JSClass *clasp)
 {
     return &js_FunctionProxyObjectOps;
 }
 
-JS_FRIEND_API(JSClass) js_FunctionProxyClass = {
+JS_FRIEND_API(JSClass) FunctionProxyClass = {
     "FunctionProxy",
     JSCLASS_HAS_RESERVED_SLOTS(3) |
     JSCLASS_NEW_ENUMERATE,
     JS_PropertyStub,        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
     JS_EnumerateStub,       JS_ResolveStub,  JS_ConvertStub,  NULL,
     fun_proxy_getObjectOps, NULL,            NULL,            NULL,
     NULL,                   NULL,            NULL,            NULL
 };
 
-JS_PUBLIC_API(JSObject *)
-JS_NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSString *className)
+JS_FRIEND_API(JSObject *)
+NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSString *className)
 {
-    JSObject *obj = NewObject(cx, &js_ObjectProxyClass, proto, parent);
+    JSObject *obj = NewObjectWithGivenProto(cx, &ObjectProxyClass, proto, parent);
     if (!obj)
         return NULL;
     obj->fslots[JSSLOT_PROXY_HANDLER] = handler;
     obj->fslots[JSSLOT_PROXY_CLASS] = className ? STRING_TO_JSVAL(className) : JSVAL_VOID;
     obj->fslots[JSSLOT_PROXY_PRIVATE] = JSVAL_VOID;
     return obj;
 }
 
-JS_PUBLIC_API(JSObject *)
-JS_NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent,
-                    JSObject *call, JSObject *construct)
+JS_FRIEND_API(JSObject *)
+NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent,
+                 JSObject *call, JSObject *construct)
 {
-    JSObject *obj = NewObject(cx, &js_FunctionProxyClass, proto, parent);
+    JSObject *obj = NewObjectWithGivenProto(cx, &FunctionProxyClass, proto, parent);
     if (!obj)
         return NULL;
     obj->fslots[JSSLOT_PROXY_HANDLER] = handler;
     obj->fslots[JSSLOT_PROXY_CALL] = call ? OBJECT_TO_JSVAL(call) : JSVAL_VOID;
     obj->fslots[JSSLOT_PROXY_CONSTRUCT] = construct ? OBJECT_TO_JSVAL(construct) : JSVAL_VOID;
     return obj;
 }
 
@@ -912,17 +914,17 @@ proxy_create(JSContext *cx, uintN argc, 
         proto = JSVAL_TO_OBJECT(vp[3]);
         parent = proto->getParent();
     } else {
         JS_ASSERT(VALUE_IS_FUNCTION(cx, vp[0]));
         proto = NULL;
         parent = JSVAL_TO_OBJECT(vp[0])->getParent();
     }
     JSString *className = (argc > 2 && JSVAL_IS_STRING(vp[4])) ? JSVAL_TO_STRING(vp[4]) : NULL;
-    JSObject *proxy = JS_NewObjectProxy(cx, OBJECT_TO_JSVAL(handler), proto, parent, className);
+    JSObject *proxy = NewObjectProxy(cx, OBJECT_TO_JSVAL(handler), proto, parent, className);
     if (!proxy)
         return false;
 
     *vp = OBJECT_TO_JSVAL(proxy);
     return true;
 }
 
 static JSBool
@@ -947,17 +949,17 @@ proxy_createFunction(JSContext *cx, uint
         return false;
     JSObject *construct = NULL;
     if (argc > 2) {
         construct = js_ValueToCallableObject(cx, &vp[4], JSV2F_SEARCH_STACK);
         if (!construct)
             return false;
     }
 
-    JSObject *proxy = JS_NewFunctionProxy(cx, OBJECT_TO_JSVAL(handler), proto, parent, call, construct);
+    JSObject *proxy = NewFunctionProxy(cx, OBJECT_TO_JSVAL(handler), proto, parent, call, construct);
     if (!proxy)
         return false;
 
     *vp = OBJECT_TO_JSVAL(proxy);
     return true;
 }
 
 #ifdef DEBUG
@@ -985,17 +987,17 @@ proxy_fix(JSContext *cx, uintN argc, jsv
                              "Proxy.fix", "0", "s");
         return false;
     }
     JSObject *obj;
     if (!(obj = NonNullObject(cx, vp[2])))
         return false;
     if (obj->isProxy()) {
         JSBool flag;
-        if (!JS_FixProxy(cx, obj, &flag))
+        if (!FixProxy(cx, obj, &flag))
             return false;
         *vp = BOOLEAN_TO_JSVAL(flag);
     } else {
         *vp = JSVAL_TRUE;
     }
     return true;
 }
 
@@ -1006,50 +1008,35 @@ static JSFunctionSpec static_methods[] =
     JS_FN("createFunction", proxy_createFunction,  3, 0),
 #ifdef DEBUG
     JS_FN("isTrapping",     proxy_isTrapping,      1, 0),
     JS_FN("fix",            proxy_fix,             1, 0),
 #endif
     JS_FS_END
 };
 
-JS_FRIEND_API(JSObject *)
-js_InitProxyClass(JSContext *cx, JSObject *obj)
-{
-    JSObject *module = NewObject(cx, &js_ObjectClass, NULL, obj);
-    if (!module)
-        return NULL;
-    if (!JS_DefineProperty(cx, obj, "Proxy", OBJECT_TO_JSVAL(module),
-                           JS_PropertyStub, JS_PropertyStub, 0)) {
-        return NULL;
-    }
-    if (!JS_DefineFunctions(cx, module, static_methods))
-        return NULL;
-    return obj;
-}
-
-extern JSClass js_CallableObjectClass;
+extern JSClass CallableObjectClass;
 
 static const uint32 JSSLOT_CALLABLE_CALL = JSSLOT_PRIVATE;
 static const uint32 JSSLOT_CALLABLE_CONSTRUCT = JSSLOT_PRIVATE + 1;
 
 static JSBool
 callable_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
     JSObject *callable = JSVAL_TO_OBJECT(argv[-2]);
-    JS_ASSERT(callable->getClass() == &js_CallableObjectClass);
+    JS_ASSERT(callable->getClass() == &CallableObjectClass);
     jsval fval = callable->fslots[JSSLOT_CALLABLE_CALL];
     return js_InternalCall(cx, obj, fval, argc, argv, rval);
 }
 
 static JSBool
 callable_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
     JSObject *callable = JSVAL_TO_OBJECT(argv[-2]);
-    JS_ASSERT(callable->getClass() == &js_CallableObjectClass);
+    JS_ASSERT(callable->getClass() == &CallableObjectClass);
     jsval fval = callable->fslots[JSSLOT_CALLABLE_CONSTRUCT];
     if (fval == JSVAL_VOID) {
         /* We don't have an explicit constructor so allocate a new object and use the call. */
         fval = callable->fslots[JSSLOT_CALLABLE_CALL];
         JS_ASSERT(JSVAL_IS_OBJECT(fval));
 
         /* callable is the constructor, so get callable.prototype is the proto of the new object. */
         if (!callable->getProperty(cx, ATOM_TO_JSID(ATOM(classPrototype)), rval))
@@ -1065,70 +1052,76 @@ callable_Construct(JSContext *cx, JSObje
         if (JSVAL_IS_PRIMITIVE(*rval))
             *rval = OBJECT_TO_JSVAL(newobj);
 
         return true;
     }
     return js_InternalCall(cx, obj, fval, argc, argv, rval);
 }
 
-JSClass js_CallableObjectClass = {
+JSClass CallableObjectClass = {
     "CallableObject",
     JSCLASS_HAS_RESERVED_SLOTS(2) |
     JSCLASS_NEW_ENUMERATE,
     JS_PropertyStub,        JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
     JS_EnumerateStub,       JS_ResolveStub,  JS_ConvertStub,  NULL,
     NULL,                   NULL,            callable_Call,   callable_Construct,
     NULL,                   NULL,            NULL,            NULL
 };
 
-JS_PUBLIC_API(JSBool)
-JS_FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp)
+JS_FRIEND_API(JSBool)
+FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp)
 {
     AutoValueRooter tvr(cx);
     if (!JSProxy::fix(cx, proxy, tvr.addr()))
         return false;
     if (tvr.value() == JSVAL_VOID) {
         *bp = false;
         return true;
     }
 
     JSObject *props;
     if (!(props = NonNullObject(cx, tvr.value())))
         return false;
 
     JSObject *proto = proxy->getProto();
     JSObject *parent = proxy->getParent();
-    JSClass *clasp = proxy->isFunctionProxy() ? &js_CallableObjectClass : &js_ObjectClass;
+    JSClass *clasp = proxy->isFunctionProxy() ? &CallableObjectClass : &js_ObjectClass;
 
     /* Make a blank object from the recipe fix provided to us. */
     JSObject *newborn = NewObjectWithGivenProto(cx, clasp, proto, parent);
     if (!newborn)
         return NULL;
     AutoValueRooter tvr2(cx, newborn);
 
-    if (clasp == &js_CallableObjectClass) {
+    if (clasp == &CallableObjectClass) {
         newborn->fslots[JSSLOT_CALLABLE_CALL] = proxy->fslots[JSSLOT_PROXY_CALL];
         newborn->fslots[JSSLOT_CALLABLE_CONSTRUCT] = proxy->fslots[JSSLOT_PROXY_CONSTRUCT];
     }
 
     if (!js_PopulateObject(cx, newborn, props))
         return false;
 
     /* Trade spaces between the newborn object and the proxy. */
     proxy->swap(newborn);
 
     /* The GC will dispose of the proxy object. */
 
     *bp = true;
     return true;
 }
 
-JS_PUBLIC_API(JSBool)
-JS_Becomes(JSContext *cx, JSObject *obj, JSObject *obj2)
+}
+
+JS_FRIEND_API(JSObject *)
+js_InitProxyClass(JSContext *cx, JSObject *obj)
 {
-#ifdef JS_THREADSAFE
-    JS_ASSERT_IF(obj->isNative(), obj->scope()->title.ownercx == cx);
-    JS_ASSERT_IF(obj2->isNative(), obj2->scope()->title.ownercx == cx);
-#endif
-    obj->swap(obj2);
-    return true;
+    JSObject *module = NewObject(cx, &js_ObjectClass, NULL, obj);
+    if (!module)
+        return NULL;
+    if (!JS_DefineProperty(cx, obj, "Proxy", OBJECT_TO_JSVAL(module),
+                           JS_PropertyStub, JS_PropertyStub, 0)) {
+        return NULL;
+    }
+    if (!JS_DefineFunctions(cx, module, static_methods))
+        return NULL;
+    return obj;
 }
--- a/js/src/jsproxy.h
+++ b/js/src/jsproxy.h
@@ -40,16 +40,18 @@
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef jsproxy_h___
 #define jsproxy_h___
 
 #include "jsapi.h"
 #include "jsobj.h"
 
+namespace js {
+
 /* Base class for all C++ proxy handlers. */
 class JSProxyHandler {
   public:
     virtual ~JSProxyHandler();
 
     /* ES5 Harmony fundamental proxy traps. */
     virtual bool getPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc) = 0;
     virtual bool getOwnPropertyDescriptor(JSContext *cx, JSObject *proxy, jsid id, JSPropertyDescriptor *desc) = 0;
@@ -140,95 +142,98 @@ class JSProxy {
 const uint32 JSSLOT_PROXY_HANDLER = JSSLOT_PRIVATE + 0;
 /* Object proxies only. */
 const uint32 JSSLOT_PROXY_CLASS = JSSLOT_PRIVATE + 1;
 const uint32 JSSLOT_PROXY_PRIVATE = JSSLOT_PRIVATE + 2;
 /* Function proxies only. */
 const uint32 JSSLOT_PROXY_CALL = JSSLOT_PRIVATE + 1;
 const uint32 JSSLOT_PROXY_CONSTRUCT = JSSLOT_PRIVATE + 2;
 
-extern JS_FRIEND_API(JSClass) js_ObjectProxyClass;
-extern JS_FRIEND_API(JSClass) js_FunctionProxyClass;
-extern JSClass js_CallableObjectClass;
+extern JS_FRIEND_API(JSClass) ObjectProxyClass;
+extern JS_FRIEND_API(JSClass) FunctionProxyClass;
+extern JSClass CallableObjectClass;
+
+}
 
 inline bool
 JSObject::isObjectProxy() const
 {
-    return getClass() == &js_ObjectProxyClass;
+    return getClass() == &js::ObjectProxyClass;
 }
 
 inline bool
 JSObject::isFunctionProxy() const
 {
-    return getClass() == &js_FunctionProxyClass;
+    return getClass() == &js::FunctionProxyClass;
 }
 
 inline bool
 JSObject::isProxy() const
 {
     return isObjectProxy() || isFunctionProxy();
 }
 
 inline jsval
 JSObject::getProxyHandler() const
 {
     JS_ASSERT(isProxy());
-    jsval handler = fslots[JSSLOT_PROXY_HANDLER];
+    jsval handler = fslots[js::JSSLOT_PROXY_HANDLER];
     JS_ASSERT(JSVAL_IS_OBJECT(handler) || JSVAL_IS_INT(handler));
     return handler;
 }
 
 inline jsval
 JSObject::getProxyPrivate() const
 {
     JS_ASSERT(isObjectProxy());
-    return fslots[JSSLOT_PROXY_PRIVATE];
+    return fslots[js::JSSLOT_PROXY_PRIVATE];
 }
 
 inline void
 JSObject::setProxyPrivate(jsval priv)
 {
     JS_ASSERT(isObjectProxy());
-    fslots[JSSLOT_PROXY_PRIVATE] = priv;
+    fslots[js::JSSLOT_PROXY_PRIVATE] = priv;
 }
 
-JS_PUBLIC_API(JSObject *)
-JS_NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSString *className);
+namespace js {
 
-JS_PUBLIC_API(JSObject *)
-JS_NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSObject *call, JSObject *construct);
+JS_FRIEND_API(JSObject *)
+NewObjectProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSString *className);
 
-JS_PUBLIC_API(JSBool)
-JS_GetProxyObjectClass(JSContext *cx, JSObject *proxy, const char **namep);
+JS_FRIEND_API(JSObject *)
+NewFunctionProxy(JSContext *cx, jsval handler, JSObject *proto, JSObject *parent, JSObject *call, JSObject *construct);
 
-JS_PUBLIC_API(JSBool)
-JS_FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp);
+JS_FRIEND_API(JSBool)
+GetProxyObjectClass(JSContext *cx, JSObject *proxy, const char **namep);
 
-JS_PUBLIC_API(JSBool)
-JS_Becomes(JSContext *cx, JSObject *obj, JSObject *obj2);
+JS_FRIEND_API(JSBool)
+FixProxy(JSContext *cx, JSObject *proxy, JSBool *bp);
 
 template <class T>
 JSObject *
 JSNoopProxyHandler::wrap(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent, JSString *className)
 {
     if (obj->isCallable()) {
         JSNoopProxyHandler *handler = new T(obj);
         if (!handler)
             return NULL;
-        JSObject *wrapper = JS_NewFunctionProxy(cx, PRIVATE_TO_JSVAL(handler), proto, parent, obj, NULL);
+        JSObject *wrapper = NewFunctionProxy(cx, PRIVATE_TO_JSVAL(handler), proto, parent, obj, NULL);
         if (!wrapper)
             delete handler;
         return wrapper;
     }
-    JSObject *wrapper = JS_NewObjectProxy(cx, PRIVATE_TO_JSVAL(&T::singleton), proto, parent, className);
+    JSObject *wrapper = NewObjectProxy(cx, PRIVATE_TO_JSVAL(&T::singleton), proto, parent, className);
     if (wrapper)
         wrapper->setProxyPrivate(OBJECT_TO_JSVAL(obj));
     return wrapper;
 }
 
+}
+
 JS_BEGIN_EXTERN_C
 
 extern JS_FRIEND_API(JSObject *)
 js_InitProxyClass(JSContext *cx, JSObject *obj);
 
 JS_END_EXTERN_C
 
 #endif