| // 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. | |
| import 'package:dart2js_info/info.dart'; | |
| import 'graph.dart'; | |
| /// Computes a graph of dependencies from [info]. | |
| Graph<Info> graphFromInfo(AllInfo info) { | |
| print( | |
| ' info: dependency graph information is work in progress and' | |
| ' might be incomplete', | |
| ); | |
| // Note: we are combining dependency information that is computed in two ways | |
| // (functionInfo.uses vs allInfo.dependencies). | |
| // TODO(sigmund): fix inconsistencies between these two ways, stick with one | |
| // of them. | |
| // TODO(sigmund): create a concrete implementation of InfoGraph, instead of | |
| // using the EdgeListGraph. | |
| var graph = EdgeListGraph<Info>(); | |
| for (var f in info.functions) { | |
| graph.addNode(f); | |
| for (var g in f.uses) { | |
| graph.addEdge(f, g.target); | |
| } | |
| final dependencies = info.dependencies[f]; | |
| if (dependencies != null) { | |
| for (var g in dependencies) { | |
| graph.addEdge(f, g); | |
| } | |
| } | |
| } | |
| for (var f in info.fields) { | |
| graph.addNode(f); | |
| for (var g in f.uses) { | |
| graph.addEdge(f, g.target); | |
| } | |
| final dependencies = info.dependencies[f]; | |
| if (dependencies != null) { | |
| for (var g in dependencies) { | |
| graph.addEdge(f, g); | |
| } | |
| } | |
| } | |
| return graph; | |
| } | |
| /// Provide a qualified name associated with [info]. Qualified names consist of | |
| /// the library's canonical URI concatenated with a library-unique kernel name. | |
| // See: https://github.com/dart-lang/sdk/blob/47eff41cdbfea4a178208dfc3137ba2b6bea0e36/pkg/compiler/lib/src/js_emitter/startup_emitter/fragment_emitter.dart#L978 | |
| // TODO(sigmund): guarantee that the name is actually unique. | |
| String qualifiedName(Info f) { | |
| assert(f is ClosureInfo || f is ClassInfo); | |
| Info? element = f; | |
| String? name; | |
| while (element != null) { | |
| if (element is LibraryInfo) { | |
| name = '${element.uri}:$name'; | |
| return name; | |
| } else { | |
| name = name ?? element.name; | |
| element = element.parent; | |
| } | |
| } | |
| return ''; | |
| } | |
| /// Provide a unique long name associated with [info]. | |
| // TODO(sigmund): guarantee that the name is actually unique. | |
| String longName(Info info, {bool useLibraryUri = false, bool forId = false}) { | |
| final infoPath = <Info>[]; | |
| Info? localInfo = info; | |
| while (localInfo != null) { | |
| infoPath.add(localInfo); | |
| localInfo = localInfo.parent; | |
| } | |
| var sb = StringBuffer(); | |
| var first = true; | |
| for (var segment in infoPath.reversed) { | |
| if (!first) sb.write('.'); | |
| // TODO(sigmund): ensure that the first segment is a LibraryInfo. | |
| // assert(!first || segment is LibraryInfo); | |
| // (today might not be true for closure classes). | |
| if (segment is LibraryInfo) { | |
| // TODO(kevmoo): Remove this when dart2js can be invoked with an app-root | |
| // custom URI | |
| if (useLibraryUri && forId && segment.uri.isScheme('file')) { | |
| assert(Uri.base.isScheme('file')); | |
| var currentBase = Uri.base.path; | |
| var segmentString = segment.uri.path; | |
| // If longName is being called to calculate an element ID (forId = true) | |
| // then use a relative path for the longName calculation | |
| // This allows a more stable ID for cases when files are generated into | |
| // temp directories – e.g. with pkg:build_web_compilers | |
| if (segmentString.startsWith(currentBase)) { | |
| segmentString = segmentString.substring(currentBase.length); | |
| } | |
| sb.write(segmentString); | |
| } else { | |
| sb.write(useLibraryUri ? segment.uri : segment.name); | |
| } | |
| sb.write('::'); | |
| } else { | |
| first = false; | |
| sb.write(segment.name); | |
| } | |
| } | |
| return sb.toString(); | |
| } | |
| /// Provides the package name associated with [info] or null otherwise. | |
| String? packageName(Info info) { | |
| while (info.parent != null) { | |
| info = info.parent!; | |
| } | |
| if (info is LibraryInfo) { | |
| if (info.uri.isScheme('package')) { | |
| return '${info.uri}'.split('/').first; | |
| } | |
| } | |
| return null; | |
| } | |
| /// Provides the group name associated with [info]. | |
| /// | |
| /// This corresponds to the package name, a Dart core library, 'file' for loose | |
| /// files, or null otherwise. | |
| String? libraryGroupName(Info info) { | |
| while (info.parent != null) { | |
| info = info.parent!; | |
| } | |
| if (info is LibraryInfo) { | |
| if (info.uri.isScheme('package')) { | |
| return '${info.uri}'.split('/').first; | |
| } | |
| if (info.uri.isScheme('dart')) { | |
| return '${info.uri}'; | |
| } | |
| if (info.uri.hasScheme) { | |
| return info.uri.scheme; | |
| } | |
| } | |
| return null; | |
| } | |
| /// Produce a string containing [value] padded with white space up to [n] chars. | |
| String pad(Object value, int n, {bool right = false}) { | |
| var s = '$value'; | |
| if (s.length >= n) return s; | |
| // ignore: avoid_dynamic_calls | |
| var pad = ' ' * (n - s.length); | |
| return right ? '$s$pad' : '$pad$s'; | |
| } |