/*
* Copyright 2003-2012 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.newTypesystem.context;
import gnu.trove.THashSet;
import jetbrains.mps.errors.IErrorReporter;
import jetbrains.mps.typesystem.inference.TypeSubstitution;
import jetbrains.mps.newTypesystem.context.typechecking.BaseTypechecking;
import jetbrains.mps.newTypesystem.context.typechecking.IncrementalTypechecking;
import jetbrains.mps.newTypesystem.context.component.SimpleTypecheckingComponent;
import jetbrains.mps.languageScope.LanguageScopeExecutor;
import jetbrains.mps.newTypesystem.state.State;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.typesystem.TypeSystemReporter;
import jetbrains.mps.typesystem.inference.EquationInfo;
import jetbrains.mps.typesystem.inference.TypeChecker;
import jetbrains.mps.util.Computable;
import jetbrains.mps.util.Pair;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
/**
* User: fyodor
* Date: 11/6/12
*/
public abstract class SimpleTypecheckingContext<
STATE extends State,
TCHECK extends BaseTypechecking<STATE, ? extends SimpleTypecheckingComponent<STATE>>>
extends BaseTypecheckingContext {
private TCHECK myTypechecking;
private STATE myState;
private boolean myCurrentlyChecking;
public SimpleTypecheckingContext(SNode rootNode, TypeChecker typeChecker) {
super(rootNode, typeChecker);
myState = createState();
setTypechecking(createTypechecking());
}
@SuppressWarnings("unchecked")
protected abstract TCHECK createTypechecking();
@SuppressWarnings("unchecked")
protected STATE createState() {
return (STATE) new State(this);
}
@Override
public STATE getState() {
assert myState != null;
return myState;
}
public TCHECK getTypechecking() {
return myTypechecking;
}
@Override
public boolean isSingleTypeComputation() {
return true;
}
@Override
public void addDependencyForCurrent(SNode node) {
// do nothing
}
@Override
public void reportMessage(SNode nodeWithError, IErrorReporter errorReporter) {
// do nothing
}
@Override
public boolean isIncrementalMode() {
return false;
}
@Override
public SNode getTypeOf_normalMode(SNode node) {
throw new IllegalStateException("Invalid usage of SimpleTypecheckingContext");
}
@Override
public SNode getTypeOf_generationMode(final SNode node) {
long start = System.nanoTime();
SNode result = LanguageScopeExecutor.execWithModelScope(node.getModel(), new Computable<SNode>() {
@Override
public SNode compute() {
return getTypechecking().computeTypesForNodeDuringGeneration(node);
}
});
TypeSystemReporter.getInstance().reportTypeOf(node, (System.nanoTime() - start));
return result;
}
@Override
public SNode getTypeOf_resolveMode(SNode node, TypeChecker typeChecker) {
throw new IllegalStateException("Invalid usage of SimpleTypecheckingContext");
}
@Override
public SNode getTypeOf(SNode node, TypeChecker typeChecker) {
if (node == null) return null;
synchronized (TYPECHECKING_LOCK) {
return getTypeOf_resolveMode(node, typeChecker);
}
}
@Override
public void setIsNonTypesystemComputation() {
assert false;
}
@Override
public void resetIsNonTypesystemComputation() {
assert false;
}
@Override
public boolean isNonTypesystemComputation() {
return false;
}
protected void applyNonTypesystemRules() {
// do nothing
}
@Override
public void clear() {
}
@Override
public IncrementalTypechecking getBaseNodeTypesComponent() {
assert false;
return null;
}
protected final void setTypechecking(TCHECK typechecking) {
assert myTypechecking == null;
assert typechecking != null;
myTypechecking = typechecking;
}
@Override
public SNode typeOf(SNode node) {
return typeOf(node, null, null, true);
}
@Override
public SNode typeOf(SNode node, String ruleModel, String ruleId, boolean addDependency) {
EquationInfo info = new EquationInfo(node, "typeOf", ruleModel, ruleId);
if (node == null) return null;
BaseTypechecking currentTypesComponent = getTypechecking(); //first, in current component
if (currentTypesComponent != null) {
//--- for incremental algorithm:
currentTypesComponent.addNodeToFrontier(node);
processDependency(node, ruleModel, ruleId, addDependency);
}
return getState().typeOf(node, info);
}
@Override
public SNode computeTypeInferenceMode(SNode node) {
synchronized (TYPECHECKING_LOCK) {
// myIsInferenceMode = true;
try {
return getTypechecking().computeTypesForNodeInferenceMode(node);
} finally {
// myIsInferenceMode = false;
}
}
}
@Override
public SNode getTypeInGenerationMode(SNode node) {
try {
return getTypeOf_generationMode(node);
} finally {
// TODO [ts] move dispose -> trace tree
getTypechecking().dispose();
}
}
@Override
public boolean checkIfNotChecked(SNode node, boolean useNonTypesystemRules) {
synchronized (TYPECHECKING_LOCK) {
// recursion guard
if (myCurrentlyChecking) return true;
try {
this.myCurrentlyChecking = true;
if (!isCheckedRoot(useNonTypesystemRules)) {
checkRoot();
if (useNonTypesystemRules) {
applyNonTypesystemRules();
}
}
return true;
}
finally {
this.myCurrentlyChecking = false;
}
}
}
@Override
public void checkRoot() {
checkRoot(false);
}
@Override
public void checkRoot(final boolean refreshTypes) {
synchronized (TYPECHECKING_LOCK) {
LanguageScopeExecutor.execWithModelScope(myNode.getModel(), new Computable<Object>() {
@Override
public Object compute() {
getTypechecking().computeTypes(refreshTypes);
getTypechecking().setCheckedTypesystem();
return null;
}
});
}
}
@Override
public Set<Pair<SNode, List<IErrorReporter>>> checkRootAndGetErrors(boolean refreshTypes) {
synchronized (TYPECHECKING_LOCK) {
// recursion guard
if (myCurrentlyChecking) Collections.emptyList();
try {
this.myCurrentlyChecking = true;
checkRoot(refreshTypes);
//non-typesystem checks
applyNonTypesystemRules();
final Set<Pair<SNode, List<IErrorReporter>>> nodesWithErrors = getTypechecking().getNodesWithErrors(true);
final THashSet<Pair<SNode, List<IErrorReporter>>> result = new THashSet<Pair<SNode, List<IErrorReporter>>>(nodesWithErrors);
result.addAll(getTypechecking().getNodesWithErrors(false));
return result;
}
finally {
this.myCurrentlyChecking = false;
}
}
}
@Override
public Set<Pair<SNode, List<IErrorReporter>>> getNodesWithErrors(boolean typesystemErrors) {
return getTypechecking().getNodesWithErrors(typesystemErrors);
}
@Override
public boolean isCheckedRoot(boolean considerNonTypesystemRules) {
return getTypechecking().isChecked(considerNonTypesystemRules);
}
@Override
public List<IErrorReporter> getTypeMessagesDontCheck(SNode node) {
return getTypechecking().getErrors(node);
}
@Override
public IErrorReporter getTypeMessageDontCheck(SNode node) {
List<IErrorReporter> messages = getTypeMessagesDontCheck(node);
if (messages.isEmpty()) {
return null;
}
Collections.sort(messages, new Comparator<IErrorReporter>() {
@Override
public int compare(IErrorReporter o1, IErrorReporter o2) {
return o2.getMessageStatus().compareTo(o1.getMessageStatus());
}
});
return messages.get(0);
}
@Override
public TypeSubstitution getSubstitution(final SNode origNode) {
return LanguageScopeExecutor.execWithLanguageScope(null, new Computable<TypeSubstitution>() {
@Override
public TypeSubstitution compute() {
return myTypechecking.getTypecheckingComponent().lookupSubstitution(origNode, SimpleTypecheckingContext.this);
}
});
}
protected void processDependency(SNode node, String ruleModel, String ruleId, boolean addDependency) {
// do nothing
}
}