#include "jit/VMFunctions.h"
#include "builtin/Promise.h"
#include "builtin/TypedObject.h"
#include "frontend/BytecodeCompiler.h"
#include "jit/arm/Simulator-arm.h"
#include "jit/BaselineIC.h"
#include "jit/JitFrames.h"
#include "jit/JitRealm.h"
#include "jit/mips32/Simulator-mips32.h"
#include "jit/mips64/Simulator-mips64.h"
#include "vm/ArrayObject.h"
#include "vm/Debugger.h"
#include "vm/EqualityOperations.h"
#include "vm/Interpreter.h"
#include "vm/SelfHosting.h"
#include "vm/TraceLogging.h"
#include "jit/BaselineFrame-inl.h"
#include "jit/JitFrames-inl.h"
#include "jit/VMFunctionList-inl.h"
#include "vm/Debugger-inl.h"
#include "vm/Interpreter-inl.h"
#include "vm/NativeObject-inl.h"
#include "vm/StringObject-inl.h"
#include "vm/TypeInference-inl.h"
#include "vm/UnboxedObject-inl.h"
using namespace js;
using namespace js::jit;
namespace js {
namespace jit {
template <typename... Args>
struct VMFunctionDataHelper;
template <class R, typename... Args>
struct VMFunctionDataHelper<R (*)(JSContext*, Args...)>
: public VMFunctionData {
using Fun = R (*)(JSContext*, Args...);
static constexpr DataType returnType() { return TypeToDataType<R>::result; }
static constexpr DataType outParam() {
return OutParamToDataType<typename LastArg<Args...>::Type>::result;
}
static constexpr RootType outParamRootType() {
return OutParamToRootType<typename LastArg<Args...>::Type>::result;
}
static constexpr size_t NbArgs() { return LastArg<Args...>::nbArgs; }
static constexpr size_t explicitArgs() {
return NbArgs() - (outParam() != Type_Void ? 1 : 0);
}
static constexpr uint32_t argumentProperties() {
return BitMask<TypeToArgProperties, uint32_t, 2, Args...>::result;
}
static constexpr uint32_t argumentPassedInFloatRegs() {
return BitMask<TypeToPassInFloatReg, uint32_t, 2, Args...>::result;
}
static constexpr uint64_t argumentRootTypes() {
return BitMask<TypeToRootType, uint64_t, 3, Args...>::result;
}
constexpr explicit VMFunctionDataHelper(const char* name)
: VMFunctionData(name, explicitArgs(), argumentProperties(),
argumentPassedInFloatRegs(), argumentRootTypes(),
outParam(), outParamRootType(), returnType(),
0, NonTailCall) {}
constexpr explicit VMFunctionDataHelper(const char* name,
MaybeTailCall expectTailCall,
PopValues extraValuesToPop)
: VMFunctionData(name, explicitArgs(), argumentProperties(),
argumentPassedInFloatRegs(), argumentRootTypes(),
outParam(), outParamRootType(), returnType(),
extraValuesToPop.numValues, expectTailCall) {}
};
#if MOZ_IS_GCC
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wignored-attributes"
#endif
static constexpr VMFunctionData vmFunctions[] = {
#define DEF_VMFUNCTION(name, fp) VMFunctionDataHelper<decltype(&(::fp))>(#name),
VMFUNCTION_LIST(DEF_VMFUNCTION)
#undef DEF_VMFUNCTION
};
static constexpr VMFunctionData tailCallVMFunctions[] = {
#define DEF_VMFUNCTION(name, fp, valuesToPop) \
VMFunctionDataHelper<decltype(&(::fp))>(#name, TailCall, \
PopValues(valuesToPop)),
TAIL_CALL_VMFUNCTION_LIST(DEF_VMFUNCTION)
#undef DEF_VMFUNCTION
};
#if MOZ_IS_GCC
# pragma GCC diagnostic pop
#endif
#define DEF_VMFUNCTION(name, fp, ...) (void*)(::fp),
static void* const vmFunctionTargets[] = {VMFUNCTION_LIST(DEF_VMFUNCTION)};
static void* const tailCallVMFunctionTargets[] = {
TAIL_CALL_VMFUNCTION_LIST(DEF_VMFUNCTION)};
#undef DEF_VMFUNCTION
const VMFunctionData& GetVMFunction(VMFunctionId id) {
return vmFunctions[size_t(id)];
}
const VMFunctionData& GetVMFunction(TailCallVMFunctionId id) {
return tailCallVMFunctions[size_t(id)];
}
static void* GetVMFunctionTarget(VMFunctionId id) {
return vmFunctionTargets[size_t(id)];
}
static void* GetVMFunctionTarget(TailCallVMFunctionId id) {
return tailCallVMFunctionTargets[size_t(id)];
}
template <typename IdT>
bool JitRuntime::generateVMWrappers(JSContext* cx, MacroAssembler& masm,
VMWrapperOffsets& offsets) {
static constexpr size_t NumVMFunctions = size_t(IdT::Count);
if (!offsets.reserve(NumVMFunctions)) {
return false;
}
#ifdef DEBUG
const char* lastName = nullptr;
#endif
for (size_t i = 0; i < NumVMFunctions; i++) {
IdT id = IdT(i);
const VMFunctionData& fun = GetVMFunction(id);
#ifdef DEBUG
if (lastName) {
MOZ_ASSERT(strcmp(lastName, fun.name()) < 0,
"VM function list must be sorted by name");
}
lastName = fun.name();
#endif
JitSpew(JitSpew_Codegen, "# VM function wrapper (%s)", fun.name());
uint32_t offset;
if (!generateVMWrapper(cx, masm, fun, GetVMFunctionTarget(id), &offset)) {
return false;
}
MOZ_ASSERT(offsets.length() == size_t(id));
offsets.infallibleAppend(offset);
}
return true;
};
bool JitRuntime::generateVMWrappers(JSContext* cx, MacroAssembler& masm) {
if (!generateVMWrappers<VMFunctionId>(cx, masm, functionWrapperOffsets_)) {
return false;
}
if (!generateVMWrappers<TailCallVMFunctionId>(
cx, masm, tailCallFunctionWrapperOffsets_)) {
return false;
}
return true;
}
AutoDetectInvalidation::AutoDetectInvalidation(JSContext* cx,
MutableHandleValue rval)
: cx_(cx),
ionScript_(GetTopJitJSScript(cx)->ionScript()),
rval_(rval),
disabled_(false) {}
bool InvokeFunction(JSContext* cx, HandleObject obj, bool constructing,
bool ignoresReturnValue, uint32_t argc, Value* argv,
MutableHandleValue rval) {
TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
TraceLogStartEvent(logger, TraceLogger_Call);
AutoArrayRooter argvRoot(cx, argc + 1 + constructing, argv);
RootedValue thisv(cx, argv[0]);
Value* argvWithoutThis = argv + 1;
RootedValue fval(cx, ObjectValue(*obj));
if (constructing) {
if (!IsConstructor(fval)) {
ReportValueError(cx, JSMSG_NOT_CONSTRUCTOR, JSDVG_IGNORE_STACK, fval,
nullptr);
return false;
}
ConstructArgs cargs(cx);
if (!cargs.init(cx, argc)) {
return false;
}
for (uint32_t i = 0; i < argc; i++) {
cargs[i].set(argvWithoutThis[i]);
}
RootedValue newTarget(cx, argvWithoutThis[argc]);
if (thisv.isMagic()) {
MOZ_ASSERT(thisv.whyMagic() == JS_IS_CONSTRUCTING ||
thisv.whyMagic() == JS_UNINITIALIZED_LEXICAL);
RootedObject obj(cx);
if (!Construct(cx, fval, cargs, newTarget, &obj)) {
return false;
}
rval.setObject(*obj);
return true;
}
return InternalConstructWithProvidedThis(cx, fval, thisv, cargs, newTarget,
rval);
}
InvokeArgsMaybeIgnoresReturnValue args(cx, ignoresReturnValue);
if (!args.init(cx, argc)) {
return false;
}
for (size_t i = 0; i < argc; i++) {
args[i].set(argvWithoutThis[i]);
}
return Call(cx, fval, thisv, args, rval);
}
bool InvokeFunctionShuffleNewTarget(JSContext* cx, HandleObject obj,
uint32_t numActualArgs,
uint32_t numFormalArgs, Value* argv,
MutableHandleValue rval) {
MOZ_ASSERT(numFormalArgs > numActualArgs);
argv[1 + numActualArgs] = argv[1 + numFormalArgs];
return InvokeFunction(cx, obj, true, false, numActualArgs, argv, rval);
}
bool InvokeFromInterpreterStub(JSContext* cx,
InterpreterStubExitFrameLayout* frame) {
JitFrameLayout* jsFrame = frame->jsFrame();
CalleeToken token = jsFrame->calleeToken();
Value* argv = jsFrame->argv();
uint32_t numActualArgs = jsFrame->numActualArgs();
bool constructing = CalleeTokenIsConstructing(token);
RootedFunction fun(cx, CalleeTokenToFunction(token));
if (constructing && numActualArgs < fun->nargs()) {
argv[1 + numActualArgs] = argv[1 + fun->nargs()];
}
RootedValue rval(cx);
if (!InvokeFunction(cx, fun, constructing,
false, numActualArgs, argv,
&rval)) {
return false;
}
argv[0] = rval;
return true;
}
bool CheckOverRecursed(JSContext* cx) {
#ifdef JS_SIMULATOR
if (cx->simulator()->overRecursedWithExtra(0)) {
ReportOverRecursed(cx);
return false;
}
#else
if (!CheckRecursionLimit(cx)) {
return false;
}
#endif
gc::MaybeVerifyBarriers(cx);
return cx->handleInterrupt();
}
bool CheckOverRecursedBaseline(JSContext* cx, BaselineFrame* frame) {
if (frame->overRecursed()) {
ReportOverRecursed(cx);
return false;
}
return CheckOverRecursed(cx);
}
bool MutatePrototype(JSContext* cx, HandlePlainObject obj, HandleValue value) {
if (!value.isObjectOrNull()) {
return true;
}
RootedObject newProto(cx, value.toObjectOrNull());
return SetPrototype(cx, obj, newProto);
}
template <bool Equal>
bool LooselyEqual(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
bool* res) {
if (!js::LooselyEqual(cx, lhs, rhs, res)) {
return false;
}
if (!Equal) {
*res = !*res;
}
return true;
}
template bool LooselyEqual<true>(JSContext* cx, MutableHandleValue lhs,
MutableHandleValue rhs, bool* res);
template bool LooselyEqual<false>(JSContext* cx, MutableHandleValue lhs,
MutableHandleValue rhs, bool* res);
template <bool Equal>
bool StrictlyEqual(JSContext* cx, MutableHandleValue lhs,
MutableHandleValue rhs, bool* res) {
if (!js::StrictlyEqual(cx, lhs, rhs, res)) {
return false;
}
if (!Equal) {
*res = !*res;
}
return true;
}
template bool StrictlyEqual<true>(JSContext* cx, MutableHandleValue lhs,
MutableHandleValue rhs, bool* res);
template bool StrictlyEqual<false>(JSContext* cx, MutableHandleValue lhs,
MutableHandleValue rhs, bool* res);
bool LessThan(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
bool* res) {
return LessThanOperation(cx, lhs, rhs, res);
}
bool LessThanOrEqual(JSContext* cx, MutableHandleValue lhs,
MutableHandleValue rhs, bool* res) {
return LessThanOrEqualOperation(cx, lhs, rhs, res);
}
bool GreaterThan(JSContext* cx, MutableHandleValue lhs, MutableHandleValue rhs,
bool* res) {
return GreaterThanOperation(cx, lhs, rhs, res);
}
bool GreaterThanOrEqual(JSContext* cx, MutableHandleValue lhs,
MutableHandleValue rhs, bool* res) {
return GreaterThanOrEqualOperation(cx, lhs, rhs, res);
}
template <bool Equal>
bool StringsEqual(JSContext* cx, HandleString lhs, HandleString rhs,
bool* res) {
if (!js::EqualStrings(cx, lhs, rhs, res)) {
return false;
}
if (!Equal) {
*res = !*res;
}
return true;
}
template bool StringsEqual<true>(JSContext* cx, HandleString lhs,
HandleString rhs, bool* res);
template bool StringsEqual<false>(JSContext* cx, HandleString lhs,
HandleString rhs, bool* res);
bool StringSplitHelper(JSContext* cx, HandleString str, HandleString sep,
HandleObjectGroup group, uint32_t limit,
MutableHandleValue result) {
JSObject* resultObj = StringSplitString(cx, group, str, sep, limit);
if (!resultObj) {
return false;
}
result.setObject(*resultObj);
return true;
}
bool ArrayPopDense(JSContext* cx, HandleObject obj, MutableHandleValue rval) {
MOZ_ASSERT(obj->is<ArrayObject>());
AutoDetectInvalidation adi(cx, rval);
JS::AutoValueArray<2> argv(cx);
argv[0].setUndefined();
argv[1].setObject(*obj);
if (!js::array_pop(cx, 0, argv.begin())) {
return false;
}
rval.set(argv[0]);
if (rval.isUndefined()) {
TypeScript::Monitor(cx, rval);
}
return true;
}
bool ArrayPushDense(JSContext* cx, HandleArrayObject arr, HandleValue v,
uint32_t* length) {
*length = arr->length();
DenseElementResult result = arr->setOrExtendDenseElements(
cx, *length, v.address(), 1, ShouldUpdateTypes::DontUpdate);
if (result != DenseElementResult::Incomplete) {
(*length)++;
return result == DenseElementResult::Success;
}
JSJitFrameIter frame(cx->activation()->asJit());
MOZ_ASSERT(frame.type() == FrameType::Exit);
++frame;
IonScript* ionScript = frame.ionScript();
JS::AutoValueArray<3> argv(cx);
AutoDetectInvalidation adi(cx, argv[0], ionScript);
argv[0].setUndefined();
argv[1].setObject(*arr);
argv[2].set(v);
if (!js::array_push(cx, 1, argv.begin())) {
return false;
}
if (argv[0].isInt32()) {
*length = argv[0].toInt32();
return true;
}
MOZ_ASSERT(adi.shouldSetReturnOverride());
MOZ_ASSERT(argv[0].toDouble() == double(INT32_MAX) + 1);
*length = 0;
return true;
}
bool ArrayShiftDense(JSContext* cx, HandleObject obj, MutableHandleValue rval) {
MOZ_ASSERT(obj->is<ArrayObject>());
AutoDetectInvalidation adi(cx, rval);
JS::AutoValueArray<2> argv(cx);
argv[0].setUndefined();
argv[1].setObject(*obj);
if (!js::array_shift(cx, 0, argv.begin())) {
return false;
}
rval.set(argv[0]);
if (rval.isUndefined()) {
TypeScript::Monitor(cx, rval);
}
return true;
}
JSString* ArrayJoin(JSContext* cx, HandleObject array, HandleString sep) {
JS::AutoValueArray<3> argv(cx);
argv[0].setUndefined();
argv[1].setObject(*array);
argv[2].setString(sep);
if (!js::array_join(cx, 1, argv.begin())) {
return nullptr;
}
return argv[0].toString();
}
bool SetArrayLength(JSContext* cx, HandleObject obj, HandleValue value,
bool strict) {
Handle<ArrayObject*> array = obj.as<ArrayObject>();
RootedId id(cx, NameToId(cx->names().length));
ObjectOpResult result;
if (array->lengthIsWritable()) {
if (!ArraySetLength(cx, array, id, JSPROP_PERMANENT, value, result)) {
return false;
}
} else {
MOZ_ALWAYS_TRUE(result.fail(JSMSG_READ_ONLY));
}
return result.checkStrictErrorOrWarning(cx, obj, id, strict);
}
bool CharCodeAt(JSContext* cx, HandleString str, int32_t index,
uint32_t* code) {
char16_t c;
if (!str->getChar(cx, index, &c)) {
return false;
}
*code = c;
return true;
}
JSFlatString* StringFromCharCode(JSContext* cx, int32_t code) {
char16_t c = char16_t(code);
if (StaticStrings::hasUnit(c)) {
return cx->staticStrings().getUnit(c);
}
return NewStringCopyNDontDeflate<CanGC>(cx, &c, 1);
}
JSString* StringFromCodePoint(JSContext* cx, int32_t codePoint) {
RootedValue rval(cx, Int32Value(codePoint));
if (!str_fromCodePoint_one_arg(cx, rval, &rval)) {
return nullptr;
}
return rval.toString();
}
bool SetProperty(JSContext* cx, HandleObject obj, HandlePropertyName name,
HandleValue value, bool strict, jsbytecode* pc) {
RootedId id(cx, NameToId(name));
JSOp op = JSOp(*pc);
if (op == JSOP_SETALIASEDVAR || op == JSOP_INITALIASEDLEXICAL) {
Shape* shape = obj->as<NativeObject>().lookup(cx, name);
MOZ_ASSERT(shape && shape->isDataProperty());
obj->as<NativeObject>().setSlotWithType(cx, shape, value);
return true;
}
RootedValue receiver(cx, ObjectValue(*obj));
ObjectOpResult result;
if (MOZ_LIKELY(!obj->getOpsSetProperty())) {
if (op == JSOP_SETNAME || op == JSOP_STRICTSETNAME || op == JSOP_SETGNAME ||
op == JSOP_STRICTSETGNAME) {
if (!NativeSetProperty<Unqualified>(cx, obj.as<NativeObject>(), id, value,
receiver, result)) {
return false;
}
} else {
if (!NativeSetProperty<Qualified>(cx, obj.as<NativeObject>(), id, value,
receiver, result)) {
return false;
}
}
} else {
if (!SetProperty(cx, obj, id, value, receiver, result)) {
return false;
}
}
return result.checkStrictErrorOrWarning(cx, obj, id, strict);
}
bool InterruptCheck(JSContext* cx) {
gc::MaybeVerifyBarriers(cx);
return CheckForInterrupt(cx);
}
void* MallocWrapper(JS::Zone* zone, size_t nbytes) {
AutoUnsafeCallWithABI unsafe;
return zone->pod_malloc<uint8_t>(nbytes);
}
JSObject* NewCallObject(JSContext* cx, HandleShape shape,
HandleObjectGroup group) {
JSObject* obj = CallObject::create(cx, shape, group);
if (!obj) {
return nullptr;
}
if (!IsInsideNursery(obj)) {
cx->runtime()->gc.storeBuffer().putWholeCell(obj);
}
return obj;
}
JSObject* NewStringObject(JSContext* cx, HandleString str) {
return StringObject::create(cx, str);
}
bool OperatorIn(JSContext* cx, HandleValue key, HandleObject obj, bool* out) {
RootedId id(cx);
return ToPropertyKey(cx, key, &id) && HasProperty(cx, obj, id, out);
}
bool OperatorInI(JSContext* cx, uint32_t index, HandleObject obj, bool* out) {
RootedValue key(cx, Int32Value(index));
return OperatorIn(cx, key, obj, out);
}
bool GetIntrinsicValue(JSContext* cx, HandlePropertyName name,
MutableHandleValue rval) {
if (!GlobalObject::getIntrinsicValue(cx, cx->global(), name, rval)) {
return false;
}
TypeScript::Monitor(cx, rval);
return true;
}
bool CreateThis(JSContext* cx, HandleObject callee, HandleObject newTarget,
MutableHandleValue rval) {
rval.set(MagicValue(JS_IS_CONSTRUCTING));
if (callee->is<JSFunction>()) {
RootedFunction fun(cx, &callee->as<JSFunction>());
if (fun->isInterpreted() && fun->isConstructor()) {
JSScript* script = JSFunction::getOrCreateScript(cx, fun);
if (!script) {
return false;
}
if (!js::CreateThis(cx, fun, script, newTarget, GenericObject, rval)) {
return false;
}
MOZ_ASSERT_IF(rval.isObject(),
fun->realm() == rval.toObject().nonCCWRealm());
}
}
return true;
}
bool GetDynamicNamePure(JSContext* cx, JSObject* envChain, JSString* str,
Value* vp) {
AutoUnsafeCallWithABI unsafe;
JSAtom* atom;
if (str->isAtom()) {
atom = &str->asAtom();
} else {
atom = AtomizeString(cx, str);
if (!atom) {
cx->recoverFromOutOfMemory();
return false;
}
}
if (!frontend::IsIdentifier(atom) || frontend::IsKeyword(atom)) {
return false;
}
PropertyResult prop;
JSObject* scope = nullptr;
JSObject* pobj = nullptr;
if (LookupNameNoGC(cx, atom->asPropertyName(), envChain, &scope, &pobj,
&prop)) {
return FetchNameNoGC(pobj, prop, vp);
}
return false;
}
void PostWriteBarrier(JSRuntime* rt, js::gc::Cell* cell) {
AutoUnsafeCallWithABI unsafe;
MOZ_ASSERT(!IsInsideNursery(cell));
rt->gc.storeBuffer().putWholeCell(cell);
}
static const size_t MAX_WHOLE_CELL_BUFFER_SIZE = 4096;
template <IndexInBounds InBounds>
void PostWriteElementBarrier(JSRuntime* rt, JSObject* obj, int32_t index) {
AutoUnsafeCallWithABI unsafe;
MOZ_ASSERT(!IsInsideNursery(obj));
if (InBounds == IndexInBounds::Yes) {
MOZ_ASSERT(uint32_t(index) <
obj->as<NativeObject>().getDenseInitializedLength());
} else {
if (MOZ_UNLIKELY(!obj->is<NativeObject>() || index < 0 ||
uint32_t(index) >=
NativeObject::MAX_DENSE_ELEMENTS_COUNT)) {
rt->gc.storeBuffer().putWholeCell(obj);
return;
}
}
NativeObject* nobj = &obj->as<NativeObject>();
if (nobj->isInWholeCellBuffer()) {
return;
}
if (nobj->getDenseInitializedLength() > MAX_WHOLE_CELL_BUFFER_SIZE
#ifdef JS_GC_ZEAL
|| rt->hasZealMode(gc::ZealMode::ElementsBarrier)
#endif
) {
rt->gc.storeBuffer().putSlot(nobj, HeapSlot::Element,
nobj->unshiftedIndex(index), 1);
return;
}
rt->gc.storeBuffer().putWholeCell(obj);
}
template void PostWriteElementBarrier<IndexInBounds::Yes>(JSRuntime* rt,
JSObject* obj,
int32_t index);
template void PostWriteElementBarrier<IndexInBounds::Maybe>(JSRuntime* rt,
JSObject* obj,
int32_t index);
void PostGlobalWriteBarrier(JSRuntime* rt, GlobalObject* obj) {
MOZ_ASSERT(obj->JSObject::is<GlobalObject>());
if (!obj->realm()->globalWriteBarriered) {
PostWriteBarrier(rt, obj);
obj->realm()->globalWriteBarriered = 1;
}
}
int32_t GetIndexFromString(JSString* str) {
AutoUnsafeCallWithABI unsafe;
if (!str->isFlat()) {
return -1;
}
uint32_t index = UINT32_MAX;
if (!str->asFlat().isIndex(&index) || index > INT32_MAX) {
return -1;
}
return int32_t(index);
}
JSObject* WrapObjectPure(JSContext* cx, JSObject* obj) {
AutoUnsafeCallWithABI unsafe;
MOZ_ASSERT(obj);
MOZ_ASSERT(cx->compartment() != obj->compartment());
obj = UncheckedUnwrap(obj, true);
if (cx->compartment() == obj->compartment()) {
MOZ_ASSERT(!IsWindow(obj));
JS::ExposeObjectToActiveJS(obj);
return obj;
}
if (WrapperMap::Ptr p = cx->compartment()->lookupWrapper(obj)) {
JSObject* wrapped = &p->value().get().toObject();
JS::ExposeObjectToActiveJS(wrapped);
return wrapped;
}
return nullptr;
}
static bool HandlePrologueResumeMode(JSContext* cx, BaselineFrame* frame,
jsbytecode* pc, bool* mustReturn,
ResumeMode resumeMode) {
*mustReturn = false;
switch (resumeMode) {
case ResumeMode::Continue:
return true;
case ResumeMode::Return:
MOZ_ASSERT(frame->hasReturnValue());
*mustReturn = true;
return jit::DebugEpilogue(cx, frame, pc, true);
case ResumeMode::Throw:
case ResumeMode::Terminate:
return false;
default:
MOZ_CRASH("bad Debugger::onEnterFrame resume mode");
}
}
bool DebugPrologue(JSContext* cx, BaselineFrame* frame, jsbytecode* pc,
bool* mustReturn) {
ResumeMode resumeMode = Debugger::onEnterFrame(cx, frame);
return HandlePrologueResumeMode(cx, frame, pc, mustReturn, resumeMode);
}
bool DebugEpilogueOnBaselineReturn(JSContext* cx, BaselineFrame* frame,
jsbytecode* pc) {
if (!DebugEpilogue(cx, frame, pc, true)) {
TraceLoggerThread* logger = TraceLoggerForCurrentThread(cx);
TraceLogStopEvent(logger, TraceLogger_Baseline);
TraceLogStopEvent(logger, TraceLogger_Scripts);
return false;
}
return true;
}
bool DebugEpilogue(JSContext* cx, BaselineFrame* frame, jsbytecode* pc,
bool ok) {
ok = Debugger::onLeaveFrame(cx, frame, pc, ok);
EnvironmentIter ei(cx, frame, pc);
UnwindAllEnvironmentsInFrame(cx, ei);
JSScript* script = frame->script();
frame->setOverridePc(script->lastPC());
if (!ok) {
JitFrameLayout* prefix = frame->framePrefix();
EnsureBareExitFrame(cx->activation()->asJit(), prefix);
return false;
}
frame->clearOverridePc();
return true;
}
void FrameIsDebuggeeCheck(BaselineFrame* frame) {
AutoUnsafeCallWithABI unsafe;
if (frame->script()->isDebuggee()) {
frame->setIsDebuggee();
}
}
JSObject* CreateGenerator(JSContext* cx, BaselineFrame* frame) {
return AbstractGeneratorObject::create(cx, frame);
}
bool NormalSuspend(JSContext* cx, HandleObject obj, BaselineFrame* frame,
jsbytecode* pc) {
MOZ_ASSERT(*pc == JSOP_YIELD || *pc == JSOP_AWAIT);
MOZ_ASSERT(frame->numValueSlots() > frame->script()->nfixed());
uint32_t stackDepth = frame->numValueSlots() - frame->script()->nfixed();
MOZ_ASSERT(stackDepth >= 1);
AutoValueVector exprStack(cx);
if (!exprStack.reserve(stackDepth - 1)) {
return false;
}
size_t firstSlot = frame->numValueSlots() - stackDepth;
for (size_t i = 0; i < stackDepth - 1; i++) {
exprStack.infallibleAppend(*frame->valueSlot(firstSlot + i));
}
MOZ_ASSERT(exprStack.length() == stackDepth - 1);
return AbstractGeneratorObject::normalSuspend(
cx, obj, frame, pc, exprStack.begin(), stackDepth - 1);
}
bool FinalSuspend(JSContext* cx, HandleObject obj, jsbytecode* pc) {
MOZ_ASSERT(*pc == JSOP_FINALYIELDRVAL);
AbstractGeneratorObject::finalSuspend(obj);
return true;
}
bool InterpretResume(JSContext* cx, HandleObject obj, HandleValue val,
HandlePropertyName kind, MutableHandleValue rval) {
MOZ_ASSERT(obj->is<AbstractGeneratorObject>());
FixedInvokeArgs<3> args(cx);
args[0].setObject(*obj);
args[1].set(val);
args[2].setString(kind);
return CallSelfHostedFunction(cx, cx->names().InterpretGeneratorResume,
UndefinedHandleValue, args, rval);
}
bool DebugAfterYield(JSContext* cx, BaselineFrame* frame, jsbytecode* pc,
bool* mustReturn) {
if (frame->script()->isDebuggee() && !frame->isDebuggee()) {
frame->setIsDebuggee();
ResumeMode resumeMode = Debugger::onResumeFrame(cx, frame);
return HandlePrologueResumeMode(cx, frame, pc, mustReturn, resumeMode);
}
*mustReturn = false;
return true;
}
bool GeneratorThrowOrReturn(JSContext* cx, BaselineFrame* frame,
Handle<AbstractGeneratorObject*> genObj,
HandleValue arg, uint32_t resumeKind) {
JSScript* script = frame->script();
uint32_t offset = script->resumeOffsets()[genObj->resumeIndex()];
jsbytecode* pc = script->offsetToPC(offset);
frame->setOverridePc(pc);
genObj->setRunning();
bool mustReturn = false;
if (!DebugAfterYield(cx, frame, pc, &mustReturn)) {
return false;
}
if (mustReturn) {
resumeKind = AbstractGeneratorObject::RETURN;
}
MOZ_ALWAYS_FALSE(
js::GeneratorThrowOrReturn(cx, frame, genObj, arg, resumeKind));
return false;
}
bool GlobalNameConflictsCheckFromIon(JSContext* cx, HandleScript script) {
Rooted<LexicalEnvironmentObject*> globalLexical(
cx, &cx->global()->lexicalEnvironment());
return CheckGlobalDeclarationConflicts(cx, script, globalLexical,
cx->global());
}
bool InitFunctionEnvironmentObjects(JSContext* cx, BaselineFrame* frame) {
return frame->initFunctionEnvironmentObjects(cx);
}
bool NewArgumentsObject(JSContext* cx, BaselineFrame* frame,
MutableHandleValue res) {
ArgumentsObject* obj = ArgumentsObject::createExpected(cx, frame);
if (!obj) {
return false;
}
res.setObject(*obj);
return true;
}
JSObject* CopyLexicalEnvironmentObject(JSContext* cx, HandleObject env,
bool copySlots) {
Handle<LexicalEnvironmentObject*> lexicalEnv =
env.as<LexicalEnvironmentObject>();
if (copySlots) {
return LexicalEnvironmentObject::clone(cx, lexicalEnv);
}
return LexicalEnvironmentObject::recreate(cx, lexicalEnv);
}
JSObject* InitRestParameter(JSContext* cx, uint32_t length, Value* rest,
HandleObject templateObj, HandleObject objRes) {
if (objRes) {
Rooted<ArrayObject*> arrRes(cx, &objRes->as<ArrayObject>());
MOZ_ASSERT(!arrRes->getDenseInitializedLength());
MOZ_ASSERT(arrRes->group() == templateObj->group());
if (length > 0) {
if (!arrRes->ensureElements(cx, length)) {
return nullptr;
}
arrRes->initDenseElements(rest, length);
arrRes->setLengthInt32(length);
}
return arrRes;
}
NewObjectKind newKind;
{
AutoSweepObjectGroup sweep(templateObj->group());
newKind = templateObj->group()->shouldPreTenure(sweep) ? TenuredObject
: GenericObject;
}
ArrayObject* arrRes = NewDenseCopiedArray(cx, length, rest, nullptr, newKind);
if (arrRes) {
arrRes->setGroup(templateObj->group());
}
return arrRes;
}
bool HandleDebugTrap(JSContext* cx, BaselineFrame* frame, uint8_t* retAddr,
bool* mustReturn) {
*mustReturn = false;
RootedScript script(cx, frame->script());
jsbytecode* pc =
script->baselineScript()->retAddrEntryFromReturnAddress(retAddr).pc(
script);
if (*pc == JSOP_DEBUGAFTERYIELD) {
MOZ_ASSERT(!frame->isDebuggee());
if (!DebugAfterYield(cx, frame, pc, mustReturn)) {
return false;
}
if (*mustReturn) {
return true;
}
}
MOZ_ASSERT(frame->isDebuggee());
MOZ_ASSERT(script->stepModeEnabled() || script->hasBreakpointsAt(pc));
RootedValue rval(cx);
ResumeMode resumeMode = ResumeMode::Continue;
if (script->stepModeEnabled()) {
resumeMode = Debugger::onSingleStep(cx, &rval);
}
if (resumeMode == ResumeMode::Continue && script->hasBreakpointsAt(pc)) {
resumeMode = Debugger::onTrap(cx, &rval);
}
switch (resumeMode) {
case ResumeMode::Continue:
break;
case ResumeMode::Terminate:
return false;
case ResumeMode::Return:
*mustReturn = true;
frame->setReturnValue(rval);
return jit::DebugEpilogue(cx, frame, pc, true);
case ResumeMode::Throw:
cx->setPendingException(rval);
return false;
default:
MOZ_CRASH("Invalid step/breakpoint resume mode");
}
return true;
}
bool OnDebuggerStatement(JSContext* cx, BaselineFrame* frame, jsbytecode* pc,
bool* mustReturn) {
*mustReturn = false;
switch (Debugger::onDebuggerStatement(cx, frame)) {
case ResumeMode::Continue:
return true;
case ResumeMode::Return:
*mustReturn = true;
return jit::DebugEpilogue(cx, frame, pc, true);
case ResumeMode::Throw:
case ResumeMode::Terminate:
return false;
default:
MOZ_CRASH("Invalid OnDebuggerStatement resume mode");
}
}
bool GlobalHasLiveOnDebuggerStatement(JSContext* cx) {
AutoUnsafeCallWithABI unsafe;
return cx->realm()->isDebuggee() &&
Debugger::hasLiveHook(cx->global(), Debugger::OnDebuggerStatement);
}
bool PushLexicalEnv(JSContext* cx, BaselineFrame* frame,
Handle<LexicalScope*> scope) {
return frame->pushLexicalEnvironment(cx, scope);
}
bool PopLexicalEnv(JSContext* cx, BaselineFrame* frame) {
frame->popOffEnvironmentChain<LexicalEnvironmentObject>();
return true;
}
bool DebugLeaveThenPopLexicalEnv(JSContext* cx, BaselineFrame* frame,
jsbytecode* pc) {
MOZ_ALWAYS_TRUE(DebugLeaveLexicalEnv(cx, frame, pc));
frame->popOffEnvironmentChain<LexicalEnvironmentObject>();
return true;
}
bool FreshenLexicalEnv(JSContext* cx, BaselineFrame* frame) {
return frame->freshenLexicalEnvironment(cx);
}
bool DebugLeaveThenFreshenLexicalEnv(JSContext* cx, BaselineFrame* frame,
jsbytecode* pc) {
MOZ_ALWAYS_TRUE(DebugLeaveLexicalEnv(cx, frame, pc));
return frame->freshenLexicalEnvironment(cx);
}
bool RecreateLexicalEnv(JSContext* cx, BaselineFrame* frame) {
return frame->recreateLexicalEnvironment(cx);
}
bool DebugLeaveThenRecreateLexicalEnv(JSContext* cx, BaselineFrame* frame,
jsbytecode* pc) {
MOZ_ALWAYS_TRUE(DebugLeaveLexicalEnv(cx, frame, pc));
return frame->recreateLexicalEnvironment(cx);
}
bool DebugLeaveLexicalEnv(JSContext* cx, BaselineFrame* frame, jsbytecode* pc) {
MOZ_ASSERT(frame->script()->baselineScript()->hasDebugInstrumentation());
if (cx->realm()->isDebuggee()) {
DebugEnvironments::onPopLexical(cx, frame, pc);
}
return true;
}
bool PushVarEnv(JSContext* cx, BaselineFrame* frame, HandleScope scope) {
return frame->pushVarEnvironment(cx, scope);
}
bool PopVarEnv(JSContext* cx, BaselineFrame* frame) {
frame->popOffEnvironmentChain<VarEnvironmentObject>();
return true;
}
bool EnterWith(JSContext* cx, BaselineFrame* frame, HandleValue val,
Handle<WithScope*> templ) {
return EnterWithOperation(cx, frame, val, templ);
}
bool LeaveWith(JSContext* cx, BaselineFrame* frame) {
if (MOZ_UNLIKELY(frame->isDebuggee())) {
DebugEnvironments::onPopWith(frame);
}
frame->popOffEnvironmentChain<WithEnvironmentObject>();
return true;
}
bool InitBaselineFrameForOsr(BaselineFrame* frame,
InterpreterFrame* interpFrame,
uint32_t numStackValues) {
return frame->initForOsr(interpFrame, numStackValues);
}
JSObject* CreateDerivedTypedObj(JSContext* cx, HandleObject descr,
HandleObject owner, int32_t offset) {
MOZ_ASSERT(descr->is<TypeDescr>());
MOZ_ASSERT(owner->is<TypedObject>());
Rooted<TypeDescr*> descr1(cx, &descr->as<TypeDescr>());
Rooted<TypedObject*> owner1(cx, &owner->as<TypedObject>());
return OutlineTypedObject::createDerived(cx, descr1, owner1, offset);
}
JSString* StringReplace(JSContext* cx, HandleString string,
HandleString pattern, HandleString repl) {
MOZ_ASSERT(string);
MOZ_ASSERT(pattern);
MOZ_ASSERT(repl);
return str_replace_string_raw(cx, string, pattern, repl);
}
bool RecompileImpl(JSContext* cx, bool force) {
MOZ_ASSERT(cx->currentlyRunningInJit());
JitActivationIterator activations(cx);
JSJitFrameIter frame(activations->asJit());
MOZ_ASSERT(frame.type() == FrameType::Exit);
++frame;
RootedScript script(cx, frame.script());
MOZ_ASSERT(script->hasIonScript());
if (!IsIonEnabled(cx)) {
return true;
}
MethodStatus status = Recompile(cx, script, nullptr, nullptr, force);
if (status == Method_Error) {
return false;
}
return true;
}
bool IonForcedRecompile(JSContext* cx) {
return RecompileImpl(cx, true);
}
bool IonRecompile(JSContext* cx) {
return RecompileImpl(cx, false);
}
bool IonForcedInvalidation(JSContext* cx) {
MOZ_ASSERT(cx->currentlyRunningInJit());
JitActivationIterator activations(cx);
JSJitFrameIter frame(activations->asJit());
MOZ_ASSERT(frame.type() == FrameType::Exit);
++frame;
RootedScript script(cx, frame.script());
MOZ_ASSERT(script->hasIonScript());
if (script->baselineScript()->hasPendingIonBuilder()) {
LinkIonScript(cx, script);
return true;
}
Invalidate(cx, script, false,
false);
return true;
}
bool SetDenseElement(JSContext* cx, HandleNativeObject obj, int32_t index,
HandleValue value, bool strict) {
DenseElementResult result = obj->setOrExtendDenseElements(
cx, index, value.address(), 1, ShouldUpdateTypes::DontUpdate);
if (result != DenseElementResult::Incomplete) {
return result == DenseElementResult::Success;
}
RootedValue indexVal(cx, Int32Value(index));
return SetObjectElement(cx, obj, indexVal, value, strict);
}
void AutoDetectInvalidation::setReturnOverride() {
cx_->setIonReturnOverride(rval_.get());
}
void AssertValidObjectPtr(JSContext* cx, JSObject* obj) {
AutoUnsafeCallWithABI unsafe;
#ifdef DEBUG
MOZ_ASSERT(obj->compartment() == cx->compartment());
MOZ_ASSERT(obj->zoneFromAnyThread() == cx->zone());
MOZ_ASSERT(obj->runtimeFromMainThread() == cx->runtime());
MOZ_ASSERT_IF(!obj->hasLazyGroup() && obj->maybeShape(),
obj->group()->clasp() == obj->maybeShape()->getObjectClass());
if (obj->isTenured()) {
MOZ_ASSERT(obj->isAligned());
gc::AllocKind kind = obj->asTenured().getAllocKind();
MOZ_ASSERT(gc::IsObjectAllocKind(kind));
}
#endif
}
void AssertValidObjectOrNullPtr(JSContext* cx, JSObject* obj) {
AutoUnsafeCallWithABI unsafe;
if (obj) {
AssertValidObjectPtr(cx, obj);
}
}
void AssertValidStringPtr(JSContext* cx, JSString* str) {
AutoUnsafeCallWithABI unsafe;
#ifdef DEBUG
if (str->runtimeFromAnyThread() != cx->runtime()) {
MOZ_ASSERT(str->isPermanentAtom());
return;
}
if (str->isAtom()) {
MOZ_ASSERT(str->zone()->isAtomsZone());
} else {
MOZ_ASSERT(str->zone() == cx->zone());
}
MOZ_ASSERT(str->isAligned());
MOZ_ASSERT(str->length() <= JSString::MAX_LENGTH);
gc::AllocKind kind = str->getAllocKind();
if (str->isFatInline()) {
MOZ_ASSERT(kind == gc::AllocKind::FAT_INLINE_STRING ||
kind == gc::AllocKind::FAT_INLINE_ATOM);
} else if (str->isExternal()) {
MOZ_ASSERT(kind == gc::AllocKind::EXTERNAL_STRING);
} else if (str->isAtom()) {
MOZ_ASSERT(kind == gc::AllocKind::ATOM);
} else if (str->isFlat()) {
MOZ_ASSERT(kind == gc::AllocKind::STRING ||
kind == gc::AllocKind::FAT_INLINE_STRING ||
kind == gc::AllocKind::EXTERNAL_STRING);
} else {
MOZ_ASSERT(kind == gc::AllocKind::STRING);
}
#endif
}
void AssertValidSymbolPtr(JSContext* cx, JS::Symbol* sym) {
AutoUnsafeCallWithABI unsafe;
if (sym->runtimeFromAnyThread() != cx->runtime()) {
MOZ_ASSERT(sym->isWellKnownSymbol());
return;
}
MOZ_ASSERT(sym->zone()->isAtomsZone());
MOZ_ASSERT(sym->isAligned());
if (JSString* desc = sym->description()) {
MOZ_ASSERT(desc->isAtom());
AssertValidStringPtr(cx, desc);
}
MOZ_ASSERT(sym->getAllocKind() == gc::AllocKind::SYMBOL);
}
void AssertValidBigIntPtr(JSContext* cx, JS::BigInt* bi) {
AutoUnsafeCallWithABI unsafe;
MOZ_ASSERT(cx->zone() == bi->zone());
MOZ_ASSERT(bi->isAligned());
MOZ_ASSERT(bi->isTenured());
MOZ_ASSERT(bi->getAllocKind() == gc::AllocKind::BIGINT);
}
void AssertValidValue(JSContext* cx, Value* v) {
AutoUnsafeCallWithABI unsafe;
if (v->isObject()) {
AssertValidObjectPtr(cx, &v->toObject());
} else if (v->isString()) {
AssertValidStringPtr(cx, v->toString());
} else if (v->isSymbol()) {
AssertValidSymbolPtr(cx, v->toSymbol());
} else if (v->isBigInt()) {
AssertValidBigIntPtr(cx, v->toBigInt());
}
}
bool ObjectIsCallable(JSObject* obj) {
AutoUnsafeCallWithABI unsafe;
return obj->isCallable();
}
bool ObjectIsConstructor(JSObject* obj) {
AutoUnsafeCallWithABI unsafe;
return obj->isConstructor();
}
void MarkValueFromJit(JSRuntime* rt, Value* vp) {
AutoUnsafeCallWithABI unsafe;
TraceManuallyBarrieredEdge(&rt->gc.marker, vp, "write barrier");
}
void MarkStringFromJit(JSRuntime* rt, JSString** stringp) {
AutoUnsafeCallWithABI unsafe;
MOZ_ASSERT(*stringp);
TraceManuallyBarrieredEdge(&rt->gc.marker, stringp, "write barrier");
}
void MarkObjectFromJit(JSRuntime* rt, JSObject** objp) {
AutoUnsafeCallWithABI unsafe;
MOZ_ASSERT(*objp);
TraceManuallyBarrieredEdge(&rt->gc.marker, objp, "write barrier");
}
void MarkShapeFromJit(JSRuntime* rt, Shape** shapep) {
AutoUnsafeCallWithABI unsafe;
TraceManuallyBarrieredEdge(&rt->gc.marker, shapep, "write barrier");
}
void MarkObjectGroupFromJit(JSRuntime* rt, ObjectGroup** groupp) {
AutoUnsafeCallWithABI unsafe;
TraceManuallyBarrieredEdge(&rt->gc.marker, groupp, "write barrier");
}
bool ThrowRuntimeLexicalError(JSContext* cx, unsigned errorNumber) {
ScriptFrameIter iter(cx);
RootedScript script(cx, iter.script());
ReportRuntimeLexicalError(cx, errorNumber, script, iter.pc());
return false;
}
bool ThrowBadDerivedReturn(JSContext* cx, HandleValue v) {
ReportValueError(cx, JSMSG_BAD_DERIVED_RETURN, JSDVG_IGNORE_STACK, v,
nullptr);
return false;
}
bool BaselineThrowUninitializedThis(JSContext* cx, BaselineFrame* frame) {
return ThrowUninitializedThis(cx, frame);
}
bool BaselineThrowInitializedThis(JSContext* cx) {
return ThrowInitializedThis(cx);
}
bool ThrowObjectCoercible(JSContext* cx, HandleValue v) {
MOZ_ASSERT(v.isUndefined() || v.isNull());
MOZ_ALWAYS_FALSE(ToObjectSlow(cx, v, true));
return false;
}
bool BaselineGetFunctionThis(JSContext* cx, BaselineFrame* frame,
MutableHandleValue res) {
return GetFunctionThis(cx, frame, res);
}
bool CallNativeGetter(JSContext* cx, HandleFunction callee, HandleObject obj,
MutableHandleValue result) {
AutoRealm ar(cx, callee);
MOZ_ASSERT(callee->isNative());
JSNative natfun = callee->native();
JS::AutoValueArray<2> vp(cx);
vp[0].setObject(*callee.get());
vp[1].setObject(*obj.get());
if (!natfun(cx, 0, vp.begin())) {
return false;
}
result.set(vp[0]);
return true;
}
bool CallNativeSetter(JSContext* cx, HandleFunction callee, HandleObject obj,
HandleValue rhs) {
AutoRealm ar(cx, callee);
MOZ_ASSERT(callee->isNative());
JSNative natfun = callee->native();
JS::AutoValueArray<3> vp(cx);
vp[0].setObject(*callee.get());
vp[1].setObject(*obj.get());
vp[2].set(rhs);
return natfun(cx, 1, vp.begin());
}
bool EqualStringsHelperPure(JSString* str1, JSString* str2) {
AutoUnsafeCallWithABI unsafe;
MOZ_ASSERT(str1->isAtom());
MOZ_ASSERT(!str2->isAtom());
MOZ_ASSERT(str1->length() == str2->length());
JSLinearString* str2Linear = str2->ensureLinear(nullptr);
if (!str2Linear) {
return false;
}
return EqualChars(&str1->asLinear(), str2Linear);
}
bool CheckIsCallable(JSContext* cx, HandleValue v, CheckIsCallableKind kind) {
if (!IsCallable(v)) {
return ThrowCheckIsCallable(cx, kind);
}
return true;
}
template <bool HandleMissing>
static MOZ_ALWAYS_INLINE bool GetNativeDataPropertyPure(JSContext* cx,
NativeObject* obj,
jsid id, Value* vp) {
AutoUnsafeCallWithABI unsafe;
MOZ_ASSERT(JSID_IS_ATOM(id) || JSID_IS_SYMBOL(id));
while (true) {
if (Shape* shape = obj->lastProperty()->search(cx, id)) {
if (!shape->isDataProperty()) {
return false;
}
*vp = obj->getSlot(shape->slot());
return true;
}
if (MOZ_UNLIKELY(!obj->is<PlainObject>())) {
if (ClassMayResolveId(cx->names(), obj->getClass(), id, obj)) {
return false;
}
}
JSObject* proto = obj->staticPrototype();
if (!proto) {
if (HandleMissing) {
vp->setUndefined();
return true;
}
return false;
}
if (!proto->isNative()) {
return false;
}
obj = &proto->as<NativeObject>();
}
}
template <bool HandleMissing>
bool GetNativeDataPropertyPure(JSContext* cx, JSObject* obj, PropertyName* name,
Value* vp) {
MOZ_ASSERT(obj->isNative());
return GetNativeDataPropertyPure<HandleMissing>(cx, &obj->as<NativeObject>(),
NameToId(name), vp);
}
template bool GetNativeDataPropertyPure<true>(JSContext* cx, JSObject* obj,
PropertyName* name, Value* vp);
template bool GetNativeDataPropertyPure<false>(JSContext* cx, JSObject* obj,
PropertyName* name, Value* vp);
static MOZ_ALWAYS_INLINE bool ValueToAtomOrSymbolPure(JSContext* cx,
Value& idVal, jsid* id) {
if (MOZ_LIKELY(idVal.isString())) {
JSString* s = idVal.toString();
JSAtom* atom;
if (s->isAtom()) {
atom = &s->asAtom();
} else {
atom = AtomizeString(cx, s);
if (!atom) {
cx->recoverFromOutOfMemory();
return false;
}
}
*id = AtomToId(atom);
} else if (idVal.isSymbol()) {
*id = SYMBOL_TO_JSID(idVal.toSymbol());
} else {
if (!ValueToIdPure(idVal, id)) {
return false;
}
}
static_assert(NativeObject::MAX_DENSE_ELEMENTS_COUNT < JSID_INT_MAX,
"All dense elements must have integer jsids");
if (MOZ_UNLIKELY(JSID_IS_INT(*id))) {
return false;
}
return true;
}
template <bool HandleMissing>
bool GetNativeDataPropertyByValuePure(JSContext* cx, JSObject* obj, Value* vp) {
AutoUnsafeCallWithABI unsafe;
MOZ_ASSERT(obj->isNative());
Value idVal = vp[0];
jsid id;
if (!ValueToAtomOrSymbolPure(cx, idVal, &id)) {
return false;
}
Value* res = vp + 1;
return GetNativeDataPropertyPure<HandleMissing>(cx, &obj->as<NativeObject>(),
id, res);
}
template bool GetNativeDataPropertyByValuePure<true>(JSContext* cx,
JSObject* obj, Value* vp);
template bool GetNativeDataPropertyByValuePure<false>(JSContext* cx,
JSObject* obj, Value* vp);
template <bool NeedsTypeBarrier>
bool SetNativeDataPropertyPure(JSContext* cx, JSObject* obj, PropertyName* name,
Value* val) {
AutoUnsafeCallWithABI unsafe;
if (MOZ_UNLIKELY(!obj->isNative())) {
return false;
}
NativeObject* nobj = &obj->as<NativeObject>();
Shape* shape = nobj->lastProperty()->search(cx, NameToId(name));
if (!shape || !shape->isDataProperty() || !shape->writable()) {
return false;
}
if (NeedsTypeBarrier && !HasTypePropertyId(nobj, NameToId(name), *val)) {
return false;
}
nobj->setSlot(shape->slot(), *val);
return true;
}
template bool SetNativeDataPropertyPure<true>(JSContext* cx, JSObject* obj,
PropertyName* name, Value* val);
template bool SetNativeDataPropertyPure<false>(JSContext* cx, JSObject* obj,
PropertyName* name, Value* val);
bool ObjectHasGetterSetterPure(JSContext* cx, JSObject* objArg,
Shape* propShape) {
AutoUnsafeCallWithABI unsafe;
MOZ_ASSERT(propShape->hasGetterObject() || propShape->hasSetterObject());
if (MOZ_UNLIKELY(!objArg->isNative() || IsWindow(objArg))) {
return false;
}
NativeObject* nobj = &objArg->as<NativeObject>();
jsid id = propShape->propid();
while (true) {
if (Shape* shape = nobj->lastProperty()->search(cx, id)) {
if (shape == propShape) {
return true;
}
if (shape->getterOrUndefined() == propShape->getterOrUndefined() &&
shape->setterOrUndefined() == propShape->setterOrUndefined()) {
return true;
}
return false;
}
if (!nobj->is<PlainObject>()) {
if (ClassMayResolveId(cx->names(), nobj->getClass(), id, nobj)) {
return false;
}
}
JSObject* proto = nobj->staticPrototype();
if (!proto) {
return false;
}
if (!proto->isNative()) {
return false;
}
nobj = &proto->as<NativeObject>();
}
}
template <bool HasOwn>
bool HasNativeDataPropertyPure(JSContext* cx, JSObject* obj, Value* vp) {
AutoUnsafeCallWithABI unsafe;
Value idVal = vp[0];
jsid id;
if (!ValueToAtomOrSymbolPure(cx, idVal, &id)) {
return false;
}
do {
if (obj->isNative()) {
if (obj->as<NativeObject>().lastProperty()->search(cx, id)) {
vp[1].setBoolean(true);
return true;
}
if (MOZ_UNLIKELY(
ClassMayResolveId(cx->names(), obj->getClass(), id, obj))) {
return false;
}
} else if (obj->is<UnboxedPlainObject>()) {
if (obj->as<UnboxedPlainObject>().containsUnboxedOrExpandoProperty(cx,
id)) {
vp[1].setBoolean(true);
return true;
}
} else if (obj->is<TypedObject>()) {
if (obj->as<TypedObject>().typeDescr().hasProperty(cx->names(), id)) {
vp[1].setBoolean(true);
return true;
}
} else {
return false;
}
if (HasOwn) {
break;
}
obj = obj->staticPrototype();
} while (obj);
vp[1].setBoolean(false);
return true;
}
template bool HasNativeDataPropertyPure<true>(JSContext* cx, JSObject* obj,
Value* vp);
template bool HasNativeDataPropertyPure<false>(JSContext* cx, JSObject* obj,
Value* vp);
bool HasNativeElementPure(JSContext* cx, NativeObject* obj, int32_t index,
Value* vp) {
AutoUnsafeCallWithABI unsafe;
MOZ_ASSERT(obj->getClass()->isNative());
MOZ_ASSERT(!obj->getOpsHasProperty());
MOZ_ASSERT(!obj->getOpsLookupProperty());
MOZ_ASSERT(!obj->getOpsGetOwnPropertyDescriptor());
if (MOZ_UNLIKELY(index < 0)) {
return false;
}
if (obj->containsDenseElement(index)) {
vp[0].setBoolean(true);
return true;
}
jsid id = INT_TO_JSID(index);
if (obj->lastProperty()->search(cx, id)) {
vp[0].setBoolean(true);
return true;
}
if (MOZ_UNLIKELY(ClassMayResolveId(cx->names(), obj->getClass(), id, obj))) {
return false;
}
if (MOZ_UNLIKELY(obj->is<TypedArrayObject>())) {
vp[0].setBoolean(uint32_t(index) < obj->as<TypedArrayObject>().length());
return true;
}
vp[0].setBoolean(false);
return true;
}
JSString* TypeOfObject(JSObject* obj, JSRuntime* rt) {
AutoUnsafeCallWithABI unsafe;
JSType type = js::TypeOfObject(obj);
return TypeName(type, *rt->commonNames);
}
bool GetPrototypeOf(JSContext* cx, HandleObject target,
MutableHandleValue rval) {
MOZ_ASSERT(target->hasDynamicPrototype());
RootedObject proto(cx);
if (!GetPrototype(cx, target, &proto)) {
return false;
}
rval.setObjectOrNull(proto);
return true;
}
static JSString* ConvertObjectToStringForConcat(JSContext* cx,
HandleValue obj) {
MOZ_ASSERT(obj.isObject());
RootedValue rootedObj(cx, obj);
if (!ToPrimitive(cx, &rootedObj)) {
return nullptr;
}
return ToString<CanGC>(cx, rootedObj);
}
bool DoConcatStringObject(JSContext* cx, HandleValue lhs, HandleValue rhs,
MutableHandleValue res) {
JSString* lstr = nullptr;
JSString* rstr = nullptr;
if (lhs.isString()) {
MOZ_ASSERT(lhs.isString() && rhs.isObject());
rstr = ConvertObjectToStringForConcat(cx, rhs);
if (!rstr) {
return false;
}
lstr = lhs.toString();
} else {
MOZ_ASSERT(rhs.isString() && lhs.isObject());
lstr = ConvertObjectToStringForConcat(cx, lhs);
if (!lstr) {
return false;
}
rstr = rhs.toString();
}
JSString* str = ConcatStrings<NoGC>(cx, lstr, rstr);
if (!str) {
RootedString nlstr(cx, lstr), nrstr(cx, rstr);
str = ConcatStrings<CanGC>(cx, nlstr, nrstr);
if (!str) {
return false;
}
}
res.setString(str);
return true;
}
MOZ_MUST_USE bool TrySkipAwait(JSContext* cx, HandleValue val,
MutableHandleValue resolved) {
bool canSkip;
if (!TrySkipAwait(cx, val, &canSkip, resolved)) {
return false;
}
if (!canSkip) {
resolved.setMagic(JS_CANNOT_SKIP_AWAIT);
}
return true;
}
bool IsPossiblyWrappedTypedArray(JSContext* cx, JSObject* obj, bool* result) {
JSObject* unwrapped = CheckedUnwrapDynamic(obj, cx);
if (!unwrapped) {
ReportAccessDenied(cx);
return false;
}
*result = unwrapped->is<TypedArrayObject>();
return true;
}
bool DoToNumber(JSContext* cx, HandleValue arg, MutableHandleValue ret) {
ret.set(arg);
return ToNumber(cx, ret);
}
bool DoToNumeric(JSContext* cx, HandleValue arg, MutableHandleValue ret) {
ret.set(arg);
return ToNumeric(cx, ret);
}
bool CopyStringSplitArray(JSContext* cx, HandleArrayObject arr,
MutableHandleValue result) {
MOZ_ASSERT(arr->isTenured(),
"ConstStringSplit needs a tenured template object");
uint32_t length = arr->getDenseInitializedLength();
MOZ_ASSERT(length == arr->length(),
"template object is a fully initialized array");
ArrayObject* nobj = NewFullyAllocatedArrayTryReuseGroup(cx, arr, length);
if (!nobj) {
return false;
}
nobj->initDenseElements(arr, 0, length);
result.setObject(*nobj);
return true;
}
} }