Skip to content

Code generation for ObjC protocols #1175

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 40 commits into from
Jun 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
3fe4c5c
Use NSProxy to implement protocols
liamappelbe May 14, 2024
c7115d5
fix analysis
liamappelbe May 14, 2024
b652b04
Update pubspec
liamappelbe May 14, 2024
d533c94
Fix changelog
liamappelbe May 14, 2024
a636316
Handle unimplemented methods
liamappelbe May 15, 2024
819cc1a
Merge branch 'main' into proto
liamappelbe May 15, 2024
9dc396e
Multiple protocol implementation
liamappelbe May 15, 2024
457406d
Fix tests
liamappelbe May 16, 2024
a82a6ed
Fix and test memory management
liamappelbe May 16, 2024
9e65a95
Separate building into DartProxyBuilder and test multithreading
liamappelbe May 17, 2024
d5a4825
Fix analysis
liamappelbe May 17, 2024
303c3bd
WIP protocol codegen
liamappelbe May 21, 2024
cc48431
Merge branch 'main' into protogen
liamappelbe May 21, 2024
03d0469
Protocol parsing done
liamappelbe May 23, 2024
e5c914f
Code gen done
liamappelbe May 24, 2024
e339326
Tests
liamappelbe May 24, 2024
45da2fb
Merge branch 'main' into proto
liamappelbe May 24, 2024
0f3e08b
Fix analysis
liamappelbe May 24, 2024
d9cf7bb
Merge branch 'main' into proto
liamappelbe May 24, 2024
bd72c4e
Merge branch 'proto' into protogen
liamappelbe May 24, 2024
2db2be0
Readmes and changelogs
liamappelbe May 26, 2024
2bc8595
Merge branch 'main' into protogen
liamappelbe May 28, 2024
097ebf5
Fix analysis
liamappelbe May 28, 2024
a139649
Rename build to implement, and add addToBuilder method
liamappelbe May 28, 2024
4ded588
Fix bugs during cupertino_http's codegen
liamappelbe May 29, 2024
708cb41
More bug cupertino fixes
liamappelbe May 30, 2024
0451c57
format
liamappelbe May 30, 2024
59df4e5
Merge branch 'main' into protogen
liamappelbe May 30, 2024
ed0c3f5
Merge branch 'main' into protogen
liamappelbe May 31, 2024
bdaadf9
More bugfixes for cupertino_http
liamappelbe Jun 5, 2024
c779543
Fix analysis
liamappelbe Jun 5, 2024
93aa9ff
fmt
liamappelbe Jun 5, 2024
c8b7625
Merge branch 'main' into protogen
liamappelbe Jun 17, 2024
14b6a81
Refactor method implementation flow to eliminate extra void* arg
liamappelbe Jun 17, 2024
a992a08
Fix analysis
liamappelbe Jun 17, 2024
13af37d
Merge branch 'main' into protogen
liamappelbe Jun 18, 2024
e9a6274
Daco's comments
liamappelbe Jun 18, 2024
7f1ce82
fmt
liamappelbe Jun 18, 2024
e81fd82
Revert spurious changes
liamappelbe Jun 18, 2024
3c4a2b0
Fix test
liamappelbe Jun 18, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions pkgs/ffigen/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
To silence the warning set config `silence-enum-warning` to `true`.
- Rename ObjC interface methods that clash with type names. Fixes
https://github.com/dart-lang/native/issues/1007.
- Added support for implementing ObjC protocols from Dart. Use the
`objc-protocols` config option to generate bindings for a protocol.

## 12.0.0

Expand Down
15 changes: 10 additions & 5 deletions pkgs/ffigen/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -666,11 +666,12 @@ import:
<tbody>
<tr>
<td>
objc-interfaces
objc-interfaces<br><br>objc-protocols
</td>
<td>
Filters for interface declarations. This option works the same as other
declaration filters like `functions` and `structs`.
Filters for Objective C interface and protocol declarations. This option
works the same as other declaration filters like `functions` and
`structs`.
</td>
<td>

Expand All @@ -687,17 +688,21 @@ objc-interfaces:
rename:
# Removes '_' prefix from interface names.
'_(.*)': '$1'
objc-protocols:
include:
# Generates bindings for a specific protocol.
- MyProtocol
```

</td>
</tr>

<tr>
<td>
objc-interfaces -> module
objc-interfaces -> module<br><br>objc-protocols -> module
</td>
<td>
Adds a module prefix to the class name when loading the class
Adds a module prefix to the interface/protocol name when loading it
from the dylib. This is only relevent for ObjC headers that are generated
wrappers for a Swift library. See example/swift for more information.
</td>
Expand Down
25 changes: 23 additions & 2 deletions pkgs/ffigen/ffigen.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,28 @@
"$ref": "#/$defs/memberRename"
},
"module": {
"$ref": "#/$defs/objcInterfaceModule"
"$ref": "#/$defs/objcModule"
}
}
},
"objc-protocols": {
"type": "object",
"additionalProperties": false,
"properties": {
"include": {
"$ref": "#/$defs/fullMatchOrRegexpList"
},
"exclude": {
"$ref": "#/$defs/fullMatchOrRegexpList"
},
"rename": {
"$ref": "#/$defs/rename"
},
"member-rename": {
"$ref": "#/$defs/memberRename"
},
"module": {
"$ref": "#/$defs/objcModule"
}
}
},
Expand Down Expand Up @@ -495,7 +516,7 @@
"opaque"
]
},
"objcInterfaceModule": {
"objcModule": {
"type": "object",
"patternProperties": {
".*": {
Expand Down
2 changes: 2 additions & 0 deletions pkgs/ffigen/lib/src/code_generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ export 'code_generator/native_type.dart';
export 'code_generator/objc_block.dart';
export 'code_generator/objc_built_in_functions.dart';
export 'code_generator/objc_interface.dart';
export 'code_generator/objc_methods.dart';
export 'code_generator/objc_nullable.dart';
export 'code_generator/objc_protocol.dart';
export 'code_generator/pointer.dart';
export 'code_generator/struct.dart';
export 'code_generator/type.dart';
Expand Down
1 change: 1 addition & 0 deletions pkgs/ffigen/lib/src/code_generator/binding_string.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,5 +24,6 @@ enum BindingStringType {
enum_,
typeDef,
objcInterface,
objcProtocol,
objcBlock,
}
23 changes: 18 additions & 5 deletions pkgs/ffigen/lib/src/code_generator/compound.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ abstract class Compound extends BindingType {
bool get isStruct => compoundType == CompoundType.struct;
bool get isUnion => compoundType == CompoundType.union;

ObjCBuiltInFunctions? objCBuiltInFunctions;

Compound({
super.usr,
super.originalName,
Expand All @@ -44,6 +46,7 @@ abstract class Compound extends BindingType {
super.dartDoc,
List<Member>? members,
super.isInternal,
this.objCBuiltInFunctions,
}) : members = members ?? [];

factory Compound.fromType({
Expand All @@ -55,6 +58,7 @@ abstract class Compound extends BindingType {
int? pack,
String? dartDoc,
List<Member>? members,
required ObjCBuiltInFunctions objCBuiltInFunctions,
}) {
switch (type) {
case CompoundType.struct:
Expand All @@ -66,6 +70,7 @@ abstract class Compound extends BindingType {
pack: pack,
dartDoc: dartDoc,
members: members,
objCBuiltInFunctions: objCBuiltInFunctions,
);
case CompoundType.union:
return Union(
Expand All @@ -76,6 +81,7 @@ abstract class Compound extends BindingType {
pack: pack,
dartDoc: dartDoc,
members: members,
objCBuiltInFunctions: objCBuiltInFunctions,
);
}
}
Expand All @@ -88,8 +94,17 @@ abstract class Compound extends BindingType {
return type.getCType(w);
}

bool get _isBuiltIn =>
objCBuiltInFunctions?.isBuiltInInterface(originalName) ?? false;

@override
BindingString toBindingString(Writer w) {
final bindingType =
isStruct ? BindingStringType.struct : BindingStringType.union;
if (_isBuiltIn) {
return BindingString(type: bindingType, string: '');
}

final s = StringBuffer();
final enclosingClassName = name;
if (dartDoc != null) {
Expand Down Expand Up @@ -135,14 +150,12 @@ abstract class Compound extends BindingType {
}
s.write('}\n\n');

return BindingString(
type: isStruct ? BindingStringType.struct : BindingStringType.union,
string: s.toString());
return BindingString(type: bindingType, string: s.toString());
}

@override
void addDependencies(Set<Binding> dependencies) {
if (dependencies.contains(this)) return;
if (dependencies.contains(this) || _isBuiltIn) return;

dependencies.add(this);
for (final m in members) {
Expand All @@ -154,7 +167,7 @@ abstract class Compound extends BindingType {
bool get isIncompleteCompound => isIncomplete;

@override
String getCType(Writer w) => name;
String getCType(Writer w) => _isBuiltIn ? '${w.objcPkgPrefix}.$name' : name;

@override
String getNativeType({String varName = ''}) => '$originalName $varName';
Expand Down
40 changes: 31 additions & 9 deletions pkgs/ffigen/lib/src/code_generator/objc_block.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,37 @@

import 'package:ffigen/src/code_generator.dart';
import 'package:ffigen/src/config_provider/config_types.dart';
import 'package:ffigen/src/header_parser/data.dart' show bindingsIndex;

import 'binding_string.dart';
import 'writer.dart';

class ObjCBlock extends BindingType {
final Type returnType;
final List<Type> argTypes;

Func? _wrapListenerBlock;

ObjCBlock({
required String usr,
factory ObjCBlock({
required Type returnType,
required List<Type> argTypes,
}) : this._(
usr: usr,
name: _getBlockName(returnType, argTypes),
returnType: returnType,
argTypes: argTypes,
);
}) {
final usr = _getBlockUsr(returnType, argTypes);

final oldBlock = bindingsIndex.getSeenObjCBlock(usr);
if (oldBlock != null) {
return oldBlock;
}

final block = ObjCBlock._(
usr: usr,
name: _getBlockName(returnType, argTypes),
returnType: returnType,
argTypes: argTypes,
);
bindingsIndex.addObjCBlockToSeen(usr, block);

return block;
}

ObjCBlock._({
required String super.usr,
Expand All @@ -42,6 +53,17 @@ class ObjCBlock extends BindingType {
type.toString().replaceAll(_illegalNameChar, '');
static final _illegalNameChar = RegExp(r'[^0-9a-zA-Z]');

static String _getBlockUsr(Type returnType, List<Type> argTypes) {
// Create a fake USR code for the block. This code is used to dedupe blocks
// with the same signature.
final usr = StringBuffer();
usr.write('objcBlock: ${returnType.cacheKey()}');
for (final type in argTypes) {
usr.write(' ${type.cacheKey()}');
}
return usr.toString();
}

bool get hasListener => returnType == voidType;

@override
Expand Down
25 changes: 24 additions & 1 deletion pkgs/ffigen/lib/src/code_generator/objc_built_in_functions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,16 @@ class ObjCBuiltInFunctions {
static const newPointerBlock = ObjCImport('newPointerBlock');
static const newClosureBlock = ObjCImport('newClosureBlock');
static const getBlockClosure = ObjCImport('getBlockClosure');
static const getProtocolMethodSignature =
ObjCImport('getProtocolMethodSignature');
static const getProtocol = ObjCImport('getProtocol');
static const objectBase = ObjCImport('ObjCObjectBase');
static const blockBase = ObjCImport('ObjCBlockBase');
static const protocolMethod = ObjCImport('ObjCProtocolMethod');
static const protocolListenableMethod =
ObjCImport('ObjCProtocolListenableMethod');
static const protocolBuilder = ObjCImport('ObjCProtocolBuilder');
static const dartProxy = ObjCImport('DartProxy');

// Keep in sync with pkgs/objective_c/ffigen_objc.yaml.
static const builtInInterfaces = {
Expand All @@ -45,12 +53,12 @@ class ObjCBuiltInFunctions {
'NSMutableArray',
'NSMutableData',
'NSMutableDictionary',
'NSMutableIndexSet',
'NSMutableSet',
'NSMutableString',
'NSNotification',
'NSNumber',
'NSObject',
'NSProgress',
'NSProxy',
'NSSet',
'NSString',
Expand All @@ -59,9 +67,18 @@ class ObjCBuiltInFunctions {
'NSValue',
'Protocol',
};
static const builtInCompounds = {
'NSFastEnumerationState',
'NSRange',
};

// TODO(https://github.com/dart-lang/native/issues/1173): Ideally this check
// would be based on more than just the name.
bool isBuiltInInterface(String name) =>
!generateForPackageObjectiveC && builtInInterfaces.contains(name);
bool isBuiltInCompound(String name) =>
!generateForPackageObjectiveC && builtInCompounds.contains(name);
bool isNSObject(String name) => name == 'NSObject';

// We need to load a separate instance of objc_msgSend for each signature. If
// the return type is a struct, we need to use objc_msgSend_stret instead, and
Expand Down Expand Up @@ -96,6 +113,12 @@ class ObjCBuiltInFunctions {
sel.addDependencies(dependencies);
}
}

static bool isInstanceType(Type type) {
if (type is ObjCInstanceType) return true;
final baseType = type.typealiasType;
return baseType is ObjCNullable && baseType.child is ObjCInstanceType;
}
}

/// A function, global variable, or helper type defined in package:objective_c.
Expand Down
Loading