Skip to content

Commit

Permalink
Merge main
Browse files Browse the repository at this point in the history
  • Loading branch information
HosseinYousefi committed Dec 8, 2024
2 parents be287c9 + acb973c commit e522eaa
Show file tree
Hide file tree
Showing 69 changed files with 607 additions and 312 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/native.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ permissions: read-all

on:
pull_request:
branches: [main]
# No `branches:` to enable stacked PRs on GitHub.
paths:
- ".github/workflows/native.yaml"
- "pkgs/native_assets_builder/**"
Expand Down
2 changes: 2 additions & 0 deletions pkgs/jnigen/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
- **Breaking Change**([#1644](https://github.com/dart-lang/native/issues/1644)):
Generate null-safe Dart bindings for Java and Kotlin.
- Fixed a potential name collision when generating in multi-file mode.
- Added the ability to add user-defined visitors to config. Currently only
capable of excluding classes, methods, and fields.

## 0.12.2

Expand Down
14 changes: 10 additions & 4 deletions pkgs/jnigen/lib/src/bindings/excluder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class Excluder extends Visitor<Classes, void> {
void visit(Classes node) {
node.decls.removeWhere((_, classDecl) {
final excluded = classDecl.isPrivate ||
classDecl.isExcluded ||
!(config.exclude?.classes?.included(classDecl) ?? true);
if (excluded) {
log.fine('Excluded class ${classDecl.binaryName}');
Expand All @@ -61,13 +62,17 @@ class _ClassExcluder extends Visitor<ClassDecl, void> {
@override
void visit(ClassDecl node) {
node.methods = node.methods.where((method) {
final isExcluded = method.isExcluded;
final isPrivate = method.isPrivate;
final isAbstractCtor = method.isConstructor && node.isAbstract;
final isBridgeMethod = method.isSynthetic && method.isBridge;
final isExcludedInConfig =
config.exclude?.methods?.included(node, method) ?? false;
final excluded =
isPrivate || isAbstractCtor || isBridgeMethod || isExcludedInConfig;
final excluded = isPrivate ||
isAbstractCtor ||
isBridgeMethod ||
isExcludedInConfig ||
isExcluded;
if (excluded) {
log.fine('Excluded method ${node.binaryName}#${method.name}');
}
Expand All @@ -80,8 +85,9 @@ class _ClassExcluder extends Visitor<ClassDecl, void> {
return !excluded;
}).toList();
node.fields = node.fields.where((field) {
final excluded = field.isPrivate &&
(config.exclude?.fields?.included(node, field) ?? true);
final excluded = field.isExcluded ||
(field.isPrivate &&
(config.exclude?.fields?.included(node, field) ?? true));
if (excluded) {
log.fine('Excluded field ${node.binaryName}#${field.name}');
}
Expand Down
40 changes: 22 additions & 18 deletions pkgs/jnigen/lib/src/config/config_types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import 'package:pub_semver/pub_semver.dart';
import 'package:yaml/yaml.dart';

import '../elements/elements.dart';
import '../elements/j_elements.dart' as j_ast;
import '../logging/logging.dart';
import '../util/find_package.dart';
import 'config_exception.dart';
Expand Down Expand Up @@ -265,24 +266,24 @@ void _validateClassName(String className) {

/// Configuration for jnigen binding generation.
class Config {
Config({
required this.outputConfig,
required this.classes,
this.experiments,
this.exclude,
this.sourcePath,
this.classPath,
this.preamble,
this.customClassBody,
this.androidSdkConfig,
this.mavenDownloads,
this.summarizerOptions,
this.nonNullAnnotations,
this.nullableAnnotations,
this.logLevel = Level.INFO,
this.dumpJsonTo,
this.imports,
}) {
Config(
{required this.outputConfig,
required this.classes,
this.experiments,
this.exclude,
this.sourcePath,
this.classPath,
this.preamble,
this.customClassBody,
this.androidSdkConfig,
this.mavenDownloads,
this.summarizerOptions,
this.nonNullAnnotations,
this.nullableAnnotations,
this.logLevel = Level.INFO,
this.dumpJsonTo,
this.imports,
this.visitors}) {
for (final className in classes) {
_validateClassName(className);
}
Expand Down Expand Up @@ -349,6 +350,9 @@ class Config {
/// Used for testing package:jnigen.
final Map<String, String>? customClassBody;

// User custom visitors.
List<j_ast.Visitor>? visitors;

Future<void> importClasses() async {
importedClasses = {};
for (final import in [
Expand Down
9 changes: 9 additions & 0 deletions pkgs/jnigen/lib/src/elements/elements.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class Classes implements Element<Classes> {
@JsonSerializable(createToJson: false)
class ClassDecl with ClassMember, Annotated implements Element<ClassDecl> {
ClassDecl({
this.isExcluded = false,
this.annotations,
this.javadoc,
required this.declKind,
Expand All @@ -77,6 +78,8 @@ class ClassDecl with ClassMember, Annotated implements Element<ClassDecl> {
this.kotlinPackage,
});

bool isExcluded;

@override
final Set<String> modifiers;

Expand Down Expand Up @@ -602,6 +605,7 @@ mixin ClassMember {
@JsonSerializable(createToJson: false)
class Method with ClassMember, Annotated implements Element<Method> {
Method({
this.isExcluded = false,
this.annotations,
this.javadoc,
this.modifiers = const {},
Expand All @@ -612,6 +616,8 @@ class Method with ClassMember, Annotated implements Element<Method> {
required this.returnType,
});

bool isExcluded;

@override
final String name;
@override
Expand Down Expand Up @@ -704,6 +710,7 @@ class Param with Annotated implements Element<Param> {
@JsonSerializable(createToJson: false)
class Field with ClassMember, Annotated implements Element<Field> {
Field({
this.isExcluded = false,
this.annotations,
this.javadoc,
this.modifiers = const {},
Expand All @@ -712,6 +719,8 @@ class Field with ClassMember, Annotated implements Element<Field> {
this.defaultValue,
});

bool isExcluded;

@override
final String name;
@override
Expand Down
85 changes: 85 additions & 0 deletions pkgs/jnigen/lib/src/elements/j_elements.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
// 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.

import 'elements.dart' as ast;

abstract class Element {
void accept(Visitor visitor);
}

abstract class Visitor {
void visitClass(ClassDecl c) {}
void visitMethod(Method method) {}
void visitField(Field field) {}
}

class Classes implements Element {
Classes(this._classes);
final ast.Classes _classes;

@override
void accept(Visitor visitor) {
for (final value in _classes.decls.values) {
final classDecl = ClassDecl(value);
classDecl.accept(visitor);
}
}

void let(void Function(dynamic userClasses) param0) {}
}

class ClassDecl implements Element {
ClassDecl(this._classDecl) : binaryName = _classDecl.binaryName;
final ast.ClassDecl _classDecl;

// Ex: com.x.Foo.
final String binaryName;

bool get isExcluded => _classDecl.isExcluded;
set isExcluded(bool value) => _classDecl.isExcluded = value;

@override
void accept(Visitor visitor) {
visitor.visitClass(this);
if (_classDecl.isExcluded) return;
for (final method in _classDecl.methods) {
Method(method).accept(visitor);
}
for (var field in _classDecl.fields) {
Field(field).accept(visitor);
}
}
}

class Method implements Element {
Method(this._method);

final ast.Method _method;

String get name => _method.name;

bool get isExcluded => _method.isExcluded;
set isExcluded(bool value) => _method.isExcluded = value;

@override
void accept(Visitor visitor) {
visitor.visitMethod(this);
}
}

class Field implements Element {
Field(this._field);

final ast.Field _field;

String get name => _field.name;

bool get isExcluded => _field.isExcluded;
set isExcluded(bool value) => _field.isExcluded = value;

@override
void accept(Visitor visitor) {
visitor.visitField(this);
}
}
4 changes: 4 additions & 0 deletions pkgs/jnigen/lib/src/generate_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'bindings/linker.dart';
import 'bindings/renamer.dart';
import 'config/config.dart';
import 'elements/elements.dart';
import 'elements/j_elements.dart' as j_ast;
import 'logging/logging.dart';
import 'summary/summary.dart';
import 'tools/tools.dart';
Expand All @@ -38,6 +39,9 @@ Future<void> generateJniBindings(Config config) async {
log.fatal(e.message);
}

final userClasses = j_ast.Classes(classes);
config.visitors?.forEach(userClasses.accept);

classes.accept(Excluder(config));
classes.accept(KotlinProcessor());
await classes.accept(Linker(config));
Expand Down
86 changes: 86 additions & 0 deletions pkgs/jnigen/test/user_excluder.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 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.

import 'package:jnigen/src/elements/elements.dart' as ast;
import 'package:jnigen/src/elements/j_elements.dart';
import 'package:test/test.dart';

extension on Iterable<ast.Method> {
List<bool> get isExcludedValues => map((c) => c.isExcluded).toList();
}

extension on Iterable<ast.Field> {
List<bool> get isExcludedValues => map((c) => c.isExcluded).toList();
}

// This is customizable by the user
class UserExcluder extends Visitor {
@override
void visitClass(ClassDecl c) {
if (c.binaryName.contains('y')) {
c.isExcluded = true;
}
}

@override
void visitMethod(Method method) {
if (method.name == 'Bar') {
method.isExcluded = true;
}
}

@override
void visitField(Field field) {
if (field.name == 'Bar') {
field.isExcluded = true;
}
}
}

void main() {
test('Exclude something using the user excluder, Simple AST', () async {
final classes = ast.Classes({
'Foo': ast.ClassDecl(
binaryName: 'Foo',
declKind: ast.DeclKind.classKind,
superclass: ast.TypeUsage.object,
methods: [
ast.Method(name: 'foo', returnType: ast.TypeUsage.object),
ast.Method(name: 'Bar', returnType: ast.TypeUsage.object),
ast.Method(name: 'foo1', returnType: ast.TypeUsage.object),
ast.Method(name: 'Bar', returnType: ast.TypeUsage.object),
],
fields: [
ast.Field(name: 'foo', type: ast.TypeUsage.object),
ast.Field(name: 'Bar', type: ast.TypeUsage.object),
ast.Field(name: 'foo1', type: ast.TypeUsage.object),
ast.Field(name: 'Bar', type: ast.TypeUsage.object),
],
),
'y.Foo': ast.ClassDecl(
binaryName: 'y.Foo',
declKind: ast.DeclKind.classKind,
superclass: ast.TypeUsage.object,
methods: [
ast.Method(name: 'foo', returnType: ast.TypeUsage.object),
ast.Method(name: 'Bar', returnType: ast.TypeUsage.object),
],
fields: [
ast.Field(name: 'foo', type: ast.TypeUsage.object),
ast.Field(name: 'Bar', type: ast.TypeUsage.object),
]),
});

final simpleClasses = Classes(classes);
simpleClasses.accept(UserExcluder());

expect(classes.decls['y.Foo']?.isExcluded, true);
expect(classes.decls['Foo']?.isExcluded, false);

expect(classes.decls['Foo']?.fields.isExcludedValues,
[false, true, false, true]);
expect(classes.decls['Foo']?.methods.isExcludedValues,
[false, true, false, true]);
});
}
6 changes: 6 additions & 0 deletions pkgs/native_assets_builder/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
## 0.9.1-wip

- **Breaking change**: Rename `supportedAssetTypes` to `buildAssetTypes`. Hooks
should no longer fail. Instead, the code should fail at runtime if an asset is
missing. This enables (1) code to run if an asset is missing but that code is
not invoked at runtime, and (2) doing fallback implementations in Dart if an
asset is missing.

## 0.9.0

- Also lock `BuildConfig` and `LinkConfig` `outputDirectoryShared` when invoking
Expand Down
Loading

0 comments on commit e522eaa

Please sign in to comment.