Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 32 additions & 0 deletions pkgs/swift2objc/lib/src/ast/_core/shared/referred_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -143,3 +143,35 @@ class OptionalType extends AstNode implements ReferredType {
visitor.visit(child);
}
}

/// An inout type, like Swift's pass-by-reference params. Eg `inout Int`.
class InoutType extends AstNode implements ReferredType {
final ReferredType child;

@override
bool get isObjCRepresentable => false;

@override
String get swiftType => child.swiftType;

@override
bool _sameAs(ReferredType other) =>
other is InoutType && child.sameAs(other.child);

@override
ReferredType get aliasedType => InoutType(child.aliasedType);

InoutType(this.child);

@override
String toString() => 'inout $child';

@override
void visit(Visitation visitation) => visitation.visitInoutType(this);

@override
void visitChildren(Visitor visitor) {
super.visitChildren(visitor);
visitor.visit(child);
}
}
1 change: 1 addition & 0 deletions pkgs/swift2objc/lib/src/ast/visitor.dart
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ abstract class Visitation {
void visitDeclaredType(DeclaredType node) => visitReferredType(node);
void visitGenericType(GenericType node) => visitReferredType(node);
void visitOptionalType(OptionalType node) => visitReferredType(node);
void visitInoutType(InoutType node) => visitReferredType(node);
void visitDeclaration(Declaration node) => visitAstNode(node);
void visitBuiltInDeclaration(BuiltInDeclaration node) =>
visitDeclaration(node);
Expand Down
5 changes: 2 additions & 3 deletions pkgs/swift2objc/lib/src/parser/parsers/parse_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,8 @@ typedef PrefixParselet =
if (_tokenId(fragments[0]) == 'text: ') {
fragments = fragments.slice(1);
}
// TODO(https://github.com/dart-lang/native/issues/1754): Mark the returned
// type as inout (maybe wrap it in a new InOutType AST node?).
return parseType(context, symbolgraph, fragments);
final (type, suffix) = parseType(context, symbolgraph, fragments);
return (InoutType(type), suffix);
}

Map<String, PrefixParselet> _prefixParsets = {
Expand Down
16 changes: 16 additions & 0 deletions pkgs/swift2objc/lib/src/transformer/_core/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ import 'unique_namer.dart';
TransformationState state, {
bool shouldWrapPrimitives = false,
}) {
if (type is InoutType) {
final (newValue, newType) = maybeWrapValue(
type.child,
value,
globalNamer,
state,
shouldWrapPrimitives: shouldWrapPrimitives,
);
return (newValue, InoutType(newType));
}

final (wrappedPrimitiveType, returnsWrappedPrimitive) =
maybeGetPrimitiveWrapper(type, shouldWrapPrimitives, state);
if (returnsWrappedPrimitive) {
Expand Down Expand Up @@ -78,6 +89,11 @@ import 'unique_namer.dart';
ReferredType type,
String value,
) {
if (type is InoutType) {
final (newValue, newType) = maybeUnwrapValue(type.child, value);
return (newValue, InoutType(newType));
}

if (!type.isObjCRepresentable) {
return (value, type);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -165,11 +165,14 @@ String generateInvocationParams(
);

assert(unwrappedType.sameAs(originalParam.type));
final invocationValue = originalParam.type is InoutType
? '&$unwrappedParamValue'
: unwrappedParamValue;

argumentsList.add(
originalParam.name.isEmpty || originalParam.name == '_'
? unwrappedParamValue
: '${originalParam.name}: $unwrappedParamValue',
? invocationValue
: '${originalParam.name}: $invocationValue',
);
}
return argumentsList.join(', ');
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import '../../ast/_core/interfaces/declaration.dart';
import '../../ast/_core/shared/referred_type.dart';
import '../../ast/declarations/typealias_declaration.dart';
import '../_core/primitive_wrappers.dart';
import '../_core/unique_namer.dart';
import '../transform.dart';

Expand All @@ -16,6 +17,18 @@ ReferredType transformReferredType(
UniqueNamer globalNamer,
TransformationState state,
) {
if (type is InoutType) {
final (wrappedPrimitive, hasWrappedPrimitive) = maybeGetPrimitiveWrapper(
type.child,
true,
state,
);
if (hasWrappedPrimitive) {
return InoutType(wrappedPrimitive);
}
return InoutType(transformReferredType(type.child, globalNamer, state));
}

if (type.isObjCRepresentable) return type;

if (type is GenericType) {
Expand Down
21 changes: 21 additions & 0 deletions pkgs/swift2objc/test/integration/inout_input.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Foundation

public class MyClass {
public func update(_ value: inout Int) {
value += 1
}
}

public class MyOtherClass {
public init() {}
}

public func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temp = a
a = b
b = temp
}

public func replaceOther(_ value: inout MyOtherClass) {
value = MyOtherClass()
}
50 changes: 50 additions & 0 deletions pkgs/swift2objc/test/integration/inout_output.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Test preamble text

import Foundation

@objc public class GlobalsWrapper: NSObject {
@objc static public func swapTwoIntsWrapper(_ a: IntWrapper, _ b: IntWrapper) {
return swapTwoInts(&a.wrappedInstance, &b.wrappedInstance)
}

@objc static public func replaceOtherWrapper(_ value: MyOtherClassWrapper) {
return replaceOther(&value.wrappedInstance)
}

}

@objc public class MyOtherClassWrapper: NSObject {
var wrappedInstance: MyOtherClass

init(_ wrappedInstance: MyOtherClass) {
self.wrappedInstance = wrappedInstance
}

@objc override public init() {
wrappedInstance = MyOtherClass()
}

}

@objc public class MyClassWrapper: NSObject {
var wrappedInstance: MyClass

init(_ wrappedInstance: MyClass) {
self.wrappedInstance = wrappedInstance
}

@objc public func update(_ value: IntWrapper) {
return wrappedInstance.update(&value.wrappedInstance)
}

}

@objc public class IntWrapper: NSObject {
var wrappedInstance: Int

init(_ wrappedInstance: Int) {
self.wrappedInstance = wrappedInstance
}

}

62 changes: 62 additions & 0 deletions pkgs/swift2objc/test/unit/parse_type_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,68 @@ void main() {
expect(remaining.length, 0);
});

test('Inout', () {
final fragments = Json(
jsonDecode('''
[
{
"kind": "keyword",
"spelling": "inout"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "typeIdentifier",
"spelling": "Int",
"preciseIdentifier": "s:Si"
}
]
'''),
);

final (type, remaining) = parseType(
context,
parsedSymbols,
TokenList(fragments),
);

expect(type.sameAs(InoutType(intType)), isTrue);
expect(remaining.length, 0);
});

test('Inout non-primitive', () {
final fragments = Json(
jsonDecode('''
[
{
"kind": "keyword",
"spelling": "inout"
},
{
"kind": "text",
"spelling": " "
},
{
"kind": "typeIdentifier",
"spelling": "Foo",
"preciseIdentifier": "Foo"
}
]
'''),
);

final (type, remaining) = parseType(
context,
parsedSymbols,
TokenList(fragments),
);

expect(type.sameAs(InoutType(classFoo.asDeclaredType)), isTrue);
expect(remaining.length, 0);
});

test('Empty tuple', () {
final fragments = Json(
jsonDecode('''
Expand Down