albertodemichelis / squirrel

Official repository for the programming language Squirrel
http://www.squirrel-lang.org
MIT License
913 stars 156 forks source link

Root table delegate causes .getclass on an Embedded class instance to fail in squirrel 3.1, but works in 2.2.5 #229

Closed obfuscated closed 3 years ago

obfuscated commented 3 years ago

Hello,

I have the following program:

#include <squirrel.h>
#include <sqstdio.h>
#include <sqstdaux.h>
#include <sqstdmath.h>

#include <stdio.h>
#include <string.h>

void printfunc(HSQUIRRELVM v,const SQChar *s,...)
{
    fprintf(stdout, "=== from-sq: ");
    va_list vl;
    va_start(vl, s);
    vfprintf(stdout, s, vl);
    va_end(vl);
}

void errorfunc(HSQUIRRELVM v,const SQChar *s,...)
{
    fprintf(stderr, "=== from-sq-err: ");
    va_list vl;
    va_start(vl, s);
    vfprintf(stderr, s, vl);
    va_end(vl);
}

SQInteger constants_get(HSQUIRRELVM v)
{
    const SQChar *name = nullptr;
    sq_getstring(v, 2, &name);

    printf("constants_get: %s\n", name);

    if (scstrcmp(name, "nativeConst") == 0)
    {
        sq_pushstring(v, _SC("This is nativeConst!"), -1);
        return 1;
    }

    return -1;//sq_throwerror(v, _SC("constants_get unknown index!"));
}

//SQInteger constants_set(HSQUIRRELVM v)
//{
//    return 0;
//}

SQInteger TestClass_ctor(HSQUIRRELVM v)
{
    printf("TestClass_ctor\n");
    SQUserPointer ptr = nullptr;
    if (SQ_FAILED(sq_getinstanceup(v, 1, &ptr, SQUserPointer(0x1100))))
        return sq_throwerror(v, _SC("TestClass_ctor: Invalid type tag!"));
    return 0;
}

SQInteger TestClass_get(HSQUIRRELVM v)
{
    const SQChar *name = nullptr;
    sq_getstring(v, 2, &name);

    printf("TestClass_get: %s\n", name);

    return -1; //sq_throwerror(v, _SC("TestClass_get unknown index"));
}

int main(int argc, char* argv[])
{
    HSQUIRRELVM v;
    v = sq_open(1024); // creates a VM with initial stack size 1024
    sqstd_seterrorhandlers(v); //registers the default error handlers

#if SQUIRREL_VERSION_NUMBER>=300
    sq_setprintfunc(v, printfunc,errorfunc);
#else
    sq_setprintfunc(v, printfunc);
#endif
    {
        sq_pushconsttable(v);
        sq_pushstring(v, _SC("SQUIRREL_VER"), -1);
        sq_pushinteger(v, SQUIRREL_VERSION_NUMBER);
        sq_newslot(v, -3, SQTrue);
        sq_pop(v, 1); // pop const table
    }

    sq_pushroottable(v); //push the root table(were the globals of the script will be stored)
    sqstd_register_mathlib(v); // register math in the root table

    {
        // setup constants
        sq_newtable(v);
        sq_pushstring(v, _SC("_get"), -1);
        sq_newclosure(v, constants_get, 0);
        sq_newslot(v, -3, SQFalse);
//        sq_pushstring(v, _SC("_set"), -1);
//        sq_newclosure(v, constants_set, 0);
//        sq_newslot(v, -3, SQFalse);

        sq_setdelegate(v, -2);

        const SQInteger tableStackIdx = sq_gettop(v);
        sq_pushstring(v, _SC("TestClass"), -1);
        sq_newclass(v, SQFalse);
        sq_settypetag(v, -1, SQUserPointer(0x1100));

        sq_pushstring(v, _SC("constructor"), -1);
        sq_newclosure(v, TestClass_ctor, 0);
        sq_setnativeclosurename(v, -1, _SC("TestClass::constructor"));
        sq_newslot(v, -3, SQFalse);

        sq_pushstring(v, _SC("_get"), -1);
        sq_newclosure(v, TestClass_get, 0);
        sq_newslot(v, -3, SQFalse);

        sq_newslot(v, tableStackIdx, SQFalse); // Put the class in the root table. This must be last!

    }

    const char script[] = R"--(
print("This is script start!\n");

function printClassChain(c)
{
    local indent="";
    while (c)
    {
        print(indent + "Type: " + typeof(c) + "; current=" + c + "\n");
        if (SQUIRREL_VER>300)
            c = c.getbase();
        else
            c = c.parent;
        indent+="  ";
    }
}

scriptVar <- "Test scriptVar!";
print("before scriptVar=" + scriptVar + "\n");
print("before nativeConst=" + nativeConst + "\n");

scriptVar = "Test scriptVar after!";
//nativeConst = "Test nativeConst after!";

//print("after nativeConst=" + nativeConst + "\n");
print("after scriptVar=" + scriptVar + "\n");

local b = TestClass(19);
printClassChain(b.getclass());

print("This is script end!\n");
)--";

    if (SQ_FAILED(sq_compilebuffer(v, script, sizeof(script), _SC("<main>"), SQTrue)))
        return -1;

    sq_pushroottable(v);
    sq_call(v, 1, 0, SQTrue);

    sq_close(v);
    return 0;
}

I build it against squirrel 2.2.5 it produces this output:

=== from-sq: This is script start! === from-sq: before scriptVar=Test scriptVar! constants_get: nativeConst === from-sq: before nativeConst=This is nativeConst! === from-sq: after scriptVar=Test scriptVar after! TestClass_ctor TestClass_get: getclass === from-sq: Type: class; current=(class : 0x0x610000000440) === from-sq: This is script end!

But if I run it against squirrel 3.1.0 it throws an error and fails with this output:

=== from-sq: This is script start! === from-sq: before scriptVar=Test scriptVar! constants_get: nativeConst === from-sq: before nativeConst=This is nativeConst! === from-sq: after scriptVar=Test scriptVar after! TestClass_ctor TestClass_get: getclass === from-sq-err: AN ERROR HAS OCCURED [the index 'nativeConst' does not exist] === from-sq-err: CALLSTACK === from-sq-err: *FUNCTION [main()]

line [29] === from-sq-err: LOCALS === from-sq-err: [b] INSTANCE === from-sq-err: [vargv] ARRAY === from-sq-err: [this] TABLE

Is this the expected behaviour? My goal is to be able to put const-like (read only from squirrel) objects in the root table or a table of choice.

It seems the problem happens because an error during delegation of the root-table infest a following delegation on the class instance.

obfuscated commented 3 years ago

BTW commenting the line print("before nativeConst=" + nativeConst + "\n"); makes the call to .getclass work correctly in squirrel 3.1.0 or even squirrel master.

obfuscated commented 3 years ago

I guess, my issue is not reading the documentation.

sq_pushnull(v);
sq_throwobject(v);

before return -1 fixes it.