Skip to content

[interop] Add Support for Anonymous Declarations #434

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

Open
wants to merge 19 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 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
4 changes: 3 additions & 1 deletion web_generator/lib/src/ast/base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ abstract class Type extends Node {
@override
String? dartName;

/// Whether the given type is nullable or not
/// (unioned with `undefined` or `null`)
abstract bool isNullable;

@override
Expand Down Expand Up @@ -118,7 +120,7 @@ abstract class CallableDeclaration extends NamedDeclaration {

enum DeclScope { private, protected, public }

abstract class DeclarationAssociatedType<T extends Declaration> extends Type {
abstract class DeclarationType<T extends Declaration> extends Type {
T get declaration;

String get declarationName;
Expand Down
27 changes: 9 additions & 18 deletions web_generator/lib/src/ast/builtin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,14 @@ class BuiltinType extends Type {
final bool fromDartJSInterop;

@override
bool get isNullable => _isNullable ?? false;

final bool? _isNullable;

@override
set isNullable(bool isNullable) {}
bool isNullable;
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we not make this late and only set it when we have the value instead of defaulting like we do in line 32?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

We can, but it isn't guaranteed to be set. It is only set if isNullable is set

/// isNullable can be null
Type _transformType(TSTypeNode type,
      {bool parameter = false, bool typeArg = false, bool? isNullable}) {
        // code 
      }


BuiltinType(
{required this.name,
this.typeParams = const [],
this.fromDartJSInterop = false,
bool? isNullable})
: _isNullable = isNullable;
: isNullable = isNullable ?? false;

@override
ID get id => ID(type: 'type', name: name);
Expand All @@ -51,7 +46,7 @@ class BuiltinType extends Type {
.where((p) => typeParams.length != 1 || p != $voidType)
.map((p) => p.emit(options)))
..url = fromDartJSInterop ? 'dart:js_interop' : null
..isNullable = _isNullable ?? options?.nullable);
..isNullable = isNullable || (options?.nullable ?? false));
Copy link
Contributor

Choose a reason for hiding this comment

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

In theory, isNullable should have been set so we should not see any late initialization errors with the propose change above.

}

static final BuiltinType $voidType = BuiltinType(name: 'void');
Expand Down Expand Up @@ -141,12 +136,7 @@ class PackageWebType extends Type {
final List<Type> typeParams;

@override
bool get isNullable => _isNullable ?? false;

final bool? _isNullable;

@override
set isNullable(bool isNullable) {}
bool isNullable;

@override
ID get id => ID(type: 'type', name: name);
Expand All @@ -155,8 +145,9 @@ class PackageWebType extends Type {
String? get dartName => null;

PackageWebType._(
{required this.name, this.typeParams = const [], bool? isNullable})
: _isNullable = isNullable;
{required this.name,
this.typeParams = const [],
this.isNullable = false});

@override
Reference emit([TypeOptions? options]) {
Expand All @@ -169,14 +160,14 @@ class PackageWebType extends Type {
.where((p) => typeParams.length != 1 || p != BuiltinType.$voidType)
.map((p) => p.emit(options)))
..url = 'package:web/web.dart'
..isNullable = _isNullable ?? options?.nullable);
..isNullable = isNullable || (options?.nullable ?? false));
}

static PackageWebType parse(String name,
{bool? isNullable, List<Type> typeParams = const []}) {
return PackageWebType._(
name: renameMap.containsKey(name) ? renameMap[name]! : name,
isNullable: isNullable,
isNullable: isNullable ?? false,
typeParams: typeParams);
}
}
Expand Down
13 changes: 7 additions & 6 deletions web_generator/lib/src/ast/declarations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,14 @@ sealed class TypeDeclaration extends NestableDeclaration
this.constructors = const [],
this.parent});

/// [assertRepType] is used to assert that the extension type generated
/// has a representation type of the first member of [extendees] if any.
/// [useFirstExtendeeAsRepType] is used to assert that the extension type
/// generated has a representation type of the first member of [extendees]
/// if any.
ExtensionType _emit(covariant DeclarationOptions? options,
{bool abstract = false,
List<Type> extendees = const [],
List<Type> implementees = const [],
bool assertRepType = false,
bool useFirstExtendeeAsRepType = false,
bool objectLiteralConstructor = false}) {
options ??= DeclarationOptions();

Expand All @@ -96,8 +97,8 @@ sealed class TypeDeclaration extends NestableDeclaration
methodDecs.addAll(operators.where((p) => p.scope == DeclScope.public).map(
(m) => m.emit(options!..override = isOverride(m.dartName ?? m.name))));

final repType = assertRepType || this is ClassDeclaration
? getClassRepresentationType(this)
final repType = useFirstExtendeeAsRepType || this is ClassDeclaration
? getRepresentationType(this)
: BuiltinType.primitiveType(PrimitiveType.object, isNullable: false);

return ExtensionType((e) => e
Expand Down Expand Up @@ -637,7 +638,7 @@ class InterfaceDeclaration extends TypeDeclaration {
ExtensionType emit([covariant DeclarationOptions? options]) {
return super._emit(options,
extendees: extendedTypes,
assertRepType: assertRepType,
useFirstExtendeeAsRepType: assertRepType,
objectLiteralConstructor: objectLiteralConstructor);
}
}
Expand Down
75 changes: 42 additions & 33 deletions web_generator/lib/src/ast/helpers.dart
Original file line number Diff line number Diff line change
Expand Up @@ -97,18 +97,18 @@ Set<String> getMemberHierarchy(TypeDeclaration type,
return members;
}

Type getClassRepresentationType(TypeDeclaration cl) {
Type getRepresentationType(TypeDeclaration cl) {
if (cl case ClassDeclaration(extendedType: final extendee?)) {
return switch (extendee) {
ReferredType(declaration: final d) when d is ClassDeclaration =>
getClassRepresentationType(d),
getRepresentationType(d),
final BuiltinType b => b,
_ => BuiltinType.primitiveType(PrimitiveType.object, isNullable: false)
};
} else if (cl case InterfaceDeclaration(extendedTypes: [final extendee])) {
return switch (extendee) {
ReferredType(declaration: final d) when d is ClassDeclaration =>
getClassRepresentationType(d),
ReferredType(declaration: final d) when d is TypeDeclaration =>
getRepresentationType(d),
final BuiltinType b => b,
_ => BuiltinType.primitiveType(PrimitiveType.object, isNullable: false)
};
Expand Down Expand Up @@ -143,6 +143,7 @@ Type getClassRepresentationType(TypeDeclaration cl) {
return (requiredParams, optionalParams);
}

/// Recursively get the generic types specified in a given type [t]
List<GenericType> getGenericTypes(Type t) {
final types = <(String, Type?)>[];
switch (t) {
Expand All @@ -152,10 +153,10 @@ List<GenericType> getGenericTypes(Type t) {
case ReferredType(typeParams: final referredTypeParams):
case UnionType(types: final referredTypeParams):
case TupleType(types: final referredTypeParams):
types.addAll(referredTypeParams
.map(getGenericTypes)
.fold(<GenericType>[], (prev, combine) => [...prev, ...combine]).map(
(t) => (t.name, t.constraint)));
for (final referredTypeParam in referredTypeParams) {
types.addAll(getGenericTypes(referredTypeParam)
.map((t) => (t.name, t.constraint)));
}
break;
case ObjectLiteralType(
properties: final objectProps,
Expand All @@ -173,36 +174,44 @@ List<GenericType> getGenericTypes(Type t) {
returnType: methodType,
parameters: methodParams
) in objectMethods) {
final typeParams = [methodType, ...methodParams.map((p) => p.type)]
.map(getGenericTypes)
.fold(<GenericType>[], (prev, combine) => [...prev, ...combine]);

types.addAll(typeParams.where((t) {
return alreadyEstablishedTypeParams.any((al) => al.name == t.name);
}).map((t) => (t.name, t.constraint)));
final typeParams = [methodType, ...methodParams.map((p) => p.type)];

for (final type in typeParams) {
final genericTypes = getGenericTypes(type);
for (final genericType in genericTypes) {
if (alreadyEstablishedTypeParams
.any((al) => al.name == genericType.name)) {
types.add((genericType.name, genericType.constraint));
}
}
}
}

for (final ConstructorDeclaration(parameters: methodParams)
in objectConstructors) {
types.addAll(methodParams.map((p) => getGenericTypes(p.type)).fold(
<GenericType>[],
(prev, combine) =>
[...prev, ...combine]).map((t) => (t.name, t.constraint)));
for (final ParameterDeclaration(type: methodParamType)
in methodParams) {
types.addAll(getGenericTypes(methodParamType)
.map((t) => (t.name, t.constraint)));
}
}

for (final OperatorDeclaration(
typeParameters: alreadyEstablishedTypeParams,
returnType: methodType,
parameters: methodParams
) in objectOperators) {
final typeParams = [methodType, ...methodParams.map((p) => p.type)]
.map(getGenericTypes)
.fold(<GenericType>[], (prev, combine) => [...prev, ...combine]);

types.addAll(typeParams.where((t) {
return !alreadyEstablishedTypeParams.contains(t) ||
alreadyEstablishedTypeParams.any((al) => al.name == t.name);
}).map((t) => (t.name, t.constraint)));
final typeParams = [methodType, ...methodParams.map((p) => p.type)];

for (final type in typeParams) {
final genericTypes = getGenericTypes(type);
for (final genericType in genericTypes) {
if (alreadyEstablishedTypeParams
.any((al) => al.name == genericType.name)) {
types.add((genericType.name, genericType.constraint));
}
}
}
}
break;
case ClosureType(
Expand All @@ -224,10 +233,10 @@ List<GenericType> getGenericTypes(Type t) {
return types.map((t) => GenericType(name: t.$1, constraint: t.$2)).toList();
}

Type getDeepType(Type t) {
Type desugarTypeAliases(Type t) {
if (t case final ReferredType ref
when ref.declaration is TypeAliasDeclaration) {
return getDeepType((ref.declaration as TypeAliasDeclaration).type);
return desugarTypeAliases((ref.declaration as TypeAliasDeclaration).type);
}
return t;
}
Expand Down Expand Up @@ -257,16 +266,16 @@ class _TupleDeclaration extends NamedDeclaration
String get name => readonly ? 'JSReadonlyTuple$count' : 'JSTuple$count';

@override
set name(String name) {}
set name(String name) {
throw Exception('Forbidden: Cannot set name on tuple declaration');
}

@override
Spec emit([covariant DeclarationOptions? options]) {
options ??= DeclarationOptions();

final repType = BuiltinType.primitiveType(PrimitiveType.array,
shouldEmitJsType: true,
// TODO: get sub type instead
typeParams: [BuiltinType.anyType]);
shouldEmitJsType: true, typeParams: [BuiltinType.anyType]);

return ExtensionType((e) => e
..name = name
Expand Down
36 changes: 21 additions & 15 deletions web_generator/lib/src/ast/types.dart
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ class TupleType extends Type {
}
}

class UnionType extends DeclarationAssociatedType {
class UnionType extends DeclarationType {
final List<Type> types;

@override
Expand Down Expand Up @@ -143,7 +143,7 @@ class UnionType extends DeclarationAssociatedType {
}

class HomogenousEnumType<T extends LiteralType, D extends Declaration>
extends UnionType implements DeclarationAssociatedType {
extends UnionType implements DeclarationType {
final List<T> _types;

@override
Expand Down Expand Up @@ -274,7 +274,7 @@ enum LiteralKind {
};
}

class ObjectLiteralType extends DeclarationAssociatedType<TypeDeclaration> {
class ObjectLiteralType extends DeclarationType<TypeDeclaration> {
final List<PropertyDeclaration> properties;

final List<MethodDeclaration> methods;
Expand Down Expand Up @@ -316,9 +316,8 @@ class ObjectLiteralType extends DeclarationAssociatedType<TypeDeclaration> {
operators: operators,
constructors: constructors,
typeParameters: getGenericTypes(this).map((g) {
final t = g;
t.constraint ??= BuiltinType.anyType;
return t;
g.constraint ??= BuiltinType.anyType;
return g;
}).toList());

@override
Expand All @@ -330,7 +329,7 @@ class ObjectLiteralType extends DeclarationAssociatedType<TypeDeclaration> {
}
}

sealed class ClosureType extends DeclarationAssociatedType {
sealed class ClosureType extends DeclarationType {
final List<ParameterDeclaration> parameters;
final Type returnType;
final List<GenericType> typeParameters;
Expand Down Expand Up @@ -445,8 +444,10 @@ class _ConstructorDeclaration extends CallableDeclaration

final repType = BuiltinType.referred('Function')!;

final isNamedParams = getDeepType(returnType) is ObjectLiteralType &&
(getDeepType(returnType) as ObjectLiteralType).constructors.isEmpty;
final isNamedParams = desugarTypeAliases(returnType) is ObjectLiteralType &&
(desugarTypeAliases(returnType) as ObjectLiteralType)
.constructors
.isEmpty;

return ExtensionType((eType) => eType
..name = name
Expand Down Expand Up @@ -517,12 +518,16 @@ class _UnionDeclaration extends NamedDeclaration
this.types = const [],
this.isNullable = false,
List<GenericType>? typeParameters})
: typeParameters = typeParameters ??
types.map(getGenericTypes).fold(<GenericType>[],
(prev, combine) => [...prev, ...combine]).map((t) {
: typeParameters = typeParameters ?? [] {
if (typeParameters == null) {
for (final type in types) {
this.typeParameters.addAll(getGenericTypes(type).map((t) {
t.constraint ??= BuiltinType.anyType;
return t;
}).toList();
}));
}
}
}

@override
String? dartName;
Expand All @@ -534,7 +539,8 @@ class _UnionDeclaration extends NamedDeclaration
Spec emit([covariant DeclarationOptions? options]) {
options ??= DeclarationOptions();

final repType = getSubTypeOfTypes(types, isNullable: isNullable);
final repType =
getLowestCommonAncestorOfTypes(types, isNullable: isNullable);

return ExtensionType((e) => e
..name = name
Expand All @@ -549,7 +555,7 @@ class _UnionDeclaration extends NamedDeclaration
final type = t.emit(options?.toTypeOptions());
final jsTypeAlt = getJSTypeAlternative(t);
return Method((m) {
final word = t is DeclarationAssociatedType
final word = t is DeclarationType
? t.declarationName
: (t.dartName ?? t.name ?? t.id.name);
m
Expand Down
4 changes: 4 additions & 0 deletions web_generator/lib/src/interop_gen/hasher.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// Copyright (c) 2025, 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.

import 'dart:convert';

import 'package:convert/convert.dart';
Expand Down
Loading
Loading