blob: 020f05dbf354b5cdfa630e8402540669c30fbe6e [file] [log] [blame] [edit]
// Copyright (c) 2022, 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:json_annotation/json_annotation.dart';
// Types to describe java API elements
import '../bindings/visitor.dart';
part 'elements.g.dart';
abstract class Element<T extends Element<T>> {
const Element();
R accept<R>(Visitor<T, R> v);
}
@JsonEnum()
/// A kind describes the type of a declaration.
enum DeclKind {
@JsonValue('CLASS')
classKind,
@JsonValue('INTERFACE')
interfaceKind,
@JsonValue('ENUM')
enumKind,
}
class Classes implements Element<Classes> {
const Classes(this.decls);
final Map<String, ClassDecl> decls;
factory Classes.fromJson(List<dynamic> json) {
final decls = <String, ClassDecl>{};
for (final declJson in json) {
final classDecl = ClassDecl.fromJson(declJson);
decls[classDecl.binaryName] = classDecl;
}
return Classes(decls);
}
@override
R accept<R>(Visitor<Classes, R> v) {
return v.visit(this);
}
}
// Note: We give default values in constructor, if the field is nullable in
// JSON. this allows us to reduce JSON size by providing Include.NON_NULL
// option in java.
@JsonSerializable(createToJson: false)
class ClassDecl extends ClassMember implements Element<ClassDecl> {
ClassDecl({
this.annotations = const [],
this.javadoc,
required this.declKind,
this.modifiers = const {},
required this.binaryName,
this.typeParams = const [],
this.methods = const [],
this.fields = const [],
this.superclass,
this.interfaces = const [],
this.hasStaticInit = false,
this.hasInstanceInit = false,
this.values,
this.kotlinClass,
});
@override
final Set<String> modifiers;
final List<Annotation> annotations;
final KotlinClass? kotlinClass;
final JavaDocComment? javadoc;
final DeclKind declKind;
final String binaryName;
List<TypeParam> typeParams;
List<Method> methods;
List<Field> fields;
final List<TypeUsage> interfaces;
final bool hasStaticInit;
final bool hasInstanceInit;
/// Will default to java.lang.Object if null by [Linker].
TypeUsage? superclass;
/// Contains enum constant names if class is an enum,
/// as obtained by `.values()` method in Java.
final List<String>? values;
String get internalName => binaryName.replaceAll(".", "/");
String get packageName => (binaryName.split('.')..removeLast()).join('.');
/// The number of super classes this type has.
///
/// Populated by [Linker].
@JsonKey(includeFromJson: false)
late final int superCount;
/// Parent's [ClassDecl] obtained from [parentName].
///
/// Populated by [Linker].
@JsonKey(includeFromJson: false)
late final ClassDecl? parent;
/// Final name of this class.
///
/// Populated by [Renamer].
@JsonKey(includeFromJson: false)
late final String finalName;
/// Name of the type class.
///
/// Populated by [Renamer].
@JsonKey(includeFromJson: false)
late final String typeClassName;
/// Unique name obtained by renaming conflicting names with a number.
///
/// This is used by C bindings instead of fully qualified name to reduce
/// the verbosity of generated bindings.
///
/// Populated by [Renamer].
@JsonKey(includeFromJson: false)
late final String uniqueName;
/// Type parameters including the ones from its ancestors
///
/// Populated by [Linker].
@JsonKey(includeFromJson: false)
List<TypeParam> allTypeParams = const [];
/// The path which this class is generated in.
///
/// Populated by [Linker].
@JsonKey(includeFromJson: false)
late final String path;
/// The numeric suffix of the methods.
///
/// Populated by [Renamer].
@JsonKey(includeFromJson: false)
late final Map<String, int> methodNumsAfterRenaming;
@override
String toString() {
return 'Java class declaration for $binaryName';
}
String get signature => 'L$internalName;';
factory ClassDecl.fromJson(Map<String, dynamic> json) =>
_$ClassDeclFromJson(json);
@override
R accept<R>(Visitor<ClassDecl, R> v) {
return v.visit(this);
}
@override
ClassDecl get classDecl => this;
@override
String get name => finalName;
bool get isObject => superCount == 0;
@JsonKey(includeFromJson: false)
late final String? parentName = binaryName.contains(r'$')
? binaryName.splitMapJoin(RegExp(r'\$[^$]+$'), onMatch: (_) => '')
: null;
@JsonKey(includeFromJson: false)
late final isNested = parentName != null;
}
@JsonEnum()
enum Kind {
@JsonValue('PRIMITIVE')
primitive,
@JsonValue('TYPE_VARIABLE')
typeVariable,
@JsonValue('WILDCARD')
wildcard,
@JsonValue('DECLARED')
declared,
@JsonValue('ARRAY')
array,
}
@JsonSerializable(createToJson: false)
class TypeUsage {
TypeUsage({
required this.shorthand,
required this.kind,
required this.typeJson,
});
static TypeUsage object = TypeUsage(
kind: Kind.declared, shorthand: 'java.lang.Object', typeJson: {})
..type = DeclaredType(binaryName: 'java.lang.Object');
final String shorthand;
final Kind kind;
@JsonKey(name: "type")
final Map<String, dynamic> typeJson;
/// Populated by [TypeUsage.fromJson].
@JsonKey(includeFromJson: false)
late final ReferredType type;
/// Populated by [Descriptor].
@JsonKey(includeFromJson: false)
late String descriptor;
String get name => type.name;
// Since json_serializable doesn't directly support union types,
// we have to temporarily store `type` in a JSON map, and switch on the
// enum value received.
factory TypeUsage.fromJson(Map<String, dynamic> json) {
final t = _$TypeUsageFromJson(json);
switch (t.kind) {
case Kind.primitive:
t.type = PrimitiveType.fromJson(t.typeJson);
break;
case Kind.typeVariable:
t.type = TypeVar.fromJson(t.typeJson);
break;
case Kind.wildcard:
t.type = Wildcard.fromJson(t.typeJson);
break;
case Kind.declared:
t.type = DeclaredType.fromJson(t.typeJson);
break;
case Kind.array:
t.type = ArrayType.fromJson(t.typeJson);
break;
}
return t;
}
R accept<R>(TypeVisitor<R> v) {
return type.accept(v);
}
}
abstract class ReferredType {
const ReferredType();
String get name;
R accept<R>(TypeVisitor<R> v);
}
class PrimitiveType extends ReferredType {
static const _primitives = {
'byte': PrimitiveType._(
name: 'byte',
signature: 'B',
dartType: 'int',
boxedName: 'Byte',
cType: 'int8_t',
ffiType: 'Int8',
),
'short': PrimitiveType._(
name: 'short',
signature: 'S',
dartType: 'int',
boxedName: 'Short',
cType: 'int16_t',
ffiType: 'Int16',
),
'char': PrimitiveType._(
name: 'char',
signature: 'C',
dartType: 'int',
boxedName: 'Character',
cType: 'uint16_t',
ffiType: 'Uint16',
),
'int': PrimitiveType._(
name: 'int',
signature: 'I',
dartType: 'int',
boxedName: 'Integer',
cType: 'int32_t',
ffiType: 'Int32',
),
'long': PrimitiveType._(
name: 'long',
signature: 'J',
dartType: 'int',
boxedName: 'Long',
cType: 'int64_t',
ffiType: 'Int64',
),
'float': PrimitiveType._(
name: 'float',
signature: 'F',
dartType: 'double',
boxedName: 'Float',
cType: 'float',
ffiType: 'Float',
),
'double': PrimitiveType._(
name: 'double',
signature: 'D',
dartType: 'double',
boxedName: 'Double',
cType: 'double',
ffiType: 'Double',
),
'boolean': PrimitiveType._(
name: 'boolean',
signature: 'Z',
dartType: 'bool',
boxedName: 'Boolean',
cType: 'uint8_t',
ffiType: 'Uint8',
),
'void': PrimitiveType._(
name: 'void',
signature: 'V',
dartType: 'void',
boxedName: 'Void', // Not used.
cType: 'void',
ffiType: 'Void',
),
};
const PrimitiveType._({
required this.name,
required this.signature,
required this.dartType,
required this.boxedName,
required this.cType,
required this.ffiType,
});
@override
final String name;
final String signature;
final String dartType;
final String boxedName;
final String cType;
final String ffiType;
factory PrimitiveType.fromJson(Map<String, dynamic> json) {
return _primitives[json['name']]!;
}
@override
R accept<R>(TypeVisitor<R> v) {
return v.visitPrimitiveType(this);
}
}
@JsonSerializable(createToJson: false)
class DeclaredType extends ReferredType {
DeclaredType({
required this.binaryName,
this.params = const [],
});
final String binaryName;
final List<TypeUsage> params;
@JsonKey(includeFromJson: false)
late ClassDecl classDecl;
@override
String get name => binaryName;
factory DeclaredType.fromJson(Map<String, dynamic> json) =>
_$DeclaredTypeFromJson(json);
@override
R accept<R>(TypeVisitor<R> v) {
return v.visitDeclaredType(this);
}
}
@JsonSerializable(createToJson: false)
class TypeVar extends ReferredType {
TypeVar({required this.name});
@override
String name;
factory TypeVar.fromJson(Map<String, dynamic> json) =>
_$TypeVarFromJson(json);
@override
R accept<R>(TypeVisitor<R> v) {
return v.visitTypeVar(this);
}
}
@JsonSerializable(createToJson: false)
class Wildcard extends ReferredType {
Wildcard({this.extendsBound, this.superBound});
TypeUsage? extendsBound, superBound;
@override
String get name => "?";
factory Wildcard.fromJson(Map<String, dynamic> json) =>
_$WildcardFromJson(json);
@override
R accept<R>(TypeVisitor<R> v) {
return v.visitWildcard(this);
}
}
@JsonSerializable(createToJson: false)
class ArrayType extends ReferredType {
ArrayType({required this.type});
TypeUsage type;
@override
String get name => "[${type.name}";
factory ArrayType.fromJson(Map<String, dynamic> json) =>
_$ArrayTypeFromJson(json);
@override
R accept<R>(TypeVisitor<R> v) {
return v.visitArrayType(this);
}
}
abstract class ClassMember {
String get name;
ClassDecl get classDecl;
Set<String> get modifiers;
bool get isStatic => modifiers.contains('static');
bool get isFinal => modifiers.contains('final');
bool get isPublic => modifiers.contains('public');
bool get isProtected => modifiers.contains('protected');
}
@JsonSerializable(createToJson: false)
class Method extends ClassMember implements Element<Method> {
Method({
this.annotations = const [],
this.javadoc,
this.modifiers = const {},
required this.name,
this.descriptor,
this.typeParams = const [],
this.params = const [],
required this.returnType,
});
@override
final String name;
@override
final Set<String> modifiers;
final List<Annotation> annotations;
final JavaDocComment? javadoc;
final List<TypeParam> typeParams;
List<Param> params;
final TypeUsage returnType;
/// Can be used to match with [KotlinFunction]'s descriptor.
///
/// Can create a unique signature in combination with [name].
/// Populated either by the ASM backend or [Descriptor].
String? descriptor;
/// The [ClassDecl] where this method is defined.
///
/// Populated by [Linker].
@JsonKey(includeFromJson: false)
@override
late ClassDecl classDecl;
/// Populated by [Renamer].
@JsonKey(includeFromJson: false)
late String finalName;
@JsonKey(includeFromJson: false)
late bool isOverridden;
/// The actual return type when the method is a Kotlin's suspend fun.
///
/// Populated by [KotlinProcessor].
@JsonKey(includeFromJson: false)
TypeUsage? asyncReturnType;
@JsonKey(includeFromJson: false)
late final String javaSig = '$name$descriptor';
bool get isCtor => name == '<init>';
factory Method.fromJson(Map<String, dynamic> json) => _$MethodFromJson(json);
@override
R accept<R>(Visitor<Method, R> v) {
return v.visit(this);
}
}
@JsonSerializable(createToJson: false)
class Param implements Element<Param> {
Param({
this.annotations = const [],
this.javadoc,
required this.name,
required this.type,
});
final List<Annotation> annotations;
final JavaDocComment? javadoc;
final String name;
final TypeUsage type;
/// Populated by [Renamer].
@JsonKey(includeFromJson: false)
late String finalName;
factory Param.fromJson(Map<String, dynamic> json) => _$ParamFromJson(json);
@override
R accept<R>(Visitor<Param, R> v) {
return v.visit(this);
}
}
@JsonSerializable(createToJson: false)
class Field extends ClassMember implements Element<Field> {
Field({
this.annotations = const [],
this.javadoc,
this.modifiers = const {},
required this.name,
required this.type,
this.defaultValue,
});
@override
final String name;
@override
final Set<String> modifiers;
final List<Annotation> annotations;
final JavaDocComment? javadoc;
final TypeUsage type;
final Object? defaultValue;
/// The [ClassDecl] where this field is defined.
///
/// Populated by [Linker].
@JsonKey(includeFromJson: false)
@override
late final ClassDecl classDecl;
/// Populated by [Renamer].
@JsonKey(includeFromJson: false)
late final String finalName;
factory Field.fromJson(Map<String, dynamic> json) => _$FieldFromJson(json);
@override
R accept<R>(Visitor<Field, R> v) {
return v.visit(this);
}
}
@JsonSerializable(createToJson: false)
class TypeParam implements Element<TypeParam> {
TypeParam({required this.name, this.bounds = const []});
final String name;
final List<TypeUsage> bounds;
@JsonKey(includeFromJson: false)
late final String erasure;
factory TypeParam.fromJson(Map<String, dynamic> json) =>
_$TypeParamFromJson(json);
@override
R accept<R>(Visitor<TypeParam, R> v) {
return v.visit(this);
}
}
@JsonSerializable(createToJson: false)
class JavaDocComment implements Element<JavaDocComment> {
JavaDocComment({this.comment = ''});
final String comment;
@JsonKey(includeFromJson: false)
late final String dartDoc;
factory JavaDocComment.fromJson(Map<String, dynamic> json) =>
_$JavaDocCommentFromJson(json);
@override
R accept<R>(Visitor<JavaDocComment, R> v) {
return v.visit(this);
}
}
@JsonSerializable(createToJson: false)
class Annotation implements Element<Annotation> {
Annotation({
required this.binaryName,
this.properties = const {},
});
final String binaryName;
final Map<String, Object> properties;
factory Annotation.fromJson(Map<String, dynamic> json) =>
_$AnnotationFromJson(json);
@override
R accept<R>(Visitor<Annotation, R> v) {
return v.visit(this);
}
}
@JsonSerializable(createToJson: false)
class KotlinClass implements Element<KotlinClass> {
KotlinClass({
required this.name,
this.functions = const [],
});
final String name;
final List<KotlinFunction> functions;
factory KotlinClass.fromJson(Map<String, dynamic> json) =>
_$KotlinClassFromJson(json);
@override
R accept<R>(Visitor<KotlinClass, R> v) {
return v.visit(this);
}
}
@JsonSerializable(createToJson: false)
class KotlinFunction implements Element<KotlinFunction> {
KotlinFunction({
required this.name,
required this.descriptor,
required this.kotlinName,
required this.isSuspend,
});
final String name;
/// Used to match with [Method]'s descriptor.
///
/// Creates a unique signature in combination with [name].
final String descriptor;
final String kotlinName;
final bool isSuspend;
factory KotlinFunction.fromJson(Map<String, dynamic> json) =>
_$KotlinFunctionFromJson(json);
@override
R accept<R>(Visitor<KotlinFunction, R> v) {
return v.visit(this);
}
}