Skip to content
This repository has been archived by the owner on Apr 29, 2024. It is now read-only.

Commit

Permalink
Add JLazyReference and JFinalString (#400)
Browse files Browse the repository at this point in the history
  • Loading branch information
HosseinYousefi committed Sep 26, 2023
1 parent 6af8ebd commit ba09324
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 2 deletions.
31 changes: 31 additions & 0 deletions jni/lib/src/jfinal_string.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:ffi';

import 'jreference.dart';
import 'lang/jstring.dart';
import 'third_party/generated_bindings.dart';

/// Used for `static final` Java strings, where the constant string is
/// available.
///
/// If only its value is used using [toDartString], the [reference] is never
/// populated, saving a method call.
class JFinalString extends JString with JLazyReference {
@override
final JObjectPtr Function() lazyReference;

final String string;

JFinalString(this.lazyReference, this.string) : super.fromRef(nullptr);

@override
String toDartString({bool releaseOriginal = false}) {
if (releaseOriginal) {
release();
}
return string;
}
}
46 changes: 44 additions & 2 deletions jni/lib/src/jreference.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,9 @@ extension ProtectedJReference on JReference {
///
/// Detaches the finalizer so the underlying pointer will not be deleted.
JObjectPtr toPointer() {
final ref = reference;
setAsReleased();
return _reference;
return ref;
}
}

Expand All @@ -42,7 +43,9 @@ abstract class JReference implements Finalizable {
NativeFinalizer(Jni.env.ptr.ref.DeleteGlobalRef.cast());

JReference.fromRef(this._reference) {
_finalizer.attach(this, _reference, detach: this);
if (_reference != nullptr) {
_finalizer.attach(this, _reference, detach: this);
}
}

bool _released = false;
Expand Down Expand Up @@ -80,6 +83,45 @@ abstract class JReference implements Finalizable {
void releasedBy(Arena arena) => arena.onReleaseAll(release);
}

/// Creates a "lazy" [JReference].
///
/// The first use of [reference] will call [lazyReference].
///
/// This is useful when the Java object is not necessarily used directly, and
/// there are alternative ways to get a Dart representation of the Object.
///
/// Object mixed in with this must call their super.[fromRef] constructor
/// with [nullptr].
///
/// Also see [JFinalString].
mixin JLazyReference on JReference {
JObjectPtr? _lazyReference;

JObjectPtr Function() get lazyReference;

@override
JObjectPtr get reference {
if (_lazyReference == null) {
_lazyReference = lazyReference();
JReference._finalizer.attach(this, _lazyReference!, detach: this);
return _lazyReference!;
}
if (_released) {
throw UseAfterReleaseError();
}
return _lazyReference!;
}

@override
void release() {
setAsReleased();
if (_lazyReference == null) {
return;
}
Jni.env.DeleteGlobalRef(_lazyReference!);
}
}

extension JReferenceUseExtension<T extends JReference> on T {
/// Applies [callback] on [this] object and then delete the underlying JNI
/// reference, returning the result of [callback].
Expand Down
43 changes: 43 additions & 0 deletions jni/test/jfinal_string_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'dart:io';

import 'package:jni/jni.dart';
import 'package:jni/src/jfinal_string.dart';
import 'package:test/test.dart';

import 'test_util/test_util.dart';

void main() {
// Don't forget to initialize JNI.
if (!Platform.isAndroid) {
checkDylibIsUpToDate();
Jni.spawnIfNotExists(dylibDir: "build/jni_libs", jvmOptions: ["-Xmx128m"]);
}
run(testRunner: test);
}

void run({required TestRunnerCallback testRunner}) {
testRunner('JFinalString', () {
const string = 'abc';
var referenceFetchedCount = 0;
final finalString = JFinalString(
() {
++referenceFetchedCount;
return string.toJString().reference;
},
string,
);
expect(finalString.toDartString(), string);
expect(referenceFetchedCount, 0);
expect(finalString.reference, isNot(nullptr));
expect(referenceFetchedCount, 1);
finalString.reference;
expect(referenceFetchedCount, 1);
expect(finalString.toDartString(releaseOriginal: true), string);
expect(() => finalString.reference, throwsA(isA<UseAfterReleaseError>()));
expect(finalString.release, throwsA(isA<DoubleReleaseError>()));
});
}

0 comments on commit ba09324

Please sign in to comment.