Skip to content

Commit

Permalink
Merge branch 'main' into ffigenast
Browse files Browse the repository at this point in the history
  • Loading branch information
liamappelbe committed Oct 16, 2024
2 parents b00886b + c638d9f commit 55b17e3
Show file tree
Hide file tree
Showing 32 changed files with 2,003 additions and 31 deletions.
1 change: 1 addition & 0 deletions pkgs/ffigen/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

- Bump minimum Dart version to 3.4.
- Dedupe `ObjCBlock` trampolines to reduce generated ObjC code.
- Update to latest `package:objective_c`.
- ObjC objects now include the methods from the protocols they implement. Both
required and optional methods are included. Optional methods will throw an
exception if the method isn't implemented.
Expand Down
14 changes: 14 additions & 0 deletions pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ class ObjCBuiltInFunctions {
ObjCImport('UnimplementedOptionalMethodException');

// Keep in sync with pkgs/objective_c/ffigen_objc.yaml.

@visibleForTesting
static const builtInInterfaces = {
'DartInputStreamAdapter',
'DartProxy',
'DartProxyBuilder',
'NSArray',
Expand All @@ -58,6 +60,7 @@ class ObjCBuiltInFunctions {
'NSEnumerator',
'NSError',
'NSIndexSet',
'NSInputStream',
'NSInvocation',
'NSItemProvider',
'NSLocale',
Expand All @@ -73,8 +76,11 @@ class ObjCBuiltInFunctions {
'NSNumber',
'NSObject',
'NSOrderedSet',
'NSOutputStream',
'NSProxy',
'NSRunLoop',
'NSSet',
'NSStream',
'NSString',
'NSURL',
'NSURLHandle',
Expand Down Expand Up @@ -104,13 +110,19 @@ class ObjCBuiltInFunctions {
'NSKeyValueSetMutationKind',
'NSOrderedCollectionDifferenceCalculationOptions',
'NSSortOptions',
'NSStreamEvent',
'NSStreamStatus',
'NSStringCompareOptions',
'NSStringEncodingConversionOptions',
'NSStringEnumerationOptions',
'NSURLBookmarkCreationOptions',
'NSURLBookmarkResolutionOptions',
'NSURLHandleStatus',
};
@visibleForTesting
static const builtInProtocols = {
'NSStreamDelegate',
};

// TODO(https://github.com/dart-lang/native/issues/1173): Ideally this check
// would be based on more than just the name.
Expand All @@ -120,6 +132,8 @@ class ObjCBuiltInFunctions {
generateForPackageObjectiveC ? null : builtInCompounds[name];
bool isBuiltInEnum(String name) =>
!generateForPackageObjectiveC && builtInEnums.contains(name);
bool isBuiltInProtocol(String name) =>
!generateForPackageObjectiveC && builtInProtocols.contains(name);
static bool isNSObject(String name) => name == 'NSObject';

// We need to load a separate instance of objc_msgSend for each signature. If
Expand Down
3 changes: 2 additions & 1 deletion pkgs/ffigen/lib/src/code_generator/objc_protocol.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ class ObjCProtocol extends NoLookUpBinding with ObjCMethods {

@override
void sort() => sortMethods();
bool get _isBuiltIn => builtInFunctions.isBuiltInProtocol(originalName);

@override
BindingString toBindingString(Writer w) {
if (!generateBindings) {
if (!generateBindings || _isBuiltIn) {
return const BindingString(
type: BindingStringType.objcProtocol, string: '');
}
Expand Down
2 changes: 1 addition & 1 deletion pkgs/ffigen/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ dev_dependencies:
dart_flutter_team_lints: ^2.0.0
json_schema: ^5.1.1
leak_tracker: ^10.0.7
objective_c: ^2.1.0
objective_c: ^3.0.0
test: ^1.16.2

dependency_overrides:
Expand Down
6 changes: 6 additions & 0 deletions pkgs/jni/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.12.1-wip

- Add `JniUtils.fromReferenceAddress` which helps with sending `JObject`s
through method channels. You can send the address of the pointer as `long` and
reconstruct the class using the helper method.

## 0.12.0

- **Breaking Change**: Renamed `castTo` to `as`.
Expand Down
13 changes: 13 additions & 0 deletions pkgs/jni/java/src/main/java/com/github/dart_lang/jni/JniUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// Copyright (c) 2024, 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.

package com.github.dart_lang.jni;

public class JniUtils {
static {
System.loadLibrary("dartjni");
}

public static native Object fromReferenceAddress(long address);
}
2 changes: 1 addition & 1 deletion pkgs/jni/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

name: jni
description: A library to access JNI from Dart and Flutter that acts as a support library for package:jnigen.
version: 0.12.0
version: 0.12.1-wip
repository: https://github.com/dart-lang/native/tree/main/pkgs/jni

topics:
Expand Down
6 changes: 6 additions & 0 deletions pkgs/jnigen/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 0.12.2-wip

- Now excludes invalid identifiers by default.
- Fixed a bug where if multiple jars have classes within the same package, only
one of them gets generated.

## 0.12.1

- Support implementing generic functions in interfaces.
Expand Down
8 changes: 4 additions & 4 deletions pkgs/jnigen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Three configuration details are needed to generate the bindings. Everything else

* _Classes_: Specify which classes or packages you need bindings for. Specifying a package includes all classes inside it recursively.

Check out the [examples](jnigen/example/) to see some sample configurations.
Check out the [examples](example/) to see some sample configurations.

## Example
It's possible to generate bindings for JAR libraries, or Java source files.
Expand Down Expand Up @@ -113,7 +113,7 @@ classes:
- 'com.example.in_app_java.AndroidUtils'
```
The complete example can be found in [jnigen/example/in_app_java](jnigen/example/in_app_java), which adds few more classes to demonstrate using classes from gradle JAR and source dependencies.
The complete example can be found in [jnigen/example/in_app_java](example/in_app_java), which adds few more classes to demonstrate using classes from gradle JAR and source dependencies.
## Supported platforms
| Platform | Dart Standalone | Flutter |
Expand Down Expand Up @@ -167,7 +167,7 @@ CMake and a standard C toolchain are required to build `package:jni`.
#### I am getting ClassNotFoundError at runtime.
`jnigen` does not handle getting the classes into application. It has to be done by target-specific mechanism. Such as adding a gradle dependency on Android, or manually providing classpath to `Jni.spawn` on desktop / standalone targets.

On Android, `proguard` prunes classes which it deems inaccessible. Since JNI class lookup happens in runtime, this leads to ClassNotFound errors in release mode even if the dependency is included in gradle. [in_app_java example](jnigen/example/in_app_java/) discusses two mechanisms to prevent this: using `Keep` annotation (`androidx.annotation.Keep`) for the code written in the application itself, and [proguard-rules file](jnigen/example/in_app_java/android/app/proguard-rules.pro) for external libraries.
On Android, `proguard` prunes classes which it deems inaccessible. Since JNI class lookup happens in runtime, this leads to ClassNotFound errors in release mode even if the dependency is included in gradle. [in_app_java example](example/in_app_java/) discusses two mechanisms to prevent this: using `Keep` annotation (`androidx.annotation.Keep`) for the code written in the application itself, and [proguard-rules file](example/in_app_java/android/app/proguard-rules.pro) for external libraries.

Lastly, some libraries such as `java.awt` do not exist in android. Attempting to use libraries which depend on them can also lead to ClassNotFound errors.

Expand Down Expand Up @@ -222,7 +222,7 @@ A `*` denotes required configuration.
| `log_level` | Logging level | Configure logging level. Defaults to `info`. |
| `android_sdk_config:` | (Subsection) | Configuration for autodetection of Android dependencies and SDK. Note that this is more experimental than others, and very likely subject to change. |
| `android_sdk_config:` >> `add_gradle_deps` | Boolean | If true, run a gradle stub during `jnigen` invocation, and add Android compile classpath to the classpath of jnigen. This requires a release build to have happened before, so that all dependencies are cached appropriately. |
| `android_sdk_config:` >> `android_example` | Directory path | In case of an Android plugin project, the plugin itself cannot be built and `add_gradle_deps` is not directly feasible. This property can be set to relative path of package example app (usually `example/` so that gradle dependencies can be collected by running a stub in this directory. See [notification_plugin example](jnigen/example/notification_plugin/jnigen.yaml) for an example. |
| `android_sdk_config:` >> `android_example` | Directory path | In case of an Android plugin project, the plugin itself cannot be built and `add_gradle_deps` is not directly feasible. This property can be set to relative path of package example app (usually `example/` so that gradle dependencies can be collected by running a stub in this directory. See [notification_plugin example](example/notification_plugin/jnigen.yaml) for an example. |
| `summarizer:` | (Subsection) | Configuration specific to summarizer component, which builds API descriptions from Java sources or JAR files. |
| `summarizer:` >> `backend` | `auto`, `doclet` or `asm` | Specifies the backend to use in API summary generation. `doclet` uses OpenJDK Doclet API to build summary from sources. `asm` uses ASM library to build summary from classes in `class_path` JARs. `auto` attempts to find the class in sources, and falls back to using ASM. |
| `summarizer:` >> `extra_args` (DEV) | List of CLI arguments | Extra arguments to pass to summarizer JAR. |
Expand Down
8 changes: 8 additions & 0 deletions pkgs/jnigen/dartdoc_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
dartdoc:
categories:
"Implementing Java interfaces from Dart":
markdown: doc/interface_implementation.md
name: "Interface Implementation"
"Syntactic and semantic differences between Java and the generated Dart bindings":
markdown: doc/java_differences.md
name: "Java Differences"
Original file line number Diff line number Diff line change
@@ -1,17 +1,5 @@
## Implementing Java interfaces from Dart

> [!NOTE]
> This feature is experimental, and in
> [active development](https://github.com/dart-lang/native/issues/1569).
>
> To opt in to use this feature, add the following to your JNIgen configuration
> yaml:
>
> ```yaml
> enable_experiment:
> - interface_implementation
> ```
Let's take a simple Java interface like `Runnable` that has a single `void`
method called `run`:

Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion pkgs/jnigen/example/in_app_java/jnigen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ output:
source_path:
- 'android/app/src/main/java'
classes:
- 'com.example.in_app_java.AndroidUtils' # from source_path
- 'com.example.in_app_java' # Generate the entire package
- 'androidx.emoji2.text.EmojiCompat' # From gradle's compile classpath
- 'androidx.emoji2.text.DefaultEmojiCompatConfig' # From gradle's compile classpath
- 'android.os.Build' # from gradle's compile classpath
Expand Down
Loading

0 comments on commit 55b17e3

Please sign in to comment.