Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cppia runtime behavior changes depending on order a function is declared in the source #818

Open
AlexRamallo opened this issue Jun 25, 2019 · 6 comments

Comments

@AlexRamallo
Copy link

This one is weird, so I'm going to need you to bear with me a little bit...

The plan:
I want to integrate Cppia scripts into my level editor. The goal is to be able to assign a class to any instance in my level editor via the UI, and write the class as Haxe code, then compile it all into a cppia module that is loaded alongside the level (kind of like a ghetto SWF). I've already implemented this, and it actually works, but I've run into a bug which seems to be coming from Cppia.

These classes are being derived from a parent class which is defined in the host (a FlxSprite, from HaxeFlixel).

The problem:
The behavior I'm seeing from the Cppia script doesn't make any sense. For example, I am overriding one function which is only ever called by the Host once after the class is instantiated via Type.createInstance. There is no other place in the entire program where this function gets called again, however at runtime I find that this function is actually being called every single frame, as if it were part of the update function.

But what's even weirder is that if I change the order that the function is declared in the parent class (in the host), then the behavior changes. Now, instead of being called every frame, it gets called in lieu of another completely unrelated function.

To illustrate:

class MyHostSprite extends FlxSprite {
    public function oninit(){
        //do something
    }

    public function update_sprite(){
        //do something
    }

}
------
class MyCppiaSprite extends MyHostSprite {
    override public function oninit(){
        //do something else
    }

    override public function update(){
        update_sprite(); 
        //...
    }
}

If I run the above code, I get oninit() called every frame, as if it were in the body of update. However, if I change the host class to this:

class MyHostSprite extends FlxSprite {
    public function update_sprite(){
        //do something
    }

    public function oninit(){
        //do something
    }
}

Then oninit doesn't get called each frame, it instead gets called in lieu of another function. Note that all I did was change the order that the function appears in the class body. Doing this on the cppia side doesn't do anything though, it appears to be a host-only thing.

Maybe looking at the call stack might show what's going on, but I'm not sure how I would do that.

I omitted a lot of code for brevity. The main things I think that are relevant though are the following:

  • The cppia script extends a class defined in the host, and through export_classes, the host class definition is not included in the cppia script.

  • I'm "loading" the cppia script using cpp.cppia.Host.run, and then using Type.resolveClass/Type.createInstance to instantiate classes. There also isn't a main function in the cppia script, although even when I add one I get the same behavior.

  • The instance of the cppia-defined class is then passed to functions that expect an instance of the parent class. So member functions defined in the parent are called on the cppia instance.

  • I am not using the standard openfl build tool since, as far as I know, it doesn't support building cppia scripts. I'm invoking the Haxe compiler manually to build the cppia script, and excluding openfl/flixel stuff using -D dll_import and --macro exclude.

Reproducing:
I've reproduced this issue with my project on Haxe versions 3.4.7, 4 rc2, and 4 rc3 on Ubuntu and Windows 10, and Hxcpp 4.0.8 and 4.0.19

If it'll help fix this, I'm open to uploading this entire project. However, it's part of a custom asset pipeline that isn't fully finished yet, requires some environment configuration, and is designed to work with a commercial product. So it's messy to say the least, but it so far can reproduce the issue on two different machines.

@AlexRamallo
Copy link
Author

I don't know if Cppia was designed to be (mis-)used this way, but I think it'd be pretty cool if it can work since the generated cppia scripts are super tiny and this workflow is very efficient. It's kind of like working with the editor in Unity or UE4, including support for prefabs and the like.

An alternative implementation might be to use something like the Lua target + vm, but that would introduce a lot of complexity. So far I've been able to use Cppia almost as if the classes were compiled with the host, and it's awesome.

@hughsando
Copy link
Member

It looks like the cppia script is not taking into account the functions counts of the parent cppia object.

I have a test set in hxcpp/test/cppia/ for things like this. I have added an 'update' function to the HostBase, and resolved an override instance, but it seem to give the correct result. So perhaps there are some subtle differences in the way the objects are inherited. Is 'update' an interface function?

If you could modify one of these host/script classes to show the problem, that would really help.

@AlexRamallo
Copy link
Author

I tried to reproduce the issue in that test directory, but I can't seem to get it to work. However, I did notice some strange behavior which seems like it may be related. Here is a zip containing my modified files from that directory: cppia.zip

The issue I noticed there is that in CppiaHost.hx, I added a trace around the call to two functions defined in HostBase, one called update and another sub_update. The weird part is that these functions return Void, yet the first trace on update prints out "ClientExtends". This seems like the return value of the function whoOverridesYou. However, I couldn't get this behavior to change by rearranging any of the functions, and adding a trace to whoOverridesYou never prints out anything, so it doesn't seem like that's actually being called. The only difference I could make was by removing the overridden version of update, it acts normally. Could this just be a bug with trace? Is tracing a void return value undefined behavior?

Luckily, I did manage to isolate the problematic code in my project, and recreated the issue in a slimmed down version of the default flixel project template. I've uploaded the repo here. The 3 main files of interest here are:

  • host/source/PlayState.hx: loads cppia file and resolves/instantiates cppia type
  • host/source/HostSprite.hx: base class extended by script.
  • client/source/ClientSprite.hx: the cppia class that extends HostSprite

If you build and run that, you'll see "oninit" being printed to your console on every frame. Also, pressing the spacebar should cause it to jump, but it instead just moves upwards indefinitely because the constant oninit calls break the gravity logic.

If you then edit HostSprite.hx and move the oninit function to appear after the function called some_function, it will work as intended.

@hughsando
Copy link
Member

This appears to be fix in the latest haxe version. Can you try with a recent haxe compiler?

@AlexRamallo
Copy link
Author

AlexRamallo commented Jul 8, 2019

I tried it on the latest version on the dev branch and hxcpp 4.0.19 from haxelib, but I'm still seeing the same behavior on my test project.

EDIT:
To clarify, I experienced the same behavior on:

@dazKind
Copy link
Contributor

dazKind commented Sep 22, 2024

This issue might related to #1150. You might wanna test this PR HaxeFoundation/haxe#11773

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants