Bug 901162 - Hoist EvaluateString into nsJSUtils. r=bz

This commit is contained in:
Bobby Holley 2013-08-08 16:51:34 -07:00
parent f2b057f9f0
commit 1315953921
3 changed files with 78 additions and 54 deletions

View file

@ -1241,70 +1241,21 @@ nsJSContext::EvaluateString(const nsAString& aScript,
bool aCoerceToString,
JS::Value* aRetValue)
{
PROFILER_LABEL("JS", "EvaluateString");
MOZ_ASSERT_IF(aOptions.versionSet, aOptions.version != JSVERSION_UNKNOWN);
MOZ_ASSERT_IF(aCoerceToString, aRetValue);
NS_ENSURE_TRUE(mIsInitialized, NS_ERROR_NOT_INITIALIZED);
// 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;
}
nsresult rv;
if (!mScriptsEnabled) {
return NS_OK;
}
nsCxPusher pusher;
pusher.Push(mContext);
xpc_UnmarkGrayObject(aScopeObject);
nsAutoMicroTask mt;
JSPrincipals* p = JS_GetCompartmentPrincipals(js::GetObjectCompartment(aScopeObject));
aOptions.setPrincipals(p);
bool ok = false;
nsresult rv = sSecurityManager->CanExecuteScripts(mContext, nsJSPrincipals::get(p), &ok);
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(ok, NS_OK);
// Scope the JSAutoCompartment so that it gets destroyed before we pop the
// cx and potentially call JS_RestoreFrameChain.
{
JSAutoCompartment ac(mContext, aScopeObject);
JS::RootedObject rootedScope(mContext, aScopeObject);
ok = JS::Evaluate(mContext, rootedScope, aOptions,
PromiseFlatString(aScript).get(),
aScript.Length(), aRetValue);
if (ok && aCoerceToString && !aRetValue->isUndefined()) {
JSString* str = JS_ValueToString(mContext, *aRetValue);
ok = !!str;
*aRetValue = ok ? JS::StringValue(str) : JS::UndefinedValue();
}
}
if (!ok) {
if (aRetValue) {
*aRetValue = JS::UndefinedValue();
}
// Tell XPConnect about any pending exceptions. This is needed
// to avoid dropping JS exceptions in case we got here through
// nested calls through XPConnect.
ReportPendingException();
AutoCxPusher pusher(mContext);
rv = nsJSUtils::EvaluateString(mContext, aScript, aScopeObject,
aOptions, aCoerceToString, aRetValue);
}
// ScriptEvaluated needs to come after we pop the stack
pusher.Pop();
ScriptEvaluated(true);
// Wrap the return value into whatever compartment mContext was in.
if (aRetValue && !JS_WrapValue(mContext, aRetValue))
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
return rv;
}
nsresult

View file

@ -22,6 +22,7 @@
#include "nsCOMPtr.h"
#include "nsIScriptSecurityManager.h"
#include "nsPIDOMWindow.h"
#include "GeckoProfiler.h"
#include "nsDOMJSUtils.h" // for GetScriptContextFromJSContext
@ -192,3 +193,68 @@ nsJSUtils::CompileFunction(JSContext* aCx,
*aFunctionObject = JS_GetFunctionObject(fun);
return NS_OK;
}
nsresult
nsJSUtils::EvaluateString(JSContext* aCx,
const nsAString& aScript,
JS::Handle<JSObject*> aScopeObject,
JS::CompileOptions& aOptions,
bool aCoerceToString,
JS::Value* aRetValue)
{
PROFILER_LABEL("JS", "EvaluateString");
MOZ_ASSERT_IF(aOptions.versionSet, aOptions.version != JSVERSION_UNKNOWN);
MOZ_ASSERT_IF(aCoerceToString, 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));
aOptions.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, aOptions,
PromiseFlatString(aScript).get(),
aScript.Length(), aRetValue);
if (ok && aCoerceToString && !aRetValue->isUndefined()) {
JSString* str = JS_ValueToString(aCx, *aRetValue);
ok = !!str;
*aRetValue = ok ? JS::StringValue(str) : JS::UndefinedValue();
}
}
if (!ok) {
if (aRetValue) {
*aRetValue = JS::UndefinedValue();
}
// Tell XPConnect about any pending exceptions. This is needed
// to avoid dropping JS exceptions in case we got here through
// nested calls through XPConnect.
ReportPendingException(aCx);
}
// Wrap the return value into whatever compartment aCx was in.
if (aRetValue && !JS_WrapValue(aCx, aRetValue))
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
}

View file

@ -64,6 +64,13 @@ public:
const nsAString& aBody,
JSObject** aFunctionObject);
static nsresult EvaluateString(JSContext* aCx,
const nsAString& aScript,
JS::Handle<JSObject*> aScopeObject,
JS::CompileOptions &aOptions,
bool aCoerceToString,
JS::Value* aRetValue);
};