blob: 310fef16ecd41a6c07fc1079aa08699d5174fdd0 [file] [log] [blame]
// 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:ffigen/src/code_generator.dart';
import 'binding_string.dart';
import 'writer.dart';
class ObjCBlock extends BindingType {
final Type returnType;
final List<Type> argTypes;
final ObjCBuiltInFunctions builtInFunctions;
ObjCBlock({
required String usr,
required String name,
required this.returnType,
required this.argTypes,
required this.builtInFunctions,
}) : super(
usr: usr,
originalName: name,
name: name,
);
@override
BindingString toBindingString(Writer w) {
final s = StringBuffer();
builtInFunctions.ensureBlockUtilsExist(w, s);
final params = <Parameter>[];
for (int i = 0; i < argTypes.length; ++i) {
params.add(Parameter(name: 'arg$i', type: argTypes[i]));
}
final isVoid = returnType == NativeType(SupportedNativeType.Void);
final voidPtr = PointerType(voidType).getCType(w);
final blockPtr = PointerType(builtInFunctions.blockStruct);
final funcType = FunctionType(returnType: returnType, parameters: params);
final natFnType = NativeFunc(funcType);
final natFnPtr = PointerType(natFnType).getCType(w);
final funcPtrTrampoline =
w.topLevelUniqueNamer.makeUnique('_${name}_fnPtrTrampoline');
final closureTrampoline =
w.topLevelUniqueNamer.makeUnique('_${name}_closureTrampoline');
final registerClosure =
w.topLevelUniqueNamer.makeUnique('_${name}_registerClosure');
final closureRegistry =
w.topLevelUniqueNamer.makeUnique('_${name}_closureRegistry');
final closureRegistryIndex =
w.topLevelUniqueNamer.makeUnique('_${name}_closureRegistryIndex');
final trampFuncType = FunctionType(
returnType: returnType,
parameters: [Parameter(type: blockPtr, name: 'block'), ...params]);
final natTrampFnType = NativeFunc(trampFuncType);
// Write the function pointer based trampoline function.
s.write(returnType.getDartType(w));
s.write(' $funcPtrTrampoline(${blockPtr.getCType(w)} block');
for (int i = 0; i < params.length; ++i) {
s.write(', ${params[i].type.getDartType(w)} ${params[i].name}');
}
s.write(') {\n');
s.write(' ${isVoid ? '' : 'return '}block.ref.target.cast<'
'${natFnType.getDartType(w)}>().asFunction<'
'${funcType.getDartType(w)}>()(');
for (int i = 0; i < params.length; ++i) {
s.write('${i == 0 ? '' : ', '}${params[i].name}');
}
s.write(');\n');
s.write('}\n');
// Write the closure registry function.
s.write('''
final $closureRegistry = <int, Function>{};
int $closureRegistryIndex = 0;
$voidPtr $registerClosure(Function fn) {
final id = ++$closureRegistryIndex;
$closureRegistry[id] = fn;
return $voidPtr.fromAddress(id);
}
''');
// Write the closure based trampoline function.
s.write(returnType.getDartType(w));
s.write(' $closureTrampoline(${blockPtr.getCType(w)} block');
for (int i = 0; i < params.length; ++i) {
s.write(', ${params[i].type.getDartType(w)} ${params[i].name}');
}
s.write(') {\n');
s.write(' ${isVoid ? '' : 'return '}$closureRegistry['
'block.ref.target.address]!(');
for (int i = 0; i < params.length; ++i) {
s.write('${i == 0 ? '' : ', '}${params[i].name}');
}
s.write(');\n');
s.write('}\n');
// Write the wrapper class.
final defaultValue = returnType.getDefaultValue(w, '_lib');
final exceptionalReturn = defaultValue == null ? '' : ', $defaultValue';
s.write('''
class $name extends _ObjCBlockBase {
$name._(${blockPtr.getCType(w)} id, ${w.className} lib) :
super._(id, lib, retain: false, release: true);
/// Creates a block from a C function pointer.
$name.fromFunctionPointer(${w.className} lib, $natFnPtr ptr) :
this._(lib.${builtInFunctions.newBlock.name}(
_cFuncTrampoline ??= ${w.ffiLibraryPrefix}.Pointer.fromFunction<
${trampFuncType.getCType(w)}>($funcPtrTrampoline
$exceptionalReturn).cast(), ptr.cast()), lib);
static $voidPtr? _cFuncTrampoline;
/// Creates a block from a Dart function.
$name.fromFunction(${w.className} lib, ${funcType.getDartType(w)} fn) :
this._(lib.${builtInFunctions.newBlock.name}(
_dartFuncTrampoline ??= ${w.ffiLibraryPrefix}.Pointer.fromFunction<
${trampFuncType.getCType(w)}>($closureTrampoline
$exceptionalReturn).cast(), $registerClosure(fn)), lib);
static $voidPtr? _dartFuncTrampoline;
''');
// Call method.
s.write(' ${returnType.getDartType(w)} call(');
for (int i = 0; i < params.length; ++i) {
s.write('${i == 0 ? '' : ', '}${params[i].type.getDartType(w)}');
s.write(' ${params[i].name}');
}
s.write(''') {
${isVoid ? '' : 'return '}_id.ref.invoke.cast<
${natTrampFnType.getCType(w)}>().asFunction<
${trampFuncType.getDartType(w)}>()(_id''');
for (int i = 0; i < params.length; ++i) {
s.write(', ${params[i].name}');
}
s.write(''');
}''');
// Get the pointer to the underlying block.
s.write(' ${blockPtr.getCType(w)} get pointer => _id;\n');
s.write('}\n');
return BindingString(
type: BindingStringType.objcBlock, string: s.toString());
}
@override
void addDependencies(Set<Binding> dependencies) {
if (dependencies.contains(this)) return;
dependencies.add(this);
returnType.addDependencies(dependencies);
for (final t in argTypes) {
t.addDependencies(dependencies);
}
builtInFunctions.addBlockDependencies(dependencies);
}
@override
String getCType(Writer w) =>
PointerType(builtInFunctions.blockStruct).getCType(w);
@override
String toString() => '($returnType (^)(${argTypes.join(', ')}))';
}