/* * ES6 21.2.3.2.2. Because this function only ever returns |obj| in the spec, * provided by the user, we omit it and just return the usual success/failure. */ static bool RegExpInitialize(JSContext* cx, Handle<RegExpObject*> obj, HandleValue patternValue, HandleValue flagsValue, RegExpStaticsUse staticsUse) { RootedAtom pattern(cx); if (patternValue.isUndefined()) { /* Step 1. */ pattern = cx->runtime()->emptyString; } else { /* Steps 2-3. */ pattern = ToAtom<CanGC>(cx, patternValue); if (!pattern) return false; } /* Step 4. */ RegExpFlag flags = RegExpFlag(0); if (!flagsValue.isUndefined()) { /* Steps 5-6. */ RootedString flagStr(cx, ToString<CanGC>(cx, flagsValue)); if (!flagStr) return false; /* Step 7. */ if (!ParseRegExpFlags(cx, flagStr, &flags)) return false; } /* Steps 9-10. */ CompileOptions options(cx); frontend::TokenStream dummyTokenStream(cx, options, nullptr, 0, nullptr); if (!irregexp::ParsePatternSyntax(dummyTokenStream, cx->tempLifoAlloc(), pattern, flags & UnicodeFlag)) { return false; } if (staticsUse == UseRegExpStatics) { RegExpStatics* res = cx->global()->getRegExpStatics(cx); if (!res) return false; flags = RegExpFlag(flags | res->getFlags()); } /* Steps 11-15. */ if (!RegExpObject::initFromAtom(cx, obj, pattern, flags)) return false; /* Step 16. */ return true; }
bool js::GeneratorThrowOrClose(JSContext *cx, AbstractFramePtr frame, Handle<GeneratorObject*> genObj, HandleValue arg, uint32_t resumeKind) { if (resumeKind == GeneratorObject::THROW) { cx->setPendingException(arg); genObj->setRunning(); } else { MOZ_ASSERT(resumeKind == GeneratorObject::CLOSE); if (genObj->is<StarGeneratorObject>()) { // Store the return value in the frame's CallObject so that we can // return it after executing finally blocks (and potentially // yielding again). MOZ_ASSERT(arg.isObject()); CallObject &callObj = frame.callObj(); Shape *shape = callObj.lookup(cx, cx->names().dotGenRVal); callObj.setSlot(shape->slot(), arg); } else { MOZ_ASSERT(arg.isUndefined()); } cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING)); genObj->setClosing(); } return false; }
bool js::GeneratorThrowOrClose(JSContext* cx, AbstractFramePtr frame, Handle<GeneratorObject*> genObj, HandleValue arg, uint32_t resumeKind) { if (resumeKind == GeneratorObject::THROW) { cx->setPendingException(arg); genObj->setRunning(); } else { MOZ_ASSERT(resumeKind == GeneratorObject::CLOSE); if (genObj->is<StarGeneratorObject>()) { MOZ_ASSERT(arg.isObject()); frame.setReturnValue(arg); } else { MOZ_ASSERT(arg.isUndefined()); } cx->setPendingException(MagicValue(JS_GENERATOR_CLOSING)); genObj->setClosing(); } return false; }
bool js::DirectEvalStringFromIon(JSContext *cx, HandleObject scopeobj, HandleScript callerScript, HandleValue thisValue, HandleString str, jsbytecode *pc, MutableHandleValue vp) { 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 steps 2-8. unsigned staticLevel = callerScript->staticLevel() + 1; Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx)); if (!flatStr) return false; EvalJSONResult ejr = TryEvalJSON(cx, callerScript, flatStr, vp); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); esg.lookupInEvalCache(flatStr, callerScript, pc); if (!esg.foundScript()) { RootedScript maybeScript(cx); const char *filename; unsigned lineno; JSPrincipals *originPrincipals; uint32_t pcOffset; DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset, &originPrincipals, 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); AutoStableStringChars flatChars(cx); if (!flatChars.initTwoByte(cx, flatStr)) return false; const char16_t *chars = flatChars.twoByteRange().start().get(); SourceBufferHolder::Ownership ownership = flatChars.maybeGiveOwnershipToCaller() ? SourceBufferHolder::GiveOwnership : SourceBufferHolder::NoOwnership; SourceBufferHolder srcBuf(chars, flatStr->length(), ownership); JSScript *compiled = frontend::CompileScript(cx, &cx->tempLifoAlloc(), scopeobj, callerScript, options, srcBuf, flatStr, staticLevel); if (!compiled) return false; esg.setNewScript(compiled); } // Primitive 'this' values should have been filtered out by Ion. If boxed, // the calling frame cannot be updated to store the new object. JS_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull()); return ExecuteKernel(cx, esg.script(), *scopeobj, thisValue, ExecuteType(DIRECT_EVAL), NullFramePtr() /* evalInFrame */, vp.address()); }
bool js::DirectEvalFromIon(JSContext *cx, HandleObject scopeobj, HandleScript callerScript, HandleValue thisValue, HandleString str, MutableHandleValue vp) { AssertInnerizedScopeChain(cx, *scopeobj); Rooted<GlobalObject*> scopeObjGlobal(cx, &scopeobj->global()); if (!GlobalObject::isRuntimeCodeGenEnabled(cx, scopeObjGlobal)) { JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_EVAL); return false; } // ES5 15.1.2.1 steps 2-8. unsigned staticLevel = callerScript->staticLevel + 1; Rooted<JSStableString*> stableStr(cx, str->ensureStable(cx)); if (!stableStr) return false; StableCharPtr chars = stableStr->chars(); size_t length = stableStr->length(); EvalJSONResult ejr = TryEvalJSON(cx, callerScript, chars, length, vp); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); // Ion will not perform cross compartment direct eval calls. JSPrincipals *principals = cx->compartment->principals; esg.lookupInEvalCache(stableStr, callerScript->function(), staticLevel); if (!esg.foundScript()) { unsigned lineno; const char *filename; JSPrincipals *originPrincipals; CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals, CALLED_FROM_JSOP_EVAL); CompileOptions options(cx); options.setFileAndLine(filename, lineno) .setCompileAndGo(true) .setNoScriptRval(false) .setPrincipals(principals) .setOriginPrincipals(originPrincipals); UnrootedScript compiled = frontend::CompileScript(cx, scopeobj, callerScript, options, chars.get(), length, stableStr, staticLevel); if (!compiled) return false; esg.setNewScript(compiled); } // Primitive 'this' values should have been filtered out by Ion. If boxed, // the calling frame cannot be updated to store the new object. JS_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull()); return ExecuteKernel(cx, esg.script(), *scopeobj, thisValue, ExecuteType(DIRECT_EVAL), NullFramePtr() /* evalInFrame */, vp.address()); }
bool js::DirectEvalStringFromIon(JSContext* cx, HandleObject scopeobj, HandleScript callerScript, HandleValue thisValue, HandleValue newTargetValue, HandleString str, jsbytecode* pc, MutableHandleValue vp) { 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 steps 2-8. unsigned staticLevel = callerScript->staticLevel() + 1; RootedLinearString linearStr(cx, str->ensureLinear(cx)); if (!linearStr) return false; EvalJSONResult ejr = TryEvalJSON(cx, linearStr, vp); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); esg.lookupInEvalCache(linearStr, callerScript, pc); if (!esg.foundScript()) { RootedScript maybeScript(cx); const char* filename; unsigned lineno; bool mutedErrors; uint32_t pcOffset; DescribeScriptedCallerForCompilation(cx, &maybeScript, &filename, &lineno, &pcOffset, &mutedErrors, CALLED_FROM_JSOP_EVAL); const char* introducerFilename = filename; if (maybeScript && maybeScript->scriptSource()->introducerFilename()) introducerFilename = maybeScript->scriptSource()->introducerFilename(); RootedObject enclosing(cx, callerScript->innermostStaticScope(pc)); 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(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, staticLevel); if (!compiled) return false; if (compiled->strict()) staticScope->setStrict(); esg.setNewScript(compiled); } // Primitive 'this' values should have been filtered out by Ion. If boxed, // the calling frame cannot be updated to store the new object. MOZ_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull()); // When eval'ing strict code in a non-strict context, compute the 'this' // value to use from what the caller passed in. This isn't necessary if // the callee is not strict, as it will compute the non-strict 'this' // value as necessary while it executes. RootedValue nthisValue(cx, thisValue); if (!callerScript->strict() && esg.script()->strict() && !thisValue.isObject()) { JSObject* obj = BoxNonStrictThis(cx, thisValue); if (!obj) return false; nthisValue = ObjectValue(*obj); } return ExecuteKernel(cx, esg.script(), *scopeobj, nthisValue, newTargetValue, ExecuteType(DIRECT_EVAL), NullFramePtr() /* evalInFrame */, vp.address()); }
/* * Experimental implementation of ArrayBuffer.transfer: * https://gist.github.com/andhow/95fb9e49996615764eff * which is currently in the early stages of proposal for ES7. */ bool ArrayBufferObject::fun_transfer(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); HandleValue oldBufferArg = args.get(0); HandleValue newByteLengthArg = args.get(1); if (!oldBufferArg.isObject()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); return false; } RootedObject oldBufferObj(cx, &oldBufferArg.toObject()); ESClassValue cls; if (!GetBuiltinClass(cx, oldBufferObj, &cls)) return false; if (cls != ESClass_ArrayBuffer) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); return false; } // Beware: oldBuffer can point across compartment boundaries. ArrayBuffer // contents are not compartment-specific so this is safe. Rooted<ArrayBufferObject*> oldBuffer(cx); if (oldBufferObj->is<ArrayBufferObject>()) { oldBuffer = &oldBufferObj->as<ArrayBufferObject>(); } else { JSObject* unwrapped = CheckedUnwrap(oldBufferObj); if (!unwrapped || !unwrapped->is<ArrayBufferObject>()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); return false; } oldBuffer = &unwrapped->as<ArrayBufferObject>(); } size_t oldByteLength = oldBuffer->byteLength(); size_t newByteLength; if (newByteLengthArg.isUndefined()) { newByteLength = oldByteLength; } else { int32_t i32; if (!ToInt32(cx, newByteLengthArg, &i32)) return false; if (i32 < 0) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH); return false; } newByteLength = size_t(i32); } if (oldBuffer->isNeutered()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_DETACHED); return false; } UniquePtr<uint8_t, JS::FreePolicy> newData; if (!newByteLength) { if (!ArrayBufferObject::neuter(cx, oldBuffer, oldBuffer->contents())) return false; } else { # if defined(ASMJS_MAY_USE_SIGNAL_HANDLERS_FOR_OOB) // With a 4gb mapped asm.js buffer, we can simply enable/disable access // to the delta as long as the requested length is page-sized. if (oldBuffer->isAsmJSMapped() && (newByteLength % AsmJSPageSize) == 0) return TransferAsmJSMappedBuffer(cx, args, oldBuffer, newByteLength); # endif // Since we try to realloc below, only allow stealing malloc'd buffers. // If !hasMallocedContents, stealContents will malloc a copy which we // can then realloc. bool steal = oldBuffer->hasMallocedContents(); auto stolenContents = ArrayBufferObject::stealContents(cx, oldBuffer, steal); if (!stolenContents) return false; UniquePtr<uint8_t, JS::FreePolicy> oldData(stolenContents.data()); if (newByteLength > oldByteLength) { // In theory, realloc+memset(0) can be optimized to avoid touching // any pages (by using OS page mapping tricks). However, in // practice, we don't seem to get this optimization in Firefox with // jemalloc so calloc+memcpy are faster. newData.reset(cx->runtime()->pod_callocCanGC<uint8_t>(newByteLength)); if (newData) { memcpy(newData.get(), oldData.get(), oldByteLength); } else { // Try malloc before giving up since it might be able to succed // by resizing oldData in-place. newData.reset(cx->pod_realloc(oldData.get(), oldByteLength, newByteLength)); if (!newData) return false; oldData.release(); memset(newData.get() + oldByteLength, 0, newByteLength - oldByteLength); } } else if (newByteLength < oldByteLength) { newData.reset(cx->pod_realloc(oldData.get(), oldByteLength, newByteLength)); if (!newData) return false; oldData.release(); } else { newData = Move(oldData); } } RootedObject newBuffer(cx, JS_NewArrayBufferWithContents(cx, newByteLength, newData.get())); if (!newBuffer) return false; newData.release(); args.rval().setObject(*newBuffer); return true; }
bool ExportFunction(JSContext *cx, HandleValue vfunction, HandleValue vscope, HandleValue voptions, MutableHandleValue rval) { bool hasOptions = !voptions.isUndefined(); if (!vscope.isObject() || !vfunction.isObject() || (hasOptions && !voptions.isObject())) { JS_ReportError(cx, "Invalid argument"); return false; } RootedObject funObj(cx, &vfunction.toObject()); RootedObject targetScope(cx, &vscope.toObject()); ExportFunctionOptions options(cx, hasOptions ? &voptions.toObject() : nullptr); if (hasOptions && !options.Parse()) return false; // We can only export functions to scopes those are transparent for us, // so if there is a security wrapper around targetScope we must throw. targetScope = CheckedUnwrap(targetScope); if (!targetScope) { JS_ReportError(cx, "Permission denied to export function into scope"); return false; } if (js::IsScriptedProxy(targetScope)) { JS_ReportError(cx, "Defining property on proxy object is not allowed"); return false; } { // We need to operate in the target scope from here on, let's enter // its compartment. JSAutoCompartment ac(cx, targetScope); // Unwrapping to see if we have a callable. funObj = UncheckedUnwrap(funObj); if (!JS_ObjectIsCallable(cx, funObj)) { JS_ReportError(cx, "First argument must be a function"); return false; } RootedId id(cx, options.defineAs); if (JSID_IS_VOID(id)) { // If there wasn't any function name specified, // copy the name from the function being imported. JSFunction *fun = JS_GetObjectFunction(funObj); RootedString funName(cx, JS_GetFunctionId(fun)); if (!funName) funName = JS_InternString(cx, ""); if (!JS_StringToId(cx, funName, &id)) return false; } MOZ_ASSERT(JSID_IS_STRING(id)); // The function forwarder will live in the target compartment. Since // this function will be referenced from its private slot, to avoid a // GC hazard, we must wrap it to the same compartment. if (!JS_WrapObject(cx, &funObj)) return false; // And now, let's create the forwarder function in the target compartment // for the function the be exported. FunctionForwarderOptions forwarderOptions(cx); forwarderOptions.allowCallbacks = options.allowCallbacks; if (!NewFunctionForwarder(cx, id, funObj, forwarderOptions, rval)) { JS_ReportError(cx, "Exporting function failed"); return false; } // We have the forwarder function in the target compartment. If // defineAs was set, we also need to define it as a property on // the target. if (!JSID_IS_VOID(options.defineAs)) { if (!JS_DefinePropertyById(cx, targetScope, id, rval, JSPROP_ENUMERATE, JS_PropertyStub, JS_StrictPropertyStub)) { return false; } } } // Finally we have to re-wrap the exported function back to the caller compartment. if (!JS_WrapValue(cx, rval)) return false; return true; }
bool js::DirectEvalStringFromIon(JSContext *cx, HandleObject scopeobj, HandleScript callerScript, HandleValue thisValue, HandleString str, jsbytecode *pc, MutableHandleValue vp) { 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 steps 2-8. unsigned staticLevel = callerScript->staticLevel() + 1; Rooted<JSFlatString*> flatStr(cx, str->ensureFlat(cx)); if (!flatStr) return false; size_t length = flatStr->length(); ConstTwoByteChars chars(flatStr->chars(), length); EvalJSONResult ejr = TryEvalJSON(cx, callerScript, chars, length, vp); if (ejr != EvalJSON_NotJSON) return ejr == EvalJSON_Success; EvalScriptGuard esg(cx); // Ion will not perform cross compartment direct eval calls. JSPrincipals *principals = cx->compartment()->principals; esg.lookupInEvalCache(flatStr, callerScript, pc); if (!esg.foundScript()) { JSScript *script; const char *filename; unsigned lineno; JSPrincipals *originPrincipals; uint32_t pcOffset; CurrentScriptFileLineOrigin(cx, &script, &filename, &lineno, &pcOffset, &originPrincipals, CALLED_FROM_JSOP_EVAL); const char *introducerFilename = filename; if (script && script->scriptSource()->introducerFilename()) introducerFilename = script->scriptSource()->introducerFilename(); CompileOptions options(cx); options.setFileAndLine(filename, 1) .setCompileAndGo(true) .setForEval(true) .setNoScriptRval(false) .setPrincipals(principals) .setOriginPrincipals(originPrincipals) .setIntroductionInfo(introducerFilename, "eval", lineno, script, 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); } // Primitive 'this' values should have been filtered out by Ion. If boxed, // the calling frame cannot be updated to store the new object. JS_ASSERT(thisValue.isObject() || thisValue.isUndefined() || thisValue.isNull()); return ExecuteKernel(cx, esg.script(), *scopeobj, thisValue, ExecuteType(DIRECT_EVAL), NullFramePtr() /* evalInFrame */, vp.address()); }