Jump to content
  • Advertisement
Boost113

Error when assigning script object to dictionary

Recommended Posts

Hi,

I got these strange errors in my application:

[SCRIPT] [ERROR]  (0, 0) : Object {245683}. GC cannot destroy an object of type '�t|' as it doesn't know how many references to there are.
[SCRIPT] [ERROR]  (0, 0) : Object {245593}. GC cannot destroy an object of type '�t|' as it doesn't know how many references to there are.
[SCRIPT] [ERROR]  (0, 0) : Object {245677}. GC cannot destroy an object of type '�t|' as it doesn't know how many references to there are.

The type string is always different. And Valgrind says this:

==29943== Invalid read of size 4
==29943==    at 0x5188E00: asCAtomic::get() const (as_atomic.cpp:52)
==29943==    by 0x51A1761: asCScriptEngine::CallObjectMethodRetInt(void*, int) const (as_scriptengine.cpp:4203)
==29943==    by 0x5192A71: asCGarbageCollector::DestroyOldGarbage() (as_gc.cpp:564)
==29943==    by 0x519370D: asCGarbageCollector::GarbageCollect(unsigned int, unsigned int) (as_gc.cpp:238)
==29943==    by 0x519DD03: asCScriptEngine::GarbageCollect(unsigned int, unsigned int) (as_scriptengine.cpp:4636)
==29943==    by 0x5190261: asCContext::Execute() (as_context.cpp:1356)
==29943==    by 0x4FDF9A4: RunScriptMethod<void> (ScriptExecutor.h:196)
==29943==    by 0x4FDF9A4: Leviathan::ScriptSystemWrapper::CreateAndDestroyNodes() (ScriptSystemWrapper.cpp:134)
==29943==    by 0x4FD2D28: Leviathan::GameWorld::HandleAddedAndDeleted() (GameWorld.cpp:593)
==29943==    by 0x4FF09A5: Leviathan::StandardWorld::HandleAddedAndDeleted() (StandardWorld.cpp:690)
==29943==    by 0x4FD87F3: Leviathan::GameWorld::Tick(int) (GameWorld.cpp:528)
==29943==    by 0x4FA1F1E: Leviathan::Engine::Tick() (Engine.cpp:895)
==29943==    by 0x507FB0F: Leviathan::LeviathanApplication::RunMessageLoop() (Application.cpp:137)
==29943==  Address 0x9e2a2c40 is 16 bytes inside a block of size 148 free'd
==29943==    at 0x4C2FDAC: free (vg_replace_malloc.c:530)
==29943==    by 0x51AE63A: asCScriptObject::Release() const (as_scriptobject.cpp:647)
==29943==    by 0x51A1120: asCScriptEngine::CallObjectMethod(void*, asSSystemFunctionInterface*, asCScriptFunction*) const (as_scriptengine.cpp:4050)
==29943==    by 0x518FE03: asCContext::ExecuteNext() (as_context.cpp:2813)
==29943==    by 0x519011C: asCContext::Execute() (as_context.cpp:1324)
==29943==    by 0x50296FC: Leviathan::ScriptRunResult<int> Leviathan::ScriptExecutor::RunScriptMethod<int, Leviathan::GenericEvent*&>(Leviathan::ScriptRunningSetup&, asIScriptFunction*, void*, Leviathan::GenericEvent*&) (ScriptExecutor.h:196)
==29943==    by 0x502BF2A: non-virtual thunk to Leviathan::Script::EventListener::OnGenericEvent(Leviathan::GenericEvent*) (new_allocator.h:125)
==29943==    by 0x50724CC: Leviathan::EventHandler::CallEvent(Leviathan::GenericEvent*) (EventHandler.cpp:65)
==29943==    by 0x50608DA: CallEvent (EventHandler.h:38)
==29943==    by 0x50608DA: Leviathan::GUI::View::OnProcessMessageReceived(scoped_refptr<CefBrowser>, cef_process_id_t, scoped_refptr<CefProcessMessage>) (GuiView.cpp:860)
==29943==    by 0x51328D6: (anonymous namespace)::client_on_process_message_received(_cef_client_t*, _cef_browser_t*, cef_process_id_t, _cef_process_message_t*) (client_cpptoc.cc:264)
==29943==    by 0x77EC397: ??? (in /home/hhyyrylainen/Projects/thrive/build/bin/lib/libcef.so)
==29943==    by 0x93A7639: ??? (in /home/hhyyrylainen/Projects/thrive/build/bin/lib/libcef.so)
==29943==  Block was alloc'd at
==29943==    at 0x4C2EBAB: malloc (vg_replace_malloc.c:299)
==29943==    by 0x518FA79: asCContext::ExecuteNext() (as_context.cpp:2710)
==29943==    by 0x519011C: asCContext::Execute() (as_context.cpp:1324)
==29943==    by 0x50296FC: Leviathan::ScriptRunResult<int> Leviathan::ScriptExecutor::RunScriptMethod<int, Leviathan::GenericEvent*&>(Leviathan::ScriptRunningSetup&, asIScriptFunction*, void*, Leviathan::GenericEvent*&) (ScriptExecutor.h:196)
==29943==    by 0x502BF2A: non-virtual thunk to Leviathan::Script::EventListener::OnGenericEvent(Leviathan::GenericEvent*) (new_allocator.h:125)
==29943==    by 0x50724CC: Leviathan::EventHandler::CallEvent(Leviathan::GenericEvent*) (EventHandler.cpp:65)
==29943==    by 0x50608DA: CallEvent (EventHandler.h:38)
==29943==    by 0x50608DA: Leviathan::GUI::View::OnProcessMessageReceived(scoped_refptr<CefBrowser>, cef_process_id_t, scoped_refptr<CefProcessMessage>) (GuiView.cpp:860)
==29943==    by 0x51328D6: (anonymous namespace)::client_on_process_message_received(_cef_client_t*, _cef_browser_t*, cef_process_id_t, _cef_process_message_t*) (client_cpptoc.cc:264)
==29943==    by 0x77EC397: ??? (in /home/hhyyrylainen/Projects/thrive/build/bin/lib/libcef.so)
==29943==    by 0x93A7639: ??? (in /home/hhyyrylainen/Projects/thrive/build/bin/lib/libcef.so)
==29943==    by 0x93A747D: ??? (in /home/hhyyrylainen/Projects/thrive/build/bin/lib/libcef.so)
==29943==    by 0x93A6AA9: ??? (in /home/hhyyrylainen/Projects/thrive/build/bin/lib/libcef.so)

After looking for a minimal example that would cause an issue here, I think I got it (this doesn't lead to the same error, though):

class Organelle{
}

class PlacedOrganelle{

    PlacedOrganelle(Organelle@ organelle)
    {
        @this.organelle = organelle;
    }

    Organelle@ organelle;
}

void RunTest()
{
    PlacedOrganelle@ organelle = PlacedOrganelle(Organelle());

    dictionary data;
	
  	// This alternative works
    // @data["organelle"] = organelle;
  
    // This causes errors
    data["organelle"] = organelle;

    PlacedOrganelle@ organelle2 = cast<PlacedOrganelle>(data["organelle"]);
}

The error that I get with that is:

[SCRIPT] [ERROR]  (0, 0) : Failed in call to function 'CreateScriptObject' (Code: asNO_FUNCTION, -6)

And it goes away if I add "@" to the assignment into the dictionary. This same thing also fixes my full application so I think this is the same error in both even if the behavior is different.

Tested with revision 2547. I tried updating to see if there was a fix (I'd like to get a compile error with the incorrect code), but revision 2557 fails to compile with (this error repeats quite a few times):

In file included from /home/hhyyrylainen/Projects/Leviathan/build/ThirdParty/include/add_on/scriptmath/scriptmathcomplex.h:6,
                 from /home/hhyyrylainen/Projects/Leviathan/build/ThirdParty/include/add_on/scriptmath/scriptmathcomplex.cpp:5:
/home/hhyyrylainen/Projects/Leviathan/build/ThirdParty/include/add_on/scriptmath/scriptmathcomplex.cpp: Funktio ”void RegisterScriptMathComplex_Native(asIScriptEngine*)”:
/home/hhyyrylainen/Projects/Leviathan/build/ThirdParty/include/angelscript.h:421:62: virhe: cast from ”float*” to ”int” loses precision [-fpermissive]
 #define asOFFSET(s,m) ((int)(&reinterpret_cast<s*>(100000)->m)-100000)
                                                              ^
/home/hhyyrylainen/Projects/Leviathan/build/ThirdParty/include/add_on/scriptmath/scriptmathcomplex.cpp:177:59: huom: in expansion of macro ”asOFFSET”
  r = engine->RegisterObjectProperty("complex", "float r", asOFFSET(Complex, r)); assert( r >= 0 );
                                                           ^~~~~~~~
/home/hhyyrylainen/Projects/Leviathan/build/ThirdParty/include/angelscript.h:421:62: virhe: cast from ”float*” to ”int” loses precision [-fpermissive]
 #define asOFFSET(s,m) ((int)(&reinterpret_cast<s*>(100000)->m)-100000)
                                                              ^
/home/hhyyrylainen/Projects/Leviathan/build/ThirdParty/include/add_on/scriptmath/scriptmathcomplex.cpp:178:59: huom: in expansion of macro ”asOFFSET”
  r = engine->RegisterObjectProperty("complex", "float i", asOFFSET(Complex, i)); assert( r >= 0 );
                                                           ^~~~~~~~

I'm using "gcc (GCC) 8.2.1 20181011 (Red Hat 8.2.1-4)"

Share this post


Link to post
Share on other sites
Advertisement

Thanks. I'll look into this. However from the evidence you showed I don't think you've managed to reproduce your original problem.

In your original problem it appears that you have a case of objects being destroyed too early, i.e. Release() called on pointers not owned by the code that is calling the method. This leads to the invalid reads that Valgrind reports. Changing the script to use a handle assign instead of value assign is probably just camoflaging the error rather than actually fixing it.

In your minimal code you've identified a runtime problem that I'll need to look into. It can probably not be caught at compile time, but should at least produce a proper script exception showing that the object doesn't have a default constructor.

The problem with asOFFSET is caused.by the latest change. I'll make an additional change to make sure the code works on gnuc again.

I'll reply when I've made the changes.

Regards,

Andreas

Share this post


Link to post
Share on other sites

I ran my full code again with valgrind (with the added "@") and I'm no longer getting any errors related to the scripts (just some other stuff like uninitialized values used deep in other libraries).

So I think that also fixes my full code as I don't get any messages on shutdown and valgrind is quiet after I trigger that code.

My full code is here if you want to have a look: https://github.com/Revolutionary-Games/Thrive/blob/fix_everything/scripts/microbe_stage/microbe_editor/microbe_editor.as#L296

You are correct that my example code can be fixed by adding a default constructor for PlacedOrganelle. I tried more accurately following what my actual code looks like like this:

class Organelle{

    Organelle(const string &in name, int cost)
    {
        this.cost = cost;
        this.name = name;
    }
    
    int cost;
    string name;    
}

class PlacedOrganelle{

    // Default constructor doesn't help
    // PlacedOrganelle(){}

    PlacedOrganelle(Organelle@ organelle, int q, int r, int rotation)
    {
        @this._organelle = organelle;
        this.q = q;
        this.r = r;
        this.rotation = rotation;
    }

    PlacedOrganelle(PlacedOrganelle@ typefromother, int q, int r, int rotation)
    {
        @this._organelle = typefromother._organelle;
        this.q = q;
        this.r = r;
        this.rotation = rotation;
    }

    PlacedOrganelle(PlacedOrganelle@ other)
    {
        @this._organelle = other._organelle;
        this.q = other.q;
        this.r = other.r;
        this.rotation = other.rotation;

        // _commonConstructor();
    }

    ~PlacedOrganelle()
    {
    }

    int q;
    int r;
    int rotation;

    const Organelle@ organelle {
        get const{
            return _organelle;
        }
    }

    private Organelle@ _organelle;
}

void RunTest()
{
    PlacedOrganelle@ organelle = PlacedOrganelle(Organelle("cytoplasm", 10), 1, 1, 0);

    dictionary data;

    data["organelle"] = organelle;

    PlacedOrganelle@ organelle2 = cast<PlacedOrganelle>(data["organelle"]);
}

And I now get (even when I add a default constructor for PlacedOrganelle) a segmentation fault:

Program received signal SIGSEGV, Segmentation fault.
0x00000000008db160 in ?? ()

0  in ??
1  in asCScriptEngine::CallObjectMethod(void*, asSSystemFunctionInterface*, asCScriptFunction*) const of /home/hhyyrylainen/Projects/Leviathan/ThirdParty/angelscript/sdk/angelscript/source/as_scriptengine.cpp:4024
2  in asCContext::ExecuteNext of /home/hhyyrylainen/Projects/Leviathan/ThirdParty/angelscript/sdk/angelscript/source/as_context.cpp:2813
3  in asCContext::Execute() of /home/hhyyrylainen/Projects/Leviathan/ThirdParty/angelscript/sdk/angelscript/source/as_context.cpp:1324
4  in Leviathan::ScriptExecutor::RunScript<void> of /home/hhyyrylainen/Projects/Leviathan/Engine/Script/ScriptExecutor.h:802
5  in Leviathan::ScriptExecutor::RunScript<void> of /home/hhyyrylainen/Projects/Leviathan/Engine/Script/ScriptExecutor.h:86
6  in ____C_A_T_C_H____T_E_S_T____34 of /home/hhyyrylainen/Projects/Leviathan/LeviathanTest/TestFiles/Script.cpp:791
7  in Catch::TestCase::invoke of /home/hhyyrylainen/Projects/Leviathan/LeviathanTest/catch/catch.hpp:10910
8  in Catch::RunContext::invokeActiveTestCase() of /home/hhyyrylainen/Projects/Leviathan/LeviathanTest/catch/catch.hpp:9774
9  in Catch::RunContext::runCurrentTest(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&) of /home/hhyyrylainen/Projects/Leviathan/LeviathanTest/catch/catch.hpp:9748
10 in Catch::RunContext::runTest(Catch::TestCase const&) of /home/hhyyrylainen/Projects/Leviathan/LeviathanTest/catch/catch.hpp:9524
11 in Catch::(anonymous namespace)::runTests of /home/hhyyrylainen/Projects/Leviathan/LeviathanTest/catch/catch.hpp:10073
12 in Catch::Session::runInternal() of /home/hhyyrylainen/Projects/Leviathan/LeviathanTest/catch/catch.hpp:10266
13 in Catch::Session::run of /home/hhyyrylainen/Projects/Leviathan/LeviathanTest/catch/catch.hpp:10223
14 in Catch::Session::run<char> of /home/hhyyrylainen/Projects/Leviathan/LeviathanTest/catch/catch.hpp:13744
15 in main of /home/hhyyrylainen/Projects/Leviathan/LeviathanTest/catch/catch.hpp:13744
16 in __libc_start_main of /lib64/libc.so.6
17 in _start of /home/hhyyrylainen/Projects/Leviathan/LeviathanTest/catch/catch.hpp:14069

I was able to extract this callstack with gdb from the asContext:

	> /home/hhyyrylainen/Projects/Leviathan/build/bin/Data/Scripts/tests/AnonymousDelegateLeak.as:72 void RunTest()

Line 72 is the last line of the file and the closing } for function RunTest so it crashes when exiting the function, I presume.

 

Share this post


Link to post
Share on other sites

Thanks for the code snippet for reproducing the problem. I've identified the cause.

The copy constructor in your PlacedOrganelle script class is taking the other object by handle rather than by reference. Unfortunately there is a bug in the library that doesn't properly treat this when the asIScriptEngine::CreateScriptObjectCopy is used. This causes the call to the copy constructor to release the handle without the refcount first being incremented, thus leading to the object being destroyed too early as I mentioned earlier.

The fix for the bug is in as_scriptobject.cpp line 166. Change the call from SetArgAddress to SetArgObject, and it will work. I'll have this fix checked in as soon as I can.

Alternatively you can change your script to use & for the copy constructor, i.e. 

 PlacedOrganelle(PlacedOrganelle& other)

 

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!