| Index: lib/src/utils.dart
|
| diff --git a/lib/src/utils.dart b/lib/src/utils.dart
|
| deleted file mode 100644
|
| index 229eef40628b14181ea81daca377a647a92b4b54..0000000000000000000000000000000000000000
|
| --- a/lib/src/utils.dart
|
| +++ /dev/null
|
| @@ -1,460 +0,0 @@
|
| -// Copyright (c) 2015, 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.
|
| -
|
| -/// Holds a couple utility functions used at various places in the system.
|
| -
|
| -import 'dart:io';
|
| -import 'package:path/path.dart' as path;
|
| -import 'package:analyzer/dart/ast/ast.dart'
|
| - show
|
| - ImportDirective,
|
| - ExportDirective,
|
| - PartDirective,
|
| - CompilationUnit,
|
| - Identifier,
|
| - AnnotatedNode,
|
| - AstNode,
|
| - Expression,
|
| - SimpleIdentifier,
|
| - MethodInvocation;
|
| -import 'package:analyzer/dart/element/element.dart';
|
| -import 'package:analyzer/dart/element/type.dart';
|
| -import 'package:analyzer/src/generated/constant.dart' show DartObject;
|
| -//TODO(leafp): Remove deprecated dependency
|
| -//ignore: DEPRECATED_MEMBER_USE
|
| -import 'package:analyzer/src/generated/element.dart' show DynamicTypeImpl;
|
| -import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
|
| -import 'package:analyzer/src/task/dart.dart' show ParseDartTask;
|
| -import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
|
| -import 'package:analyzer/src/generated/source.dart' show LineInfo, Source;
|
| -import 'package:analyzer/analyzer.dart' show parseDirectives;
|
| -import 'package:source_span/source_span.dart';
|
| -
|
| -import 'codegen/js_names.dart' show invalidVariableName;
|
| -
|
| -bool isDartPrivateLibrary(LibraryElement library) {
|
| - var uri = library.source.uri;
|
| - if (uri.scheme != "dart") return false;
|
| - return Identifier.isPrivateName(uri.path);
|
| -}
|
| -
|
| -/// Choose a canonical name from the library element. This is safe to use as a
|
| -/// namespace in JS and Dart code generation. This never uses the library's
|
| -/// name (the identifier in the `library` declaration) as it doesn't have any
|
| -/// meaningful rules enforced.
|
| -String canonicalLibraryName(LibraryElement library) {
|
| - var uri = library.source.uri;
|
| - var name = path.basenameWithoutExtension(uri.pathSegments.last);
|
| - return _toIdentifier(name);
|
| -}
|
| -
|
| -/// Escape [name] to make it into a valid identifier.
|
| -String _toIdentifier(String name) {
|
| - if (name.length == 0) return r'$';
|
| -
|
| - // Escape any invalid characters
|
| - StringBuffer buffer = null;
|
| - for (int i = 0; i < name.length; i++) {
|
| - var ch = name[i];
|
| - var needsEscape = ch == r'$' || _invalidCharInIdentifier.hasMatch(ch);
|
| - if (needsEscape && buffer == null) {
|
| - buffer = new StringBuffer(name.substring(0, i));
|
| - }
|
| - if (buffer != null) {
|
| - buffer.write(needsEscape ? '\$${ch.codeUnits.join("")}' : ch);
|
| - }
|
| - }
|
| -
|
| - var result = buffer != null ? '$buffer' : name;
|
| - // Ensure the identifier first character is not numeric and that the whole
|
| - // identifier is not a keyword.
|
| - if (result.startsWith(new RegExp('[0-9]')) || invalidVariableName(result)) {
|
| - return '\$$result';
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -// Invalid characters for identifiers, which would need to be escaped.
|
| -final _invalidCharInIdentifier = new RegExp(r'[^A-Za-z_$0-9]');
|
| -
|
| -/// Returns all libraries transitively imported or exported from [start].
|
| -List<LibraryElement> reachableLibraries(LibraryElement start) {
|
| - var results = <LibraryElement>[];
|
| - var seen = new Set();
|
| - void find(LibraryElement lib) {
|
| - if (seen.contains(lib)) return;
|
| - seen.add(lib);
|
| - results.add(lib);
|
| - lib.importedLibraries.forEach(find);
|
| - lib.exportedLibraries.forEach(find);
|
| - }
|
| - find(start);
|
| - return results;
|
| -}
|
| -
|
| -/// Returns all sources transitively imported or exported from [start] in
|
| -/// post-visit order. Internally this uses digest parsing to read only
|
| -/// directives from each source, that way library resolution can be done
|
| -/// bottom-up and improve performance of the analyzer internal cache.
|
| -Iterable<Source> reachableSources(Source start, AnalysisContext context) {
|
| - var results = <Source>[];
|
| - var seen = new Set();
|
| - void find(Source source) {
|
| - if (seen.contains(source)) return;
|
| - seen.add(source);
|
| - _importsAndExportsOf(source, context).forEach(find);
|
| - results.add(source);
|
| - }
|
| - find(start);
|
| - return results;
|
| -}
|
| -
|
| -/// Returns sources that are imported or exported in [source] (parts are
|
| -/// excluded).
|
| -Iterable<Source> _importsAndExportsOf(Source source, AnalysisContext context) {
|
| - var unit =
|
| - parseDirectives(context.getContents(source).data, name: source.fullName);
|
| - return unit.directives
|
| - .where((d) => d is ImportDirective || d is ExportDirective)
|
| - .map((d) {
|
| - var res = ParseDartTask.resolveDirective(context, source, d, null);
|
| - if (res == null) print('error: couldn\'t resolve $d');
|
| - return res;
|
| - }).where((d) => d != null);
|
| -}
|
| -
|
| -/// Returns the enclosing library of [e].
|
| -LibraryElement enclosingLibrary(Element e) {
|
| - while (e != null && e is! LibraryElement) e = e.enclosingElement;
|
| - return e;
|
| -}
|
| -
|
| -/// Returns sources that are included with part directives from [unit].
|
| -Iterable<Source> partsOf(CompilationUnit unit, AnalysisContext context) {
|
| - return unit.directives.where((d) => d is PartDirective).map((d) {
|
| - var res =
|
| - ParseDartTask.resolveDirective(context, unit.element.source, d, null);
|
| - if (res == null) print('error: couldn\'t resolve $d');
|
| - return res;
|
| - }).where((d) => d != null);
|
| -}
|
| -
|
| -/// Looks up the declaration that matches [member] in [type] or its superclasses
|
| -/// and interfaces, and returns its declared type.
|
| -// TODO(sigmund): add this to lookUp* in analyzer. The difference here is that
|
| -// we also look in interfaces in addition to superclasses.
|
| -FunctionType searchTypeFor(InterfaceType start, ExecutableElement member) {
|
| - var getMemberTypeHelper = _memberTypeGetter(member);
|
| - FunctionType search(InterfaceType type, bool first) {
|
| - if (type == null) return null;
|
| - var res = null;
|
| - if (!first) {
|
| - res = getMemberTypeHelper(type);
|
| - if (res != null) return res;
|
| - }
|
| -
|
| - for (var m in type.mixins.reversed) {
|
| - res = search(m, false);
|
| - if (res != null) return res;
|
| - }
|
| -
|
| - res = search(type.superclass, false);
|
| - if (res != null) return res;
|
| -
|
| - for (var i in type.interfaces) {
|
| - res = search(i, false);
|
| - if (res != null) return res;
|
| - }
|
| -
|
| - return null;
|
| - }
|
| -
|
| - return search(start, true);
|
| -}
|
| -
|
| -/// Looks up the declaration that matches [member] in [type] and returns it's
|
| -/// declared type.
|
| -FunctionType getMemberType(InterfaceType type, ExecutableElement member) =>
|
| - _memberTypeGetter(member)(type);
|
| -
|
| -typedef FunctionType _MemberTypeGetter(InterfaceType type);
|
| -
|
| -_MemberTypeGetter _memberTypeGetter(ExecutableElement member) {
|
| - String memberName = member.name;
|
| - final isGetter = member is PropertyAccessorElement && member.isGetter;
|
| - final isSetter = member is PropertyAccessorElement && member.isSetter;
|
| -
|
| - FunctionType f(InterfaceType type) {
|
| - ExecutableElement baseMethod;
|
| - try {
|
| - if (isGetter) {
|
| - assert(!isSetter);
|
| - // Look for getter or field.
|
| - baseMethod = type.getGetter(memberName);
|
| - } else if (isSetter) {
|
| - baseMethod = type.getSetter(memberName);
|
| - } else {
|
| - baseMethod = type.getMethod(memberName);
|
| - }
|
| - } catch (e) {
|
| - // TODO(sigmund): remove this try-catch block (see issue #48).
|
| - }
|
| - if (baseMethod == null || baseMethod.isStatic) return null;
|
| - return baseMethod.type;
|
| - }
|
| - ;
|
| - return f;
|
| -}
|
| -
|
| -bool isDynamicTarget(Expression node) {
|
| - if (node == null) return false;
|
| -
|
| - if (isLibraryPrefix(node)) return false;
|
| -
|
| - // Null type happens when we have unknown identifiers, like a dart: import
|
| - // that doesn't resolve.
|
| - var type = node.staticType;
|
| - return type == null || type.isDynamic;
|
| -}
|
| -
|
| -bool isLibraryPrefix(Expression node) =>
|
| - node is SimpleIdentifier && node.staticElement is PrefixElement;
|
| -
|
| -/// Returns an ANSII color escape sequence corresponding to [levelName]. Colors
|
| -/// are defined for: severe, error, warning, or info. Returns null if the level
|
| -/// name is not recognized.
|
| -String colorOf(String levelName) {
|
| - levelName = levelName.toLowerCase();
|
| - if (levelName == 'shout' || levelName == 'severe' || levelName == 'error') {
|
| - return _RED_COLOR;
|
| - }
|
| - if (levelName == 'warning') return _MAGENTA_COLOR;
|
| - if (levelName == 'info') return _CYAN_COLOR;
|
| - return null;
|
| -}
|
| -
|
| -const String _RED_COLOR = '\u001b[31m';
|
| -const String _MAGENTA_COLOR = '\u001b[35m';
|
| -const String _CYAN_COLOR = '\u001b[36m';
|
| -const String GREEN_COLOR = '\u001b[32m';
|
| -const String NO_COLOR = '\u001b[0m';
|
| -
|
| -class OutWriter {
|
| - final String _path;
|
| - final StringBuffer _sb = new StringBuffer();
|
| - int _indent = 0;
|
| - String _prefix = "";
|
| - bool _needsIndent = true;
|
| -
|
| - OutWriter(this._path);
|
| -
|
| - void write(String string, [int indent = 0]) {
|
| - if (indent < 0) inc(indent);
|
| -
|
| - var lines = string.split('\n');
|
| - for (var i = 0, end = lines.length - 1; i < end; i++) {
|
| - _writeln(lines[i]);
|
| - }
|
| - _write(lines.last);
|
| -
|
| - if (indent > 0) inc(indent);
|
| - }
|
| -
|
| - void _writeln(String string) {
|
| - if (_needsIndent && string.isNotEmpty) _sb.write(_prefix);
|
| - _sb.writeln(string);
|
| - _needsIndent = true;
|
| - }
|
| -
|
| - void _write(String string) {
|
| - if (_needsIndent && string.isNotEmpty) {
|
| - _sb.write(_prefix);
|
| - _needsIndent = false;
|
| - }
|
| - _sb.write(string);
|
| - }
|
| -
|
| - void inc([int n = 2]) {
|
| - _indent = _indent + n;
|
| - assert(_indent >= 0);
|
| - _prefix = "".padRight(_indent);
|
| - }
|
| -
|
| - void dec([int n = 2]) {
|
| - _indent = _indent - n;
|
| - assert(_indent >= 0);
|
| - _prefix = "".padRight(_indent);
|
| - }
|
| -
|
| - void close() {
|
| - new File(_path).writeAsStringSync('$_sb');
|
| - }
|
| -}
|
| -
|
| -SourceLocation locationForOffset(LineInfo lineInfo, Uri uri, int offset) {
|
| - var loc = lineInfo.getLocation(offset);
|
| - return new SourceLocation(offset,
|
| - sourceUrl: uri, line: loc.lineNumber - 1, column: loc.columnNumber - 1);
|
| -}
|
| -
|
| -String resourceOutputPath(Uri resourceUri, Uri entryUri, String runtimeDir) {
|
| - if (resourceUri.scheme == 'package') return resourceUri.path;
|
| -
|
| - if (resourceUri.scheme != 'file') return null;
|
| -
|
| - var entryPath = entryUri.path;
|
| - // The entry uri is either a directory or a dart/html file. If the latter,
|
| - // trim the file.
|
| - var entryDir = entryPath.endsWith('.dart') || entryPath.endsWith('.html')
|
| - ? path.dirname(entryPath)
|
| - : entryPath;
|
| - var filepath = path.normalize(path.join(entryDir, resourceUri.path));
|
| - if (path.isWithin(runtimeDir, filepath)) {
|
| - filepath = path.relative(filepath, from: runtimeDir);
|
| - return path.join('dev_compiler', 'runtime', filepath);
|
| - }
|
| -
|
| - return path.relative(resourceUri.path, from: entryDir);
|
| -}
|
| -
|
| -/// Given an annotated [node] and a [test] function, returns the first matching
|
| -/// constant valued annotation.
|
| -///
|
| -/// For example if we had the ClassDeclaration node for `FontElement`:
|
| -///
|
| -/// @js.JS('HTMLFontElement')
|
| -/// @deprecated
|
| -/// class FontElement { ... }
|
| -///
|
| -/// We could match `@deprecated` with a test function like:
|
| -///
|
| -/// (v) => v.type.name == 'Deprecated' && v.type.element.library.isDartCore
|
| -///
|
| -DartObject findAnnotation(Element element, bool test(DartObject value)) {
|
| - for (var metadata in element.metadata) {
|
| - var value = metadata.constantValue;
|
| - if (value != null && test(value)) return value;
|
| - }
|
| - return null;
|
| -}
|
| -
|
| -/// Given a constant [value], a [fieldName], and an [expectedType], returns the
|
| -/// value of that field.
|
| -///
|
| -/// If the field is missing or is not [expectedType], returns null.
|
| -DartObject getConstantField(
|
| - DartObject value, String fieldName, DartType expectedType) {
|
| - var f = value?.getField(fieldName);
|
| - return (f == null || f.type != expectedType) ? null : f;
|
| -}
|
| -
|
| -DartType fillDynamicTypeArgs(DartType t, TypeProvider types) {
|
| - if (t is ParameterizedType) {
|
| - var dyn = new List.filled(t.typeArguments.length, types.dynamicType);
|
| - return t.substitute2(dyn, t.typeArguments);
|
| - }
|
| - return t;
|
| -}
|
| -
|
| -/// Similar to [SimpleIdentifier] inGetterContext, inSetterContext, and
|
| -/// inDeclarationContext, this method returns true if [node] is used in an
|
| -/// invocation context such as a MethodInvocation.
|
| -bool inInvocationContext(SimpleIdentifier node) {
|
| - var parent = node.parent;
|
| - return parent is MethodInvocation && parent.methodName == node;
|
| -}
|
| -
|
| -// TODO(vsm): Move this onto the appropriate class. Ideally, we'd attach
|
| -// it to TypeProvider.
|
| -
|
| -/// Searches all supertype, in order of most derived members, to see if any
|
| -/// [match] a condition. If so, returns the first match, otherwise returns null.
|
| -InterfaceType findSupertype(InterfaceType type, bool match(InterfaceType t)) {
|
| - for (var m in type.mixins.reversed) {
|
| - if (match(m)) return m;
|
| - }
|
| - var s = type.superclass;
|
| - if (s == null) return null;
|
| -
|
| - if (match(s)) return type;
|
| - return findSupertype(s, match);
|
| -}
|
| -
|
| -SourceSpanWithContext createSpanHelper(
|
| - LineInfo lineInfo, int start, int end, Source source, String content) {
|
| - var startLoc = locationForOffset(lineInfo, source.uri, start);
|
| - var endLoc = locationForOffset(lineInfo, source.uri, end);
|
| -
|
| - var lineStart = startLoc.offset - startLoc.column;
|
| - // Find the end of the line. This is not exposed directly on LineInfo, but
|
| - // we can find it pretty easily.
|
| - // TODO(jmesserly): for now we do the simple linear scan. Ideally we can get
|
| - // some help from the LineInfo API.
|
| - int lineEnd = endLoc.offset;
|
| - int lineNum = lineInfo.getLocation(lineEnd).lineNumber;
|
| - while (lineEnd < content.length &&
|
| - lineInfo.getLocation(++lineEnd).lineNumber == lineNum);
|
| -
|
| - var text = content.substring(start, end);
|
| - var lineText = content.substring(lineStart, lineEnd);
|
| - return new SourceSpanWithContext(startLoc, endLoc, text, lineText);
|
| -}
|
| -
|
| -bool isInlineJS(Element e) =>
|
| - e is FunctionElement &&
|
| - e.library.source.uri.toString() == 'dart:_foreign_helper' &&
|
| - e.name == 'JS';
|
| -
|
| -bool isDartMathMinMax(Element e) =>
|
| - e is FunctionElement &&
|
| - e.library.source.uri.toString() == 'dart:math' &&
|
| - (e.name == 'min' || e.name == 'max');
|
| -
|
| -/// Parses an enum value out of a string.
|
| -// TODO(ochafik): generic signature.
|
| -dynamic parseEnum(String s, List enumValues) =>
|
| - enumValues.firstWhere((v) => s == getEnumName(v),
|
| - orElse: () => throw new ArgumentError('Unknown enum value: $s '
|
| - '(expected one of ${enumValues.map(getEnumName)})'));
|
| -
|
| -/// Gets the "simple" name of an enum value.
|
| -getEnumName(v) {
|
| - var parts = '$v'.split('.');
|
| - if (parts.length != 2 || !parts.every((p) => p.isNotEmpty)) {
|
| - throw new ArgumentError('Invalid enum value: $v');
|
| - }
|
| - return parts[1];
|
| -}
|
| -
|
| -class FileSystem {
|
| - const FileSystem();
|
| -
|
| - void _ensureParentExists(String file) {
|
| - var dir = new Directory(path.dirname(file));
|
| - if (!dir.existsSync()) dir.createSync(recursive: true);
|
| - }
|
| -
|
| - void copySync(String source, String destination) {
|
| - _ensureParentExists(destination);
|
| - new File(source).copySync(destination);
|
| - }
|
| -
|
| - void writeAsStringSync(String file, String contents) {
|
| - _ensureParentExists(file);
|
| - new File(file).writeAsStringSync(contents);
|
| - }
|
| -}
|
| -
|
| -//TODO(leafp): Is this really necessary? In theory I think
|
| -// the static type should always be filled in for resolved
|
| -// ASTs. This may be a vestigial workaround.
|
| -DartType getStaticType(Expression e) =>
|
| - e.staticType ?? DynamicTypeImpl.instance;
|
| -
|
| -// TODO(leafp) Factor this out or use an existing library
|
| -class Tuple2<T0, T1> {
|
| - final T0 e0;
|
| - final T1 e1;
|
| - Tuple2(this.e0, this.e1);
|
| -}
|
|
|