diff --git a/src/com/google/javascript/jscomp/AbstractCompiler.java b/src/com/google/javascript/jscomp/AbstractCompiler.java index e494a32751b..9d75928f84b 100644 --- a/src/com/google/javascript/jscomp/AbstractCompiler.java +++ b/src/com/google/javascript/jscomp/AbstractCompiler.java @@ -316,6 +316,15 @@ LifeCycleStage getLifeCycleStage() { */ 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 getIndex(Class type); + /** Let the PhaseOptimizer know which scope a pass is currently analyzing */ abstract void setChangeScope(Node n); diff --git a/src/com/google/javascript/jscomp/Compiler.java b/src/com/google/javascript/jscomp/Compiler.java index 1d4eca988f8..07e2fe02516 100644 --- a/src/com/google/javascript/jscomp/Compiler.java +++ b/src/com/google/javascript/jscomp/Compiler.java @@ -304,9 +304,9 @@ public Compiler() { /** * Creates a Compiler that reports errors and warnings to an output stream. */ - public Compiler(PrintStream stream) { + public Compiler(PrintStream outStream) { addChangeHandler(recentChange); - this.outStream = stream; + this.outStream = outStream; } /** @@ -2500,6 +2500,8 @@ void recordFunctionInformation() { protected final RecentChange recentChange = new RecentChange(); private final List codeChangeHandlers = new ArrayList<>(); + private final Map, IndexProvider> indexProvidersByType = + new LinkedHashMap<>(); /** Name of the synthetic input that holds synthesized externs. */ static final String SYNTHETIC_EXTERNS = "{SyntheticVarsDeclar}"; @@ -2525,6 +2527,25 @@ void removeChangeHandler(CodeChangeHandler handler) { codeChangeHandlers.remove(handler); } + @Override + void addIndexProvider(IndexProvider indexProvider) { + Class type = indexProvider.getType(); + if (indexProvidersByType.put(type, indexProvider) != null) { + throw new IllegalStateException( + "A provider is already registered for index of type " + type.getSimpleName()); + } + } + + @SuppressWarnings("unchecked") + @Override + T getIndex(Class key) { + IndexProvider indexProvider = (IndexProvider) indexProvidersByType.get(key); + if (indexProvider == null) { + return null; + } + return indexProvider.get(); + } + Node getExternsRoot() { return externsRoot; } @@ -2585,12 +2606,12 @@ private Node getChangeScopeForNode(Node n) { return n; } - n = NodeUtil.getEnclosingChangeScopeRoot(n.getParent()); - if (n == null) { + Node enclosingScopeNode = NodeUtil.getEnclosingChangeScopeRoot(n.getParent()); + if (enclosingScopeNode == null) { throw new IllegalStateException( "An enclosing scope is required for change reports but node " + n + " doesn't have one."); } - return n; + return enclosingScopeNode; } private void recordChange(Node n) { diff --git a/src/com/google/javascript/jscomp/IndexProvider.java b/src/com/google/javascript/jscomp/IndexProvider.java new file mode 100644 index 00000000000..d6985d53f5b --- /dev/null +++ b/src/com/google/javascript/jscomp/IndexProvider.java @@ -0,0 +1,27 @@ +/* + * Copyright 2017 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; + +/** Builds a datastructure and incrementally updates it when scopes change. */ +interface IndexProvider { + + /** Returns an instance of type T. */ + T get(); + + /** Returns a class literal specialized on T. */ + Class getType(); +} diff --git a/test/com/google/javascript/jscomp/CompilerTest.java b/test/com/google/javascript/jscomp/CompilerTest.java index 41e0ade18d9..75dce8592e4 100644 --- a/test/com/google/javascript/jscomp/CompilerTest.java +++ b/test/com/google/javascript/jscomp/CompilerTest.java @@ -1337,6 +1337,66 @@ public void testGetChangesAndDeletions_onlySeesChangesSinceLastRequest() { assertThat(compiler.getDeletedScopeNodesForPass("FunctionInliner")).isEmpty(); } + public void testAddIndexProvider_ThenGetIndex() { + Compiler compiler = new Compiler(); + + compiler.addIndexProvider(new IndexProvider() { + @Override + public String get() { + // Normally some shared index would be constructed/updated/returned here. + return "String"; + } + + @Override + public Class getType() { + return String.class; + }}); + compiler.addIndexProvider(new IndexProvider() { + @Override + public Double get() { + // Normally some shared index would be constructed/updated/returned here. + return Double.MAX_VALUE; + } + + @Override + public Class getType() { + return Double.class; + }}); + + // Have registered providers. + assertThat(compiler.getIndex(String.class)).isEqualTo("String"); + assertThat(compiler.getIndex(Double.class)).isEqualTo(Double.MAX_VALUE); + + // Has no registered provider. + assertThat(compiler.getIndex(Object.class)).isNull(); + } + + public void testAddIndexProviderTwice_isException() { + Compiler compiler = new Compiler(); + + IndexProvider stringIndexProvider = + new IndexProvider() { + @Override + public String get() { + // Normally some shared index would be constructed/updated/returned here. + return "String"; + } + + @Override + public Class getType() { + return String.class; + } + }; + compiler.addIndexProvider(stringIndexProvider); + + try { + compiler.addIndexProvider(stringIndexProvider); + fail("expected duplicate index addition to fail"); + } catch (IllegalStateException e) { + // expected + } + } + private static CompilerOptions createNewFlagBasedOptions() { CompilerOptions opt = new CompilerOptions(); CompilationLevel.ADVANCED_OPTIMIZATIONS.setOptionsForCompilationLevel(opt);