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

Merged
merged 24 commits into from
Aug 15, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
1c76d12
wip: union types
nikeokoronkwo Aug 1, 2025
d79c2cf
wip: completed anonymous unions
nikeokoronkwo Aug 2, 2025
38b3aab
implemented JS Tuple
nikeokoronkwo Aug 2, 2025
cb90943
completed nullability support for `undefined` and `null`
nikeokoronkwo Aug 2, 2025
37e0d1a
Merge branch 'main' into interop/anonymous
nikeokoronkwo Aug 6, 2025
0b943e7
wip: object decl
nikeokoronkwo Aug 6, 2025
e7535a4
implemented anonymous objects
nikeokoronkwo Aug 6, 2025
c49ce6c
added support for closures and constructors
nikeokoronkwo Aug 7, 2025
f1a9e1c
wip: type hierarchy
nikeokoronkwo Aug 7, 2025
7f0a2e0
implemented sub type deduction
nikeokoronkwo Aug 7, 2025
f57165c
added type generics to union types
nikeokoronkwo Aug 7, 2025
49aa015
changed `.reduce` to `.fold`
nikeokoronkwo Aug 7, 2025
574250f
resolved newline and license headers
nikeokoronkwo Aug 8, 2025
283ab5d
isNullable updates and renamed `DeclarationAssociatedType`
nikeokoronkwo Aug 8, 2025
4a9bca6
updated algorithm to use LCA via Topological Ordering
nikeokoronkwo Aug 11, 2025
78a535f
resolved some more comments
nikeokoronkwo Aug 12, 2025
5421359
rm hasher_test
nikeokoronkwo Aug 12, 2025
f92ffc9
resolved some more comments
nikeokoronkwo Aug 12, 2025
9944b7f
refactored tuple generation (common types) and more
nikeokoronkwo Aug 12, 2025
c665449
added LCA test
nikeokoronkwo Aug 14, 2025
3dae04e
removed stray prints
nikeokoronkwo Aug 14, 2025
6632bc0
Merge branch 'main' into interop/anonymous
nikeokoronkwo Aug 14, 2025
5326ae0
added doc support and resolved merge
nikeokoronkwo Aug 14, 2025
552a461
updated documentation formatting using formatting.dart
nikeokoronkwo Aug 15, 2025
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
25 changes: 23 additions & 2 deletions web_generator/lib/src/ast/base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,14 @@ abstract class NamedDeclaration extends Declaration {
@override
abstract String name;

ReferredType asReferredType([List<Type>? typeArgs, String? url]) =>
ReferredType asReferredType(
[List<Type>? typeArgs, bool isNullable = false, String? url]) =>
ReferredType(
name: name, declaration: this, typeParams: typeArgs ?? [], url: url);
name: name,
declaration: this,
typeParams: typeArgs ?? [],
url: url,
isNullable: isNullable);
}

abstract interface class ExportableDeclaration extends Declaration {
Expand All @@ -85,8 +90,18 @@ abstract class Type extends Node {
@override
String? dartName;

abstract bool isNullable;

@override
Reference emit([covariant TypeOptions? options]);

@override
bool operator ==(Object other) {
return other is Type && id == other.id;
}

@override
int get hashCode => Object.hashAll([id]);
}

abstract class FieldDeclaration extends NamedDeclaration {
Expand All @@ -103,6 +118,12 @@ abstract class CallableDeclaration extends NamedDeclaration {

enum DeclScope { private, protected, public }

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

String get declarationName;
}

class ParameterDeclaration {
final String name;

Expand Down
41 changes: 26 additions & 15 deletions web_generator/lib/src/ast/builtin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,15 +21,20 @@ class BuiltinType extends Type {
/// Whether the given type is present in "dart:js_interop"
final bool fromDartJSInterop;

// TODO(nikeokoronkwo): Types in general should have an `isNullable`
// property on them to indicate nullability for Dart generated code.
final bool? isNullable;
@override
bool get isNullable => _isNullable ?? false;

final bool? _isNullable;

@override
set isNullable(bool isNullable) {}

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

@override
ID get id => ID(type: 'type', name: name);
Expand All @@ -39,16 +44,14 @@ class BuiltinType extends Type {

@override
Reference emit([TypeOptions? options]) {
options ??= TypeOptions();

return TypeReference((t) => t
..symbol = name
..types.addAll(typeParams
// if there is only one type param, and it is void, ignore
.where((p) => typeParams.length != 1 || p != $voidType)
.map((p) => p.emit(TypeOptions())))
.map((p) => p.emit(options)))
..url = fromDartJSInterop ? 'dart:js_interop' : null
..isNullable = isNullable ?? options!.nullable);
..isNullable = _isNullable ?? options?.nullable);
}

static final BuiltinType $voidType = BuiltinType(name: 'void');
Expand Down Expand Up @@ -81,7 +84,10 @@ class BuiltinType extends Type {
name: 'JSString', fromDartJSInterop: true, isNullable: isNullable)
: BuiltinType(name: 'String', isNullable: isNullable),
PrimitiveType.$void || PrimitiveType.undefined => $voidType,
PrimitiveType.any || PrimitiveType.unknown => anyType,
PrimitiveType.any => (isNullable ?? false)
? anyType
: BuiltinType(name: 'JSAny', fromDartJSInterop: true),
PrimitiveType.unknown => anyType,
PrimitiveType.object => BuiltinType(
name: 'JSObject', fromDartJSInterop: true, isNullable: isNullable),
PrimitiveType.symbol => BuiltinType(
Expand Down Expand Up @@ -134,7 +140,13 @@ class PackageWebType extends Type {

final List<Type> typeParams;

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

final bool? _isNullable;

@override
set isNullable(bool isNullable) {}

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

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

@override
Reference emit([TypeOptions? options]) {
options ??= TypeOptions();

// TODO: We can make this a shared function as it is called a lot
// between types
return TypeReference((t) => t
..symbol = name
..types.addAll(typeParams
// if there is only one type param, and it is void, ignore
.where((p) => typeParams.length != 1 || p != BuiltinType.$voidType)
.map((p) => p.emit(TypeOptions())))
.map((p) => p.emit(options)))
..url = 'package:web/web.dart'
..isNullable = isNullable ?? options!.nullable);
..isNullable = _isNullable ?? options?.nullable);
}

static PackageWebType parse(String name,
Expand Down
74 changes: 51 additions & 23 deletions web_generator/lib/src/ast/declarations.dart
Original file line number Diff line number Diff line change
Expand Up @@ -63,11 +63,14 @@ sealed class TypeDeclaration extends NestableDeclaration
this.constructors = const [],
this.parent});

ExtensionType _emit(
[covariant DeclarationOptions? options,
bool abstract = false,
/// [assertRepType] 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 []]) {
List<Type> implementees = const [],
bool assertRepType = false,
bool objectLiteralConstructor = false}) {
options ??= DeclarationOptions();

final hierarchy = getMemberHierarchy(this);
Expand All @@ -93,8 +96,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 = this is ClassDeclaration
? getClassRepresentationType(this as ClassDeclaration)
final repType = assertRepType || this is ClassDeclaration
? getClassRepresentationType(this)
: BuiltinType.primitiveType(PrimitiveType.object, isNullable: false);

return ExtensionType((e) => e
Expand All @@ -120,6 +123,15 @@ sealed class TypeDeclaration extends NestableDeclaration
..types
.addAll(typeParameters.map((t) => t.emit(options?.toTypeOptions())))
..constructors.addAll([
if (objectLiteralConstructor)
Constructor((c) => c
..external = true
..optionalParameters.addAll(properties
.where((p) => p.scope == DeclScope.public)
.map((p) => Parameter((param) => param
..named = true
..name = p.name
..type = p.type.emit(options?.toTypeOptions()))))),
if (!abstract)
if (constructors.isEmpty && this is ClassDeclaration)
ConstructorDeclaration.defaultFor(this).emit(options)
Expand All @@ -135,6 +147,8 @@ abstract class MemberDeclaration {
late final TypeDeclaration parent;

abstract final DeclScope scope;

String? get name;
}

class VariableDeclaration extends FieldDeclaration
Expand Down Expand Up @@ -186,9 +200,9 @@ class VariableDeclaration extends FieldDeclaration

@override
ReferredType<VariableDeclaration> asReferredType(
[List<Type>? typeArgs, String? url]) {
[List<Type>? typeArgs, bool? isNullable, String? url]) {
return ReferredType<VariableDeclaration>.fromType(type, this,
typeParams: typeArgs ?? [], url: url);
typeParams: typeArgs ?? [], url: url, isNullable: isNullable ?? false);
}
}

Expand Down Expand Up @@ -248,11 +262,11 @@ class FunctionDeclaration extends CallableDeclaration

@override
ReferredType<FunctionDeclaration> asReferredType(
[List<Type>? typeArgs, String? url]) {
[List<Type>? typeArgs, bool? isNullable, String? url]) {
// TODO: We could do better here and make the function type typed
return ReferredType<FunctionDeclaration>.fromType(
BuiltinType.referred('Function', typeParams: typeArgs ?? [])!, this,
typeParams: typeArgs ?? [], url: url);
typeParams: typeArgs ?? [], url: url, isNullable: isNullable ?? false);
}
}

Expand Down Expand Up @@ -435,21 +449,22 @@ class NamespaceDeclaration extends NestableDeclaration

@override
ExtensionType emit([covariant DeclarationOptions? options]) {
options ??= DeclarationOptions();
options.static = true;
options?.static = true;
// static props and vars
final methods = <Method>[];
final fields = <Field>[];

for (final decl in topLevelDeclarations) {
if (decl case final VariableDeclaration variable) {
if (variable.modifier == VariableModifier.$const) {
methods.add(variable.emit(options) as Method);
methods.add(variable.emit(options ?? DeclarationOptions(static: true))
as Method);
} else {
fields.add(variable.emit(options) as Field);
fields.add(variable.emit(options ?? DeclarationOptions(static: true))
as Field);
}
} else if (decl case final FunctionDeclaration fn) {
methods.add(fn.emit(options));
methods.add(fn.emit(options ?? DeclarationOptions(static: true)));
}
}

Expand Down Expand Up @@ -569,8 +584,10 @@ class ClassDeclaration extends TypeDeclaration {

@override
ExtensionType emit([covariant DeclarationOptions? options]) {
return super._emit(options, abstract,
[if (extendedType case final extendee?) extendee], implementedTypes);
return super._emit(options,
abstract: abstract,
extendees: [if (extendedType case final extendee?) extendee],
implementees: implementedTypes);
}

@override
Expand All @@ -592,6 +609,15 @@ class InterfaceDeclaration extends TypeDeclaration {

final List<Type> extendedTypes;

/// This asserts that the extension type generated produces a rep type
/// other than its default, which is denoted by the first member of
/// [extendedTypes] if any.
final bool assertRepType;

/// This asserts generating a constructor for creating the given interface
/// as an object literal via an object literal constructor
final bool objectLiteralConstructor;

InterfaceDeclaration(
{required super.name,
required super.exported,
Expand All @@ -602,16 +628,17 @@ class InterfaceDeclaration extends TypeDeclaration {
super.methods,
super.properties,
super.operators,
super.constructors})
super.constructors,
this.assertRepType = false,
this.objectLiteralConstructor = false})
: _id = id;

@override
ExtensionType emit([covariant DeclarationOptions? options]) {
return super._emit(
options,
false,
extendedTypes,
);
return super._emit(options,
extendees: extendedTypes,
assertRepType: assertRepType,
objectLiteralConstructor: objectLiteralConstructor);
}
}

Expand Down Expand Up @@ -798,6 +825,7 @@ class ConstructorDeclaration implements MemberDeclaration {

final List<ParameterDeclaration> parameters;

@override
final String? name;

final ID id;
Expand Down
Loading
Loading