HaxeFoundation / hashlink

A virtual machine for Haxe
https://hashlink.haxe.org/
MIT License
797 stars 151 forks source link

JIT/VM calls incorrect method #685

Open m0rkeulv opened 1 month ago

m0rkeulv commented 1 month ago

When testing one of my projects with HL i got this unexpected behavior where the VM seems to call the wrong function, it only happens when the object is retrieved from “container” object. I have created a small project that hopefully should be able to reproduce this problem.

it was compiled on Windows 10 using haxe 4.3.4 and running on Hashlink 1.14.0 (downloaded form the release page here on Git)

Code to reproduce the problem: HL_JIT_Bug.zip

image image

Replacing the interface used with a typedef pointing to the base class changes the behavior. It looks like the correct method is called, but it causes an Access Violation, The debugger also no longer seems to recognize the type showing it as {...}

code to reproduce: HL_JIT_Bug_typedef.zip

image image

If i change the container to specific type instead of interface (replacing Userwith BasicUser) the code runs fine, so it seems to be related to the interface part.

UPDATE:

After some more experimentation i think i have found the cause of the issue, i think its when one interface inherits/ extends another one and they contain identical member names but returns a type extending the original type. this works on other targets and compiles fine, so i guess this is an issue with the HL VM

//Entity.hx (interface)
 function getVariable(varName:String):Variable
 //  User.hx (interface) (User extends Entity)
 function getVariable(name:String):UserVariable;
m0rkeulv commented 2 weeks ago

i've been playing around with the code and i got things working, but i am not a hundred percent sure i am not breaking anything. my changes are in safe cast, to me it looks like the issue is that since my other class contains a member that is first in the fields array it breaks and return false without checking if the expected fields are included.

this seems to work fine in what little tesiting i've done

case HVIRTUAL:
        if( to->virt->nfields < t->virt->nfields ) {
            int i, j;
            int matches = 0;
            for(i=0;i<to->virt->nfields;i++) {
                hl_obj_field *f2 = to->virt->fields + i;
                for(j=i; j < t->virt->nfields;j++) {
                    hl_obj_field *f1 = t->virt->fields + j;
                    if( f1->hashed_name == f2->hashed_name && hl_same_type(f1->t,f2->t) ) {
                        matches++;
                        break;
                    }
                }
            }
            if( matches == to->virt->nfields )
                return true;
        }
        break;
ncannasse commented 2 weeks ago

ping @yuxiaomao

On Tue, Jun 18, 2024 at 8:52 PM m0rkeulv @.***> wrote:

i've been playing around with the code and i got things working, but i am not a hundred percent sure i am not breaking anything. my changes are in safe cast, to me it looks like the issue is that since my other class contains a member that is first in the fields array it breaks and return false without checking if the expected fields are included.

this seems to work fine in what little tesiting i've done

case HVIRTUAL: if( to->virt->nfields < t->virt->nfields ) { int i, j; int matches = 0; for(i=0;ivirt->nfields;i++) { hl_obj_field f2 = to->virt->fields + i; for(j=i; j < t->virt->nfields;j++) { hl_obj_field f1 = t->virt->fields + j; if( f1->hashed_name == f2->hashed_name && hl_same_type(f1->t,f2->t) ) { matches++; break; } } } if( matches == to->virt->nfields ) return true; } break;



—
Reply to this email directly, view it on GitHub
<https://github.com/HaxeFoundation/hashlink/issues/685#issuecomment-2176751919>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAHZXQDBFL2TVRMMO3C3RDDZIB6X3AVCNFSM6AAAAABH6MJ5Z2VHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDCNZWG42TCOJRHE>
.
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
m0rkeulv commented 2 weeks ago

just adding this for some more context, to maybe help find a different root causeof my problem. if i add this to hl_safe_cast

if (t->kind == HOBJ && to->kind == HVIRTUAL) {
        vdynamic * d =  *(vdynamic**)t->obj->global_value;
        vvirtual* conv = hl_to_virtual(to, d);
        return hl_safe_cast(conv->t, to);
    }

(must of course be before this line to have any effect)

if( t->kind != to->kind )
        return false;

the typedef example also runs just fine, but it feels more like the issue is related to hl_to_virtual and what effect null values for fields ( hl_vfields(v)[i] = NULL;) has to the later execution.

before i modified the HVIRTUAL case in hl_safe_cast hl_to_virtual would set the field to NULL and this some how made the code try to execute a different method, maybe some kind of index missmatch between the real object type and the interface.

without the code above, the typdef seems to get the same problem, hl_vfields(v)[i] = NULL, and later on it throws an Access Violatio error.