nsresult nsJSUtils::CompileFunction(JSContext* aCx, JS::Handle<JSObject*> aTarget, JS::CompileOptions& aOptions, const nsACString& aName, uint32_t aArgCount, const char** aArgArray, const nsAString& aBody, JSObject** aFunctionObject) { MOZ_ASSERT(js::GetEnterCompartmentDepth(aCx) > 0); MOZ_ASSERT_IF(aTarget, js::IsObjectInContextCompartment(aTarget, aCx)); MOZ_ASSERT_IF(aOptions.versionSet, aOptions.version != JSVERSION_UNKNOWN); mozilla::DebugOnly<nsIScriptContext*> ctx = GetScriptContextFromJSContext(aCx); MOZ_ASSERT_IF(ctx, ctx->IsContextInitialized()); // Do the junk Gecko is supposed to do before calling into JSAPI. if (aTarget) { JS::ExposeObjectToActiveJS(aTarget); } // Compile. JSFunction* fun = JS::CompileFunction(aCx, aTarget, aOptions, PromiseFlatCString(aName).get(), aArgCount, aArgArray, PromiseFlatString(aBody).get(), aBody.Length()); if (!fun) { ReportPendingException(aCx); return NS_ERROR_FAILURE; } *aFunctionObject = JS_GetFunctionObject(fun); return NS_OK; }
nsresult nsJSUtils::CompileFunction(JSContext* aCx, JS::HandleObject aTarget, JS::CompileOptions& aOptions, const nsACString& aName, uint32_t aArgCount, const char** aArgArray, const nsAString& aBody, JSObject** aFunctionObject) { MOZ_ASSERT(js::GetEnterCompartmentDepth(aCx) > 0); MOZ_ASSERT_IF(aTarget, js::IsObjectInContextCompartment(aTarget, aCx)); MOZ_ASSERT_IF(aOptions.versionSet, aOptions.version != JSVERSION_UNKNOWN); mozilla::DebugOnly<nsIScriptContext*> ctx = GetScriptContextFromJSContext(aCx); MOZ_ASSERT_IF(ctx, ctx->IsContextInitialized()); // Since aTarget and aCx are same-compartment, there should be no distinction // between the object principal and the cx principal. // However, aTarget may be null in the wacky aShared case. So use the cx. JSPrincipals* p = JS_GetCompartmentPrincipals(js::GetContextCompartment(aCx)); aOptions.setPrincipals(p); // Do the junk Gecko is supposed to do before calling into JSAPI. if (aTarget) { JS::ExposeObjectToActiveJS(aTarget); } // Compile. JSFunction* fun = JS::CompileFunction(aCx, aTarget, aOptions, PromiseFlatCString(aName).get(), aArgCount, aArgArray, PromiseFlatString(aBody).get(), aBody.Length()); if (!fun) { ReportPendingException(aCx); return NS_ERROR_FAILURE; } *aFunctionObject = JS_GetFunctionObject(fun); return NS_OK; }
nsresult nsJSUtils::EvaluateString(JSContext* aCx, JS::SourceBufferHolder& aSrcBuf, JS::Handle<JSObject*> aScopeObject, JS::CompileOptions& aCompileOptions, const EvaluateOptions& aEvaluateOptions, JS::MutableHandle<JS::Value> aRetValue, void **aOffThreadToken) { PROFILER_LABEL("nsJSUtils", "EvaluateString", js::ProfileEntry::Category::JS); MOZ_ASSERT_IF(aCompileOptions.versionSet, aCompileOptions.version != JSVERSION_UNKNOWN); MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, aEvaluateOptions.needResult); MOZ_ASSERT_IF(!aEvaluateOptions.reportUncaught, aEvaluateOptions.needResult); MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); MOZ_ASSERT(aSrcBuf.get()); // Unfortunately, the JS engine actually compiles scripts with a return value // in a different, less efficient way. Furthermore, it can't JIT them in many // cases. So we need to be explicitly told whether the caller cares about the // return value. Callers can do this by calling the other overload of // EvaluateString() which calls this function with aEvaluateOptions.needResult // set to false. aRetValue.setUndefined(); JS::ExposeObjectToActiveJS(aScopeObject); nsAutoMicroTask mt; nsresult rv = NS_OK; bool ok = false; nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); NS_ENSURE_TRUE(ssm->ScriptAllowed(js::GetGlobalForObjectCrossCompartment(aScopeObject)), NS_OK); mozilla::Maybe<AutoDontReportUncaught> dontReport; if (!aEvaluateOptions.reportUncaught) { // We need to prevent AutoLastFrameCheck from reporting and clearing // any pending exceptions. dontReport.emplace(aCx); } // Scope the JSAutoCompartment so that we can later wrap the return value // into the caller's cx. { JSAutoCompartment ac(aCx, aScopeObject); JS::Rooted<JSObject*> rootedScope(aCx, aScopeObject); if (aOffThreadToken) { JS::Rooted<JSScript*> script(aCx, JS::FinishOffThreadScript(aCx, JS_GetRuntime(aCx), *aOffThreadToken)); *aOffThreadToken = nullptr; // Mark the token as having been finished. if (script) { if (aEvaluateOptions.needResult) { ok = JS_ExecuteScript(aCx, rootedScope, script, aRetValue); } else { ok = JS_ExecuteScript(aCx, rootedScope, script); } } else { ok = false; } } else { if (aEvaluateOptions.needResult) { ok = JS::Evaluate(aCx, rootedScope, aCompileOptions, aSrcBuf, aRetValue); } else { ok = JS::Evaluate(aCx, rootedScope, aCompileOptions, aSrcBuf); } } if (ok && aEvaluateOptions.coerceToString && !aRetValue.isUndefined()) { JS::Rooted<JS::Value> value(aCx, aRetValue); JSString* str = JS::ToString(aCx, value); ok = !!str; aRetValue.set(ok ? JS::StringValue(str) : JS::UndefinedValue()); } } if (!ok) { if (aEvaluateOptions.reportUncaught) { ReportPendingException(aCx); if (aEvaluateOptions.needResult) { aRetValue.setUndefined(); } } else { rv = JS_IsExceptionPending(aCx) ? NS_ERROR_FAILURE : NS_ERROR_OUT_OF_MEMORY; JS::Rooted<JS::Value> exn(aCx); JS_GetPendingException(aCx, &exn); if (aEvaluateOptions.needResult) { aRetValue.set(exn); } JS_ClearPendingException(aCx); } } // Wrap the return value into whatever compartment aCx was in. if (aEvaluateOptions.needResult) { JS::Rooted<JS::Value> v(aCx, aRetValue); if (!JS_WrapValue(aCx, &v)) { return NS_ERROR_OUT_OF_MEMORY; } aRetValue.set(v); } return rv; }
nsresult nsJSUtils::EvaluateString(JSContext* aCx, const nsAString& aScript, JS::Handle<JSObject*> aScopeObject, JS::CompileOptions& aCompileOptions, EvaluateOptions& aEvaluateOptions, JS::Value* aRetValue, void **aOffThreadToken) { PROFILER_LABEL("JS", "EvaluateString"); MOZ_ASSERT_IF(aCompileOptions.versionSet, aCompileOptions.version != JSVERSION_UNKNOWN); MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, aRetValue); MOZ_ASSERT_IF(!aEvaluateOptions.reportUncaught, aRetValue); MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); // Unfortunately, the JS engine actually compiles scripts with a return value // in a different, less efficient way. Furthermore, it can't JIT them in many // cases. So we need to be explicitly told whether the caller cares about the // return value. Callers use null to indicate they don't care. if (aRetValue) { *aRetValue = JSVAL_VOID; } JS::ExposeObjectToActiveJS(aScopeObject); nsAutoMicroTask mt; nsresult rv = NS_OK; bool ok = false; nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); NS_ENSURE_TRUE(ssm->ScriptAllowed(js::GetGlobalForObjectCrossCompartment(aScopeObject)), NS_OK); mozilla::Maybe<AutoDontReportUncaught> dontReport; if (!aEvaluateOptions.reportUncaught) { // We need to prevent AutoLastFrameCheck from reporting and clearing // any pending exceptions. dontReport.construct(aCx); } // Scope the JSAutoCompartment so that we can later wrap the return value // into the caller's cx. { JSAutoCompartment ac(aCx, aScopeObject); JS::Rooted<JSObject*> rootedScope(aCx, aScopeObject); if (aOffThreadToken) { JSScript *script = JS::FinishOffThreadScript(aCx, JS_GetRuntime(aCx), *aOffThreadToken); *aOffThreadToken = nullptr; // Mark the token as having been finished. if (script) { ok = JS_ExecuteScript(aCx, rootedScope, script, aRetValue); } else { ok = false; } } else { ok = JS::Evaluate(aCx, rootedScope, aCompileOptions, PromiseFlatString(aScript).get(), aScript.Length(), aRetValue); } if (ok && aEvaluateOptions.coerceToString && !aRetValue->isUndefined()) { JS::Rooted<JS::Value> value(aCx, *aRetValue); JSString* str = JS::ToString(aCx, value); ok = !!str; *aRetValue = ok ? JS::StringValue(str) : JS::UndefinedValue(); } } if (!ok) { if (aEvaluateOptions.reportUncaught) { ReportPendingException(aCx); if (aRetValue) { *aRetValue = JS::UndefinedValue(); } } else { rv = JS_IsExceptionPending(aCx) ? NS_ERROR_FAILURE : NS_ERROR_OUT_OF_MEMORY; JS::Rooted<JS::Value> exn(aCx); JS_GetPendingException(aCx, &exn); if (aRetValue) { *aRetValue = exn; } JS_ClearPendingException(aCx); } } // Wrap the return value into whatever compartment aCx was in. if (aRetValue) { JS::Rooted<JS::Value> v(aCx, *aRetValue); if (!JS_WrapValue(aCx, &v)) { return NS_ERROR_OUT_OF_MEMORY; } *aRetValue = v; } return rv; }
nsresult nsJSUtils::EvaluateString(JSContext* aCx, const nsAString& aScript, JS::Handle<JSObject*> aScopeObject, JS::CompileOptions& aCompileOptions, EvaluateOptions& aEvaluateOptions, JS::Value* aRetValue) { PROFILER_LABEL("JS", "EvaluateString"); MOZ_ASSERT_IF(aCompileOptions.versionSet, aCompileOptions.version != JSVERSION_UNKNOWN); MOZ_ASSERT_IF(aEvaluateOptions.coerceToString, aRetValue); MOZ_ASSERT_IF(!aEvaluateOptions.reportUncaught, aRetValue); MOZ_ASSERT(aCx == nsContentUtils::GetCurrentJSContext()); // Unfortunately, the JS engine actually compiles scripts with a return value // in a different, less efficient way. Furthermore, it can't JIT them in many // cases. So we need to be explicitly told whether the caller cares about the // return value. Callers use null to indicate they don't care. if (aRetValue) { *aRetValue = JSVAL_VOID; } xpc_UnmarkGrayObject(aScopeObject); nsAutoMicroTask mt; JSPrincipals* p = JS_GetCompartmentPrincipals(js::GetObjectCompartment(aScopeObject)); aCompileOptions.setPrincipals(p); bool ok = false; nsresult rv = nsContentUtils::GetSecurityManager()-> CanExecuteScripts(aCx, nsJSPrincipals::get(p), &ok); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(ok, NS_OK); // Scope the JSAutoCompartment so that we can later wrap the return value // into the caller's cx. { JSAutoCompartment ac(aCx, aScopeObject); JS::RootedObject rootedScope(aCx, aScopeObject); ok = JS::Evaluate(aCx, rootedScope, aCompileOptions, PromiseFlatString(aScript).get(), aScript.Length(), aRetValue); if (ok && aEvaluateOptions.coerceToString && !aRetValue->isUndefined()) { JSString* str = JS_ValueToString(aCx, *aRetValue); ok = !!str; *aRetValue = ok ? JS::StringValue(str) : JS::UndefinedValue(); } } if (!ok) { if (aEvaluateOptions.reportUncaught) { ReportPendingException(aCx); if (aRetValue) { *aRetValue = JS::UndefinedValue(); } } else { rv = JS_IsExceptionPending(aCx) ? NS_ERROR_FAILURE : NS_ERROR_OUT_OF_MEMORY; JS_GetPendingException(aCx, aRetValue); JS_ClearPendingException(aCx); } } // Wrap the return value into whatever compartment aCx was in. if (aRetValue && !JS_WrapValue(aCx, aRetValue)) return NS_ERROR_OUT_OF_MEMORY; return rv; }