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 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
33 changes: 29 additions & 4 deletions web_generator/lib/src/ast/base.dart
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ class ASTOptions {
}

sealed class Node {
String? get name;
abstract final ID id;
String? get dartName;

Expand All @@ -54,7 +53,6 @@ sealed class Node {
}

abstract class Declaration extends Node {
@override
String get name;

@override
Expand All @@ -65,9 +63,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 +88,20 @@ 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
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 DeclarationType<T extends Declaration> extends Type {
T get declaration;

String get declarationName;
}

class ParameterDeclaration {
final String name;

Expand All @@ -124,3 +145,7 @@ class ParameterDeclaration {
..type = type.emit(TypeOptions(nullable: optional)));
}
}

abstract class NamedType extends Type {
String get name;
}
38 changes: 20 additions & 18 deletions web_generator/lib/src/ast/builtin.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import 'base.dart';

/// A built in type supported by `dart:js_interop` or by this library
/// (with generated declarations)
class BuiltinType extends Type {
class BuiltinType extends NamedType {
@override
final String name;

Expand All @@ -21,15 +21,15 @@ 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 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,
this.isNullable});
bool? isNullable})
: isNullable = isNullable ?? false;

@override
ID get id => ID(type: 'type', name: name);
Expand All @@ -39,16 +39,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 ?? 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 @@ -81,7 +79,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 @@ -128,13 +129,14 @@ class BuiltinType extends Type {
}
}

class PackageWebType extends Type {
class PackageWebType extends NamedType {
@override
final String name;

final List<Type> typeParams;

final bool? isNullable;
@override
bool isNullable;

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

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

@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 ?? 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
75 changes: 52 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,15 @@ sealed class TypeDeclaration extends NestableDeclaration
this.constructors = const [],
this.parent});

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

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

return ExtensionType((e) => e
Expand All @@ -120,6 +124,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 +148,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 +201,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 +263,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 +450,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 +585,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 +610,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 +629,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,
useFirstExtendeeAsRepType: assertRepType,
objectLiteralConstructor: objectLiteralConstructor);
}
}

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

final List<ParameterDeclaration> parameters;

@override
final String? name;

final ID id;
Expand Down
Loading