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()); }
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()); }
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()); }
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; }
/*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; }
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; }
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()); }
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; }
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(); }
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; }
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); }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
/* * 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; }