Skip to content

Commit

Permalink
Use extension types and generics internally (#184)
Browse files Browse the repository at this point in the history
Migrate @staticInterop to extension types, which enables us
to use generic arguments to JSArray and JSPromise.
  • Loading branch information
srujzs authored Feb 23, 2024
1 parent 975e55c commit d96c01d
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 155 deletions.
22 changes: 8 additions & 14 deletions tool/generator/filesystem_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,31 +2,25 @@
// 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.

// TODO(srujzs): Remove this workaround once we use an SDK version that contains
// https://github.com/dart-lang/sdk/issues/54801.
@JS()
library;

import 'dart:js_interop';

@JS()
external FileSystem get fs;

@JS()
@anonymous
@staticInterop
class JSMkdirOptions {
extension type JSMkdirOptions._(JSObject _) implements JSObject {
external factory JSMkdirOptions({JSBoolean? recursive});
}

@JS()
@anonymous
@staticInterop
class JSReadFileOptions {
extension type JSReadFileOptions._(JSObject _) implements JSObject {
external factory JSReadFileOptions({JSString? encoding});
}

@JS()
@staticInterop
class FileSystem {}

// TODO(joshualitt): Replace `void` with `JSVoid`
extension FileSystemExtension on FileSystem {
extension type FileSystem._(JSObject _) implements JSObject {
external JSBoolean existsSync(JSString path);

@JS('mkdirSync')
Expand Down
17 changes: 9 additions & 8 deletions tool/generator/generate_bindings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,21 @@ import 'dart:js_interop';

import 'translator.dart';
import 'util.dart';
import 'webidl_api.dart' as webidl;
import 'webref_css_api.dart';
import 'webref_idl_api.dart';

/// Generate CSS property names for setting / getting CSS properties in JS.
Future<List<String>> _generateCSSStyleDeclarations() async {
final cssStyleDeclarations = <String>{};
final array = objectEntries(await css.listAll().toDart as JSObject);
final array = objectEntries(await css.listAll().toDart);
for (var i = 0; i < array.length; i++) {
final entry = array[i] as JSArray;
final data = entry[1] as CSSEntries;
final entry = array[i] as JSArray<CSSEntries>;
final data = entry[1];
final properties = data.properties;
if (properties != null) {
for (var j = 0; j < properties.length; j++) {
final property = properties[j] as CSSEntry;
final property = properties[j];
// There are three cases for [styleDeclaration]:
// 1) Length == 1, a single word CSS property.
// 2) Length == 2, a kebab case property + a camel case property.
Expand All @@ -32,7 +33,7 @@ Future<List<String>> _generateCSSStyleDeclarations() async {
}
// For now we ignore browser specific properties.
if (length == 3) continue;
final style = (styleDeclaration[length - 1] as JSString).toDart;
final style = styleDeclaration[length - 1].toDart;
if (style.contains('-')) {
throw Exception('Unexpected style declaration $styleDeclaration');
}
Expand All @@ -49,11 +50,11 @@ Future<TranslationResult> generateBindings(
final cssStyleDeclarations = await _generateCSSStyleDeclarations();
final translator =
Translator(packageRoot, librarySubDir, cssStyleDeclarations);
final array = objectEntries(await idl.parseAll().toDart as JSObject);
final array = objectEntries(await idl.parseAll().toDart);
for (var i = 0; i < array.length; i++) {
final entry = array[i] as JSArray;
final entry = array[i] as JSArray<JSAny?>;
final shortname = (entry[0] as JSString).toDart;
final ast = entry[1] as JSArray;
final ast = entry[1] as JSArray<webidl.Node>;
translator.collect(shortname, ast);
}
translator.setOrUpdateInterfacelikes();
Expand Down
16 changes: 8 additions & 8 deletions tool/generator/translator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -287,21 +287,21 @@ class _Parameter {
class _OverridableMember {
final List<_Parameter> parameters = [];

_OverridableMember(JSArray rawParameters) {
_OverridableMember(JSArray<idl.Argument> rawParameters) {
for (var i = 0; i < rawParameters.length; i++) {
parameters.add(_Parameter(rawParameters[i] as idl.Argument));
parameters.add(_Parameter(rawParameters[i]));
}
}

void _processParameters(JSArray thoseParameters) {
void _processParameters(JSArray<idl.Argument> thoseParameters) {
// Assume if we have extra arguments beyond what was provided in some other
// method, that these are all optional.
final thatLength = thoseParameters.length;
for (var i = thatLength; i < parameters.length; i++) {
parameters[i].isOptional = true;
}
for (var i = 0; i < thatLength; i++) {
final argument = thoseParameters[i] as idl.Argument;
final argument = thoseParameters[i];
if (i >= parameters.length) {
// We assume these parameters must be optional, regardless of what the
// IDL says.
Expand Down Expand Up @@ -361,9 +361,9 @@ class _PartialInterfacelike {
return partialInterfacelike;
}

void _processMembers(JSArray nodeMembers) {
void _processMembers(JSArray<idl.Member> nodeMembers) {
for (var i = 0; i < nodeMembers.length; i++) {
final member = nodeMembers[i] as idl.Member;
final member = nodeMembers[i];
final type = member.type;
switch (type) {
case 'constructor':
Expand Down Expand Up @@ -532,14 +532,14 @@ class Translator {
}
}

void collect(String shortName, JSArray ast) {
void collect(String shortName, JSArray<idl.Node> ast) {
final libraryPath = '$_librarySubDir/${shortName.kebabToSnake}.dart';
assert(!_libraries.containsKey(libraryPath));

final library = _Library(this, '$packageRoot/$libraryPath');

for (var i = 0; i < ast.length; i++) {
library.add(ast[i] as idl.Node);
library.add(ast[i]);
}

if (_shouldGenerate(shortName, library)) {
Expand Down
7 changes: 4 additions & 3 deletions tool/generator/util.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ import 'filesystem_api.dart';

// TODO(joshualitt): Let's find a better place for these.
@JS('Object.entries')
external JSArray objectEntries(JSObject o);
external JSArray<JSAny?> objectEntries(JSObject o);

extension JSArrayExtension on JSArray {
external JSAny? operator [](int i);
// TODO(srujzs): Remove once this is in dart:js_interop.
extension JSArrayExtension<T extends JSAny?> on JSArray<T> {
external T operator [](int i);
external int get length;
}

Expand Down
122 changes: 24 additions & 98 deletions tool/generator/webidl_api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,7 @@

import 'dart:js_interop';

@JS()
@staticInterop
class IDLType {}

extension IDLTypeExtension on IDLType {
extension type IDLType._(JSObject _) implements JSObject {
external String? get type;
external String get generic;
external JSAny get idlType;
Expand All @@ -18,21 +14,13 @@ extension IDLTypeExtension on IDLType {

/// The abstract node interface in the IDL AST. All nodes that can occur at the
/// root of the IDL inherit from [Node].
@JS()
@staticInterop
class Node {}

extension NodeExtension on Node {
extension type Node._(JSObject _) implements JSObject {
external String get type;
}

/// The abstract node interface for named nodes in the IDL. Most root nodes have
/// names, with the exception of `includes`.
@JS()
@staticInterop
class Named extends Node {}

extension NamedExtension on Named {
extension type Named._(JSObject _) implements Node {
external String get name;
}

Expand All @@ -43,69 +31,39 @@ extension NamedExtension on Named {
/// * callback interface
/// * dictionary
/// To disambiguate, use the `type` getter.
@JS()
@staticInterop
class Interfacelike extends Named {}

extension InterfaceExtension on Interfacelike {
extension type Interfacelike._(JSObject _) implements Named {
external bool get partial;
external JSArray get members;
external JSArray<Member> get members;
external String? get inheritance;
}

@JS()
@staticInterop
class Callback extends Named {}

extension CallbackExtension on Callback {
extension type Callback._(JSObject _) implements Named {
external IDLType get idlType;
external JSArray get arguments;
external JSArray<Argument> get arguments;
}

@JS()
@staticInterop
class EnumValue {}

extension EnumValueExtension on EnumValue {
extension type EnumValue._(JSObject _) implements JSObject {
external String get type;
external String get value;
}

@JS()
@staticInterop
class Enum extends Named {}

@JS()
@staticInterop
class Typedef extends Named {}
extension type Enum._(JSObject _) implements Named {}

extension TypedefExtension on Typedef {
extension type Typedef._(JSObject _) implements Named {
external IDLType get idlType;
}

@JS()
@staticInterop
class Includes extends Node {}

extension IncludesExtension on Includes {
extension type Includes._(JSObject _) implements Node {
external String get target;
external String get includes;
}

/// All members inherit from the [Member] node.
@JS()
@staticInterop
class Member {}

extension MemberExtension on Member {
extension type Member._(JSObject _) implements JSObject {
external String get type;
}

@JS()
@staticInterop
class Argument {}

extension ArgumentExtension on Argument {
extension type Argument._(JSObject _) implements JSObject {
external String get type;
@JS('default')
external Value? get defaultValue;
Expand All @@ -115,63 +73,39 @@ extension ArgumentExtension on Argument {
external String get name;
}

@JS()
@staticInterop
class Operation extends Member {}

extension OperationExtension on Operation {
extension type Operation._(JSObject _) implements Member {
external String get special;
external IDLType get idlType;
external String get name;
external JSArray get arguments;
external JSArray<Argument> get arguments;
}

@JS()
@staticInterop
class Constructor extends Member {}

extension ConstructorExtension on Constructor {
external JSArray get arguments;
extension type Constructor._(JSObject _) implements Member {
external JSArray<Argument> get arguments;
}

@JS()
@staticInterop
class Attribute extends Member {}

extension AttributeExtension on Attribute {
extension type Attribute._(JSObject _) implements Member {
external String get special;
external bool get readonly;
external IDLType get idlType;
external String get name;
}

@JS()
@staticInterop
class Field extends Member {}

extension FieldExtension on Field {
extension type Field._(JSObject _) implements Member {
external String get name;
external bool get required;
external IDLType get idlType;
@JS('default')
external Value? get defaultValue;
}

@JS()
@staticInterop
class Value {}

extension ValueExtension on Value {
extension type Value._(JSObject _) implements JSObject {
external String get type;
external JSAny? get value;
external bool? get negative;
}

@JS()
@staticInterop
class Constant extends Member {}

extension ConstantExtension on Constant {
extension type Constant._(JSObject _) implements Member {
external IDLType get idlType;
external String get name;
external Value get value;
Expand All @@ -182,23 +116,15 @@ extension ConstantExtension on Constant {
/// * async iterable<>
/// * maplike<>
/// * setlike<>
@JS()
@staticInterop
class MemberDeclaration {}

extension MemberDeclarationExtension on MemberDeclaration {
extension type MemberDeclaration._(JSObject _) implements JSObject {
external String get type;
external IDLType get idlType;
external bool get readonly;
external bool get async;
external JSArray get arguments;
external JSArray<Argument> get arguments;
}

@JS()
@staticInterop
class EOF {}

extension EOFExtension on EOF {
extension type EOF._(JSObject _) implements JSObject {
external String get type;
external String get value;
}
Loading

0 comments on commit d96c01d

Please sign in to comment.