blob: ee1a37b23e6fdcefe36be45e06d20b5593b794d9 [file] [log] [blame]
// Copyright (c) 2017, 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.
library entity_utils;
import 'package:front_end/src/api_unstable/dart2js.dart'
show isUserDefinableOperator, isMinusOperator;
import 'entities.dart' show Entity, FunctionEntity;
// Somewhat stable ordering for libraries using [Uri]s
int compareLibrariesUris(Uri a, Uri b) {
if (a == b) return 0;
int byCanonicalUriPath() {
return a.path.compareTo(b.path);
}
// Order: platform < package < other.
if (a.isScheme('dart')) {
if (b.isScheme('dart')) return byCanonicalUriPath();
return -1;
}
if (b.isScheme('dart')) return 1;
if (a.isScheme('package')) {
if (b.isScheme('package')) return byCanonicalUriPath();
return -1;
}
if (b.isScheme('package')) return 1;
return _compareCanonicalUri(a, b);
}
int _compareCanonicalUri(Uri a, Uri b) {
int r = a.scheme.compareTo(b.scheme);
if (r != 0) return r;
// We would like the order of 'file:' Uris to be stable across different
// users or different builds from temporary directories. We sort by
// pathSegments elements from the last to the first since that tends to find
// a stable distinction regardless of directory root.
List<String> aSegments = a.pathSegments;
List<String> bSegments = b.pathSegments;
int aI = aSegments.length;
int bI = bSegments.length;
while (aI > 0 && bI > 0) {
String aSegment = aSegments[--aI];
String bSegment = bSegments[--bI];
r = aSegment.compareTo(bSegment);
if (r != 0) return r;
}
return aI.compareTo(bI); // Shortest first.
}
/// Compare URIs of compilation units.
int compareSourceUris(Uri uri1, Uri uri2) {
if (uri1 == uri2) return 0;
// Compilation units are compared only within the same library so we expect
// the Uris to usually be clustered together with a common scheme and path
// prefix.
return '${uri1}'.compareTo('${uri2}');
}
/// Compare entities within the same compilation unit.
int compareEntities(Entity element1, int? line1, int? column1, Entity element2,
int? line2, int? column2) {
line1 ??= -1;
line2 ??= -1;
int r = line1.compareTo(line2);
if (r != 0) return r;
column1 ??= -1;
column2 ??= -1;
r = column1.compareTo(column2);
if (r != 0) return r;
r = element1.name!.compareTo(element2.name!);
if (r != 0) return r;
// Same file, position and name. If this happens, we should find out why
// and make the order total and independent of hashCode.
return element1.hashCode.compareTo(element2.hashCode);
}
String reconstructConstructorName(FunctionEntity element) {
String className = element.enclosingClass!.name;
if (element.name == '') {
return className;
} else {
return '$className\$${element.name}';
}
}
String reconstructConstructorNameSourceString(FunctionEntity element) {
if (element.name == '') {
return element.enclosingClass!.name;
} else {
return reconstructConstructorName(element);
}
}
/// Map an operator-name to a valid JavaScript identifier.
///
/// For non-operator names, this method just returns its input.
///
/// The results returned from this method are guaranteed to be valid
/// JavaScript identifiers, except it may include reserved words for
/// non-operator names.
// TODO(sra): The namer uses another, different, version of this function. Make
// it clearer that this function is not used for JavaScript naming, but is
// useful in creating identifiers for other purposes like data formats for file
// names. Rename this function and move it to a more general String utils
// place.
String? operatorNameToIdentifier(String? name) {
if (name == null) {
return name;
} else if (name == '==') {
return r'operator$eq';
} else if (name == '~') {
return r'operator$not';
} else if (name == '[]') {
return r'operator$index';
} else if (name == '[]=') {
return r'operator$indexSet';
} else if (name == '*') {
return r'operator$mul';
} else if (name == '/') {
return r'operator$div';
} else if (name == '%') {
return r'operator$mod';
} else if (name == '~/') {
return r'operator$tdiv';
} else if (name == '+') {
return r'operator$add';
} else if (name == '<<') {
return r'operator$shl';
} else if (name == '>>') {
return r'operator$shr';
} else if (name == '>>>') {
return r'operator$shru';
} else if (name == '>=') {
return r'operator$ge';
} else if (name == '>') {
return r'operator$gt';
} else if (name == '<=') {
return r'operator$le';
} else if (name == '<') {
return r'operator$lt';
} else if (name == '&') {
return r'operator$and';
} else if (name == '^') {
return r'operator$xor';
} else if (name == '|') {
return r'operator$or';
} else if (name == '-') {
return r'operator$sub';
} else if (name == 'unary-') {
return r'operator$negate';
} else {
return name.replaceAll(_nonIdentifierRE, '_');
}
}
final RegExp _nonIdentifierRE = RegExp(r'[^A-Za-z0-9_$]');
String? constructOperatorNameOrNull(String op, bool isUnary) {
if (isMinusOperator(op)) {
return isUnary ? 'unary-' : op;
} else if (isUserDefinableOperator(op) || op == '??') {
return op;
} else {
return null;
}
}
String constructOperatorName(String op, bool isUnary) {
String? operatorName = constructOperatorNameOrNull(op, isUnary);
if (operatorName == null)
throw 'Unhandled operator: $op';
else
return operatorName;
}