[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:
parent
f7b6e3815c
commit
89e859fb2b
@ -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.
|
||||
|
12
src/api.cc
12
src/api.cc
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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, ".") \
|
||||
|
@ -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() ==
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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) {
|
||||
|
@ -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) \
|
||||
|
@ -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());
|
||||
|
Loading…
Reference in New Issue
Block a user