HaxeFoundation / haxe

Haxe - The Cross-Platform Toolkit
https://haxe.org
6.11k stars 648 forks source link

Call stack missing type names on JS in Chrome #11735

Open justin-espedal opened 1 month ago

justin-espedal commented 1 month ago

This seems like a significant oversight, so in addition to reporting this, I'm kinda hoping for a sanity check and maybe some additional historical context from anybody who knows (was this definitely tested before?).

When haxe.CallStack.callStack() is called on the javascript target with ES5 code generation, running in Chrome (or presumably anywhere that V8 is used), any Method stack item that comes from a static or abstract function call will include the type information, but those that come from function calls on an instance of a type will not.

Running the same when ES6 code generation is enabled is even worse. No type names at all are included.

try.haxe program: https://try.haxe.org/#98c971A4

import js.lib.Error;

using StringTools;

class Test {
  static function main() {
    staticFunction();
  }

  static function staticFunction()
  {
    var abstractObject = new AbstractObject(new RealObject());
    abstractObject.abstractFunction();
  }
}

abstract AbstractObject(RealObject)
{
  public function new(real:RealObject)
  {
    this = real;
  }

  public function abstractFunction()
  {
    this.instanceFunction();
  }
}

class RealObject
{
  public function new()
  {

  }

  public function instanceFunction()
  {
    //js.Syntax.code("debugger");
    var stack = haxe.CallStack.callStack();

    for(stackItem in stack)
    {
      trace(stackItem);
    }
    /* ES5
Test.hx:44:,FilePos(Method(null,instanceFunction),_,_,_)
Test.hx:44:,FilePos(Method(AbstractObject,abstractFunction),_,_,_)
Test.hx:44:,FilePos(Method(Test,staticFunction),_,_,_)
Test.hx:44:,FilePos(Method(Test,main),_,_,_)
Test.hx:44:,FilePos(null,_,_,_)
Test.hx:44:,FilePos(null,_,_,_)
    */

    /* ES6
Test.hx:44:,FilePos(Method(null,instanceFunction),_,_,_)
Test.hx:44:,FilePos(Method(null,abstractFunction),_,_,_)
Test.hx:44:,FilePos(Method(null,staticFunction),_,_,_)
Test.hx:44:,FilePos(Method(null,main),_,_,_)
Test.hx:44:,FilePos(null,_,_,_)
Test.hx:44:,FilePos(null,_,_,_)
    */
  }
}

This is simple enough to fix in the standard library's javascript implementation of NativeStackTrace.prepareHxStackTrace for ES5 code. I have a commit prepared with a fix for that case (https://github.com/justin-espedal/haxe/commit/243c4d972b15b3d787700e25756c82adcf155748), but thought I'd withhold it pending discussion. For ES6 code I have no idea because site.getTypeName(), site.getFunctionName(), and site.getMethodName() all don't provide the needed information for static functions and abstracts.

justin-espedal commented 1 month ago

Okay, I think I see what's going on with ES6.

JavaScript's strict mode is enabled at the top of the program with "use strict";

The V8 stack trace API has this to say about strict mode (https://v8.dev/docs/stack-trace-api):

To maintain restrictions imposed on strict mode functions, frames that have a strict mode function and all frames below (its caller etc.) are not allow to access their receiver and function objects. For those frames, getFunction() and getThis() returns undefined.

site.getTypeName() only returns something useful for instance functions. To use haxe_NativeStackTrace.callStack as an example of a static/abstract function that we can't deal with:

Using the default string encoding of the callsite provides the info we need, but there's no other way to access the type name using the callsite API on ES6 in strict mode. I can only assume that this or function would have provided the information we need if they were available. In this case I guess we should fall back on parsing the default string output of the callstack.