[runtime] Do not refer directly to the closure stored in the context

This is is a preparatory CL to detach the JSFunction from the Context.
We mainly rewrite the DebugScopeInterator to no longer rely on the a
JSFunction to be around. Additionally the empty_function needs to have
a proper ScopeInfo now.

Drive-by-fix: Improve ScopeInfo debug printing

Bug: v8:7066
Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel
Change-Id: I2f2fa0e78914a12e076384e0e1234c2322ad1ee8
Reviewed-on: https://chromium-review.googlesource.com/918721
Commit-Queue: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Toon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#52791}
This commit is contained in:
Camillo Bruni 2018-04-25 11:52:59 +02:00 committed by Commit Bot
parent e570e67383
commit a3142476ba
19 changed files with 393 additions and 154 deletions

View File

@ -161,7 +161,7 @@ class Genesis BASE_EMBEDDED {
// Creates some basic objects. Used for creating a context from scratch.
void CreateRoots();
// Creates the empty function. Used for creating a context from scratch.
Handle<JSFunction> CreateEmptyFunction(Isolate* isolate);
Handle<JSFunction> CreateEmptyFunction();
// Returns the %ThrowTypeError% intrinsic function.
// See ES#sec-%throwtypeerror% for details.
Handle<JSFunction> GetThrowTypeErrorIntrinsic();
@ -590,29 +590,33 @@ V8_NOINLINE void InstallSpeciesGetter(Handle<JSFunction> constructor) {
} // namespace
Handle<JSFunction> Genesis::CreateEmptyFunction(Isolate* isolate) {
Factory* factory = isolate->factory();
Handle<JSFunction> Genesis::CreateEmptyFunction() {
// Allocate the function map first and then patch the prototype later.
Handle<Map> empty_function_map = factory->CreateSloppyFunctionMap(
Handle<Map> empty_function_map = factory()->CreateSloppyFunctionMap(
FUNCTION_WITHOUT_PROTOTYPE, MaybeHandle<JSFunction>());
empty_function_map->set_is_prototype_map(true);
DCHECK(!empty_function_map->is_dictionary_map());
// Allocate ScopeInfo for the empty function.
Handle<ScopeInfo> scope_info = ScopeInfo::CreateForEmptyFunction(isolate());
// Allocate the empty function as the prototype for function according to
// ES#sec-properties-of-the-function-prototype-object
NewFunctionArgs args = NewFunctionArgs::ForBuiltin(
factory->empty_string(), empty_function_map, Builtins::kEmptyFunction);
Handle<JSFunction> empty_function = factory->NewFunction(args);
factory()->empty_string(), empty_function_map, Builtins::kEmptyFunction);
Handle<JSFunction> empty_function = factory()->NewFunction(args);
native_context()->set_empty_function(*empty_function);
// --- E m p t y ---
Handle<String> source = factory->NewStringFromStaticChars("() {}");
Handle<Script> script = factory->NewScript(source);
Handle<String> source = factory()->NewStringFromStaticChars("() {}");
Handle<Script> script = factory()->NewScript(source);
script->set_type(Script::TYPE_NATIVE);
Handle<WeakFixedArray> infos = factory->NewWeakFixedArray(2);
Handle<WeakFixedArray> infos = factory()->NewWeakFixedArray(2);
script->set_shared_function_infos(*infos);
// TODO(cbruni): fix position information here.
empty_function->shared()->set_raw_start_position(0);
empty_function->shared()->set_raw_end_position(source->length());
empty_function->shared()->set_scope_info(*scope_info);
empty_function->shared()->set_function_literal_id(1);
empty_function->shared()->DontAdaptArguments();
SharedFunctionInfo::SetScript(handle(empty_function->shared()), script);
@ -5385,7 +5389,7 @@ Genesis::Genesis(
DCHECK_EQ(0u, context_snapshot_index);
// We get here if there was no context snapshot.
CreateRoots();
Handle<JSFunction> empty_function = CreateEmptyFunction(isolate);
Handle<JSFunction> empty_function = CreateEmptyFunction();
CreateSloppyModeFunctionMaps(empty_function);
CreateStrictModeFunctionMaps(empty_function);
CreateObjectFunction(empty_function);

View File

@ -764,7 +764,8 @@ TF_BUILTIN(TypedArrayConstructor, TypedArrayBuiltinsAssembler) {
BIND(&throwtypeerror);
{
Node* name = CallRuntime(Runtime::kGetFunctionName, context, target);
TNode<String> name =
CAST(CallRuntime(Runtime::kGetFunctionName, context, target));
ThrowTypeError(context, MessageTemplate::kConstructorNotFunction, name);
}
}

View File

@ -114,6 +114,11 @@ JSReceiver* Context::extension_receiver() {
: extension_object();
}
ScopeInfo* Context::raw_scope_info() {
DCHECK(!IsNativeContext());
return closure()->shared()->scope_info();
}
ScopeInfo* Context::scope_info() {
DCHECK(!IsNativeContext());
if (IsFunctionContext() || IsModuleContext() || IsEvalContext()) {

View File

@ -137,6 +137,7 @@ enum ContextLookupFlags {
V(DATA_VIEW_FUN_INDEX, JSFunction, data_view_fun) \
V(DATE_FUNCTION_INDEX, JSFunction, date_function) \
V(DEBUG_CONTEXT_ID_INDEX, Object, debug_context_id) \
V(EMPTY_FUNCTION_INDEX, JSFunction, empty_function) \
V(ERROR_MESSAGE_FOR_CODE_GEN_FROM_STRINGS_INDEX, Object, \
error_message_for_code_gen_from_strings) \
V(ERRORS_THROWN_INDEX, Smi, errors_thrown) \
@ -487,6 +488,7 @@ class Context: public FixedArray {
inline void set_extension(HeapObject* object);
JSObject* extension_object();
JSReceiver* extension_receiver();
ScopeInfo* raw_scope_info();
ScopeInfo* scope_info();
String* catch_name();

View File

@ -440,6 +440,9 @@ class ScopeIterator {
virtual ScopeType GetType() = 0;
virtual v8::Local<v8::Object> GetObject() = 0;
virtual v8::Local<v8::Function> GetFunction() = 0;
virtual v8::Local<v8::Value> GetFunctionDebugName() = 0;
virtual int GetScriptId() = 0;
virtual bool HasLocationInfo() = 0;
virtual debug::Location GetStartLocation() = 0;
virtual debug::Location GetEndLocation() = 0;
@ -463,7 +466,7 @@ class StackTraceIterator {
virtual int GetContextId() const = 0;
virtual v8::MaybeLocal<v8::Value> GetReceiver() const = 0;
virtual v8::Local<v8::Value> GetReturnValue() const = 0;
virtual v8::Local<v8::String> GetFunctionName() const = 0;
virtual v8::Local<v8::String> GetFunctionDebugName() const = 0;
virtual v8::Local<v8::debug::Script> GetScript() const = 0;
virtual debug::Location GetSourceLocation() const = 0;
virtual v8::Local<v8::Function> GetFunction() const = 0;

View File

@ -113,28 +113,34 @@ v8::Local<v8::Object> DebugScopeIterator::GetObject() {
v8::Local<v8::Function> DebugScopeIterator::GetFunction() {
DCHECK(!Done());
Handle<JSFunction> closure = iterator_.GetClosure();
Handle<JSFunction> closure = iterator_.GetFunction();
if (closure.is_null()) return v8::Local<v8::Function>();
return Utils::ToLocal(closure);
}
int DebugScopeIterator::GetScriptId() {
DCHECK(!Done());
return iterator_.GetScript()->id();
}
v8::Local<v8::Value> DebugScopeIterator::GetFunctionDebugName() {
DCHECK(!Done());
Handle<Object> name = iterator_.GetFunctionDebugName();
return Utils::ToLocal(name);
}
bool DebugScopeIterator::HasLocationInfo() {
return iterator_.HasPositionInfo();
}
debug::Location DebugScopeIterator::GetStartLocation() {
DCHECK(!Done());
Handle<JSFunction> closure = iterator_.GetClosure();
if (closure.is_null()) return debug::Location();
Object* obj = closure->shared()->script();
if (!obj->IsScript()) return debug::Location();
return ToApiHandle<v8::debug::Script>(handle(Script::cast(obj)))
return ToApiHandle<v8::debug::Script>(iterator_.GetScript())
->GetSourceLocation(iterator_.start_position());
}
debug::Location DebugScopeIterator::GetEndLocation() {
DCHECK(!Done());
Handle<JSFunction> closure = iterator_.GetClosure();
if (closure.is_null()) return debug::Location();
Object* obj = closure->shared()->script();
if (!obj->IsScript()) return debug::Location();
return ToApiHandle<v8::debug::Script>(handle(Script::cast(obj)))
return ToApiHandle<v8::debug::Script>(iterator_.GetScript())
->GetSourceLocation(iterator_.end_position());
}
@ -190,11 +196,23 @@ v8::Local<v8::Object> DebugWasmScopeIterator::GetObject() {
return v8::Local<v8::Object>();
}
int DebugWasmScopeIterator::GetScriptId() {
DCHECK(!Done());
return -1;
}
v8::Local<v8::Function> DebugWasmScopeIterator::GetFunction() {
DCHECK(!Done());
return v8::Local<v8::Function>();
}
v8::Local<v8::Value> DebugWasmScopeIterator::GetFunctionDebugName() {
DCHECK(!Done());
return Utils::ToLocal(isolate_->factory()->empty_string());
}
bool DebugWasmScopeIterator::HasLocationInfo() { return false; }
debug::Location DebugWasmScopeIterator::GetStartLocation() {
DCHECK(!Done());
return debug::Location();

View File

@ -24,6 +24,9 @@ class DebugScopeIterator final : public debug::ScopeIterator {
ScopeType GetType() override;
v8::Local<v8::Object> GetObject() override;
v8::Local<v8::Function> GetFunction() override;
v8::Local<v8::Value> GetFunctionDebugName() override;
int GetScriptId() override;
bool HasLocationInfo() override;
debug::Location GetStartLocation() override;
debug::Location GetEndLocation() override;
@ -46,12 +49,14 @@ class DebugWasmScopeIterator final : public debug::ScopeIterator {
ScopeType GetType() override;
v8::Local<v8::Object> GetObject() override;
v8::Local<v8::Function> GetFunction() override;
v8::Local<v8::Value> GetFunctionDebugName() override;
int GetScriptId() override;
bool HasLocationInfo() override;
debug::Location GetStartLocation() override;
debug::Location GetEndLocation() override;
bool SetVariableValue(v8::Local<v8::String> name,
v8::Local<v8::Value> value) override;
private:
Isolate* isolate_;
StandardFrame* frame_;

View File

@ -23,11 +23,14 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
ScopeIterator::Option option)
: isolate_(isolate),
frame_inspector_(frame_inspector),
function_(frame_inspector_->GetFunction()),
script_(frame_inspector_->GetScript()),
seen_script_scope_(false) {
if (!frame_inspector->GetContext()->IsContext()) {
// Optimized frame, context or function cannot be materialized. Give up.
return;
}
context_ = Handle<Context>::cast(frame_inspector->GetContext());
// We should not instantiate a ScopeIterator for wasm frames.
DCHECK(frame_inspector->GetScript()->type() != Script::TYPE_WASM);
@ -35,17 +38,47 @@ ScopeIterator::ScopeIterator(Isolate* isolate, FrameInspector* frame_inspector,
TryParseAndRetrieveScopes(option);
}
void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
context_ = GetContext();
Handle<Object> ScopeIterator::GetFunctionDebugName() const {
if (HasNestedScopeChain()) return JSFunction::GetDebugName(function_);
if (!context_->IsNativeContext()) {
DisallowHeapAllocation no_gc;
Handle<String> debug_name(context_->raw_scope_info()->FunctionDebugName());
if (debug_name->length() > 0) return debug_name;
}
return isolate_->factory()->undefined_value();
}
ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
: isolate_(isolate),
context_(function->context()),
script_(Script::cast(function->shared()->script())),
seen_script_scope_(false) {
if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>();
UnwrapEvaluationContext();
}
ScopeIterator::ScopeIterator(Isolate* isolate,
Handle<JSGeneratorObject> generator)
: isolate_(isolate),
generator_(generator),
function_(generator->function()),
context_(generator->context()),
script_(Script::cast(function_->shared()->script())),
seen_script_scope_(false) {
if (!function_->shared()->IsSubjectToDebugging()) {
context_ = Handle<Context>();
return;
}
TryParseAndRetrieveScopes(DEFAULT);
}
void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
// Catch the case when the debugger stops in an internal function.
Handle<JSFunction> function = GetFunction();
Handle<SharedFunctionInfo> shared_info(function->shared());
Handle<SharedFunctionInfo> shared_info(function_->shared());
Handle<ScopeInfo> scope_info(shared_info->scope_info());
if (shared_info->script()->IsUndefined(isolate_)) {
while (context_->closure() == *function) {
context_ = Handle<Context>(context_->previous(), isolate_);
}
context_ = handle(function_->context());
function_ = Handle<JSFunction>();
return;
}
@ -77,9 +110,7 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
if (scope_info->HasContext()) {
context_ = Handle<Context>(context_->declaration_context(), isolate_);
} else {
while (context_->closure() == *function) {
context_ = Handle<Context>(context_->previous(), isolate_);
}
context_ = handle(function_->context());
}
if (scope_info->scope_type() == FUNCTION_SCOPE) {
nested_scope_chain_.emplace_back(scope_info, shared_info->StartPosition(),
@ -97,8 +128,8 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
info.reset(new ParseInfo(script));
if (scope_info->scope_type() == EVAL_SCOPE) {
info->set_eval();
if (!function->context()->IsNativeContext()) {
info->set_outer_scope_info(handle(function->context()->scope_info()));
if (!function_->context()->IsNativeContext()) {
info->set_outer_scope_info(handle(function_->context()->scope_info()));
}
// Language mode may be inherited from the eval caller.
// Retrieve it from shared function info.
@ -141,27 +172,6 @@ void ScopeIterator::TryParseAndRetrieveScopes(ScopeIterator::Option option) {
UnwrapEvaluationContext();
}
ScopeIterator::ScopeIterator(Isolate* isolate, Handle<JSFunction> function)
: isolate_(isolate),
context_(function->context()),
seen_script_scope_(false) {
if (!function->shared()->IsSubjectToDebugging()) context_ = Handle<Context>();
UnwrapEvaluationContext();
}
ScopeIterator::ScopeIterator(Isolate* isolate,
Handle<JSGeneratorObject> generator)
: isolate_(isolate),
generator_(generator),
context_(generator->context()),
seen_script_scope_(false) {
if (!generator->function()->shared()->IsSubjectToDebugging()) {
context_ = Handle<Context>();
return;
}
TryParseAndRetrieveScopes(DEFAULT);
}
void ScopeIterator::UnwrapEvaluationContext() {
while (true) {
if (context_.is_null()) return;
@ -188,46 +198,37 @@ ScopeIterator::MaterializeScopeDetails() {
details->set(kScopeDetailsObjectIndex, *scope_object);
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript) {
return isolate_->factory()->NewJSArrayWithElements(details);
}
Handle<JSFunction> js_function = GetClosure();
if (!js_function.is_null()) {
Handle<String> closure_name = JSFunction::GetDebugName(js_function);
if (!closure_name.is_null() && closure_name->length() != 0) {
} else if (HasContext()) {
Handle<Object> closure_name = GetFunctionDebugName();
details->set(kScopeDetailsNameIndex, *closure_name);
}
details->set(kScopeDetailsStartPositionIndex,
Smi::FromInt(start_position()));
details->set(kScopeDetailsEndPositionIndex, Smi::FromInt(end_position()));
details->set(kScopeDetailsFunctionIndex, *js_function);
if (HasNestedScopeChain()) {
details->set(kScopeDetailsFunctionIndex, *function_);
}
}
return isolate_->factory()->NewJSArrayWithElements(details);
}
Handle<JSFunction> ScopeIterator::GetClosure() {
if (Type() == ScopeTypeGlobal || Type() == ScopeTypeScript)
return Handle<JSFunction>::null();
if (HasNestedScopeChain()) return GetFunction();
return HasContext() ? handle(CurrentContext()->closure())
: Handle<JSFunction>::null();
bool ScopeIterator::HasPositionInfo() {
return HasNestedScopeChain() || !context_->IsNativeContext();
}
int ScopeIterator::start_position() {
if (HasNestedScopeChain()) {
return LastNestedScopeChain().start_position;
}
if (!HasContext()) return 0;
Handle<JSFunction> js_function = handle(CurrentContext()->closure());
return js_function.is_null() ? 0 : js_function->shared()->StartPosition();
if (context_->IsNativeContext()) return 0;
return context_->raw_scope_info()->StartPosition();
}
int ScopeIterator::end_position() {
if (HasNestedScopeChain()) {
return LastNestedScopeChain().end_position;
}
if (!HasContext()) return 0;
Handle<JSFunction> js_function = handle(CurrentContext()->closure());
return js_function.is_null() ? 0 : js_function->shared()->EndPosition();
if (context_->IsNativeContext()) return 0;
return context_->raw_scope_info()->EndPosition();
}
void ScopeIterator::Next() {
@ -261,6 +262,7 @@ void ScopeIterator::Next() {
// Repeat to skip hidden scopes.
} while (LastNestedScopeChain().is_hidden());
}
if (!HasNestedScopeChain()) function_ = Handle<JSFunction>();
UnwrapEvaluationContext();
}
@ -427,7 +429,7 @@ void ScopeIterator::DebugPrint() {
case ScopeIterator::ScopeTypeLocal: {
os << "Local:\n";
GetFunction()->shared()->scope_info()->Print();
function_->shared()->scope_info()->Print();
if (!CurrentContext().is_null()) {
CurrentContext()->Print(os);
if (CurrentContext()->has_extension()) {
@ -478,24 +480,6 @@ void ScopeIterator::DebugPrint() {
}
#endif
inline Handle<Context> ScopeIterator::GetContext() {
if (frame_inspector_) {
return Handle<Context>::cast(frame_inspector_->GetContext());
} else {
DCHECK(!generator_.is_null());
return handle(generator_->context());
}
}
Handle<JSFunction> ScopeIterator::GetFunction() {
if (frame_inspector_) {
return frame_inspector_->GetFunction();
} else {
DCHECK(!generator_.is_null());
return handle(generator_->function());
}
}
int ScopeIterator::GetSourcePosition() {
if (frame_inspector_) {
return frame_inspector_->GetSourcePosition();
@ -561,26 +545,24 @@ void ScopeIterator::MaterializeStackLocals(Handle<JSObject> local_scope,
}
MaybeHandle<JSObject> ScopeIterator::MaterializeLocalScope() {
Handle<JSFunction> function(GetFunction());
Handle<SharedFunctionInfo> shared(function->shared());
DCHECK(HasNestedScopeChain());
Handle<SharedFunctionInfo> shared(function_->shared());
Handle<ScopeInfo> scope_info(shared->scope_info());
Handle<JSObject> local_scope =
isolate_->factory()->NewJSObjectWithNullProto();
MaterializeStackLocals(local_scope, scope_info);
Handle<Context> frame_context = GetContext();
if (!scope_info->HasContext()) return local_scope;
// Fill all context locals.
Handle<Context> function_context(frame_context->closure_context());
Handle<Context> function_context(context_->closure_context());
DCHECK_EQ(context_->scope_info(), *scope_info);
CopyContextLocalsToScopeObject(scope_info, function_context, local_scope);
// Finally copy any properties from the function context extension.
// These will be variables introduced by eval.
if (function_context->closure() == *function &&
!function_context->IsNativeContext()) {
if (!function_context->IsNativeContext()) {
CopyContextExtensionToScopeObject(function_context, local_scope,
KeyCollectionMode::kIncludePrototypes);
}
@ -595,8 +577,7 @@ Handle<JSObject> ScopeIterator::MaterializeClosure() {
Handle<Context> context = CurrentContext();
DCHECK(context->IsFunctionContext() || context->IsEvalContext());
Handle<SharedFunctionInfo> shared(context->closure()->shared());
Handle<ScopeInfo> scope_info(shared->scope_info());
Handle<ScopeInfo> scope_info(context_->scope_info());
// Allocate and initialize a JSObject with all the content of this function
// closure.
@ -610,7 +591,6 @@ Handle<JSObject> ScopeIterator::MaterializeClosure() {
// be variables introduced by eval.
CopyContextExtensionToScopeObject(context, closure_scope,
KeyCollectionMode::kOwnOnly);
return closure_scope;
}
@ -765,7 +745,14 @@ bool ScopeIterator::SetContextVariableValue(Handle<ScopeInfo> scope_info,
bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
Handle<Object> new_value) {
Handle<ScopeInfo> scope_info(GetFunction()->shared()->scope_info());
Handle<ScopeInfo> scope_info;
if (HasNestedScopeChain()) {
scope_info = handle(function_->shared()->scope_info());
DCHECK_IMPLIES(scope_info->HasContext(),
context_->scope_info() == *scope_info);
} else {
scope_info = handle(context_->scope_info());
}
// Parameter might be shadowed in context. Don't stop here.
bool result = SetParameterValue(scope_info, variable_name, new_value);
@ -776,8 +763,7 @@ bool ScopeIterator::SetLocalVariableValue(Handle<String> variable_name,
}
if (scope_info->HasContext() &&
SetContextVariableValue(scope_info, CurrentContext(), variable_name,
new_value)) {
SetContextVariableValue(scope_info, context_, variable_name, new_value)) {
return true;
}
@ -981,7 +967,7 @@ void ScopeIterator::GetNestedScopeChain(Isolate* isolate, Scope* scope,
}
}
bool ScopeIterator::HasNestedScopeChain() {
bool ScopeIterator::HasNestedScopeChain() const {
return !nested_scope_chain_.empty();
}

View File

@ -78,7 +78,14 @@ class ScopeIterator {
Handle<StringSet> GetNonLocals();
// Return function which represents closure for current scope.
Handle<JSFunction> GetClosure();
Handle<JSFunction> GetFunction() { return function_; }
// Similar to JSFunction::GetName return the function's name or it's inferred
// name.
Handle<Object> GetFunctionDebugName() const;
Handle<Script> GetScript() const { return script_; }
bool HasPositionInfo();
int start_position();
int end_position();
@ -102,7 +109,10 @@ class ScopeIterator {
Isolate* isolate_;
FrameInspector* const frame_inspector_ = nullptr;
Handle<JSGeneratorObject> generator_;
Handle<JSFunction> function_;
Handle<ScopeInfo> function_scope_info_;
Handle<Context> context_;
Handle<Script> script_;
std::vector<ExtendedScopeInfo> nested_scope_chain_;
Handle<StringSet> non_locals_;
bool seen_script_scope_;
@ -112,7 +122,6 @@ class ScopeIterator {
}
Handle<Context> GetContext();
Handle<JSFunction> GetFunction();
int GetSourcePosition();
void MaterializeStackLocals(Handle<JSObject> local_scope,
@ -176,7 +185,7 @@ class ScopeIterator {
void GetNestedScopeChain(Isolate* isolate, Scope* scope,
int statement_position);
bool HasNestedScopeChain();
bool HasNestedScopeChain() const;
ExtendedScopeInfo& LastNestedScopeChain();
DISALLOW_IMPLICIT_CONSTRUCTORS(ScopeIterator);

View File

@ -125,7 +125,7 @@ v8::Local<v8::Value> DebugStackTraceIterator::GetReturnValue() const {
return Utils::ToLocal(isolate_->debug()->return_value_handle());
}
v8::Local<v8::String> DebugStackTraceIterator::GetFunctionName() const {
v8::Local<v8::String> DebugStackTraceIterator::GetFunctionDebugName() const {
DCHECK(!Done());
return Utils::ToLocal(frame_inspector_->GetFunctionName());
}

View File

@ -23,7 +23,7 @@ class DebugStackTraceIterator final : public debug::StackTraceIterator {
int GetContextId() const override;
v8::MaybeLocal<v8::Value> GetReceiver() const override;
v8::Local<v8::Value> GetReturnValue() const override;
v8::Local<v8::String> GetFunctionName() const override;
v8::Local<v8::String> GetFunctionDebugName() const override;
v8::Local<v8::debug::Script> GetScript() const override;
debug::Location GetSourceLocation() const override;
v8::Local<v8::Function> GetFunction() const override;

View File

@ -255,26 +255,33 @@ Response buildScopes(v8::debug::ScopeIterator* iterator,
std::unique_ptr<Array<Scope>>* scopes) {
*scopes = Array<Scope>::create();
if (!injectedScript) return Response::OK();
if (iterator->Done()) return Response::OK();
String16 scriptId = String16::fromInteger(iterator->GetScriptId());
for (; !iterator->Done(); iterator->Advance()) {
std::unique_ptr<RemoteObject> object;
Response result = injectedScript->wrapObject(
iterator->GetObject(), kBacktraceObjectGroup, false, false, &object);
if (!result.isSuccess()) return result;
auto scope = Scope::create()
.setType(scopeType(iterator->GetType()))
.setObject(std::move(object))
.build();
v8::Local<v8::Function> closure = iterator->GetFunction();
if (!closure.IsEmpty()) {
String16 name = toProtocolStringWithTypeCheck(closure->GetDebugName());
String16 name =
toProtocolStringWithTypeCheck(iterator->GetFunctionDebugName());
if (!name.isEmpty()) scope->setName(name);
String16 scriptId = String16::fromInteger(closure->ScriptId());
if (iterator->HasLocationInfo()) {
v8::debug::Location start = iterator->GetStartLocation();
scope->setStartLocation(protocol::Debugger::Location::create()
.setScriptId(scriptId)
.setLineNumber(start.GetLineNumber())
.setColumnNumber(start.GetColumnNumber())
.build());
v8::debug::Location end = iterator->GetEndLocation();
scope->setEndLocation(protocol::Debugger::Location::create()
.setScriptId(scriptId)
@ -1335,7 +1342,7 @@ Response V8DebuggerAgentImpl::currentCallFrames(
auto frame =
CallFrame::create()
.setCallFrameId(callFrameId)
.setFunctionName(toProtocolString(iterator->GetFunctionName()))
.setFunctionName(toProtocolString(iterator->GetFunctionDebugName()))
.setLocation(std::move(location))
.setUrl(url)
.setScopeChain(std::move(scopes))

View File

@ -653,6 +653,11 @@ v8::MaybeLocal<v8::Value> V8Debugger::getTargetScopes(
v8::Local<v8::Function> closure = iterator->GetFunction();
if (!closure.IsEmpty()) {
name = toProtocolStringWithTypeCheck(closure->GetDebugName());
} else {
v8::Local<v8::Value> maybe_name = iterator->GetFunctionDebugName();
if (!maybe_name->IsUndefined()) {
name = toProtocolStringWithTypeCheck(maybe_name);
}
}
v8::Local<v8::Object> object = iterator->GetObject();
createDataProperty(context, scope,

View File

@ -1798,13 +1798,28 @@ void ScopeInfo::ScopeInfoPrint(std::ostream& os) { // NOLINT
os << "\n - length = 0\n";
return;
}
int flags = Flags();
os << "\n - parameters: " << ParameterCount();
os << "\n - stack locals: " << StackLocalCount();
os << "\n - context locals : " << ContextLocalCount();
os << "\n - scope type: " << scope_type();
if (CallsSloppyEval()) os << "\n - sloppy eval";
os << "\n - language mode: " << language_mode();
os << "\n - local count: " << LocalCount();
os << "\n - stack slot count: " << StackSlotCount();
if (HasReceiver()) os << "\n - has receiver";
if (is_declaration_scope()) os << "\n - declaration scope";
if (HasReceiver()) {
os << "\n - receiver: " << ReceiverVariableField::decode(flags);
}
if (HasNewTarget()) os << "\n - needs new target";
if (HasFunctionName()) {
os << "\n - function name(" << FunctionVariableField::decode(flags)
<< "): ";
FunctionName()->ShortPrint(os);
}
if (IsAsmModule()) os << "\n - asm module";
if (HasSimpleParameters()) os << "\n - simple parameters";
os << "\n - function kind: " << function_kind();
if (HasOuterScopeInfo()) {
os << "\n - outer scope info: " << Brief(OuterScopeInfo());
}

View File

@ -13692,8 +13692,10 @@ void SharedFunctionInfo::set_debugger_hints(int value) {
}
String* SharedFunctionInfo::DebugName() {
if (Name()->length() == 0) return inferred_name();
return Name();
DisallowHeapAllocation no_gc;
String* function_name = Name();
if (function_name->length() > 0) return function_name;
return inferred_name();
}
// static

View File

@ -57,6 +57,7 @@ bool ScopeInfo::Equals(ScopeInfo* other) const {
}
#endif
// static
Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope,
MaybeHandle<ScopeInfo> outer_scope) {
// Collect variables.
@ -321,6 +322,7 @@ Handle<ScopeInfo> ScopeInfo::Create(Isolate* isolate, Zone* zone, Scope* scope,
return scope_info;
}
// static
Handle<ScopeInfo> ScopeInfo::CreateForWithScope(
Isolate* isolate, MaybeHandle<ScopeInfo> outer_scope) {
const bool has_outer_scope_info = !outer_scope.is_null();
@ -365,33 +367,49 @@ Handle<ScopeInfo> ScopeInfo::CreateForWithScope(
return scope_info;
}
// static
Handle<ScopeInfo> ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) {
DCHECK(isolate->bootstrapper()->IsActive());
return CreateForBootstrapping(isolate, SCRIPT_SCOPE);
}
// static
Handle<ScopeInfo> ScopeInfo::CreateForEmptyFunction(Isolate* isolate) {
return CreateForBootstrapping(isolate, FUNCTION_SCOPE);
}
// static
Handle<ScopeInfo> ScopeInfo::CreateForBootstrapping(Isolate* isolate,
ScopeType type) {
DCHECK(isolate->bootstrapper()->IsActive());
DCHECK(type == SCRIPT_SCOPE || type == FUNCTION_SCOPE);
const int stack_local_count = 0;
const int context_local_count = 1;
const VariableAllocationInfo receiver_info = CONTEXT;
const VariableAllocationInfo function_name_info = NONE;
const bool has_receiver = true;
const bool has_position_info = true;
const int parameter_count = 0;
const int stack_local_count = 0;
const bool is_empty_function = type == FUNCTION_SCOPE;
const int context_local_count = is_empty_function ? 0 : 1;
const bool has_receiver = !is_empty_function;
const bool has_inferred_function_name = is_empty_function;
const bool has_position_info = true;
const int length = kVariablePartIndex + parameter_count +
(1 + stack_local_count) + 2 * context_local_count +
(has_receiver ? 1 : 0) +
(is_empty_function ? kFunctionNameEntries : 0) +
(has_inferred_function_name ? 1 : 0) +
(has_position_info ? kPositionInfoEntries : 0);
Factory* factory = isolate->factory();
Handle<ScopeInfo> scope_info = factory->NewScopeInfo(length);
// Encode the flags.
int flags = ScopeTypeField::encode(SCRIPT_SCOPE) |
CallsSloppyEvalField::encode(false) |
int flags =
ScopeTypeField::encode(type) | CallsSloppyEvalField::encode(false) |
LanguageModeField::encode(LanguageMode::kSloppy) |
DeclarationScopeField::encode(true) |
ReceiverVariableField::encode(receiver_info) |
FunctionVariableField::encode(function_name_info) |
AsmModuleField::encode(false) |
HasSimpleParametersField::encode(true) |
ReceiverVariableField::encode(is_empty_function ? UNUSED : CONTEXT) |
HasNewTargetField::encode(false) |
FunctionVariableField::encode(is_empty_function ? UNUSED : NONE) |
HasInferredFunctionNameField::encode(has_inferred_function_name) |
AsmModuleField::encode(false) | HasSimpleParametersField::encode(true) |
FunctionKindField::encode(FunctionKind::kNormalFunction) |
HasOuterScopeInfoField::encode(false) |
IsDebugEvaluateScopeField::encode(false);
@ -408,28 +426,45 @@ Handle<ScopeInfo> ScopeInfo::CreateGlobalThisBinding(Isolate* isolate) {
// Here we add info for context-allocated "this".
DCHECK_EQ(index, scope_info->ContextLocalNamesIndex());
if (context_local_count) {
scope_info->set(index++, isolate->heap()->this_string());
}
DCHECK_EQ(index, scope_info->ContextLocalInfosIndex());
if (context_local_count) {
const uint32_t value = VariableModeField::encode(CONST) |
InitFlagField::encode(kCreatedInitialized) |
MaybeAssignedFlagField::encode(kNotAssigned);
scope_info->set(index++, Smi::FromInt(value));
}
// And here we record that this scopeinfo binds a receiver.
DCHECK_EQ(index, scope_info->ReceiverInfoIndex());
const int receiver_index = Context::MIN_CONTEXT_SLOTS + 0;
if (!is_empty_function) {
scope_info->set(index++, Smi::FromInt(receiver_index));
}
DCHECK_EQ(index, scope_info->FunctionNameInfoIndex());
if (is_empty_function) {
scope_info->set(index++, *isolate->factory()->empty_string());
scope_info->set(index++, Smi::kZero);
}
DCHECK_EQ(index, scope_info->InferredFunctionNameIndex());
if (has_inferred_function_name) {
scope_info->set(index++, *isolate->factory()->empty_string());
}
DCHECK_EQ(index, scope_info->PositionInfoIndex());
// Store dummy position to be in sync with the {scope_type}.
scope_info->set(index++, Smi::kZero);
scope_info->set(index++, Smi::kZero);
DCHECK_EQ(index, scope_info->OuterScopeInfoIndex());
DCHECK_EQ(index, scope_info->length());
DCHECK_EQ(scope_info->ParameterCount(), 0);
DCHECK_EQ(scope_info->ParameterCount(), parameter_count);
if (type == FUNCTION_SCOPE) {
DCHECK_EQ(scope_info->ContextLength(), 0);
} else {
DCHECK_EQ(scope_info->ContextLength(), Context::MIN_CONTEXT_SLOTS + 1);
}
return scope_info;
}
@ -576,6 +611,18 @@ Object* ScopeInfo::InferredFunctionName() const {
return get(InferredFunctionNameIndex());
}
String* ScopeInfo::FunctionDebugName() const {
Object* name = FunctionName();
if (name->IsString() && String::cast(name)->length() > 0) {
return String::cast(name);
}
if (HasInferredFunctionName()) {
name = InferredFunctionName();
if (name->IsString()) return String::cast(name);
}
return GetHeap()->empty_string();
}
int ScopeInfo::StartPosition() const {
DCHECK(HasPositionInfo());
return Smi::cast(get(PositionInfoIndex()))->value();
@ -877,6 +924,22 @@ void ScopeInfo::ModuleVariable(int i, String** name, int* index,
}
}
std::ostream& operator<<(std::ostream& os,
ScopeInfo::VariableAllocationInfo var_info) {
switch (var_info) {
case ScopeInfo::VariableAllocationInfo::NONE:
return os << "NONE";
case ScopeInfo::VariableAllocationInfo::STACK:
return os << "STACK";
case ScopeInfo::VariableAllocationInfo::CONTEXT:
return os << "CONTEXT";
case ScopeInfo::VariableAllocationInfo::UNUSED:
return os << "UNUSED";
}
UNREACHABLE();
return os;
}
Handle<ModuleInfoEntry> ModuleInfoEntry::New(Isolate* isolate,
Handle<Object> export_name,
Handle<Object> local_name,

View File

@ -103,6 +103,10 @@ class ScopeInfo : public FixedArray {
// Return the function_name if present.
Object* FunctionName() const;
// The function's name if it is non-empty, otherwise the inferred name or an
// empty string.
String* FunctionDebugName() const;
// Return the function's inferred name if present.
// See SharedFunctionInfo::function_identifier.
Object* InferredFunctionName() const;
@ -203,6 +207,7 @@ class ScopeInfo : public FixedArray {
MaybeHandle<ScopeInfo> outer_scope);
static Handle<ScopeInfo> CreateForWithScope(
Isolate* isolate, MaybeHandle<ScopeInfo> outer_scope);
static Handle<ScopeInfo> CreateForEmptyFunction(Isolate* isolate);
static Handle<ScopeInfo> CreateGlobalThisBinding(Isolate* isolate);
// Serializes empty scope info.
@ -295,6 +300,8 @@ class ScopeInfo : public FixedArray {
int ModuleVariablesIndex() const;
static bool NeedsPositionInfo(ScopeType type);
static Handle<ScopeInfo> CreateForBootstrapping(Isolate* isolate,
ScopeType type);
int Lookup(Handle<String> name, int start, int end, VariableMode* mode,
VariableLocation* location, InitializationFlag* init_flag,
@ -354,8 +361,13 @@ class ScopeInfo : public FixedArray {
class MaybeAssignedFlagField : public BitField<MaybeAssignedFlag, 4, 1> {};
friend class ScopeIterator;
friend std::ostream& operator<<(std::ostream& os,
ScopeInfo::VariableAllocationInfo var);
};
std::ostream& operator<<(std::ostream& os,
ScopeInfo::VariableAllocationInfo var);
} // namespace internal
} // namespace v8

View File

@ -722,6 +722,89 @@ closure_9();
EndTest();
// Closure with inferred name.
BeginTest("Closure with Inferred Name 1");
function closure_1_inferred_name(a) {
let foo = {};
foo.bar = function() {
debugger;
return a;
};
return foo.bar;
}
listener_delegate = function(exec_state) {
CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Closure,
debug.ScopeType.Script,
debug.ScopeType.Global], exec_state);
CheckScopeContent({a:1}, 1, exec_state);
CheckScopeChainNames(["foo.bar", "closure_1_inferred_name", undefined,
undefined], exec_state);
};
closure_1_inferred_name(1)();
EndTest();
// Closure with nested inferred name.
BeginTest("Closure with Inferred Name 2");
function closure_2_inferred_name(a) {
let foo = {};
function FooBar(b) {
foo.baz = function() {
debugger;
return a+b;
}
return foo.baz;
};
return FooBar;
}
listener_delegate = function(exec_state) {
CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Closure,
debug.ScopeType.Closure,
debug.ScopeType.Script,
debug.ScopeType.Global], exec_state);
CheckScopeContent({b:0x1235}, 1, exec_state);
CheckScopeContent({a:0x1234}, 2, exec_state);
CheckScopeChainNames(["FooBar.foo.baz", "FooBar", "closure_2_inferred_name",
undefined, undefined], exec_state);
};
closure_2_inferred_name(0x1234)(0x1235)();
EndTest();
// Closure with nested inferred name.
BeginTest("Closure with Inferred Name 3");
function closure_3_inferred_name(a) {
let foo = {};
foo.bar = function(b) {
foo.baz = function() {
debugger;
return a+b;
}
return foo.baz;
};
return foo.bar;
}
listener_delegate = function(exec_state) {
CheckScopeChain([debug.ScopeType.Local,
debug.ScopeType.Closure,
debug.ScopeType.Closure,
debug.ScopeType.Script,
debug.ScopeType.Global], exec_state);
CheckScopeContent({b:0x1235}, 1, exec_state);
CheckScopeContent({a:0x1234}, 2, exec_state);
CheckScopeChainNames(["foo.baz", "foo.bar", "closure_3_inferred_name",
undefined, undefined], exec_state);
};
closure_3_inferred_name(0x1234)(0x1235)();
EndTest();
BeginTest("Closure passed to optimized Array.prototype.forEach");
function closure_10(a) {
var x = a + 2;

View File

@ -6,6 +6,7 @@
#include <iostream>
#include <limits>
#include "src/compiler.h"
#include "src/objects-inl.h"
#include "src/objects.h"
#include "test/unittests/test-utils.h"
@ -149,5 +150,23 @@ TEST_F(ObjectWithIsolate, DictionaryGrowth) {
CHECK_EQ(64, dict->Capacity());
}
TEST_F(TestWithNativeContext, EmptyFunctionScopeInfo) {
// Check that the empty_function has a properly set up ScopeInfo.
Handle<JSFunction> function = RunJS<JSFunction>("(function(){})");
Handle<ScopeInfo> scope_info(function->shared()->scope_info());
Handle<ScopeInfo> empty_function_scope_info(
isolate()->empty_function()->shared()->scope_info());
EXPECT_EQ(scope_info->length(), empty_function_scope_info->length());
EXPECT_EQ(scope_info->Flags(), empty_function_scope_info->Flags());
EXPECT_EQ(scope_info->ParameterCount(),
empty_function_scope_info->ParameterCount());
EXPECT_EQ(scope_info->StackLocalCount(),
empty_function_scope_info->StackLocalCount());
EXPECT_EQ(scope_info->ContextLocalCount(),
empty_function_scope_info->ContextLocalCount());
}
} // namespace internal
} // namespace v8