[V8] Unify get function name for debugging purpose

Following logic is using for getting function name in JSFunction::GetDebugName:
1. if function has displayName and its type is string then use it
2. if function has defined property Function.name as value and its type string then use it
3. otherwise use SharedFunctionInfo::DebugName as functionName.

JSFunction::GetDebugName is exposed in V8 API and in FunctionMirror interface.

BUG=chromium:17356
R=yangguo@chromium.org,mstarzinger@chromium.org
LOG=Y

Review URL: https://codereview.chromium.org/1449473005

Cr-Commit-Position: refs/heads/master@{#32124}
This commit is contained in:
kozyatinskiy 2015-11-19 11:32:31 -08:00 committed by Commit bot
parent f7b6e3815c
commit 89e859fb2b
10 changed files with 191 additions and 2 deletions

View File

@ -3251,6 +3251,12 @@ class V8_EXPORT Function : public Object {
*/
Local<Value> GetInferredName() const;
/**
* displayName if it is set, otherwise name if it is configured, otherwise
* function name, otherwise inferred name.
*/
Local<Value> GetDebugName() const;
/**
* User-defined name assigned to the "displayName" property of this function.
* Used to facilitate debugging and profiling of JavaScript code.

View File

@ -4465,6 +4465,18 @@ Local<Value> Function::GetInferredName() const {
}
Local<Value> Function::GetDebugName() const {
auto self = Utils::OpenHandle(this);
if (!self->IsJSFunction()) {
return ToApiHandle<Primitive>(
self->GetIsolate()->factory()->undefined_value());
}
auto func = i::Handle<i::JSFunction>::cast(self);
i::Handle<i::String> name = i::JSFunction::GetDebugName(func);
return Utils::ToLocal(i::Handle<i::Object>(*name, name->GetIsolate()));
}
Local<Value> Function::GetDisplayName() const {
i::Isolate* isolate = Utils::OpenHandle(this)->GetIsolate();
ENTER_V8(isolate);

View File

@ -986,6 +986,16 @@ FunctionMirror.prototype.name = function() {
};
/**
* Returns the displayName if it is set, otherwise name, otherwise inferred
* name.
* @return {string} Name of the function
*/
FunctionMirror.prototype.debugName = function() {
return %FunctionGetDebugName(this.value_);
}
/**
* Returns the inferred name of the function.
* @return {string} Name of the function

View File

@ -239,6 +239,7 @@ namespace internal {
V(default_string, "default") \
V(defineProperty_string, "defineProperty") \
V(deleteProperty_string, "deleteProperty") \
V(display_name_string, "displayName") \
V(done_string, "done") \
V(dot_result_string, ".result") \
V(dot_string, ".") \

View File

@ -168,8 +168,9 @@ Handle<Object> CallSite::GetFileName() {
Handle<Object> CallSite::GetFunctionName() {
Handle<String> result = JSFunction::GetDebugName(fun_);
Handle<String> result = JSFunction::GetName(fun_);
if (result->length() != 0) return result;
Handle<Object> script(fun_->shared()->script(), isolate_);
if (script->IsScript() &&
Handle<Script>::cast(script)->compilation_type() ==

View File

@ -12098,7 +12098,7 @@ bool JSFunction::PassesFilter(const char* raw_filter) {
}
Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) {
Handle<String> JSFunction::GetName(Handle<JSFunction> function) {
Isolate* isolate = function->GetIsolate();
Handle<Object> name =
JSReceiver::GetDataProperty(function, isolate->factory()->name_string());
@ -12107,6 +12107,15 @@ Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) {
}
Handle<String> JSFunction::GetDebugName(Handle<JSFunction> function) {
Isolate* isolate = function->GetIsolate();
Handle<Object> name = JSReceiver::GetDataProperty(
function, isolate->factory()->display_name_string());
if (name->IsString()) return Handle<String>::cast(name);
return JSFunction::GetName(function);
}
void Oddball::Initialize(Isolate* isolate, Handle<Oddball> oddball,
const char* to_string, Handle<Object> to_number,
const char* type_of, byte kind) {

View File

@ -7411,6 +7411,11 @@ class JSFunction: public JSObject {
// The function's name if it is configured, otherwise shared function info
// debug name.
static Handle<String> GetName(Handle<JSFunction> function);
// The function's displayName if it is set, otherwise name if it is
// configured, otherwise shared function info
// debug name.
static Handle<String> GetDebugName(Handle<JSFunction> function);
// Layout descriptors. The last property (from kNonWeakFieldsEndOffset to

View File

@ -1486,6 +1486,16 @@ RUNTIME_FUNCTION(Runtime_FunctionGetInferredName) {
}
RUNTIME_FUNCTION(Runtime_FunctionGetDebugName) {
HandleScope scope(isolate);
DCHECK(args.length() == 1);
CONVERT_ARG_HANDLE_CHECKED(JSFunction, f, 0);
Handle<Object> name = JSFunction::GetDebugName(f);
return *name;
}
// A testing entry. Returns statement position which is the closest to
// source_position.
RUNTIME_FUNCTION(Runtime_GetFunctionCodePositionFromSource) {

View File

@ -193,6 +193,7 @@ namespace internal {
F(DebugGetPrototype, 1, 1) \
F(DebugSetScriptSource, 2, 1) \
F(FunctionGetInferredName, 1, 1) \
F(FunctionGetDebugName, 1, 1) \
F(GetFunctionCodePositionFromSource, 2, 1) \
F(ExecuteInDebugContext, 1, 1) \
F(GetDebugContext, 0, 1) \

View File

@ -14472,6 +14472,11 @@ void AnalyzeStackInNativeCode(const v8::FunctionCallbackInfo<v8::Value>& args) {
const char* origin = "capture-stack-trace-test";
const int kOverviewTest = 1;
const int kDetailedTest = 2;
const int kFunctionName = 3;
const int kDisplayName = 4;
const int kFunctionNameAndDisplayName = 5;
const int kDisplayNameIsNotString = 6;
const int kFunctionNameIsNotString = 7;
DCHECK(args.Length() == 1);
@ -14505,6 +14510,35 @@ void AnalyzeStackInNativeCode(const v8::FunctionCallbackInfo<v8::Value>& args) {
checkStackFrame(origin, "", 10, 1, false, false, stackTrace->GetFrame(3));
CHECK(stackTrace->AsArray()->IsArray());
} else if (testGroup == kFunctionName) {
v8::Handle<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
args.GetIsolate(), 5, v8::StackTrace::kOverview);
CHECK_EQ(3, stackTrace->GetFrameCount());
checkStackFrame(origin, "function.name", 2, 24, false, false,
stackTrace->GetFrame(0));
} else if (testGroup == kDisplayName) {
v8::Handle<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
args.GetIsolate(), 5, v8::StackTrace::kOverview);
CHECK_EQ(3, stackTrace->GetFrameCount());
checkStackFrame(origin, "function.displayName", 2, 24, false, false,
stackTrace->GetFrame(0));
} else if (testGroup == kFunctionNameAndDisplayName) {
v8::Handle<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
args.GetIsolate(), 5, v8::StackTrace::kOverview);
CHECK_EQ(3, stackTrace->GetFrameCount());
checkStackFrame(origin, "function.displayName", 2, 24, false, false,
stackTrace->GetFrame(0));
} else if (testGroup == kDisplayNameIsNotString) {
v8::Handle<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
args.GetIsolate(), 5, v8::StackTrace::kOverview);
CHECK_EQ(3, stackTrace->GetFrameCount());
checkStackFrame(origin, "function.name", 2, 24, false, false,
stackTrace->GetFrame(0));
} else if (testGroup == kFunctionNameIsNotString) {
v8::Handle<v8::StackTrace> stackTrace = v8::StackTrace::CurrentStackTrace(
args.GetIsolate(), 5, v8::StackTrace::kOverview);
CHECK_EQ(3, stackTrace->GetFrameCount());
checkStackFrame(origin, "f", 2, 24, false, false, stackTrace->GetFrame(0));
}
}
@ -14566,6 +14600,33 @@ TEST(CaptureStackTrace) {
detailed_script->BindToCurrentContext()->Run());
CHECK(!detailed_result.IsEmpty());
CHECK(detailed_result->IsObject());
// Test using function.name and function.displayName in stack trace
const char* function_name_source =
"function bar(function_name, display_name, testGroup) {\n"
" var f = function() { AnalyzeStackInNativeCode(testGroup); };\n"
" if (function_name) {\n"
" Object.defineProperty(f, 'name', { value: function_name });\n"
" }\n"
" if (display_name) {\n"
" f.displayName = display_name;"
" }\n"
" f()\n"
"}\n"
"bar('function.name', undefined, 3);\n"
"bar(undefined, 'function.displayName', 4);\n"
"bar('function.name', 'function.displayName', 5);\n"
"bar('function.name', 239, 6);\n"
"bar(239, undefined, 7);\n";
v8::Handle<v8::String> function_name_src =
v8::String::NewFromUtf8(isolate, function_name_source);
v8::ScriptCompiler::Source script_source3(function_name_src,
v8::ScriptOrigin(origin));
v8::Handle<Value> function_name_result(
v8::ScriptCompiler::CompileUnbound(isolate, &script_source3)
->BindToCurrentContext()
->Run());
CHECK(!function_name_result.IsEmpty());
}
@ -16099,6 +16160,79 @@ THREADED_TEST(FunctionGetInferredName) {
}
THREADED_TEST(FunctionGetDebugName) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());
const char* code =
"var error = false;"
"function a() { this.x = 1; };"
"a.displayName = 'display_a';"
"var b = (function() {"
" var f = function() { this.x = 2; };"
" f.displayName = 'display_b';"
" return f;"
"})();"
"var c = function() {};"
"c.__defineGetter__('displayName', function() {"
" error = true;"
" throw new Error();"
"});"
"function d() {};"
"d.__defineGetter__('displayName', function() {"
" error = true;"
" return 'wrong_display_name';"
"});"
"function e() {};"
"e.displayName = 'wrong_display_name';"
"e.__defineSetter__('displayName', function() {"
" error = true;"
" throw new Error();"
"});"
"function f() {};"
"f.displayName = { 'foo': 6, toString: function() {"
" error = true;"
" return 'wrong_display_name';"
"}};"
"var g = function() {"
" arguments.callee.displayName = 'set_in_runtime';"
"}; g();"
"var h = function() {};"
"h.displayName = 'displayName';"
"Object.defineProperty(h, 'name', { value: 'function.name' });"
"var i = function() {};"
"i.displayName = 239;"
"Object.defineProperty(i, 'name', { value: 'function.name' });"
"var j = function() {};"
"Object.defineProperty(j, 'name', { value: 'function.name' });"
"var foo = { bar : { baz : function() {}}}; var k = foo.bar.baz;";
v8::ScriptOrigin origin =
v8::ScriptOrigin(v8::String::NewFromUtf8(env->GetIsolate(), "test"));
v8::Script::Compile(v8::String::NewFromUtf8(env->GetIsolate(), code), &origin)
->Run();
v8::Local<v8::Value> error =
env->Global()->Get(v8::String::NewFromUtf8(env->GetIsolate(), "error"));
CHECK_EQ(false, error->BooleanValue());
const char* functions[] = {"a", "display_a",
"b", "display_b",
"c", "c",
"d", "d",
"e", "e",
"f", "f",
"g", "set_in_runtime",
"h", "displayName",
"i", "function.name",
"j", "function.name",
"k", "foo.bar.baz"};
for (size_t i = 0; i < sizeof(functions) / sizeof(functions[0]) / 2; ++i) {
v8::Local<v8::Function> f =
v8::Local<v8::Function>::Cast(env->Global()->Get(
v8::String::NewFromUtf8(env->GetIsolate(), functions[i * 2])));
CHECK_EQ(0, strcmp(functions[i * 2 + 1],
*v8::String::Utf8Value(f->GetDebugName())));
}
}
THREADED_TEST(FunctionGetDisplayName) {
LocalContext env;
v8::HandleScope scope(env->GetIsolate());