Sq Plus Create Construct Native Class Instance Bug
Last changed: -173.16.151.55

.

acsitdo

[Fix added to release 18, 5/28/06. jcs]

The following code produces an error:

#include <stdarg.h> 
#include <stdio.h> 

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

#ifdef SQUNICODE 
#define scvprintf vwprintf 
#else 
#define scvprintf vprintf 
#endif 

class Point3
{
public:
  float x, y, z;
};

DECLARE_INSTANCE_TYPE(Point3);

class Entity
{
public:
  void test(Point3& p)
  {
    if (scriptTest.func.IsNull())
      printf("invalid function\n");
    else
    {
      printf("p = {%f, %f, %f}\n", p.x, p.y, p.z);
      scriptTest(p);
    }
  }

  static int construct(HSQUIRRELVM v)
  {
    printf("construct()\n");
    StackHandler sa(v);

    Entity *self = new Entity();
    self->scriptTest = SqPlus::SquirrelFunction<void>(sa.GetObjectHandle(1),_T("test"));

    self->scriptObjHandle = sa.GetObjectHandle(1);
    sq_addref(SquirrelVM::GetVMPtr(), &self->scriptObjHandle);

    return SqPlus::PostConstruct<Entity>(v, self, release);
  }
  SQ_DECLARE_RELEASE(Entity);

private:
  SqPlus::SquirrelFunction<void> scriptTest;
  HSQOBJECT scriptObjHandle;
};


DECLARE_INSTANCE_TYPE(Entity);

void printfunc(HSQUIRRELVM v, const SQChar *s, ...)
{
  va_list arglist;
  va_start(arglist, s);
  vprintf(s, arglist);
  va_end(arglist);
}

int main()
{
  SquirrelVM::Init();

  sq_setprintfunc(SquirrelVM::GetVMPtr(), printfunc);

  HSQUIRRELVM v = SquirrelVM::GetVMPtr();

  SqPlus::SQClassDef<Entity>("Entity")
    .staticFuncVarArgs(&Entity::construct, "constructor", "*")
  ;
  SqPlus::SQClassDef<Point3>("Point3")
  ;


  SquirrelObject inst = SquirrelVM::CompileBuffer("\
    class A extends Entity {\n\
      function test(pos) {value=1; print(\"pos.x = \"+pos.x);}\n\
      value = 10;\n\
    };\n\
    function create() {return A();}\n\
  ");

  SquirrelVM::RunScript(inst);

  Entity *e = SqPlus::SquirrelFunction<Entity*>("create")();

  Point3 pt = {1,2,3};
  e->test(pt);

  SquirrelVM::Shutdown();

  return 0;
}

When executed it produces the following output:

construct()
p = {1.000000, 2.000000, 3.000000}

AN ERROR HAS OCCURED [the index 'value' does not exist]

CALLSTACK
*FUNCTION [test()] console buffer line [2]

LOCALS
[pos] INSTANCE
[this] TABLE

Note that error is in referencing member value and type of this is table.

That is because of bug in CreateConstructNativeClassInstance function.

inline BOOL CreateConstructNativeClassInstance(HSQUIRRELVM v,const SQChar * className) {
  int oldtop = sq_gettop(v);
  sq_pushroottable(v);
  sq_pushstring(v,className,-1);
  if (SQ_FAILED(sq_rawget(v,-2))) { // Get the class (created with sq_newclass()).
    sq_settop(v,oldtop);
    return FALSE;
  } // if
  sq_remove(v,-3); // Remove the root table.
  sq_push(v,1);    // Push the 'this'.
  if (SQ_FAILED(sq_call(v,1,SQTrue))) { // Call ClassName(): creates new instance and calls constructor (instead of sq_createinstance() where constructor is not called).
    sq_settop(v,oldtop);
    return FALSE;
  } // if
  sq_remove(v,-2); // Remove the class.
  //  int newtop = sq_gettop(v);
  return TRUE;
} // CreateConstructNativeClassInstance

Bug is in these lines:

sq_remove(v,-3); // Remove the root table. 
//^ -3 is index before root table pushed first at beginning of this function! 

sq_push(v,1); // Push the 'this'. 
//^ 1 is also invalid index because there was something on stack before

To fix this it is needed to replace them with

sq_remove(v,-2); // Remove the root table. 
sq_pushroottable(v); // Push the 'this'.