-
Notifications
You must be signed in to change notification settings - Fork 1.1k
/
AbstractCompiler.java
696 lines (572 loc) · 21.7 KB
/
AbstractCompiler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
/*
* Copyright 2009 The Closure Compiler Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.google.javascript.jscomp;
import static com.google.common.base.Preconditions.checkArgument;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Supplier;
import com.google.common.collect.ImmutableMap;
import com.google.javascript.jscomp.deps.ModuleLoader;
import com.google.javascript.jscomp.modules.ModuleMap;
import com.google.javascript.jscomp.modules.ModuleMetadataMap;
import com.google.javascript.jscomp.parsing.Config;
import com.google.javascript.jscomp.parsing.parser.FeatureSet;
import com.google.javascript.jscomp.parsing.parser.trees.Comment;
import com.google.javascript.jscomp.type.ReverseAbstractInterpreter;
import com.google.javascript.rhino.ErrorReporter;
import com.google.javascript.rhino.InputId;
import com.google.javascript.rhino.Node;
import com.google.javascript.rhino.jstype.JSTypeRegistry;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.annotation.Nullable;
/**
* An abstract compiler, to help remove the circular dependency of
* passes on JSCompiler.
*
* This is an abstract class, so that we can make the methods package-private.
*
* @author nicksantos@google.com (Nick Santos)
*/
public abstract class AbstractCompiler implements SourceExcerptProvider {
static final DiagnosticType READ_ERROR = DiagnosticType.error(
"JSC_READ_ERROR", "Cannot read file {0}: {1}");
protected Map<String, Object> annotationMap = new HashMap<>();
/** Will be called before each pass runs. */
abstract void beforePass(String passName);
/**
* Will be called after each pass finishes.
*/
abstract void afterPass(String passName);
private LifeCycleStage stage = LifeCycleStage.RAW;
// TODO(nicksantos): Decide if all of these are really necessary.
// Many of them are just accessors that should be passed to the
// CompilerPass's constructor.
/**
* Looks up an input (possibly an externs input) by input id.
* May return null.
*/
public abstract CompilerInput getInput(InputId inputId);
/** Looks up a source file by name. May return null. */
@Nullable
abstract SourceFile getSourceFileByName(String sourceName);
@Nullable
abstract Node getScriptNode(String filename);
/** Gets the module graph. */
@Nullable
abstract JSModuleGraph getModuleGraph();
/**
* Gets the inputs in the order in which they are being processed. Only for use by {@code
* AbstractCompilerRunner}.
*/
abstract Iterable<CompilerInput> getInputsInOrder();
/**
* Gets the total number of inputs.
*
* <p>This can be useful as a guide for the initial allocated size for data structures.
*/
abstract int getNumberOfInputs();
//
// Intermediate state and results produced and needed by particular passes.
// TODO(rluble): move these into the general structure for keeping state between pass runs.
//
/** Adds exported names to keep track. */
public abstract void addExportedNames(Set<String> exportedVariableNames);
/** Gets the names that have been exported. */
public abstract Set<String> getExportedNames();
/** Sets the variable renaming map */
public abstract void setVariableMap(VariableMap variableMap);
/** Sets the property renaming map */
public abstract void setPropertyMap(VariableMap propertyMap);
/** Sets the string replacement map */
public abstract void setStringMap(VariableMap stringMap);
/** Sets the css names found during compilation. */
public abstract void setCssNames(Map<String, Integer> newCssNames);
/** Sets the id generator for cross-module motion. */
public abstract void setIdGeneratorMap(String serializedIdMappings);
/** Gets the id generator for cross-module motion. */
public abstract IdGenerator getCrossModuleIdGenerator();
/** Sets the naming map for anonymous functions */
public abstract void setAnonymousFunctionNameMap(VariableMap functionMap);
//
// End of intermediate state needed by passes.
//
/**
* Sets the type-checking pass that ran most recently.
*/
abstract void setTypeCheckingHasRun(boolean hasRun);
/** Gets the type-checking pass that ran most recently. */
abstract boolean hasTypeCheckingRun();
/**
* Gets a central registry of type information from the compiled JS.
*/
public abstract JSTypeRegistry getTypeRegistry();
public abstract void clearJSTypeRegistry();
abstract void forwardDeclareType(String typeName);
/**
* Gets a memoized scope creator with type information. Only used by jsdev.
*/
abstract ScopeCreator getTypedScopeCreator();
/**
* Gets the top scope.
*/
public abstract TypedScope getTopScope();
/**
* Gets a memoized scope creator without type information, used by the checks and optimization
* passes to avoid continuously recreating the entire scope.
*/
abstract IncrementalScopeCreator getScopeCreator();
/**
* Stores a memoized scope creator without type information, used by the checks and optimization
* passes to avoid continuously recreating the entire scope.
*/
abstract void putScopeCreator(IncrementalScopeCreator creator);
/**
* Report an error or warning.
*/
public abstract void report(JSError error);
/**
* Report an internal error.
*/
abstract void throwInternalError(String msg, Throwable cause);
/**
* Gets the current coding convention.
*/
public abstract CodingConvention getCodingConvention();
/**
* Passes that make modifications in a scope that is different than the Compiler.currentScope use
* this (eg, InlineVariables and many others)
*/
public abstract void reportChangeToEnclosingScope(Node n);
/**
* Mark modifications in a scope that is different than the Compiler.currentScope use this (eg,
* InlineVariables and many others)
*/
public abstract void reportChangeToChangeScope(Node changeScopeRoot);
/**
* Mark a specific function node as known to be deleted. Is part of having accurate change
* tracking which is necessary to streamline optimizations.
*/
abstract void reportFunctionDeleted(Node node);
/**
* Sets the CssRenamingMap.
*/
abstract void setCssRenamingMap(CssRenamingMap map);
/**
* Gets the CssRenamingMap.
*/
abstract CssRenamingMap getCssRenamingMap();
/**
* Gets a suitable SCRIPT node to serve as a parent for code insertion. If
* {@code module} contains any inputs, the returned node will be the SCRIPT
* node corresponding to its first input. If {@code module} is empty, on the
* other hand, then the returned node will be the first SCRIPT node in a
* non-empty module that {@code module} depends on (the deepest one possible).
*
* @param module A module. If null, will return the first SCRIPT node of all
* modules.
* @return A SCRIPT node (never null).
*/
abstract Node getNodeForCodeInsertion(@Nullable JSModule module);
/**
* Only used by passes in the old type checker.
*/
abstract TypeValidator getTypeValidator();
/**
* Gets the central registry of type violations.
*/
abstract Iterable<TypeMismatch> getTypeMismatches();
/**
* Gets all types that are used implicitly as a
* matching interface type. These are
* recorded as TypeMismatchs only for convenience
*/
abstract Iterable<TypeMismatch> getImplicitInterfaceUses();
abstract void setExternExports(String externExports);
/**
* Parses code for injecting.
*/
abstract Node parseSyntheticCode(String code);
/**
* Parses code for injecting, and associate it with a given source file.
*/
abstract Node parseSyntheticCode(String filename, String code);
/**
* Parses code for testing.
*/
@VisibleForTesting
abstract Node parseTestCode(String code);
/**
* Prints a node to source code.
*/
public abstract String toSource();
/**
* Prints a node to source code.
*/
public abstract String toSource(Node root);
/**
* Gets a default error reporter for injecting into Rhino.
*/
abstract ErrorReporter getDefaultErrorReporter();
/**
* Get an interpreter for type analysis.
*/
public abstract ReverseAbstractInterpreter getReverseAbstractInterpreter();
/**
* @return The current life-cycle stage of the AST we're working on.
*/
LifeCycleStage getLifeCycleStage() {
return stage;
}
/**
* Generates unique ids.
*/
abstract Supplier<String> getUniqueNameIdSupplier();
/**
* @return Whether any errors have been encountered that
* should stop the compilation process.
*/
abstract boolean hasHaltingErrors();
/**
* Register a listener for code change events.
*/
abstract void addChangeHandler(CodeChangeHandler handler);
/**
* Remove a listener for code change events.
*/
abstract void removeChangeHandler(CodeChangeHandler handler);
/** Register a provider for some type of index. */
abstract void addIndexProvider(IndexProvider<?> indexProvider);
/**
* Returns, from a provider, the desired index of type T, otherwise null if no provider is
* registered for the given type.
*/
abstract <T> T getIndex(Class<T> type);
/** A monotonically increasing value to identify a change */
abstract int getChangeStamp();
/**
* An accumulation of changed scope nodes since the last time the given pass was run. A returned
* empty list means no scope nodes have changed since the last run and a returned null means this
* is the first time the pass has run.
*/
abstract List<Node> getChangedScopeNodesForPass(String passName);
/**
* An accumulation of deleted scope nodes since the last time the given pass was run. A returned
* null or empty list means no scope nodes have been deleted since the last run or this is the
* first time the pass has run.
*/
abstract List<Node> getDeletedScopeNodesForPass(String passName);
/** Called to indicate that the current change stamp has been used */
abstract void incrementChangeStamp();
/** Returns the root of the source tree, ignoring externs */
abstract Node getJsRoot();
/** True iff a function changed since the last time a pass was run */
abstract boolean hasScopeChanged(Node n);
/**
* Represents the different contexts for which the compiler could have
* distinct configurations.
*/
static enum ConfigContext {
/**
* Normal JavaScript.
*/
DEFAULT,
/**
* Externs files.
*/
EXTERNS
}
/**
* Returns the parser configuration for the specified context.
*/
abstract Config getParserConfig(ConfigContext context);
/**
* Normalizes the types of AST nodes in the given tree, and
* annotates any nodes to which the coding convention applies so that passes
* can read the annotations instead of using the coding convention.
*/
abstract void prepareAst(Node root);
/**
* Gets the error manager.
*/
public abstract ErrorManager getErrorManager();
/**
* Set the current life-cycle state.
*/
void setLifeCycleStage(LifeCycleStage stage) {
this.stage = stage;
}
/**
* Are the nodes equal for the purpose of inlining?
* If type aware optimizations are on, type equality is checked.
*/
abstract boolean areNodesEqualForInlining(Node n1, Node n2);
/**
* Set if RegExp global properties are used.
* @param references Whether there are references to the RegExp global object
* properties.
*/
abstract void setHasRegExpGlobalReferences(boolean references);
/**
* @return Whether the AST contains references to the RegExp global object
* properties.
*/
abstract boolean hasRegExpGlobalReferences();
/**
* @return The error level the given error object will be reported at.
*/
abstract CheckLevel getErrorLevel(JSError error);
static enum LifeCycleStage implements Serializable {
RAW,
// See constraints put on the tree by Normalize.java
NORMALIZED,
// The normalize pass has put constraints on the tree,
// but variables and properties have been renamed so
// coding conventions no longer apply.
NORMALIZED_OBFUSCATED;
boolean isNormalized() {
return this == NORMALIZED || this == NORMALIZED_OBFUSCATED;
}
boolean isNormalizedUnobfuscated() {
return this == NORMALIZED;
}
boolean isNormalizedObfuscated() {
return this == NORMALIZED_OBFUSCATED;
}
}
/**
* Runs a given compiler-pass by calling its {@code process()} method.
* @param pass The pass to be run.
*/
abstract void process(CompilerPass pass);
/**
* Returns the root node of the AST, which includes both externs and source.
*/
public abstract Node getRoot();
abstract CompilerOptions getOptions();
abstract FeatureSet getFeatureSet();
abstract void setFeatureSet(FeatureSet fs);
// TODO(bashir) It would be good to extract a single dumb data object with
// only getters and setters that keeps all global information we keep for a
// compiler instance. Then move some of the functions of this class there.
/**
* Updates the list of references for variables in global scope.
*
* @param refMapPatch Maps each variable to all of its references; may contain
* references collected from the whole AST or only a SCRIPT sub-tree.
* @param collectionRoot The root of sub-tree in which reference collection
* has been done. This should either be a SCRIPT node (if collection is
* done on a single file) or it is assumed that collection is on full AST.
*/
abstract void updateGlobalVarReferences(Map<Var, ReferenceCollection>
refMapPatch, Node collectionRoot);
/**
* This can be used to get the list of all references to all global variables
* based on all previous calls to {@code updateGlobalVarReferences}.
*
* @return The reference collection map associated to global scope variable.
*/
abstract GlobalVarReferenceMap getGlobalVarReferences();
/**
* @return a CompilerInput that can be modified to add additional extern
* definitions to the beginning of the externs AST
*/
abstract CompilerInput getSynthesizedExternsInput();
/**
* @return a CompilerInput that can be modified to add additional extern
* definitions to the end of the externs AST
*/
abstract CompilerInput getSynthesizedExternsInputAtEnd();
/**
* @return a number in [0,1] range indicating an approximate progress of the
* last compile. Note this should only be used as a hint and no assumptions
* should be made on accuracy, even a completed compile may choose not to set
* this to 1.0 at the end.
*/
public abstract double getProgress();
/**
* Gets the last pass name set by setProgress.
*/
abstract String getLastPassName();
/**
* Sets the progress percentage as well as the name of the last pass that
* ran (if available).
* @param progress A percentage expressed as a double in the range [0, 1].
* Use -1 if you just want to set the last pass name.
*/
abstract void setProgress(double progress, @Nullable String lastPassName);
/**
* The subdir js/ contains libraries of code that we inject
* at compile-time only if requested by this function.
*
* Notice that these libraries will almost always create global symbols.
*
* @param resourceName The name of the library. For example, if "base" is
* is specified, then we load js/base.js
* @param force Inject the library even if compiler options say not to.
* @return The last node of the most-recently-injected runtime library.
* If new code was injected, this will be the last expression node of the
* library. If the caller needs to add additional code, they should add
* it as the next sibling of this node. If no runtime libraries have been
* injected, then null is returned.
*/
abstract Node ensureLibraryInjected(String resourceName, boolean force);
/**
* Sets the names of the properties defined in externs.
* @param externProperties The set of property names defined in externs.
*/
abstract void setExternProperties(Set<String> externProperties);
/**
* Gets the names of the properties defined in externs or null if
* GatherExternProperties pass was not run yet.
*/
abstract Set<String> getExternProperties();
/**
* Adds a {@link SourceMapInput} for the given {@code sourceFileName}, to be used for error
* reporting and source map combining.
*/
public abstract void addInputSourceMap(String name, SourceMapInput sourceMap);
abstract void addComments(String filename, List<Comment> comments);
/** Indicates whether a property has a getter or a setter, or both. */
public enum PropertyAccessKind {
// To save space properties without getters or setters won't appear
// in the maps at all, but NORMAL will be returned by some methods.
NORMAL(0),
GETTER_ONLY(1),
SETTER_ONLY(2),
GETTER_AND_SETTER(3);
final byte flags;
PropertyAccessKind(int flags) {
this.flags = (byte) flags;
}
boolean hasGetter() {
return (flags & 1) != 0;
}
boolean hasSetter() {
return (flags & 2) != 0;
}
boolean hasGetterOrSetter() {
return (flags & 3) != 0;
}
// used to combine information from externs and from sources
PropertyAccessKind unionWith(PropertyAccessKind other) {
int combinedFlags = this.flags | other.flags;
switch (combinedFlags) {
case 0:
return NORMAL;
case 1:
return GETTER_ONLY;
case 2:
return SETTER_ONLY;
case 3:
return GETTER_AND_SETTER;
default:
throw new IllegalStateException("unexpected value: " + combinedFlags);
}
}
}
/**
* Returns a map containing an entry for every property name found in the externs files with
* a getter and / or setter defined.
*
* <p>Property names for which there are no getters or setters will not be in the map.
*/
abstract ImmutableMap<String, PropertyAccessKind> getExternGetterAndSetterProperties();
/** Sets the map of extern properties with getters and setters. */
abstract void setExternGetterAndSetterProperties(
ImmutableMap<String, PropertyAccessKind> externGetterAndSetterProperties);
/**
* Returns a map containing an entry for every property name found in the source AST with
* a getter and / or setter defined.
*
* <p>Property names for which there are no getters or setters will not be in the map.
*/
abstract ImmutableMap<String, PropertyAccessKind> getSourceGetterAndSetterProperties();
/** Sets the map of properties with getters and setters defined in the sources AST. */
abstract void setSourceGetterAndSetterProperties(
ImmutableMap<String, PropertyAccessKind> externGetterAndSetterProperties);
/**
* Returns any property seen in the externs or source with the given name was a getter, setter, or
* both.
*
* <p>This defaults to {@link PropertyAccessKind#NORMAL} for any property not known to have a
* getter or setter, even for property names that do not exist in the given program.
*/
final PropertyAccessKind getPropertyAccessKind(String property) {
return getExternGetterAndSetterProperties()
.getOrDefault(property, PropertyAccessKind.NORMAL)
.unionWith(
getSourceGetterAndSetterProperties().getOrDefault(property, PropertyAccessKind.NORMAL));
}
/**
* Returns all the comments from the given file.
*/
abstract List<Comment> getComments(String filename);
/**
* Stores a map of default @define values. These values
* can be overridden by values specifically set in the CompilerOptions.
*/
abstract void setDefaultDefineValues(ImmutableMap<String, Node> values);
/**
* Gets a map of default @define values. These values
* can be overridden by values specifically set in the CompilerOptions.
*/
abstract ImmutableMap<String, Node> getDefaultDefineValues();
/**
* Gets the module loader.
*/
abstract ModuleLoader getModuleLoader();
/** Lookup the type of a module from its name. */
abstract CompilerInput.ModuleType getModuleTypeByName(String moduleName);
/**
* Sets an annotation for the given key.
*
* @param key the annotation key
* @param object the object to store as the annotation
*/
void setAnnotation(String key, Object object) {
checkArgument(object != null, "The stored annotation value cannot be null.");
Preconditions.checkArgument(
!annotationMap.containsKey(key), "Cannot overwrite the existing annotation '%s'.", key);
annotationMap.put(key, object);
}
/**
* Gets the annotation for the given key.
*
* @param key the annotation key
* @return the annotation object for the given key if it has been set, or null
*/
@Nullable
Object getAnnotation(String key) {
return annotationMap.get(key);
}
/**
* Returns a new AstFactory that will add type information to the nodes it creates if and only if
* type type checking has already happened.
*/
public AstFactory createAstFactory() {
return hasTypeCheckingRun()
? AstFactory.createFactoryWithTypes(getTypeRegistry())
: AstFactory.createFactoryWithoutTypes();
}
public abstract ModuleMetadataMap getModuleMetadataMap();
public abstract void setModuleMetadataMap(ModuleMetadataMap moduleMetadataMap);
public abstract ModuleMap getModuleMap();
public abstract void setModuleMap(ModuleMap moduleMap);
}