forked from dart-lang/native
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathprotocol_builder.dart
More file actions
194 lines (173 loc) · 6.86 KB
/
Copy pathprotocol_builder.dart
File metadata and controls
194 lines (173 loc) · 6.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
// Copyright (c) 2024, 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 'dart:ffi';
import 'dart:isolate';
import 'dart:math';
import 'package:ffi/ffi.dart';
import 'c_bindings_generated.dart' as c;
import 'internal.dart'
show FailedToLoadProtocolMethodException, GetProtocolName, ObjCBlockBase;
import 'objective_c_bindings_generated.dart' as objc;
import 'runtime_bindings_generated.dart' as r;
import 'selector.dart';
/// Helper class for building Objective C objects that implement protocols.
class ObjCProtocolBuilder {
final objc.DartProtocolBuilder _builder;
var _built = false;
objc.DartProtocolBuilder get builder => _builder;
ObjCProtocolBuilder({String debugName = 'DOBJCDartProtocol'})
: _builder = _createBuilder(debugName);
/// Add a method implementation to the protocol.
///
/// It is not recommended to call this method directly. Instead, use the
/// implement methods on [ObjCProtocolMethod] and its subclasses.
///
/// Note: You cannot call this method after you have called [build].
void implementMethod(
Pointer<r.ObjCSelector> sel,
Pointer<Char> signature,
Pointer<Void> trampoline,
ObjCBlockBase block,
) {
if (_built) {
throw StateError('Protocol is already built');
}
final blockRef = block.ref;
_builder.implementMethod(
sel,
withBlock: blockRef.pointer.cast(),
withTrampoline: trampoline,
withSignature: signature,
);
}
/// Builds the object.
///
/// This can be called multiple times to construct multiple object instances
/// that all implement the same protocol methods using the same functions.
objc.NSObject build({bool keepIsolateAlive = true}) {
if (!_built) {
_builder.registerClass();
_built = true;
}
var disposePort = c.ILLEGAL_PORT;
if (keepIsolateAlive) {
late final RawReceivePort keepAlivePort;
keepAlivePort = RawReceivePort((_) => keepAlivePort.close());
disposePort = keepAlivePort.sendPort.nativePort;
}
return _builder.buildInstance(disposePort);
}
/// Add the [protocol] to this implementation.
///
/// This essentially declares that the implementation implements the protocol.
/// There is no automatic check that ensures that the implementation actually
/// implements all the methods of the protocol.
void addProtocol(objc.Protocol protocol) => _builder.addProtocol(protocol);
static final _rand = Random();
static objc.DartProtocolBuilder _createBuilder(String debugName) {
final name = '${debugName}_${_rand.nextInt(1 << 32)}'.toNativeUtf8();
final builder = objc.DartProtocolBuilder.alloc().initWithClassName(
name.cast(),
);
calloc.free(name);
return builder;
}
}
/// A method in an ObjC protocol.
///
/// Do not try to construct this class directly. The recommended way of getting
/// a method object is to use FFIgen to generate bindings for the protocol you
/// want to implement. The generated bindings will include a
/// [ObjCProtocolMethod] for each method of the protocol.
class ObjCProtocolMethod<T extends Function> {
final Pointer<r.ObjCProtocolImpl> _proto;
final Pointer<r.ObjCSelector> _sel;
final Pointer<Void> _trampoline;
final Pointer<Char>? _signature;
final ObjCBlockBase Function(T) _createBlock;
/// Only for use by FFIgen bindings.
ObjCProtocolMethod(
this._proto,
this._sel,
this._trampoline,
this._signature,
this._createBlock,
);
/// Implement this method on the protocol [builder] using a Dart [function].
///
/// The implemented method must be invoked by ObjC code running on the same
/// thread as the isolate that called implementMethod. Invoking the method on
/// the wrong thread will result in a crash.
///
/// Note: You cannot call this method after you have called `builder.build`.
void implement(ObjCProtocolBuilder builder, T? function) {
if (function != null) {
implementWithBlock(builder, _createBlock(function));
}
}
/// Implement this method on the protocol [builder] using an ObjC [block].
///
/// **IMPORTANT**: The [block] must have the same signature as the method,
/// but with an extra `void*` argument as the first parameter, before all the
/// method parameters. Most users should use one of the other `implement`
/// methods, which handles this signature change automatically.
///
/// Note: You cannot call this method after you have called `builder.build`.
void implementWithBlock(ObjCProtocolBuilder builder, ObjCBlockBase block) =>
builder.implementMethod(_sel, _sig, _trampoline, block);
bool get isAvailable => _signature != null;
Pointer<Char> get _sig {
final sig = _signature;
if (sig != null) return sig;
throw FailedToLoadProtocolMethodException(_proto.name, _sel.toDartString());
}
}
/// A method in an ObjC protocol that can be implemented as a listener.
///
/// Do not try to construct this class directly. The recommended way of getting
/// a method object is to use FFIgen to generate bindings for the protocol you
/// want to implement. The generated bindings will include a
/// [ObjCProtocolMethod] for each method of the protocol.
class ObjCProtocolListenableMethod<T extends Function>
extends ObjCProtocolMethod<T> {
final ObjCBlockBase Function(T) _createListenerBlock;
final ObjCBlockBase Function(T) _createBlockingBlock;
/// Only for use by FFIgen bindings.
ObjCProtocolListenableMethod(
super._proto,
super._sel,
super._trampoline,
super._signature,
super._createBlock,
this._createListenerBlock,
this._createBlockingBlock,
);
/// Implement this method on the protocol [builder] as a listener using a Dart
/// [function].
///
/// This is based on FFI's NativeCallable.listener, and has the same
/// capabilities and limitations. This method can be invoked by ObjC from any
/// thread, but only supports void functions, and is not run synchronously.
/// See NativeCallable.listener for more details.
///
/// Note: You cannot call this method after you have called `builder.build`.
void implementAsListener(ObjCProtocolBuilder builder, T? function) {
if (function != null) {
implementWithBlock(builder, _createListenerBlock(function));
}
}
/// Implement this method on the protocol [builder] as a blocking listener
/// using a Dart [function].
///
/// This callback can be invoked from any native thread, and will block the
/// caller until the callback is handled by the Dart isolate that implemented
/// the method. Async functions are not supported.
///
/// Note: You cannot call this method after you have called `builder.build`.
void implementAsBlocking(ObjCProtocolBuilder builder, T? function) {
if (function != null) {
implementWithBlock(builder, _createBlockingBlock(function));
}
}
}