I managed to solve most of my problems, but encountered a new one - and since it's related to refcounting script registered OBJ_REF classes, I thought I will continue here rather than start a new topic. I've spent some hours debugging this and I have literally no idea why this happens, so hope @WitchLord or anyone knowing AS internals much better than me will be able to help.
asCHECK(engine->RegisterObjectType("CollisionShape", 0, asOBJ_REF));
asCHECK(engine->RegisterObjectBehaviour("CollisionShape", asBEHAVE_ADDREF, "void f()", asMETHOD(CollisionShapeScriptProxy, addRef), asCALL_THISCALL));
asCHECK(engine->RegisterObjectBehaviour("CollisionShape", asBEHAVE_RELEASE, "void f()", asMETHOD(CollisionShapeScriptProxy, release), asCALL_THISCALL));
asCHECK(engine->SetDefaultNamespace("CollisionShape"));
asCHECK(engine->RegisterGlobalFunction("CollisionShape@ makeSphere(float)", asMETHOD(CollisionShapeFactory, makeSphere), asCALL_THISCALL_ASGLOBAL, this));
asCHECK(engine->RegisterGlobalFunction("CollisionShape@ makePlane(const vec3 &in, float)", asMETHOD(CollisionShapeFactory, makePlane), asCALL_THISCALL_ASGLOBAL, this));
asCHECK(engine->RegisterGlobalFunction("CollisionShape@ makeBox(const vec3 &in)", asMETHOD(CollisionShapeFactory, makeBox), asCALL_THISCALL_ASGLOBAL, this));
asCHECK(engine->SetDefaultNamespace(""));
Important note on what's different here than my other classes (which work fine!): this class has no factory registered, because I want to have several factory functions which are registered as globals to make different kinds of the same CollisionShape.
Now, the class has the same mechanism of refcounting as my other component-classes (transform, animator, skeleton etc.) but only this class has weird behaviour of AddRef happening one time too many. Lifetime of this class is as follow:
1. Inside Script Object it's created via
@collisionShape = CollisionShape::makeBox(...);
2. Default ref count is 1, class is created with new CollisionShapeProxy, then it's destroyed when refcount reaches 0.
3. CollisionShape is passed to RigidBody which AddRefs it during its lifetime then Releases, but the problem does not lie there, and to rule it out I stopped affecting refcount when it's passed to RigidBody for now. So all that keeps hold on that object is @collisionShape assignment shown above.
4. When I destroy the object, it cleans up doing this:
@collisionShape = null;
So this should effectively delete the proxy because neither RigidBody nor ScriptObject holds reference anymore... but it doesn't. All other objects behave like this but this one stays on 1 refcount for some unknown reason. I started dumping all addrefs & releases including few lines of callstack and this is what happens during its lifetime:
1 2018-10-21 21:08:59,136891 slot_map.h:88 [VERBOSE] [class CollisionShape] Creating new element @ sparseIndex 0, version: 0
2 2018-10-21 21:09:02,875345 slotmap_script_proxy.h:61 [VERBOSE] class CollisionShape[1370FB38]++ (1) ->
3 asCScriptEngine::CallObjectMethod [as_scriptengine.cpp:4069]
4 asCScriptEngine::CallObjectMethod [as_scriptengine.cpp:4013]
5 asCContext::ExecuteNext [as_context.cpp:2893]
6 asCContext::Execute [as_context.cpp:1328]
7 2018-10-21 21:09:02,875345 slotmap_script_proxy.h:67 [VERBOSE] class CollisionShape[1370FB38]-- (2) ->
8 asCScriptEngine::CallObjectMethod [as_scriptengine.cpp:4069]
9 asCScriptEngine::CallObjectMethod [as_scriptengine.cpp:4013]
10 asCContext::ExecuteNext [as_context.cpp:2815]
11 asCContext::Execute [as_context.cpp:1328]
12 2018-10-21 21:09:06,515185 slotmap_script_proxy.h:61 [VERBOSE] class CollisionShape[1370FB38]++ (1) ->
13 asCScriptEngine::CallObjectMethod [as_scriptengine.cpp:4069]
14 asCScriptEngine::CallObjectMethod [as_scriptengine.cpp:4013]
15 asCContext::ExecuteNext [as_context.cpp:4152]
16 asCContext::Execute [as_context.cpp:1328]
--- here script object is destroyed, all resources released (except CollisionShape which stays alive ---
17 2018-10-21 21:09:08,394326 slotmap_script_proxy.h:67 [VERBOSE] class CollisionShape[1370FB38]-- (2) ->
18 asCScriptEngine::CallObjectMethod [as_scriptengine.cpp:4069]
19 asCScriptEngine::CallObjectMethod [as_scriptengine.cpp:4013]
20 asCContext::ExecuteNext [as_context.cpp:2888]
21 asCContext::Execute [as_context.cpp:1328]
Numbers in ( ) are refCount BEFORE it's decreased or increased. So we begin with line 2, refcount (1) which is initial refcount and it's increased (2).
Then it's decreased on line 7, so we're back to (1), but then again it's increased to (2) on line 12 and it stays like this. So the only thing that's referencing
our CollisionShape is the script, and somehow it holds 2 references. Then at line 17 we have decref due to destruction, so it goes down to (1) and stays
like this forever - it will never be cleaned up. Nothing like this happens with other classes using same base refcounted class... The only difference I see
is that this class is the only one made not through factory behaviour but global function, but it has the same declaration as factory.
I took screenshots of these 3 calls, inside context where it seems to be most relevant - 2 addrefs & 1 release:
\
I have no idea what happens here - seems like there are two things that increase ref: asBC_REFCPY, then asBC_RefCpyV, but only one that decreases asBC_FREE. This leaves the proxy refcount at 2 (initial 1 + non-released ref during script execution). The whole thing happens during construction of script object that holds the reference, and CollisionShape@ is created inside its constructor like this:
So... if anyone has any idea what's going on with this additional addRef - please halp! Sorry for that long post, but I lack the insight to follow what's going on there regarding lifetime and refcounting - I just don't know when AS increases those and when not etc. I hope that by providing that much information someone will spot the problem..