From d96c01d2252372c9e0fa7ffa52d2d1df331c248b Mon Sep 17 00:00:00 2001 From: Srujan Gaddam <58529443+srujzs@users.noreply.github.com> Date: Fri, 23 Feb 2024 13:05:37 -0800 Subject: [PATCH] Use extension types and generics internally (#184) Migrate @staticInterop to extension types, which enables us to use generic arguments to JSArray and JSPromise. --- tool/generator/filesystem_api.dart | 22 ++--- tool/generator/generate_bindings.dart | 17 ++-- tool/generator/translator.dart | 16 ++-- tool/generator/util.dart | 7 +- tool/generator/webidl_api.dart | 122 +++++--------------------- tool/generator/webref_css_api.dart | 24 ++--- tool/generator/webref_idl_api.dart | 8 +- 7 files changed, 61 insertions(+), 155 deletions(-) diff --git a/tool/generator/filesystem_api.dart b/tool/generator/filesystem_api.dart index 0afd1a4a..09c503e8 100644 --- a/tool/generator/filesystem_api.dart +++ b/tool/generator/filesystem_api.dart @@ -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') diff --git a/tool/generator/generate_bindings.dart b/tool/generator/generate_bindings.dart index 070c6ecb..a2f2f308 100644 --- a/tool/generator/generate_bindings.dart +++ b/tool/generator/generate_bindings.dart @@ -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> _generateCSSStyleDeclarations() async { final cssStyleDeclarations = {}; - 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; + 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. @@ -32,7 +33,7 @@ Future> _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'); } @@ -49,11 +50,11 @@ Future 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; final shortname = (entry[0] as JSString).toDart; - final ast = entry[1] as JSArray; + final ast = entry[1] as JSArray; translator.collect(shortname, ast); } translator.setOrUpdateInterfacelikes(); diff --git a/tool/generator/translator.dart b/tool/generator/translator.dart index dcfab8bd..94ce9439 100644 --- a/tool/generator/translator.dart +++ b/tool/generator/translator.dart @@ -287,13 +287,13 @@ class _Parameter { class _OverridableMember { final List<_Parameter> parameters = []; - _OverridableMember(JSArray rawParameters) { + _OverridableMember(JSArray 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 thoseParameters) { // Assume if we have extra arguments beyond what was provided in some other // method, that these are all optional. final thatLength = thoseParameters.length; @@ -301,7 +301,7 @@ class _OverridableMember { 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. @@ -361,9 +361,9 @@ class _PartialInterfacelike { return partialInterfacelike; } - void _processMembers(JSArray nodeMembers) { + void _processMembers(JSArray 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': @@ -532,14 +532,14 @@ class Translator { } } - void collect(String shortName, JSArray ast) { + void collect(String shortName, JSArray 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)) { diff --git a/tool/generator/util.dart b/tool/generator/util.dart index 3e57ba2f..c2a420ac 100644 --- a/tool/generator/util.dart +++ b/tool/generator/util.dart @@ -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 objectEntries(JSObject o); -extension JSArrayExtension on JSArray { - external JSAny? operator [](int i); +// TODO(srujzs): Remove once this is in dart:js_interop. +extension JSArrayExtension on JSArray { + external T operator [](int i); external int get length; } diff --git a/tool/generator/webidl_api.dart b/tool/generator/webidl_api.dart index 2d81a2ca..f467a642 100644 --- a/tool/generator/webidl_api.dart +++ b/tool/generator/webidl_api.dart @@ -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; @@ -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; } @@ -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 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 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; @@ -115,41 +73,25 @@ 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 get arguments; } -@JS() -@staticInterop -class Constructor extends Member {} - -extension ConstructorExtension on Constructor { - external JSArray get arguments; +extension type Constructor._(JSObject _) implements Member { + external JSArray 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; @@ -157,21 +99,13 @@ extension FieldExtension on Field { 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; @@ -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 get arguments; } -@JS() -@staticInterop -class EOF {} - -extension EOFExtension on EOF { +extension type EOF._(JSObject _) implements JSObject { external String get type; external String get value; } diff --git a/tool/generator/webref_css_api.dart b/tool/generator/webref_css_api.dart index 4491037f..df939619 100644 --- a/tool/generator/webref_css_api.dart +++ b/tool/generator/webref_css_api.dart @@ -7,26 +7,14 @@ import 'dart:js_interop'; @JS() external WebRefCSS get css; -@JS() -@staticInterop -class WebRefCSS {} - -extension WebRefIDLExtension on WebRefCSS { - external JSPromise listAll(); +extension type WebRefCSS._(JSObject _) implements JSObject { + external JSPromise listAll(); } -@JS() -@staticInterop -class CSSEntries {} - -extension CSSEntriesExtension on CSSEntries { - external JSArray? get properties; +extension type CSSEntries._(JSObject _) implements JSObject { + external JSArray? get properties; } -@JS() -@staticInterop -class CSSEntry {} - -extension CSSEntryExtension on CSSEntry { - external JSArray? get styleDeclaration; +extension type CSSEntry._(JSObject _) implements JSObject { + external JSArray? get styleDeclaration; } diff --git a/tool/generator/webref_idl_api.dart b/tool/generator/webref_idl_api.dart index f2196447..80645268 100644 --- a/tool/generator/webref_idl_api.dart +++ b/tool/generator/webref_idl_api.dart @@ -7,10 +7,6 @@ import 'dart:js_interop'; @JS() external WebRefIDL get idl; -@JS() -@staticInterop -class WebRefIDL {} - -extension WebRefIDLExtension on WebRefIDL { - external JSPromise parseAll(); +extension type WebRefIDL._(JSObject _) implements JSObject { + external JSPromise parseAll(); }