author | Jan de Mooij <jdemooij@mozilla.com> |
Fri, 06 Sep 2013 13:52:16 +0200 | |
changeset 146402 | d660739f74981545b16aeee25fae6a4bcb10bc8a |
parent 146401 | a2013b29212c12e298c6ca56895056a53b59be39 |
child 146403 | 097fd1744dfe0bc0670caee7dcd5846b38a268a9 |
push id | 25260 |
push user | ryanvm@gmail.com |
push date | Wed, 11 Sep 2013 00:29:30 +0000 |
treeherder | mozilla-central@f73bed2856a8 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | bhackett |
bugs | 913424 |
milestone | 26.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
|
--- a/js/src/jit/BaselineIC.cpp +++ b/js/src/jit/BaselineIC.cpp @@ -1596,20 +1596,21 @@ static const VMFunction DoCallNativeGett // This_Fallback // static bool DoThisFallback(JSContext *cx, ICThis_Fallback *stub, HandleValue thisv, MutableHandleValue ret) { FallbackICSpew(cx, stub, "This"); - ret.set(thisv); - bool modified; - if (!BoxNonStrictThis(cx, ret, &modified)) + JSObject *thisObj = BoxNonStrictThis(cx, thisv); + if (!thisObj) return false; + + ret.setObject(*thisObj); return true; } typedef bool (*DoThisFallbackFn)(JSContext *, ICThis_Fallback *, HandleValue, MutableHandleValue); static const VMFunction DoThisFallbackInfo = FunctionInfo<DoThisFallbackFn>(DoThisFallback); bool ICThis_Fallback::Compiler::generateStubCode(MacroAssembler &masm)
--- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -3523,16 +3523,37 @@ CodeGenerator::visitReturnFromCtor(LRetu Register payload = masm.extractObject(value, output); if (payload != output) masm.movePtr(payload, output); masm.bind(&end); return true; } +typedef JSObject *(*BoxNonStrictThisFn)(JSContext *, HandleValue); +static const VMFunction BoxNonStrictThisInfo = FunctionInfo<BoxNonStrictThisFn>(BoxNonStrictThis); + +bool +CodeGenerator::visitComputeThis(LComputeThis *lir) +{ + ValueOperand value = ToValue(lir, LComputeThis::ValueIndex); + Register output = ToRegister(lir->output()); + + OutOfLineCode *ool = oolCallVM(BoxNonStrictThisInfo, lir, (ArgList(), value), + StoreRegisterTo(output)); + if (!ool) + return false; + + masm.branchTestObject(Assembler::NotEqual, value, ool->entry()); + masm.unboxObject(value, output); + + masm.bind(ool->rejoin()); + return true; +} + bool CodeGenerator::visitArrayLength(LArrayLength *lir) { Address length(ToRegister(lir->elements()), ObjectElements::offsetOfLength()); masm.load32(length, ToRegister(lir->output())); return true; }
--- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -138,16 +138,17 @@ class CodeGenerator : public CodeGenerat bool visitInitPropGetterSetter(LInitPropGetterSetter *lir); bool visitCreateThis(LCreateThis *lir); bool visitCreateThisWithProto(LCreateThisWithProto *lir); bool visitCreateThisWithTemplate(LCreateThisWithTemplate *lir); bool visitCreateArgumentsObject(LCreateArgumentsObject *lir); bool visitGetArgumentsObjectArg(LGetArgumentsObjectArg *lir); bool visitSetArgumentsObjectArg(LSetArgumentsObjectArg *lir); bool visitReturnFromCtor(LReturnFromCtor *lir); + bool visitComputeThis(LComputeThis *lir); bool visitArrayLength(LArrayLength *lir); bool visitTypedArrayLength(LTypedArrayLength *lir); bool visitTypedArrayElements(LTypedArrayElements *lir); bool visitStringLength(LStringLength *lir); bool visitInitializedLength(LInitializedLength *lir); bool visitSetInitializedLength(LSetInitializedLength *lir); bool visitNotO(LNotO *ins); bool visitNotV(LNotV *ins);
--- a/js/src/jit/IonBuilder.cpp +++ b/js/src/jit/IonBuilder.cpp @@ -8879,17 +8879,18 @@ IonBuilder::jsop_deffun(uint32_t index) } bool IonBuilder::jsop_this() { if (!info().fun()) return abort("JSOP_THIS outside of a JSFunction."); - if (script()->strict) { + if (script()->strict || info().fun()->isSelfHostedBuiltin()) { + // No need to wrap primitive |this| in strict mode or self-hosted code. current->pushSlot(info().thisSlot()); return true; } types::StackTypeSet *types = types::TypeScript::ThisTypes(script()); if (types && (types->getKnownTypeTag() == JSVAL_TYPE_OBJECT || (types->empty() && baselineFrame_ && baselineFrame_->thisValue().isObject()))) { @@ -8904,17 +8905,32 @@ IonBuilder::jsop_this() // |this| type as its type object is being created right now. Instead of // bailing out just push the |this| slot, as this code won't actually // execute and it does not matter whether |this| is primitive. if (info().executionMode() == DefinitePropertiesAnalysis) { current->pushSlot(info().thisSlot()); return true; } - return abort("JSOP_THIS hard case not yet handled"); + // Hard case: |this| may be a primitive we have to wrap. + MDefinition *def = current->getSlot(info().thisSlot()); + + if (def->type() == MIRType_Object) { + // If we already computed a |this| object, we can reuse it. + current->push(def); + return true; + } + + MComputeThis *thisObj = MComputeThis::New(def); + current->add(thisObj); + current->push(thisObj); + + current->setSlot(info().thisSlot(), thisObj); + + return resumeAfter(thisObj); } bool IonBuilder::jsop_typeof() { MDefinition *input = current->pop(); MTypeOf *ins = MTypeOf::New(input, input->type());
--- a/js/src/jit/LIR-Common.h +++ b/js/src/jit/LIR-Common.h @@ -899,16 +899,32 @@ class LReturnFromCtor : public LInstruct const LAllocation *getObject() { return getOperand(LReturnFromCtor::ObjectIndex); } static const size_t ValueIndex = 0; static const size_t ObjectIndex = BOX_PIECES; }; +class LComputeThis : public LInstructionHelper<1, BOX_PIECES, 0> +{ + public: + LIR_HEADER(ComputeThis) + + static const size_t ValueIndex = 0; + + const LDefinition *output() { + return getDef(0); + } + + MComputeThis *mir() const { + return mir_->toComputeThis(); + } +}; + // Writes a typed argument for a function call to the frame's argument vector. class LStackArgT : public LInstructionHelper<0, 1, 0> { uint32_t argslot_; // Index into frame-scope argument vector. public: LIR_HEADER(StackArgT)
--- a/js/src/jit/LOpcodes.h +++ b/js/src/jit/LOpcodes.h @@ -53,16 +53,17 @@ _(StackArgV) \ _(CreateThis) \ _(CreateThisWithProto) \ _(CreateThisWithTemplate) \ _(CreateArgumentsObject) \ _(GetArgumentsObjectArg) \ _(SetArgumentsObjectArg) \ _(ReturnFromCtor) \ + _(ComputeThis) \ _(BitNotI) \ _(BitNotV) \ _(BitOpI) \ _(BitOpV) \ _(ShiftI) \ _(UrshD) \ _(Return) \ _(Throw) \
--- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -373,16 +373,30 @@ LIRGenerator::visitReturnFromCtor(MRetur LReturnFromCtor *lir = new LReturnFromCtor(useRegister(ins->getObject())); if (!useBox(lir, LReturnFromCtor::ValueIndex, ins->getValue())) return false; return define(lir, ins); } bool +LIRGenerator::visitComputeThis(MComputeThis *ins) +{ + JS_ASSERT(ins->type() == MIRType_Object); + JS_ASSERT(ins->input()->type() == MIRType_Value); + + LComputeThis *lir = new LComputeThis(); + if (!useBoxAtStart(lir, LComputeThis::ValueIndex, ins->input())) + return false; + + return define(lir, ins) && assignSafepoint(lir, ins); +} + + +bool LIRGenerator::visitCall(MCall *call) { JS_ASSERT(CallTempReg0 != CallTempReg1); JS_ASSERT(CallTempReg0 != ArgumentsRectifierReg); JS_ASSERT(CallTempReg1 != ArgumentsRectifierReg); JS_ASSERT(call->getFunction()->type() == MIRType_Object); // Height of the current argument vector.
--- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -105,16 +105,17 @@ class LIRGenerator : public LIRGenerator bool visitPassArg(MPassArg *arg); bool visitCreateThisWithTemplate(MCreateThisWithTemplate *ins); bool visitCreateThisWithProto(MCreateThisWithProto *ins); bool visitCreateThis(MCreateThis *ins); bool visitCreateArgumentsObject(MCreateArgumentsObject *ins); bool visitGetArgumentsObjectArg(MGetArgumentsObjectArg *ins); bool visitSetArgumentsObjectArg(MSetArgumentsObjectArg *ins); bool visitReturnFromCtor(MReturnFromCtor *ins); + bool visitComputeThis(MComputeThis *ins); bool visitCall(MCall *call); bool visitApplyArgs(MApplyArgs *apply); bool visitBail(MBail *bail); bool visitGetDynamicName(MGetDynamicName *ins); bool visitFilterArguments(MFilterArguments *ins); bool visitCallDirectEval(MCallDirectEval *ins); bool visitTest(MTest *test); bool visitFunctionDispatch(MFunctionDispatch *ins);
--- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -3924,16 +3924,50 @@ class MFromCharCode return new MFromCharCode(code); } virtual AliasSet getAliasSet() const { return AliasSet::None(); } }; +// Returns an object to use as |this| value. See also ComputeThis and +// BoxNonStrictThis in Interpreter.h. +class MComputeThis + : public MUnaryInstruction, + public BoxPolicy<0> +{ + MComputeThis(MDefinition *def) + : MUnaryInstruction(def) + { + setResultType(MIRType_Object); + setResultTypeSet(def->resultTypeSet()); + } + + public: + INSTRUCTION_HEADER(ComputeThis) + + static MComputeThis *New(MDefinition *def) { + return new MComputeThis(def); + } + + MDefinition *input() const { + return getOperand(0); + } + TypePolicy *typePolicy() { + return this; + } + bool possiblyCalls() const { + return true; + } + + // Note: don't override getAliasSet: the thisObject hook can be + // effectful. +}; + class MPhi MOZ_FINAL : public MDefinition, public InlineForwardListNode<MPhi> { js::Vector<MUse, 2, IonAllocPolicy> inputs_; uint32_t slot_; bool hasBackedgeType_; bool triedToSpecialize_; bool isIterator_;
--- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -29,16 +29,17 @@ namespace jit { _(DefVar) \ _(DefFun) \ _(CreateThis) \ _(CreateThisWithProto) \ _(CreateThisWithTemplate) \ _(CreateArgumentsObject) \ _(GetArgumentsObjectArg) \ _(SetArgumentsObjectArg) \ + _(ComputeThis) \ _(PrepareCall) \ _(PassArg) \ _(Call) \ _(ApplyArgs) \ _(Bail) \ _(GetDynamicName) \ _(FilterArguments) \ _(CallDirectEval) \
--- a/js/src/jit/ParallelSafetyAnalysis.cpp +++ b/js/src/jit/ParallelSafetyAnalysis.cpp @@ -121,16 +121,17 @@ class ParallelSafetyVisitor : public MIn UNSAFE_OP(DefVar) UNSAFE_OP(DefFun) UNSAFE_OP(CreateThis) UNSAFE_OP(CreateThisWithTemplate) UNSAFE_OP(CreateThisWithProto) UNSAFE_OP(CreateArgumentsObject) UNSAFE_OP(GetArgumentsObjectArg) UNSAFE_OP(SetArgumentsObjectArg) + UNSAFE_OP(ComputeThis) SAFE_OP(PrepareCall) SAFE_OP(PassArg) CUSTOM_OP(Call) UNSAFE_OP(ApplyArgs) UNSAFE_OP(Bail) UNSAFE_OP(GetDynamicName) UNSAFE_OP(FilterArguments) UNSAFE_OP(CallDirectEval)
--- a/js/src/jit/arm/MacroAssembler-arm.cpp +++ b/js/src/jit/arm/MacroAssembler-arm.cpp @@ -2584,26 +2584,34 @@ MacroAssemblerARMCompat::unboxDouble(con void MacroAssemblerARMCompat::unboxDouble(const Address &src, const FloatRegister &dest) { ma_vldr(Operand(src), dest); } void -MacroAssemblerARMCompat::unboxString(const ValueOperand &operand, const Register &dest) { +MacroAssemblerARMCompat::unboxString(const ValueOperand &operand, const Register &dest) +{ ma_mov(operand.payloadReg(), dest); } void -MacroAssemblerARMCompat::unboxString(const Address &src, const Register &dest) { +MacroAssemblerARMCompat::unboxString(const Address &src, const Register &dest) +{ ma_ldr(payloadOf(src), dest); } void +MacroAssemblerARMCompat::unboxObject(const ValueOperand &src, const Register &dest) +{ + ma_mov(src.payloadReg(), dest); +} + +void MacroAssemblerARMCompat::unboxValue(const ValueOperand &src, AnyRegister dest) { if (dest.isFloat()) { Label notInt32, end; branchTestInt32(Assembler::NotEqual, src, ¬Int32); convertInt32ToDouble(src.payloadReg(), dest.fpu()); ma_b(&end); bind(¬Int32);
--- a/js/src/jit/arm/MacroAssembler-arm.h +++ b/js/src/jit/arm/MacroAssembler-arm.h @@ -715,16 +715,17 @@ class MacroAssemblerARMCompat : public M void unboxInt32(const ValueOperand &operand, const Register &dest); void unboxInt32(const Address &src, const Register &dest); void unboxBoolean(const ValueOperand &operand, const Register &dest); void unboxBoolean(const Address &src, const Register &dest); void unboxDouble(const ValueOperand &operand, const FloatRegister &dest); void unboxDouble(const Address &src, const FloatRegister &dest); void unboxString(const ValueOperand &operand, const Register &dest); void unboxString(const Address &src, const Register &dest); + void unboxObject(const ValueOperand &src, const Register &dest); void unboxValue(const ValueOperand &src, AnyRegister dest); void unboxPrivate(const ValueOperand &src, Register dest); void notBoolean(const ValueOperand &val) { ma_eor(Imm32(1), val.payloadReg()); } // boxing code
--- a/js/src/jit/x86/MacroAssembler-x86.h +++ b/js/src/jit/x86/MacroAssembler-x86.h @@ -742,16 +742,20 @@ class MacroAssemblerX86 : public MacroAs movsd(Operand(src), dest); } void unboxBoolean(const ValueOperand &src, const Register &dest) { movl(src.payloadReg(), dest); } void unboxBoolean(const Address &src, const Register &dest) { movl(payloadOf(src), dest); } + void unboxObject(const ValueOperand &src, const Register &dest) { + if (src.payloadReg() != dest) + movl(src.payloadReg(), dest); + } void unboxDouble(const ValueOperand &src, const FloatRegister &dest) { JS_ASSERT(dest != ScratchFloatReg); if (Assembler::HasSSE41()) { movd(src.payloadReg(), dest); pinsrd(src.typeReg(), dest); } else { movd(src.payloadReg(), dest); movd(src.typeReg(), ScratchFloatReg);
--- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -5138,41 +5138,30 @@ js_GetClassPrototype(ExclusiveContext *c } } protop.set(v.get().isObject() ? &v.get().toObject() : NULL); return true; } JSObject * -PrimitiveToObject(JSContext *cx, const Value &v) +js::PrimitiveToObject(JSContext *cx, const Value &v) { if (v.isString()) { Rooted<JSString*> str(cx, v.toString()); return StringObject::create(cx, str); } if (v.isNumber()) return NumberObject::create(cx, v.toNumber()); JS_ASSERT(v.isBoolean()); return BooleanObject::create(cx, v.toBoolean()); } bool -js_PrimitiveToObject(JSContext *cx, Value *vp) -{ - JSObject *obj = PrimitiveToObject(cx, *vp); - if (!obj) - return false; - - vp->setObject(*obj); - return true; -} - -bool js_ValueToObjectOrNull(JSContext *cx, const Value &v, MutableHandleObject objp) { JSObject *obj; if (v.isObjectOrNull()) { obj = v.toObjectOrNull(); } else if (v.isUndefined()) { obj = NULL;
--- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -1369,24 +1369,21 @@ CheckAccess(JSContext *cx, JSObject *obj MutableHandleValue v, unsigned *attrsp); extern bool IsDelegate(JSContext *cx, HandleObject obj, const Value &v, bool *result); bool GetObjectElementOperationPure(ThreadSafeContext *cx, JSObject *obj, const Value &prop, Value *vp); -} /* namespace js */ +/* Wrap boolean, number or string as Boolean, Number or String object. */ +extern JSObject * +PrimitiveToObject(JSContext *cx, const Value &v); -/* - * Wrap boolean, number or string as Boolean, Number or String object. - * *vp must not be an object, null or undefined. - */ -extern bool -js_PrimitiveToObject(JSContext *cx, js::Value *vp); +} /* namespace js */ extern bool js_ValueToObjectOrNull(JSContext *cx, const js::Value &v, JS::MutableHandleObject objp); /* Throws if v could not be converted to an object. */ extern JSObject * js_ValueToNonNullObject(JSContext *cx, const js::Value &v);
--- a/js/src/vm/Interpreter-inl.h +++ b/js/src/vm/Interpreter-inl.h @@ -43,21 +43,22 @@ ComputeThis(JSContext *cx, AbstractFrame * eval's frame will get the wrapper, but the function's frame will not. To prevent * this, we always wrap a function's |this| before pushing an eval frame, and should * thus never see an unwrapped primitive in a non-strict eval function frame. Null * and undefined |this| values will unwrap to the same object in the function and * eval frames, so are not required to be wrapped. */ JS_ASSERT_IF(frame.isEvalFrame(), thisv.isUndefined() || thisv.isNull()); } - bool modified; - if (!BoxNonStrictThis(cx, &thisv, &modified)) + + JSObject *thisObj = BoxNonStrictThis(cx, thisv); + if (!thisObj) return false; - frame.thisValue() = thisv; + frame.thisValue().setObject(*thisObj); return true; } /* * Every possible consumer of MagicValue(JS_OPTIMIZED_ARGUMENTS) (as determined * by ScriptAnalysis::needsArgsObj) must check for these magic values and, when * one is received, act as if the value were the function's ArgumentsObject. * Additionally, it is possible that, after 'arguments' was copied into a
--- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -101,43 +101,34 @@ LooseEqualityOp(JSContext *cx, FrameRegs if (!LooselyEqual(cx, lval, rval, &cond)) return false; cond = (cond == Eq); regs.sp--; regs.sp[-1].setBoolean(cond); return true; } -bool -js::BoxNonStrictThis(JSContext *cx, MutableHandleValue thisv, bool *modified) +JSObject * +js::BoxNonStrictThis(JSContext *cx, HandleValue thisv) { /* * Check for SynthesizeFrame poisoning and fast constructors which * didn't check their callee properly. */ JS_ASSERT(!thisv.isMagic()); - *modified = false; if (thisv.isNullOrUndefined()) { Rooted<GlobalObject*> global(cx, cx->global()); - JSObject *thisp = JSObject::thisObject(cx, global); - if (!thisp) - return false; - thisv.set(ObjectValue(*thisp)); - *modified = true; - return true; + return JSObject::thisObject(cx, global); } - if (!thisv.isObject()) { - if (!js_PrimitiveToObject(cx, thisv.address())) - return false; - *modified = true; - } - - return true; + if (thisv.isObject()) + return &thisv.toObject(); + + return PrimitiveToObject(cx, thisv); } /* * ECMA requires "the global object", but in embeddings such as the browser, * which have multiple top-level objects (windows, frames, etc. in the DOM), * we prefer fun's parent. An example that causes this code to run: * * // in window w1 @@ -152,30 +143,28 @@ js::BoxNonStrictThis(JSContext *cx, Muta */ bool js::BoxNonStrictThis(JSContext *cx, const CallReceiver &call) { /* * Check for SynthesizeFrame poisoning and fast constructors which * didn't check their callee properly. */ - RootedValue thisv(cx, call.thisv()); - JS_ASSERT(!thisv.isMagic()); + JS_ASSERT(!call.thisv().isMagic()); #ifdef DEBUG JSFunction *fun = call.callee().is<JSFunction>() ? &call.callee().as<JSFunction>() : NULL; JS_ASSERT_IF(fun && fun->isInterpreted(), !fun->strict()); #endif - bool modified; - if (!BoxNonStrictThis(cx, &thisv, &modified)) + JSObject *thisObj = BoxNonStrictThis(cx, call.thisv()); + if (!thisObj) return false; - if (modified) - call.setThis(thisv); - + + call.setThis(ObjectValue(*thisObj)); return true; } #if JS_HAS_NO_SUCH_METHOD static const uint32_t JSSLOT_FOUND_FUNCTION = 0; static const uint32_t JSSLOT_SAVED_ID = 1;
--- a/js/src/vm/Interpreter.h +++ b/js/src/vm/Interpreter.h @@ -79,18 +79,18 @@ DebugExceptionUnwind(JSContext *cx, Abst * For a given |call|, convert null/undefined |this| into the global object for * the callee and replace other primitives with boxed versions. This assumes * that call.callee() is not strict mode code. This is the special/slow case of * ComputeThis. */ extern bool BoxNonStrictThis(JSContext *cx, const CallReceiver &call); -extern bool -BoxNonStrictThis(JSContext *cx, MutableHandleValue thisv, bool *modified); +extern JSObject * +BoxNonStrictThis(JSContext *cx, HandleValue thisv); /* * Ensure that fp->thisValue() is the correct value of |this| for the scripted * call represented by |fp|. ComputeThis is necessary because fp->thisValue() * may be set to 'undefined' when 'this' should really be the global object (as * an optimization to avoid global-this computation). */ inline bool
--- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -809,16 +809,27 @@ JSStructuredCloneWriter::traverseObject( if (!objs.append(ObjectValue(*obj)) || !counts.append(count)) return false; checkStack(); /* Write the header for obj. */ return out.writePair(obj->is<ArrayObject>() ? SCTAG_ARRAY_OBJECT : SCTAG_OBJECT_OBJECT, 0); } +static bool +PrimitiveToObject(JSContext *cx, Value *vp) +{ + JSObject *obj = PrimitiveToObject(cx, *vp); + if (!obj) + return false; + + vp->setObject(*obj); + return true; +} + bool JSStructuredCloneWriter::startWrite(const Value &v) { assertSameCompartment(context(), v); if (v.isString()) { return writeString(SCTAG_STRING, v.toString()); } else if (v.isNumber()) { @@ -1174,37 +1185,37 @@ JSStructuredCloneReader::startRead(Value case SCTAG_UNDEFINED: vp->setUndefined(); break; case SCTAG_BOOLEAN: case SCTAG_BOOLEAN_OBJECT: vp->setBoolean(!!data); - if (tag == SCTAG_BOOLEAN_OBJECT && !js_PrimitiveToObject(context(), vp)) + if (tag == SCTAG_BOOLEAN_OBJECT && !PrimitiveToObject(context(), vp)) return false; break; case SCTAG_STRING: case SCTAG_STRING_OBJECT: { JSString *str = readString(data); if (!str) return false; vp->setString(str); - if (tag == SCTAG_STRING_OBJECT && !js_PrimitiveToObject(context(), vp)) + if (tag == SCTAG_STRING_OBJECT && !PrimitiveToObject(context(), vp)) return false; break; } case SCTAG_NUMBER_OBJECT: { double d; if (!in.readDouble(&d) || !checkDouble(d)) return false; vp->setDouble(d); - if (!js_PrimitiveToObject(context(), vp)) + if (!PrimitiveToObject(context(), vp)) return false; break; } case SCTAG_DATE_OBJECT: { double d; if (!in.readDouble(&d) || !checkDouble(d)) return false;