Skip to content
This repository was archived by the owner on Mar 8, 2020. It is now read-only.

Commit b754be3

Browse files
bzzncordon
authored andcommitted
jni: partially address #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 5b0972d commit b754be3

File tree

1 file changed

+24
-5
lines changed

1 file changed

+24
-5
lines changed

src/main/native/org_bblfsh_client_v2_libuast_Libuast.cc

+24-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#include <cassert>
2-
#include <map>
2+
#include <unordered_map>
33

44
#include "jni_utils.h"
55
#include "org_bblfsh_client_v2_Context.h"
@@ -364,6 +364,7 @@ class Node : public uast::Node<Node *> {
364364
JNIEnv *env = getJNIEnv();
365365
jobject val =
366366
ObjectMethod(env, "valueAt", METHOD_JNODE_VALUE_AT, CLS_JNODE, obj, i);
367+
// TODO(#113) investigate, it looks like a potential memory leak
367368
return lookupOrCreate(env->NewGlobalRef(val)); // new ref
368369
}
369370

@@ -400,11 +401,29 @@ class Node : public uast::Node<Node *> {
400401
}
401402
};
402403

404+
// Custom comparator for keys in std::map<object>.
405+
// Compares actual objects instead of JNI references.
406+
struct EqualByObj {
407+
bool operator()(jobject a, jobject b) const {
408+
return getJNIEnv()->IsSameObject(a, b);
409+
}
410+
};
411+
412+
// Custom hasing function for keys in std::map<object>.
413+
// Deligates actual hasing to the managed .hashCode() impl.
414+
struct HashByObj {
415+
std::size_t operator()(jobject obj) const noexcept {
416+
auto hash = IntMethod(getJNIEnv(), "hashCode", "()I", CLS_OBJ, obj);
417+
checkJvmException("failed to call hashCode()");
418+
return hash;
419+
}
420+
};
421+
403422
class Context;
404423

405424
class Interface : public uast::NodeCreator<Node *> {
406425
private:
407-
std::map<jobject, Node *> obj2node;
426+
std::unordered_map<jobject, Node *, HashByObj, EqualByObj> obj2node;
408427

409428
// lookupOrCreate either creates a new object or returns existing one.
410429
// In the second case it creates a new reference.
@@ -415,15 +434,15 @@ class Interface : public uast::NodeCreator<Node *> {
415434
if (node) return node;
416435

417436
node = new Node(this, obj);
418-
obj2node[obj] = node;
437+
obj2node[node->obj] = node;
419438
return node;
420439
}
421440

422441
// create makes a new object with a specified kind.
423442
// Steals the reference.
424443
Node *create(NodeKind kind, jobject obj) {
425444
Node *node = new Node(this, kind, obj);
426-
obj2node[obj] = node;
445+
obj2node[node->obj] = node;
427446
return node;
428447
}
429448

@@ -441,9 +460,9 @@ class Interface : public uast::NodeCreator<Node *> {
441460
}
442461

443462
// toJ returns a JVM object associated with a node.
444-
// Returns a new reference.
445463
jobject toJ(Node *node) {
446464
if (!node) return nullptr;
465+
// TODO(#113) investigate, it looks like a potential memory leak
447466
jobject obj = getJNIEnv()->NewGlobalRef(node->obj);
448467
return obj;
449468
}

0 commit comments

Comments
 (0)