michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/VMFunctions.h" michael@0: michael@0: #include "builtin/TypedObject.h" michael@0: #include "frontend/BytecodeCompiler.h" michael@0: #include "jit/arm/Simulator-arm.h" michael@0: #include "jit/BaselineIC.h" michael@0: #include "jit/IonFrames.h" michael@0: #include "jit/JitCompartment.h" michael@0: #include "vm/ArrayObject.h" michael@0: #include "vm/Debugger.h" michael@0: #include "vm/Interpreter.h" michael@0: michael@0: #include "jsinferinlines.h" michael@0: michael@0: #include "jit/BaselineFrame-inl.h" michael@0: #include "jit/IonFrames-inl.h" michael@0: #include "vm/Interpreter-inl.h" michael@0: #include "vm/StringObject-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace js::jit; michael@0: michael@0: namespace js { michael@0: namespace jit { michael@0: michael@0: // Don't explicitly initialize, it's not guaranteed that this initializer will michael@0: // run before the constructors for static VMFunctions. michael@0: /* static */ VMFunction *VMFunction::functions; michael@0: michael@0: AutoDetectInvalidation::AutoDetectInvalidation(JSContext *cx, Value *rval, IonScript *ionScript) michael@0: : cx_(cx), michael@0: ionScript_(ionScript ? ionScript : GetTopIonJSScript(cx)->ionScript()), michael@0: rval_(rval), michael@0: disabled_(false) michael@0: { } michael@0: michael@0: void michael@0: VMFunction::addToFunctions() michael@0: { michael@0: static bool initialized = false; michael@0: if (!initialized) { michael@0: initialized = true; michael@0: functions = nullptr; michael@0: } michael@0: this->next = functions; michael@0: functions = this; michael@0: } michael@0: michael@0: bool michael@0: InvokeFunction(JSContext *cx, HandleObject obj0, uint32_t argc, Value *argv, Value *rval) michael@0: { michael@0: RootedObject obj(cx, obj0); michael@0: if (obj->is()) { michael@0: RootedFunction fun(cx, &obj->as()); michael@0: if (fun->isInterpreted()) { michael@0: if (fun->isInterpretedLazy() && !fun->getOrCreateScript(cx)) michael@0: return false; michael@0: michael@0: // Clone function at call site if needed. michael@0: if (fun->nonLazyScript()->shouldCloneAtCallsite()) { michael@0: jsbytecode *pc; michael@0: RootedScript script(cx, cx->currentScript(&pc)); michael@0: fun = CloneFunctionAtCallsite(cx, fun, script, pc); michael@0: if (!fun) michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: // Data in the argument vector is arranged for a JIT -> JIT call. michael@0: Value thisv = argv[0]; michael@0: Value *argvWithoutThis = argv + 1; michael@0: michael@0: // For constructing functions, |this| is constructed at caller side and we can just call Invoke. michael@0: // When creating this failed / is impossible at caller site, i.e. MagicValue(JS_IS_CONSTRUCTING), michael@0: // we use InvokeConstructor that creates it at the callee side. michael@0: RootedValue rv(cx); michael@0: if (thisv.isMagic(JS_IS_CONSTRUCTING)) { michael@0: if (!InvokeConstructor(cx, ObjectValue(*obj), argc, argvWithoutThis, rv.address())) michael@0: return false; michael@0: } else { michael@0: if (!Invoke(cx, thisv, ObjectValue(*obj), argc, argvWithoutThis, &rv)) michael@0: return false; michael@0: } michael@0: michael@0: if (obj->is()) { michael@0: jsbytecode *pc; michael@0: RootedScript script(cx, cx->currentScript(&pc)); michael@0: types::TypeScript::Monitor(cx, script, pc, rv.get()); michael@0: } michael@0: michael@0: *rval = rv; michael@0: return true; michael@0: } michael@0: michael@0: JSObject * michael@0: NewGCObject(JSContext *cx, gc::AllocKind allocKind, gc::InitialHeap initialHeap) michael@0: { michael@0: return js::NewGCObject(cx, allocKind, 0, initialHeap); michael@0: } michael@0: michael@0: bool michael@0: CheckOverRecursed(JSContext *cx) michael@0: { michael@0: // IonMonkey's stackLimit is equal to nativeStackLimit by default. When we michael@0: // request an interrupt, we set the jitStackLimit to nullptr, which causes michael@0: // the stack limit check to fail. michael@0: // michael@0: // There are two states we're concerned about here: michael@0: // (1) The interrupt bit is set, and we need to fire the interrupt callback. michael@0: // (2) The stack limit has been exceeded, and we need to throw an error. michael@0: // michael@0: // Note that we can reach here if jitStackLimit is MAXADDR, but interrupt michael@0: // has not yet been set to 1. That's okay; it will be set to 1 very shortly, michael@0: // and in the interim we might just fire a few useless calls to michael@0: // CheckOverRecursed. michael@0: #ifdef JS_ARM_SIMULATOR michael@0: JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, 0, return false); michael@0: #else michael@0: JS_CHECK_RECURSION(cx, return false); michael@0: #endif michael@0: michael@0: if (cx->runtime()->interrupt) michael@0: return InterruptCheck(cx); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // This function can get called in two contexts. In the usual context, it's michael@0: // called with ealyCheck=false, after the scope chain has been initialized on michael@0: // a baseline frame. In this case, it's ok to throw an exception, so a failed michael@0: // stack check returns false, and a successful stack check promps a check for michael@0: // an interrupt from the runtime, which may also cause a false return. michael@0: // michael@0: // In the second case, it's called with earlyCheck=true, prior to frame michael@0: // initialization. An exception cannot be thrown in this instance, so instead michael@0: // an error flag is set on the frame and true returned. michael@0: bool michael@0: CheckOverRecursedWithExtra(JSContext *cx, BaselineFrame *frame, michael@0: uint32_t extra, uint32_t earlyCheck) michael@0: { michael@0: JS_ASSERT_IF(earlyCheck, !frame->overRecursed()); michael@0: michael@0: // See |CheckOverRecursed| above. This is a variant of that function which michael@0: // accepts an argument holding the extra stack space needed for the Baseline michael@0: // frame that's about to be pushed. michael@0: uint8_t spDummy; michael@0: uint8_t *checkSp = (&spDummy) - extra; michael@0: if (earlyCheck) { michael@0: #ifdef JS_ARM_SIMULATOR michael@0: (void)checkSp; michael@0: JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, frame->setOverRecursed()); michael@0: #else michael@0: JS_CHECK_RECURSION_WITH_SP(cx, checkSp, frame->setOverRecursed()); michael@0: #endif michael@0: return true; michael@0: } michael@0: michael@0: // The OVERRECURSED flag may have already been set on the frame by an michael@0: // early over-recursed check. If so, throw immediately. michael@0: if (frame->overRecursed()) michael@0: return false; michael@0: michael@0: #ifdef JS_ARM_SIMULATOR michael@0: JS_CHECK_SIMULATOR_RECURSION_WITH_EXTRA(cx, extra, return false); michael@0: #else michael@0: JS_CHECK_RECURSION_WITH_SP(cx, checkSp, return false); michael@0: #endif michael@0: michael@0: if (cx->runtime()->interrupt) michael@0: return InterruptCheck(cx); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DefVarOrConst(JSContext *cx, HandlePropertyName dn, unsigned attrs, HandleObject scopeChain) michael@0: { michael@0: // Given the ScopeChain, extract the VarObj. michael@0: RootedObject obj(cx, scopeChain); michael@0: while (!obj->isVarObj()) michael@0: obj = obj->enclosingScope(); michael@0: michael@0: return DefVarOrConstOperation(cx, obj, dn, attrs); michael@0: } michael@0: michael@0: bool michael@0: SetConst(JSContext *cx, HandlePropertyName name, HandleObject scopeChain, HandleValue rval) michael@0: { michael@0: // Given the ScopeChain, extract the VarObj. michael@0: RootedObject obj(cx, scopeChain); michael@0: while (!obj->isVarObj()) michael@0: obj = obj->enclosingScope(); michael@0: michael@0: return SetConstOperation(cx, obj, name, rval); michael@0: } michael@0: michael@0: bool michael@0: MutatePrototype(JSContext *cx, HandleObject obj, HandleValue value) michael@0: { michael@0: MOZ_ASSERT(obj->is(), "must only be used with object literals"); michael@0: if (!value.isObjectOrNull()) michael@0: return true; michael@0: michael@0: RootedObject newProto(cx, value.toObjectOrNull()); michael@0: michael@0: bool succeeded; michael@0: if (!JSObject::setProto(cx, obj, newProto, &succeeded)) michael@0: return false; michael@0: MOZ_ASSERT(succeeded); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: InitProp(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value) michael@0: { michael@0: // Copy the incoming value. This may be overwritten; the return value is discarded. michael@0: RootedValue rval(cx, value); michael@0: RootedId id(cx, NameToId(name)); michael@0: michael@0: MOZ_ASSERT(name != cx->names().proto, michael@0: "__proto__ should have been handled by JSOP_MUTATEPROTO"); michael@0: return DefineNativeProperty(cx, obj, id, rval, nullptr, nullptr, JSPROP_ENUMERATE); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: LooselyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) michael@0: { michael@0: if (!js::LooselyEqual(cx, lhs, rhs, res)) michael@0: return false; michael@0: if (!Equal) michael@0: *res = !*res; michael@0: return true; michael@0: } michael@0: michael@0: template bool LooselyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res); michael@0: template bool LooselyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res); michael@0: michael@0: template michael@0: bool michael@0: StrictlyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) michael@0: { michael@0: if (!js::StrictlyEqual(cx, lhs, rhs, res)) michael@0: return false; michael@0: if (!Equal) michael@0: *res = !*res; michael@0: return true; michael@0: } michael@0: michael@0: template bool StrictlyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res); michael@0: template bool StrictlyEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res); michael@0: michael@0: bool michael@0: LessThan(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) michael@0: { michael@0: return LessThanOperation(cx, lhs, rhs, res); michael@0: } michael@0: michael@0: bool michael@0: LessThanOrEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) michael@0: { michael@0: return LessThanOrEqualOperation(cx, lhs, rhs, res); michael@0: } michael@0: michael@0: bool michael@0: GreaterThan(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) michael@0: { michael@0: return GreaterThanOperation(cx, lhs, rhs, res); michael@0: } michael@0: michael@0: bool michael@0: GreaterThanOrEqual(JSContext *cx, MutableHandleValue lhs, MutableHandleValue rhs, bool *res) michael@0: { michael@0: return GreaterThanOrEqualOperation(cx, lhs, rhs, res); michael@0: } michael@0: michael@0: template michael@0: bool michael@0: StringsEqual(JSContext *cx, HandleString lhs, HandleString rhs, bool *res) michael@0: { michael@0: if (!js::EqualStrings(cx, lhs, rhs, res)) michael@0: return false; michael@0: if (!Equal) michael@0: *res = !*res; michael@0: return true; michael@0: } michael@0: michael@0: template bool StringsEqual(JSContext *cx, HandleString lhs, HandleString rhs, bool *res); michael@0: template bool StringsEqual(JSContext *cx, HandleString lhs, HandleString rhs, bool *res); michael@0: michael@0: bool michael@0: IteratorMore(JSContext *cx, HandleObject obj, bool *res) michael@0: { michael@0: RootedValue tmp(cx); michael@0: if (!js_IteratorMore(cx, obj, &tmp)) michael@0: return false; michael@0: michael@0: *res = tmp.toBoolean(); michael@0: return true; michael@0: } michael@0: michael@0: JSObject* michael@0: NewInitArray(JSContext *cx, uint32_t count, types::TypeObject *typeArg) michael@0: { michael@0: RootedTypeObject type(cx, typeArg); michael@0: NewObjectKind newKind = !type ? SingletonObject : GenericObject; michael@0: if (type && type->shouldPreTenure()) michael@0: newKind = TenuredObject; michael@0: RootedObject obj(cx, NewDenseAllocatedArray(cx, count, nullptr, newKind)); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: if (type) michael@0: obj->setType(type); michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: JSObject* michael@0: NewInitObject(JSContext *cx, HandleObject templateObject) michael@0: { michael@0: NewObjectKind newKind = templateObject->hasSingletonType() ? SingletonObject : GenericObject; michael@0: if (!templateObject->hasLazyType() && templateObject->type()->shouldPreTenure()) michael@0: newKind = TenuredObject; michael@0: RootedObject obj(cx, CopyInitializerObject(cx, templateObject, newKind)); michael@0: michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: if (!templateObject->hasSingletonType()) michael@0: obj->setType(templateObject->type()); michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: JSObject * michael@0: NewInitObjectWithClassPrototype(JSContext *cx, HandleObject templateObject) michael@0: { michael@0: JS_ASSERT(!templateObject->hasSingletonType()); michael@0: JS_ASSERT(!templateObject->hasLazyType()); michael@0: michael@0: NewObjectKind newKind = templateObject->type()->shouldPreTenure() michael@0: ? TenuredObject michael@0: : GenericObject; michael@0: JSObject *obj = NewObjectWithGivenProto(cx, michael@0: templateObject->getClass(), michael@0: templateObject->getProto(), michael@0: cx->global(), michael@0: newKind); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: obj->setType(templateObject->type()); michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: bool michael@0: ArraySpliceDense(JSContext *cx, HandleObject obj, uint32_t start, uint32_t deleteCount) michael@0: { michael@0: JS::AutoValueArray<4> argv(cx); michael@0: argv[0].setUndefined(); michael@0: argv[1].setObject(*obj); michael@0: argv[2].set(Int32Value(start)); michael@0: argv[3].set(Int32Value(deleteCount)); michael@0: michael@0: return js::array_splice_impl(cx, 2, argv.begin(), false); michael@0: } michael@0: michael@0: bool michael@0: ArrayPopDense(JSContext *cx, HandleObject obj, MutableHandleValue rval) michael@0: { michael@0: JS_ASSERT(obj->is()); michael@0: michael@0: AutoDetectInvalidation adi(cx, rval.address()); michael@0: michael@0: JS::AutoValueArray<2> argv(cx); michael@0: argv[0].setUndefined(); michael@0: argv[1].setObject(*obj); michael@0: if (!js::array_pop(cx, 0, argv.begin())) michael@0: return false; michael@0: michael@0: // If the result is |undefined|, the array was probably empty and we michael@0: // have to monitor the return value. michael@0: rval.set(argv[0]); michael@0: if (rval.isUndefined()) michael@0: types::TypeScript::Monitor(cx, rval); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ArrayPushDense(JSContext *cx, HandleObject obj, HandleValue v, uint32_t *length) michael@0: { michael@0: JS_ASSERT(obj->is()); michael@0: michael@0: JS::AutoValueArray<3> argv(cx); michael@0: argv[0].setUndefined(); michael@0: argv[1].setObject(*obj); michael@0: argv[2].set(v); michael@0: if (!js::array_push(cx, 1, argv.begin())) michael@0: return false; michael@0: michael@0: *length = argv[0].toInt32(); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: ArrayShiftDense(JSContext *cx, HandleObject obj, MutableHandleValue rval) michael@0: { michael@0: JS_ASSERT(obj->is()); michael@0: michael@0: AutoDetectInvalidation adi(cx, rval.address()); michael@0: michael@0: JS::AutoValueArray<2> argv(cx); michael@0: argv[0].setUndefined(); michael@0: argv[1].setObject(*obj); michael@0: if (!js::array_shift(cx, 0, argv.begin())) michael@0: return false; michael@0: michael@0: // If the result is |undefined|, the array was probably empty and we michael@0: // have to monitor the return value. michael@0: rval.set(argv[0]); michael@0: if (rval.isUndefined()) michael@0: types::TypeScript::Monitor(cx, rval); michael@0: return true; michael@0: } michael@0: michael@0: JSObject * michael@0: ArrayConcatDense(JSContext *cx, HandleObject obj1, HandleObject obj2, HandleObject objRes) michael@0: { michael@0: Rooted arr1(cx, &obj1->as()); michael@0: Rooted arr2(cx, &obj2->as()); michael@0: Rooted arrRes(cx, objRes ? &objRes->as() : nullptr); michael@0: michael@0: if (arrRes) { michael@0: // Fast path if we managed to allocate an object inline. michael@0: if (!js::array_concat_dense(cx, arr1, arr2, arrRes)) michael@0: return nullptr; michael@0: return arrRes; michael@0: } michael@0: michael@0: JS::AutoValueArray<3> argv(cx); michael@0: argv[0].setUndefined(); michael@0: argv[1].setObject(*arr1); michael@0: argv[2].setObject(*arr2); michael@0: if (!js::array_concat(cx, 1, argv.begin())) michael@0: return nullptr; michael@0: return &argv[0].toObject(); michael@0: } michael@0: michael@0: bool michael@0: CharCodeAt(JSContext *cx, HandleString str, int32_t index, uint32_t *code) michael@0: { michael@0: jschar c; michael@0: if (!str->getChar(cx, index, &c)) michael@0: return false; michael@0: *code = c; michael@0: return true; michael@0: } michael@0: michael@0: JSFlatString * michael@0: StringFromCharCode(JSContext *cx, int32_t code) michael@0: { michael@0: jschar c = jschar(code); michael@0: michael@0: if (StaticStrings::hasUnit(c)) michael@0: return cx->staticStrings().getUnit(c); michael@0: michael@0: return js_NewStringCopyN(cx, &c, 1); michael@0: } michael@0: michael@0: bool michael@0: SetProperty(JSContext *cx, HandleObject obj, HandlePropertyName name, HandleValue value, michael@0: bool strict, jsbytecode *pc) michael@0: { michael@0: RootedValue v(cx, value); michael@0: RootedId id(cx, NameToId(name)); michael@0: michael@0: JSOp op = JSOp(*pc); michael@0: michael@0: if (op == JSOP_SETALIASEDVAR) { michael@0: // Aliased var assigns ignore readonly attributes on the property, as michael@0: // required for initializing 'const' closure variables. michael@0: Shape *shape = obj->nativeLookup(cx, name); michael@0: JS_ASSERT(shape && shape->hasSlot()); michael@0: obj->nativeSetSlotWithType(cx, shape, value); michael@0: return true; michael@0: } michael@0: michael@0: if (MOZ_LIKELY(!obj->getOps()->setProperty)) { michael@0: return baseops::SetPropertyHelper( michael@0: cx, obj, obj, id, michael@0: (op == JSOP_SETNAME || op == JSOP_SETGNAME) michael@0: ? baseops::Unqualified michael@0: : baseops::Qualified, michael@0: &v, michael@0: strict); michael@0: } michael@0: michael@0: return JSObject::setGeneric(cx, obj, obj, id, &v, strict); michael@0: } michael@0: michael@0: bool michael@0: InterruptCheck(JSContext *cx) michael@0: { michael@0: gc::MaybeVerifyBarriers(cx); michael@0: michael@0: // Fix loop backedges so that they do not invoke the interrupt again. michael@0: // No lock is held here and it's possible we could segv in the middle here michael@0: // and end up with a state where some fraction of the backedges point to michael@0: // the interrupt handler and some don't. This is ok since the interrupt michael@0: // is definitely about to be handled; if there are still backedges michael@0: // afterwards which point to the interrupt handler, the next time they are michael@0: // taken the backedges will just be reset again. michael@0: cx->runtime()->jitRuntime()->patchIonBackedges(cx->runtime(), michael@0: JitRuntime::BackedgeLoopHeader); michael@0: michael@0: return CheckForInterrupt(cx); michael@0: } michael@0: michael@0: HeapSlot * michael@0: NewSlots(JSRuntime *rt, unsigned nslots) michael@0: { michael@0: JS_STATIC_ASSERT(sizeof(Value) == sizeof(HeapSlot)); michael@0: michael@0: Value *slots = reinterpret_cast(rt->malloc_(nslots * sizeof(Value))); michael@0: if (!slots) michael@0: return nullptr; michael@0: michael@0: for (unsigned i = 0; i < nslots; i++) michael@0: slots[i] = UndefinedValue(); michael@0: michael@0: return reinterpret_cast(slots); michael@0: } michael@0: michael@0: JSObject * michael@0: NewCallObject(JSContext *cx, HandleShape shape, HandleTypeObject type, HeapSlot *slots) michael@0: { michael@0: JSObject *obj = CallObject::create(cx, shape, type, slots); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: // The JIT creates call objects in the nursery, so elides barriers for michael@0: // the initializing writes. The interpreter, however, may have allocated michael@0: // the call object tenured, so barrier as needed before re-entering. michael@0: if (!IsInsideNursery(cx->runtime(), obj)) michael@0: cx->runtime()->gcStoreBuffer.putWholeCell(obj); michael@0: #endif michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: JSObject * michael@0: NewSingletonCallObject(JSContext *cx, HandleShape shape, HeapSlot *slots) michael@0: { michael@0: JSObject *obj = CallObject::createSingleton(cx, shape, slots); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: // The JIT creates call objects in the nursery, so elides barriers for michael@0: // the initializing writes. The interpreter, however, may have allocated michael@0: // the call object tenured, so barrier as needed before re-entering. michael@0: MOZ_ASSERT(!IsInsideNursery(cx->runtime(), obj), michael@0: "singletons are created in the tenured heap"); michael@0: cx->runtime()->gcStoreBuffer.putWholeCell(obj); michael@0: #endif michael@0: michael@0: return obj; michael@0: } michael@0: michael@0: JSObject * michael@0: NewStringObject(JSContext *cx, HandleString str) michael@0: { michael@0: return StringObject::create(cx, str); michael@0: } michael@0: michael@0: bool michael@0: SPSEnter(JSContext *cx, HandleScript script) michael@0: { michael@0: return cx->runtime()->spsProfiler.enter(script, script->functionNonDelazifying()); michael@0: } michael@0: michael@0: bool michael@0: SPSExit(JSContext *cx, HandleScript script) michael@0: { michael@0: cx->runtime()->spsProfiler.exit(script, script->functionNonDelazifying()); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: OperatorIn(JSContext *cx, HandleValue key, HandleObject obj, bool *out) michael@0: { michael@0: RootedId id(cx); michael@0: if (!ValueToId(cx, key, &id)) michael@0: return false; michael@0: michael@0: RootedObject obj2(cx); michael@0: RootedShape prop(cx); michael@0: if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &prop)) michael@0: return false; michael@0: michael@0: *out = !!prop; michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: OperatorInI(JSContext *cx, uint32_t index, HandleObject obj, bool *out) michael@0: { michael@0: RootedValue key(cx, Int32Value(index)); michael@0: return OperatorIn(cx, key, obj, out); michael@0: } michael@0: michael@0: bool michael@0: GetIntrinsicValue(JSContext *cx, HandlePropertyName name, MutableHandleValue rval) michael@0: { michael@0: if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, rval)) michael@0: return false; michael@0: michael@0: // This function is called when we try to compile a cold getintrinsic michael@0: // op. MCallGetIntrinsicValue has an AliasSet of None for optimization michael@0: // purposes, as its side effect is not observable from JS. We are michael@0: // guaranteed to bail out after this function, but because of its AliasSet, michael@0: // type info will not be reflowed. Manually monitor here. michael@0: types::TypeScript::Monitor(cx, rval); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: CreateThis(JSContext *cx, HandleObject callee, MutableHandleValue rval) michael@0: { michael@0: rval.set(MagicValue(JS_IS_CONSTRUCTING)); michael@0: michael@0: if (callee->is()) { michael@0: JSFunction *fun = &callee->as(); michael@0: if (fun->isInterpretedConstructor()) { michael@0: JSScript *script = fun->getOrCreateScript(cx); michael@0: if (!script || !script->ensureHasTypes(cx)) michael@0: return false; michael@0: JSObject *thisObj = CreateThisForFunction(cx, callee, GenericObject); michael@0: if (!thisObj) michael@0: return false; michael@0: rval.set(ObjectValue(*thisObj)); michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: GetDynamicName(JSContext *cx, JSObject *scopeChain, JSString *str, Value *vp) michael@0: { michael@0: // Lookup a string on the scope chain, returning either the value found or michael@0: // undefined through rval. This function is infallible, and cannot GC or michael@0: // invalidate. michael@0: michael@0: JSAtom *atom; michael@0: if (str->isAtom()) { michael@0: atom = &str->asAtom(); michael@0: } else { michael@0: atom = AtomizeString(cx, str); michael@0: if (!atom) { michael@0: vp->setUndefined(); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: if (!frontend::IsIdentifier(atom) || frontend::IsKeyword(atom)) { michael@0: vp->setUndefined(); michael@0: return; michael@0: } michael@0: michael@0: Shape *shape = nullptr; michael@0: JSObject *scope = nullptr, *pobj = nullptr; michael@0: if (LookupNameNoGC(cx, atom->asPropertyName(), scopeChain, &scope, &pobj, &shape)) { michael@0: if (FetchNameNoGC(pobj, shape, MutableHandleValue::fromMarkedLocation(vp))) michael@0: return; michael@0: } michael@0: michael@0: vp->setUndefined(); michael@0: } michael@0: michael@0: bool michael@0: FilterArgumentsOrEval(JSContext *cx, JSString *str) michael@0: { michael@0: // getChars() is fallible, but cannot GC: it can only allocate a character michael@0: // for the flattened string. If this call fails then the calling Ion code michael@0: // will bailout, resume in the interpreter and likely fail again when michael@0: // trying to flatten the string and unwind the stack. michael@0: const jschar *chars = str->getChars(cx); michael@0: if (!chars) michael@0: return false; michael@0: michael@0: static const jschar arguments[] = {'a', 'r', 'g', 'u', 'm', 'e', 'n', 't', 's'}; michael@0: static const jschar eval[] = {'e', 'v', 'a', 'l'}; michael@0: michael@0: return !StringHasPattern(chars, str->length(), arguments, mozilla::ArrayLength(arguments)) && michael@0: !StringHasPattern(chars, str->length(), eval, mozilla::ArrayLength(eval)); michael@0: } michael@0: michael@0: #ifdef JSGC_GENERATIONAL michael@0: void michael@0: PostWriteBarrier(JSRuntime *rt, JSObject *obj) michael@0: { michael@0: JS_ASSERT(!IsInsideNursery(rt, obj)); michael@0: rt->gcStoreBuffer.putWholeCell(obj); michael@0: } michael@0: michael@0: void michael@0: PostGlobalWriteBarrier(JSRuntime *rt, JSObject *obj) michael@0: { michael@0: JS_ASSERT(obj->is()); michael@0: if (!obj->compartment()->globalWriteBarriered) { michael@0: PostWriteBarrier(rt, obj); michael@0: obj->compartment()->globalWriteBarriered = true; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: uint32_t michael@0: GetIndexFromString(JSString *str) michael@0: { michael@0: // Masks the return value UINT32_MAX as failure to get the index. michael@0: // I.e. it is impossible to distinguish between failing to get the index michael@0: // or the actual index UINT32_MAX. michael@0: michael@0: if (!str->isAtom()) michael@0: return UINT32_MAX; michael@0: michael@0: uint32_t index; michael@0: JSAtom *atom = &str->asAtom(); michael@0: if (!atom->isIndex(&index)) michael@0: return UINT32_MAX; michael@0: michael@0: return index; michael@0: } michael@0: michael@0: bool michael@0: DebugPrologue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn) michael@0: { michael@0: *mustReturn = false; michael@0: michael@0: JSTrapStatus status = ScriptDebugPrologue(cx, frame, pc); michael@0: switch (status) { michael@0: case JSTRAP_CONTINUE: michael@0: return true; michael@0: michael@0: case JSTRAP_RETURN: michael@0: // The script is going to return immediately, so we have to call the michael@0: // debug epilogue handler as well. michael@0: JS_ASSERT(frame->hasReturnValue()); michael@0: *mustReturn = true; michael@0: return jit::DebugEpilogue(cx, frame, pc, true); michael@0: michael@0: case JSTRAP_THROW: michael@0: case JSTRAP_ERROR: michael@0: return false; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid trap status"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: DebugEpilogue(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool ok) michael@0: { michael@0: // Unwind scope chain to stack depth 0. michael@0: ScopeIter si(frame, pc, cx); michael@0: UnwindScope(cx, si, frame->script()->main()); michael@0: michael@0: // If ScriptDebugEpilogue returns |true| we have to return the frame's michael@0: // return value. If it returns |false|, the debugger threw an exception. michael@0: // In both cases we have to pop debug scopes. michael@0: ok = ScriptDebugEpilogue(cx, frame, pc, ok); michael@0: michael@0: if (frame->isNonEvalFunctionFrame()) { michael@0: JS_ASSERT_IF(ok, frame->hasReturnValue()); michael@0: DebugScopes::onPopCall(frame, cx); michael@0: } else if (frame->isStrictEvalFrame()) { michael@0: JS_ASSERT_IF(frame->hasCallObj(), frame->scopeChain()->as().isForEval()); michael@0: DebugScopes::onPopStrictEvalScope(frame); michael@0: } michael@0: michael@0: // If the frame has a pushed SPS frame, make sure to pop it. michael@0: if (frame->hasPushedSPSFrame()) { michael@0: cx->runtime()->spsProfiler.exit(frame->script(), frame->maybeFun()); michael@0: // Unset the pushedSPSFrame flag because DebugEpilogue may get called before michael@0: // probes::ExitScript in baseline during exception handling, and we don't michael@0: // want to double-pop SPS frames. michael@0: frame->unsetPushedSPSFrame(); michael@0: } michael@0: michael@0: if (!ok) { michael@0: // Pop this frame by updating ionTop, so that the exception handling michael@0: // code will start at the previous frame. michael@0: michael@0: IonJSFrameLayout *prefix = frame->framePrefix(); michael@0: EnsureExitFrame(prefix); michael@0: cx->mainThread().ionTop = (uint8_t *)prefix; michael@0: } michael@0: michael@0: return ok; michael@0: } michael@0: michael@0: bool michael@0: StrictEvalPrologue(JSContext *cx, BaselineFrame *frame) michael@0: { michael@0: return frame->strictEvalPrologue(cx); michael@0: } michael@0: michael@0: bool michael@0: HeavyweightFunPrologue(JSContext *cx, BaselineFrame *frame) michael@0: { michael@0: return frame->heavyweightFunPrologue(cx); michael@0: } michael@0: michael@0: bool michael@0: NewArgumentsObject(JSContext *cx, BaselineFrame *frame, MutableHandleValue res) michael@0: { michael@0: ArgumentsObject *obj = ArgumentsObject::createExpected(cx, frame); michael@0: if (!obj) michael@0: return false; michael@0: res.setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: JSObject * michael@0: InitRestParameter(JSContext *cx, uint32_t length, Value *rest, HandleObject templateObj, michael@0: HandleObject objRes) michael@0: { michael@0: if (objRes) { michael@0: Rooted arrRes(cx, &objRes->as()); michael@0: michael@0: JS_ASSERT(!arrRes->getDenseInitializedLength()); michael@0: JS_ASSERT(arrRes->type() == templateObj->type()); michael@0: michael@0: // Fast path: we managed to allocate the array inline; initialize the michael@0: // slots. michael@0: if (length > 0) { michael@0: if (!arrRes->ensureElements(cx, length)) michael@0: return nullptr; michael@0: arrRes->setDenseInitializedLength(length); michael@0: arrRes->initDenseElements(0, rest, length); michael@0: arrRes->setLengthInt32(length); michael@0: } michael@0: return arrRes; michael@0: } michael@0: michael@0: NewObjectKind newKind = templateObj->type()->shouldPreTenure() michael@0: ? TenuredObject michael@0: : GenericObject; michael@0: ArrayObject *arrRes = NewDenseCopiedArray(cx, length, rest, nullptr, newKind); michael@0: if (arrRes) michael@0: arrRes->setType(templateObj->type()); michael@0: return arrRes; michael@0: } michael@0: michael@0: bool michael@0: HandleDebugTrap(JSContext *cx, BaselineFrame *frame, uint8_t *retAddr, bool *mustReturn) michael@0: { michael@0: *mustReturn = false; michael@0: michael@0: RootedScript script(cx, frame->script()); michael@0: jsbytecode *pc = script->baselineScript()->icEntryFromReturnAddress(retAddr).pc(script); michael@0: michael@0: JS_ASSERT(cx->compartment()->debugMode()); michael@0: JS_ASSERT(script->stepModeEnabled() || script->hasBreakpointsAt(pc)); michael@0: michael@0: RootedValue rval(cx); michael@0: JSTrapStatus status = JSTRAP_CONTINUE; michael@0: JSInterruptHook hook = cx->runtime()->debugHooks.interruptHook; michael@0: michael@0: if (hook || script->stepModeEnabled()) { michael@0: if (hook) michael@0: status = hook(cx, script, pc, rval.address(), cx->runtime()->debugHooks.interruptHookData); michael@0: if (status == JSTRAP_CONTINUE && script->stepModeEnabled()) michael@0: status = Debugger::onSingleStep(cx, &rval); michael@0: } michael@0: michael@0: if (status == JSTRAP_CONTINUE && script->hasBreakpointsAt(pc)) michael@0: status = Debugger::onTrap(cx, &rval); michael@0: michael@0: switch (status) { michael@0: case JSTRAP_CONTINUE: michael@0: break; michael@0: michael@0: case JSTRAP_ERROR: michael@0: return false; michael@0: michael@0: case JSTRAP_RETURN: michael@0: *mustReturn = true; michael@0: frame->setReturnValue(rval); michael@0: return jit::DebugEpilogue(cx, frame, pc, true); michael@0: michael@0: case JSTRAP_THROW: michael@0: cx->setPendingException(rval); michael@0: return false; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid trap status"); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: OnDebuggerStatement(JSContext *cx, BaselineFrame *frame, jsbytecode *pc, bool *mustReturn) michael@0: { michael@0: *mustReturn = false; michael@0: michael@0: RootedScript script(cx, frame->script()); michael@0: JSTrapStatus status = JSTRAP_CONTINUE; michael@0: RootedValue rval(cx); michael@0: michael@0: if (JSDebuggerHandler handler = cx->runtime()->debugHooks.debuggerHandler) michael@0: status = handler(cx, script, pc, rval.address(), cx->runtime()->debugHooks.debuggerHandlerData); michael@0: michael@0: if (status == JSTRAP_CONTINUE) michael@0: status = Debugger::onDebuggerStatement(cx, &rval); michael@0: michael@0: switch (status) { michael@0: case JSTRAP_ERROR: michael@0: return false; michael@0: michael@0: case JSTRAP_CONTINUE: michael@0: return true; michael@0: michael@0: case JSTRAP_RETURN: michael@0: frame->setReturnValue(rval); michael@0: *mustReturn = true; michael@0: return jit::DebugEpilogue(cx, frame, pc, true); michael@0: michael@0: case JSTRAP_THROW: michael@0: cx->setPendingException(rval); michael@0: return false; michael@0: michael@0: default: michael@0: MOZ_ASSUME_UNREACHABLE("Invalid trap status"); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: PushBlockScope(JSContext *cx, BaselineFrame *frame, Handle block) michael@0: { michael@0: return frame->pushBlock(cx, block); michael@0: } michael@0: michael@0: bool michael@0: PopBlockScope(JSContext *cx, BaselineFrame *frame) michael@0: { michael@0: frame->popBlock(cx); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: DebugLeaveBlock(JSContext *cx, BaselineFrame *frame, jsbytecode *pc) michael@0: { michael@0: JS_ASSERT(frame->script()->baselineScript()->debugMode()); michael@0: michael@0: DebugScopes::onPopBlock(cx, frame, pc); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: EnterWith(JSContext *cx, BaselineFrame *frame, HandleValue val, Handle templ) michael@0: { michael@0: return EnterWithOperation(cx, frame, val, templ); michael@0: } michael@0: michael@0: bool michael@0: LeaveWith(JSContext *cx, BaselineFrame *frame) michael@0: { michael@0: frame->popWith(cx); michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: InitBaselineFrameForOsr(BaselineFrame *frame, InterpreterFrame *interpFrame, michael@0: uint32_t numStackValues) michael@0: { michael@0: return frame->initForOsr(interpFrame, numStackValues); michael@0: } michael@0: michael@0: JSObject * michael@0: CreateDerivedTypedObj(JSContext *cx, HandleObject descr, michael@0: HandleObject owner, int32_t offset) michael@0: { michael@0: JS_ASSERT(descr->is()); michael@0: JS_ASSERT(owner->is()); michael@0: Rooted descr1(cx, &descr->as()); michael@0: Rooted owner1(cx, &owner->as()); michael@0: return TypedObject::createDerived(cx, descr1, owner1, offset); michael@0: } michael@0: michael@0: JSString * michael@0: RegExpReplace(JSContext *cx, HandleString string, HandleObject regexp, HandleString repl) michael@0: { michael@0: JS_ASSERT(string); michael@0: JS_ASSERT(repl); michael@0: michael@0: RootedValue rval(cx); michael@0: if (!str_replace_regexp_raw(cx, string, regexp, repl, &rval)) michael@0: return nullptr; michael@0: michael@0: return rval.toString(); michael@0: } michael@0: michael@0: JSString * michael@0: StringReplace(JSContext *cx, HandleString string, HandleString pattern, HandleString repl) michael@0: { michael@0: JS_ASSERT(string); michael@0: JS_ASSERT(pattern); michael@0: JS_ASSERT(repl); michael@0: michael@0: RootedValue rval(cx); michael@0: if (!str_replace_string_raw(cx, string, pattern, repl, &rval)) michael@0: return nullptr; michael@0: michael@0: return rval.toString(); michael@0: } michael@0: michael@0: bool michael@0: Recompile(JSContext *cx) michael@0: { michael@0: JS_ASSERT(cx->currentlyRunningInJit()); michael@0: JitActivationIterator activations(cx->runtime()); michael@0: JitFrameIterator iter(activations); michael@0: michael@0: JS_ASSERT(iter.type() == JitFrame_Exit); michael@0: ++iter; michael@0: michael@0: bool isConstructing = iter.isConstructing(); michael@0: RootedScript script(cx, iter.script()); michael@0: JS_ASSERT(script->hasIonScript()); michael@0: michael@0: if (!IsIonEnabled(cx)) michael@0: return true; michael@0: michael@0: MethodStatus status = Recompile(cx, script, nullptr, nullptr, isConstructing); michael@0: if (status == Method_Error) michael@0: return false; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: bool michael@0: SetDenseElement(JSContext *cx, HandleObject obj, int32_t index, HandleValue value, michael@0: bool strict) michael@0: { michael@0: // This function is called from Ion code for StoreElementHole's OOL path. michael@0: // In this case we know the object is native, has no indexed properties michael@0: // and we can use setDenseElement instead of setDenseElementWithType. michael@0: michael@0: MOZ_ASSERT(obj->isNative()); michael@0: MOZ_ASSERT(!obj->isIndexed()); michael@0: michael@0: JSObject::EnsureDenseResult result = JSObject::ED_SPARSE; michael@0: do { michael@0: if (index < 0) michael@0: break; michael@0: bool isArray = obj->is(); michael@0: if (isArray && !obj->as().lengthIsWritable()) michael@0: break; michael@0: uint32_t idx = uint32_t(index); michael@0: result = obj->ensureDenseElements(cx, idx, 1); michael@0: if (result != JSObject::ED_OK) michael@0: break; michael@0: if (isArray) { michael@0: ArrayObject &arr = obj->as(); michael@0: if (idx >= arr.length()) michael@0: arr.setLengthInt32(idx + 1); michael@0: } michael@0: obj->setDenseElement(idx, value); michael@0: return true; michael@0: } while (false); michael@0: michael@0: if (result == JSObject::ED_FAILED) michael@0: return false; michael@0: MOZ_ASSERT(result == JSObject::ED_SPARSE); michael@0: michael@0: RootedValue indexVal(cx, Int32Value(index)); michael@0: return SetObjectElement(cx, obj, indexVal, value, strict); michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: void michael@0: AssertValidObjectPtr(JSContext *cx, JSObject *obj) michael@0: { michael@0: // Check what we can, so that we'll hopefully assert/crash if we get a michael@0: // bogus object (pointer). michael@0: JS_ASSERT(obj->compartment() == cx->compartment()); michael@0: JS_ASSERT(obj->runtimeFromMainThread() == cx->runtime()); michael@0: michael@0: JS_ASSERT_IF(!obj->hasLazyType(), michael@0: obj->type()->clasp() == obj->lastProperty()->getObjectClass()); michael@0: michael@0: if (obj->isTenured()) { michael@0: JS_ASSERT(obj->isAligned()); michael@0: gc::AllocKind kind = obj->tenuredGetAllocKind(); michael@0: JS_ASSERT(kind >= js::gc::FINALIZE_OBJECT0 && kind <= js::gc::FINALIZE_OBJECT_LAST); michael@0: JS_ASSERT(obj->tenuredZone() == cx->zone()); michael@0: } michael@0: } michael@0: michael@0: void michael@0: AssertValidStringPtr(JSContext *cx, JSString *str) michael@0: { michael@0: // We can't closely inspect strings from another runtime. michael@0: if (str->runtimeFromAnyThread() != cx->runtime()) { michael@0: JS_ASSERT(str->isPermanentAtom()); michael@0: return; michael@0: } michael@0: michael@0: if (str->isAtom()) michael@0: JS_ASSERT(cx->runtime()->isAtomsZone(str->tenuredZone())); michael@0: else michael@0: JS_ASSERT(str->tenuredZone() == cx->zone()); michael@0: michael@0: JS_ASSERT(str->runtimeFromMainThread() == cx->runtime()); michael@0: JS_ASSERT(str->isAligned()); michael@0: JS_ASSERT(str->length() <= JSString::MAX_LENGTH); michael@0: michael@0: gc::AllocKind kind = str->tenuredGetAllocKind(); michael@0: if (str->isFatInline()) michael@0: JS_ASSERT(kind == gc::FINALIZE_FAT_INLINE_STRING); michael@0: else if (str->isExternal()) michael@0: JS_ASSERT(kind == gc::FINALIZE_EXTERNAL_STRING); michael@0: else if (str->isAtom() || str->isFlat()) michael@0: JS_ASSERT(kind == gc::FINALIZE_STRING || kind == gc::FINALIZE_FAT_INLINE_STRING); michael@0: else michael@0: JS_ASSERT(kind == gc::FINALIZE_STRING); michael@0: } michael@0: michael@0: void michael@0: AssertValidValue(JSContext *cx, Value *v) michael@0: { michael@0: if (v->isObject()) { michael@0: AssertValidObjectPtr(cx, &v->toObject()); michael@0: return; michael@0: } michael@0: if (v->isString()) { michael@0: AssertValidStringPtr(cx, v->toString()); michael@0: return; michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: } // namespace jit michael@0: } // namespace js