Skip to content

Commit e9eaf52

Browse files
committed
jni: partially address bblfsh#113 by avoiding refs as map keys
Original impl used a reference as a key in a native map, but in JNI object references are neither constant nor unique, and thus were violating the contract on hashCode for a map key. This should allow to get rid of global reference in interface lookup, but there are 2 more (clearly identified) cases where we still rely on side-effects of having a glabal reference and thus leak some memory Signed-off-by: Alexander Bezzubov <[email protected]>
1 parent ead70a8 commit e9eaf52

File tree

1 file changed

+20
-4
lines changed

1 file changed

+20
-4
lines changed

src/main/native/org_bblfsh_client_v2_libuast_Libuast.cc

+20-4
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <cassert>
2+
#include <unordered_map>
23

34
#include "jni_utils.h"
45
#include "org_bblfsh_client_v2_Context.h"
@@ -363,6 +364,7 @@ class Node : public uast::Node<Node *> {
363364
JNIEnv *env = getJNIEnv();
364365
jobject val =
365366
ObjectMethod(env, "valueAt", METHOD_JNODE_VALUE_AT, CLS_JNODE, obj, i);
367+
// TODO(#113) investigate, it looks like a potential memory leak
366368
return lookupOrCreate(env->NewGlobalRef(val)); // new ref
367369
}
368370

@@ -399,11 +401,25 @@ class Node : public uast::Node<Node *> {
399401
}
400402
};
401403

404+
struct EqByObj {
405+
bool operator()(jobject a, jobject b) const {
406+
return getJNIEnv()->IsSameObject(a, b);
407+
}
408+
};
409+
410+
struct HashByObj {
411+
std::size_t operator()(jobject obj) const noexcept {
412+
auto hash = IntMethod(getJNIEnv(), "hashCode", "()I", CLS_OBJ, obj);
413+
checkJvmException("failed to call hashCode()");
414+
return hash;
415+
}
416+
};
417+
402418
class Context;
403419

404420
class Interface : public uast::NodeCreator<Node *> {
405421
private:
406-
std::map<jobject, Node *> obj2node;
422+
std::unordered_map<jobject, Node *, HashByObj, EqByObj> obj2node;
407423

408424
// lookupOrCreate either creates a new object or returns existing one.
409425
// In the second case it creates a new reference.
@@ -414,15 +430,15 @@ class Interface : public uast::NodeCreator<Node *> {
414430
if (node) return node;
415431

416432
node = new Node(this, obj);
417-
obj2node[obj] = node;
433+
obj2node[node->obj] = node;
418434
return node;
419435
}
420436

421437
// create makes a new object with a specified kind.
422438
// Steals the reference.
423439
Node *create(NodeKind kind, jobject obj) {
424440
Node *node = new Node(this, kind, obj);
425-
obj2node[obj] = node;
441+
obj2node[node->obj] = node;
426442
return node;
427443
}
428444

@@ -440,9 +456,9 @@ class Interface : public uast::NodeCreator<Node *> {
440456
}
441457

442458
// toJ returns a JVM object associated with a node.
443-
// Returns a new reference.
444459
jobject toJ(Node *node) {
445460
if (!node) return nullptr;
461+
// TODO(#113) investigate, it looks like a potential memory leak
446462
jobject obj = getJNIEnv()->NewGlobalRef(node->obj);
447463
return obj;
448464
}

0 commit comments

Comments
 (0)