зеркало из 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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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
|
||||
DebuggerFrame::isLive() const
|
||||
{
|
||||
|
@ -7841,173 +8009,6 @@ DebuggerFrame_setOnPop(JSContext* cx, unsigned argc, Value* vp)
|
|||
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
|
||||
DebuggerFrame_eval(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
|
|
Загрузка…
Ссылка в новой задаче