Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion test-app/app/src/main/assets/app/tests/byte-buffer-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,4 +80,31 @@ describe("Tests mapped ByteBuffer conversion", function () {
}
expect(exceptionCaught).toBe(true);
});
});
});

describe("Tests JNI references of ArrayBuffers passed to Java", function () {
// ART aborts the process once its JNI global reference table holds 51200
// entries. Each ArrayBuffer or typed array crossing to Java for the first
// time used to take a global reference that was never deleted, so these
// loops cross more distinct buffers than the table can hold.
var MORE_THAN_THE_GLOBAL_REF_TABLE = 60000;

it("should not keep a global reference per buffer passed as a method argument", function () {
var holder = new com.tns.tests.ByteBufferHolder();
for (var i = 0; i < MORE_THAN_THE_GLOBAL_REF_TABLE; i++) {
var buffer = i % 2 ? new ArrayBuffer(1) : new Uint8Array(1);
new Uint8Array(buffer.buffer || buffer)[0] = i & 0x7f;
holder.hold(buffer);
}
expect(holder.get(0)).toBe((MORE_THAN_THE_GLOBAL_REF_TABLE - 1) & 0x7f);
});

it("should not keep a global reference per buffer stored in a Java array", function () {
var array = Array.create(java.nio.ByteBuffer, 1);
for (var i = 0; i < MORE_THAN_THE_GLOBAL_REF_TABLE; i++) {
array[0] = new ArrayBuffer(1);
}
// reaching here without the runtime aborting is the real assertion
expect(array[0]).toBeTruthy();
});
});
7 changes: 5 additions & 2 deletions test-app/runtime/src/main/cpp/JsArgConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -331,8 +331,11 @@ bool JsArgConverter::ConvertArg(const Local<Value> &arg, int index) {
buffer = directBuffer;
}

buffer = env.NewGlobalRef(buffer);

// The buffer needs no global ref of its own: GetOrCreateObjectId keeps
// it strongly reachable from Java until the JS object is collected.
// One taken here would never be deleted, pinning the buffer (and, via
// JSObjectFinalizer, its ArrayBuffer) and filling the JNI global
// reference table.
int id = objectManager->GetOrCreateObjectId(buffer);
auto clazz = env.GetObjectClass(buffer);

Expand Down
7 changes: 5 additions & 2 deletions test-app/runtime/src/main/cpp/JsArgToArrayConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -334,8 +334,11 @@ bool JsArgToArrayConverter::ConvertArg(Local<Context> context, const Local<Value
buffer = directBuffer;
}

buffer = env.NewGlobalRef(buffer);

// The buffer needs no global ref of its own: GetOrCreateObjectId keeps
// it strongly reachable from Java until the JS object is collected.
// One taken here would never be deleted, pinning the buffer (and, via
// JSObjectFinalizer, its ArrayBuffer) and filling the JNI global
// reference table.
int id = objectManager->GetOrCreateObjectId(buffer);
auto clazz = env.GetObjectClass(buffer);
objectManager->Link(jsObj, id, clazz);
Expand Down