b42103c9d1
Our maps are string-based and assume there's only one one @Native class with a given name. This isn't true for these four classes, where there are two types with the same name for each one. This then results in us dropping the definition of one of the two types. Since one of the two types is essentially unused, we should choose to drop that one instead for now. Long-term, we should deprecate the unused types or change our conformance handling to handle multiple types with the same name. We could also choose to unify both definitions, but this may lead to either false positives or renames of members being dropped. This allows us to catch uses of the types that are actually used. Change-Id: I9386cad5b014bee60cd72d21cdf0546640138704 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/341331 Reviewed-by: Sigmund Cherem <sigmund@google.com> Commit-Queue: Srujan Gaddam <srujzs@google.com>
144 lines
5.8 KiB
Dart
144 lines
5.8 KiB
Dart
// 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;
|
|
}
|