blob: e442a6326733ce0154dcad3ebaa6c08fc36cf8a9 [file] [log] [blame]
// Copyright (c) 2020, 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:ffigen/src/code_generator.dart';
import 'compound.dart';
import 'typealias.dart';
import 'writer.dart';
class _SubType {
final String c;
final String dart;
const _SubType({required this.c, required this.dart});
}
enum SupportedNativeType {
Void,
Char,
Int8,
Int16,
Int32,
Int64,
Uint8,
Uint16,
Uint32,
Uint64,
Float,
Double,
IntPtr,
}
/// The basic types in which all types can be broadly classified into.
enum BroadType {
Boolean,
NativeType,
Pointer,
Compound,
NativeFunction,
/// Represents a function type.
FunctionType,
/// Represents a typealias.
Typealias,
/// Represents a Dart_Handle.
Handle,
Enum,
/// Represents an Array type.
ConstantArray,
IncompleteArray,
/// Used as a marker, so that declarations having these can exclude them.
Unimplemented,
}
/// Type class for return types, variable types, etc.
class Type {
static const _primitives = <SupportedNativeType, _SubType>{
SupportedNativeType.Void: _SubType(c: 'Void', dart: 'void'),
SupportedNativeType.Char: _SubType(c: 'Uint8', dart: 'int'),
SupportedNativeType.Int8: _SubType(c: 'Int8', dart: 'int'),
SupportedNativeType.Int16: _SubType(c: 'Int16', dart: 'int'),
SupportedNativeType.Int32: _SubType(c: 'Int32', dart: 'int'),
SupportedNativeType.Int64: _SubType(c: 'Int64', dart: 'int'),
SupportedNativeType.Uint8: _SubType(c: 'Uint8', dart: 'int'),
SupportedNativeType.Uint16: _SubType(c: 'Uint16', dart: 'int'),
SupportedNativeType.Uint32: _SubType(c: 'Uint32', dart: 'int'),
SupportedNativeType.Uint64: _SubType(c: 'Uint64', dart: 'int'),
SupportedNativeType.Float: _SubType(c: 'Float', dart: 'double'),
SupportedNativeType.Double: _SubType(c: 'Double', dart: 'double'),
SupportedNativeType.IntPtr: _SubType(c: 'IntPtr', dart: 'int'),
};
/// Enum type is mapped to [SupportedNativeType.Int32].
static const enumNativeType = SupportedNativeType.Int32;
/// Reference to the [Compound] binding this type refers to.
Compound? compound;
/// Reference to the [NativeFunc] this type refers to.
NativeFunc? nativeFunc;
/// Reference to the [Typealias] this type refers to.
Typealias? typealias;
/// Reference to the [FunctionType] this type refers to.
FunctionType? functionType;
/// Reference to the [EnumClass] this type refers to.
EnumClass? enumClass;
/// For providing [SupportedNativeType] only.
final SupportedNativeType? nativeType;
/// The BroadType of this Type.
final BroadType broadType;
/// Child Type, e.g Pointer(Parent) to Int(Child), or Child Type of an Array.
final Type? child;
/// For ConstantArray and IncompleteArray type.
final int? length;
/// For storing cursor type info for an unimplemented type.
String? unimplementedReason;
Type._({
required this.broadType,
this.child,
this.compound,
this.enumClass,
this.nativeType,
this.nativeFunc,
this.typealias,
this.functionType,
this.length,
this.unimplementedReason,
});
factory Type.pointer(Type child) {
return Type._(broadType: BroadType.Pointer, child: child);
}
factory Type.compound(Compound compound) {
return Type._(broadType: BroadType.Compound, compound: compound);
}
factory Type.struct(Struc struc) {
return Type._(broadType: BroadType.Compound, compound: struc);
}
factory Type.union(Union union) {
return Type._(broadType: BroadType.Compound, compound: union);
}
factory Type.enumClass(EnumClass enumClass) {
return Type._(broadType: BroadType.Enum, enumClass: enumClass);
}
factory Type.functionType(FunctionType functionType) {
return Type._(
broadType: BroadType.FunctionType, functionType: functionType);
}
factory Type.nativeFunc(NativeFunc nativeFunc) {
return Type._(broadType: BroadType.NativeFunction, nativeFunc: nativeFunc);
}
factory Type.typealias(Typealias typealias) {
return Type._(broadType: BroadType.Typealias, typealias: typealias);
}
factory Type.nativeType(SupportedNativeType nativeType) {
return Type._(broadType: BroadType.NativeType, nativeType: nativeType);
}
factory Type.constantArray(int length, Type elementType) {
return Type._(
broadType: BroadType.ConstantArray,
child: elementType,
length: length,
);
}
factory Type.incompleteArray(Type elementType) {
return Type._(
broadType: BroadType.IncompleteArray,
child: elementType,
);
}
factory Type.boolean() {
return Type._(
broadType: BroadType.Boolean,
);
}
factory Type.unimplemented(String reason) {
return Type._(
broadType: BroadType.Unimplemented, unimplementedReason: reason);
}
factory Type.handle() {
return Type._(broadType: BroadType.Handle);
}
/// Get all dependencies of this type and save them in [dependencies].
void addDependencies(Set<Binding> dependencies) {
switch (broadType) {
case BroadType.Compound:
return compound!.addDependencies(dependencies);
case BroadType.NativeFunction:
return nativeFunc!.addDependencies(dependencies);
case BroadType.FunctionType:
return functionType!.addDependencies(dependencies);
case BroadType.Typealias:
return typealias!.addDependencies(dependencies);
case BroadType.Enum:
return enumClass!.addDependencies(dependencies);
default:
if (child != null) {
return child!.addDependencies(dependencies);
}
}
}
/// Get base type for any type.
///
/// E.g int** has base [Type] of int.
/// double[2][3] has base [Type] of double.
Type getBaseType() {
if (child != null) {
return child!.getBaseType();
} else {
return this;
}
}
/// Get base Array type.
///
/// Returns itself if it's not an Array Type.
Type getBaseArrayType() {
if (broadType == BroadType.ConstantArray ||
broadType == BroadType.IncompleteArray) {
return child!.getBaseArrayType();
} else {
return this;
}
}
/// Get base typealias type.
///
/// Returns itself if it's not a Typealias.
Type getBaseTypealiasType() {
if (broadType == BroadType.Typealias) {
return typealias!.type.getBaseTypealiasType();
} else {
return this;
}
}
/// Function to check if the dart and C type string are same.
bool sameDartAndCType(Writer w) => getCType(w) == getDartType(w);
/// Returns true if the type is a [Compound] and is incomplete.
bool get isIncompleteCompound {
final baseTypealiasType = getBaseTypealiasType();
if (baseTypealiasType == this) {
return (broadType == BroadType.Compound &&
compound != null &&
compound!.isInComplete) ||
(broadType == BroadType.ConstantArray &&
getBaseArrayType().isIncompleteCompound);
} else {
return baseTypealiasType.isIncompleteCompound;
}
}
String getCType(Writer w) {
switch (broadType) {
case BroadType.NativeType:
return '${w.ffiLibraryPrefix}.${_primitives[nativeType!]!.c}';
case BroadType.Pointer:
return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>';
case BroadType.Compound:
return compound!.name;
case BroadType.Enum:
return '${w.ffiLibraryPrefix}.${_primitives[enumNativeType]!.c}';
case BroadType.NativeFunction:
return '${w.ffiLibraryPrefix}.NativeFunction<${nativeFunc!.type.getCType(w)}>';
case BroadType
.IncompleteArray: // Array parameters are treated as Pointers in C.
return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>';
case BroadType
.ConstantArray: // Array parameters are treated as Pointers in C.
return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>';
case BroadType.Boolean: // Booleans are treated as uint8.
return '${w.ffiLibraryPrefix}.${_primitives[SupportedNativeType.Uint8]!.c}';
case BroadType.Handle:
return '${w.ffiLibraryPrefix}.Handle';
case BroadType.FunctionType:
return functionType!.getCType(w);
case BroadType.Typealias:
return typealias!.name;
case BroadType.Unimplemented:
throw UnimplementedError('C type unknown for ${broadType.toString()}');
}
}
String getDartType(Writer w) {
switch (broadType) {
case BroadType.NativeType:
return _primitives[nativeType!]!.dart;
case BroadType.Pointer:
return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>';
case BroadType.Compound:
return compound!.name;
case BroadType.Enum:
return _primitives[enumNativeType]!.dart;
case BroadType.NativeFunction:
return '${w.ffiLibraryPrefix}.NativeFunction<${nativeFunc!.type.getDartType(w)}>';
case BroadType
.IncompleteArray: // Array parameters are treated as Pointers in C.
return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>';
case BroadType
.ConstantArray: // Array parameters are treated as Pointers in C.
return '${w.ffiLibraryPrefix}.Pointer<${child!.getCType(w)}>';
case BroadType.Boolean: // Booleans are treated as uint8.
return _primitives[SupportedNativeType.Uint8]!.dart;
case BroadType.Handle:
return 'Object';
case BroadType.FunctionType:
return functionType!.getDartType(w);
case BroadType.Typealias:
// Typealias cannot be used by name in Dart types unless both the C and
// Dart type of the underlying types are same.
if (typealias!.type.sameDartAndCType(w)) {
return typealias!.name;
} else {
return typealias!.type.getDartType(w);
}
case BroadType.Unimplemented:
throw UnimplementedError(
'dart type unknown for ${broadType.toString()}');
}
}
@override
String toString() {
return 'Type: $broadType';
}
}
/// Represents a function type.
class FunctionType {
final Type returnType;
final List<Parameter> parameters;
FunctionType({
required this.returnType,
required this.parameters,
});
String getCType(Writer w, {bool writeArgumentNames = true}) {
final sb = StringBuffer();
// Write return Type.
sb.write(returnType.getCType(w));
// Write Function.
sb.write(' Function(');
sb.write(parameters.map<String>((p) {
return '${p.type.getCType(w)} ${writeArgumentNames ? p.name : ""}';
}).join(', '));
sb.write(')');
return sb.toString();
}
String getDartType(Writer w, {bool writeArgumentNames = true}) {
final sb = StringBuffer();
// Write return Type.
sb.write(returnType.getDartType(w));
// Write Function.
sb.write(' Function(');
sb.write(parameters.map<String>((p) {
return '${p.type.getDartType(w)} ${writeArgumentNames ? p.name : ""}';
}).join(', '));
sb.write(')');
return sb.toString();
}
void addDependencies(Set<Binding> dependencies) {
returnType.addDependencies(dependencies);
for (final p in parameters) {
p.type.addDependencies(dependencies);
}
}
}
/// Represents a NativeFunction<Function>.
class NativeFunc {
final Type type;
NativeFunc.fromFunctionType(FunctionType functionType)
: type = Type.functionType(functionType);
NativeFunc.fromFunctionTypealias(Typealias typealias)
: type = Type.typealias(typealias);
void addDependencies(Set<Binding> dependencies) {
type.addDependencies(dependencies);
}
}