blob: a8517f8ea3aa1f4cb1ae3877f88f07bfbb8cd727 [file] [edit]
// 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:meta/meta.dart' show internal;
import 'jni.dart' show Jni;
import 'third_party/generated_bindings.dart';
// TODO(#567): Add the fact that [JException] is now a [JObject] to the
// CHANGELOG.
mixin _ExplainsRelease on StateError {
/// The stack trace where the object was released, if captured.
///
/// Stack traces at release time can be captured by setting
/// [Jni.captureStackTraceOnRelease].
String? get releaseStackTrace;
@override
String toString() {
final sb = StringBuffer(super.toString());
if (releaseStackTrace != null) {
sb.write('\n');
sb.write(releaseStackTrace);
} else {
sb.writeln('\nTo see where the object was released, '
'set `Jni.captureStackTraceOnRelease = true`.');
}
return sb.toString();
}
}
/// Error thrown when an operation is performed on an object whose underlying
/// JNI reference has been released.
final class UseAfterReleaseError extends StateError with _ExplainsRelease {
/// The stack trace where the object was released, if captured.
///
/// Stack traces at release time can be captured by setting
/// [Jni.captureStackTraceOnRelease].
@override
final String? releaseStackTrace;
UseAfterReleaseError([this.releaseStackTrace])
: super('Use after release error');
}
// TODO(#567): Use NullPointerError once it's available.
/// Error thrown when an unexpected null reference is encountered.
final class JNullError extends StateError {
JNullError() : super('The reference was null');
}
/// Error thrown when a Java method or field cannot be found.
final class NoSuchMethodError extends StateError {
final String name;
NoSuchMethodError(this.name) : super('No such method or field: $name');
}
/// Error thrown when attempting to release an already-released JNI reference.
final class DoubleReleaseError extends StateError with _ExplainsRelease {
/// The stack trace where the object was first released, if captured.
///
/// Stack traces at release time can be captured by setting
/// [Jni.captureStackTraceOnRelease].
@override
final String? releaseStackTrace;
DoubleReleaseError([this.releaseStackTrace]) : super('Double release error');
}
/// Represents JNI errors that might be returned by methods like `CreateJavaVM`.
sealed class JniError extends Error {
static const _errors = {
JniErrorCode.ERR: JniGenericError.new,
JniErrorCode.EDETACHED: JniThreadDetachedError.new,
JniErrorCode.EVERSION: JniVersionError.new,
JniErrorCode.ENOMEM: JniOutOfMemoryError.new,
JniErrorCode.EEXIST: JniVmExistsError.new,
JniErrorCode.EINVAL: JniArgumentError.new,
};
final String message;
JniError(this.message);
@internal
factory JniError.of(JniErrorCode status) {
if (!_errors.containsKey(status)) {
status = JniErrorCode.ERR;
}
return _errors[status]!();
}
@override
String toString() {
return 'JniError: $message';
}
}
/// Error representing a generic JNI failure.
final class JniGenericError extends JniError {
JniGenericError() : super('Generic JNI error');
}
/// Error representing a thread detached from the Java VM.
final class JniThreadDetachedError extends JniError {
JniThreadDetachedError() : super('Thread detached from VM');
}
/// Error representing a JNI version error.
final class JniVersionError extends JniError {
JniVersionError() : super('JNI version error');
}
/// Error representing a JNI out of memory condition.
final class JniOutOfMemoryError extends JniError {
JniOutOfMemoryError() : super('Out of memory');
}
/// Error representing that a Java VM has already been created.
final class JniVmExistsError extends JniError {
JniVmExistsError() : super('VM Already created');
}
/// Error representing invalid arguments passed to a JNI function.
final class JniArgumentError extends JniError {
JniArgumentError() : super('Invalid arguments');
}
/// Error thrown when attempting an operation that requires a JVM instance,
/// but none is running.
final class NoJvmInstanceError extends Error {
@override
String toString() => 'No JNI instance is available';
}
/// Error thrown when the JNI helper shared library cannot be found.
final class HelperNotFoundError extends Error {
final String path;
HelperNotFoundError(this.path);
@override
String toString() => '''
Unable to locate the helper library.
Ensure that the helper library is available at the path: $path
Run `dart jni:setup` to generate the shared library if it does not exist.
Note: If the --build-path option is passed to jni:setup, Jni.spawn must be
called with same dylibDir. Also when creating new Dart isolates, Jni.setDylibDir
must be called.
''';
}
/// Error thrown when loading a dynamic library fails.
final class DynamicLibraryLoadError extends Error {
final String libraryPath;
DynamicLibraryLoadError(this.libraryPath);
@override
String toString() {
return '''
Failed to load dynamic library at path: $libraryPath
The library was found at the specified path, but it could not be loaded.
This might be due to missing dependencies or incorrect file permissions.
Please ensure ${Platform.isWindows ? r'that `\bin\server\jvm.dll` is in the PATH, and ' : ''}that the file has the correct permissions.
''';
}
}
/// Exception thrown when converting a Dart string to a Java string fails.
final class JniNewStringException implements Exception {
final String string;
JniNewStringException(this.string);
@override
String toString() => 'Failed to convert string to a JString: $string';
}