Skip to content

Commit e533125

Browse files
committed
Merge pull request #423 from NativeScript/plamen5kov/object_manager_refactor
Plamen5kov/object manager refactor
2 parents 1d960bb + 7120d0c commit e533125

File tree

5 files changed

+76
-44
lines changed

5 files changed

+76
-44
lines changed

src/jni/CallbackHandlers.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
#include "v8.h"
88
#include "v8-debug.h"
99
#include "JEnv.h"
10-
#include "JSInstanceInfo.h"
1110
#include "ArgsWrapper.h"
1211
#include "MetadataEntry.h"
1312
#include "FieldCallbackData.h"

src/jni/DirectBuffer.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ bool DirectBuffer::Write(int value)
5252
if (canWrite)
5353
{
5454
int bigEndianInt = __builtin_bswap32(value);
55-
*m_pos++ = bigEndianInt;
55+
*(m_pos++) = bigEndianInt;
5656
}
5757
return canWrite;
5858
}

src/jni/JSInstanceInfo.h

Lines changed: 0 additions & 23 deletions
This file was deleted.

src/jni/ObjectManager.cpp

Lines changed: 59 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
125128
jweak 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

161164
int 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+
*/
216223
void 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+
* */
293306
void 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+
* */
373388
void 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+
* */
430450
void 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+
* */
618644
void 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+
* */
659691
void 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+
* */
685722
void 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+
* */
718759
void ObjectManager::CheckWeakObjectsAreAlive(const vector<PersistentObjectIdPair>& instances, DirectBuffer& inputBuff, DirectBuffer& outputBuff)
719760
{
720761
for (const auto& poIdPair : instances)

src/jni/ObjectManager.h

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
#include "JEnv.h"
66
#include "JniLocalRef.h"
77
#include "ArgsWrapper.h"
8-
#include "JSInstanceInfo.h"
98
#include "DirectBuffer.h"
109
#include "LRUCache.h"
1110
#include <map>
@@ -58,6 +57,20 @@ namespace tns
5857
};
5958

6059
private:
60+
61+
struct JSInstanceInfo
62+
{
63+
public:
64+
JSInstanceInfo(bool isJavaObjectWeak, uint32_t javaObjectID, jclass claz)
65+
:IsJavaObjectWeak(isJavaObjectWeak), JavaObjectID(javaObjectID), ObjectClazz(claz)
66+
{
67+
}
68+
69+
bool IsJavaObjectWeak;
70+
uint32_t JavaObjectID;
71+
jclass ObjectClazz;
72+
};
73+
6174
struct ObjectWeakCallbackState
6275
{
6376
ObjectWeakCallbackState(ObjectManager *_thisPtr, JSInstanceInfo *_jsInfo, v8::Persistent<v8::Object> *_target)
@@ -121,6 +134,8 @@ namespace tns
121134
int javaObjectId;
122135
};
123136

137+
bool IsJsRuntimeObject(const v8::Local<v8::Object>& object);
138+
124139
JSInstanceInfo* GetJSInstanceInfo(const v8::Local<v8::Object>& object);
125140

126141
void ReleaseJSInstance(v8::Persistent<v8::Object> *po, JSInstanceInfo *jsInstanceInfo);

0 commit comments

Comments
 (0)