Make ParallelArray safe for moving GC (bug 755564 part 1, r=billm).
authorDavid Anderson <danderson@mozilla.com>
Thu, 17 May 2012 13:40:06 -0700
changeset 95531 fe35715a3f013889b651ad90c42c6810ca7801fe
parent 95530 d114473c2eab780d6a94ebe4c2936326c100ea5c
child 95532 b15990ffc15d74082b85baa9559f9a1ddbd70fc4
push id913
push userdanderson@mozilla.com
push dateThu, 17 May 2012 21:19:49 +0000
reviewersbillm
bugs755564
milestone15.0a1
Make ParallelArray safe for moving GC (bug 755564 part 1, r=billm).
js/src/builtin/ParallelArray.cpp
--- a/js/src/builtin/ParallelArray.cpp
+++ b/js/src/builtin/ParallelArray.cpp
@@ -50,44 +50,47 @@ using namespace js;
 
 enum {
     JSSLOT_PA_LENGTH = 0,
     JSSLOT_PA_BUFFER,
     JSSLOT_PA_MAX
 };
 
 inline uint32_t
-GetLength(JSObject *obj) {
+GetLength(JSObject *obj)
+{
     return uint32_t(obj->getSlot(JSSLOT_PA_LENGTH).toInt32());
 }
 
 inline JSObject *
-GetBuffer(JSObject *obj) {
+GetBuffer(JSObject *obj)
+{
     return &(obj->getSlot(JSSLOT_PA_BUFFER).toObject());
 }
 
-static bool
-NewParallelArray(JSContext *cx, JSObject *buffer, uint32_t length, JSObject **result) {
-    *result = NewBuiltinClassInstance(cx, &ParallelArrayClass);
-    if (!*result)
-        return false;
+static JSObject *
+NewParallelArray(JSContext *cx, JSObject *buffer, uint32_t length)
+{
+    JSObject *result = NewBuiltinClassInstance(cx, &ParallelArrayClass);
+    if (!result)
+        return NULL;
 
-    (*result)->setSlot(JSSLOT_PA_LENGTH, Int32Value(int32_t(length)));
-    (*result)->setSlot(JSSLOT_PA_BUFFER, ObjectValue(*buffer));
+    result->setSlot(JSSLOT_PA_LENGTH, Int32Value(int32_t(length)));
+    result->setSlot(JSSLOT_PA_BUFFER, ObjectValue(*buffer));
 
-    return true;
+    return result;
 }
 
 static JSBool
 ParallelArray_get(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool ok;
-    JSObject *obj = NonGenericMethodGuard(cx, args, ParallelArray_get, &ParallelArrayClass, &ok);
+    RootedVarObject obj(cx, NonGenericMethodGuard(cx, args, ParallelArray_get, &ParallelArrayClass, &ok));
     if (!obj)
         return ok;
 
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              "ParallelArray.get", "0", "s");
         return false;
     }
@@ -106,24 +109,24 @@ ParallelArray_get(JSContext *cx, unsigne
     return true;
 }
 
 static JSBool
 ParallelArray_build(JSContext *cx, uint32_t length, const Value &thisv, JSObject *elementalFun,
                     bool passElement, unsigned extrasc, Value *extrasp, Value *vp)
 {
     /* create data store for results */
-    JSObject *buffer = NewDenseAllocatedArray(cx, length);
+    RootedVarObject buffer(cx, NewDenseAllocatedArray(cx, length));
     if (!buffer)
         return false;
 
     buffer->ensureDenseArrayInitializedLength(cx, length, 0);
 
     /* grab source buffer if we need to pass elements */
-    JSObject *srcBuffer;
+    RootedVarObject srcBuffer(cx);
     if (passElement)
         srcBuffer = &(thisv.toObject().getSlot(JSSLOT_PA_BUFFER).toObject());
 
     /* prepare call frame on stack */
     InvokeArgsGuard args;
     cx->stack.pushInvokeArgs(cx, extrasc + 1, &args);
 
     for (uint32_t i = 0; i < length; i++) {
@@ -148,18 +151,18 @@ ParallelArray_build(JSContext *cx, uint3
         if (!Invoke(cx, args))
             return false;
 
         /* set result element */
         buffer->setDenseArrayElementWithType(cx, i, args.rval());
     }
 
     /* create ParallelArray wrapper class */
-    JSObject *result;
-    if (!NewParallelArray(cx, buffer, length, &result))
+    RootedVarObject result(cx, NewParallelArray(cx, buffer, length));
+    if (!result)
         return false;
 
     vp->setObject(*result);
     return true;
 }
 
 static JSBool
 ParallelArray_construct(JSContext *cx, unsigned argc, Value *vp)
@@ -174,24 +177,24 @@ ParallelArray_construct(JSContext *cx, u
 
     if (args.length() == 1) {
         /* first case: init using an array value */
         if (!args[0].isObject()) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
             return false;
         }
 
-        JSObject *src = &(args[0].toObject());
+        RootedVarObject src(cx, &(args[0].toObject()));
 
         uint32_t srcLen;
         if (!js_GetLengthProperty(cx, src, &srcLen))
             return false;
 
         /* allocate buffer for result */
-        JSObject *buffer = NewDenseAllocatedArray(cx, srcLen);
+        RootedVarObject buffer(cx, NewDenseAllocatedArray(cx, srcLen));
         if (!buffer)
             return false;
 
         buffer->ensureDenseArrayInitializedLength(cx, srcLen, 0);
 
         for (uint32_t i = 0; i < srcLen; i++) {
             Value elem;
             if (src->isDenseArray() && (i < src->getDenseArrayInitializedLength())) {
@@ -203,32 +206,32 @@ ParallelArray_construct(JSContext *cx, u
                 /* generic case */
                 if (!src->getElement(cx, src, i, &elem))
                     return false;
             }
 
             buffer->setDenseArrayElementWithType(cx, i, elem);
         }
 
-        JSObject *result;
-        if (!NewParallelArray(cx, buffer, srcLen, &result))
+        RootedVarObject result(cx, NewParallelArray(cx, buffer, srcLen));
+        if (!result)
             return false;
 
         args.rval().setObject(*result);
         return true;
     }
 
     /* second case: init using length and function */
     /* extract first argument, the length */
     uint32_t length;
     if (!ToUint32(cx, args[0], &length))
         return false;
 
     /* extract second argument, the elemental function */
-    JSObject *elementalFun = js_ValueToCallableObject(cx, &args[1], JSV2F_SEARCH_STACK);
+    RootedVarObject elementalFun(cx, js_ValueToCallableObject(cx, &args[1], JSV2F_SEARCH_STACK));
     if (!elementalFun)
         return false;
 
     /* use build with |this| set to |undefined| */
     return ParallelArray_build(cx, length, UndefinedValue(), elementalFun, false, 0, NULL, &(args.rval()));
 }
 
 /* forward declaration */
@@ -249,43 +252,46 @@ ParallelArray_combine(JSContext *cx, uns
 
 static JSBool
 ParallelArray_mapOrCombine(JSContext *cx, unsigned argc, Value *vp, bool isMap)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* make sure we are called on a ParallelArray */
     bool ok;
-    JSObject *obj = NonGenericMethodGuard(cx, args, (isMap ? ParallelArray_map : ParallelArray_combine),
-                                          &ParallelArrayClass, &ok);
+    RootedVarObject obj(cx);
+    obj = NonGenericMethodGuard(cx, args, (isMap ? ParallelArray_map : ParallelArray_combine),
+                                &ParallelArrayClass, &ok);
     if (!obj)
         return ok;
 
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              (isMap ? "map" : "combine"), "0", "s");
         return false;
     }
 
     /* extract first argument, the elemental function */
-    JSObject *elementalFun = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
+    RootedVarObject elementalFun(cx, js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK));
     if (!elementalFun)
         return false;
 
     /* check extra arguments for map to be objects */
-    if (isMap && (argc > 1))
-        for (unsigned i = 1; i < argc; i++)
+    if (isMap && (argc > 1)) {
+        for (unsigned i = 1; i < argc; i++) {
             if (!args[i].isObject()) {
                 char buffer[4];
                 JS_snprintf(buffer, 4, "%d", i+1);
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_MAP_INVALID_ARG,
                                      buffer);
 
                 return false;
             }
+        }
+    }
 
     return ParallelArray_build(cx, GetLength(obj), ObjectValue(*obj), elementalFun, isMap,
                                (isMap ? argc-1 : 0), ((argc > 1) ? &(args[1]) : NULL), &(args.rval()));
 }
 
 /* forward declaration */
 static JSBool
 ParallelArray_scanOrReduce(JSContext *cx, unsigned argc, Value *vp, bool isScan);
@@ -304,54 +310,57 @@ ParallelArray_reduce(JSContext *cx, unsi
 
 static JSBool
 ParallelArray_scanOrReduce(JSContext *cx, unsigned argc, Value *vp, bool isScan)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* make sure we are called on a ParallelArray */
     bool ok;
-    JSObject *obj = NonGenericMethodGuard(cx, args, (isScan ? ParallelArray_scan : ParallelArray_reduce), &ParallelArrayClass, &ok);
+    RootedVarObject obj(cx);
+    obj = NonGenericMethodGuard(cx, args, (isScan ? ParallelArray_scan : ParallelArray_reduce), &ParallelArrayClass, &ok);
     if (!obj)
         return ok;
 
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              (isScan ? "scan" : "reduce"), "0", "s");
         return false;
     }
 
     uint32_t length = GetLength(obj);
 
-    JSObject *result = NULL, *resBuffer = NULL;
+    RootedVarObject result(cx);
+    RootedVarObject resBuffer(cx);
     if (isScan) {
         /* create data store for results */
         resBuffer = NewDenseAllocatedArray(cx, length);
         if (!resBuffer)
             return false;
 
         resBuffer->ensureDenseArrayInitializedLength(cx, length, 0);
 
         /* create ParallelArray wrapper class */
-        if (!NewParallelArray(cx, resBuffer, length, &result))
+        result = NewParallelArray(cx, resBuffer, length);
+        if (!result)
             return false;
     }
 
     /* extract first argument, the elemental function */
-    JSObject *elementalFun = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
+    RootedVarObject elementalFun(cx, js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK));
     if (!elementalFun)
         return false;
 
     /* special case of empty arrays */
     if (length == 0) {
         args.rval() = (isScan ? ObjectValue(*result) : UndefinedValue());
         return true;
     }
 
-    JSObject *buffer = GetBuffer(obj);
+    RootedVarObject buffer(cx, GetBuffer(obj));
 
     Value accu = buffer->getDenseArrayElement(0);
     if (isScan)
         resBuffer->setDenseArrayElementWithType(cx, 0, accu);
 
     /* prepare call frame on stack */
     InvokeArgsGuard ag;
     if (!cx->stack.pushInvokeArgs(cx, 2, &ag))
@@ -378,41 +387,43 @@ ParallelArray_scanOrReduce(JSContext *cx
     }
 
     args.rval() = (isScan ? ObjectValue(*result) : accu);
 
     return true;
 }
 
 static JSBool
-ParallelArray_filter(JSContext *cx, unsigned argc, Value *vp) {
+ParallelArray_filter(JSContext *cx, unsigned argc, Value *vp)
+{
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* make sure we are called on a ParallelArray */
     bool ok;
-    JSObject *obj = NonGenericMethodGuard(cx, args, ParallelArray_filter, &ParallelArrayClass, &ok);
+    RootedVarObject obj(cx);
+    obj = NonGenericMethodGuard(cx, args, ParallelArray_filter, &ParallelArrayClass, &ok);
     if (!obj)
-        return false;
+        return ok;
 
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              "filter", "0", "s");
         return false;
     }
 
     /* extract first argument, the elemental function */
-    JSObject *elementalFun = js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK);
+    RootedVarObject elementalFun(cx, js_ValueToCallableObject(cx, &args[0], JSV2F_SEARCH_STACK));
     if (!elementalFun)
         return false;
 
-    JSObject *buffer = GetBuffer(obj);
+    RootedVarObject buffer(cx, GetBuffer(obj));
     uint32_t length = GetLength(obj);
 
     /* We just assume the length of the input as the length of the output. */
-    JSObject *resBuffer = NewDenseAllocatedArray(cx, length);
+    RootedVarObject resBuffer(cx, NewDenseAllocatedArray(cx, length));
     if (!resBuffer)
         return false;
 
     resBuffer->ensureDenseArrayInitializedLength(cx, length, 0);
 
     /* prepare call frame on stack */
     InvokeArgsGuard frame;
     cx->stack.pushInvokeArgs(cx, 1, &frame);
@@ -430,36 +441,37 @@ ParallelArray_filter(JSContext *cx, unsi
         if (js_ValueToBoolean(frame.rval()))
             resBuffer->setDenseArrayElementWithType(cx, pos++, buffer->getDenseArrayElement(i));
     }
 
     /* shrink the array to the proper size */
     resBuffer->setArrayLength(cx, pos);
 
     /* create ParallelArray wrapper class */
-    JSObject *result;
-    if (!NewParallelArray(cx, resBuffer, pos, &result))
+    RootedVarObject result(cx, NewParallelArray(cx, resBuffer, pos));
+    if (!result)
         return false;
 
     args.rval() = ObjectValue(*result);
     return true;
 }
 
 static JSBool
 ParallelArray_scatter(JSContext *cx, unsigned argc, Value *vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     /* make sure we are called on a ParallelArray */
     bool ok;
-    JSObject *obj = NonGenericMethodGuard(cx, args, ParallelArray_scatter, &ParallelArrayClass, &ok);
+    RootedVarObject obj(cx);
+    obj = NonGenericMethodGuard(cx, args, ParallelArray_scatter, &ParallelArrayClass, &ok);
     if (!obj)
-        return false;
+        return ok;
 
-    JSObject *buffer = GetBuffer(obj);
+    RootedVarObject buffer(cx, GetBuffer(obj));
 
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
                              "scatter", "0", "s");
         return false;
     }
 
     /* grab the scatter vector */
@@ -474,21 +486,21 @@ ParallelArray_scatter(JSContext *cx, uns
         return false;
 
     if (scatterLen > GetLength(obj)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_SCATTER_INVALID_VEC);
         return false;
     }
 
     /* next, default value */
-    Value defValue = UndefinedValue();
+    RootedVarValue defValue(cx, UndefinedValue());
     if (args.length() >= 2)
         defValue = args[1];
 
-    JSObject *conflictFun = NULL;
+    RootedVarObject conflictFun(cx);
     /* conflict resolution function */
     if ((args.length() >= 3) && !args[2].isUndefined()) {
         conflictFun = js_ValueToCallableObject(cx, &args[2], JSV2F_SEARCH_STACK);
         if (!conflictFun)
             return false;
     }
 
     /* optional length */
@@ -497,44 +509,44 @@ ParallelArray_scatter(JSContext *cx, uns
         if (!ToUint32(cx, args[3], &length))
             return false;
     } else {
         /* we assume the source's length */
         length = GetLength(obj);
     }
 
     /* allocate space for the result */
-    JSObject *resBuffer = NewDenseAllocatedArray(cx, length);
+    RootedVarObject resBuffer(cx, NewDenseAllocatedArray(cx, length));
     if (!resBuffer)
         return false;
 
     resBuffer->ensureDenseArrayInitializedLength(cx, length, 0);
 
     /* iterate over the scatter vector */
     for (uint32_t i = 0; i < scatterLen; i++) {
         /* read target index */
-        Value elem;
-        if (!targets.getElement(cx, &targets, i, &elem))
+        RootedVarValue elem(cx);
+        if (!targets.getElement(cx, &targets, i, elem.address()))
             return false;
 
         uint32_t targetIdx;
         if (!ToUint32(cx, elem, &targetIdx))
             return false;
 
         if (targetIdx >= length) {
             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_PAR_ARRAY_SCATTER_BNDS);
             return false;
         }
 
         /* read current value */
-        Value readV = buffer->getDenseArrayElement(i);
+        RootedVarValue readV(cx, buffer->getDenseArrayElement(i));
 
-        Value previous = resBuffer->getDenseArrayElement(targetIdx);
+        RootedVarValue previous(cx, resBuffer->getDenseArrayElement(targetIdx));
 
-        if (!previous.isMagic(JS_ARRAY_HOLE)) {
+        if (!previous.reference().isMagic(JS_ARRAY_HOLE)) {
             if (conflictFun) {
                 /* we have a conflict, so call the resolution function to resovle it */
                 InvokeArgsGuard ag;
                 if (!cx->stack.pushInvokeArgs(cx, 2, &ag))
                     return false;
                 ag.setCallee(ObjectValue(*conflictFun));
                 ag[0] = readV;
                 ag[1] = previous;
@@ -559,41 +571,41 @@ ParallelArray_scatter(JSContext *cx, uns
 
     /* fill holes */
     for (uint32_t i = 0; i < length; i++) {
         if (resBuffer->getDenseArrayElement(i).isMagic(JS_ARRAY_HOLE))
             resBuffer->setDenseArrayElementWithType(cx, i, defValue);
     }
 
     /* create ParallelArray wrapper class */
-    JSObject *result;
-    if (!NewParallelArray(cx, resBuffer, length, &result))
+    RootedVarObject result(cx, NewParallelArray(cx, resBuffer, length));
+    if (!result)
         return false;
 
     args.rval() = ObjectValue(*result);
     return true;
 }
 
 static JSBool
 ParallelArray_forward_method(JSContext *cx, unsigned argc, Value *vp, Native native, jsid id)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
     bool ok;
-    JSObject *obj = NonGenericMethodGuard(cx, args, native, &ParallelArrayClass, &ok);
-    if (!ok)
+    RootedVarObject obj(cx, NonGenericMethodGuard(cx, args, native, &ParallelArrayClass, &ok));
+    if (!obj)
+        return ok;
+
+    RootedVarValue callable(cx);
+    RootedVarObject buffer(cx, GetBuffer(obj));
+    if (!js_GetMethod(cx, buffer, id, 0, callable.address()))
         return false;
 
-    Value callable;
-    RootedVarObject buffer(cx, GetBuffer(obj));
-    if (!js_GetMethod(cx, buffer, id, 0, &callable))
-        return false;
-
-    Value rval;
-    if (!Invoke(cx, ObjectOrNullValue(buffer), callable, argc, vp, &rval))
+    RootedVarValue rval(cx);
+    if (!Invoke(cx, ObjectOrNullValue(buffer), callable, argc, vp, rval.address()))
         return false;
 
     *vp = rval;
     return true;
 }
 
 static JSBool
 ParallelArray_toString(JSContext *cx, unsigned argc, Value *vp)
@@ -650,27 +662,27 @@ IsDenseArrayId(JSContext *cx, JSObject *
     uint32_t i;
     return (js_IdIsIndex(id, &i) && IsDenseArrayIndex(obj, i));
 }
 
 static JSBool
 ParallelArray_lookupGeneric(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
                             JSProperty **propp)
 {
-    JSObject *buffer = GetBuffer(obj);
+    RootedVarObject buffer(cx, GetBuffer(obj));
 
     if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom) ||
         IsDenseArrayId(cx, buffer, id))
     {
         *propp = (JSProperty *) 1; /* TRUE */
         *objp = obj;
         return true;
     }
 
-    JSObject *proto = obj->getProto();
+    RootedVarObject proto(cx, obj->getProto());
     if (proto)
         return proto->lookupGeneric(cx, id, objp, propp);
 
     *objp = NULL;
     *propp = NULL;
     return true;
 }
 
@@ -705,50 +717,49 @@ ParallelArray_lookupSpecial(JSContext *c
                             JSProperty **propp)
 {
     return ParallelArray_lookupGeneric(cx, obj, SPECIALID_TO_JSID(sid), objp, propp);
 }
 
 static JSBool
 ParallelArray_getGeneric(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
 {
-
     if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
         vp->setNumber(GetLength(obj));
         return true;
     }
 
-    JSObject *buffer = GetBuffer(obj);
+    RootedVarObject buffer(cx, GetBuffer(obj));
     if (IsDenseArrayId(cx, buffer, id))
         return buffer->getGeneric(cx, receiver, id, vp);
 
-    JSObject *proto = obj->getProto();
+    RootedVarObject proto(cx, obj->getProto());
     if (proto)
         return proto->getGeneric(cx, receiver, id, vp);
 
     vp->setUndefined();
     return true;
 }
 
 static JSBool
 ParallelArray_getProperty(JSContext *cx, JSObject *obj, JSObject *receiver, PropertyName *name, Value *vp)
 {
     return ParallelArray_getGeneric(cx, obj, receiver, AtomToId(name), vp);
 }
 
 static JSBool
 ParallelArray_getElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
 {
-    JSObject *buffer = GetBuffer(obj);
+    RootedVarObject buffer(cx, GetBuffer(obj));
     if (IsDenseArrayIndex(buffer, index)) {
         *vp = buffer->getDenseArrayElement(index);
         return true;
     }
 
-    JSObject *proto = obj->getProto();
+    RootedVarObject proto(cx, obj->getProto());
     if (proto)
         return proto->getElement(cx, receiver, index, vp);
 
     vp->setUndefined();
     return true;
 }
 
 static JSBool
@@ -1031,31 +1042,33 @@ static JSFunctionSpec parallel_array_met
 
 JSObject *
 js_InitParallelArrayClass(JSContext *cx, JSObject *obj)
 {
     JS_ASSERT(obj->isNative());
 
     GlobalObject *global = &obj->asGlobal();
 
-    JSObject *parallelArrayProto = global->createBlankPrototype(cx, &ParallelArrayProtoClass);
+    RootedVarObject parallelArrayProto(cx, global->createBlankPrototype(cx, &ParallelArrayProtoClass));
     if (!parallelArrayProto)
         return NULL;
     /* define the length property */
     const jsid lengthId = AtomToId(cx->runtime->atomState.lengthAtom);
 
     parallelArrayProto->addProperty(cx, lengthId, ParallelArray_length_getter, NULL,
                                     SHAPE_INVALID_SLOT, JSPROP_PERMANENT | JSPROP_READONLY, 0, 0);
 
-    JSFunction *ctor = global->createConstructor(cx, ParallelArray_construct, CLASS_NAME(cx, ParallelArray), 0);
+    RootedVarFunction ctor(cx);
+    ctor = global->createConstructor(cx, ParallelArray_construct, CLASS_NAME(cx, ParallelArray), 0);
     if (!ctor)
         return NULL;
 
     if (!LinkConstructorAndPrototype(cx, ctor, parallelArrayProto))
         return NULL;
 
     if (!DefinePropertiesAndBrand(cx, parallelArrayProto, NULL, parallel_array_methods))
         return NULL;
 
     if (!DefineConstructorAndPrototype(cx, global, JSProto_ParallelArray, ctor, parallelArrayProto))
         return NULL;
     return parallelArrayProto;
 }
+