Skip to content

Commit

Permalink
Support throws
Browse files Browse the repository at this point in the history
  • Loading branch information
liamappelbe committed Nov 29, 2024
1 parent d60f12d commit af9da2f
Show file tree
Hide file tree
Showing 27 changed files with 327 additions and 101 deletions.
9 changes: 9 additions & 0 deletions pkgs/swift2objc/lib/src/ast/_core/interfaces/can_throw.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// 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.

/// An interface to describe a Swift entity's ability to be annotated
/// with `throws`.
abstract interface class CanThrow {
abstract final bool throws;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,19 @@
// BSD-style license that can be found in the LICENSE file.

import '../shared/referred_type.dart';
import 'can_throw.dart';
import 'declaration.dart';
import 'executable.dart';
import 'parameterizable.dart';
import 'type_parameterizable.dart';

/// Describes a function-like entity.
abstract interface class FunctionDeclaration
implements Declaration, Parameterizable, Executable, TypeParameterizable {
implements
Declaration,
Parameterizable,
Executable,
TypeParameterizable,
CanThrow {
abstract final ReferredType returnType;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// BSD-style license that can be found in the LICENSE file.

/// An interface to describe a Swift entity's ability to be annotated
/// with `@objc`.
/// with `override`.
abstract interface class Overridable {
abstract final bool isOverriding;
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
// BSD-style license that can be found in the LICENSE file.

import '../shared/referred_type.dart';
import 'can_throw.dart';
import 'declaration.dart';

/// Describes a variable-like entity.
abstract interface class VariableDeclaration implements Declaration {
///
/// This declaration [CanThrow] because Swift variables can have explicit
/// getters, which can be marked with `throws`. Such variables may not have a
/// setter.
abstract interface class VariableDeclaration implements Declaration, CanThrow {
abstract final bool isConstant;
abstract final ReferredType type;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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 '../../../_core/interfaces/can_throw.dart';
import '../../../_core/interfaces/declaration.dart';
import '../../../_core/interfaces/executable.dart';
import '../../../_core/interfaces/objc_annotatable.dart';
Expand All @@ -16,7 +17,8 @@ class InitializerDeclaration
Executable,
Parameterizable,
ObjCAnnotatable,
Overridable {
Overridable,
CanThrow {
@override
String id;

Expand All @@ -29,6 +31,9 @@ class InitializerDeclaration
@override
bool isOverriding;

@override
bool throws;

bool isFailable;

@override
Expand All @@ -48,6 +53,7 @@ class InitializerDeclaration
this.statements = const [],
required this.hasObjCAnnotation,
required this.isOverriding,
required this.throws,
required this.isFailable,
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ class MethodDeclaration
@override
bool isOverriding;

@override
bool throws;

@override
List<String> statements;

Expand All @@ -53,5 +56,6 @@ class MethodDeclaration
this.statements = const [],
this.isStatic = false,
this.isOverriding = false,
this.throws = false,
}) : assert(!isStatic || !isOverriding);
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ class PropertyDeclaration implements VariableDeclaration, ObjCAnnotatable {
@override
bool isConstant;

@override
bool throws;

bool hasSetter;

PropertyStatements? getter;
Expand All @@ -42,7 +45,9 @@ class PropertyDeclaration implements VariableDeclaration, ObjCAnnotatable {
this.getter,
this.setter,
this.isStatic = false,
}) : assert(!isConstant || !hasSetter);
this.throws = false,
}) : assert(!(isConstant && hasSetter)),
assert(!(hasSetter && throws));
}

class PropertyStatements implements Executable {
Expand Down
10 changes: 9 additions & 1 deletion pkgs/swift2objc/lib/src/ast/declarations/globals/globals.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ class GlobalFunctionDeclaration implements FunctionDeclaration {
@override
List<GenericType> typeParams;

@override
bool throws;

@override
ReferredType returnType;

Expand All @@ -46,6 +49,7 @@ class GlobalFunctionDeclaration implements FunctionDeclaration {
required this.returnType,
this.typeParams = const [],
this.statements = const [],
this.throws = false,
});
}

Expand All @@ -63,10 +67,14 @@ class GlobalVariableDeclaration implements VariableDeclaration {
@override
bool isConstant;

@override
bool throws;

GlobalVariableDeclaration({
required this.id,
required this.name,
required this.type,
required this.isConstant,
});
required this.throws,
}) : assert(!(throws && !isConstant));
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,10 @@ List<String> _generateInitializer(InitializerDeclaration initializer) {

header.write('(${generateParameters(initializer.params)})');

if (initializer.throws) {
header.write(' throws');
}

return [
'$header {',
...initializer.statements.indent(),
Expand Down Expand Up @@ -116,6 +120,10 @@ List<String> _generateClassMethod(MethodDeclaration method) {
'public func ${method.name}(${generateParameters(method.params)})',
);

if (method.throws) {
header.write(' throws');
}

if (!method.returnType.sameAs(voidType)) {
header.write(' -> ${method.returnType.swiftType}');
}
Expand Down
15 changes: 7 additions & 8 deletions pkgs/swift2objc/lib/src/parser/_core/json.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
// 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:collection';
import 'dart:convert';

/// This is a helper class that helps with parsing Json values. It supports
Expand All @@ -18,13 +17,13 @@ import 'dart:convert';
/// The class is also an `Iterable` so if the json is an array, you can directly
/// iterate over it with a `for` loop. If the json isn't an array, attempting to
/// iterate over it will throw an error.
class Json extends IterableBase<Json> {
final List<String> _pathSegments;
class Json extends Iterable<Json> {
final List<String> pathSegments;
final dynamic _json;

String get path => _pathSegments.join('/');
String get path => pathSegments.join('/');

Json(this._json, [this._pathSegments = const []]);
Json(this._json, [this.pathSegments = const []]);

/// The subscript syntax is intended to access a value at a field of a map or
/// at an index if an array, and thus, the `index` parameter here can either
Expand All @@ -37,7 +36,7 @@ class Json extends IterableBase<Json> {
'Expected a map at "$path", found a ${_json.runtimeType}',
);
}
return Json(_json[index], [..._pathSegments, index]);
return Json(_json[index], [...pathSegments, index]);
}

if (index is int) {
Expand All @@ -57,7 +56,7 @@ class Json extends IterableBase<Json> {
'Invalid negative index at "$path" (supplied index: $index)',
);
}
return Json(_json[index], [..._pathSegments, '$index']);
return Json(_json[index], [...pathSegments, '$index']);
}

throw Exception(
Expand Down Expand Up @@ -115,7 +114,7 @@ class _JsonIterator implements Iterator<Json> {
_JsonIterator(this._json) : _list = _json.get();

@override
Json get current => Json(_list[_index], [..._json._pathSegments, '$_index']);
Json get current => Json(_list[_index], [..._json.pathSegments, '$_index']);

@override
bool moveNext() {
Expand Down
81 changes: 62 additions & 19 deletions pkgs/swift2objc/lib/src/parser/_core/token_list.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
// 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:meta/meta.dart';

import 'json.dart';
import 'utils.dart';

Expand All @@ -15,7 +17,7 @@ import 'utils.dart';
/// can pass it to parseType, because certain tokens get concatenated by the
/// swift compiler. This class performs that preprocessing, as well as providing
/// convenience methods for parsing, like slicing.
class TokenList {
class TokenList extends Iterable<Json> {
final List<Json> _list;
final int _start;
final int _end;
Expand All @@ -24,30 +26,55 @@ class TokenList {
: assert(0 <= _start && _start <= _end && _end <= _list.length);

factory TokenList(Json fragments) {
const splits = {
'?(': ['?', '('],
'?)': ['?', ')'],
'?, ': ['?', ', '],
') -> ': [')', '->'],
'?) -> ': ['?', ')', '->'],
};

final list = <Json>[];
for (final token in fragments) {
final split = splits[getSpellingForKind(token, 'text')];
if (split != null) {
for (final sub in split) {
list.add(Json({'kind': 'text', 'spelling': sub}));
final list = [for (final token in fragments) ...splitToken(token)];
return TokenList._(list, 0, list.length);
}

@visibleForTesting
static Iterable<Json> splitToken(Json token) sync* {
const splittables = ['(', ')', '?', ',', '->'];
Json textToken(String text) =>
Json({'kind': 'text', 'spelling': text}, token.pathSegments);

final text = getSpellingForKind(token, 'text')?.trim();
if (text == null) {
// Not a text token. Pass it though unchanged.
yield token;
return;
}

if (text.isEmpty) {
// Input text token was nothing but whitespace. The loop below would yield
// nothing, but we still need it as a separator.
yield textToken(text);
return;
}

var suffix = text;
while (true) {
var any = false;
for (final prefix in splittables) {
if (suffix.startsWith(prefix)) {
yield textToken(prefix);
suffix = suffix.substring(prefix.length).trim();
any = true;
break;
}
} else {
list.add(token);
}
if (!any) {
// Remaining text isn't splittable.
if (suffix.isNotEmpty) yield textToken(suffix);
break;
}
}
return TokenList._(list, 0, list.length);
}

@override
int get length => _end - _start;
bool get isEmpty => length == 0;

@override
Iterator<Json> get iterator => _TokenListIterator(this);

Json operator [](int index) => _list[index + _start];

int indexWhere(bool Function(Json element) test) {
Expand All @@ -63,3 +90,19 @@ class TokenList {
@override
String toString() => _list.getRange(_start, _end).toString();
}

class _TokenListIterator implements Iterator<Json> {
final TokenList _list;
var _index = -1;

_TokenListIterator(this._list);

@override
Json get current => _list[_index];

@override
bool moveNext() {
_index++;
return _index < _list.length;
}
}
2 changes: 1 addition & 1 deletion pkgs/swift2objc/lib/src/parser/_core/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ ReferredType parseTypeAfterSeparator(
) {
// fragments = [..., ': ', type tokens...]
final separatorIndex =
fragments.indexWhere((token) => matchFragment(token, 'text', ': '));
fragments.indexWhere((token) => matchFragment(token, 'text', ':'));
final (type, suffix) =
parseType(symbolgraph, fragments.slice(separatorIndex + 1));
assert(suffix.isEmpty, '$suffix');
Expand Down
Loading

0 comments on commit af9da2f

Please sign in to comment.