Move more parts of stack trace formatting to runtime.
authoryangguo <yangguo@chromium.org>
Tue, 5 May 2015 13:55:28 +0000 (06:55 -0700)
committerCommit bot <commit-bot@chromium.org>
Tue, 5 May 2015 14:17:43 +0000 (14:17 +0000)
R=ishell@chromium.org

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

Cr-Commit-Position: refs/heads/master@{#28223}

src/messages.cc
src/messages.h
src/messages.js
src/objects.cc
src/objects.h
src/runtime/runtime-internal.cc
src/runtime/runtime.h
test/mjsunit/stack-traces-custom.js [new file with mode: 0644]

index 7dc5f64..81fcdec 100644 (file)
@@ -197,6 +197,67 @@ Handle<Object> CallSite::GetScriptNameOrSourceUrl(Isolate* isolate) {
 }
 
 
+bool CheckMethodName(Handle<JSObject> obj, Handle<Name> name,
+                     Handle<JSFunction> fun,
+                     LookupIterator::Configuration config) {
+  LookupIterator iter(obj, name, config);
+  if (iter.state() == LookupIterator::DATA) {
+    return iter.GetDataValue().is_identical_to(fun);
+  } else if (iter.state() == LookupIterator::ACCESSOR) {
+    Handle<Object> accessors = iter.GetAccessors();
+    if (accessors->IsAccessorPair()) {
+      Handle<AccessorPair> pair = Handle<AccessorPair>::cast(accessors);
+      return pair->getter() == *fun || pair->setter() == *fun;
+    }
+  }
+  return false;
+}
+
+
+Handle<Object> CallSite::GetMethodName(Isolate* isolate) {
+  MaybeHandle<JSReceiver> maybe = Object::ToObject(isolate, receiver_);
+  Handle<JSReceiver> receiver;
+  if (!maybe.ToHandle(&receiver) || !receiver->IsJSObject()) {
+    return isolate->factory()->null_value();
+  }
+
+  Handle<JSObject> obj = Handle<JSObject>::cast(receiver);
+  Handle<Object> function_name(fun_->shared()->name(), isolate);
+  if (function_name->IsName()) {
+    Handle<Name> name = Handle<Name>::cast(function_name);
+    if (CheckMethodName(obj, name, fun_,
+                        LookupIterator::PROTOTYPE_CHAIN_SKIP_INTERCEPTOR))
+      return name;
+  }
+
+  HandleScope outer_scope(isolate);
+  Handle<Object> result;
+  for (PrototypeIterator iter(isolate, obj,
+                              PrototypeIterator::START_AT_RECEIVER);
+       !iter.IsAtEnd(); iter.Advance()) {
+    Handle<Object> current = PrototypeIterator::GetCurrent(iter);
+    if (!current->IsJSObject()) break;
+    Handle<JSObject> current_obj = Handle<JSObject>::cast(current);
+    if (current_obj->IsAccessCheckNeeded()) break;
+    Handle<FixedArray> keys = JSObject::GetEnumPropertyKeys(current_obj, false);
+    for (int i = 0; i < keys->length(); i++) {
+      HandleScope inner_scope(isolate);
+      if (!keys->get(i)->IsName()) continue;
+      Handle<Name> name_key(Name::cast(keys->get(i)), isolate);
+      if (!CheckMethodName(current_obj, name_key, fun_,
+                           LookupIterator::OWN_SKIP_INTERCEPTOR))
+        continue;
+      // Return null in case of duplicates to avoid confusion.
+      if (!result.is_null()) return isolate->factory()->null_value();
+      result = inner_scope.CloseAndEscape(name_key);
+    }
+  }
+
+  if (!result.is_null()) return outer_scope.CloseAndEscape(result);
+  return isolate->factory()->null_value();
+}
+
+
 int CallSite::GetLineNumber(Isolate* isolate) {
   if (pos_ >= 0) {
     Handle<Object> script_obj(fun_->shared()->script(), isolate);
@@ -242,6 +303,15 @@ bool CallSite::IsEval(Isolate* isolate) {
 }
 
 
+bool CallSite::IsConstructor(Isolate* isolate) {
+  if (!receiver_->IsJSObject()) return false;
+  Handle<Object> constructor =
+      JSObject::GetDataProperty(Handle<JSObject>::cast(receiver_),
+                                isolate->factory()->constructor_string());
+  return constructor.is_identical_to(fun_);
+}
+
+
 MaybeHandle<String> MessageTemplate::FormatMessage(int template_index,
                                                    Handle<String> arg0,
                                                    Handle<String> arg1,
index 7c7b26f..7e18beb 100644 (file)
@@ -96,6 +96,7 @@ class CallSite {
   Handle<Object> GetFileName(Isolate* isolate);
   Handle<Object> GetFunctionName(Isolate* isolate);
   Handle<Object> GetScriptNameOrSourceUrl(Isolate* isolate);
+  Handle<Object> GetMethodName(Isolate* isolate);
   // Return 1-based line number, including line offset.
   int GetLineNumber(Isolate* isolate);
   // Return 1-based column number, including column offset if first line.
@@ -103,6 +104,7 @@ class CallSite {
   bool IsNative(Isolate* isolate);
   bool IsToplevel(Isolate* isolate);
   bool IsEval(Isolate* isolate);
+  bool IsConstructor(Isolate* isolate);
 
  private:
   Handle<Object> receiver_;
index 8bfd0bc..20b7cb3 100644 (file)
@@ -778,31 +778,8 @@ function CallSiteGetMethodName() {
   // this function.
   var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
   var fun = GET_PRIVATE(this, CallSiteFunctionKey);
-  var ownName = fun.name;
-  if (ownName && receiver &&
-      (%_CallFunction(receiver, ownName, $objectLookupGetter) === fun ||
-       %_CallFunction(receiver, ownName, $objectLookupSetter) === fun ||
-       (IS_OBJECT(receiver) && %GetDataProperty(receiver, ownName) === fun))) {
-    // To handle DontEnum properties we guess that the method has
-    // the same name as the function.
-    return ownName;
-  }
-  var name = null;
-  for (var prop in receiver) {
-    if (%_CallFunction(receiver, prop, $objectLookupGetter) === fun ||
-        %_CallFunction(receiver, prop, $objectLookupSetter) === fun ||
-        (IS_OBJECT(receiver) && %GetDataProperty(receiver, prop) === fun)) {
-      // If we find more than one match bail out to avoid confusion.
-      if (name) {
-        return null;
-      }
-      name = prop;
-    }
-  }
-  if (name) {
-    return name;
-  }
-  return null;
+  var pos = GET_PRIVATE(this, CallSitePositionKey);
+  return %CallSiteGetMethodNameRT(receiver, fun, pos);
 }
 
 function CallSiteGetFileName() {
@@ -835,10 +812,9 @@ function CallSiteIsNative() {
 
 function CallSiteIsConstructor() {
   var receiver = GET_PRIVATE(this, CallSiteReceiverKey);
-  var constructor = (receiver != null && IS_OBJECT(receiver))
-                        ? %GetDataProperty(receiver, "constructor") : null;
-  if (!constructor) return false;
-  return GET_PRIVATE(this, CallSiteFunctionKey) === constructor;
+  var fun = GET_PRIVATE(this, CallSiteFunctionKey);
+  var pos = GET_PRIVATE(this, CallSitePositionKey);
+  return %CallSiteIsConstructorRT(receiver, fun, pos);
 }
 
 function CallSiteToString() {
index 002c3c2..a694c0a 100644 (file)
@@ -6240,8 +6240,8 @@ static Handle<FixedArray> ReduceFixedArrayTo(
 }
 
 
-static Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
-                                              bool cache_result) {
+Handle<FixedArray> JSObject::GetEnumPropertyKeys(Handle<JSObject> object,
+                                                 bool cache_result) {
   Isolate* isolate = object->GetIsolate();
   if (object->HasFastProperties()) {
     int own_property_count = object->map()->EnumLength();
@@ -6422,7 +6422,7 @@ MaybeHandle<FixedArray> JSReceiver::GetKeys(Handle<JSReceiver> object,
     ASSIGN_RETURN_ON_EXCEPTION(
         isolate, content,
         FixedArray::UnionOfKeys(
-            content, GetEnumPropertyKeys(current, cache_enum_keys)),
+            content, JSObject::GetEnumPropertyKeys(current, cache_enum_keys)),
         FixedArray);
     DCHECK(ContainsOnlyValidKeys(content));
 
index 801e986..fa7ffcb 100644 (file)
@@ -2069,6 +2069,9 @@ class JSObject: public JSReceiver {
   // Returns the number of enumerable elements.
   int GetEnumElementKeys(FixedArray* storage);
 
+  static Handle<FixedArray> GetEnumPropertyKeys(Handle<JSObject> object,
+                                                bool cache_result);
+
   // Returns a new map with all transitions dropped from the object's current
   // map and the ElementsKind set.
   static Handle<Map> GetElementsTransitionMap(Handle<JSObject> object,
index f17c4f6..6a17dfb 100644 (file)
@@ -354,11 +354,13 @@ static inline Object* ReturnBoolean(bool value, Isolate* isolate) {
 CALLSITE_GET(GetFileName, ReturnDereferencedHandle)
 CALLSITE_GET(GetFunctionName, ReturnDereferencedHandle)
 CALLSITE_GET(GetScriptNameOrSourceUrl, ReturnDereferencedHandle)
+CALLSITE_GET(GetMethodName, ReturnDereferencedHandle)
 CALLSITE_GET(GetLineNumber, ReturnPositiveSmiOrNull)
 CALLSITE_GET(GetColumnNumber, ReturnPositiveSmiOrNull)
 CALLSITE_GET(IsNative, ReturnBoolean)
 CALLSITE_GET(IsToplevel, ReturnBoolean)
 CALLSITE_GET(IsEval, ReturnBoolean)
+CALLSITE_GET(IsConstructor, ReturnBoolean)
 
 #undef CALLSITE_GET
 
index a132edf..864e448 100644 (file)
@@ -291,11 +291,13 @@ namespace internal {
   F(CallSiteGetFileNameRT, 3, 1)              \
   F(CallSiteGetFunctionNameRT, 3, 1)          \
   F(CallSiteGetScriptNameOrSourceUrlRT, 3, 1) \
+  F(CallSiteGetMethodNameRT, 3, 1)            \
   F(CallSiteGetLineNumberRT, 3, 1)            \
   F(CallSiteGetColumnNumberRT, 3, 1)          \
   F(CallSiteIsNativeRT, 3, 1)                 \
   F(CallSiteIsToplevelRT, 3, 1)               \
   F(CallSiteIsEvalRT, 3, 1)                   \
+  F(CallSiteIsConstructorRT, 3, 1)            \
   F(IS_VAR, 1, 1)                             \
   F(GetFromCache, 2, 1)                       \
   F(IncrementStatsCounter, 1, 1)              \
diff --git a/test/mjsunit/stack-traces-custom.js b/test/mjsunit/stack-traces-custom.js
new file mode 100644 (file)
index 0000000..fbf650d
--- /dev/null
@@ -0,0 +1,20 @@
+// Copyright 2015 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var o = { f: function() { throw new Error(); } };
+o.g1 = function() { o.f() }
+o.g2 = o.g1;
+o.h = function() { o.g1() }
+
+Error.prepareStackTrace = function(e, frames) { return frames; }
+
+try {
+  o.h();
+} catch (e) {
+  var frames = e.stack;
+  assertEquals("f", frames[0].getMethodName());
+  assertEquals(null, frames[1].getMethodName());
+  assertEquals("h", frames[2].getMethodName());
+  assertEquals(null, frames[3].getMethodName());
+}