Bug 1398601 - Support target objects in js::ExecuteInJSMEnvironment r=jorendorff

This allows js::ExecuteInJSMEnvironment to take a target object argument
as used by the subscript loader. This adds WithEnvironments with a
corresponding lexical on top of the ordinary NonSyntacticVariablesObject
environment chain.

MozReview-Commit-ID: JhHEfV92Zpv

--HG--
extra : rebase_source : d1ef9564d30a25fd9e1cf1ca7e95bf40c780dcdf
This commit is contained in:
Ted Campbell 2017-09-10 14:03:42 -04:00
parent 39e87f69a6
commit 0260bcbae4
3 changed files with 110 additions and 15 deletions

View File

@ -445,19 +445,14 @@ js::IsAnyBuiltinEval(JSFunction* fun)
}
static bool
ExecuteInNonSyntacticGlobalInternal(JSContext* cx, HandleObject global, HandleScript scriptArg,
HandleObject varEnv, HandleObject lexEnv)
ExecuteInExtensibleLexicalEnvironment(JSContext* cx, HandleScript scriptArg, HandleObject env)
{
CHECK_REQUEST(cx);
assertSameCompartment(cx, global, varEnv, lexEnv);
MOZ_ASSERT(global->is<GlobalObject>());
MOZ_ASSERT(varEnv->is<NonSyntacticVariablesObject>());
MOZ_ASSERT(IsExtensibleLexicalEnvironment(lexEnv));
MOZ_ASSERT(lexEnv->enclosingEnvironment() == varEnv);
assertSameCompartment(cx, env);
MOZ_ASSERT(IsExtensibleLexicalEnvironment(env));
MOZ_RELEASE_ASSERT(scriptArg->hasNonSyntacticScope());
RootedScript script(cx, scriptArg);
Rooted<GlobalObject*> globalRoot(cx, &global->as<GlobalObject>());
if (script->compartment() != cx->compartment()) {
script = CloneGlobalScript(cx, ScopeKind::NonSyntactic, script);
if (!script)
@ -467,7 +462,7 @@ ExecuteInNonSyntacticGlobalInternal(JSContext* cx, HandleObject global, HandleSc
}
RootedValue rval(cx);
return ExecuteKernel(cx, script, *lexEnv, UndefinedValue(),
return ExecuteKernel(cx, script, *env, UndefinedValue(),
NullFramePtr() /* evalInFrame */, rval.address());
}
@ -485,7 +480,7 @@ js::ExecuteInGlobalAndReturnScope(JSContext* cx, HandleObject global, HandleScri
if (!lexEnv)
return false;
if (!ExecuteInNonSyntacticGlobalInternal(cx, global, scriptArg, varEnv, lexEnv))
if (!ExecuteInExtensibleLexicalEnvironment(cx, scriptArg, lexEnv))
return false;
envArg.set(lexEnv);
@ -509,14 +504,51 @@ js::NewJSMEnvironment(JSContext* cx)
JS_FRIEND_API(bool)
js::ExecuteInJSMEnvironment(JSContext* cx, HandleScript scriptArg, HandleObject varEnv)
{
AutoObjectVector emptyChain(cx);
return ExecuteInJSMEnvironment(cx, scriptArg, varEnv, emptyChain);
}
JS_FRIEND_API(bool)
js::ExecuteInJSMEnvironment(JSContext* cx, HandleScript scriptArg, HandleObject varEnv,
AutoObjectVector& targetObj)
{
assertSameCompartment(cx, varEnv);
MOZ_ASSERT(cx->compartment()->getNonSyntacticLexicalEnvironment(varEnv));
MOZ_DIAGNOSTIC_ASSERT(scriptArg->noScriptRval());
RootedObject global(cx, &varEnv->global());
RootedObject lexEnv(cx, JS_ExtensibleLexicalEnvironment(varEnv));
RootedObject env(cx, JS_ExtensibleLexicalEnvironment(varEnv));
return ExecuteInNonSyntacticGlobalInternal(cx, global, scriptArg, varEnv, lexEnv);
// If the Gecko subscript loader specifies target objects, we need to add
// them to the environment. These are added after the NSVO environment.
if (!targetObj.empty()) {
// The environment chain will be as follows:
// GlobalObject / BackstagePass
// LexicalEnvironmentObject[this=global]
// NonSyntacticVariablesObject (the JSMEnvironment)
// LexicalEnvironmentObject[this=nsvo]
// WithEnvironmentObject[target=targetObj]
// LexicalEnvironmentObject[this=targetObj] (*)
//
// (*) This environment intentionally intercepts JSOP_GLOBALTHIS, but
// not JSOP_FUNCTIONTHIS (which instead will fallback to the NSVO). I
// don't make the rules, I just record them.
// Wrap the target objects in WithEnvironments.
if (!js::CreateObjectsForEnvironmentChain(cx, targetObj, env, &env))
return false;
// See CreateNonSyntacticEnvironmentChain
if (!JSObject::setQualifiedVarObj(cx, env))
return false;
// Create an extensible LexicalEnvironmentObject for target object
env = cx->compartment()->getOrCreateNonSyntacticLexicalEnvironment(cx, env);
if (!env)
return false;
}
return ExecuteInExtensibleLexicalEnvironment(cx, scriptArg, env);
}
JS_FRIEND_API(JSObject*)
@ -536,3 +568,11 @@ js::GetJSMEnvironmentOfScriptedCaller(JSContext* cx)
return env;
}
JS_FRIEND_API(bool)
js::IsJSMEnvironment(JSObject* obj)
{
// NOTE: This also returns true if the NonSyntacticVariablesObject was
// created for reasons other than the JSM loader.
return obj->is<NonSyntacticVariablesObject>();
}

View File

@ -24,6 +24,7 @@ BEGIN_TEST(testExecuteInJSMEnvironment_Basic)
JS::CompileOptions options(cx);
options.setFileAndLine(__FILE__, __LINE__);
options.setNoScriptRval(true);
JS::RootedScript script(cx);
CHECK(JS::CompileForNonSyntacticScope(cx, options, src, sizeof(src)-1, &script));
@ -82,6 +83,7 @@ BEGIN_TEST(testExecuteInJSMEnvironment_Callback)
JS::CompileOptions options(cx);
options.setFileAndLine(__FILE__, __LINE__);
options.setNoScriptRval(true);
JS::RootedScript script(cx);
CHECK(JS::CompileForNonSyntacticScope(cx, options, src, sizeof(src)-1, &script));

View File

@ -2888,16 +2888,69 @@ extern JS_FRIEND_API(bool)
ExecuteInGlobalAndReturnScope(JSContext* cx, JS::HandleObject obj, JS::HandleScript script,
JS::MutableHandleObject scope);
// These functions are only for JSM component loader, please don't use this for anything else!
// These functions are provided for the JSM component loader in Gecko.
//
// A 'JSMEnvironment' refers to an environment chain constructed for JSM loading
// in a shared global. Internally it is a NonSyntacticVariablesObject with a
// corresponding extensible LexicalEnvironmentObject that is accessible by
// JS_ExtensibleLexicalEnvironment. The |this| value of that lexical environment
// is the NSVO itself.
//
// Normal global environment (ES6): JSM "global" environment:
//
// * - extensible lexical environment
// | (code runs in this environment;
// | `let/const` bindings go here)
// |
// * - JSMEnvironment (=== `this`)
// | (`var` bindings go here)
// |
// * - extensible lexical environment * - extensible lexical environment
// | (code runs in this environment; | (empty)
// | `let/const` bindings go here) |
// | |
// * - actual global (=== `this`) * - shared JSM global
// (var bindings go here; and (Object, Math, etc. live here)
// Object, Math, etc. live here)
// Allocate a new environment in current compartment that is compatible with JSM
// shared loading.
extern JS_FRIEND_API(JSObject*)
NewJSMEnvironment(JSContext* cx);
// Execute the given script (copied into compartment if necessary) in the given
// JSMEnvironment. The script must have been compiled for hasNonSyntacticScope.
// The |jsmEnv| must have been previously allocated by NewJSMEnvironment.
//
// NOTE: The associated extensible lexical environment is reused.
extern JS_FRIEND_API(bool)
ExecuteInJSMEnvironment(JSContext* cx, JS::HandleScript script, JS::HandleObject nsvo);
ExecuteInJSMEnvironment(JSContext* cx, JS::HandleScript script, JS::HandleObject jsmEnv);
// Additionally, target objects may be specified as required by the Gecko
// subscript loader. These are wrapped in non-syntactic WithEnvironments and
// temporarily placed on environment chain.
//
// See also: JS::CloneAndExecuteScript(...)
extern JS_FRIEND_API(bool)
ExecuteInJSMEnvironment(JSContext* cx, JS::HandleScript script, JS::HandleObject jsmEnv,
JS::AutoObjectVector& targetObj);
// Used by native methods to determine the JSMEnvironment of caller if possible
// by looking at stack frames. Returns nullptr if top frame isn't a scripted
// caller in a JSM.
//
// NOTE: This may find NonSyntacticVariablesObject generated by other embedding
// such as a Gecko FrameScript. Caller can check the compartment if needed.
extern JS_FRIEND_API(JSObject*)
GetJSMEnvironmentOfScriptedCaller(JSContext* cx);
// Determine if obj is a JSMEnvironment
//
// NOTE: This may return true for an NonSyntacticVariablesObject generated by
// other embedding such as a Gecko FrameScript. Caller can check compartment.
extern JS_FRIEND_API(bool)
IsJSMEnvironment(JSObject* obj);
#if defined(XP_WIN) && defined(_WIN64)
// Parameters use void* types to avoid #including windows.h. The return value of