/* Steps 4-5. */ JSString *str = js::obj_toStringHelper(cx, obj); if (!str) return false; args.rval().setString(str); return true; } /* ES5 15.2.4.3. */ static JSBool obj_toLocaleString(JSContext *cx, unsigned argc, Value *vp) { JS_CHECK_RECURSION(cx, return false); CallArgs args = CallArgsFromVp(argc, vp); /* Step 1. */ JSObject *obj = ToObject(cx, args.thisv()); if (!obj) return false; /* Steps 2-4. */ RootedId id(cx, NameToId(cx->names().toString)); return obj->callMethod(cx, id, 0, NULL, args.rval()); } static JSBool obj_valueOf(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp);
bool MapObject::iterator_impl(JSContext* cx, const CallArgs& args, IteratorKind kind) { RootedObject obj(cx, &args.thisv().toObject()); return iterator(cx, kind, obj, args.rval()); }
static JSBool Exception(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); /* * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when * called as functions, without operator new. But as we do not give * each constructor a distinct JSClass, whose .name member is used by * NewNativeClassInstance to find the class prototype, we must get the * class prototype ourselves. */ Value protov; if (!args.callee().getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov)) return false; if (!protov.isObject()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_PROTOTYPE, "Error"); return false; } JSObject *errProto = &protov.toObject(); RootedVarObject obj(cx, NewObjectWithGivenProto(cx, &ErrorClass, errProto, NULL)); if (!obj) return false; /* Set the 'message' property. */ RootedVarString message(cx); if (args.hasDefined(0)) { message = ToString(cx, args[0]); if (!message) return false; args[0].setString(message); } else { message = NULL; } /* Find the scripted caller. */ ScriptFrameIter iter(cx); /* Set the 'fileName' property. */ RootedVarString filename(cx); if (args.length() > 1) { filename = ToString(cx, args[1]); if (!filename) return false; args[1].setString(filename); } else { if (!iter.done()) { filename = FilenameToString(cx, iter.script()->filename); if (!filename) return false; } else { filename = cx->runtime->emptyString; } } /* Set the 'lineNumber' property. */ uint32_t lineno; if (args.length() > 2) { if (!ToUint32(cx, args[2], &lineno)) return false; } else { lineno = iter.done() ? 0 : PCToLineNumber(iter.script(), iter.pc()); } int exnType = args.callee().toFunction()->getExtendedSlot(0).toInt32(); if (!InitExnPrivate(cx, obj, message, filename, lineno, NULL, exnType)) 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; }
bool MapObject::construct(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); if (!ThrowIfNotConstructing(cx, args, "Map")) return false; RootedObject proto(cx); RootedObject newTarget(cx, &args.newTarget().toObject()); if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) return false; Rooted<MapObject*> obj(cx, MapObject::create(cx, proto)); if (!obj) return false; if (!args.get(0).isNullOrUndefined()) { RootedValue adderVal(cx); if (!GetProperty(cx, obj, obj, cx->names().set, &adderVal)) return false; if (!IsCallable(adderVal)) return ReportIsNotFunction(cx, adderVal); bool isOriginalAdder = IsNativeFunction(adderVal, MapObject::set); RootedValue mapVal(cx, ObjectValue(*obj)); FastInvokeGuard fig(cx, adderVal); InvokeArgs& args2 = fig.args(); ForOfIterator iter(cx); if (!iter.init(args[0])) return false; RootedValue pairVal(cx); RootedObject pairObj(cx); Rooted<HashableValue> hkey(cx); ValueMap* map = obj->getData(); while (true) { bool done; if (!iter.next(&pairVal, &done)) return false; if (done) break; if (!pairVal.isObject()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INVALID_MAP_ITERABLE, "Map"); return false; } pairObj = &pairVal.toObject(); if (!pairObj) return false; RootedValue key(cx); if (!GetElement(cx, pairObj, pairObj, 0, &key)) return false; RootedValue val(cx); if (!GetElement(cx, pairObj, pairObj, 1, &val)) return false; if (isOriginalAdder) { if (!hkey.setValue(cx, key)) return false; RelocatableValue rval(val); if (!map->put(hkey, rval)) { ReportOutOfMemory(cx); return false; } WriteBarrierPost(cx->runtime(), map, key); } else { if (!args2.init(cx, 2)) return false; args2.setCallee(adderVal); args2.setThis(mapVal); args2[0].set(key); args2[1].set(val); if (!fig.invoke(cx)) return false; } } } args.rval().setObject(*obj); return true; }
static bool ProtoSetterImpl(JSContext *cx, CallArgs args) { JS_ASSERT(TestProtoSetterThis(args.thisv())); const Value &thisv = args.thisv(); if (thisv.isPrimitive()) { JS_ASSERT(!thisv.isNullOrUndefined()); // Mutating a boxed primitive's [[Prototype]] has no side effects. args.rval().setUndefined(); return true; } if (!cx->runningWithTrustedPrincipals()) ++sSetProtoCalled; Rooted<JSObject*> obj(cx, &args.thisv().toObject()); /* ES5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */ bool extensible; if (!JSObject::isExtensible(cx, obj, &extensible)) return false; if (!extensible) { obj->reportNotExtensible(cx); return false; } /* * Disallow mutating the [[Prototype]] of a proxy that wasn't simply * wrapping some other object. Also disallow it on ArrayBuffer objects, * which due to their complicated delegate-object shenanigans can't easily * have a mutable [[Prototype]]. */ if (obj->is<ProxyObject>() || obj->is<ArrayBufferObject>()) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INCOMPATIBLE_PROTO, "Object", "__proto__ setter", obj->is<ProxyObject>() ? "Proxy" : "ArrayBuffer"); return false; } /* Do nothing if __proto__ isn't being set to an object or null. */ if (args.length() == 0 || !args[0].isObjectOrNull()) { args.rval().setUndefined(); return true; } Rooted<JSObject*> newProto(cx, args[0].toObjectOrNull()); unsigned dummy; RootedId nid(cx, NameToId(cx->names().proto)); RootedValue v(cx); if (!CheckAccess(cx, obj, nid, JSAccessMode(JSACC_PROTO | JSACC_WRITE), &v, &dummy)) return false; if (!SetClassAndProto(cx, obj, obj->getClass(), newProto, true)) return false; args.rval().setUndefined(); return true; }
js::ForwardToNative(JSContext* cx, JSNative native, const CallArgs& args) { return native(cx, args.length(), args.base()); }
/* Steps 4-5. */ JSString *str = JS_BasicObjectToString(cx, obj); if (!str) return false; args.rval().setString(str); return true; } /* ES5 15.2.4.3. */ static bool obj_toLocaleString(JSContext *cx, unsigned argc, Value *vp) { JS_CHECK_RECURSION(cx, return false); CallArgs args = CallArgsFromVp(argc, vp); /* Step 1. */ RootedObject obj(cx, ToObject(cx, args.thisv())); if (!obj) return false; /* Steps 2-4. */ RootedId id(cx, NameToId(cx->names().toString)); return obj->callMethod(cx, id, 0, NULL, args.rval()); } static bool obj_valueOf(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp);
// Common code implementing direct and indirect eval. // // Evaluate call.argv[2], if it is a string, in the context of the given calling // frame, with the provided scope chain, with the semantics of either a direct // or indirect eval (see ES5 10.4.2). If this is an indirect eval, scopeobj // must be a global object. // // On success, store the completion value in call.rval and return true. static bool EvalKernel(JSContext* cx, const CallArgs& args, EvalType evalType, AbstractFramePtr caller, HandleObject scopeobj, jsbytecode* pc) { MOZ_ASSERT((evalType == INDIRECT_EVAL) == !caller); MOZ_ASSERT((evalType == INDIRECT_EVAL) == !pc); MOZ_ASSERT_IF(evalType == INDIRECT_EVAL, IsGlobalLexicalScope(scopeobj)); AssertInnerizedScopeChain(cx, *scopeobj); Rooted<GlobalObject*> scopeObjGlobal(cx, &scopeobj->global()); if (!GlobalObject::isRuntimeCodeGenEnabled(cx, scopeObjGlobal)) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL); return false; } // ES5 15.1.2.1 step 1. if (args.length() < 1) { args.rval().setUndefined(); return true; } if (!args[0].isString()) { args.rval().set(args[0]); return true; } RootedString str(cx, args[0].toString()); // ES5 15.1.2.1 steps 2-8. // Per ES5, indirect eval runs in the global scope. (eval is specified this // way so that the compiler can make assumptions about what bindings may or // may not exist in the current frame if it doesn't see 'eval'.) MOZ_ASSERT_IF(evalType != DIRECT_EVAL, args.callee().global() == scopeobj->as<ClonedBlockObject>().global()); RootedLinearString linearStr(cx, str->ensureLinear(cx)); if (!linearStr) return false; RootedScript callerScript(cx, caller ? caller.script() : nullptr); EvalJSONResult ejr = TryEvalJSON(cx, linearStr, args.rval()); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); if (evalType == DIRECT_EVAL && caller.isFunctionFrame()) esg.lookupInEvalCache(linearStr, callerScript, pc); if (!esg.foundScript()) { RootedScript maybeScript(cx); unsigned lineno; const char* filename; bool mutedErrors; uint32_t pcOffset; DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset, &mutedErrors, evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL : NOT_CALLED_FROM_JSOP_EVAL); const char* introducerFilename = filename; if (maybeScript && maybeScript->scriptSource()->introducerFilename()) introducerFilename = maybeScript->scriptSource()->introducerFilename(); RootedObject enclosing(cx); if (evalType == DIRECT_EVAL) enclosing = callerScript->innermostStaticScope(pc); else enclosing = &cx->global()->lexicalScope().staticBlock(); Rooted<StaticEvalObject*> staticScope(cx, StaticEvalObject::create(cx, enclosing)); if (!staticScope) return false; CompileOptions options(cx); options.setFileAndLine(filename, 1) .setIsRunOnce(true) .setForEval(true) .setNoScriptRval(false) .setMutedErrors(mutedErrors) .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset) .maybeMakeStrictMode(evalType == DIRECT_EVAL && IsStrictEvalPC(pc)); AutoStableStringChars linearChars(cx); if (!linearChars.initTwoByte(cx, linearStr)) return false; const char16_t* chars = linearChars.twoByteRange().start().get(); SourceBufferHolder::Ownership ownership = linearChars.maybeGiveOwnershipToCaller() ? SourceBufferHolder::GiveOwnership : SourceBufferHolder::NoOwnership; SourceBufferHolder srcBuf(chars, linearStr->length(), ownership); JSScript* compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(), scopeobj, staticScope, callerScript, options, srcBuf, linearStr); if (!compiled) return false; if (compiled->strict()) staticScope->setStrict(); esg.setNewScript(compiled); } // Look up the newTarget from the frame iterator. Value newTargetVal = NullValue(); return ExecuteKernel(cx, esg.script(), *scopeobj, newTargetVal, ExecuteType(evalType), NullFramePtr() /* evalInFrame */, args.rval().address()); }
static JSBool CallAsmJS(JSContext *cx, unsigned argc, Value *vp) { CallArgs callArgs = CallArgsFromVp(argc, vp); RootedFunction callee(cx, callArgs.callee().toFunction()); // 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 RootedObject moduleObj(cx, &callee->getExtendedSlot(ASM_MODULE_SLOT).toObject()); const AsmJSModule &module = AsmJSModuleObjectToModule(moduleObj); // An exported function points to the code as well as the exported // function's signature, which implies the dynamic coercions performed on // the arguments. unsigned exportIndex = callee->getExtendedSlot(ASM_EXPORT_INDEX_SLOT).toInt32(); const AsmJSModule::ExportedFunction &func = module.exportedFunction(exportIndex); // 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). 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; } } { AsmJSActivation activation(cx, module); // Call into generated code. #ifdef JS_CPU_ARM if (!func.code()(coercedArgs.begin(), module.globalData())) return false; #else if (!func.code()(coercedArgs.begin())) return false; #endif } 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; }
bool js::math_hypot(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); return math_hypot_handle(cx, args, args.rval()); }
static bool DynamicallyLinkModule(JSContext *cx, CallArgs args, AsmJSModule &module) { if (module.isLinked()) return LinkFail(cx, "As a temporary limitation, modules cannot be linked more than " "once. This limitation should be removed in a future release. To " "work around this, compile a second module (e.g., using the " "Function constructor)."); RootedValue globalVal(cx, UndefinedValue()); if (args.length() > 0) globalVal = args[0]; RootedValue importVal(cx, UndefinedValue()); if (args.length() > 1) importVal = args[1]; RootedValue bufferVal(cx, UndefinedValue()); if (args.length() > 2) bufferVal = args[2]; Rooted<ArrayBufferObject*> heap(cx); if (module.hasArrayView()) { if (!IsTypedArrayBuffer(bufferVal)) return LinkFail(cx, "bad ArrayBuffer argument"); heap = &bufferVal.toObject().asArrayBuffer(); if (!IsPowerOfTwo(heap->byteLength()) || heap->byteLength() < AsmJSAllocationGranularity) return LinkFail(cx, "ArrayBuffer byteLength must be a power of two greater than or equal to 4096"); if (!ArrayBufferObject::prepareForAsmJS(cx, heap)) return LinkFail(cx, "Unable to prepare ArrayBuffer for asm.js use"); #if defined(JS_CPU_X86) void *heapOffset = (void*)heap->dataPointer(); void *heapLength = (void*)heap->byteLength(); uint8_t *code = module.functionCode(); for (unsigned i = 0; i < module.numHeapAccesses(); i++) { const AsmJSHeapAccess &access = module.heapAccess(i); JSC::X86Assembler::setPointer(access.patchLengthAt(code), heapLength); JSC::X86Assembler::setPointer(access.patchOffsetAt(code), heapOffset); } #elif defined(JS_CPU_ARM) // Now the length of the array is know, patch all of the bounds check sites // with the new length. ion::IonContext ic(cx, NULL); module.patchBoundsChecks(heap->byteLength()); #endif } AutoObjectVector ffis(cx); if (!ffis.resize(module.numFFIs())) return false; for (unsigned i = 0; i < module.numGlobals(); i++) { AsmJSModule::Global &global = module.global(i); switch (global.which()) { case AsmJSModule::Global::Variable: if (!ValidateGlobalVariable(cx, module, global, importVal)) return false; break; case AsmJSModule::Global::FFI: if (!ValidateFFI(cx, global, importVal, &ffis)) return false; break; case AsmJSModule::Global::ArrayView: if (!ValidateArrayView(cx, global, globalVal, bufferVal)) return false; break; case AsmJSModule::Global::MathBuiltin: if (!ValidateMathBuiltin(cx, global, globalVal)) return false; break; case AsmJSModule::Global::Constant: if (!ValidateGlobalConstant(cx, global, globalVal)) return false; break; } } for (unsigned i = 0; i < module.numExits(); i++) module.exitIndexToGlobalDatum(i).fun = ffis[module.exit(i).ffiIndex()]->toFunction(); module.setIsLinked(heap); return true; }
bool Error(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); // ES6 19.5.1.1 mandates the .prototype lookup happens before the toString RootedObject proto(cx); if (!GetPrototypeFromCallableConstructor(cx, args, &proto)) return false; /* Compute the error message, if any. */ RootedString message(cx, nullptr); if (args.hasDefined(0)) { message = ToString<CanGC>(cx, args[0]); if (!message) return false; } /* Find the scripted caller, but only ones we're allowed to know about. */ NonBuiltinFrameIter iter(cx, cx->compartment()->principals()); /* Set the 'fileName' property. */ RootedString fileName(cx); if (args.length() > 1) { fileName = ToString<CanGC>(cx, args[1]); } else { fileName = cx->runtime()->emptyString; if (!iter.done()) { if (const char* cfilename = iter.filename()) fileName = JS_NewStringCopyZ(cx, cfilename); } } if (!fileName) return false; /* Set the 'lineNumber' property. */ uint32_t lineNumber, columnNumber = 0; if (args.length() > 2) { if (!ToUint32(cx, args[2], &lineNumber)) return false; } else { lineNumber = iter.done() ? 0 : iter.computeLine(&columnNumber); // XXX: Make the column 1-based as in other browsers, instead of 0-based // which is how SpiderMonkey stores it internally. This will be // unnecessary once bug 1144340 is fixed. ++columnNumber; } RootedObject stack(cx); if (!CaptureStack(cx, &stack)) return false; /* * ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when * called as functions, without operator new. But as we do not give * each constructor a distinct JSClass, we must get the exception type * ourselves. */ JSExnType exnType = JSExnType(args.callee().as<JSFunction>().getExtendedSlot(0).toInt32()); RootedObject obj(cx, ErrorObject::create(cx, exnType, stack, fileName, lineNumber, columnNumber, nullptr, message, proto)); if (!obj) return false; args.rval().setObject(*obj); return true; }
static JSBool obj_toSource(JSContext *cx, unsigned argc, Value *vp) { CallArgs args = CallArgsFromVp(argc, vp); JS_CHECK_RECURSION(cx, return false); RootedObject obj(cx, ToObject(cx, args.thisv())); if (!obj) return false; /* If outermost, we need parentheses to be an expression, not a block. */ bool outermost = (cx->cycleDetectorSet.count() == 0); AutoCycleDetector detector(cx, obj); if (!detector.init()) return false; if (detector.foundCycle()) { JSString *str = js_NewStringCopyZ<CanGC>(cx, "{}"); if (!str) return false; args.rval().setString(str); return true; } StringBuffer buf(cx); if (outermost && !buf.append('(')) return false; if (!buf.append('{')) return false; RootedValue v0(cx), v1(cx); MutableHandleValue val[2] = {&v0, &v1}; RootedString str0(cx), str1(cx); MutableHandleString gsop[2] = {&str0, &str1}; AutoIdVector idv(cx); if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &idv)) return false; bool comma = false; for (size_t i = 0; i < idv.length(); ++i) { RootedId id(cx, idv[i]); RootedObject obj2(cx); RootedShape shape(cx); if (!JSObject::lookupGeneric(cx, obj, id, &obj2, &shape)) return false; /* Decide early whether we prefer get/set or old getter/setter syntax. */ int valcnt = 0; if (shape) { bool doGet = true; if (obj2->isNative() && !IsImplicitDenseElement(shape)) { unsigned attrs = shape->attributes(); if (attrs & JSPROP_GETTER) { doGet = false; val[valcnt].set(shape->getterValue()); gsop[valcnt].set(cx->names().get); valcnt++; } if (attrs & JSPROP_SETTER) { doGet = false; val[valcnt].set(shape->setterValue()); gsop[valcnt].set(cx->names().set); valcnt++; } } if (doGet) { valcnt = 1; gsop[0].set(NULL); if (!JSObject::getGeneric(cx, obj, obj, id, val[0])) return false; } } /* Convert id to a linear string. */ RawString s = ToString<CanGC>(cx, IdToValue(id)); if (!s) return false; Rooted<JSLinearString*> idstr(cx, s->ensureLinear(cx)); if (!idstr) return false; /* * If id is a string that's not an identifier, or if it's a negative * integer, then it must be quoted. */ if (JSID_IS_ATOM(id) ? !IsIdentifier(idstr) : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) { s = js_QuoteString(cx, idstr, jschar('\'')); if (!s || !(idstr = s->ensureLinear(cx))) return false; } for (int j = 0; j < valcnt; j++) { /* * Censor an accessor descriptor getter or setter part if it's * undefined. */ if (gsop[j] && val[j].isUndefined()) continue; /* Convert val[j] to its canonical source form. */ RootedString valstr(cx, ValueToSource(cx, val[j])); if (!valstr) return false; const jschar *vchars = valstr->getChars(cx); if (!vchars) return false; size_t vlength = valstr->length(); /* * Remove '(function ' from the beginning of valstr and ')' from the * end so that we can put "get" in front of the function definition. */ if (gsop[j] && IsFunctionObject(val[j])) { const jschar *start = vchars; const jschar *end = vchars + vlength; uint8_t parenChomp = 0; if (vchars[0] == '(') { vchars++; parenChomp = 1; } /* Try to jump "function" keyword. */ if (vchars) vchars = js_strchr_limit(vchars, ' ', end); /* * Jump over the function's name: it can't be encoded as part * of an ECMA getter or setter. */ if (vchars) vchars = js_strchr_limit(vchars, '(', end); if (vchars) { if (*vchars == ' ') vchars++; vlength = end - vchars - parenChomp; } else { gsop[j].set(NULL); vchars = start; } } if (comma && !buf.append(", ")) return false; comma = true; if (gsop[j]) if (!buf.append(gsop[j]) || !buf.append(' ')) return false; if (!buf.append(idstr)) return false; if (!buf.append(gsop[j] ? ' ' : ':')) return false; if (!buf.append(vchars, vlength)) return false; } } if (!buf.append('}')) return false; if (outermost && !buf.append(')')) return false; RawString str = buf.finishString(); if (!str) return false; args.rval().setString(str); return true; }
// ES7 0c1bd3004329336774cbc90de727cd0cf5f11e93 9.5.14 Proxy.[[Construct]] bool ScriptedProxyHandler::construct(JSContext* cx, HandleObject proxy, const CallArgs& args) const { // Steps 1-3. RootedObject handler(cx, ScriptedProxyHandler::handlerObject(proxy)); if (!handler) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_REVOKED); return false; } // Step 4. RootedObject target(cx, proxy->as<ProxyObject>().target()); MOZ_ASSERT(target); MOZ_ASSERT(target->isConstructor()); // Step 5. RootedValue trap(cx); if (!GetProxyTrap(cx, handler, cx->names().construct, &trap)) return false; // Step 6. if (trap.isUndefined()) { ConstructArgs cargs(cx); if (!FillArgumentsFromArraylike(cx, cargs, args)) return false; RootedValue targetv(cx, ObjectValue(*target)); RootedObject obj(cx); if (!Construct(cx, targetv, cargs, args.newTarget(), &obj)) return false; args.rval().setObject(*obj); return true; } // Step 7. RootedObject argArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); if (!argArray) return false; // Steps 8, 10. { FixedInvokeArgs<3> iargs(cx); iargs[0].setObject(*target); iargs[1].setObject(*argArray); iargs[2].set(args.newTarget()); RootedValue thisv(cx, ObjectValue(*handler)); if (!Call(cx, trap, thisv, iargs, args.rval())) return false; } // Step 9. if (!args.rval().isObject()) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_PROXY_CONSTRUCT_OBJECT); return false; } return true; }
static bool WeakMap_construct(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); // ES6 draft rev 31 (15 Jan 2015) 23.3.1.1 step 1. if (!ThrowIfNotConstructing(cx, args, "WeakMap")) return false; RootedObject newTarget(cx, &args.newTarget().toObject()); RootedObject obj(cx, CreateThis(cx, &WeakMapObject::class_, newTarget)); if (!obj) return false; // Steps 5-6, 11. if (!args.get(0).isNullOrUndefined()) { // Steps 7a-b. RootedValue adderVal(cx); if (!GetProperty(cx, obj, obj, cx->names().set, &adderVal)) return false; // Step 7c. if (!IsCallable(adderVal)) return ReportIsNotFunction(cx, adderVal); bool isOriginalAdder = IsNativeFunction(adderVal, WeakMap_set); RootedValue mapVal(cx, ObjectValue(*obj)); FastCallGuard fig(cx, adderVal); InvokeArgs& args2 = fig.args(); // Steps 7d-e. JS::ForOfIterator iter(cx); if (!iter.init(args[0])) return false; RootedValue pairVal(cx); RootedObject pairObject(cx); RootedValue keyVal(cx); RootedObject keyObject(cx); RootedValue val(cx); RootedValue dummy(cx); while (true) { // Steps 12a-e. bool done; if (!iter.next(&pairVal, &done)) return false; if (done) break; // Step 12f. if (!pairVal.isObject()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_INVALID_MAP_ITERABLE, "WeakMap"); return false; } pairObject = &pairVal.toObject(); if (!pairObject) return false; // Steps 12g-h. if (!GetElement(cx, pairObject, pairObject, 0, &keyVal)) return false; // Steps 12i-j. if (!GetElement(cx, pairObject, pairObject, 1, &val)) return false; // Steps 12k-l. if (isOriginalAdder) { if (keyVal.isPrimitive()) { UniqueChars bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, keyVal, nullptr); if (!bytes) return false; JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_NOT_NONNULL_OBJECT, bytes.get()); return false; } keyObject = &keyVal.toObject(); if (!SetWeakMapEntry(cx, obj, keyObject, val)) return false; } else { if (!args2.init(cx, 2)) return false; args2[0].set(keyVal); args2[1].set(val); if (!fig.call(cx, adderVal, mapVal, &dummy)) return false; } } } args.rval().setObject(*obj); return true; }
// Common code implementing direct and indirect eval. // // Evaluate call.argv[2], if it is a string, in the context of the given calling // frame, with the provided scope chain, with the semantics of either a direct // or indirect eval (see ES5 10.4.2). If this is an indirect eval, scopeobj // must be a global object. // // On success, store the completion value in call.rval and return true. static bool EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, AbstractFramePtr caller, HandleObject scopeobj, jsbytecode *pc) { JS_ASSERT((evalType == INDIRECT_EVAL) == !caller); JS_ASSERT((evalType == INDIRECT_EVAL) == !pc); JS_ASSERT_IF(evalType == INDIRECT_EVAL, scopeobj->is<GlobalObject>()); AssertInnerizedScopeChain(cx, *scopeobj); Rooted<GlobalObject*> scopeObjGlobal(cx, &scopeobj->global()); if (!GlobalObject::isRuntimeCodeGenEnabled(cx, scopeObjGlobal)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CSP_BLOCKED_EVAL); return false; } // ES5 15.1.2.1 step 1. if (args.length() < 1) { args.rval().setUndefined(); return true; } if (!args[0].isString()) { args.rval().set(args[0]); return true; } RootedString str(cx, args[0].toString()); // ES5 15.1.2.1 steps 2-8. // Per ES5, indirect eval runs in the global scope. (eval is specified this // way so that the compiler can make assumptions about what bindings may or // may not exist in the current frame if it doesn't see 'eval'.) unsigned staticLevel; RootedValue thisv(cx); if (evalType == DIRECT_EVAL) { JS_ASSERT_IF(caller.isStackFrame(), !caller.asStackFrame()->runningInJit()); staticLevel = caller.script()->staticLevel() + 1; // Direct calls to eval are supposed to see the caller's |this|. If we // haven't wrapped that yet, do so now, before we make a copy of it for // the eval code to use. if (!ComputeThis(cx, caller)) return false; thisv = caller.thisValue(); } else { JS_ASSERT(args.callee().global() == *scopeobj); staticLevel = 0; // Use the global as 'this', modulo outerization. JSObject *thisobj = JSObject::thisObject(cx, scopeobj); if (!thisobj) return false; thisv = ObjectValue(*thisobj); } Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx)); if (!flatStr) return false; size_t length = flatStr->length(); ConstTwoByteChars chars(flatStr->chars(), length); RootedScript callerScript(cx, caller ? caller.script() : nullptr); EvalJSONResult ejr = TryEvalJSON(cx, callerScript, chars, length, args.rval()); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); if (evalType == DIRECT_EVAL && caller.isNonEvalFunctionFrame()) esg.lookupInEvalCache(flatStr, callerScript, pc); if (!esg.foundScript()) { JSScript *maybeScript; unsigned lineno; const char *filename; JSPrincipals *originPrincipals; uint32_t pcOffset; DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset, &originPrincipals, evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL : NOT_CALLED_FROM_JSOP_EVAL); const char *introducerFilename = filename; if (maybeScript && maybeScript->scriptSource()->introducerFilename()) introducerFilename = maybeScript->scriptSource()->introducerFilename(); CompileOptions options(cx); options.setFileAndLine(filename, 1) .setCompileAndGo(true) .setForEval(true) .setNoScriptRval(false) .setOriginPrincipals(originPrincipals) .setIntroductionInfo(introducerFilename, "eval", lineno, maybeScript, pcOffset); JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(), scopeobj, callerScript, options, chars.get(), length, flatStr, staticLevel); if (!compiled) return false; MarkFunctionsWithinEvalScript(compiled); esg.setNewScript(compiled); } return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType), NullFramePtr() /* evalInFrame */, args.rval().address()); }
static bool DynamicallyLinkModule(JSContext* cx, const CallArgs& args, AsmJSModule& module) { module.setIsDynamicallyLinked(cx->runtime()); HandleValue globalVal = args.get(0); HandleValue importVal = args.get(1); HandleValue bufferVal = args.get(2); Rooted<ArrayBufferObjectMaybeShared*> heap(cx); if (module.hasArrayView()) { if (module.isSharedView() && !IsSharedArrayBuffer(bufferVal)) return LinkFail(cx, "shared views can only be constructed onto SharedArrayBuffer"); if (!module.isSharedView() && !IsArrayBuffer(bufferVal)) return LinkFail(cx, "unshared views can only be constructed onto ArrayBuffer"); heap = &AsAnyArrayBuffer(bufferVal); if (!LinkModuleToHeap(cx, module, heap)) return false; } AutoObjectVector ffis(cx); if (!ffis.resize(module.numFFIs())) return false; for (unsigned i = 0; i < module.numGlobals(); i++) { AsmJSModule::Global& global = module.global(i); switch (global.which()) { case AsmJSModule::Global::Variable: if (!ValidateGlobalVariable(cx, module, global, importVal)) return false; break; case AsmJSModule::Global::FFI: if (!ValidateFFI(cx, global, importVal, &ffis)) return false; break; case AsmJSModule::Global::ArrayView: case AsmJSModule::Global::SharedArrayView: case AsmJSModule::Global::ArrayViewCtor: if (!ValidateArrayView(cx, global, globalVal, module.hasArrayView() && module.isSharedView())) return false; break; case AsmJSModule::Global::ByteLength: if (!ValidateByteLength(cx, globalVal)) return false; break; case AsmJSModule::Global::MathBuiltinFunction: if (!ValidateMathBuiltinFunction(cx, global, globalVal)) return false; break; case AsmJSModule::Global::AtomicsBuiltinFunction: if (!ValidateAtomicsBuiltinFunction(cx, global, globalVal)) return false; break; case AsmJSModule::Global::Constant: if (!ValidateConstant(cx, global, globalVal)) return false; break; case AsmJSModule::Global::SimdCtor: if (!ValidateSimdType(cx, global, globalVal)) return false; break; case AsmJSModule::Global::SimdOperation: if (!ValidateSimdOperation(cx, global, globalVal)) return false; break; } } for (unsigned i = 0; i < module.numExits(); i++) module.exitIndexToGlobalDatum(i).fun = &ffis[module.exit(i).ffiIndex()]->as<JSFunction>(); module.initGlobalNaN(); // See the comment in AllocateExecutableMemory. ExecutableAllocator::makeExecutable(module.codeBase(), module.codeBytes()); return true; }
// Common code implementing direct and indirect eval. // // Evaluate call.argv[2], if it is a string, in the context of the given calling // frame, with the provided scope chain, with the semantics of either a direct // or indirect eval (see ES5 10.4.2). If this is an indirect eval, scopeobj // must be a global object. // // On success, store the completion value in call.rval and return true. static bool EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *caller, HandleObject scopeobj) { JS_ASSERT((evalType == INDIRECT_EVAL) == (caller == NULL)); JS_ASSERT_IF(evalType == INDIRECT_EVAL, scopeobj->isGlobal()); AssertInnerizedScopeChain(cx, *scopeobj); if (!scopeobj->global().isRuntimeCodeGenEnabled(cx)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_EVAL); return false; } // ES5 15.1.2.1 step 1. if (args.length() < 1) { args.rval().setUndefined(); return true; } if (!args[0].isString()) { args.rval().set(args[0]); return true; } JSString *str = args[0].toString(); // ES5 15.1.2.1 steps 2-8. // Per ES5, indirect eval runs in the global scope. (eval is specified this // way so that the compiler can make assumptions about what bindings may or // may not exist in the current frame if it doesn't see 'eval'.) unsigned staticLevel; RootedValue thisv(cx); if (evalType == DIRECT_EVAL) { staticLevel = caller->script()->staticLevel + 1; // Direct calls to eval are supposed to see the caller's |this|. If we // haven't wrapped that yet, do so now, before we make a copy of it for // the eval code to use. if (!ComputeThis(cx, caller)) return false; thisv = caller->thisValue(); } else { JS_ASSERT(args.callee().global() == *scopeobj); staticLevel = 0; // Use the global as 'this', modulo outerization. JSObject *thisobj = scopeobj->thisObject(cx); if (!thisobj) return false; thisv = ObjectValue(*thisobj); } Rooted<JSLinearString*> linearStr(cx, str->ensureLinear(cx)); if (!linearStr) return false; const jschar *chars = linearStr->chars(); size_t length = linearStr->length(); SkipRoot skip(cx, &chars); // If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON. // Try the JSON parser first because it's much faster. If the eval string // isn't JSON, JSON parsing will probably fail quickly, so little time // will be lost. // // Don't use the JSON parser if the caller is strict mode code, because in // strict mode object literals must not have repeated properties, and the // JSON parser cheerfully (and correctly) accepts them. If you're parsing // JSON with eval and using strict mode, you deserve to be slow. if (length > 2 && ((chars[0] == '[' && chars[length - 1] == ']') || (chars[0] == '(' && chars[length - 1] == ')')) && (!caller || !caller->script()->strictModeCode)) { // Remarkably, JavaScript syntax is not a superset of JSON syntax: // strings in JavaScript cannot contain the Unicode line and paragraph // terminator characters U+2028 and U+2029, but strings in JSON can. // Rather than force the JSON parser to handle this quirk when used by // eval, we simply don't use the JSON parser when either character // appears in the provided string. See bug 657367. for (const jschar *cp = &chars[1], *end = &chars[length - 2]; ; cp++) { if (*cp == 0x2028 || *cp == 0x2029) break; if (cp == end) { bool isArray = (chars[0] == '['); JSONParser parser(cx, isArray ? chars : chars + 1, isArray ? length : length - 2, JSONParser::StrictJSON, JSONParser::NoError); RootedValue tmp(cx); if (!parser.parse(&tmp)) return false; if (tmp.isUndefined()) break; args.rval().set(tmp); return true; } } } EvalScriptGuard esg(cx); JSPrincipals *principals = PrincipalsForCompiledCode(args, cx); if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame()) esg.lookupInEvalCache(linearStr, caller->fun(), staticLevel); if (!esg.foundScript()) { unsigned lineno; const char *filename; JSPrincipals *originPrincipals; CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals, evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL : NOT_CALLED_FROM_JSOP_EVAL); CompileOptions options(cx); options.setFileAndLine(filename, lineno) .setCompileAndGo(true) .setNoScriptRval(false) .setPrincipals(principals) .setOriginPrincipals(originPrincipals); JSScript *compiled = frontend::CompileScript(cx, scopeobj, caller, options, chars, length, linearStr, staticLevel); if (!compiled) return false; esg.setNewScript(compiled); } return ExecuteKernel(cx, esg.script(), *scopeobj, thisv, ExecuteType(evalType), NULL /* evalInFrame */, args.rval().address()); }
// 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. // // We needn't provide an entry script pointer; that's only used for // reporting entry points to performance-monitoring tools, and asm.js -> // Ion calls will never be entry points. AsmJSActivation activation(cx, module); JitActivation jitActivation(cx, /* entryScript */ nullptr, /* 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); if (!obj) return false; 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; }
js::ConvertArgsToArray(JSContext* cx, const CallArgs& args) { RootedObject argsArray(cx, NewDenseCopiedArray(cx, args.length(), args.array())); return argsArray; }
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)) return false; // Call the function we just recompiled. args.setCallee(ObjectValue(*fun)); return Invoke(cx, args, args.isConstructing() ? CONSTRUCT : NO_CONSTRUCT); }
bool SetObject::construct(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); if (!ThrowIfNotConstructing(cx, args, "Set")) return false; RootedObject proto(cx); RootedObject newTarget(cx, &args.newTarget().toObject()); if (!GetPrototypeFromConstructor(cx, newTarget, &proto)) return false; Rooted<SetObject*> obj(cx, SetObject::create(cx, proto)); if (!obj) return false; if (!args.get(0).isNullOrUndefined()) { RootedValue adderVal(cx); if (!GetProperty(cx, obj, obj, cx->names().add, &adderVal)) return false; if (!IsCallable(adderVal)) return ReportIsNotFunction(cx, adderVal); bool isOriginalAdder = IsNativeFunction(adderVal, SetObject::add); RootedValue setVal(cx, ObjectValue(*obj)); FastInvokeGuard fig(cx, adderVal); InvokeArgs& args2 = fig.args(); RootedValue keyVal(cx); ForOfIterator iter(cx); if (!iter.init(args[0])) return false; Rooted<HashableValue> key(cx); ValueSet* set = obj->getData(); while (true) { bool done; if (!iter.next(&keyVal, &done)) return false; if (done) break; if (isOriginalAdder) { if (!key.setValue(cx, keyVal)) return false; if (!set->put(key)) { ReportOutOfMemory(cx); return false; } WriteBarrierPost(cx->runtime(), set, keyVal); } else { if (!args2.init(cx, 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; }
/* * Compile a new |RegExpShared| for the |RegExpObject|. * * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as * arguments: * * RegExp, undefined => flags := pattern.flags * RegExp, _ => throw TypeError * _ => pattern := ToString(pattern) if defined(pattern) else '' * flags := ToString(flags) if defined(flags) else '' */ static bool CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args) { if (args.length() == 0) { RegExpStatics *res = cx->global()->getRegExpStatics(); Rooted<JSAtom*> empty(cx, cx->runtime()->emptyString); RegExpObject *reobj = builder.build(empty, res->getFlags()); if (!reobj) return false; args.rval().setObject(*reobj); return true; } RootedValue sourceValue(cx, args[0]); /* * If we get passed in an object whose internal [[Class]] property is * "RegExp", return a new object with the same source/flags. */ if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) { /* * Beware, sourceObj may be a (transparent) proxy to a RegExp, so only * use generic (proxyable) operations on sourceObj that do not assume * sourceObj.is<RegExpObject>(). */ RootedObject sourceObj(cx, &sourceValue.toObject()); if (args.hasDefined(1)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED); return false; } /* * Only extract the 'flags' out of sourceObj; do not reuse the * RegExpShared since it may be from a different compartment. */ RegExpFlag flags; { RegExpGuard g(cx); if (!RegExpToShared(cx, sourceObj, &g)) return false; flags = g->getFlags(); } /* * 'toSource' is a permanent read-only property, so this is equivalent * to executing RegExpObject::getSource on the unwrapped object. */ RootedValue v(cx); if (!JSObject::getProperty(cx, sourceObj, sourceObj, cx->names().source, &v)) return false; Rooted<JSAtom*> sourceAtom(cx, &v.toString()->asAtom()); RegExpObject *reobj = builder.build(sourceAtom, flags); if (!reobj) return false; args.rval().setObject(*reobj); return true; } RootedAtom source(cx); if (sourceValue.isUndefined()) { source = cx->runtime()->emptyString; } else { /* Coerce to string and compile. */ source = ToAtom<CanGC>(cx, sourceValue); if (!source) return false; } RegExpFlag flags = RegExpFlag(0); if (args.hasDefined(1)) { RootedString flagStr(cx, ToString<CanGC>(cx, args[1])); if (!flagStr) return false; args[1].setString(flagStr); if (!ParseRegExpFlags(cx, flagStr, &flags)) return false; } RootedAtom escapedSourceStr(cx, EscapeNakedForwardSlashes(cx, source)); if (!escapedSourceStr) return false; if (!js::RegExpShared::checkSyntax(cx, NULL, escapedSourceStr)) return false; RegExpStatics *res = cx->global()->getRegExpStatics(); RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags())); if (!reobj) return false; args.rval().setObject(*reobj); return true; }
bool MapObject::get_impl(JSContext* cx, const CallArgs& args) { RootedObject obj(cx, &args.thisv().toObject()); return get(cx, obj, args.get(0), args.rval()); }
static inline bool UncachedInlineCall(VMFrame &f, InitialFrameFlags initial, void **pret, bool *unjittable, uint32_t argc) { JSContext *cx = f.cx; CallArgs args = CallArgsFromSp(argc, f.regs.sp); RootedFunction newfun(cx, args.callee().toFunction()); RootedScript newscript(cx, newfun->nonLazyScript()); if (!newscript) return false; bool construct = InitialFrameFlagsAreConstructing(initial); RootedScript fscript(cx, f.script()); bool newType = construct && cx->typeInferenceEnabled() && types::UseNewType(cx, fscript, f.pc()); if (!types::TypeMonitorCall(cx, args, construct)) return false; /* Try to compile if not already compiled. */ if (ShouldJaegerCompileCallee(cx, f.script(), newscript, f.jit())) { CompileStatus status = CanMethodJIT(cx, newscript, newscript->code, construct, CompileRequest_JIT, f.fp()); if (status == Compile_Error) { /* A runtime exception was thrown, get out. */ return false; } if (status == Compile_Abort) *unjittable = true; } /* * Make sure we are not calling from an inline frame if we need to make a * call object for the callee, as doing so could trigger GC and cause * jitcode discarding / frame expansion. */ if (f.regs.inlined() && newfun->isHeavyweight()) { ExpandInlineFrames(cx->zone()); JS_ASSERT(!f.regs.inlined()); } /* * Preserve f.regs.fp while pushing the new frame, for the invariant that * f.regs reflects the state when we entered the stub call. This handoff is * tricky: we need to make sure that f.regs is not updated to the new * frame, and we also need to ensure that cx->regs still points to f.regs * when space is reserved, in case doing so throws an exception. */ FrameRegs regs = f.regs; /* Get pointer to new frame/slots, prepare arguments. */ if (!cx->stack.pushInlineFrame(cx, regs, args, newfun, newscript, initial, &f.stackLimit)) return false; /* Finish the handoff to the new frame regs. */ PreserveRegsGuard regsGuard(cx, regs); /* * If newscript was successfully compiled, run it. Skip for calls which * will be constructing a new type object for 'this'. */ if (!newType) { if (JITScript *jit = newscript->getJIT(regs.fp()->isConstructing(), cx->zone()->compileBarriers())) { if (jit->invokeEntry) { *pret = jit->invokeEntry; /* Restore the old fp around and let the JIT code repush the new fp. */ regs.popFrame((Value *) regs.fp()); return true; } } } /* * Otherwise, run newscript in the interpreter. Expand any inlined frame we * are calling from, as the new frame is not associated with the VMFrame * and will not have its prevpc info updated if frame expansion is * triggered while interpreting. */ if (f.regs.inlined()) { ExpandInlineFrames(cx->zone()); JS_ASSERT(!f.regs.inlined()); regs.fp()->resetInlinePrev(f.fp(), f.regs.pc); } JS_CHECK_RECURSION(cx, return false); bool ok = RunScript(cx, cx->fp()); f.cx->stack.popInlineFrame(regs); if (ok) { RootedScript fscript(cx, f.script()); types::TypeScript::Monitor(f.cx, fscript, f.pc(), args.rval()); } *pret = NULL; return ok; }
static bool ModuleValueGetterImpl(JSContext* cx, const CallArgs& args) { args.rval().set(ValueGetter(&args.thisv().toObject().as<T>())); return true; }
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; }
static bool CountHeap(JSContext *cx, unsigned argc, jsval *vp) { CallArgs args = CallArgsFromVp(argc, vp); RootedValue startValue(cx, UndefinedValue()); if (args.length() > 0) { jsval v = args[0]; if (JSVAL_IS_TRACEABLE(v)) { startValue = v; } else if (!JSVAL_IS_NULL(v)) { JS_ReportError(cx, "the first argument is not null or a heap-allocated " "thing"); return false; } } RootedValue traceValue(cx); int32_t traceKind = -1; void *traceThing = nullptr; if (args.length() > 1) { JSString *str = ToString(cx, args[1]); if (!str) return false; JSFlatString *flatStr = JS_FlattenString(cx, str); if (!flatStr) return false; if (JS_FlatStringEqualsAscii(flatStr, "specific")) { if (args.length() < 3) { JS_ReportError(cx, "tracing of specific value requested " "but no value provided"); return false; } traceValue = args[2]; if (!JSVAL_IS_TRACEABLE(traceValue)){ JS_ReportError(cx, "cannot trace this kind of value"); return false; } traceThing = JSVAL_TO_TRACEABLE(traceValue); } else { for (size_t i = 0; ;) { if (JS_FlatStringEqualsAscii(flatStr, traceKindNames[i].name)) { traceKind = traceKindNames[i].kind; break; } if (++i == ArrayLength(traceKindNames)) { JSAutoByteString bytes(cx, str); if (!!bytes) JS_ReportError(cx, "trace kind name '%s' is unknown", bytes.ptr()); return false; } } } } JSCountHeapTracer countTracer; JS_TracerInit(&countTracer.base, JS_GetRuntime(cx), CountHeapNotify); if (!countTracer.visited.init()) { JS_ReportOutOfMemory(cx); return false; } countTracer.ok = true; countTracer.traceList = nullptr; countTracer.recycleList = nullptr; if (startValue.isUndefined()) { JS_TraceRuntime(&countTracer.base); } else { JS_CallValueTracer(&countTracer.base, startValue.address(), "root"); } JSCountHeapNode *node; size_t counter = 0; while ((node = countTracer.traceList) != nullptr) { if (traceThing == nullptr) { // We are looking for all nodes with a specific kind if (traceKind == -1 || node->kind == traceKind) counter++; } else { // We are looking for some specific thing if (node->thing == traceThing) counter++; } countTracer.traceList = node->next; node->next = countTracer.recycleList; countTracer.recycleList = node; JS_TraceChildren(&countTracer.base, node->thing, node->kind); } while ((node = countTracer.recycleList) != nullptr) { countTracer.recycleList = node->next; js_free(node); } if (!countTracer.ok) { JS_ReportOutOfMemory(cx); return false; } *vp = JS_NumberValue((double) counter); return true; }
bool WrapperOwner::callOrConstruct(JSContext* cx, HandleObject proxy, const CallArgs& args, bool construct) { ObjectId objId = idOf(proxy); InfallibleTArray<JSParam> vals; AutoValueVector outobjects(cx); RootedValue v(cx); for (size_t i = 0; i < args.length() + 2; i++) { // The |this| value for constructors is a magic value that we won't be // able to convert, so skip it. if (i == 1 && construct) v = UndefinedValue(); else v = args.base()[i]; if (v.isObject()) { RootedObject obj(cx, &v.toObject()); if (xpc::IsOutObject(cx, obj)) { // Make sure it is not an in-out object. bool found; if (!JS_HasProperty(cx, obj, "value", &found)) return false; if (found) { JS_ReportError(cx, "in-out objects cannot be sent via CPOWs yet"); return false; } vals.AppendElement(JSParam(void_t())); if (!outobjects.append(ObjectValue(*obj))) return false; continue; } } JSVariant val; if (!toVariant(cx, v, &val)) return false; vals.AppendElement(JSParam(val)); } JSVariant result; ReturnStatus status; InfallibleTArray<JSParam> outparams; if (!SendCallOrConstruct(objId, vals, construct, &status, &result, &outparams)) return ipcfail(cx); LOG_STACK(); if (!ok(cx, status)) return false; if (outparams.Length() != outobjects.length()) return ipcfail(cx); RootedObject obj(cx); for (size_t i = 0; i < outparams.Length(); i++) { // Don't bother doing anything for outparams that weren't set. if (outparams[i].type() == JSParam::Tvoid_t) continue; // Take the value the child process returned, and set it on the XPC // object. if (!fromVariant(cx, outparams[i], &v)) return false; obj = &outobjects[i].toObject(); if (!JS_SetProperty(cx, obj, "value", v)) return false; } if (!fromVariant(cx, result, args.rval())) return false; return true; }