HaxeFoundation / hxcpp

Runtime files for c++ backend for haxe
Other
295 stars 184 forks source link

[cppia] Cant seem to get host to call extern super functions #1150

Closed ianharrigan closed 1 day ago

ianharrigan commented 2 weeks ago

So Ive reduced my problem scope to the following:

In the cppia host:

class FakeButton extends FakeInteractiveComponent {
    public override function someFunction() {
        super.someFunction();
        trace(">>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeButton");
    }
}

class FakeInteractiveComponent extends FakeComponent {
    public override function someFunction() {
        super.someFunction();
        trace(">>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeInteractiveComponent");
    }
}

class FakeComponent extends FakeComponentBase {
    public override function someFunction() {
        super.someFunction();
        trace(">>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeComponent");
    }
}

class FakeComponentBase {
    public function new() {
    }

    public function someFunction() {
        trace(">>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeComponentBase");
    }
}

As expected if you create an instance of this in the host (new FakeButton()) and call the someFunction() then things trace as you would imagine:

src/fake/FakeComponentBase.hx:8: >>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeComponentBase
src/fake/FakeComponent.hx:6: >>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeComponent
src/fake/FakeInteractiveComponent.hx:6: >>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeInteractiveComponent
src/fake/FakeButton.hx:6: >>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeButton

All fine, however, in the script file, i have a set of externs for these classes, as well as a class that extends one of these externs:

@:expose("MyFakeButton")
class MyFakeButton extends FakeButton {
    public override function someFunction() {
        super.someFunction();
        trace(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> MyFakeButton.someFunction");
    }
}

extern class FakeButton extends FakeInteractiveComponent {
    public function someFunction():Void;
}

extern class FakeInteractiveComponent extends FakeComponent {
    public function someFunction():Void;
}

extern class FakeComponent extends FakeComponentBase {
    public function someFunction():Void;
}

extern class FakeComponentBase {
    public function someFunction():Void;
}

When i load this script into the host, create an instance of the FakeButton (via cpp.cppia.Module.resolveClass) and call someFunction i, weirdly, get the following:

src/fake/FakeComponentBase.hx:8: >>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeComponentBase
src/MyFakeButton.hx:9: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> MyFakeButton.someFunction

Its like its skipped the rest of the class hierarchy or something, ive tried various things but can never seem to get it to call, for example, FakeComponent.someFunction

Any thoughts, is there something ive doing incredibly wrong here?

Note1: i tried adding override to the extern functions, same thing

Note2: a similar thing is perfectly fine with js (obviously not using cppia), but dynamically loading a .js file and creating class instances

Note3: i thought it might be somewhat related to: https://github.com/HaxeFoundation/hxcpp/issues/423 - albiet not using externs, etc - but my understanding is that issue was fixed anyway

ianharrigan commented 2 weeks ago

If i do the following (in the host):

                    var superClass = Type.getSuperClass(Type.getClass(button));
                    while (superClass != null) {
                        trace(Type.getClassName(superClass));
                        superClass = Type.getSuperClass(superClass);
                    }

(button being the class instance that is created from the cppia script) Then everything seems normal:

src/MainView.hx:83: fake.FakeButton
src/MainView.hx:83: fake.FakeInteractiveComponent
src/MainView.hx:83: fake.FakeComponent
src/MainView.hx:83: fake.FakeComponentBase

/shrug

hughsando commented 2 weeks ago

This stuff can get a little confusing. Is the problem basically in the "super.xxx" function call syntax in a class hierarchy, while "getSuperClass" is ok? It's been a while since i looked at this.

ianharrigan commented 2 weeks ago

Is the problem basically in the "super.xxx" function call syntax in a class hierarchy, while "getSuperClass" is ok?

Im not sure im fully following what you mean, but as far as i can tell, yeah, the issue seems to be that "super.xxx" calls the super class at the base of the class tree (thats an assumption on my part), but it certainly doesnt seem to call the "in between ones"

ianharrigan commented 2 weeks ago

FYI - @dazKind has also repro'd it and actually, extern classes arent needed at all - i didnt think to test that, i assumed extern was part of the problem

dazKind commented 2 weeks ago

I think CppiaClassInfo::getSuperClass() returns an already wrong superId when loading the cppia bytecode, in this case for FakeComponentBase. So I suspect the generator skips the hierarchy somewhere

dazKind commented 2 weeks ago

Here is a minimal example: cppia-superclass.zip

bar@foo /cygdrive/D/Development/regressions
$ make native
haxe native.hxml -debug
haxelib run hxcpp Build.xml haxe -Ddebug -Dhaxe="4.3.4" -Dhaxe3="1" -Dhaxe4="1" -Dhaxe_ver="4.304" -Dhxcpp_api_level="430" -Dhxcpp_smart_strings="1" -Dsource_header="Generated by Haxe 4.3.4" -Dstatic="1" -Dtarget.atomics="1" -Dtarget.name="cpp" -Dtarget.static="1" -Dtarget.sys="1" -Dtarget.threaded="1" -Dtarget.unicode="1" -Dtarget.utf16="1" -Dutf16="1" -Isrc/ -I -IC:\\haxe\\extraLibs/ -IC:\\haxe\\std/cpp/_std/ -IC:\\haxe\\std/
bin/Host-debug.exe
src/FakeTypes.hx:27: >>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeComponentBase
src/FakeTypes.hx:18: >>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeComponent
src/FakeTypes.hx:11: >>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeInteractiveComponent
src/FakeTypes.hx:4: >>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeButton
src/Script.hx:14: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> MyFakeButton.someFunction

bar@foo /cygdrive/D/Development/regressions
$ make
haxe host.hxml -debug
haxelib run hxcpp Build.xml haxe -Ddebug -Ddll_export="bin/export_classes.info" -Dhaxe="4.3.4" -Dhaxe3="1" -Dhaxe4="1" -Dhaxe_ver="4.304" -Dhxcpp_api_level="430" -Dhxcpp_smart_strings="1" -Dscriptable="1" -Dsource_header="Generated by Haxe 4.3.4" -Dstatic="1" -Dtarget.atomics="1" -Dtarget.name="cpp" -Dtarget.static="1" -Dtarget.sys="1" -Dtarget.threaded="1" -Dtarget.unicode="1" -Dtarget.utf16="1" -Dutf16="1" -Isrc/ -I -IC:\\haxe\\extraLibs/ -IC:\\haxe\\std/cpp/_std/ -IC:\\haxe\\std/
Link: D:/.hxcpp_cache/hxcpp_runtime/lib/msvc1964_debug_c_hxcpp_runtime.lib
Link: Host-debug.exe
   Creating library Host-debug.lib and object Host-debug.exp
haxe script.hxml -debug
bin/Host-debug.exe
src/Host.hx:7: Hello from cppia HOST
src/FakeTypes.hx:27: >>>>>>>>>>>>>>>>>>>>>>>>> SOME FUNCTION: FakeComponentBase
src/Script.hx:14: >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> MyFakeButton.someFunction
Aidan63 commented 3 days ago

I had a look at this since I got abstract classes working on cppia a while back.

Turning on the cppia logging everything looked alright, it recognised MyFakeButton has the correct parent and someFunction is overridden. I also placed a few breakpoints around the cppia linking functions and when MyFakeButton was linked it appeared to have the full inheritance tree.

image

The problems seems to be registering those classes in the host which are between the cppia child and the root parent, all those intermediate parents have __scriptableFunctions set to null. So when MyFakeButton links someFunction it resolves it against the root parents as thats the only one which registers a script function named someFunction.

Looking at gencpp the function which builds the list of scriptable functions does something very odd when it encounters a function marked as override

let current_virtual_functions_rev clazz base_functions =
   List.fold_left (fun result elem -> match follow elem.cf_type, elem.cf_kind  with
      | _, Method MethDynamic -> result
      | TFun (args,return_type), Method _  ->
          if (is_override elem ) then
             List.map (fun (e,a,r) ->  if e.cf_name<>elem.cf_name then (e,a,r) else  (elem,args,return_type) ) result
          else
             (elem,args,return_type) :: result
      | _,_ -> result
    ) base_functions clazz.cl_ordered_fields
;;

It just maps the existing accumulated results, never adding the overridden function, hence the null scriptable functions. I'm assuming at some point in history it traversed the class tree since thats the only thing I can think of where this code might make sense, but if it once did it does not anymore.

https://github.com/Aidan63/haxe/tree/cppia-overloads

I seem to have a fix in the above branch, I'm a bit nervous about it due to the odd original code and the big comment warning about the order of functions for cppia. But all tests still pass and the posted sample works, so hopefully even if its not perfect its a bit better than before and nothing has regressed.

I'll give it another look tomorrow and open a PR.