blob: afa32787b855e16b17b808c92ac6aebfe9cd3213 [file] [log] [blame] [edit]
// 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'
};
const Set<String> duplicateClassNames = {
'ImageElement',
'ScriptElement',
'StyleElement',
'TitleElement'
};
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).map((name) => "'$name'").toList();
if (nativeTypes.isEmpty) nativeTypes = [clsName];
// There are a couple of cases where there are two classes with the same
// name. They are all element classes bound to an `HTML` and an `SVG`
// version. For now, ignore the `SVG` version, as they're unused in
// google3 and most of them are marked unstable, and their `HTML`
// variants are much more common.
// TODO(srujzs): Remove this if we decide to deprecate these classes.
if (duplicateClassNames.contains(cls.name)) {
if (nativeTypes.length == 1 && nativeTypes[0] == "'SVG${cls.name}'") {
continue;
}
}
assert(!dartTypeToNativeTypes.containsKey(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;
}