blob: 0b315de7ef0e92cd4caff312c80f63a83a04ef04 [file] [log] [blame] [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 '../elements/elements.dart';
import 'visitor.dart';
String _toJavaBinaryName(String kotlinBinaryName) {
final binaryName =
kotlinBinaryName.replaceAll('.', r'$').replaceAll('/', '.');
return const {
'kotlin.Any': 'java.lang.Object',
'kotlin.Byte': 'java.lang.Byte',
'kotlin.Short': 'java.lang.Short',
'kotlin.Int': 'java.lang.Integer',
'kotlin.Long': 'java.lang.Long',
'kotlin.Char': 'java.lang.Character',
'kotlin.Float': 'java.lang.Float',
'kotlin.Double': 'java.lang.Double',
'kotlin.Boolean': 'java.lang.Boolean',
'kotlin.Cloneable': 'java.lang.Cloneable',
'kotlin.Comparable': 'java.lang.Comparable',
'kotlin.Enum': 'java.lang.Enum',
'kotlin.Annotation': 'java.lang.annotation.Annotation',
'kotlin.CharSequence': 'java.lang.CharSequence',
'kotlin.String': 'java.lang.String',
'kotlin.Number': 'java.lang.Number',
'kotlin.Throwable': 'java.lang.Throwable',
}[binaryName] ??
binaryName;
}
/// A [Visitor] that adds the the information from Kotlin's metadata to the Java
/// classes and methods.
class KotlinProcessor extends Visitor<Classes, void> with TopLevelVisitor {
@override
final GenerationStage stage = GenerationStage.kotlinProcessor;
@override
void visit(Classes node) {
final classProcessor = _KotlinClassProcessor();
for (final classDecl in node.decls.values) {
classDecl.accept(classProcessor);
}
}
}
class _KotlinClassProcessor extends Visitor<ClassDecl, void> {
@override
void visit(ClassDecl node) {
if (node.kotlinClass == null && node.kotlinPackage == null) {
return;
}
// This [ClassDecl] is actually a Kotlin class.
if (node.kotlinClass != null) {
for (var i = 0; i < node.kotlinClass!.typeParameters.length; ++i) {
node.typeParams[i].accept(
_KotlinTypeParamProcessor(node.kotlinClass!.typeParameters[i]));
}
if (node.superclass case final superClass?) {
final kotlinSuperTypes = node.kotlinClass!.superTypes.where(
(superType) =>
_toJavaBinaryName(superType.name ?? '') == superClass.name,
);
if (kotlinSuperTypes.isNotEmpty) {
superClass.accept(_KotlinTypeProcessor(kotlinSuperTypes.single));
}
}
}
// Matching fields and properties from the metadata.
final properties = <String, KotlinProperty>{};
final getters = <String, KotlinProperty>{};
final setters = <String, KotlinProperty>{};
final kotlinProperties =
(node.kotlinClass?.properties ?? node.kotlinPackage?.properties)!;
for (final property in kotlinProperties) {
if (property.fieldName case final fieldName?) {
properties[fieldName] = property;
}
if (property.getterName case final getterName?) {
final getterSignature = getterName + property.getterDescriptor!;
getters[getterSignature] = property;
}
if (property.setterName case final setterName?) {
final setterSignature = setterName + property.setterDescriptor!;
setters[setterSignature] = property;
}
}
for (final field in node.fields) {
if (properties[field.name] case final property?) {
field.accept(_KotlinPropertyProcessor(property));
}
}
// Matching methods and functions from the metadata.
final functions = <String, KotlinFunction>{};
final kotlinFunctions =
(node.kotlinClass?.functions ?? node.kotlinPackage?.functions)!;
for (final function in kotlinFunctions) {
final signature = function.name + function.descriptor;
functions[signature] = function;
}
final constructors = <String, KotlinConstructor>{};
final kotlinConstructors = node.kotlinClass?.constructors ?? [];
for (final constructor in kotlinConstructors) {
final signature = constructor.name + constructor.descriptor;
constructors[signature] = constructor;
}
for (final method in node.methods) {
final signature = method.name + method.descriptor!;
if (functions[signature] case final function?) {
method.accept(_KotlinMethodProcessor(function));
} else if (constructors[signature] case final constructor?) {
method.accept(_KotlinConstructorProcessor(constructor));
} else if (getters[signature] case final getter?) {
method.accept(_KotlinGetterProcessor(getter));
} else if (setters[signature] case final setter?) {
method.accept(_KotlinSetterProcessor(setter));
}
}
}
}
void _processParams(
List<Param> params, List<KotlinValueParameter> kotlinParams) {
if (params.length != kotlinParams.length) {
return;
}
for (var i = 0; i < params.length; ++i) {
params[i].accept(_KotlinParamProcessor(kotlinParams[i]));
}
}
class _KotlinMethodProcessor extends Visitor<Method, void> {
final KotlinFunction function;
_KotlinMethodProcessor(this.function);
@override
void visit(Method node) {
_processParams(node.params, function.valueParameters);
node.kotlinFunction = function;
for (var i = 0; i < node.typeParams.length; ++i) {
node.typeParams[i]
.accept(_KotlinTypeParamProcessor(function.typeParameters[i]));
}
if (function.isSuspend) {
const kotlinContinutationType = 'kotlin.coroutines.Continuation';
assert(node.params.isNotEmpty &&
node.params.last.type is DeclaredType &&
node.params.last.type.name == kotlinContinutationType);
var continuationType =
(node.params.last.type as DeclaredType).params.firstOrNull;
if (continuationType != null &&
continuationType is Wildcard &&
continuationType.superBound != null) {
continuationType = continuationType.superBound!;
}
node.asyncReturnType = continuationType == null
? DeclaredType.object.clone()
: continuationType.clone();
node.asyncReturnType!.accept(_KotlinTypeProcessor(function.returnType));
if (!node.asyncReturnType!.isNullable) {
node.returnType.annotations ??= [];
node.returnType.annotations!.add(Annotation.nonNull);
}
} else {
node.returnType.accept(_KotlinTypeProcessor(function.returnType));
}
}
}
class _KotlinConstructorProcessor extends Visitor<Method, void> {
final KotlinConstructor constructor;
_KotlinConstructorProcessor(this.constructor);
@override
void visit(Method node) {
_processParams(node.params, constructor.valueParameters);
}
}
class _KotlinGetterProcessor extends Visitor<Method, void> {
final KotlinProperty getter;
_KotlinGetterProcessor(this.getter);
@override
void visit(Method node) {
node.returnType.accept(_KotlinTypeProcessor(getter.returnType));
}
}
class _KotlinSetterProcessor extends Visitor<Method, void> {
final KotlinProperty setter;
_KotlinSetterProcessor(this.setter);
@override
void visit(Method node) {
if (setter.setterParameter case final setterParam?) {
node.params.single.type.accept(_KotlinTypeProcessor(setterParam.type));
}
node.params.single.type.accept(_KotlinTypeProcessor(setter.returnType));
}
}
class _KotlinPropertyProcessor extends Visitor<Field, void> {
final KotlinProperty property;
_KotlinPropertyProcessor(this.property);
@override
void visit(Field node) {
node.type.accept(_KotlinTypeProcessor(property.returnType));
}
}
class _KotlinParamProcessor extends Visitor<Param, void> {
final KotlinValueParameter kotlinParam;
_KotlinParamProcessor(this.kotlinParam);
@override
void visit(Param node) {
node.type.accept(_KotlinTypeProcessor(kotlinParam.type));
}
}
class _KotlinTypeParamProcessor extends Visitor<TypeParam, void> {
final KotlinTypeParameter kotlinTypeParam;
_KotlinTypeParamProcessor(this.kotlinTypeParam);
@override
void visit(TypeParam node) {
final kotlinBounds = kotlinTypeParam.upperBounds;
final bounds = <String, KotlinType>{};
for (final bound in kotlinBounds) {
if (bound.name case final boundName?) {
bounds[_toJavaBinaryName(boundName)] = bound;
}
}
for (final bound in node.bounds) {
if (bounds[bound.name] case final kotlinBound?) {
bound.accept(_KotlinTypeProcessor(kotlinBound));
}
}
}
}
class _KotlinTypeProcessor extends TypeVisitor<void> {
final KotlinType kotlinType;
_KotlinTypeProcessor(this.kotlinType);
@override
void visitDeclaredType(DeclaredType node) {
for (var i = 0; i < node.params.length; ++i) {
if (kotlinType.arguments[i] case final KotlinTypeProjection projection) {
node.params[i].accept(_KotlinTypeProcessor(projection.type));
}
}
super.visitDeclaredType(node);
}
@override
void visitArrayType(ArrayType node) {
if (kotlinType.arguments.isNotEmpty) {
if (kotlinType.arguments.first
case final KotlinTypeProjection projection) {
node.elementType.accept(_KotlinTypeProcessor(projection.type));
}
}
super.visitArrayType(node);
}
@override
void visitWildcard(Wildcard node) {
node.extendsBound?.accept(_KotlinTypeProcessor(kotlinType));
node.superBound?.accept(_KotlinTypeProcessor(kotlinType));
super.visitWildcard(node);
}
@override
void visitNonPrimitiveType(ReferredType node) {
node.annotations ??= [];
node.annotations!
.add(kotlinType.isNullable ? Annotation.nullable : Annotation.nonNull);
}
@override
void visitPrimitiveType(PrimitiveType node) {
// Do nothing.
}
}