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

Side by Side Diff: pkg/analyzer/lib/src/summary/prelink.dart

Issue 1576743002: Create a prelinker for summaries. (Closed) Base URL: git@github.com:dart-lang/sdk.git@master
Patch Set: Created 4 years, 11 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
OLDNEW
(Empty)
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
scheglov 2016/01/10 05:15:12 2016 :-)
Paul Berry 2016/01/10 22:15:17 Done.
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 import 'package:analyzer/src/summary/base.dart';
6 import 'package:analyzer/src/summary/format.dart';
7
8 /**
9 * Create a [PrelinkedLibraryBuilder] corresponding to the given
10 * [definingUnit], which should be the defining compilation unit for a library.
11 * Compilation units referenced by the defining compilation unit via `part`
12 * declarations will be retrieved using [getPart]. Public namespaces for
13 * libraries referenced by the defining compilation unit via `import`
14 * declarations (and files reachable from them via `part` and `export`
15 * declarations) will be retrieved using [getImport].
16 */
17 PrelinkedLibraryBuilder prelink(BuilderContext ctx, UnlinkedUnit definingUnit,
18 GetPartCallback getPart, GetImportCallback getImport) {
19 return new _Prelinker(ctx, definingUnit, getPart, getImport).prelink();
20 }
21
22 /**
23 * Type of the callback used by the prelinker to obtain public namespace
24 * information about libraries imported by the library to be prelinked (and
25 * the transitive closure of parts and exports reachable from those libraries).
26 * [relativeUri] should be interpreted relative to the defining compilation
27 * unit.
scheglov 2016/01/10 05:15:12 The defining compilation unit of... which library?
Paul Berry 2016/01/10 22:15:17 The defining compilation unit of the library being
28 *
29 * If no file exists at the given uri, null should be returned.
30 */
31 typedef UnlinkedPublicNamespace GetImportCallback(String relativeUri);
32
33 /**
34 * Type of the callback used by the prelinker to obtain unlinked summaries of
35 * part files of the library to be prelinked. [relaviteUri] should be
36 * interpreted relative to the defining compilation unit.
Brian Wilkerson 2016/01/10 16:45:26 Ditto
Paul Berry 2016/01/10 22:15:17 Done.
37 *
38 * If no file exists at the given uri, null should be returned.
Brian Wilkerson 2016/01/10 16:45:26 nit: `null`
Paul Berry 2016/01/10 22:15:17 Done.
39 */
40 typedef UnlinkedUnit GetPartCallback(String relativeUri);
41
42 /**
43 * A [NameFilter] represents the set of filtering rules implied by zero or more
44 * combinators in an `export` or `import` statement.
45 */
46 class NameFilter {
47 /**
48 * A [NameFilter] representing no filtering at all (i.e. no combinators).
49 */
50 static final NameFilter identity =
51 new NameFilter._(hiddenNames: new Set<String>());
52
53 /**
54 * If this [NameFilter] accepts a finite number of names and hides all
55 * others, the (possibly empty) set of names it accepts. Otherwise `null`.
56 */
57 final Set<String> shownNames;
58
59 /**
60 * If [shownNames] is `null`, the (possibly empty) set of names not accepted
61 * by this filter (all other names are accepted). If [shownNames] is not
62 * `null`, then [hiddenNames] will be `null`.
63 */
64 final Set<String> hiddenNames;
65
66 /**
67 * Create a [NameFilter] based on the given [combinator].
68 */
69 factory NameFilter.forCombinator(UnlinkedCombinator combinator) {
70 if (combinator.shows.isNotEmpty) {
71 return new NameFilter._(shownNames: combinator.shows.toSet());
72 } else {
73 return new NameFilter._(hiddenNames: combinator.hides.toSet());
74 }
75 }
76
77 /**
78 * Create a [NameFilter] based on the given (possibly empty) sequence of
79 * [combinators].
80 */
81 factory NameFilter.forCombinators(List<UnlinkedCombinator> combinators) {
82 NameFilter result = identity;
83 for (UnlinkedCombinator combinator in combinators) {
84 result = result.merge(new NameFilter.forCombinator(combinator));
85 }
86 return result;
87 }
88
89 const NameFilter._({this.shownNames, this.hiddenNames});
90
91 /**
92 * Determine if the given [name] is accepted by this [NameFilter].
93 */
94 bool accepts(String name) {
95 if (shownNames != null) {
96 return shownNames.contains(name);
97 } else {
98 return !hiddenNames.contains(name);
99 }
100 }
101
102 /**
103 * Produce a new [NameFilter] by combining this [NameFilter] with another
104 * one. The new [NameFilter] will only accept names that would be accepted
105 * by both input filters.
106 */
107 NameFilter merge(NameFilter other) {
108 if (shownNames != null) {
109 if (other.shownNames != null) {
110 return new NameFilter._(
111 shownNames: shownNames.intersection(other.shownNames));
112 } else {
113 return new NameFilter._(
114 shownNames: shownNames.difference(other.hiddenNames));
115 }
116 } else {
117 if (other.shownNames != null) {
118 return new NameFilter._(
119 shownNames: other.shownNames.difference(hiddenNames));
120 } else {
121 return new NameFilter._(
122 hiddenNames: hiddenNames.union(other.hiddenNames));
123 }
124 }
125 }
126 }
127
128 /**
129 * A [_Meaning] stores all the information necessary to find the declaration
130 * referred to by a name in a namespace.
131 */
132 class _Meaning {
133 /**
134 * Which unit in the dependent library contains the declared entity.
135 */
136 final int unit;
137
138 /**
139 * The kind of entity being referred to.
140 */
141 final PrelinkedReferenceKind kind;
142
143 /**
144 * Which of the dependencies of the library being prelinked contains the
145 * declared entity.
146 */
147 final int dependency;
148
149 /**
150 * If the entity being referred to is generic, the number of type parameters
151 * it accepts. Otherwise zero.
152 */
153 final int numTypeParameters;
154
155 _Meaning(this.unit, this.kind, this.dependency, this.numTypeParameters);
156
157 /**
158 * Encode this [_Meaning] as a [PrelinkedReference].
159 */
160 PrelinkedReferenceBuilder encode(BuilderContext ctx) {
161 return encodePrelinkedReference(ctx,
162 unit: unit,
163 kind: kind,
164 dependency: dependency,
165 numTypeParameters: numTypeParameters);
166 }
167 }
168
169 /**
170 * A [_Meaning] representing a prefix introduced by an import directive.
171 */
172 class _PrefixMeaning extends _Meaning {
173 final Map<String, _Meaning> namespace = <String, _Meaning>{};
174
175 _PrefixMeaning() : super(0, PrelinkedReferenceKind.prefix, 0, 0);
176 }
177
178 /**
179 * Helper class containing temporary data structures needed to prelink a single
180 * library.
181 *
182 * Note: throughout this class, a `null` value for a relative URI represents
183 * the defining compilation unit of the library being prelinked.
184 */
185 class _Prelinker {
186 final BuilderContext ctx;
187 final UnlinkedUnit definingUnit;
188 final GetPartCallback getPart;
189 final GetImportCallback getImport;
190
191 /**
192 * Cache of values returned by [getImport].
193 */
194 final Map<String, UnlinkedPublicNamespace> importCache =
195 <String, UnlinkedPublicNamespace>{};
196
197 /**
198 * Cache of values returned by [getPart].
199 */
200 final Map<String, UnlinkedUnit> partCache = <String, UnlinkedUnit>{};
201
202 /**
203 * Names defined inside the library being prelinked.
204 */
205 final Map<String, _Meaning> privateNamespace;
206
207 /**
208 * List of dependencies of the library being prelinked. This will be output
209 * to [PrelinkedLibrary.dependencies].
210 */
211 final List<PrelinkedDependencyBuilder> dependencies;
212
213 /**
214 * Map from the relative URI of a dependent library to the index of the
215 * corresponding entry in [dependencies].
216 */
217 final Map<String, int> uriToDependency = <String, int>{null: 0};
218
219 /**
220 * List of public namespaces corresponding to each entry in [dependencies].
221 */
222 final List<Map<String, _Meaning>> dependencyToPublicNamespace =
223 <Map<String, _Meaning>>[null];
224
225 _Prelinker(
226 BuilderContext ctx, this.definingUnit, this.getPart, this.getImport)
227 : ctx = ctx,
228 dependencies = <PrelinkedDependencyBuilder>[
229 encodePrelinkedDependency(ctx)
230 ],
231 privateNamespace = <String, _Meaning>{
232 '': new _Meaning(0, PrelinkedReferenceKind.classOrEnum, 0, 0)
233 } {
234 partCache[null] = definingUnit;
235 importCache[null] = definingUnit.publicNamespace;
236 }
237
238 /**
239 * Compute the public namespace for the library whose URI is reachable from
240 * [definingUnit] via [relativeUri], by aggregating together public namespace
241 * information from all of its parts.
242 */
243 Map<String, _Meaning> aggregatePublicNamespace(String relativeUri) {
244 if (uriToDependency.containsKey(relativeUri)) {
245 return dependencyToPublicNamespace[uriToDependency[relativeUri]];
246 }
247 assert(dependencies.length == dependencyToPublicNamespace.length);
248 int dependency = dependencies.length;
249 uriToDependency[relativeUri] = dependency;
250 dependencies.add(encodePrelinkedDependency(ctx, uri: relativeUri));
251
252 Map<String, _Meaning> aggregated = <String, _Meaning>{};
253
254 List<String> unitUris = getUnitUris(relativeUri);
255 for (int unitNum = 0; unitNum < unitUris.length; unitNum++) {
256 String unitUri = unitUris[unitNum];
257 UnlinkedPublicNamespace importedNamespace = getImportCached(unitUri);
258 if (importedNamespace == null) {
259 continue;
260 }
261 for (UnlinkedPublicName name in importedNamespace.names) {
262 aggregated.putIfAbsent(
263 name.name,
264 () => new _Meaning(
265 unitNum, name.kind, dependency, name.numTypeParameters));
266 }
267 }
268
269 dependencyToPublicNamespace.add(aggregated);
270 return aggregated;
271 }
272
273 /**
274 * Compute the export namespace for the library whose URI is reachable from
275 * [definingUnit] via [relativeUri], by aggregating together public namespace
276 * information from the library and the transitive closure of its exports.
277 */
278 Map<String, _Meaning> computeExportNamespace(String relativeUri) {
279 Map<String, _Meaning> exportNamespace =
280 aggregatePublicNamespace(relativeUri);
281 void chaseExports(
282 NameFilter filter, String relativeUri, Set<String> seenUris) {
283 if (seenUris.add(relativeUri)) {
284 UnlinkedPublicNamespace exportedNamespace =
285 getImportCached(relativeUri);
286 if (exportedNamespace != null) {
287 for (UnlinkedExportPublic export in exportedNamespace.exports) {
288 String exportUri = resolveUri(relativeUri, export.uri);
289 aggregatePublicNamespace(exportUri)
290 .forEach((String name, _Meaning meaning) {
291 if (filter.accepts(name) && !exportNamespace.containsKey(name)) {
292 exportNamespace[name] = meaning;
293 }
294 });
295 chaseExports(
296 filter.merge(new NameFilter.forCombinators(export.combinators)),
297 exportUri,
298 seenUris);
299 }
300 }
301 seenUris.remove(relativeUri);
302 }
303 }
304 chaseExports(NameFilter.identity, relativeUri, new Set<String>());
305 return exportNamespace;
306 }
307
308 /**
309 * Extract all the names defined in [unit] (which is the [unitNum]th unit in
310 * the library being prelinked) and store them in [privateNamespace].
311 * Excludes names introduced by `import` statements.
312 */
313 void extractPrivateNames(UnlinkedUnit unit, int unitNum) {
314 for (UnlinkedClass cls in unit.classes) {
315 privateNamespace.putIfAbsent(
316 cls.name,
317 () => new _Meaning(unitNum, PrelinkedReferenceKind.classOrEnum, 0,
318 cls.typeParameters.length));
319 }
320 for (UnlinkedEnum enm in unit.enums) {
321 privateNamespace.putIfAbsent(
322 enm.name,
323 () =>
324 new _Meaning(unitNum, PrelinkedReferenceKind.classOrEnum, 0, 0));
325 }
326 for (UnlinkedExecutable executable in unit.executables) {
327 privateNamespace.putIfAbsent(
328 executable.name,
329 () => new _Meaning(unitNum, PrelinkedReferenceKind.other, 0,
330 executable.typeParameters.length));
331 }
332 for (UnlinkedTypedef typedef in unit.typedefs) {
333 privateNamespace.putIfAbsent(
334 typedef.name,
335 () => new _Meaning(unitNum, PrelinkedReferenceKind.typedef, 0,
336 typedef.typeParameters.length));
337 }
338 for (UnlinkedVariable variable in unit.variables) {
339 privateNamespace.putIfAbsent(variable.name,
340 () => new _Meaning(unitNum, PrelinkedReferenceKind.other, 0, 0));
341 }
342 }
343
344 /**
345 * Filter the export namespace for the library whose URI is reachable from
346 * [definingUnit] via [relativeUri], retaining only those names accepted by
347 * [combinators], and store the resulting names in [result]. Names that
348 * already exist in [result] are not overwritten.
349 */
350 void filterExportNamespace(String relativeUri,
351 List<UnlinkedCombinator> combinators, Map<String, _Meaning> result) {
352 Map<String, _Meaning> exportNamespace = computeExportNamespace(relativeUri);
353 NameFilter filter = new NameFilter.forCombinators(combinators);
354 exportNamespace.forEach((String name, _Meaning meaning) {
355 if (filter.accepts(name) && !result.containsKey(name)) {
356 result[name] = meaning;
357 }
358 });
359 }
360
361 /**
362 * Wrapper around [getImport] that caches the return value in [importCache].
363 */
364 UnlinkedPublicNamespace getImportCached(String relativeUri) {
365 return importCache.putIfAbsent(relativeUri, () => getImport(relativeUri));
366 }
367
368 /**
369 * Wrapper around [getPart] that caches the return value in [partCache] and
370 * updates [importCache] appropriately.
371 */
372 UnlinkedUnit getPartCached(String relativeUri) {
373 return partCache.putIfAbsent(relativeUri, () {
374 UnlinkedUnit unit = getPart(relativeUri);
375 importCache[relativeUri] = unit?.publicNamespace;
376 return unit;
377 });
378 }
379
380 /**
381 * Compute the set of relative URIs of all the compilation units in the
382 * library whose URI is reachable from [definingUnit] via [relativeUri].
383 */
384 List<String> getUnitUris(String relativeUri) {
385 List<String> result = <String>[relativeUri];
386 UnlinkedPublicNamespace publicNamespace = getImportCached(relativeUri);
387 if (publicNamespace != null) {
388 result.addAll(publicNamespace.parts
389 .map((String uri) => resolveUri(relativeUri, uri)));
390 }
391 return result;
392 }
393
394 /**
395 * Process a single `import` declaration in the library being prelinked. The
396 * return value is the index of the imported library in [dependencies].
397 */
398 int handleImport(UnlinkedImport import) {
399 String uri = import.isImplicit ? 'dart:core' : import.uri;
400 Map<String, _Meaning> targetNamespace = null;
401 if (import.prefixReference != 0) {
402 // The name introduced by an import declaration can't have a prefix of
403 // its own.
404 assert(
405 definingUnit.references[import.prefixReference].prefixReference == 0);
406 String prefix = definingUnit.references[import.prefixReference].name;
407 _Meaning prefixMeaning = privateNamespace[prefix];
408 if (prefixMeaning is _PrefixMeaning) {
409 targetNamespace = prefixMeaning.namespace;
410 }
411 } else {
412 targetNamespace = privateNamespace;
413 }
414 filterExportNamespace(uri, import.combinators, targetNamespace);
415 return uriToDependency[uri];
416 }
417
418 /**
419 * Produce a [PrelinkedUnit] for the given [unit], by resolving every one of
420 * its references.
421 */
422 PrelinkedUnitBuilder linkUnit(UnlinkedUnit unit) {
423 if (unit == null) {
424 return encodePrelinkedUnit(ctx);
425 }
426 Map<int, Map<String, _Meaning>> prefixNamespaces =
427 <int, Map<String, _Meaning>>{};
428 List<PrelinkedReferenceBuilder> references = <PrelinkedReferenceBuilder>[];
429 for (int i = 0; i < unit.references.length; i++) {
430 UnlinkedReference reference = unit.references[i];
431 Map<String, _Meaning> namespace;
432 if (reference.prefixReference != 0) {
433 // Prefix references must always point backward.
434 assert(reference.prefixReference < i);
435 namespace = prefixNamespaces[reference.prefixReference];
436 // Prefix references must always point to proper prefixes.
437 assert(namespace != null);
438 } else {
439 namespace = privateNamespace;
440 }
441 _Meaning meaning = namespace[reference.name];
442 if (meaning != null) {
443 if (meaning is _PrefixMeaning) {
444 prefixNamespaces[i] = meaning.namespace;
445 }
446 references.add(meaning.encode(ctx));
447 } else {
448 references.add(encodePrelinkedReference(ctx,
449 kind: PrelinkedReferenceKind.unresolved));
450 }
451 }
452 return encodePrelinkedUnit(ctx, references: references);
453 }
454
455 /**
456 * Form the [PrelinkedLibrary] for the [definingUnit] that was passed to the
457 * constructor.
458 */
459 PrelinkedLibraryBuilder prelink() {
460 // Gather up the unlinked summaries for all the compilation units in the
461 // library.
462 List<UnlinkedUnit> units = getUnitUris(null).map(getPartCached).toList();
463
464 // Create the private namespace for the library by gathering all the names
465 // defined in its compilation units.
466 for (int unitNum = 0; unitNum < units.length; unitNum++) {
467 UnlinkedUnit unit = units[unitNum];
468 if (unit != null) {
469 extractPrivateNames(unit, unitNum);
470 }
471 }
472
473 // Fill in prefixes defined in import declarations.
474 for (var import in units[0].imports) {
scheglov 2016/01/10 05:15:12 Type?
Paul Berry 2016/01/10 22:15:17 Done.
475 if (import.prefixReference != 0) {
476 privateNamespace.putIfAbsent(
477 units[0].references[import.prefixReference].name,
478 () => new _PrefixMeaning());
479 }
480 }
481
482 // Fill in imported names.
483 List<int> importDependencies =
484 definingUnit.imports.map(handleImport).toList();
485
486 // Link each compilation unit.
487 List<PrelinkedUnitBuilder> linkedUnits = units.map(linkUnit).toList();
488
489 return encodePrelinkedLibrary(ctx,
490 units: linkedUnits,
491 dependencies: dependencies,
492 importDependencies: importDependencies);
493 }
494
495 /**
496 * Resolve [relativeUri] relative to [sourceUri]. Works correctly if
497 * [sourceUri] is also relative.
498 */
499 String resolveUri(String sourceUri, String relativeUri) {
500 if (sourceUri == null) {
501 return relativeUri;
502 } else {
503 return Uri.parse(sourceUri).resolve(relativeUri).toString();
504 }
505 }
506 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698