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); |
-} |