Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(56)

Side by Side Diff: lib/src/utils.dart

Issue 1879373004: Implement modular compilation (Closed) Base URL: git@github.com:dart-lang/dev_compiler.git@master
Patch Set: Created 4 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « lib/src/runner/v8_runner.dart ('k') | pubspec.yaml » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
2 // for details. All rights reserved. Use of this source code is governed by a
3 // BSD-style license that can be found in the LICENSE file.
4
5 /// Holds a couple utility functions used at various places in the system.
6
7 import 'dart:io';
8 import 'package:path/path.dart' as path;
9 import 'package:analyzer/dart/ast/ast.dart'
10 show
11 ImportDirective,
12 ExportDirective,
13 PartDirective,
14 CompilationUnit,
15 Identifier,
16 AnnotatedNode,
17 AstNode,
18 Expression,
19 SimpleIdentifier,
20 MethodInvocation;
21 import 'package:analyzer/dart/element/element.dart';
22 import 'package:analyzer/dart/element/type.dart';
23 import 'package:analyzer/src/generated/constant.dart' show DartObject;
24 //TODO(leafp): Remove deprecated dependency
25 //ignore: DEPRECATED_MEMBER_USE
26 import 'package:analyzer/src/generated/element.dart' show DynamicTypeImpl;
27 import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
28 import 'package:analyzer/src/task/dart.dart' show ParseDartTask;
29 import 'package:analyzer/src/generated/resolver.dart' show TypeProvider;
30 import 'package:analyzer/src/generated/source.dart' show LineInfo, Source;
31 import 'package:analyzer/analyzer.dart' show parseDirectives;
32 import 'package:source_span/source_span.dart';
33
34 import 'codegen/js_names.dart' show invalidVariableName;
35
36 bool isDartPrivateLibrary(LibraryElement library) {
37 var uri = library.source.uri;
38 if (uri.scheme != "dart") return false;
39 return Identifier.isPrivateName(uri.path);
40 }
41
42 /// Choose a canonical name from the library element. This is safe to use as a
43 /// namespace in JS and Dart code generation. This never uses the library's
44 /// name (the identifier in the `library` declaration) as it doesn't have any
45 /// meaningful rules enforced.
46 String canonicalLibraryName(LibraryElement library) {
47 var uri = library.source.uri;
48 var name = path.basenameWithoutExtension(uri.pathSegments.last);
49 return _toIdentifier(name);
50 }
51
52 /// Escape [name] to make it into a valid identifier.
53 String _toIdentifier(String name) {
54 if (name.length == 0) return r'$';
55
56 // Escape any invalid characters
57 StringBuffer buffer = null;
58 for (int i = 0; i < name.length; i++) {
59 var ch = name[i];
60 var needsEscape = ch == r'$' || _invalidCharInIdentifier.hasMatch(ch);
61 if (needsEscape && buffer == null) {
62 buffer = new StringBuffer(name.substring(0, i));
63 }
64 if (buffer != null) {
65 buffer.write(needsEscape ? '\$${ch.codeUnits.join("")}' : ch);
66 }
67 }
68
69 var result = buffer != null ? '$buffer' : name;
70 // Ensure the identifier first character is not numeric and that the whole
71 // identifier is not a keyword.
72 if (result.startsWith(new RegExp('[0-9]')) || invalidVariableName(result)) {
73 return '\$$result';
74 }
75 return result;
76 }
77
78 // Invalid characters for identifiers, which would need to be escaped.
79 final _invalidCharInIdentifier = new RegExp(r'[^A-Za-z_$0-9]');
80
81 /// Returns all libraries transitively imported or exported from [start].
82 List<LibraryElement> reachableLibraries(LibraryElement start) {
83 var results = <LibraryElement>[];
84 var seen = new Set();
85 void find(LibraryElement lib) {
86 if (seen.contains(lib)) return;
87 seen.add(lib);
88 results.add(lib);
89 lib.importedLibraries.forEach(find);
90 lib.exportedLibraries.forEach(find);
91 }
92 find(start);
93 return results;
94 }
95
96 /// Returns all sources transitively imported or exported from [start] in
97 /// post-visit order. Internally this uses digest parsing to read only
98 /// directives from each source, that way library resolution can be done
99 /// bottom-up and improve performance of the analyzer internal cache.
100 Iterable<Source> reachableSources(Source start, AnalysisContext context) {
101 var results = <Source>[];
102 var seen = new Set();
103 void find(Source source) {
104 if (seen.contains(source)) return;
105 seen.add(source);
106 _importsAndExportsOf(source, context).forEach(find);
107 results.add(source);
108 }
109 find(start);
110 return results;
111 }
112
113 /// Returns sources that are imported or exported in [source] (parts are
114 /// excluded).
115 Iterable<Source> _importsAndExportsOf(Source source, AnalysisContext context) {
116 var unit =
117 parseDirectives(context.getContents(source).data, name: source.fullName);
118 return unit.directives
119 .where((d) => d is ImportDirective || d is ExportDirective)
120 .map((d) {
121 var res = ParseDartTask.resolveDirective(context, source, d, null);
122 if (res == null) print('error: couldn\'t resolve $d');
123 return res;
124 }).where((d) => d != null);
125 }
126
127 /// Returns the enclosing library of [e].
128 LibraryElement enclosingLibrary(Element e) {
129 while (e != null && e is! LibraryElement) e = e.enclosingElement;
130 return e;
131 }
132
133 /// Returns sources that are included with part directives from [unit].
134 Iterable<Source> partsOf(CompilationUnit unit, AnalysisContext context) {
135 return unit.directives.where((d) => d is PartDirective).map((d) {
136 var res =
137 ParseDartTask.resolveDirective(context, unit.element.source, d, null);
138 if (res == null) print('error: couldn\'t resolve $d');
139 return res;
140 }).where((d) => d != null);
141 }
142
143 /// Looks up the declaration that matches [member] in [type] or its superclasses
144 /// and interfaces, and returns its declared type.
145 // TODO(sigmund): add this to lookUp* in analyzer. The difference here is that
146 // we also look in interfaces in addition to superclasses.
147 FunctionType searchTypeFor(InterfaceType start, ExecutableElement member) {
148 var getMemberTypeHelper = _memberTypeGetter(member);
149 FunctionType search(InterfaceType type, bool first) {
150 if (type == null) return null;
151 var res = null;
152 if (!first) {
153 res = getMemberTypeHelper(type);
154 if (res != null) return res;
155 }
156
157 for (var m in type.mixins.reversed) {
158 res = search(m, false);
159 if (res != null) return res;
160 }
161
162 res = search(type.superclass, false);
163 if (res != null) return res;
164
165 for (var i in type.interfaces) {
166 res = search(i, false);
167 if (res != null) return res;
168 }
169
170 return null;
171 }
172
173 return search(start, true);
174 }
175
176 /// Looks up the declaration that matches [member] in [type] and returns it's
177 /// declared type.
178 FunctionType getMemberType(InterfaceType type, ExecutableElement member) =>
179 _memberTypeGetter(member)(type);
180
181 typedef FunctionType _MemberTypeGetter(InterfaceType type);
182
183 _MemberTypeGetter _memberTypeGetter(ExecutableElement member) {
184 String memberName = member.name;
185 final isGetter = member is PropertyAccessorElement && member.isGetter;
186 final isSetter = member is PropertyAccessorElement && member.isSetter;
187
188 FunctionType f(InterfaceType type) {
189 ExecutableElement baseMethod;
190 try {
191 if (isGetter) {
192 assert(!isSetter);
193 // Look for getter or field.
194 baseMethod = type.getGetter(memberName);
195 } else if (isSetter) {
196 baseMethod = type.getSetter(memberName);
197 } else {
198 baseMethod = type.getMethod(memberName);
199 }
200 } catch (e) {
201 // TODO(sigmund): remove this try-catch block (see issue #48).
202 }
203 if (baseMethod == null || baseMethod.isStatic) return null;
204 return baseMethod.type;
205 }
206 ;
207 return f;
208 }
209
210 bool isDynamicTarget(Expression node) {
211 if (node == null) return false;
212
213 if (isLibraryPrefix(node)) return false;
214
215 // Null type happens when we have unknown identifiers, like a dart: import
216 // that doesn't resolve.
217 var type = node.staticType;
218 return type == null || type.isDynamic;
219 }
220
221 bool isLibraryPrefix(Expression node) =>
222 node is SimpleIdentifier && node.staticElement is PrefixElement;
223
224 /// Returns an ANSII color escape sequence corresponding to [levelName]. Colors
225 /// are defined for: severe, error, warning, or info. Returns null if the level
226 /// name is not recognized.
227 String colorOf(String levelName) {
228 levelName = levelName.toLowerCase();
229 if (levelName == 'shout' || levelName == 'severe' || levelName == 'error') {
230 return _RED_COLOR;
231 }
232 if (levelName == 'warning') return _MAGENTA_COLOR;
233 if (levelName == 'info') return _CYAN_COLOR;
234 return null;
235 }
236
237 const String _RED_COLOR = '\u001b[31m';
238 const String _MAGENTA_COLOR = '\u001b[35m';
239 const String _CYAN_COLOR = '\u001b[36m';
240 const String GREEN_COLOR = '\u001b[32m';
241 const String NO_COLOR = '\u001b[0m';
242
243 class OutWriter {
244 final String _path;
245 final StringBuffer _sb = new StringBuffer();
246 int _indent = 0;
247 String _prefix = "";
248 bool _needsIndent = true;
249
250 OutWriter(this._path);
251
252 void write(String string, [int indent = 0]) {
253 if (indent < 0) inc(indent);
254
255 var lines = string.split('\n');
256 for (var i = 0, end = lines.length - 1; i < end; i++) {
257 _writeln(lines[i]);
258 }
259 _write(lines.last);
260
261 if (indent > 0) inc(indent);
262 }
263
264 void _writeln(String string) {
265 if (_needsIndent && string.isNotEmpty) _sb.write(_prefix);
266 _sb.writeln(string);
267 _needsIndent = true;
268 }
269
270 void _write(String string) {
271 if (_needsIndent && string.isNotEmpty) {
272 _sb.write(_prefix);
273 _needsIndent = false;
274 }
275 _sb.write(string);
276 }
277
278 void inc([int n = 2]) {
279 _indent = _indent + n;
280 assert(_indent >= 0);
281 _prefix = "".padRight(_indent);
282 }
283
284 void dec([int n = 2]) {
285 _indent = _indent - n;
286 assert(_indent >= 0);
287 _prefix = "".padRight(_indent);
288 }
289
290 void close() {
291 new File(_path).writeAsStringSync('$_sb');
292 }
293 }
294
295 SourceLocation locationForOffset(LineInfo lineInfo, Uri uri, int offset) {
296 var loc = lineInfo.getLocation(offset);
297 return new SourceLocation(offset,
298 sourceUrl: uri, line: loc.lineNumber - 1, column: loc.columnNumber - 1);
299 }
300
301 String resourceOutputPath(Uri resourceUri, Uri entryUri, String runtimeDir) {
302 if (resourceUri.scheme == 'package') return resourceUri.path;
303
304 if (resourceUri.scheme != 'file') return null;
305
306 var entryPath = entryUri.path;
307 // The entry uri is either a directory or a dart/html file. If the latter,
308 // trim the file.
309 var entryDir = entryPath.endsWith('.dart') || entryPath.endsWith('.html')
310 ? path.dirname(entryPath)
311 : entryPath;
312 var filepath = path.normalize(path.join(entryDir, resourceUri.path));
313 if (path.isWithin(runtimeDir, filepath)) {
314 filepath = path.relative(filepath, from: runtimeDir);
315 return path.join('dev_compiler', 'runtime', filepath);
316 }
317
318 return path.relative(resourceUri.path, from: entryDir);
319 }
320
321 /// Given an annotated [node] and a [test] function, returns the first matching
322 /// constant valued annotation.
323 ///
324 /// For example if we had the ClassDeclaration node for `FontElement`:
325 ///
326 /// @js.JS('HTMLFontElement')
327 /// @deprecated
328 /// class FontElement { ... }
329 ///
330 /// We could match `@deprecated` with a test function like:
331 ///
332 /// (v) => v.type.name == 'Deprecated' && v.type.element.library.isDartCore
333 ///
334 DartObject findAnnotation(Element element, bool test(DartObject value)) {
335 for (var metadata in element.metadata) {
336 var value = metadata.constantValue;
337 if (value != null && test(value)) return value;
338 }
339 return null;
340 }
341
342 /// Given a constant [value], a [fieldName], and an [expectedType], returns the
343 /// value of that field.
344 ///
345 /// If the field is missing or is not [expectedType], returns null.
346 DartObject getConstantField(
347 DartObject value, String fieldName, DartType expectedType) {
348 var f = value?.getField(fieldName);
349 return (f == null || f.type != expectedType) ? null : f;
350 }
351
352 DartType fillDynamicTypeArgs(DartType t, TypeProvider types) {
353 if (t is ParameterizedType) {
354 var dyn = new List.filled(t.typeArguments.length, types.dynamicType);
355 return t.substitute2(dyn, t.typeArguments);
356 }
357 return t;
358 }
359
360 /// Similar to [SimpleIdentifier] inGetterContext, inSetterContext, and
361 /// inDeclarationContext, this method returns true if [node] is used in an
362 /// invocation context such as a MethodInvocation.
363 bool inInvocationContext(SimpleIdentifier node) {
364 var parent = node.parent;
365 return parent is MethodInvocation && parent.methodName == node;
366 }
367
368 // TODO(vsm): Move this onto the appropriate class. Ideally, we'd attach
369 // it to TypeProvider.
370
371 /// Searches all supertype, in order of most derived members, to see if any
372 /// [match] a condition. If so, returns the first match, otherwise returns null.
373 InterfaceType findSupertype(InterfaceType type, bool match(InterfaceType t)) {
374 for (var m in type.mixins.reversed) {
375 if (match(m)) return m;
376 }
377 var s = type.superclass;
378 if (s == null) return null;
379
380 if (match(s)) return type;
381 return findSupertype(s, match);
382 }
383
384 SourceSpanWithContext createSpanHelper(
385 LineInfo lineInfo, int start, int end, Source source, String content) {
386 var startLoc = locationForOffset(lineInfo, source.uri, start);
387 var endLoc = locationForOffset(lineInfo, source.uri, end);
388
389 var lineStart = startLoc.offset - startLoc.column;
390 // Find the end of the line. This is not exposed directly on LineInfo, but
391 // we can find it pretty easily.
392 // TODO(jmesserly): for now we do the simple linear scan. Ideally we can get
393 // some help from the LineInfo API.
394 int lineEnd = endLoc.offset;
395 int lineNum = lineInfo.getLocation(lineEnd).lineNumber;
396 while (lineEnd < content.length &&
397 lineInfo.getLocation(++lineEnd).lineNumber == lineNum);
398
399 var text = content.substring(start, end);
400 var lineText = content.substring(lineStart, lineEnd);
401 return new SourceSpanWithContext(startLoc, endLoc, text, lineText);
402 }
403
404 bool isInlineJS(Element e) =>
405 e is FunctionElement &&
406 e.library.source.uri.toString() == 'dart:_foreign_helper' &&
407 e.name == 'JS';
408
409 bool isDartMathMinMax(Element e) =>
410 e is FunctionElement &&
411 e.library.source.uri.toString() == 'dart:math' &&
412 (e.name == 'min' || e.name == 'max');
413
414 /// Parses an enum value out of a string.
415 // TODO(ochafik): generic signature.
416 dynamic parseEnum(String s, List enumValues) =>
417 enumValues.firstWhere((v) => s == getEnumName(v),
418 orElse: () => throw new ArgumentError('Unknown enum value: $s '
419 '(expected one of ${enumValues.map(getEnumName)})'));
420
421 /// Gets the "simple" name of an enum value.
422 getEnumName(v) {
423 var parts = '$v'.split('.');
424 if (parts.length != 2 || !parts.every((p) => p.isNotEmpty)) {
425 throw new ArgumentError('Invalid enum value: $v');
426 }
427 return parts[1];
428 }
429
430 class FileSystem {
431 const FileSystem();
432
433 void _ensureParentExists(String file) {
434 var dir = new Directory(path.dirname(file));
435 if (!dir.existsSync()) dir.createSync(recursive: true);
436 }
437
438 void copySync(String source, String destination) {
439 _ensureParentExists(destination);
440 new File(source).copySync(destination);
441 }
442
443 void writeAsStringSync(String file, String contents) {
444 _ensureParentExists(file);
445 new File(file).writeAsStringSync(contents);
446 }
447 }
448
449 //TODO(leafp): Is this really necessary? In theory I think
450 // the static type should always be filled in for resolved
451 // ASTs. This may be a vestigial workaround.
452 DartType getStaticType(Expression e) =>
453 e.staticType ?? DynamicTypeImpl.instance;
454
455 // TODO(leafp) Factor this out or use an existing library
456 class Tuple2<T0, T1> {
457 final T0 e0;
458 final T1 e1;
459 Tuple2(this.e0, this.e1);
460 }
OLDNEW
« no previous file with comments | « lib/src/runner/v8_runner.dart ('k') | pubspec.yaml » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698