blob: 472ade68572ba57e88b77e8250a0952285987e91 [file] [log] [blame]
// 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:code_builder/code_builder.dart';
import 'base.dart';
import 'builtin.dart';
import 'declarations.dart';
import 'documentation.dart';
import 'types.dart';
Type getJSTypeAlternative(Type type) {
if (type is BuiltinType) {
if (type.fromDartJSInterop) return type;
final primitiveType = switch (type.name) {
'num' => PrimitiveType.num,
'int' => PrimitiveType.int,
'double' => PrimitiveType.double,
'String' => PrimitiveType.string,
'bool' => PrimitiveType.boolean,
_ => null
};
if (primitiveType == null) return BuiltinType.anyType;
return BuiltinType.primitiveType(primitiveType, shouldEmitJsType: true);
} else if (type is ReferredType) {
switch (type.declaration) {
case TypeAliasDeclaration(type: final t):
case EnumDeclaration(baseType: final t):
final jsTypeT = getJSTypeAlternative(t);
return jsTypeT == t ? type : jsTypeT;
default:
return type;
}
}
return type;
}
Expression generateJSAnnotation([String? name]) {
return refer('JS', 'dart:js_interop')
.call([if (name != null) literalString(name)]);
}
List<Parameter> spreadParam(ParameterDeclaration p, int count) {
return List.generate(count - 1, (i) {
final paramNumber = i + 2;
final paramName = '${p.name}$paramNumber';
return ParameterDeclaration(name: paramName, type: p.type).emit();
});
}
final Map<String, Set<String>> _memberHierarchyCache = {};
Set<String> getMemberHierarchy(TypeDeclaration type,
[bool addDirectMembers = false]) {
final members = <String>{};
void addMembersIfReferredType(Type type) {
if (type case ReferredType<Declaration>(declaration: final d)
when d is TypeDeclaration) {
members.addAll(getMemberHierarchy(d, true));
}
}
if (addDirectMembers) {
if (_memberHierarchyCache.containsKey(type.name)) {
return _memberHierarchyCache[type.name]!;
}
// add direct members
members.addAll(type.methods.map((m) => m.name));
members.addAll(type.properties.map((m) => m.name));
members.addAll(type.operators.map((m) => m.name));
}
switch (type) {
case ClassDeclaration(
extendedType: final extendee,
implementedTypes: final implementees
):
if (extendee case final extendedType?) {
addMembersIfReferredType(extendedType);
}
implementees.forEach(addMembersIfReferredType);
break;
case InterfaceDeclaration(extendedTypes: final extendees):
extendees.forEach(addMembersIfReferredType);
break;
}
if (addDirectMembers) {
_memberHierarchyCache[type.name] ??= members;
}
return members;
}
Type getClassRepresentationType(ClassDeclaration cl) {
if (cl.extendedType case final extendee?) {
return switch (extendee) {
final ClassDeclaration classExtendee =>
getClassRepresentationType(classExtendee),
_ => BuiltinType.primitiveType(PrimitiveType.object, isNullable: false)
};
} else {
final primitiveType = switch (cl.name) {
'Array' => PrimitiveType.array,
_ => PrimitiveType.object
};
return BuiltinType.primitiveType(primitiveType, isNullable: false);
}
}
(List<String>, List<Expression>) generateFromDocumentation(
Documentation? docs) {
if (docs == null) return ([], []);
if (docs.docs.trim().isEmpty) {
return ([], docs.annotations.map((d) => d.emit()).toList());
}
return (
const LineSplitter()
.convert(docs.docs.trim())
.map((d) => '/// $d')
.toList(),
docs.annotations.map((d) => d.emit()).toList()
);
}
(List<Parameter>, List<Parameter>) emitParameters(
List<ParameterDeclaration> parameters,
[DeclarationOptions? options]) {
final requiredParams = <Parameter>[];
final optionalParams = <Parameter>[];
for (final p in parameters) {
if (p.variadic) {
optionalParams.addAll(spreadParam(p, GlobalOptions.variadicArgsCount));
requiredParams.add(p.emit(options));
} else {
if (p.optional) {
optionalParams.add(p.emit(options));
} else {
requiredParams.add(p.emit(options));
}
}
}
return (requiredParams, optionalParams);
}