// ES6 rev 24 (2014 Apr 27) 19.4.1.1 and 19.4.1.2 bool SymbolObject::construct(JSContext *cx, unsigned argc, Value *vp) { // According to a note in the draft standard, "Symbol has ordinary // [[Construct]] behaviour but the definition of its @@create method causes // `new Symbol` to throw a TypeError exception." We do not support @@create // yet, so just throw a TypeError. CallArgs args = CallArgsFromVp(argc, vp); if (args.isConstructing()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_CONSTRUCTOR, "Symbol"); return false; } // steps 1-3 RootedString desc(cx); if (!args.get(0).isUndefined()) { desc = ToString(cx, args.get(0)); if (!desc) return false; } // step 4 RootedSymbol symbol(cx, JS::Symbol::new_(cx, JS::SymbolCode::UniqueSymbol, desc)); if (!symbol) return false; args.rval().setSymbol(symbol); return true; }
bool SharedArrayBufferObject::class_constructor(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); if (!args.isConstructing()) { if (args.hasDefined(0)) { ESClassValue cls; if (!GetClassOfValue(cx, args[0], &cls)) return false; if (cls == ESClass_SharedArrayBuffer) { args.rval().set(args[0]); return true; } } JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_BAD_OBJECT); return false; } // Bugs 1068458, 1161298: Limit length to 2^31-1. uint32_t length; bool overflow_unused; if (!ToLengthClamped(cx, args.get(0), &length, &overflow_unused) || length > INT32_MAX) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_BAD_LENGTH); return false; } JSObject* bufobj = New(cx, length); if (!bufobj) return false; args.rval().setObject(*bufobj); return true; }
bool SharedArrayBufferObject::class_constructor(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (!args.isConstructing()) { if (args.hasDefined(0) && IsObjectWithClass(args[0], ESClass_SharedArrayBuffer, cx)) { args.rval().set(args[0]); return true; } JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_BAD_OBJECT); return false; } uint32_t length; bool overflow; if (!ToLengthClamped(cx, args.get(0), &length, &overflow)) { // Bug 1068458: Limit length to 2^31-1. if (overflow || length > INT32_MAX) JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_ARRAY_BAD_LENGTH); return false; } JSObject *bufobj = New(cx, length); if (!bufobj) return false; args.rval().setObject(*bufobj); return true; }
static bool HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, HandlePropertyName name) { if (cx->isExceptionPending()) return false; uint32_t begin = module.srcBodyStart(); // starts right after 'use asm' uint32_t end = module.srcEndBeforeCurly(); Rooted<JSFlatString*> src(cx, module.scriptSource()->substringDontDeflate(cx, begin, end)); if (!src) return false; RootedFunction fun(cx, NewScriptedFunction(cx, 0, JSFunction::INTERPRETED, name, JSFunction::FinalizeKind, TenuredObject)); if (!fun) return false; AutoNameVector formals(cx); if (!formals.reserve(3)) return false; if (module.globalArgumentName()) formals.infallibleAppend(module.globalArgumentName()); if (module.importArgumentName()) formals.infallibleAppend(module.importArgumentName()); if (module.bufferArgumentName()) formals.infallibleAppend(module.bufferArgumentName()); CompileOptions options(cx); options.setMutedErrors(module.scriptSource()->mutedErrors()) .setFile(module.scriptSource()->filename()) .setCompileAndGo(false) .setNoScriptRval(false); // The exported function inherits an implicit strict context if the module // also inherited it somehow. if (module.strict()) options.strictOption = true; AutoStableStringChars stableChars(cx); if (!stableChars.initTwoByte(cx, src)) return false; const char16_t *chars = stableChars.twoByteRange().start().get(); SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller() ? SourceBufferHolder::GiveOwnership : SourceBufferHolder::NoOwnership; SourceBufferHolder srcBuf(chars, end - begin, ownership); if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf, /* enclosingScope = */ NullPtr())) return false; // Call the function we just recompiled. args.setCallee(ObjectValue(*fun)); return Invoke(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT); }
bool js::intl_DateTimeFormat(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 2); MOZ_ASSERT(!args.isConstructing()); // intl_DateTimeFormat is an intrinsic for self-hosted JavaScript, so it // cannot be used with "new", but it still has to be treated as a // constructor. return DateTimeFormat(cx, args, true, DateTimeFormatOptions::Standard); }
bool js::proxy(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); if (!args.isConstructing()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_FUNCTION, "Proxy"); return false; } return NewScriptedProxy(cx, args, "Proxy"); }
static bool Boolean(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); bool b = args.length() != 0 ? JS::ToBoolean(args[0]) : false; if (args.isConstructing()) { JSObject *obj = BooleanObject::create(cx, b); if (!obj) return false; args.rval().setObject(*obj); } else { args.rval().setBoolean(b); } return true; }
static bool HandleDynamicLinkFailure(JSContext *cx, CallArgs args, AsmJSModule &module, HandlePropertyName name) { if (cx->isExceptionPending()) return false; uint32_t begin = module.offsetToEndOfUseAsm(); uint32_t end = module.funcEndBeforeCurly(); Rooted<JSFlatString*> src(cx, module.scriptSource()->substring(cx, begin, end)); if (!src) return false; RootedFunction fun(cx, NewFunction(cx, NullPtr(), nullptr, 0, JSFunction::INTERPRETED, cx->global(), name, JSFunction::FinalizeKind, TenuredObject)); if (!fun) return false; AutoNameVector formals(cx); formals.reserve(3); if (module.globalArgumentName()) formals.infallibleAppend(module.globalArgumentName()); if (module.importArgumentName()) formals.infallibleAppend(module.importArgumentName()); if (module.bufferArgumentName()) formals.infallibleAppend(module.bufferArgumentName()); CompileOptions options(cx); options.setOriginPrincipals(module.scriptSource()->originPrincipals()) .setFile(module.scriptSource()->filename()) .setCompileAndGo(false) .setNoScriptRval(false); // The exported function inherits an implicit strict context if the module // also inherited it somehow. if (module.strict()) options.strictOption = true; SourceBufferHolder srcBuf(src->chars(), end - begin, SourceBufferHolder::NoOwnership); if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf)) return false; // Call the function we just recompiled. args.setCallee(ObjectValue(*fun)); return Invoke(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT); }
bool WeakSetObject::construct(JSContext *cx, unsigned argc, Value *vp) { Rooted<WeakSetObject*> obj(cx, WeakSetObject::create(cx)); if (!obj) return false; // Based on our "Set" implementation instead of the more general ES6 steps. CallArgs args = CallArgsFromVp(argc, vp); if (!args.isConstructing()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_FUNCTION, "WeakSet"); return false; } if (args.hasDefined(0)) { RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject()); ForOfIterator iter(cx); if (!iter.init(args[0])) return false; RootedValue keyVal(cx); RootedObject keyObject(cx); RootedValue placeholder(cx, BooleanValue(true)); while (true) { bool done; if (!iter.next(&keyVal, &done)) return false; if (done) break; if (keyVal.isPrimitive()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT); return false; } keyObject = &keyVal.toObject(); if (!SetWeakMapEntry(cx, map, keyObject, placeholder)) return false; } } args.rval().setObject(*obj); return true; }
static bool regexp_construct(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (!args.isConstructing()) { /* * If first arg is regexp and no flags are given, just return the arg. * Otherwise, delegate to the standard constructor. * See ECMAv5 15.10.3.1. */ if (args.hasDefined(0) && IsObjectWithClass(args[0], ESClass_RegExp, cx) && !args.hasDefined(1)) { args.rval().set(args[0]); return true; } } RegExpObjectBuilder builder(cx); return CompileRegExpObject(cx, builder, args); }
bool js::regexp_construct_no_statics(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); MOZ_ASSERT(args.length() == 1 || args.length() == 2); MOZ_ASSERT(args[0].isString()); MOZ_ASSERT_IF(args.length() == 2, args[1].isString()); MOZ_ASSERT(!args.isConstructing()); /* Steps 1-6 are not required since pattern is always string. */ /* Steps 7-10. */ Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx)); if (!regexp) return false; if (!RegExpInitialize(cx, regexp, args[0], args.get(1), DontUseRegExpStatics)) return false; args.rval().setObject(*regexp); return true; }
/* * Shared{Type}Array(object) * * new Shared{Type}Array(length) * new Shared{Type}Array(SharedArrayBuffer, [optional] byteOffset, [optional] length) */ static bool class_constructor(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); if (!args.isConstructing()) { if (args.hasDefined(0) && args[0].isObject() && args[0].toObject().is<SharedTypedArrayObject>() && AnyTypedArrayType(&args[0].toObject()) == ArrayTypeID()) { args.rval().set(args[0]); return true; } JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SHARED_TYPED_ARRAY_BAD_LENGTH); return false; } JSObject *obj = create(cx, args); if (!obj) return false; args.rval().setObject(*obj); return true; }
bool js::obj_construct(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedObject obj(cx, nullptr); if (args.isConstructing() && (&args.newTarget().toObject() != &args.callee())) { RootedObject newTarget(cx, &args.newTarget().toObject()); obj = CreateThis(cx, &PlainObject::class_, newTarget); if (!obj) return false; } else if (args.length() > 0 && !args[0].isNullOrUndefined()) { obj = ToObject(cx, args[0]); if (!obj) return false; } else { /* Make an object whether this was called with 'new' or not. */ if (!NewObjectScriptedCall(cx, &obj)) return false; } args.rval().setObject(*obj); return true; }
bool Module::callExport(JSContext* cx, uint32_t exportIndex, CallArgs args) { MOZ_ASSERT(dynamicallyLinked_); const Export& exp = exports()[exportIndex]; // Enable/disable profiling in the Module to match the current global // profiling state. Don't do this if the Module is already active on the // stack since this would leave the Module in a state where profiling is // enabled but the stack isn't unwindable. if (profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !activation()) { if (!setProfilingEnabled(cx, cx->runtime()->spsProfiler.enabled())) return false; } // The calling convention for an external call into wasm is to pass an // array of 16-byte values where each value contains either a coerced int32 // (in the low word), a double value (in the low dword) or a SIMD vector // value, with the coercions specified by the wasm signature. The external // entry point unpacks this array into the system-ABI-specified registers // and stack memory and then calls into the internal entry point. The return // value is stored in the first element of the array (which, therefore, must // have length >= 1). Vector<Module::EntryArg, 8> coercedArgs(cx); if (!coercedArgs.resize(Max<size_t>(1, exp.sig().args().length()))) return false; RootedValue v(cx); for (unsigned i = 0; i < exp.sig().args().length(); ++i) { v = i < args.length() ? args[i] : UndefinedValue(); switch (exp.sig().arg(i)) { case ValType::I32: if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i])) return false; break; case ValType::I64: MOZ_CRASH("int64"); case ValType::F32: if (!RoundFloat32(cx, v, (float*)&coercedArgs[i])) return false; break; case ValType::F64: if (!ToNumber(cx, v, (double*)&coercedArgs[i])) return false; break; case ValType::I32x4: { SimdConstant simd; if (!ToSimdConstant<Int32x4>(cx, v, &simd)) return false; memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize); break; } case ValType::F32x4: { SimdConstant simd; if (!ToSimdConstant<Float32x4>(cx, v, &simd)) return false; memcpy(&coercedArgs[i], simd.asFloat32x4(), Simd128DataSize); break; } case ValType::B32x4: { SimdConstant simd; if (!ToSimdConstant<Bool32x4>(cx, v, &simd)) return false; // Bool32x4 uses the same representation as Int32x4. memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize); break; } case ValType::Limit: MOZ_CRASH("Limit"); } } { // Push a WasmActivation to describe the wasm frames we're about to push // when running this module. Additionally, push a JitActivation so that // the optimized wasm-to-Ion FFI call path (which we want to be very // fast) can avoid doing so. The JitActivation is marked as inactive so // stack iteration will skip over it. WasmActivation activation(cx, *this); JitActivation jitActivation(cx, /* active */ false); // Call the per-exported-function trampoline created by GenerateEntry. auto entry = JS_DATA_TO_FUNC_PTR(EntryFuncPtr, code() + exp.stubOffset()); if (!CALL_GENERATED_2(entry, coercedArgs.begin(), globalData())) return false; } if (args.isConstructing()) { // By spec, when a function is called as a constructor and this function // returns a primary type, which is the case for all wasm exported // functions, the returned value is discarded and an empty object is // returned instead. PlainObject* obj = NewBuiltinClassInstance<PlainObject>(cx); if (!obj) return false; args.rval().set(ObjectValue(*obj)); return true; } JSObject* simdObj; switch (exp.sig().ret()) { case ExprType::Void: args.rval().set(UndefinedValue()); break; case ExprType::I32: args.rval().set(Int32Value(*(int32_t*)&coercedArgs[0])); break; case ExprType::I64: MOZ_CRASH("int64"); case ExprType::F32: case ExprType::F64: args.rval().set(NumberValue(*(double*)&coercedArgs[0])); break; case ExprType::I32x4: simdObj = CreateSimd<Int32x4>(cx, (int32_t*)&coercedArgs[0]); if (!simdObj) return false; args.rval().set(ObjectValue(*simdObj)); break; case ExprType::F32x4: simdObj = CreateSimd<Float32x4>(cx, (float*)&coercedArgs[0]); if (!simdObj) return false; args.rval().set(ObjectValue(*simdObj)); break; case ExprType::B32x4: simdObj = CreateSimd<Bool32x4>(cx, (int32_t*)&coercedArgs[0]); if (!simdObj) return false; args.rval().set(ObjectValue(*simdObj)); break; case ExprType::Limit: MOZ_CRASH("Limit"); } return true; }
// The JSNative for the functions nested in an asm.js module. Calling this // native will trampoline into generated code. static bool CallAsmJS(JSContext *cx, unsigned argc, Value *vp) { CallArgs callArgs = CallArgsFromVp(argc, vp); RootedFunction callee(cx, &callArgs.callee().as<JSFunction>()); // An asm.js function stores, in its extended slots: // - a pointer to the module from which it was returned // - its index in the ordered list of exported functions AsmJSModule &module = FunctionToEnclosingModule(callee); // Enable/disable profiling in the asm.js module to match the current global // profiling state. Don't do this if the module is already active on the // stack since this would leave the module in a state where profiling is // enabled but the stack isn't unwindable. if (module.profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !module.active()) module.setProfilingEnabled(cx->runtime()->spsProfiler.enabled(), cx); // An exported function points to the code as well as the exported // function's signature, which implies the dynamic coercions performed on // the arguments. const AsmJSModule::ExportedFunction &func = FunctionToExportedFunction(callee, module); // The calling convention for an external call into asm.js is to pass an // array of 8-byte values where each value contains either a coerced int32 // (in the low word) or double value, with the coercions specified by the // asm.js signature. The external entry point unpacks this array into the // system-ABI-specified registers and stack memory and then calls into the // internal entry point. The return value is stored in the first element of // the array (which, therefore, must have length >= 1). js::Vector<uint64_t, 8> coercedArgs(cx); if (!coercedArgs.resize(Max<size_t>(1, func.numArgs()))) return false; RootedValue v(cx); for (unsigned i = 0; i < func.numArgs(); ++i) { v = i < callArgs.length() ? callArgs[i] : UndefinedValue(); switch (func.argCoercion(i)) { case AsmJS_ToInt32: if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i])) return false; break; case AsmJS_ToNumber: if (!ToNumber(cx, v, (double*)&coercedArgs[i])) return false; break; case AsmJS_FRound: if (!RoundFloat32(cx, v, (float *)&coercedArgs[i])) return false; break; } } // An asm.js module is specialized to its heap's base address and length // which is normally immutable except for the neuter operation that occurs // when an ArrayBuffer is transfered. Throw an internal error if we're // about to run with a neutered heap. if (module.maybeHeapBufferObject() && module.maybeHeapBufferObject()->isNeutered()) { js_ReportOverRecursed(cx); return false; } { // Push an AsmJSActivation to describe the asm.js frames we're about to // push when running this module. Additionally, push a JitActivation so // that the optimized asm.js-to-Ion FFI call path (which we want to be // very fast) can avoid doing so. The JitActivation is marked as // inactive so stack iteration will skip over it. AsmJSActivation activation(cx, module); JitActivation jitActivation(cx, /* firstFrameIsConstructing = */ false, /* active */ false); // Call the per-exported-function trampoline created by GenerateEntry. AsmJSModule::CodePtr enter = module.entryTrampoline(func); if (!CALL_GENERATED_ASMJS(enter, coercedArgs.begin(), module.globalData())) return false; } if (callArgs.isConstructing()) { // By spec, when a function is called as a constructor and this function // returns a primary type, which is the case for all asm.js exported // functions, the returned value is discarded and an empty object is // returned instead. JSObject *obj = NewBuiltinClassInstance(cx, &JSObject::class_); callArgs.rval().set(ObjectValue(*obj)); return true; } switch (func.returnType()) { case AsmJSModule::Return_Void: callArgs.rval().set(UndefinedValue()); break; case AsmJSModule::Return_Int32: callArgs.rval().set(Int32Value(*(int32_t*)&coercedArgs[0])); break; case AsmJSModule::Return_Double: callArgs.rval().set(NumberValue(*(double*)&coercedArgs[0])); break; } return true; }
static bool DateTimeFormat(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); return DateTimeFormat(cx, args, args.isConstructing(), DateTimeFormatOptions::Standard); }
bool WeakSetObject::construct(JSContext* cx, unsigned argc, Value* vp) { Rooted<WeakSetObject*> obj(cx, WeakSetObject::create(cx)); if (!obj) return false; // Based on our "Set" implementation instead of the more general ES6 steps. CallArgs args = CallArgsFromVp(argc, vp); if (!args.isConstructing()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_FUNCTION, "WeakSet"); return false; } if (!args.get(0).isNullOrUndefined()) { RootedObject map(cx, &obj->getReservedSlot(WEAKSET_MAP_SLOT).toObject()); RootedValue adderVal(cx); if (!GetProperty(cx, obj, obj, cx->names().add, &adderVal)) return false; if (!IsCallable(adderVal)) return ReportIsNotFunction(cx, adderVal); JSFunction* adder; bool isOriginalAdder = IsFunctionObject(adderVal, &adder) && IsSelfHostedFunctionWithName(adder, cx->names().WeakSet_add); RootedValue setVal(cx, ObjectValue(*obj)); FastInvokeGuard fig(cx, adderVal); InvokeArgs& args2 = fig.args(); JS::ForOfIterator iter(cx); if (!iter.init(args[0])) return false; RootedValue keyVal(cx); RootedObject keyObject(cx); RootedValue placeholder(cx, BooleanValue(true)); while (true) { bool done; if (!iter.next(&keyVal, &done)) return false; if (done) break; if (isOriginalAdder) { if (keyVal.isPrimitive()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT); return false; } keyObject = &keyVal.toObject(); if (!SetWeakMapEntry(cx, map, keyObject, placeholder)) return false; } else { if (!args2.init(1)) return false; args2.setCallee(adderVal); args2.setThis(setVal); args2[0].set(keyVal); if (!fig.invoke(cx)) return false; } } } args.rval().setObject(*obj); return true; }
/* ES6 21.2.3.1. */ bool js::regexp_construct(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); // Steps 1-2. bool patternIsRegExp; if (!IsRegExp(cx, args.get(0), &patternIsRegExp)) return false; // We can delay step 3 and step 4a until later, during // GetPrototypeFromCallableConstructor calls. Accessing the new.target // and the callee from the stack is unobservable. if (!args.isConstructing()) { // Step 4b. if (patternIsRegExp && !args.hasDefined(1)) { RootedObject patternObj(cx, &args[0].toObject()); // Steps 4b.i-ii. RootedValue patternConstructor(cx); if (!GetProperty(cx, patternObj, patternObj, cx->names().constructor, &patternConstructor)) return false; // Step 4b.iii. if (patternConstructor.isObject() && patternConstructor.toObject() == args.callee()) { args.rval().set(args[0]); return true; } } } RootedValue patternValue(cx, args.get(0)); // Step 5. ESClassValue cls; if (!GetClassOfValue(cx, patternValue, &cls)) return false; if (cls == ESClass_RegExp) { // Beware! |patternObj| might be a proxy into another compartment, so // don't assume |patternObj.is<RegExpObject>()|. For the same reason, // don't reuse the RegExpShared below. RootedObject patternObj(cx, &patternValue.toObject()); // Step 5 RootedAtom sourceAtom(cx); RegExpFlag flags; { // Step 5.a. RegExpGuard g(cx); if (!RegExpToShared(cx, patternObj, &g)) return false; sourceAtom = g->getSource(); if (!args.hasDefined(1)) { // Step 5b. flags = g->getFlags(); } } // Steps 8-9. RootedObject proto(cx); if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) return false; Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, proto)); if (!regexp) return false; // Step 10. if (args.hasDefined(1)) { // Step 5c / 21.2.3.2.2 RegExpInitialize step 5. flags = RegExpFlag(0); RootedString flagStr(cx, ToString<CanGC>(cx, args[1])); if (!flagStr) return false; if (!ParseRegExpFlags(cx, flagStr, &flags)) return false; } if (!RegExpObject::initFromAtom(cx, regexp, sourceAtom, flags)) return false; args.rval().setObject(*regexp); return true; } RootedValue P(cx); RootedValue F(cx); // Step 6. if (patternIsRegExp) { RootedObject patternObj(cx, &patternValue.toObject()); // Steps 6a-b. if (!GetProperty(cx, patternObj, patternObj, cx->names().source, &P)) return false; // Steps 6c-d. F = args.get(1); if (F.isUndefined()) { if (!GetProperty(cx, patternObj, patternObj, cx->names().flags, &F)) return false; } } else { // Steps 7a-b. P = patternValue; F = args.get(1); } // Steps 8-9. RootedObject proto(cx); if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) return false; Rooted<RegExpObject*> regexp(cx, RegExpAlloc(cx, proto)); if (!regexp) return false; // Step 10. if (!RegExpInitialize(cx, regexp, P, F, UseRegExpStatics)) return false; args.rval().setObject(*regexp); return true; }
// This is the js::Native for functions exported by an asm.js module. static bool CallAsmJS(JSContext *cx, unsigned argc, Value *vp) { CallArgs callArgs = CallArgsFromVp(argc, vp); RootedFunction callee(cx, &callArgs.callee().as<JSFunction>()); AsmJSModule &module = FunctionToEnclosingModule(callee); const AsmJSModule::ExportedFunction &func = FunctionToExportedFunction(callee, module); // The heap-changing function is a special-case and is implemented by C++. if (func.isChangeHeap()) return ChangeHeap(cx, module, callArgs); // Enable/disable profiling in the asm.js module to match the current global // profiling state. Don't do this if the module is already active on the // stack since this would leave the module in a state where profiling is // enabled but the stack isn't unwindable. if (module.profilingEnabled() != cx->runtime()->spsProfiler.enabled() && !module.active()) module.setProfilingEnabled(cx->runtime()->spsProfiler.enabled(), cx); // The calling convention for an external call into asm.js is to pass an // array of 16-byte values where each value contains either a coerced int32 // (in the low word), a double value (in the low dword) or a SIMD vector // value, with the coercions specified by the asm.js signature. The // external entry point unpacks this array into the system-ABI-specified // registers and stack memory and then calls into the internal entry point. // The return value is stored in the first element of the array (which, // therefore, must have length >= 1). js::Vector<AsmJSModule::EntryArg, 8> coercedArgs(cx); if (!coercedArgs.resize(Max<size_t>(1, func.numArgs()))) return false; RootedValue v(cx); for (unsigned i = 0; i < func.numArgs(); ++i) { v = i < callArgs.length() ? callArgs[i] : UndefinedValue(); switch (func.argCoercion(i)) { case AsmJS_ToInt32: if (!ToInt32(cx, v, (int32_t*)&coercedArgs[i])) return false; break; case AsmJS_ToNumber: if (!ToNumber(cx, v, (double*)&coercedArgs[i])) return false; break; case AsmJS_FRound: if (!RoundFloat32(cx, v, (float *)&coercedArgs[i])) return false; break; case AsmJS_ToInt32x4: { SimdConstant simd; if (!ToSimdConstant<Int32x4>(cx, v, &simd)) return false; memcpy(&coercedArgs[i], simd.asInt32x4(), Simd128DataSize); break; } case AsmJS_ToFloat32x4: { SimdConstant simd; if (!ToSimdConstant<Float32x4>(cx, v, &simd)) return false; memcpy(&coercedArgs[i], simd.asFloat32x4(), Simd128DataSize); break; } } } // The correct way to handle this situation would be to allocate a new range // of PROT_NONE memory and module.changeHeap to this memory. That would // cause every access to take the out-of-bounds signal-handler path which // does the right thing. For now, just throw an out-of-memory exception // since these can technically pop out anywhere and the full fix may // actually OOM when trying to allocate the PROT_NONE memory. if (module.hasDetachedHeap()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_OUT_OF_MEMORY); return false; } { // Push an AsmJSActivation to describe the asm.js frames we're about to // push when running this module. Additionally, push a JitActivation so // that the optimized asm.js-to-Ion FFI call path (which we want to be // very fast) can avoid doing so. The JitActivation is marked as // inactive so stack iteration will skip over it. AsmJSActivation activation(cx, module); JitActivation jitActivation(cx, /* active */ false); // Call the per-exported-function trampoline created by GenerateEntry. AsmJSModule::CodePtr enter = module.entryTrampoline(func); if (!CALL_GENERATED_2(enter, coercedArgs.begin(), module.globalData())) return false; } if (callArgs.isConstructing()) { // By spec, when a function is called as a constructor and this function // returns a primary type, which is the case for all asm.js exported // functions, the returned value is discarded and an empty object is // returned instead. PlainObject *obj = NewBuiltinClassInstance<PlainObject>(cx); callArgs.rval().set(ObjectValue(*obj)); return true; } JSObject *simdObj; switch (func.returnType()) { case AsmJSModule::Return_Void: callArgs.rval().set(UndefinedValue()); break; case AsmJSModule::Return_Int32: callArgs.rval().set(Int32Value(*(int32_t*)&coercedArgs[0])); break; case AsmJSModule::Return_Double: callArgs.rval().set(NumberValue(*(double*)&coercedArgs[0])); break; case AsmJSModule::Return_Int32x4: simdObj = CreateSimd<Int32x4>(cx, (int32_t*)&coercedArgs[0]); if (!simdObj) return false; callArgs.rval().set(ObjectValue(*simdObj)); break; case AsmJSModule::Return_Float32x4: simdObj = CreateSimd<Float32x4>(cx, (float*)&coercedArgs[0]); if (!simdObj) return false; callArgs.rval().set(ObjectValue(*simdObj)); break; } return true; }
static bool HandleDynamicLinkFailure(JSContext* cx, const CallArgs& args, AsmJSModule& module, HandlePropertyName name) { if (cx->isExceptionPending()) return false; // Source discarding is allowed to affect JS semantics because it is never // enabled for normal JS content. bool haveSource = module.scriptSource()->hasSourceData(); if (!haveSource && !JSScript::loadSource(cx, module.scriptSource(), &haveSource)) return false; if (!haveSource) { JS_ReportError(cx, "asm.js link failure with source discarding enabled"); return false; } uint32_t begin = module.srcBodyStart(); // starts right after 'use asm' uint32_t end = module.srcEndBeforeCurly(); Rooted<JSFlatString*> src(cx, module.scriptSource()->substringDontDeflate(cx, begin, end)); if (!src) return false; RootedFunction fun(cx, NewScriptedFunction(cx, 0, JSFunction::INTERPRETED_NORMAL, name, gc::AllocKind::FUNCTION, TenuredObject)); if (!fun) return false; Rooted<PropertyNameVector> formals(cx, PropertyNameVector(cx)); if (!formals.reserve(3)) return false; if (module.globalArgumentName()) formals.infallibleAppend(module.globalArgumentName()); if (module.importArgumentName()) formals.infallibleAppend(module.importArgumentName()); if (module.bufferArgumentName()) formals.infallibleAppend(module.bufferArgumentName()); CompileOptions options(cx); options.setMutedErrors(module.scriptSource()->mutedErrors()) .setFile(module.scriptSource()->filename()) .setNoScriptRval(false); // The exported function inherits an implicit strict context if the module // also inherited it somehow. if (module.strict()) options.strictOption = true; AutoStableStringChars stableChars(cx); if (!stableChars.initTwoByte(cx, src)) return false; const char16_t* chars = stableChars.twoByteRange().start().get(); SourceBufferHolder::Ownership ownership = stableChars.maybeGiveOwnershipToCaller() ? SourceBufferHolder::GiveOwnership : SourceBufferHolder::NoOwnership; SourceBufferHolder srcBuf(chars, end - begin, ownership); if (!frontend::CompileFunctionBody(cx, &fun, options, formals, srcBuf, /* enclosingScope = */ nullptr)) return false; // Call the function we just recompiled. args.setCallee(ObjectValue(*fun)); return Invoke(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT); }
bool Instance::callExport(JSContext* cx, uint32_t funcIndex, CallArgs args) { if (!cx->compartment()->wasm.ensureProfilingState(cx)) return false; const FuncExport& func = metadata().lookupFuncExport(funcIndex); // The calling convention for an external call into wasm is to pass an // array of 16-byte values where each value contains either a coerced int32 // (in the low word), a double value (in the low dword) or a SIMD vector // value, with the coercions specified by the wasm signature. The external // entry point unpacks this array into the system-ABI-specified registers // and stack memory and then calls into the internal entry point. The return // value is stored in the first element of the array (which, therefore, must // have length >= 1). Vector<ExportArg, 8> exportArgs(cx); if (!exportArgs.resize(Max<size_t>(1, func.sig().args().length()))) return false; RootedValue v(cx); for (unsigned i = 0; i < func.sig().args().length(); ++i) { v = i < args.length() ? args[i] : UndefinedValue(); switch (func.sig().arg(i)) { case ValType::I32: if (!ToInt32(cx, v, (int32_t*)&exportArgs[i])) return false; break; case ValType::I64: if (!JitOptions.wasmTestMode) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64); return false; } if (!ReadI64Object(cx, v, (int64_t*)&exportArgs[i])) return false; break; case ValType::F32: if (JitOptions.wasmTestMode && v.isObject()) { if (!ReadCustomFloat32NaNObject(cx, v, (float*)&exportArgs[i])) return false; break; } if (!RoundFloat32(cx, v, (float*)&exportArgs[i])) return false; break; case ValType::F64: if (JitOptions.wasmTestMode && v.isObject()) { if (!ReadCustomDoubleNaNObject(cx, v, (double*)&exportArgs[i])) return false; break; } if (!ToNumber(cx, v, (double*)&exportArgs[i])) return false; break; case ValType::I8x16: { SimdConstant simd; if (!ToSimdConstant<Int8x16>(cx, v, &simd)) return false; memcpy(&exportArgs[i], simd.asInt8x16(), Simd128DataSize); break; } case ValType::I16x8: { SimdConstant simd; if (!ToSimdConstant<Int16x8>(cx, v, &simd)) return false; memcpy(&exportArgs[i], simd.asInt16x8(), Simd128DataSize); break; } case ValType::I32x4: { SimdConstant simd; if (!ToSimdConstant<Int32x4>(cx, v, &simd)) return false; memcpy(&exportArgs[i], simd.asInt32x4(), Simd128DataSize); break; } case ValType::F32x4: { SimdConstant simd; if (!ToSimdConstant<Float32x4>(cx, v, &simd)) return false; memcpy(&exportArgs[i], simd.asFloat32x4(), Simd128DataSize); break; } case ValType::B8x16: { SimdConstant simd; if (!ToSimdConstant<Bool8x16>(cx, v, &simd)) return false; // Bool8x16 uses the same representation as Int8x16. memcpy(&exportArgs[i], simd.asInt8x16(), Simd128DataSize); break; } case ValType::B16x8: { SimdConstant simd; if (!ToSimdConstant<Bool16x8>(cx, v, &simd)) return false; // Bool16x8 uses the same representation as Int16x8. memcpy(&exportArgs[i], simd.asInt16x8(), Simd128DataSize); break; } case ValType::B32x4: { SimdConstant simd; if (!ToSimdConstant<Bool32x4>(cx, v, &simd)) return false; // Bool32x4 uses the same representation as Int32x4. memcpy(&exportArgs[i], simd.asInt32x4(), Simd128DataSize); break; } case ValType::Limit: MOZ_CRASH("Limit"); } } { // Push a WasmActivation to describe the wasm frames we're about to push // when running this module. Additionally, push a JitActivation so that // the optimized wasm-to-Ion FFI call path (which we want to be very // fast) can avoid doing so. The JitActivation is marked as inactive so // stack iteration will skip over it. WasmActivation activation(cx); JitActivation jitActivation(cx, /* active */ false); // Call the per-exported-function trampoline created by GenerateEntry. auto funcPtr = JS_DATA_TO_FUNC_PTR(ExportFuncPtr, codeBase() + func.entryOffset()); if (!CALL_GENERATED_2(funcPtr, exportArgs.begin(), &tlsData_)) return false; } if (args.isConstructing()) { // By spec, when a function is called as a constructor and this function // returns a primary type, which is the case for all wasm exported // functions, the returned value is discarded and an empty object is // returned instead. PlainObject* obj = NewBuiltinClassInstance<PlainObject>(cx); if (!obj) return false; args.rval().set(ObjectValue(*obj)); return true; } void* retAddr = &exportArgs[0]; JSObject* retObj = nullptr; switch (func.sig().ret()) { case ExprType::Void: args.rval().set(UndefinedValue()); break; case ExprType::I32: args.rval().set(Int32Value(*(int32_t*)retAddr)); break; case ExprType::I64: if (!JitOptions.wasmTestMode) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_I64); return false; } retObj = CreateI64Object(cx, *(int64_t*)retAddr); if (!retObj) return false; break; case ExprType::F32: if (JitOptions.wasmTestMode && IsNaN(*(float*)retAddr)) { retObj = CreateCustomNaNObject(cx, (float*)retAddr); if (!retObj) return false; break; } args.rval().set(NumberValue(*(float*)retAddr)); break; case ExprType::F64: if (JitOptions.wasmTestMode && IsNaN(*(double*)retAddr)) { retObj = CreateCustomNaNObject(cx, (double*)retAddr); if (!retObj) return false; break; } args.rval().set(NumberValue(*(double*)retAddr)); break; case ExprType::I8x16: retObj = CreateSimd<Int8x16>(cx, (int8_t*)retAddr); if (!retObj) return false; break; case ExprType::I16x8: retObj = CreateSimd<Int16x8>(cx, (int16_t*)retAddr); if (!retObj) return false; break; case ExprType::I32x4: retObj = CreateSimd<Int32x4>(cx, (int32_t*)retAddr); if (!retObj) return false; break; case ExprType::F32x4: retObj = CreateSimd<Float32x4>(cx, (float*)retAddr); if (!retObj) return false; break; case ExprType::B8x16: retObj = CreateSimd<Bool8x16>(cx, (int8_t*)retAddr); if (!retObj) return false; break; case ExprType::B16x8: retObj = CreateSimd<Bool16x8>(cx, (int16_t*)retAddr); if (!retObj) return false; break; case ExprType::B32x4: retObj = CreateSimd<Bool32x4>(cx, (int32_t*)retAddr); if (!retObj) return false; break; case ExprType::Limit: MOZ_CRASH("Limit"); } if (retObj) args.rval().set(ObjectValue(*retObj)); return true; }