Skip to content
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

Add [] and []= operators for unnamed getters and setters #293

Merged
merged 3 commits into from
Aug 28, 2024
Merged
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
2 changes: 2 additions & 0 deletions web/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
`dart:io` and `dart:html`.
- Added `JSImmutableListWrapper` which helps create a dart list from a JS list.
- Deprecated `TouchListWrapper` and `TouchListConvert` in favor of `JSImmutableListWrapper`.
- Added `[]` and `[]=` overloaded operators to types which define unnamed
`getter`s and `setter`s, respectively.

## 1.0.0

Expand Down
2 changes: 2 additions & 0 deletions web/lib/src/dom/css_animations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,8 @@ extension type CSSKeyframeRule._(JSObject _) implements CSSRule, JSObject {
/// API documentation sourced from
/// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/CSSKeyframesRule).
extension type CSSKeyframesRule._(JSObject _) implements CSSRule, JSObject {
external CSSKeyframeRule operator [](int index);

/// The **`appendRule()`** method of the [CSSKeyframeRule] interface appends a
/// [CSSKeyFrameRule] to the end of the rules.
external void appendRule(String rule);
Expand Down
14 changes: 14 additions & 0 deletions web/lib/src/dom/css_typed_om.dart
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,12 @@ extension type CSSUnparsedValue._(JSObject _)
implements CSSStyleValue, JSObject {
external factory CSSUnparsedValue(JSArray<CSSUnparsedSegment> members);

external CSSUnparsedSegment operator [](int index);
external void operator []=(
int index,
CSSUnparsedSegment val,
);

/// The **`length`** read-only property of the
/// [CSSUnparsedValue] interface returns the number of items in the object.
external int get length;
Expand Down Expand Up @@ -468,6 +474,8 @@ extension type CSSMathClamp._(JSObject _) implements CSSMathValue, JSObject {
/// API documentation sourced from
/// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/CSSNumericArray).
extension type CSSNumericArray._(JSObject _) implements JSObject {
external CSSNumericValue operator [](int index);

/// The read-only **`length`** property of the
/// [CSSNumericArray] interface returns the number of
/// [CSSNumericValue] objects in the list.
Expand All @@ -486,6 +494,12 @@ extension type CSSTransformValue._(JSObject _)
implements CSSStyleValue, JSObject {
external factory CSSTransformValue(JSArray<CSSTransformComponent> transforms);

external CSSTransformComponent operator [](int index);
external void operator []=(
int index,
CSSTransformComponent val,
);

/// The **`toMatrix()`** method of the
/// [CSSTransformValue] interface returns a [DOMMatrix] object.
external DOMMatrix toMatrix();
Expand Down
1 change: 1 addition & 0 deletions web/lib/src/dom/dom.dart
Original file line number Diff line number Diff line change
Expand Up @@ -1568,6 +1568,7 @@ extension type Document._(JSObject _) implements Node, JSObject {
/// fullscreen mode, restoring the previous state of the screen. This usually
/// reverses the effects of a previous call to [Element.requestFullscreen].
external JSPromise<JSAny?> exitFullscreen();
external JSObject operator [](String name);

/// The **`getElementsByName()`** method
/// of the [Document] object returns a [NodeList] Collection of
Expand Down
31 changes: 30 additions & 1 deletion web/lib/src/dom/html.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ typedef WorkerType = String;
/// API documentation sourced from
/// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/HTMLAllCollection).
extension type HTMLAllCollection._(JSObject _) implements JSObject {
external Element operator [](int index);

/// The **`namedItem()`** method of the [HTMLAllCollection] interface returns
/// the first [Element] in the collection whose `id` or `name` attribute
/// matches the specified name, or `null` if no element matches.
Expand Down Expand Up @@ -197,6 +199,10 @@ extension type RadioNodeList._(JSObject _) implements NodeList, JSObject {
/// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/HTMLOptionsCollection).
extension type HTMLOptionsCollection._(JSObject _)
implements HTMLCollection, JSObject {
external void operator []=(
int index,
HTMLOptionElement? option,
);
external void add(
JSObject element, [
JSAny? before,
Expand Down Expand Up @@ -1271,7 +1277,13 @@ extension type HTMLUnknownElement._(JSObject _)
///
/// API documentation sourced from
/// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/DOMStringMap).
extension type DOMStringMap._(JSObject _) implements JSObject {}
extension type DOMStringMap._(JSObject _) implements JSObject {
external String operator [](String name);
external void operator []=(
String name,
String value,
);
}

/// The **`HTMLHtmlElement`** interface serves as the root node for a given HTML
/// document. This object inherits the properties and methods described in the
Expand Down Expand Up @@ -3728,6 +3740,8 @@ extension type MediaError._(JSObject _) implements JSObject {
/// API documentation sourced from
/// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/AudioTrackList).
extension type AudioTrackList._(JSObject _) implements EventTarget, JSObject {
external AudioTrack operator [](int index);

/// The **[AudioTrackList]** method **`getTrackById()`** returns the first
/// [AudioTrack] object from the track list whose [AudioTrack.id] matches the
/// specified string.
Expand Down Expand Up @@ -3829,6 +3843,8 @@ extension type AudioTrack._(JSObject _) implements JSObject {
/// API documentation sourced from
/// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/VideoTrackList).
extension type VideoTrackList._(JSObject _) implements EventTarget, JSObject {
external VideoTrack operator [](int index);

/// The **[VideoTrackList]** method
/// **`getTrackById()`** returns the first
/// [VideoTrack] object from the track list whose [VideoTrack.id] matches the
Expand Down Expand Up @@ -3949,6 +3965,8 @@ extension type VideoTrack._(JSObject _) implements JSObject {
/// API documentation sourced from
/// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/TextTrackList).
extension type TextTrackList._(JSObject _) implements EventTarget, JSObject {
external TextTrack operator [](int index);

/// The **[TextTrackList]** method
/// **`getTrackById()`** returns the first
/// [TextTrack] object from the track list whose
Expand Down Expand Up @@ -4096,6 +4114,8 @@ extension type TextTrack._(JSObject _) implements EventTarget, JSObject {
/// API documentation sourced from
/// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/TextTrackCueList).
extension type TextTrackCueList._(JSObject _) implements JSObject {
external TextTrackCue operator [](int index);

/// The **`getCueById()`** method of the [TextTrackCueList] interface returns
/// the first [VTTCue] in the list represented by the `TextTrackCueList`
/// object whose identifier matches the value of `id`.
Expand Down Expand Up @@ -5045,6 +5065,8 @@ extension type HTMLFormElement._(JSObject _) implements HTMLElement, JSObject {
/// Creates an [HTMLFormElement] using the tag 'form'.
HTMLFormElement() : _ = document.createElement('form');

external JSObject operator [](JSAny indexOrName);

/// The **`HTMLFormElement.submit()`** method submits a given
/// `form`.
///
Expand Down Expand Up @@ -5900,6 +5922,10 @@ extension type HTMLSelectElement._(JSObject _)
/// at the specified index from the options collection for this select
/// element.
external void remove([int index]);
external void operator []=(
int index,
HTMLOptionElement? option,
);

/// The **`HTMLSelectElement.checkValidity()`** method checks
/// whether the element has any constraints and whether it satisfies them. If
Expand Down Expand Up @@ -9823,6 +9849,8 @@ extension type DataTransfer._(JSObject _) implements JSObject {
/// API documentation sourced from
/// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItemList).
extension type DataTransferItemList._(JSObject _) implements JSObject {
external DataTransferItem operator [](int index);

/// The **`DataTransferItemList.add()`** method creates a new
/// [DataTransferItem] using the specified data and adds it to the drag data
/// list. The item may be a [File] or a string of a
Expand Down Expand Up @@ -10136,6 +10164,7 @@ extension type Window._(JSObject _) implements EventTarget, JSObject {
String target,
String features,
]);
external JSObject operator [](String name);

/// `window.alert()` instructs the browser to display a dialog with an
/// optional message, and to wait until the user dismisses the dialog.
Expand Down
2 changes: 2 additions & 0 deletions web/lib/src/dom/media_source.dart
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,8 @@ extension type SourceBuffer._(JSObject _) implements EventTarget, JSObject {
/// API documentation sourced from
/// [MDN Web Docs](https://developer.mozilla.org/en-US/docs/Web/API/SourceBufferList).
extension type SourceBufferList._(JSObject _) implements EventTarget, JSObject {
external SourceBuffer operator [](int index);

/// The **`length`** read-only property of the
/// [SourceBufferList] interface returns the number of
/// [SourceBuffer] objects in the list.
Expand Down
20 changes: 20 additions & 0 deletions web/lib/src/dom/svg.dart
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,10 @@ extension type SVGNumberList._(JSObject _) implements JSObject {
);
external SVGNumber removeItem(int index);
external SVGNumber appendItem(SVGNumber newItem);
external void operator []=(
int index,
SVGNumber newItem,
);
external int get length;
external int get numberOfItems;
}
Expand All @@ -534,6 +538,10 @@ extension type SVGLengthList._(JSObject _) implements JSObject {
);
external SVGLength removeItem(int index);
external SVGLength appendItem(SVGLength newItem);
external void operator []=(
int index,
SVGLength newItem,
);
external int get length;
external int get numberOfItems;
}
Expand All @@ -558,6 +566,10 @@ extension type SVGStringList._(JSObject _) implements JSObject {
);
external String removeItem(int index);
external String appendItem(String newItem);
external void operator []=(
int index,
String newItem,
);
external int get numberOfItems;
}

Expand Down Expand Up @@ -1093,6 +1105,10 @@ extension type SVGTransformList._(JSObject _) implements JSObject {
);
external SVGTransform removeItem(int index);
external SVGTransform appendItem(SVGTransform newItem);
external void operator []=(
int index,
SVGTransform newItem,
);
external SVGTransform createSVGTransformFromMatrix([DOMMatrix2DInit matrix]);
external SVGTransform? consolidate();
external int get numberOfItems;
Expand Down Expand Up @@ -1317,6 +1333,10 @@ extension type SVGPointList._(JSObject _) implements JSObject {
/// The **`appendItem()`** method of the [SVGPointList] interface adds a
/// [SVGPoint] to the end of the list.
external DOMPoint appendItem(DOMPoint newItem);
external void operator []=(
int index,
DOMPoint newItem,
);

/// The **`length`** read-only property of the [SVGPointList] interface
/// returns the number of items in the list.
Expand Down
11 changes: 11 additions & 0 deletions web/test/smoke_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,15 @@ void main() {
expect(div.style.textOverflow, equals('ellipsis'));
expect(div.style.getPropertyValue('text-overflow'), equals('ellipsis'));
});

test('External [] and []= operators work as expected.', () {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

❤️ ❤️ ❤️ ❤️

// []
expect(window['document'], window.document);
expect(window.document['body'], window.document.body);
// []=
final select = HTMLSelectElement();
final option = HTMLOptionElement();
select[0] = option;
expect(select.item(0), option);
});
}
65 changes: 50 additions & 15 deletions web_generator/lib/src/translator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@ class _RawType {
nullable = union.nullable;
typeParameter = union.typeParameter;
}

@override
String toString() => '_RawType(type: $type, nullable: $nullable, '
'typeParameter: $typeParameter)';
}

class _Parameter {
Expand Down Expand Up @@ -366,19 +370,21 @@ class _OverridableOperation extends _OverridableMember {
bool _finalized = false;
_MemberName _name;

final bool isStatic;
final String special;
final _RawType returnType;
final MdnProperty? mdnProperty;
late final _MemberName name = _generateName();

_OverridableOperation._(this._name, this.isStatic, this.returnType,
_OverridableOperation._(this._name, this.special, this.returnType,
this.mdnProperty, super.parameters);

factory _OverridableOperation(idl.Operation operation, _MemberName name,
factory _OverridableOperation(idl.Operation operation, _MemberName memberName,
MdnProperty? mdnProperty) =>
_OverridableOperation._(name, operation.special == 'static',
_OverridableOperation._(memberName, operation.special,
_getRawType(operation.idlType), mdnProperty, operation.arguments);

bool get isStatic => special == 'static';

_MemberName _generateName() {
// The name is determined after all updates are done, so finalize the
// operation.
Expand All @@ -404,7 +410,8 @@ class _OverridableOperation extends _OverridableMember {
'finalized.');
final jsOverride = _name.jsOverride;
final thisName = jsOverride.isNotEmpty ? jsOverride : _name.name;
assert(thisName == that.name && isStatic == (that.special == 'static'));
assert((that.name.isEmpty || thisName == that.name) &&
special == that.special);
returnType.update(that.idlType);
_processParameters(that.arguments);
}
Expand Down Expand Up @@ -449,7 +456,7 @@ class _PartialInterfacelike {
case 'constructor':
if (!_shouldGenerateMember(name)) break;
final idlConstructor = member as idl.Constructor;
if (_hasHTMLConstructorAttribute(idlConstructor)) continue;
if (_hasHTMLConstructorAttribute(idlConstructor)) break;
if (constructor == null) {
constructor = _OverridableConstructor(idlConstructor);
} else {
Expand Down Expand Up @@ -487,16 +494,39 @@ class _PartialInterfacelike {
break;
case 'operation':
final operation = member as idl.Operation;
final operationName = operation.name;
if (operationName.isEmpty) {
// TODO(joshualitt): We may be able to handle some unnamed
// operations.
continue;
final special = operation.special;
var operationName = operation.name;
// Some special operations may not have any MDN data and may be given
// a name that is irrelevant to the IDL, so avoid querying in that
// case and always emit.
var shouldQueryMDN = true;
switch (special) {
case 'getter':
if (operationName.isEmpty) {
operationName = 'operator []';
shouldQueryMDN = false;
}
break;
case 'setter':
if (operationName.isEmpty) {
operationName = 'operator []=';
shouldQueryMDN = false;
}
break;
case 'static':
break;
default:
// TODO(srujzs): Should we handle other special operations,
// unnamed or otherwise? For now, don't emit the unnamed ones and
// do nothing special for the named ones.
if (operationName.isEmpty) break;
}
final isStatic = operation.special == 'static';
if (!_shouldGenerateMember(operationName, isStatic: isStatic)) break;
final docs =
mdnInterface?.propertyFor(operationName, isStatic: isStatic);
if (shouldQueryMDN &&
!_shouldGenerateMember(operationName, isStatic: isStatic)) break;
final docs = shouldQueryMDN
? mdnInterface?.propertyFor(operationName, isStatic: isStatic)
: null;
// Static member may have the same name as instance members in the
// IDL, but not in Dart. Rename the static member if so.
if (isStatic) {
Expand Down Expand Up @@ -986,13 +1016,18 @@ class Translator {

code.Method _operation(_OverridableOperation operation) {
final memberName = operation.name;
// The IDL may return the value that is set. Dart doesn't let us use any
// type besides `void` for `[]=`, so we ignore the return value.
final returnType = memberName.name == 'operator []='
? code.TypeReference((b) => b..symbol = 'void')
: _typeReference(operation.returnType, returnType: true);
return _overridableMember<code.Method>(
operation,
(requiredParameters, optionalParameters) => code.Method((b) => b
..annotations.addAll(_jsOverride(memberName.jsOverride))
..external = true
..static = operation.isStatic
..returns = _typeReference(operation.returnType, returnType: true)
..returns = returnType
..name = memberName.name
..docs.addAll(operation.mdnProperty?.formattedDocs ?? [])
..requiredParameters.addAll(requiredParameters)
Expand Down