1
2 // The CScriptWeakRef class was originally implemented by vroad in March 2013
3
4 #include "weakref.h"
5 #include <new>
6 #include <assert.h>
7 #include <string.h> // strstr()
8
9 BEGIN_AS_NAMESPACE
10
ScriptWeakRefConstruct(asITypeInfo * type,void * mem)11 static void ScriptWeakRefConstruct(asITypeInfo *type, void *mem)
12 {
13 new(mem) CScriptWeakRef(type);
14 }
15
ScriptWeakRefConstruct2(asITypeInfo * type,void * ref,void * mem)16 static void ScriptWeakRefConstruct2(asITypeInfo *type, void *ref, void *mem)
17 {
18 new(mem) CScriptWeakRef(ref, type);
19
20 // It's possible the constructor raised a script exception, in which case we
21 // need to call the destructor in order to cleanup the memory before returning
22 asIScriptContext *ctx = asGetActiveContext();
23 if( ctx && ctx->GetState() == asEXECUTION_EXCEPTION )
24 reinterpret_cast<CScriptWeakRef*>(mem)->~CScriptWeakRef();
25 }
26
ScriptWeakRefDestruct(CScriptWeakRef * obj)27 static void ScriptWeakRefDestruct(CScriptWeakRef *obj)
28 {
29 obj->~CScriptWeakRef();
30 }
31
ScriptWeakRefTemplateCallback(asITypeInfo * ti,bool &)32 static bool ScriptWeakRefTemplateCallback(asITypeInfo *ti, bool &/*dontGarbageCollect*/)
33 {
34 asITypeInfo *subType = ti->GetSubType();
35
36 // Weak references only work for reference types
37 if( subType == 0 ) return false;
38 if( !(subType->GetFlags() & asOBJ_REF) ) return false;
39
40 // The subtype shouldn't be a handle
41 if( ti->GetSubTypeId() & asTYPEID_OBJHANDLE )
42 return false;
43
44 // Make sure the type really supports weak references
45 asUINT cnt = subType->GetBehaviourCount();
46 for( asUINT n = 0; n < cnt; n++ )
47 {
48 asEBehaviours beh;
49 subType->GetBehaviourByIndex(n, &beh);
50 if( beh == asBEHAVE_GET_WEAKREF_FLAG )
51 return true;
52 }
53
54 ti->GetEngine()->WriteMessage("weakref", 0, 0, asMSGTYPE_ERROR, "The subtype doesn't support weak references");
55 return false;
56 }
57
CScriptWeakRef(asITypeInfo * type)58 CScriptWeakRef::CScriptWeakRef(asITypeInfo *type)
59 {
60 m_ref = 0;
61 m_type = type;
62 m_type->AddRef();
63 m_weakRefFlag = 0;
64 }
65
CScriptWeakRef(const CScriptWeakRef & other)66 CScriptWeakRef::CScriptWeakRef(const CScriptWeakRef &other)
67 {
68 m_ref = other.m_ref;
69 m_type = other.m_type;
70 m_type->AddRef();
71 m_weakRefFlag = other.m_weakRefFlag;
72 if( m_weakRefFlag )
73 m_weakRefFlag->AddRef();
74 }
75
CScriptWeakRef(void * ref,asITypeInfo * type)76 CScriptWeakRef::CScriptWeakRef(void *ref, asITypeInfo *type)
77 {
78 m_ref = ref;
79 m_type = type;
80 m_type->AddRef();
81
82 // The given type should be the weakref template instance
83 assert( strcmp(type->GetName(), "weakref") == 0 ||
84 strcmp(type->GetName(), "const_weakref") == 0 );
85
86 // Get the shared flag that will tell us when the object has been destroyed
87 // This is threadsafe as we hold a strong reference to the object
88 m_weakRefFlag = m_type->GetEngine()->GetWeakRefFlagOfScriptObject(m_ref, m_type->GetSubType());
89 if( m_weakRefFlag )
90 m_weakRefFlag->AddRef();
91 }
92
~CScriptWeakRef()93 CScriptWeakRef::~CScriptWeakRef()
94 {
95 if( m_type )
96 m_type->Release();
97 if( m_weakRefFlag )
98 m_weakRefFlag->Release();
99 }
100
operator =(const CScriptWeakRef & other)101 CScriptWeakRef &CScriptWeakRef::operator =(const CScriptWeakRef &other)
102 {
103 // Don't do anything if it is the same reference
104 if( m_ref == other.m_ref )
105 return *this;
106
107 // Must not allow changing the type
108 if( m_type != other.m_type )
109 {
110 // We can allow a weakref to be assigned to a const_weakref
111 if( !(strcmp(m_type->GetName(), "const_weakref") == 0 &&
112 strcmp(other.m_type->GetName(), "weakref") == 0 &&
113 m_type->GetSubType() == other.m_type->GetSubType()) )
114 {
115 assert( false );
116 return *this;
117 }
118 }
119
120 m_ref = other.m_ref;
121
122 if( m_weakRefFlag )
123 m_weakRefFlag->Release();
124 m_weakRefFlag = other.m_weakRefFlag;
125 if( m_weakRefFlag )
126 m_weakRefFlag->AddRef();
127
128 return *this;
129 }
130
Set(void * newRef)131 CScriptWeakRef &CScriptWeakRef::Set(void *newRef)
132 {
133 // Release the previous weak ref
134 if( m_weakRefFlag )
135 m_weakRefFlag->Release();
136
137 // Retrieve the new weak ref
138 m_ref = newRef;
139 if( newRef )
140 {
141 m_weakRefFlag = m_type->GetEngine()->GetWeakRefFlagOfScriptObject(newRef, m_type->GetSubType());
142 m_weakRefFlag->AddRef();
143 }
144 else
145 m_weakRefFlag = 0;
146
147 // Release the newRef since we're only supposed to hold a weakref
148 m_type->GetEngine()->ReleaseScriptObject(newRef, m_type->GetSubType());
149
150 return *this;
151 }
152
GetRefType() const153 asITypeInfo *CScriptWeakRef::GetRefType() const
154 {
155 return m_type->GetSubType();
156 }
157
operator ==(const CScriptWeakRef & o) const158 bool CScriptWeakRef::operator==(const CScriptWeakRef &o) const
159 {
160 if( m_ref == o.m_ref &&
161 m_type == o.m_type )
162 return true;
163
164 // TODO: If type is not the same, we should attempt to do a dynamic cast,
165 // which may change the pointer for application registered classes
166
167 return false;
168 }
169
operator !=(const CScriptWeakRef & o) const170 bool CScriptWeakRef::operator!=(const CScriptWeakRef &o) const
171 {
172 return !(*this == o);
173 }
174
175 // AngelScript: used as '@obj = ref.get();'
Get() const176 void *CScriptWeakRef::Get() const
177 {
178 // If we hold a null handle, then just return null
179 if( m_ref == 0 || m_weakRefFlag == 0 )
180 return 0;
181
182 // Lock on the shared bool, so we can be certain it won't be changed to true
183 // between the inspection of the flag and the increase of the ref count in the
184 // owning object.
185 m_weakRefFlag->Lock();
186 if( !m_weakRefFlag->Get() )
187 {
188 m_type->GetEngine()->AddRefScriptObject(m_ref, m_type->GetSubType());
189 m_weakRefFlag->Unlock();
190 return m_ref;
191 }
192 m_weakRefFlag->Unlock();
193
194 return 0;
195 }
196
Equals(void * ref) const197 bool CScriptWeakRef::Equals(void *ref) const
198 {
199 // Release the ref since we'll not keep it
200 m_type->GetEngine()->ReleaseScriptObject(ref, m_type->GetSubType());
201
202 if( m_ref == ref )
203 return true;
204
205 return false;
206 }
207
RegisterScriptWeakRef_Native(asIScriptEngine * engine)208 void RegisterScriptWeakRef_Native(asIScriptEngine *engine)
209 {
210 int r;
211
212 // Register a type for non-const handles
213 r = engine->RegisterObjectType("weakref<class T>", sizeof(CScriptWeakRef), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_TEMPLATE | asOBJ_APP_CLASS_DAK); assert( r >= 0 );
214
215 r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptWeakRefConstruct), asCALL_CDECL_OBJLAST); assert( r>= 0 );
216 r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in, T@+)", asFUNCTION(ScriptWeakRefConstruct2), asCALL_CDECL_OBJLAST); assert( r>= 0 );
217 r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptWeakRefDestruct), asCALL_CDECL_OBJLAST); assert( r >= 0 );
218 r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptWeakRefTemplateCallback), asCALL_CDECL); assert( r >= 0 );
219
220 r = engine->RegisterObjectMethod("weakref<T>", "T@ opImplCast()", asMETHOD(CScriptWeakRef, Get), asCALL_THISCALL); assert(r >= 0);
221 r = engine->RegisterObjectMethod("weakref<T>", "T@ get() const", asMETHODPR(CScriptWeakRef, Get, () const, void*), asCALL_THISCALL); assert( r >= 0 );
222 r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opHndlAssign(const weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert( r >= 0 );
223 r = engine->RegisterObjectMethod("weakref<T>", "bool opEquals(const weakref<T> &in) const", asMETHODPR(CScriptWeakRef, operator==, (const CScriptWeakRef &) const, bool), asCALL_THISCALL); assert( r >= 0 );
224 r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opHndlAssign(T@)", asMETHOD(CScriptWeakRef, Set), asCALL_THISCALL); assert( r >= 0 );
225 r = engine->RegisterObjectMethod("weakref<T>", "bool opEquals(const T@) const", asMETHOD(CScriptWeakRef, Equals), asCALL_THISCALL); assert(r >= 0);
226
227 // Register another type for const handles
228 r = engine->RegisterObjectType("const_weakref<class T>", sizeof(CScriptWeakRef), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_TEMPLATE | asOBJ_APP_CLASS_DAK); assert( r >= 0 );
229
230 r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptWeakRefConstruct), asCALL_CDECL_OBJLAST); assert( r>= 0 );
231 r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in, const T@+)", asFUNCTION(ScriptWeakRefConstruct2), asCALL_CDECL_OBJLAST); assert( r>= 0 );
232 r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptWeakRefDestruct), asCALL_CDECL_OBJLAST); assert( r >= 0 );
233 r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptWeakRefTemplateCallback), asCALL_CDECL); assert( r >= 0 );
234
235 r = engine->RegisterObjectMethod("const_weakref<T>", "const T@ opImplCast() const", asMETHOD(CScriptWeakRef, Get), asCALL_THISCALL); assert(r >= 0);
236 r = engine->RegisterObjectMethod("const_weakref<T>", "const T@ get() const", asMETHODPR(CScriptWeakRef, Get, () const, void*), asCALL_THISCALL); assert( r >= 0 );
237 r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const const_weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert( r >= 0 );
238 r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const const_weakref<T> &in) const", asMETHODPR(CScriptWeakRef, operator==, (const CScriptWeakRef &) const, bool), asCALL_THISCALL); assert( r >= 0 );
239 r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const T@)", asMETHOD(CScriptWeakRef, Set), asCALL_THISCALL); assert( r >= 0 );
240 r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const T@) const", asMETHOD(CScriptWeakRef, Equals), asCALL_THISCALL); assert(r >= 0);
241
242 // Allow non-const weak references to be converted to const weak references
243 r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const weakref<T> &in)", asMETHOD(CScriptWeakRef, operator=), asCALL_THISCALL); assert( r >= 0 );
244 r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const weakref<T> &in) const", asMETHODPR(CScriptWeakRef, operator==, (const CScriptWeakRef &) const, bool), asCALL_THISCALL); assert( r >= 0 );
245 }
246
ScriptWeakRefConstruct_Generic(asIScriptGeneric * gen)247 static void ScriptWeakRefConstruct_Generic(asIScriptGeneric *gen)
248 {
249 asITypeInfo *ti = *reinterpret_cast<asITypeInfo**>(gen->GetAddressOfArg(0));
250
251 ScriptWeakRefConstruct(ti, gen->GetObject());
252 }
253
ScriptWeakRefConstruct2_Generic(asIScriptGeneric * gen)254 static void ScriptWeakRefConstruct2_Generic(asIScriptGeneric *gen)
255 {
256 asITypeInfo *ti = *reinterpret_cast<asITypeInfo**>(gen->GetAddressOfArg(0));
257 void *ref = gen->GetArgAddress(1);
258
259 ScriptWeakRefConstruct2(ti, ref, gen->GetObject());
260 }
261
ScriptWeakRefDestruct_Generic(asIScriptGeneric * gen)262 static void ScriptWeakRefDestruct_Generic(asIScriptGeneric *gen)
263 {
264 CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
265 self->~CScriptWeakRef();
266 }
267
CScriptWeakRef_Get_Generic(asIScriptGeneric * gen)268 void CScriptWeakRef_Get_Generic(asIScriptGeneric *gen)
269 {
270 CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
271 gen->SetReturnAddress(self->Get());
272 }
273
CScriptWeakRef_Assign_Generic(asIScriptGeneric * gen)274 void CScriptWeakRef_Assign_Generic(asIScriptGeneric *gen)
275 {
276 CScriptWeakRef *other = reinterpret_cast<CScriptWeakRef*>(gen->GetArgAddress(0));
277 CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
278 *self = *other;
279 gen->SetReturnAddress(self);
280 }
281
CScriptWeakRef_Assign2_Generic(asIScriptGeneric * gen)282 void CScriptWeakRef_Assign2_Generic(asIScriptGeneric *gen)
283 {
284 void *other = gen->GetArgAddress(0);
285 CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
286
287 // Must increase the refcount of the object, since Set() will decrease it
288 // If this is not done, the object will be destroyed too early since the
289 // generic interface also automatically decreases the refcount of received handles
290 gen->GetEngine()->AddRefScriptObject(other, self->GetRefType());
291
292 self->Set(other);
293 gen->SetReturnAddress(self);
294 }
295
CScriptWeakRef_Equals_Generic(asIScriptGeneric * gen)296 void CScriptWeakRef_Equals_Generic(asIScriptGeneric *gen)
297 {
298 CScriptWeakRef *other = reinterpret_cast<CScriptWeakRef*>(gen->GetArgAddress(0));
299 CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
300 gen->SetReturnByte(*self == *other);
301 }
302
CScriptWeakRef_Equals2_Generic(asIScriptGeneric * gen)303 void CScriptWeakRef_Equals2_Generic(asIScriptGeneric *gen)
304 {
305 void *other = gen->GetArgAddress(0);
306 CScriptWeakRef *self = reinterpret_cast<CScriptWeakRef*>(gen->GetObject());
307
308 // Must increase the refcount of the object, since Equals() will decrease it
309 // If this is not done, the object will be destroyed too early since the
310 // generic interface also automatically decreases the refcount of received handles
311 gen->GetEngine()->AddRefScriptObject(other, self->GetRefType());
312
313 gen->SetReturnByte(self->Equals(other));
314 }
315
ScriptWeakRefTemplateCallback_Generic(asIScriptGeneric * gen)316 static void ScriptWeakRefTemplateCallback_Generic(asIScriptGeneric *gen)
317 {
318 asITypeInfo *ti = *reinterpret_cast<asITypeInfo**>(gen->GetAddressOfArg(0));
319 bool *dontGarbageCollect = *reinterpret_cast<bool**>(gen->GetAddressOfArg(1));
320 *reinterpret_cast<bool*>(gen->GetAddressOfReturnLocation()) = ScriptWeakRefTemplateCallback(ti, *dontGarbageCollect);
321 }
322
RegisterScriptWeakRef_Generic(asIScriptEngine * engine)323 void RegisterScriptWeakRef_Generic(asIScriptEngine *engine)
324 {
325 int r;
326
327 // Register a type for non-const handles
328 r = engine->RegisterObjectType("weakref<class T>", sizeof(CScriptWeakRef), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_TEMPLATE | asOBJ_APP_CLASS_DAK); assert( r >= 0 );
329
330 r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptWeakRefConstruct_Generic), asCALL_GENERIC); assert( r>= 0 );
331 r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in, T@)", asFUNCTION(ScriptWeakRefConstruct2_Generic), asCALL_GENERIC); assert( r>= 0 );
332 r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptWeakRefTemplateCallback_Generic), asCALL_GENERIC); assert( r >= 0 );
333 r = engine->RegisterObjectBehaviour("weakref<T>", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptWeakRefDestruct_Generic), asCALL_GENERIC); assert( r >= 0 );
334
335 r = engine->RegisterObjectMethod("weakref<T>", "T@ opImplCast()", asFUNCTION(CScriptWeakRef_Get_Generic), asCALL_GENERIC); assert(r >= 0);
336 r = engine->RegisterObjectMethod("weakref<T>", "T@ get() const", asFUNCTION(CScriptWeakRef_Get_Generic), asCALL_GENERIC); assert( r >= 0 );
337 r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opHndlAssign(const weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
338 r = engine->RegisterObjectMethod("weakref<T>", "bool opEquals(const weakref<T> &in) const", asFUNCTION(CScriptWeakRef_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
339 r = engine->RegisterObjectMethod("weakref<T>", "weakref<T> &opHndlAssign(T@)", asFUNCTION(CScriptWeakRef_Assign2_Generic), asCALL_GENERIC); assert( r >= 0 );
340 r = engine->RegisterObjectMethod("weakref<T>", "bool opEquals(const T@) const", asFUNCTION(CScriptWeakRef_Equals2_Generic), asCALL_GENERIC); assert(r >= 0);
341
342 // Register another type for const handles
343 r = engine->RegisterObjectType("const_weakref<class T>", sizeof(CScriptWeakRef), asOBJ_VALUE | asOBJ_ASHANDLE | asOBJ_TEMPLATE | asOBJ_APP_CLASS_DAK); assert( r >= 0 );
344
345 r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in)", asFUNCTION(ScriptWeakRefConstruct_Generic), asCALL_GENERIC); assert( r>= 0 );
346 r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_CONSTRUCT, "void f(int&in, const T@)", asFUNCTION(ScriptWeakRefConstruct2_Generic), asCALL_GENERIC); assert( r>= 0 );
347 r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_TEMPLATE_CALLBACK, "bool f(int&in, bool&out)", asFUNCTION(ScriptWeakRefTemplateCallback_Generic), asCALL_GENERIC); assert( r >= 0 );
348 r = engine->RegisterObjectBehaviour("const_weakref<T>", asBEHAVE_DESTRUCT, "void f()", asFUNCTION(ScriptWeakRefDestruct_Generic), asCALL_GENERIC); assert( r >= 0 );
349
350 r = engine->RegisterObjectMethod("const_weakref<T>", "const T@ opImplCast() const", asFUNCTION(CScriptWeakRef_Get_Generic), asCALL_GENERIC); assert(r >= 0);
351 r = engine->RegisterObjectMethod("const_weakref<T>", "const T@ get() const", asFUNCTION(CScriptWeakRef_Get_Generic), asCALL_GENERIC); assert( r >= 0 );
352 r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const const_weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
353 r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const const_weakref<T> &in) const", asFUNCTION(CScriptWeakRef_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
354 r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const T@)", asFUNCTION(CScriptWeakRef_Assign2_Generic), asCALL_GENERIC); assert( r >= 0 );
355 r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const T@) const", asFUNCTION(CScriptWeakRef_Equals2_Generic), asCALL_GENERIC); assert(r >= 0);
356
357 // Allow non-const weak references to be converted to const weak references
358 r = engine->RegisterObjectMethod("const_weakref<T>", "const_weakref<T> &opHndlAssign(const weakref<T> &in)", asFUNCTION(CScriptWeakRef_Assign_Generic), asCALL_GENERIC); assert( r >= 0 );
359 r = engine->RegisterObjectMethod("const_weakref<T>", "bool opEquals(const weakref<T> &in) const", asFUNCTION(CScriptWeakRef_Equals_Generic), asCALL_GENERIC); assert( r >= 0 );
360 }
361
RegisterScriptWeakRef(asIScriptEngine * engine)362 void RegisterScriptWeakRef(asIScriptEngine *engine)
363 {
364 if( strstr(asGetLibraryOptions(), "AS_MAX_PORTABILITY") )
365 RegisterScriptWeakRef_Generic(engine);
366 else
367 RegisterScriptWeakRef_Native(engine);
368 }
369
370
371 END_AS_NAMESPACE
372