/* * Copyright 2003-2017 JetBrains s.r.o. * * 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 jetbrains.mps.typesystem.inference; import jetbrains.mps.components.CoreComponent; import jetbrains.mps.lang.typesystem.runtime.RuntimeSupport; import jetbrains.mps.lang.typesystem.runtime.performance.RuntimeSupport_Tracer; import jetbrains.mps.lang.typesystem.runtime.performance.SubtypingManager_Tracer; import jetbrains.mps.newTypesystem.RuntimeSupportNew; import jetbrains.mps.newTypesystem.SubTypingManagerNew; import jetbrains.mps.newTypesystem.context.HoleTypecheckingContext; import jetbrains.mps.smodel.language.LanguageRegistry; import jetbrains.mps.smodel.language.LanguageRegistryListener; import jetbrains.mps.smodel.language.LanguageRuntime; import jetbrains.mps.typesystem.TypeSystemReporter; import jetbrains.mps.typesystem.inference.util.ConcurrentSubtypingCache; import jetbrains.mps.typesystem.inference.util.SubtypingCache; import jetbrains.mps.util.Computable; import jetbrains.mps.util.performance.IPerformanceTracer; import org.jetbrains.annotations.Nullable; import org.jetbrains.mps.openapi.model.SNode; import java.util.ArrayList; import java.util.List; public class TypeChecker implements CoreComponent, LanguageRegistryListener { private static TypeChecker INSTANCE; public final Object LISTENERS_LOCK = new Object(); //TypeChecker is a singleton, so we can omit remove() here though the field is not static private ThreadLocal<TypesReadListener> myTypesReadListener = new ThreadLocal<TypesReadListener>(); private List<TypeRecalculatedListener> myTypeRecalculatedListeners = new ArrayList<TypeRecalculatedListener>(5); private final LanguageRegistry myLanguageRegistry; private IPerformanceTracer myPerformanceTracer = null; private SubtypingManager mySubtypingManager; private SubtypingManager mySubtypingManagerTracer; private RuntimeSupport myRuntimeSupport; private RuntimeSupport myRuntimeSupportTracer; private RulesManager myRulesManager; private SubtypingCache myGenerationSubTypingCache = null; private ThreadLocal<Boolean> myIsGenerationThread = new ThreadLocal<Boolean>() { @Override protected Boolean initialValue() { return Boolean.FALSE; } }; private Thread myMainGenerationThread; public TypeChecker(LanguageRegistry languageRegistry) { myLanguageRegistry = languageRegistry; myRuntimeSupport = new RuntimeSupportNew(this); mySubtypingManager = new SubTypingManagerNew(this); myRulesManager = new RulesManager(this); myRuntimeSupportTracer = new RuntimeSupport_Tracer(this); mySubtypingManagerTracer = new SubtypingManager_Tracer(this); } @Override public void init() { if (INSTANCE != null) { throw new IllegalStateException("double initialization"); } INSTANCE = this; myLanguageRegistry.addRegistryListener(this); } @Override public void dispose() { myLanguageRegistry.removeRegistryListener(this); INSTANCE = null; } @Override public void afterLanguagesLoaded(Iterable<LanguageRuntime> languages) { myRulesManager.loadLanguages(languages); } @Override public void beforeLanguagesUnloaded(Iterable<LanguageRuntime> languages) { myRulesManager.unloadLanguages(languages); } /*package*/ LanguageRegistry getLanguageRegistry() { return myLanguageRegistry; } public static TypeChecker getInstance() { return INSTANCE; } private boolean isMainGenerationThread() { return Thread.currentThread() == myMainGenerationThread; } public SubtypingManager getSubtypingManager() { if (isMainGenerationThread()) { return mySubtypingManagerTracer; } return mySubtypingManager; } public RuntimeSupport getRuntimeSupport() { if (isMainGenerationThread()) { return myRuntimeSupportTracer; } return myRuntimeSupport; } public SubtypingCache getSubtypingCache() { if (isGenerationMode()) { SubtypingCache generationSubTypingCache = myGenerationSubTypingCache; if (generationSubTypingCache != null) { return generationSubTypingCache; } } return TypeContextManager.getInstance().getSubtypingCache(); } public RulesManager getRulesManager() { return myRulesManager; } private SubtypingCache createSubtypingCache() { return new ConcurrentSubtypingCache(); } public void generationStarted(IPerformanceTracer performanceTracer) { myGenerationSubTypingCache = createSubtypingCache(); initTracing(performanceTracer); myIsGenerationThread.set(Boolean.TRUE); myMainGenerationThread = Thread.currentThread(); } public void generationFinished() { myGenerationSubTypingCache = null; disposeTracing(); myIsGenerationThread.set(Boolean.FALSE); myMainGenerationThread = null; } public void generationWorkerStarted() { myIsGenerationThread.set(Boolean.TRUE); } public void generationWorkerFinished() { myIsGenerationThread.set(Boolean.FALSE); } public boolean isGenerationMode() { return myIsGenerationThread.get(); } private void initTracing(IPerformanceTracer performanceTracer) { if (performanceTracer != null) { myPerformanceTracer = performanceTracer; TypeSystemReporter.getInstance().reset(); } } private void disposeTracing() { if (myPerformanceTracer != null) { TypeSystemReporter.getInstance().printReport(10, myPerformanceTracer); myPerformanceTracer = null; } } public boolean hasPerformanceTracer() { return myPerformanceTracer != null; } public <T> T computeWithTrace(Computable<T> c, String taskName) { if (myPerformanceTracer != null) { try { myPerformanceTracer.push(taskName, true); return c.compute(); } finally { myPerformanceTracer.pop(); } } else { return c.compute(); } } public InequalitySystem getInequalitiesForHole(SNode hole, boolean holeIsAType) { HoleTypecheckingContext typeCheckingContext = TypeContextManager.getInstance().createHoleTypecheckingContext(hole); InequalitySystem inequalitySystem = typeCheckingContext.getTypechecking().computeInequalitiesForHole(hole, holeIsAType); typeCheckingContext.dispose(); return inequalitySystem; } public SNode getInferredTypeOf(final SNode node) { if (node == null) return null; TypeCheckingContext typeCheckingContext = TypeContextManager.getInstance().createInferenceTypeCheckingContext(node); SNode type = typeCheckingContext.computeTypeInferenceMode(node); typeCheckingContext.dispose(); return type; } @Nullable public SNode getTypeOf(final SNode node) { if (node == null || node.getModel() == null) return null; fireNodeTypeAccessed(node); return TypeContextManager.getInstance().getTypeOf(node); } private List<TypeRecalculatedListener> copyTypeRecalculatedListeners() { synchronized (LISTENERS_LOCK) { return new ArrayList<TypeRecalculatedListener>(myTypeRecalculatedListeners); } } public void addTypesReadListener(TypesReadListener typesReadListener) { assert myTypesReadListener.get() == null; myTypesReadListener.set(typesReadListener); } public void removeTypesReadListener(TypesReadListener typesReadListener) { assert myTypesReadListener.get() == typesReadListener; myTypesReadListener.set(null); } public void removeTypeRecalculatedListener(TypeRecalculatedListener typeRecalculatedListener) { synchronized (LISTENERS_LOCK) { myTypeRecalculatedListeners.remove(typeRecalculatedListener); } } public void addTypeRecalculatedListener(TypeRecalculatedListener typeRecalculatedListener) { synchronized (LISTENERS_LOCK) { if (!myTypeRecalculatedListeners.contains(typeRecalculatedListener)) { myTypeRecalculatedListeners.add(typeRecalculatedListener); } } } public void fireTypeWillBeRecalculatedForTerm(SNode term) { for (TypeRecalculatedListener typeRecalculatedListener : copyTypeRecalculatedListeners()) { typeRecalculatedListener.typeWillBeRecalculatedForTerm(term); } } private void fireNodeTypeAccessed(SNode term) { TypesReadListener typesReadListener = myTypesReadListener.get(); if (typesReadListener != null) { typesReadListener.nodeTypeAccessed(term); } } }