@@ -82,19 +82,17 @@ JniLocalRef ObjectManager::GetJavaObjectByJsObject(const Local<Object>& object)
8282 return JniLocalRef ();
8383}
8484
85- JSInstanceInfo* ObjectManager::GetJSInstanceInfo (const Local<Object>& object)
85+ ObjectManager:: JSInstanceInfo* ObjectManager::GetJSInstanceInfo (const Local<Object>& object)
8686{
8787 DEBUG_WRITE (" ObjectManager::GetJSInstanceInfo: called" );
8888 JSInstanceInfo *jsInstanceInfo = nullptr ;
8989
9090 auto isolate = Isolate::GetCurrent ();
9191 HandleScope handleScope (isolate);
9292
93- int internalFieldCound = NativeScriptExtension::GetInternalFieldCount (object);
94- const int count = static_cast <int >(MetadataNodeKeys::END);
95- const int jsInfoIdx = static_cast <int >(MetadataNodeKeys::JsInfo);
96- if (internalFieldCound == count)
93+ if (IsJsRuntimeObject (object))
9794 {
95+ const int jsInfoIdx = static_cast <int >(MetadataNodeKeys::JsInfo);
9896 auto jsInfo = object->GetInternalField (jsInfoIdx);
9997 if (jsInfo->IsUndefined ())
10098 {
@@ -104,8 +102,7 @@ JSInstanceInfo* ObjectManager::GetJSInstanceInfo(const Local<Object>& object)
104102 if (!prototypeObject.IsEmpty () && prototypeObject->IsObject ())
105103 {
106104 DEBUG_WRITE (" GetJSInstanceInfo: need to check prototype :%d" , prototypeObject->GetIdentityHash ());
107- internalFieldCound = NativeScriptExtension::GetInternalFieldCount (prototypeObject);
108- if (internalFieldCound == count)
105+ if (IsJsRuntimeObject (prototypeObject))
109106 {
110107 jsInfo = prototypeObject->GetInternalField (jsInfoIdx);
111108 }
@@ -122,6 +119,12 @@ JSInstanceInfo* ObjectManager::GetJSInstanceInfo(const Local<Object>& object)
122119 return jsInstanceInfo;
123120}
124121
122+ bool ObjectManager::IsJsRuntimeObject (const v8::Local<v8::Object>& object) {
123+ int internalFieldCount = NativeScriptExtension::GetInternalFieldCount (object);
124+ const int count = static_cast <int >(MetadataNodeKeys::END);
125+ return internalFieldCount == count;
126+ }
127+
125128jweak ObjectManager::GetJavaObjectByID (uint32_t javaObjectID)
126129{
127130 jweak obj = m_cache (javaObjectID);
@@ -145,7 +148,7 @@ jclass ObjectManager::GetJavaClass(const Local<Object>& instance)
145148 DEBUG_WRITE (" GetClass called" );
146149
147150 JSInstanceInfo *jsInfo = GetJSInstanceInfo (instance);
148- jclass clazz = jsInfo->clazz ;
151+ jclass clazz = jsInfo->ObjectClazz ;
149152
150153 return clazz;
151154}
@@ -155,7 +158,7 @@ void ObjectManager::SetJavaClass(const Local<Object>& instance, jclass clazz)
155158 DEBUG_WRITE (" SetClass called" );
156159
157160 JSInstanceInfo *jsInfo = GetJSInstanceInfo (instance);
158- jsInfo->clazz = clazz;
161+ jsInfo->ObjectClazz = clazz;
159162}
160163
161164int ObjectManager::GetOrCreateObjectId (jobject object)
@@ -213,11 +216,13 @@ Local<Object> ObjectManager::CreateJSWrapperHelper(jint javaObjectID, const stri
213216 return jsWrapper;
214217}
215218
219+
220+ /* *
221+ * Link the JavaScript object and it's java counterpart with an ID
222+ */
216223void ObjectManager::Link (const Local<Object>& object, uint32_t javaObjectID, jclass clazz)
217224{
218- int internalFieldCound = NativeScriptExtension::GetInternalFieldCount (object);
219- const int count = static_cast <int >(MetadataNodeKeys::END);
220- if (internalFieldCound != count)
225+ if (!IsJsRuntimeObject (object))
221226 {
222227 string errMsg (" Trying to link invalid 'this' to a Java object" );
223228 throw NativeScriptException (errMsg);
@@ -227,19 +232,19 @@ void ObjectManager::Link(const Local<Object>& object, uint32_t javaObjectID, jcl
227232
228233 DEBUG_WRITE (" Linking js object: %d and java instance id: %d" , object->GetIdentityHash (), javaObjectID);
229234
230- auto jsInstanceInfo = new JSInstanceInfo ();
231- jsInstanceInfo->JavaObjectID = javaObjectID;
232- jsInstanceInfo->clazz = clazz;
235+ auto jsInstanceInfo = new JSInstanceInfo (false /* isJavaObjWeak*/ , javaObjectID, clazz);
233236
234237 auto objectHandle = new Persistent<Object>(isolate, object);
235238 auto state = new ObjectWeakCallbackState (this , jsInstanceInfo, objectHandle);
239+
240+ // subscribe for JS GC event
236241 objectHandle->SetWeak (state, JSObjectWeakCallbackStatic);
237242
238243 auto jsInfoIdx = static_cast <int >(MetadataNodeKeys::JsInfo);
239- bool alreadyLinked = !object->GetInternalField (jsInfoIdx)->IsUndefined ();
240- // TODO: fail if alreadyLinked is true?
241244
242245 auto jsInfo = External::New (isolate, jsInstanceInfo);
246+
247+ // link
243248 object->SetInternalField (jsInfoIdx, jsInfo);
244249
245250 idToObject.insert (make_pair (javaObjectID, objectHandle));
@@ -290,6 +295,14 @@ void ObjectManager::JSObjectWeakCallbackStatic(const WeakCallbackData<Object, Ob
290295 thisPtr->JSObjectWeakCallback (isolate, callbackState);
291296}
292297
298+ /*
299+ * When JS GC happens change state of the java counterpart to mirror state of JS object and REVIVE the JS object unconditionally
300+ * "Regular" js objects are pushed into the "regular objects" array
301+ * "Callback" js objects (ones that have implementation object) are pushed into two "special objects" array:
302+ * -ones called for the first time which are originally strong in java
303+ * -ones called for the second or next time which are already weak in java too
304+ * These objects are categorized by "regular" and "callback" and saved in different arrays for performance optimizations during GC
305+ * */
293306void ObjectManager::JSObjectWeakCallback (Isolate *isolate, ObjectWeakCallbackState *callbackState)
294307{
295308 DEBUG_WRITE (" JSObjectWeakCallback called" );
@@ -305,7 +318,6 @@ void ObjectManager::JSObjectWeakCallback(Isolate *isolate, ObjectWeakCallbackSta
305318 m_visitedPOs.insert (po);
306319
307320 auto obj = Local<Object>::New (isolate, *po);
308-
309321 JSInstanceInfo *jsInstanceInfo = GetJSInstanceInfo (obj);
310322 int javaObjectID = jsInstanceInfo->JavaObjectID ;
311323
@@ -370,6 +382,9 @@ void ObjectManager::ReleaseJSInstance(Persistent<Object> *po, JSInstanceInfo *js
370382 DEBUG_WRITE (" ReleaseJSObject instance disposed. id:%d" , javaObjectID);
371383}
372384
385+ /*
386+ * The "regular" JS objects added on ObjectManager::JSObjectWeakCallback are dealt with(released) here.
387+ * */
373388void ObjectManager::ReleaseRegularObjects ()
374389{
375390 Isolate *isolate = Isolate::GetCurrent ();
@@ -399,6 +414,7 @@ void ObjectManager::ReleaseRegularObjects()
399414 {
400415 int objGcNum = gcNum->Int32Value ();
401416
417+ // done so we can release only java objects from this GC stack and pass all objects that will be released in parent GC stacks
402418 isReachableFromImplementationObject = objGcNum >= numberOfGC;
403419 }
404420
@@ -427,6 +443,10 @@ bool ObjectManager::HasImplObject(Isolate *isolate, const Local<Object>& obj)
427443 return hasImplObj;
428444}
429445
446+ /*
447+ * When "MarkReachableObjects" is called V8 has marked all JS objects that can be released.
448+ * This method builds on top of V8s marking phase, because we need to consider the JAVA counterpart objects (is it "regular" or "callback"), when marking JS ones.
449+ * */
430450void ObjectManager::MarkReachableObjects (Isolate *isolate, const Local<Object>& obj)
431451{
432452 stack<Local<Value> > s;
@@ -461,6 +481,9 @@ void ObjectManager::MarkReachableObjects(Isolate *isolate, const Local<Object>&
461481 auto hasImplObject = HasImplObject (isolate, o);
462482 if (hasImplObject)
463483 {
484+ // this is a special case when one "callback1" object (one we are currently traversing)
485+ // can reach another "callback2" object (jsInfo->JavaObjectID)
486+ // here we are leaving "callback2" object to remain strong in java
464487 m_implObjStrong[jsInfo->JavaObjectID ] = nullptr ;
465488 }
466489 o->SetHiddenValue (propName, curGCNumValue);
@@ -615,10 +638,14 @@ void ObjectManager::OnGcStarted(GCType type, GCCallbackFlags flags)
615638 m_markedForGC.push (gcInfo);
616639}
617640
641+ /*
642+ * When GC is called we need to evaluate the situation and decide what js objects to release
643+ * */
618644void ObjectManager::OnGcFinished (GCType type, GCCallbackFlags flags)
619645{
620646 assert (!m_markedForGC.empty ());
621647
648+ // deal with all "callback" objects
622649 auto isolate = Isolate::GetCurrent ();
623650 for (auto weakObj : m_implObjWeak)
624651 {
@@ -635,6 +662,7 @@ void ObjectManager::OnGcFinished(GCType type, GCCallbackFlags flags)
635662 }
636663 }
637664
665+ // deal with regular objects
638666 ReleaseRegularObjects ();
639667
640668 m_markedForGC.pop ();
@@ -656,6 +684,10 @@ void ObjectManager::OnGcFinished(GCType type, GCCallbackFlags flags)
656684 }
657685}
658686
687+ /*
688+ * We have all the JS "regular" objects that JS has made weak and ready to by GC'd,
689+ * so we tell java to take the JAVA objects out of strong reference so they can be collected by JAVA GC
690+ * */
659691void ObjectManager::MakeRegularObjectsWeak (const set<int >& instances, DirectBuffer& inputBuff)
660692{
661693 jboolean keepAsWeak = JNI_FALSE;
@@ -682,6 +714,11 @@ void ObjectManager::MakeRegularObjectsWeak(const set<int>& instances, DirectBuff
682714 inputBuff.Reset ();
683715}
684716
717+ /*
718+ * We have all the JS "callback" objects that JS has made weak and ready to by GC'd,
719+ * so we tell java to take the JAVA objects out of strong, BUT KEEP THEM AS WEEK REFERENCES,
720+ * so that if java needs to release them, it can, on a later stage.
721+ * */
685722void ObjectManager::MakeImplObjectsWeak (const map<int , Persistent<Object>*>& instances, DirectBuffer& inputBuff)
686723{
687724 jboolean keepAsWeak = JNI_TRUE;
@@ -715,6 +752,10 @@ void ObjectManager::MakeImplObjectsWeak(const map<int, Persistent<Object>*>& ins
715752 inputBuff.Reset ();
716753}
717754
755+ /*
756+ * Consult with JAVA world to check if a java object is still in kept as a strong or weak reference
757+ * If the JAVA objects are released, we can release the their counterpart JS objects
758+ * */
718759void ObjectManager::CheckWeakObjectsAreAlive (const vector<PersistentObjectIdPair>& instances, DirectBuffer& inputBuff, DirectBuffer& outputBuff)
719760{
720761 for (const auto & poIdPair : instances)
0 commit comments