зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1271650 - Move EvaluateInEnv and DebuggerGenericEval. r=jimb
This commit is contained in:
Родитель
d125232b98
Коммит
e430a1e3bd
|
@ -7291,6 +7291,174 @@ DebuggerFrame::getImplementation(HandleDebuggerFrame frame)
|
||||||
return DebuggerFrameImplementation::Interpreter;
|
return DebuggerFrameImplementation::Interpreter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Evaluate |chars[0..length-1]| in the environment |env|, treating that
|
||||||
|
* source as appearing starting at |lineno| in |filename|. Store the return
|
||||||
|
* value in |*rval|. Use |thisv| as the 'this' value.
|
||||||
|
*
|
||||||
|
* If |frame| is non-nullptr, evaluate as for a direct eval in that frame; |env|
|
||||||
|
* must be either |frame|'s DebugScopeObject, or some extension of that
|
||||||
|
* environment; either way, |frame|'s scope is where newly declared variables
|
||||||
|
* go. In this case, |frame| must have a computed 'this' value, equal to |thisv|.
|
||||||
|
*/
|
||||||
|
static bool
|
||||||
|
EvaluateInEnv(JSContext* cx, Handle<Env*> env, AbstractFramePtr frame,
|
||||||
|
jsbytecode* pc, mozilla::Range<const char16_t> chars, const char* filename,
|
||||||
|
unsigned lineno, MutableHandleValue rval)
|
||||||
|
{
|
||||||
|
assertSameCompartment(cx, env, frame);
|
||||||
|
MOZ_ASSERT_IF(frame, pc);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pass in a StaticEvalScope *not* linked to env for evalStaticScope, as
|
||||||
|
* ScopeIter should stop at any non-ScopeObject or non-syntactic With
|
||||||
|
* boundaries, and we are putting a DebugScopeProxy or non-syntactic With on
|
||||||
|
* the scope chain.
|
||||||
|
*/
|
||||||
|
Rooted<StaticScope*> enclosingStaticScope(cx);
|
||||||
|
if (!IsGlobalLexicalScope(env)) {
|
||||||
|
// If we are doing a global evalWithBindings, we will still need to
|
||||||
|
// link the static global lexical scope to the static non-syntactic
|
||||||
|
// scope.
|
||||||
|
if (IsGlobalLexicalScope(env->enclosingScope()))
|
||||||
|
enclosingStaticScope = &cx->global()->lexicalScope().staticBlock();
|
||||||
|
enclosingStaticScope = StaticNonSyntacticScope::create(cx, enclosingStaticScope);
|
||||||
|
if (!enclosingStaticScope)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
enclosingStaticScope = &cx->global()->lexicalScope().staticBlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do not consider executeInGlobal{WithBindings} as an eval, but instead
|
||||||
|
// as executing a series of statements at the global level. This is to
|
||||||
|
// circumvent the fresh lexical scope that all eval have, so that the
|
||||||
|
// users of executeInGlobal, like the web console, may add new bindings to
|
||||||
|
// the global scope.
|
||||||
|
Rooted<StaticScope*> staticScope(cx);
|
||||||
|
if (frame) {
|
||||||
|
staticScope = StaticEvalScope::create(cx, enclosingStaticScope);
|
||||||
|
if (!staticScope)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
staticScope = enclosingStaticScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
CompileOptions options(cx);
|
||||||
|
options.setIsRunOnce(true)
|
||||||
|
.setForEval(true)
|
||||||
|
.setNoScriptRval(false)
|
||||||
|
.setFileAndLine(filename, lineno)
|
||||||
|
.setCanLazilyParse(false)
|
||||||
|
.setIntroductionType("debugger eval")
|
||||||
|
.maybeMakeStrictMode(frame ? frame.script()->strict() : false);
|
||||||
|
RootedScript callerScript(cx, frame ? frame.script() : nullptr);
|
||||||
|
SourceBufferHolder srcBuf(chars.start().get(), chars.length(), SourceBufferHolder::NoOwnership);
|
||||||
|
RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(), env, staticScope,
|
||||||
|
callerScript, options, srcBuf,
|
||||||
|
/* source = */ nullptr));
|
||||||
|
if (!script)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Again, executeInGlobal is not considered eval.
|
||||||
|
if (frame) {
|
||||||
|
if (script->strict())
|
||||||
|
staticScope->as<StaticEvalScope>().setStrict();
|
||||||
|
script->setActiveEval();
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExecuteKernel(cx, script, *env, NullValue(), frame, rval.address());
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
DebuggerGenericEval(JSContext* cx, const mozilla::Range<const char16_t> chars,
|
||||||
|
HandleObject bindings, const EvalOptions& options,
|
||||||
|
JSTrapStatus& status, MutableHandleValue value,
|
||||||
|
Debugger* dbg, HandleObject scope, ScriptFrameIter* iter)
|
||||||
|
{
|
||||||
|
/* Either we're specifying the frame, or a global. */
|
||||||
|
MOZ_ASSERT_IF(iter, !scope);
|
||||||
|
MOZ_ASSERT_IF(!iter, scope && IsGlobalLexicalScope(scope));
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Gather keys and values of bindings, if any. This must be done in the
|
||||||
|
* debugger compartment, since that is where any exceptions must be
|
||||||
|
* thrown.
|
||||||
|
*/
|
||||||
|
AutoIdVector keys(cx);
|
||||||
|
AutoValueVector values(cx);
|
||||||
|
if (bindings) {
|
||||||
|
if (!GetPropertyKeys(cx, bindings, JSITER_OWNONLY, &keys) ||
|
||||||
|
!values.growBy(keys.length()))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (size_t i = 0; i < keys.length(); i++) {
|
||||||
|
MutableHandleValue valp = values[i];
|
||||||
|
if (!GetProperty(cx, bindings, bindings, keys[i], valp) ||
|
||||||
|
!dbg->unwrapDebuggeeValue(cx, valp))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Maybe<AutoCompartment> ac;
|
||||||
|
if (iter)
|
||||||
|
ac.emplace(cx, iter->scopeChain(cx));
|
||||||
|
else
|
||||||
|
ac.emplace(cx, scope);
|
||||||
|
|
||||||
|
Rooted<Env*> env(cx);
|
||||||
|
if (iter) {
|
||||||
|
env = GetDebugScopeForFrame(cx, iter->abstractFramePtr(), iter->pc());
|
||||||
|
if (!env)
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
env = scope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* If evalWithBindings, create the inner environment. */
|
||||||
|
if (bindings) {
|
||||||
|
RootedPlainObject nenv(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
|
||||||
|
if (!nenv)
|
||||||
|
return false;
|
||||||
|
RootedId id(cx);
|
||||||
|
for (size_t i = 0; i < keys.length(); i++) {
|
||||||
|
id = keys[i];
|
||||||
|
MutableHandleValue val = values[i];
|
||||||
|
if (!cx->compartment()->wrap(cx, val) ||
|
||||||
|
!NativeDefineProperty(cx, nenv, id, val, nullptr, nullptr, 0))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AutoObjectVector scopeChain(cx);
|
||||||
|
if (!scopeChain.append(nenv))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RootedObject dynamicScope(cx);
|
||||||
|
if (!CreateScopeObjectsForScopeChain(cx, scopeChain, env, &dynamicScope))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
env = dynamicScope;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Run the code and produce the completion value. */
|
||||||
|
LeaveDebuggeeNoExecute nnx(cx);
|
||||||
|
RootedValue rval(cx);
|
||||||
|
AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr();
|
||||||
|
jsbytecode* pc = iter ? iter->pc() : nullptr;
|
||||||
|
|
||||||
|
bool ok = EvaluateInEnv(cx, env, frame, pc, chars,
|
||||||
|
options.filename() ? options.filename() : "debugger eval code",
|
||||||
|
options.lineno(), &rval);
|
||||||
|
Debugger::resultToCompletion(cx, ok, rval, &status, value);
|
||||||
|
ac.reset();
|
||||||
|
return dbg->wrapDebuggeeValue(cx, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* statuc */ bool
|
/* statuc */ bool
|
||||||
DebuggerFrame::isLive() const
|
DebuggerFrame::isLive() const
|
||||||
{
|
{
|
||||||
|
@ -7841,173 +8009,6 @@ DebuggerFrame_setOnPop(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Evaluate |chars[0..length-1]| in the environment |env|, treating that
|
|
||||||
* source as appearing starting at |lineno| in |filename|. Store the return
|
|
||||||
* value in |*rval|. Use |thisv| as the 'this' value.
|
|
||||||
*
|
|
||||||
* If |frame| is non-nullptr, evaluate as for a direct eval in that frame; |env|
|
|
||||||
* must be either |frame|'s DebugScopeObject, or some extension of that
|
|
||||||
* environment; either way, |frame|'s scope is where newly declared variables
|
|
||||||
* go. In this case, |frame| must have a computed 'this' value, equal to |thisv|.
|
|
||||||
*/
|
|
||||||
static bool
|
|
||||||
EvaluateInEnv(JSContext* cx, Handle<Env*> env, AbstractFramePtr frame,
|
|
||||||
jsbytecode* pc, mozilla::Range<const char16_t> chars, const char* filename,
|
|
||||||
unsigned lineno, MutableHandleValue rval)
|
|
||||||
{
|
|
||||||
assertSameCompartment(cx, env, frame);
|
|
||||||
MOZ_ASSERT_IF(frame, pc);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Pass in a StaticEvalScope *not* linked to env for evalStaticScope, as
|
|
||||||
* ScopeIter should stop at any non-ScopeObject or non-syntactic With
|
|
||||||
* boundaries, and we are putting a DebugScopeProxy or non-syntactic With on
|
|
||||||
* the scope chain.
|
|
||||||
*/
|
|
||||||
Rooted<StaticScope*> enclosingStaticScope(cx);
|
|
||||||
if (!IsGlobalLexicalScope(env)) {
|
|
||||||
// If we are doing a global evalWithBindings, we will still need to
|
|
||||||
// link the static global lexical scope to the static non-syntactic
|
|
||||||
// scope.
|
|
||||||
if (IsGlobalLexicalScope(env->enclosingScope()))
|
|
||||||
enclosingStaticScope = &cx->global()->lexicalScope().staticBlock();
|
|
||||||
enclosingStaticScope = StaticNonSyntacticScope::create(cx, enclosingStaticScope);
|
|
||||||
if (!enclosingStaticScope)
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
enclosingStaticScope = &cx->global()->lexicalScope().staticBlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do not consider executeInGlobal{WithBindings} as an eval, but instead
|
|
||||||
// as executing a series of statements at the global level. This is to
|
|
||||||
// circumvent the fresh lexical scope that all eval have, so that the
|
|
||||||
// users of executeInGlobal, like the web console, may add new bindings to
|
|
||||||
// the global scope.
|
|
||||||
Rooted<StaticScope*> staticScope(cx);
|
|
||||||
if (frame) {
|
|
||||||
staticScope = StaticEvalScope::create(cx, enclosingStaticScope);
|
|
||||||
if (!staticScope)
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
staticScope = enclosingStaticScope;
|
|
||||||
}
|
|
||||||
|
|
||||||
CompileOptions options(cx);
|
|
||||||
options.setIsRunOnce(true)
|
|
||||||
.setForEval(true)
|
|
||||||
.setNoScriptRval(false)
|
|
||||||
.setFileAndLine(filename, lineno)
|
|
||||||
.setCanLazilyParse(false)
|
|
||||||
.setIntroductionType("debugger eval")
|
|
||||||
.maybeMakeStrictMode(frame ? frame.script()->strict() : false);
|
|
||||||
RootedScript callerScript(cx, frame ? frame.script() : nullptr);
|
|
||||||
SourceBufferHolder srcBuf(chars.start().get(), chars.length(), SourceBufferHolder::NoOwnership);
|
|
||||||
RootedScript script(cx, frontend::CompileScript(cx, &cx->tempLifoAlloc(), env, staticScope,
|
|
||||||
callerScript, options, srcBuf,
|
|
||||||
/* source = */ nullptr));
|
|
||||||
if (!script)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Again, executeInGlobal is not considered eval.
|
|
||||||
if (frame) {
|
|
||||||
if (script->strict())
|
|
||||||
staticScope->as<StaticEvalScope>().setStrict();
|
|
||||||
script->setActiveEval();
|
|
||||||
}
|
|
||||||
|
|
||||||
return ExecuteKernel(cx, script, *env, NullValue(), frame, rval.address());
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
DebuggerGenericEval(JSContext* cx, const mozilla::Range<const char16_t> chars,
|
|
||||||
HandleObject bindings, const EvalOptions& options,
|
|
||||||
JSTrapStatus& status, MutableHandleValue value,
|
|
||||||
Debugger* dbg, HandleObject scope, ScriptFrameIter* iter)
|
|
||||||
{
|
|
||||||
/* Either we're specifying the frame, or a global. */
|
|
||||||
MOZ_ASSERT_IF(iter, !scope);
|
|
||||||
MOZ_ASSERT_IF(!iter, scope && IsGlobalLexicalScope(scope));
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Gather keys and values of bindings, if any. This must be done in the
|
|
||||||
* debugger compartment, since that is where any exceptions must be
|
|
||||||
* thrown.
|
|
||||||
*/
|
|
||||||
AutoIdVector keys(cx);
|
|
||||||
AutoValueVector values(cx);
|
|
||||||
if (bindings) {
|
|
||||||
if (!GetPropertyKeys(cx, bindings, JSITER_OWNONLY, &keys) ||
|
|
||||||
!values.growBy(keys.length()))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (size_t i = 0; i < keys.length(); i++) {
|
|
||||||
MutableHandleValue valp = values[i];
|
|
||||||
if (!GetProperty(cx, bindings, bindings, keys[i], valp) ||
|
|
||||||
!dbg->unwrapDebuggeeValue(cx, valp))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Maybe<AutoCompartment> ac;
|
|
||||||
if (iter)
|
|
||||||
ac.emplace(cx, iter->scopeChain(cx));
|
|
||||||
else
|
|
||||||
ac.emplace(cx, scope);
|
|
||||||
|
|
||||||
Rooted<Env*> env(cx);
|
|
||||||
if (iter) {
|
|
||||||
env = GetDebugScopeForFrame(cx, iter->abstractFramePtr(), iter->pc());
|
|
||||||
if (!env)
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
env = scope;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If evalWithBindings, create the inner environment. */
|
|
||||||
if (bindings) {
|
|
||||||
RootedPlainObject nenv(cx, NewObjectWithGivenProto<PlainObject>(cx, nullptr));
|
|
||||||
if (!nenv)
|
|
||||||
return false;
|
|
||||||
RootedId id(cx);
|
|
||||||
for (size_t i = 0; i < keys.length(); i++) {
|
|
||||||
id = keys[i];
|
|
||||||
MutableHandleValue val = values[i];
|
|
||||||
if (!cx->compartment()->wrap(cx, val) ||
|
|
||||||
!NativeDefineProperty(cx, nenv, id, val, nullptr, nullptr, 0))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AutoObjectVector scopeChain(cx);
|
|
||||||
if (!scopeChain.append(nenv))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
RootedObject dynamicScope(cx);
|
|
||||||
if (!CreateScopeObjectsForScopeChain(cx, scopeChain, env, &dynamicScope))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
env = dynamicScope;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Run the code and produce the completion value. */
|
|
||||||
LeaveDebuggeeNoExecute nnx(cx);
|
|
||||||
RootedValue rval(cx);
|
|
||||||
AbstractFramePtr frame = iter ? iter->abstractFramePtr() : NullFramePtr();
|
|
||||||
jsbytecode* pc = iter ? iter->pc() : nullptr;
|
|
||||||
|
|
||||||
bool ok = EvaluateInEnv(cx, env, frame, pc, chars,
|
|
||||||
options.filename() ? options.filename() : "debugger eval code",
|
|
||||||
options.lineno(), &rval);
|
|
||||||
Debugger::resultToCompletion(cx, ok, rval, &status, value);
|
|
||||||
ac.reset();
|
|
||||||
return dbg->wrapDebuggeeValue(cx, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
DebuggerFrame_eval(JSContext* cx, unsigned argc, Value* vp)
|
DebuggerFrame_eval(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
|
|
Загрузка…
Ссылка в новой задаче