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

Commit

Permalink
Convert incorrect exceptions into errors (#397)
Browse files Browse the repository at this point in the history
Closes #394.
  • Loading branch information
HosseinYousefi committed Sep 19, 2023
1 parent 4443ceb commit 292f896
Show file tree
Hide file tree
Showing 12 changed files with 244 additions and 176 deletions.
17 changes: 17 additions & 0 deletions jni/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
## 0.8.0-wip

- **Breaking Change** ([#394](https://github.com/dart-lang/jnigen/issues/394)):
Converted various `Exception`s into `Error`s:
- `UseAfterReleaseException` -> `UseAfterReleaseError`
- `DoubleReleaseException` -> `DoubleReleaseError`
- `SpawnException` -> `JniError` (It's now a `sealed class`)
- `JNullException` -> `JNullError`
- `InvalidCallTypeException` -> `InvalidCallTypeError`
- `HelperNotFoundException` -> `HelperNotFoundError`
- `JvmExistsException` -> `JniVmExistsError`
- `NoJvmInstanceException` -> `NoJvmInstanceError`
- **Breaking Change**: Removed `InvalidJStringException`.
- **Breaking Change**: The default return `callType` of type parameter `int` for
methods such as `JObject.callMethodByName<int>` is now Java's `long` instead
of `int` to be consistent with the way arguments work.

## 0.7.0

- **Breaking Change** ([#387](https://github.com/dart-lang/jnigen/issues/387)):
Expand Down
8 changes: 5 additions & 3 deletions jni/lib/jni.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,19 +60,21 @@
/// This library provides classes and functions for JNI interop from Dart.
library jni;

export 'src/third_party/generated_bindings.dart'
hide JniBindings, JniEnv, JniEnv1, JniExceptionDetails;
export 'src/errors.dart';
export 'src/jni.dart' hide ProtectedJniExtensions;
export 'src/jvalues.dart' hide JValueArgs, toJValues;
export 'src/types.dart';
export 'src/jarray.dart';
export 'src/jexceptions.dart';
export 'src/jobject.dart';
export 'src/jprimitives.dart';
export 'src/jreference.dart' show JReferenceUseExtension;

export 'src/lang/lang.dart';
export 'src/nio/nio.dart';
export 'src/util/util.dart';

export 'src/third_party/generated_bindings.dart'
hide JniBindings, JniEnv, JniEnv1, JniExceptionDetails;

export 'package:ffi/ffi.dart' show using, Arena;
export 'dart:ffi' show nullptr;
2 changes: 1 addition & 1 deletion jni/lib/src/accessors.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import 'package:ffi/ffi.dart' show using;

import 'package:jni/src/jvalues.dart';

import 'jexceptions.dart';
import 'errors.dart';
import 'third_party/generated_bindings.dart';
import 'jni.dart';

Expand Down
144 changes: 144 additions & 0 deletions jni/lib/src/errors.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
// 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 'package:jni/src/third_party/generated_bindings.dart';

// TODO(#393): Add the fact that [JException] is now a [JObject] to the
// CHANGELOG.

final class UseAfterReleaseError extends Error {
@override
String toString() {
return 'Use after release error';
}
}

// TODO(#393): Use NullPointerError once it's available.
final class JNullError extends Error {
@override
String toString() => 'The reference was null';
}

final class DoubleReleaseError extends Error {
@override
String toString() {
return 'Double release error';
}
}

/// Represents JNI errors that might be returned by methods like
/// `JNI_CreateJavaVM`.
sealed class JniError extends Error {
static const _errors = {
JniErrorCode.JNI_ERR: JniGenericError.new,
JniErrorCode.JNI_EDETACHED: JniThreadDetachedError.new,
JniErrorCode.JNI_EVERSION: JniVersionError.new,
JniErrorCode.JNI_ENOMEM: JniOutOfMemoryError.new,
JniErrorCode.JNI_EEXIST: JniVmExistsError.new,
JniErrorCode.JNI_EINVAL: JniArgumentError.new,
};

final String message;

JniError(this.message);

factory JniError.of(int status) {
if (!_errors.containsKey(status)) {
status = JniErrorCode.JNI_ERR;
}
return _errors[status]!();
}

@override
String toString() {
return 'JniError: $message';
}
}

final class JniGenericError extends JniError {
JniGenericError() : super('Generic JNI error');
}

final class JniThreadDetachedError extends JniError {
JniThreadDetachedError() : super('Thread detached from VM');
}

final class JniVersionError extends JniError {
JniVersionError() : super('JNI version error');
}

final class JniOutOfMemoryError extends JniError {
JniOutOfMemoryError() : super('Out of memory');
}

final class JniVmExistsError extends JniError {
JniVmExistsError() : super('VM Already created');
}

final class JniArgumentError extends JniError {
JniArgumentError() : super('Invalid arguments');
}

final class NoJvmInstanceError extends Error {
@override
String toString() => 'No JNI instance is available';
}

// TODO(#395): Remove this when calltypes are removed.
extension on int {
static const _names = {
JniCallType.booleanType: 'bool',
JniCallType.byteType: 'byte',
JniCallType.shortType: 'short',
JniCallType.charType: 'char',
JniCallType.intType: 'int',
JniCallType.longType: 'long',
JniCallType.floatType: 'float',
JniCallType.doubleType: 'double',
JniCallType.objectType: 'object',
JniCallType.voidType: 'void',
};
String str() => _names[this]!;
}

// TODO(#395): Remove this when `JniCallType`s are removed.
final class InvalidCallTypeError extends Error {
final int type;
final Set<int> allowed;

InvalidCallTypeError(this.type, this.allowed);

@override
String toString() => 'Invalid type for call ${type.str()}. '
'Allowed types are ${allowed.map((t) => t.str()).toSet()}';
}

// TODO(#393): Remove this class in favor of `JThrowable`.
class JniException implements Exception {
/// Error message from Java exception.
final String message;

/// Stack trace from Java.
final String stackTrace;

JniException(this.message, this.stackTrace);

@override
String toString() => 'Exception in Java code called through JNI: '
'$message\n\n$stackTrace\n';
}

final class HelperNotFoundError extends Error {
final String path;

HelperNotFoundError(this.path);

@override
String toString() => '''
Lookup for helper library $path failed.
Please ensure that `dartjni` shared library is built.
Provided jni:setup script can be used to build the shared library.
If the library is already built, ensure that the JVM libraries can be
loaded from Dart.''';
}
123 changes: 0 additions & 123 deletions jni/lib/src/jexceptions.dart

This file was deleted.

20 changes: 10 additions & 10 deletions jni/lib/src/jni.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import 'dart:isolate';
import 'package:ffi/ffi.dart';
import 'package:path/path.dart';

import 'jexceptions.dart';
import 'errors.dart';
import 'jobject.dart';
import 'third_party/generated_bindings.dart';
import 'jvalues.dart';
Expand Down Expand Up @@ -38,7 +38,7 @@ DynamicLibrary _loadDartJniLibrary({String? dir, String baseName = "dartjni"}) {
final dylib = DynamicLibrary.open(libPath);
return dylib;
} on Error {
throw HelperNotFoundException(libPath);
throw HelperNotFoundError(libPath);
}
}

Expand Down Expand Up @@ -97,12 +97,12 @@ abstract class Jni {
jniVersion: jniVersion,
);
if (status == false) {
throw JvmExistsException();
throw JniVmExistsError();
}
}

/// Same as [spawn] but if a JVM exists, returns silently instead of
/// throwing [JvmExistsException].
/// throwing [JvmExistsError].
///
/// If the options are different than that of existing VM, the existing VM's
/// options will remain in effect.
Expand All @@ -129,7 +129,7 @@ abstract class Jni {
} else if (status == DART_JNI_SINGLETON_EXISTS) {
return false;
} else {
throw SpawnException.of(status);
throw JniError.of(status);
}
});

Expand Down Expand Up @@ -176,12 +176,12 @@ abstract class Jni {
return _bindings.GetJavaVM();
}

/// Returns the instance of [GlobalJniEnvStruct], which is an abstraction over JNIEnv
/// without the same-thread restriction.
/// Returns the instance of [GlobalJniEnvStruct], which is an abstraction over
/// JNIEnv without the same-thread restriction.
static Pointer<GlobalJniEnvStruct> _fetchGlobalEnv() {
final env = _bindings.GetGlobalEnv();
if (env == nullptr) {
throw NoJvmInstanceException();
throw NoJvmInstanceError();
}
return env;
}
Expand Down Expand Up @@ -336,11 +336,11 @@ extension AdditionalEnvMethods on GlobalJniEnv {
/// DeleteGlobalRef.
String toDartString(JStringPtr jstringPtr, {bool releaseOriginal = false}) {
if (jstringPtr == nullptr) {
throw const JNullException();
throw JNullError();
}
final chars = GetStringChars(jstringPtr, nullptr);
if (chars == nullptr) {
throw InvalidJStringException(jstringPtr);
throw ArgumentError('Not a valid jstring pointer.');
}
final result = chars.cast<Utf16>().toDartString();
ReleaseStringChars(jstringPtr, chars);
Expand Down
Loading

0 comments on commit 292f896

Please sign in to comment.