Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 19 additions & 2 deletions pkgs/jnigen/lib/src/bindings/dart_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1393,7 +1393,9 @@ ${modifier}final _$name = $_protectedExtension
} else {
s.writeAll(node.modifiers.map((m) => '$m '));
s.write('${node.returnType} ${node.name}(');
s.writeAll(node.params.map((p) => '${p.type} ${p.name}'), ', ');
// Filter out Kotlin synthetic params from documentation.
final docParams = node.params.where((p) => !p.isKotlinSynthetic);
s.writeAll(docParams.map((p) => '${p.type} ${p.name}'), ', ');
s.writeln(')`');
}
if (node.returnType is! PrimitiveType || node.isConstructor) {
Expand All @@ -1402,7 +1404,9 @@ ${modifier}final _$name = $_protectedExtension
node.javadoc?.accept(_DocGenerator(s, depth: 1));

// Used for inferring the type parameter from the given parameters.
// Exclude Kotlin synthetic params since they're not part of the Dart API.
final typeLocators = node.params
.where((p) => !p.isKotlinSynthetic)
.accept(_ParamTypeLocator(resolver: resolver))
.fold(<String, List<String>>{}, _mergeMapValues).map(
(key, value) =>
Expand All @@ -1424,15 +1428,22 @@ ${modifier}final _$name = $_protectedExtension
.join(_newLine(depth: 2));
// This is needed to keep the references alive in the scope while waiting
// for the FFI call.
// Filter out Kotlin synthetic params from local references since they're
// not part of the Dart API.
final localReferences = node.params
.where((p) => !p.isKotlinSynthetic)
.accept(const _ParamReference())
.where((ref) => ref.isNotEmpty)
.toList();
if (node.isConstructor) {
final className = node.classDecl.finalName;
final name = node.finalName;
final ctorName = name == 'new\$' ? className : '$className.$name';
final paramsDef = node.params.accept(_ParamDef(resolver)).delimited(', ');
// Filter out Kotlin synthetic params from the Dart API signature.
final dartApiParams =
node.params.where((p) => !p.isKotlinSynthetic).toList();
final paramsDef =
dartApiParams.accept(_ParamDef(resolver)).delimited(', ');
final typeParamsCall = node.classDecl.allTypeParams
.map((typeParam) => '$_typeParamPrefix${typeParam.name}')
.join(', ')
Expand Down Expand Up @@ -1705,6 +1716,12 @@ class _ParamCall extends Visitor<Param, String> {

@override
String visit(Param node) {
// Kotlin synthetic parameters (e.g., DefaultConstructorMarker) should
// always receive jNullReference in JNI calls.
if (node.isKotlinSynthetic) {
return '$_jni.jNullReference.pointer';
}

final nativeSuffix = node.type.accept(const _ToNativeSuffix());
final nonPrimitive = node.type is PrimitiveType ? '' : r'_$';
final paramCall = '$nonPrimitive${node.finalName}$nativeSuffix';
Expand Down
18 changes: 16 additions & 2 deletions pkgs/jnigen/lib/src/bindings/excluder.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,8 +67,22 @@ class _ClassExcluder extends Visitor<ClassDecl, void> {
final isPrivate = method.isPrivate;
final isAbstractCtor = method.isConstructor && node.isAbstract;
final isBridgeMethod = method.isSynthetic && method.isBridge;
final excluded =
isPrivate || isAbstractCtor || isBridgeMethod || isExcluded;

// Exclude synthetic Kotlin constructors with DefaultConstructorMarker.
// These are compiler-generated overloads for default parameters and
// should not be exposed in the Dart API.
final isSyntheticDefaultCtorMarker = method.isConstructor &&
method.isSynthetic &&
method.params.any((param) =>
param.type is DeclaredType &&
(param.type as DeclaredType).binaryName ==
'kotlin.jvm.internal.DefaultConstructorMarker');
Comment thread
sagar-h007 marked this conversation as resolved.
Outdated

final excluded = isPrivate ||
isAbstractCtor ||
isBridgeMethod ||
isSyntheticDefaultCtorMarker ||
isExcluded;
if (excluded) {
log.fine('Excluded method ${node.binaryName}#${method.name}');
}
Expand Down
11 changes: 11 additions & 0 deletions pkgs/jnigen/lib/src/bindings/kotlin_processor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,17 @@ class _KotlinConstructorProcessor extends Visitor<Method, void> {
@override
void visit(Method node) {
_processParams(node.params, constructor.valueParameters);

// Mark DefaultConstructorMarker parameters as Kotlin synthetic.
// These are compiler-generated parameters for constructors with default
// values and should not be exposed in the Dart API.
for (final param in node.params) {
if (param.type case final DeclaredType type) {
if (type.binaryName == 'kotlin.jvm.internal.DefaultConstructorMarker') {
param.isKotlinSynthetic = true;
}
}
}
}
}

Expand Down
13 changes: 13 additions & 0 deletions pkgs/jnigen/lib/src/elements/elements.dart
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,16 @@ class Param with Annotated implements Element<Param> {
@JsonKey(includeFromJson: false)
late String finalName;

/// Whether this parameter is a Kotlin synthetic parameter
/// (e.g., DefaultConstructorMarker).
///
/// These parameters should be hidden from the generated Dart API but still
/// passed to the JNI constructor (as jNullReference).
///
/// Populated by [KotlinProcessor].
@JsonKey(includeFromJson: false)
bool isKotlinSynthetic = false;

factory Param.fromJson(Map<String, dynamic> json) => _$ParamFromJson(json);

Param clone({GenerationStage until = GenerationStage.userVisitors}) {
Expand All @@ -842,6 +852,9 @@ class Param with Annotated implements Element<Param> {
if (GenerationStage.linker <= until) {
cloned.method = method;
}
if (GenerationStage.kotlinProcessor <= until) {
cloned.isKotlinSynthetic = isKotlinSynthetic;
}
if (GenerationStage.renamer <= until) {
cloned.finalName = finalName;
}
Expand Down
Loading
Loading