Esempio n. 1
0
bool
CrossCompartmentWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                                    CallArgs srcArgs)
{
    Rooted<JSObject*> wrapper(cx, &srcArgs.thisv().toObject());
    JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) ||
              !UnwrapObject(wrapper)->isCrossCompartmentWrapper());

    RootedObject wrapped(cx, wrappedObject(wrapper));
    {
        AutoCompartment call(cx, wrapped);
        InvokeArgsGuard dstArgs;
        if (!cx->stack.pushInvokeArgs(cx, srcArgs.length(), &dstArgs))
            return false;

        Value *src = srcArgs.base();
        Value *srcend = srcArgs.array() + srcArgs.length();
        Value *dst = dstArgs.base();
        for (; src < srcend; ++src, ++dst) {
            *dst = *src;
            if (!cx->compartment->wrap(cx, dst))
                return false;
        }

        if (!CallNonGenericMethod(cx, test, impl, dstArgs))
            return false;

        srcArgs.rval().set(dstArgs.rval());
        dstArgs.pop();
    }
    return cx->compartment->wrap(cx, srcArgs.rval().address());
}
Esempio n. 2
0
bool
CrossCompartmentWrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs srcArgs)
{
    JS_ASSERT_IF(!srcArgs.calleev().isUndefined(),
                 srcArgs.callee().toFunction()->native() == native ||
                 srcArgs.callee().toFunction()->native() == js_generic_native_method_dispatcher);
    JS_ASSERT(&srcArgs.thisv().toObject() == wrapper);
    JS_ASSERT(!UnwrapObject(wrapper)->isCrossCompartmentWrapper());

    JSObject *wrapped = wrappedObject(wrapper);
    AutoCompartment call(cx, wrapped);
    if (!call.enter())
        return false;

    InvokeArgsGuard dstArgs;
    if (!cx->stack.pushInvokeArgs(cx, srcArgs.length(), &dstArgs))
        return false;

    Value *src = srcArgs.base(); 
    Value *srcend = srcArgs.array() + srcArgs.length();
    Value *dst = dstArgs.base();
    for (; src != srcend; ++src, ++dst) {
        *dst = *src;
        if (!call.destination->wrap(cx, dst))
            return false;
    }

    if (!Wrapper::nativeCall(cx, wrapper, clasp, native, dstArgs))
        return false;

    dstArgs.pop();
    call.leave();
    srcArgs.rval() = dstArgs.rval();
    return call.origin->wrap(cx, &srcArgs.rval());
}
Esempio n. 3
0
bool
CrossCompartmentWrapper::nativeCall(JSContext *cx, JSObject *wrapper, Class *clasp, Native native, CallArgs srcArgs)
{
    JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) || &srcArgs.thisv().toObject() == wrapper);
    JS_ASSERT(!UnwrapObject(wrapper)->isCrossCompartmentWrapper());

    JSObject *wrapped = wrappedObject(wrapper);
    AutoCompartment call(cx, wrapped);
    if (!call.enter())
        return false;

    InvokeArgsGuard dstArgs;
    if (!cx->stack.pushInvokeArgs(cx, srcArgs.length(), &dstArgs))
        return false;

    Value *src = srcArgs.base();
    Value *srcend = srcArgs.array() + srcArgs.length();
    Value *dst = dstArgs.base();
    for (; src != srcend; ++src, ++dst) {
        *dst = *src;
        if (!call.destination->wrap(cx, dst))
            return false;
    }

    if (!CallJSNative(cx, native, dstArgs))
        return false;

    srcArgs.rval() = dstArgs.rval();
    dstArgs.pop();
    call.leave();
    return cx->compartment->wrap(cx, &srcArgs.rval());
}
Esempio n. 4
0
bool
js::parallel::Do(JSContext *cx, CallArgs &args)
{
    JS_ASSERT(args[0].isObject());
    JS_ASSERT(args[0].toObject().isFunction());

    RootedObject fun(cx, &args[0].toObject());
    ParallelDo op(cx, fun);
    ExecutionStatus status = op.apply();
    if (status == ExecutionFatal)
        return false;

    if (args[1].isObject()) {
        RootedObject feedback(cx, &args[1].toObject());
        if (feedback && feedback->isFunction()) {
            InvokeArgsGuard feedbackArgs;
            if (!cx->stack.pushInvokeArgs(cx, 1, &feedbackArgs))
                return false;
            feedbackArgs.setCallee(ObjectValue(*feedback));
            feedbackArgs.setThis(UndefinedValue());
            if (status == ExecutionParallel)
                feedbackArgs[0].setInt32(op.bailouts);
            else
                feedbackArgs[0] = cx->runtime->positiveInfinityValue;
            if (!Invoke(cx, feedbackArgs))
                return false;
        }
    }

    return true;
}
Esempio n. 5
0
/*static*/ JSBool
ParallelArrayObject::constructHelper(JSContext *cx, MutableHandleFunction ctor, CallArgs &args0)
{
    RootedObject result(cx, newInstance(cx));
    if (!result)
        return false;

    if (cx->typeInferenceEnabled()) {
        jsbytecode *pc;
        RootedScript script(cx, cx->stack.currentScript(&pc));
        if (script) {
            if (ctor->nonLazyScript()->shouldCloneAtCallsite) {
                ctor.set(CloneFunctionAtCallsite(cx, ctor, script, pc));
                if (!ctor)
                    return false;
            }

            // Create the type object for the PA.  Add in the current
            // properties as definite properties if this type object is newly
            // created.  To tell if it is newly created, we check whether it
            // has any properties yet or not, since any returned type object
            // must have been created by this same C++ code and hence would
            // already have properties if it had been returned before.
            types::TypeObject *paTypeObject =
                types::TypeScript::InitObject(cx, script, pc, JSProto_ParallelArray);
            if (!paTypeObject)
                return false;
            if (paTypeObject->getPropertyCount() == 0 && !paTypeObject->unknownProperties()) {
                if (!paTypeObject->addDefiniteProperties(cx, result))
                    return false;

                // addDefiniteProperties() above should have added one
                // property for each of the fixed slots:
                JS_ASSERT(paTypeObject->getPropertyCount() == NumFixedSlots);
            }
            result->setType(paTypeObject);
        }
    }

    InvokeArgsGuard args;
    if (!cx->stack.pushInvokeArgs(cx, args0.length(), &args))
        return false;

    args.setCallee(ObjectValue(*ctor));
    args.setThis(ObjectValue(*result));

    for (uint32_t i = 0; i < args0.length(); i++)
        args[i] = args0[i];

    if (!Invoke(cx, args))
        return false;

    args0.rval().setObject(*result);
    return true;
}
Esempio n. 6
0
bool
js::BooleanGetPrimitiveValueSlow(JSContext *cx, JSObject &obj, Value *vp)
{
    InvokeArgsGuard ag;
    if (!cx->stack.pushInvokeArgs(cx, 0, &ag))
        return false;
    ag.setCallee(cx->compartment->maybeGlobal()->booleanValueOf());
    ag.setThis(ObjectValue(obj));
    if (!Invoke(cx, ag))
        return false;
    *vp = ag.rval();
    return true;
}
Esempio n. 7
0
bool
CrossCompartmentWrapper::nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
                                    CallArgs srcArgs)
{
    RootedObject wrapper(cx, &srcArgs.thisv().toObject());
    JS_ASSERT(srcArgs.thisv().isMagic(JS_IS_CONSTRUCTING) ||
              !UncheckedUnwrap(wrapper)->isCrossCompartmentWrapper());

    RootedObject wrapped(cx, wrappedObject(wrapper));
    {
        AutoCompartment call(cx, wrapped);
        InvokeArgsGuard dstArgs;
        if (!cx->stack.pushInvokeArgs(cx, srcArgs.length(), &dstArgs))
            return false;

        Value *src = srcArgs.base();
        Value *srcend = srcArgs.array() + srcArgs.length();
        Value *dst = dstArgs.base();

        RootedValue source(cx);
        for (; src < srcend; ++src, ++dst) {
            source = *src;
            if (!cx->compartment->wrap(cx, &source))
                return false;
            *dst = source.get();

            // Handle |this| specially. When we rewrap on the other side of the
            // membrane, we might apply a same-compartment security wrapper that
            // will stymie this whole process. If that happens, unwrap the wrapper.
            // This logic can go away when same-compartment security wrappers go away.
            if ((src == srcArgs.base() + 1) && dst->isObject()) {
                RootedObject thisObj(cx, &dst->toObject());
                if (thisObj->isWrapper() &&
                    !Wrapper::wrapperHandler(thisObj)->isSafeToUnwrap())
                {
                    JS_ASSERT(!IsCrossCompartmentWrapper(thisObj));
                    *dst = ObjectValue(*Wrapper::wrappedObject(thisObj));
                }
            }
        }

        if (!CallNonGenericMethod(cx, test, impl, dstArgs))
            return false;

        srcArgs.rval().set(dstArgs.rval());
        dstArgs.pop();
    }
    return cx->compartment->wrap(cx, srcArgs.rval());
}
Esempio n. 8
0
bool
js::ForkJoin(JSContext *cx, CallArgs &args)
{
    JS_ASSERT(args[0].isObject());
    JS_ASSERT(args[0].toObject().isFunction());

    RootedObject fun(cx, &args[0].toObject());
    ParallelDo op(cx, fun);
    ExecutionStatus status = op.apply();
    if (status == ExecutionFatal)
        return false;

    if (args[1].isObject()) {
        RootedObject feedback(cx, &args[1].toObject());
        if (feedback && feedback->isFunction()) {
            InvokeArgsGuard feedbackArgs;
            if (!cx->stack.pushInvokeArgs(cx, 3, &feedbackArgs))
                return false;

            const char *resultString;
            switch (status) {
              case ExecutionParallel:
                resultString = (op.bailouts == 0 ? "success" : "bailout");
                break;

              case ExecutionFatal:
              case ExecutionSequential:
                resultString = "disqualified";
                break;
            }
            feedbackArgs.setCallee(ObjectValue(*feedback));
            feedbackArgs.setThis(UndefinedValue());
            feedbackArgs[0].setString(JS_NewStringCopyZ(cx, resultString));
            feedbackArgs[1].setInt32(op.bailouts);
            feedbackArgs[2].setInt32(op.cause);
            if (!Invoke(cx, feedbackArgs))
                return false;
        }
    }

    return true;
}
Esempio n. 9
0
void
ContextStack::popInvokeArgs(const InvokeArgsGuard &iag)
{
    JS_ASSERT(iag.pushed());
    JS_ASSERT(onTop());
    JS_ASSERT(space().firstUnused() == seg_->calls().end());

    seg_->popCall();
    if (iag.pushedSeg_)
        popSegment();
}
Esempio n. 10
0
bool
BooleanGetPrimitiveValueSlow(JSContext *cx, JSObject &obj, Value *vp)
{
    JS_ASSERT(ObjectClassIs(obj, ESClass_Boolean, cx));
    JS_ASSERT(obj.isProxy());

    /*
     * To respect the proxy abstraction, we can't simply unwrap and call
     * getPrimitiveThis on the wrapped object. All we know is that obj says
     * its [[Class]] is "Boolean". Boolean.prototype.valueOf is specified to
     * return the [[PrimitiveValue]] internal property, so call that instead.
     */
    InvokeArgsGuard ag;
    if (!cx->stack.pushInvokeArgs(cx, 0, &ag))
        return false;
    ag.calleev().setUndefined();
    ag.thisv().setObject(obj);
    if (!GetProxyHandler(&obj)->nativeCall(cx, &obj, &BooleanClass, bool_valueOf, ag))
        return false;
    *vp = ag.rval();
    return true;
}
Esempio n. 11
0
void
ContextStack::popInvokeArgs(const InvokeArgsGuard &iag)
{
    JS_ASSERT(iag.pushed());
    JS_ASSERT(onTop());
    JS_ASSERT(space().firstUnused() == seg_->calls().end());

    Value *oldend = seg_->end();

    seg_->popCall();
    if (iag.pushedSeg_)
        popSegment();

    Debug_SetValueRangeToCrashOnTouch(space().firstUnused(), oldend);
}
Esempio n. 12
0
bool
js::SetElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver,
               uint32_t index, const Value &v, unsigned resolveFlags, bool *succeeded)
{
    NEW_OBJECT_REPRESENTATION_ONLY();

    Rooted<ObjectImpl*> current(cx, obj);
    RootedValue setter(cx);

    MOZ_ASSERT(receiver);

    do {
        MOZ_ASSERT(current);

        if (Downcast(current)->isProxy()) {
            MOZ_NOT_REACHED("NYI: proxy [[SetP]]");
            return false;
        }

        PropDesc ownDesc;
        if (!GetOwnElement(cx, current, index, resolveFlags, &ownDesc))
            return false;

        if (!ownDesc.isUndefined()) {
            if (ownDesc.isDataDescriptor()) {
                if (!ownDesc.writable()) {
                    *succeeded = false;
                    return true;
                }

                if (receiver == current) {
                    PropDesc updateDesc = PropDesc::valueOnly(v);
                    return DefineElement(cx, receiver, index, updateDesc, false, resolveFlags,
                                         succeeded);
                }

                PropDesc newDesc;
                return DefineElement(cx, receiver, index, newDesc, false, resolveFlags, succeeded);
            }

            if (ownDesc.isAccessorDescriptor()) {
                setter = ownDesc.setterValue();
                if (setter.isUndefined()) {
                    *succeeded = false;
                    return true;
                }

                InvokeArgsGuard args;
                if (!cx->stack.pushInvokeArgs(cx, 1, &args))
                    return false;

                /* Push set, receiver, and v as the sole argument. */
                args.setCallee(setter);
                args.setThis(ObjectValue(*current));
                args[0] = v;

                *succeeded = true;
                return Invoke(cx, args);
            }

            MOZ_NOT_REACHED("NYI: setting PropertyOp-based property");
            return false;
        }

        current = current->getProto();
        if (current)
            continue;

        PropDesc newDesc(v, PropDesc::Writable, PropDesc::Enumerable, PropDesc::Configurable);
        return DefineElement(cx, receiver, index, newDesc, false, resolveFlags, succeeded);
    } while (false);

    MOZ_NOT_REACHED("buggy control flow");
    return false;
}
Esempio n. 13
0
bool
js::GetElement(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver, uint32_t index,
               unsigned resolveFlags, Value *vp)
{
    NEW_OBJECT_REPRESENTATION_ONLY();

    Rooted<ObjectImpl*> current(cx, obj);

    RootedValue getter(cx);
    do {
        MOZ_ASSERT(current);

        if (Downcast(current)->isProxy()) {
            MOZ_NOT_REACHED("NYI: proxy [[GetP]]");
            return false;
        }

        PropDesc desc;
        if (!GetOwnElement(cx, current, index, resolveFlags, &desc))
            return false;

        /* No property?  Recur or bottom out. */
        if (desc.isUndefined()) {
            current = current->getProto();
            if (current)
                continue;

            vp->setUndefined();
            return true;
        }

        /* If it's a data property, return the value. */
        if (desc.isDataDescriptor()) {
            *vp = desc.value();
            return true;
        }

        /* If it's an accessor property, call its [[Get]] with the receiver. */
        if (desc.isAccessorDescriptor()) {
            getter = desc.getterValue();
            if (getter.isUndefined()) {
                vp->setUndefined();
                return true;
            }

            InvokeArgsGuard args;
            if (!cx->stack.pushInvokeArgs(cx, 0, &args))
                return false;

            /* Push getter, receiver, and no args. */
            args.setCallee(getter);
            args.setThis(ObjectValue(*current));

            bool ok = Invoke(cx, args);
            *vp = args.rval();
            return ok;
        }

        /* Otherwise it's a PropertyOp-based property.  XXX handle this! */
        MOZ_NOT_REACHED("NYI: handle PropertyOp'd properties here");
        return false;
    } while (false);

    MOZ_NOT_REACHED("buggy control flow");
    return false;
}
Esempio n. 14
0
bool
js::GetProperty(JSContext *cx, Handle<ObjectImpl*> obj, Handle<ObjectImpl*> receiver,
                Handle<PropertyId> pid, unsigned resolveFlags, MutableHandle<Value> vp)
{
    NEW_OBJECT_REPRESENTATION_ONLY();

    MOZ_ASSERT(receiver);

    Rooted<ObjectImpl*> current(cx, obj);

    do {
        MOZ_ASSERT(obj);

        if (Downcast(current)->isProxy()) {
            MOZ_NOT_REACHED("NYI: proxy [[GetP]]");
            return false;
        }

        PropDesc desc;
        PropDesc::AutoRooter rootDesc(cx, &desc);
        if (!GetOwnProperty(cx, current, pid, resolveFlags, &desc))
            return false;

        /* No property?  Recur or bottom out. */
        if (desc.isUndefined()) {
            current = current->getProto();
            if (current)
                continue;

            vp.setUndefined();
            return true;
        }

        /* If it's a data property, return the value. */
        if (desc.isDataDescriptor()) {
            vp.set(desc.value());
            return true;
        }

        /* If it's an accessor property, call its [[Get]] with the receiver. */
        if (desc.isAccessorDescriptor()) {
            Rooted<Value> get(cx, desc.getterValue());
            if (get.isUndefined()) {
                vp.setUndefined();
                return true;
            }

            InvokeArgsGuard args;
            if (!cx->stack.pushInvokeArgs(cx, 0, &args))
                return false;

            args.setCallee(get);
            args.setThis(ObjectValue(*receiver));

            bool ok = Invoke(cx, args);
            vp.set(args.rval());
            return ok;
        }

        /* Otherwise it's a PropertyOp-based property.  XXX handle this! */
        MOZ_NOT_REACHED("NYI: handle PropertyOp'd properties here");
        return false;
    } while (false);

    MOZ_NOT_REACHED("buggy control flow");
    return false;
}
Esempio n. 15
0
static bool
PreprocessValue(JSContext *cx, HandleObject holder, KeyType key, MutableHandleValue vp, StringifyContext *scx)
{
    RootedString keyStr(cx);

    /* Step 2. */
    if (vp.get().isObject()) {
        RootedValue toJSON(cx);
        RootedId id(cx, NameToId(cx->names().toJSON));
        Rooted<JSObject*> obj(cx, &vp.get().toObject());
        if (!GetMethod(cx, obj, id, 0, &toJSON))
            return false;

        if (js_IsCallable(toJSON)) {
            keyStr = KeyStringifier<KeyType>::toString(cx, key);
            if (!keyStr)
                return false;

            InvokeArgsGuard args;
            if (!cx->stack.pushInvokeArgs(cx, 1, &args))
                return false;

            args.setCallee(toJSON);
            args.setThis(vp);
            args[0] = StringValue(keyStr);

            if (!Invoke(cx, args))
                return false;
            vp.set(args.rval());
        }
    }

    /* Step 3. */
    if (scx->replacer && scx->replacer->isCallable()) {
        if (!keyStr) {
            keyStr = KeyStringifier<KeyType>::toString(cx, key);
            if (!keyStr)
                return false;
        }

        InvokeArgsGuard args;
        if (!cx->stack.pushInvokeArgs(cx, 2, &args))
            return false;

        args.setCallee(ObjectValue(*scx->replacer));
        args.setThis(ObjectValue(*holder));
        args[0] = StringValue(keyStr);
        args[1] = vp;

        if (!Invoke(cx, args))
            return false;
        vp.set(args.rval());
    }

    /* Step 4. */
    if (vp.get().isObject()) {
        JSObject &obj = vp.get().toObject();
        if (ObjectClassIs(obj, ESClass_Number, cx)) {
            double d;
            if (!ToNumber(cx, vp, &d))
                return false;
            vp.set(NumberValue(d));
        } else if (ObjectClassIs(obj, ESClass_String, cx)) {
            JSString *str = ToStringSlow(cx, vp);
            if (!str)
                return false;
            vp.set(StringValue(str));
        } else if (ObjectClassIs(obj, ESClass_Boolean, cx)) {
            RootedObject nobj(cx, &obj);
            if (!BooleanGetPrimitiveValue(cx, nobj, vp.address()))
                return false;
            JS_ASSERT(vp.get().isBoolean());
        }
    }

    return true;
}
Esempio n. 16
0
bool
js::SetElement(JSContext *cx, ObjectImpl *obj, ObjectImpl *receiver, uint32_t index,
               const Value &v, bool *succeeded)
{
    NEW_OBJECT_REPRESENTATION_ONLY();

    do {
        MOZ_ASSERT(obj);

        if (static_cast<JSObject *>(obj)->isProxy()) { // XXX
            MOZ_NOT_REACHED("NYI: proxy [[SetP]]");
            return false;
        }

        PropDesc ownDesc;
        if (!GetOwnElement(cx, obj, index, &ownDesc))
            return false;

        if (!ownDesc.isUndefined()) {
            if (ownDesc.isDataDescriptor()) {
                if (!ownDesc.writable()) {
                    *succeeded = false;
                    return true;
                }

                if (receiver == obj) {
                    PropDesc updateDesc = PropDesc::valueOnly(v);
                    return DefineElement(cx, receiver, index, updateDesc, false, succeeded);
                }

                PropDesc newDesc;
                return DefineElement(cx, receiver, index, newDesc, false, succeeded);
            }

            if (ownDesc.isAccessorDescriptor()) {
                Value setter = ownDesc.setterValue();
                if (setter.isUndefined()) {
                    *succeeded = false;
                    return true;
                }

                InvokeArgsGuard args;
                if (!cx->stack.pushInvokeArgs(cx, 1, &args))
                    return false;

                /* Push set, receiver, and v as the sole argument. */
                args.calleev() = setter;
                args.thisv() = ObjectValue(*receiver);
                args[0] = v;

                *succeeded = true;
                return Invoke(cx, args);
            }

            MOZ_NOT_REACHED("NYI: setting PropertyOp-based property");
            return false;
        }

        obj = obj->getProto();
        if (obj)
            continue;

        PropDesc newDesc(v, PropDesc::Writable, PropDesc::Enumerable, PropDesc::Configurable);
        return DefineElement(cx, receiver, index, newDesc, false, succeeded);
    } while (false);

    MOZ_NOT_REACHED("buggy control flow");
    return false;
}
Esempio n. 17
0
bool
js::GetElement(JSContext *cx, ObjectImpl *obj, ObjectImpl *receiver, uint32_t index,
               Value *vp)
{
    NEW_OBJECT_REPRESENTATION_ONLY();

    do {
        MOZ_ASSERT(obj);

        if (static_cast<JSObject *>(obj)->isProxy()) { // XXX
            MOZ_NOT_REACHED("NYI: proxy [[GetP]]");
            return false;
        }

        PropDesc desc;
        if (!GetOwnElement(cx, obj, index, &desc))
            return false;

        /* No property?  Recur or bottom out. */
        if (desc.isUndefined()) {
            obj = obj->getProto();
            if (obj)
                continue;

            vp->setUndefined();
            return true;
        }

        /* If it's a data property, return the value. */
        if (desc.isDataDescriptor()) {
            *vp = desc.value();
            return true;
        }

        /* If it's an accessor property, call its [[Get]] with the receiver. */
        if (desc.isAccessorDescriptor()) {
            Value get = desc.getterValue();
            if (get.isUndefined()) {
                vp->setUndefined();
                return true;
            }

            InvokeArgsGuard args;
            if (!cx->stack.pushInvokeArgs(cx, 0, &args))
                return false;

            /* Push get, receiver, and no args. */
            args.calleev() = get;
            args.thisv() = ObjectValue(*receiver);

            bool ok = Invoke(cx, args);
            *vp = args.rval();
            return ok;
        }

        /* Otherwise it's a PropertyOp-based property.  XXX handle this! */
        MOZ_NOT_REACHED("NYI: handle PropertyOp'd properties here");
        return false;
    } while (false);

    MOZ_NOT_REACHED("buggy control flow");
    return false;
}
Esempio n. 18
0
static bool
PreprocessValue(JSContext *cx, JSObject *holder, KeyType key, Value *vp, StringifyContext *scx)
{
    JSString *keyStr = NULL;

    /* Step 2. */
    if (vp->isObject()) {
        Value toJSON;
        jsid id = NameToId(cx->runtime->atomState.toJSONAtom);
        if (!js_GetMethod(cx, RootedVarObject(cx, &vp->toObject()), id, 0, &toJSON))
            return false;

        if (js_IsCallable(toJSON)) {
            keyStr = KeyStringifier<KeyType>::toString(cx, key);
            if (!keyStr)
                return false;

            InvokeArgsGuard args;
            if (!cx->stack.pushInvokeArgs(cx, 1, &args))
                return false;

            args.calleev() = toJSON;
            args.thisv() = *vp;
            args[0] = StringValue(keyStr);

            if (!Invoke(cx, args))
                return false;
            *vp = args.rval();
        }
    }

    /* Step 3. */
    if (scx->replacer && scx->replacer->isCallable()) {
        if (!keyStr) {
            keyStr = KeyStringifier<KeyType>::toString(cx, key);
            if (!keyStr)
                return false;
        }

        InvokeArgsGuard args;
        if (!cx->stack.pushInvokeArgs(cx, 2, &args))
            return false;

        args.calleev() = ObjectValue(*scx->replacer);
        args.thisv() = ObjectValue(*holder);
        args[0] = StringValue(keyStr);
        args[1] = *vp;

        if (!Invoke(cx, args))
            return false;
        *vp = args.rval();
    }

    /* Step 4. */
    if (vp->isObject()) {
        JSObject &obj = vp->toObject();
        if (ObjectClassIs(obj, ESClass_Number, cx)) {
            double d;
            if (!ToNumber(cx, *vp, &d))
                return false;
            vp->setNumber(d);
        } else if (ObjectClassIs(obj, ESClass_String, cx)) {
            JSString *str = ToStringSlow(cx, *vp);
            if (!str)
                return false;
            vp->setString(str);
        } else if (ObjectClassIs(obj, ESClass_Boolean, cx)) {
            if (!BooleanGetPrimitiveValue(cx, obj, vp))
                return false;
            JS_ASSERT(vp->isBoolean());
        }
    }

    return true;
}
Esempio n. 19
0
/*
 * ES5 15.12.3 Str, steps 2-4, extracted to enable preprocessing of property
 * values when stringifying objects in JO.
 */
static bool
PreprocessValue(JSContext *cx, JSObject *holder, jsid key, Value *vp, StringifyContext *scx)
{
    JSString *keyStr = NULL;

    /* Step 2. */
    if (vp->isObject()) {
        Value toJSON;
        jsid id = ATOM_TO_JSID(cx->runtime->atomState.toJSONAtom);
        if (!js_GetMethod(cx, &vp->toObject(), id, JSGET_NO_METHOD_BARRIER, &toJSON))
            return false;

        if (js_IsCallable(toJSON)) {
            keyStr = IdToString(cx, key);
            if (!keyStr)
                return false;

            LeaveTrace(cx);
            InvokeArgsGuard args;
            if (!cx->stack.pushInvokeArgs(cx, 1, &args))
                return false;

            args.calleev() = toJSON;
            args.thisv() = *vp;
            args[0] = StringValue(keyStr);

            if (!Invoke(cx, args))
                return false;
            *vp = args.rval();
        }
    }

    /* Step 3. */
    if (scx->replacer && scx->replacer->isCallable()) {
        if (!keyStr) {
            keyStr = IdToString(cx, key);
            if (!keyStr)
                return false;
        }

        LeaveTrace(cx);
        InvokeArgsGuard args;
        if (!cx->stack.pushInvokeArgs(cx, 2, &args))
            return false;

        args.calleev() = ObjectValue(*scx->replacer);
        args.thisv() = ObjectValue(*holder);
        args[0] = StringValue(keyStr);
        args[1] = *vp;

        if (!Invoke(cx, args))
            return false;
        *vp = args.rval();
    }

    /* Step 4. */
    if (vp->isObject()) {
        JSObject *obj = &vp->toObject();
        Class *clasp = obj->getClass();
        if (clasp == &js_NumberClass) {
            double d;
            if (!ValueToNumber(cx, *vp, &d))
                return false;
            vp->setNumber(d);
        } else if (clasp == &js_StringClass) {
            JSString *str = js_ValueToString(cx, *vp);
            if (!str)
                return false;
            vp->setString(str);
        } else if (clasp == &js_BooleanClass) {
            *vp = obj->getPrimitiveThis();
            JS_ASSERT(vp->isBoolean());
        }
    }

    return true;
}