blob: 7d516a94736dc5201083a2e01ff18ff13eaba1f8 [file] [log] [blame]
// Copyright (c) 2022, 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:collection';
import 'dart:io';
import 'package:_js_interop_checks/src/js_interop.dart';
import 'package:front_end/src/api_unstable/dart2js.dart';
import 'package:kernel/kernel.dart';
// Computes string maps that encode information about the bindings between the
// Dart types/members and their native JS equivalents.
void main(List<String> args) {
if (args.length != 1) {
print('usage: ${Platform.script} \$OUTPUT_FILE');
return;
}
var component = loadComponentFromBinary(computePlatformBinariesLocation()
.resolve('dart2js_platform.dill')
.toFilePath());
// We use `SplayTreeMap`s and `SplayTreeSet`s to retain the sorted order. This
// is to ensure a deterministic order for the output file.
final SplayTreeMap<String, SplayTreeMap<String, SplayTreeSet<String>>>
nativeTypeToDartMembers =
SplayTreeMap<String, SplayTreeMap<String, SplayTreeSet<String>>>();
final SplayTreeMap<String, SplayTreeMap<String, String>>
dartTypeToNativeMembers =
SplayTreeMap<String, SplayTreeMap<String, String>>();
final SplayTreeMap<String, SplayTreeSet<String>> dartTypeToNativeTypes =
SplayTreeMap<String, SplayTreeSet<String>>();
const Set<String> webLibraries = {
'dart:html',
'dart:indexed_db',
'dart:svg',
'dart:web_audio',
'dart:web_gl'
};
for (var library in component.libraries) {
if (webLibraries.contains(library.importUri.toString())) {
for (var cls in library.classes) {
if (cls.isMixinApplication) continue;
// All strings in the maps are annotated with quotes, so that we print
// proper Dart code when we print the maps.
var clsName = "'${cls.name}'";
var nativeTypes = getNativeNames(cls);
nativeTypes = nativeTypes.map((name) => "'$name'").toList();
if (nativeTypes.isEmpty) nativeTypes = [clsName];
dartTypeToNativeTypes[clsName] = SplayTreeSet.from(nativeTypes);
var nativePropToDartProp = SplayTreeMap<String, SplayTreeSet<String>>();
var dartPropToNativeProp = SplayTreeMap<String, String>();
for (var member in [
...cls.fields,
// Ignore synthetic members.
...cls.procedures.where((procedure) => !procedure.isSynthetic),
...cls.constructors.where((constructor) => !constructor.isSynthetic),
]) {
// Only record external members as they map to the native names.
if (!member.isExternal) continue;
// Private members can't be accessed.
if (member.name.isPrivate) continue;
var dartMember = "'${member.name}'";
var nativeMember = "'${_getJSNameValue(member) ?? member.name.text}'";
// Multiple `dart:html` members may be bound to the same native
// symbol.
nativePropToDartProp
.putIfAbsent(nativeMember, () => SplayTreeSet<String>())
.add(dartMember);
for (var nativeType in nativeTypes) {
nativeTypeToDartMembers[nativeType] = nativePropToDartProp;
}
dartPropToNativeProp[dartMember] = nativeMember;
dartTypeToNativeMembers[clsName] = dartPropToNativeProp;
}
}
}
}
var outputCode = """
// A series of maps that record the various bindings we use in the web
// libraries. The term bindings here can either refer to the value in an
// `@Native` annotation or the value in a `@JSName` annotation. These maps
// compute the relationship between those values and the Dart members they
// annotate for fast lookup.
/// Mapping of native types that are bound in the web libraries via
/// `@Native` to a map of their members to those members' Dart names.
final Map<String, Map<String, Set<String>>> nativeTypeToDartMembers = $nativeTypeToDartMembers;
/// Mapping of `@Native` types in the web libraries to a map of their
/// members to the native members that those members bind to.
final Map<String, Map<String, String>> dartTypeToNativeMembers = $dartTypeToNativeMembers;
/// Mapping of `@Native` types in the web libraries to the native types they
/// bind.
final Map<String, Set<String>> dartTypeToNativeTypes = $dartTypeToNativeTypes;
""";
var outputFile = args[0];
File(outputFile).writeAsStringSync(outputCode);
}
/// If [member] has a `@JSName('...')` annotation, returns the value inside the
/// parentheses.
///
/// If there is no value or the class does not have a `@JSName()` annotation,
/// returns null.
String? _getJSNameValue(Member member) {
String? value;
for (var annotation in member.annotations) {
var c = annotationClass(annotation);
if (c != null &&
c.name == 'JSName' &&
c.enclosingLibrary.importUri == Uri.parse('dart:_js_helper')) {
var values = stringAnnotationValues(annotation);
if (values.isNotEmpty) {
value = values[0];
}
}
}
return value;
}