Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion liquidjava-verifier/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<groupId>io.github.liquid-java</groupId>
<artifactId>liquidjava-verifier</artifactId>
<version>0.0.14</version>
<version>0.0.15</version>
<name>liquidjava-verifier</name>
<description>LiquidJava Verifier</description>
<url>https://github.com/liquid-java/liquidjava</url>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,32 +104,31 @@ public void addVarToContext(RefinedVariable var) {
var.addSuperTypes(type.getSuperclass(), type.getSuperInterfaces());
}

public RefinedVariable addVarToContext(String simpleName, CtTypeReference<?> type, Predicate c,
CtElement placementInCode) {
public RefinedVariable addVarToContext(String simpleName, CtTypeReference<?> type, Predicate c, CtElement element) {
RefinedVariable vi = new Variable(simpleName, type, c);
vi.addPlacementInCode(PlacementInCode.createPlacement(placementInCode));
vi.addPlacementInCode(element);
vi.addSuperTypes(type.getSuperclass(), type.getSuperInterfaces());
addVarToContext(vi);
return vi;
}

public RefinedVariable addInstanceToContext(String simpleName, CtTypeReference<?> type, Predicate c,
CtElement placementInCode) {
CtElement element) {
RefinedVariable vi = new VariableInstance(simpleName, type, c);
vi.addPlacementInCode(PlacementInCode.createPlacement(placementInCode));
vi.addPlacementInCode(element);
if (!ctxInstanceVars.contains(vi))
addInstanceVariable(vi);
return vi;
}

public void addRefinementToVariableInContext(String name, CtTypeReference<?> type, Predicate et,
CtElement placementInCode) {
CtElement element) {
if (hasVariable(name)) {
RefinedVariable vi = getVariableByName(name);
vi.setRefinement(et);
vi.addPlacementInCode(PlacementInCode.createPlacement(placementInCode));
vi.addPlacementInCode(element);
} else {
addVarToContext(name, type, et, placementInCode);
addVarToContext(name, type, et, element);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,27 @@
import java.util.Map;
import java.util.Set;

import liquidjava.utils.Utils;
import spoon.reflect.cu.SourcePosition;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.declaration.CtExecutable;
import spoon.reflect.declaration.CtParameter;

public class ContextHistory {
private static ContextHistory instance;

private Map<String, Map<String, Set<RefinedVariable>>> fileScopeVars; // file -> (scope -> variables in scope)
private Map<String, Set<GhostState>> ghosts; // file -> ghosts

// globals
private Set<AliasWrapper> aliases;
private Set<RefinedVariable> instanceVars;
private Set<RefinedVariable> globalVars;
private Set<GhostState> ghosts;
private Set<AliasWrapper> aliases;

private ContextHistory() {
fileScopeVars = new HashMap<>();
instanceVars = new HashSet<>();
globalVars = new HashSet<>();
ghosts = new HashSet<>();
ghosts = new HashMap<>();
aliases = new HashSet<>();
}

Expand All @@ -41,31 +44,42 @@ public void clearHistory() {
}

public void saveContext(CtElement element, Context context) {
SourcePosition pos = element.getPosition();
if (pos == null || pos.getFile() == null)
String file = getFile(element);
if (file == null)
return;

// add variables in scope for this position
String file = pos.getFile().getAbsolutePath();
// add variables in scope
String scope = getScopePosition(element);
fileScopeVars.putIfAbsent(file, new HashMap<>());
fileScopeVars.get(file).put(scope, new HashSet<>(context.getCtxVars()));

// add other elements in context
// add other elements in context (except ghosts)
instanceVars.addAll(context.getCtxInstanceVars());
globalVars.addAll(context.getCtxGlobalVars());
ghosts.addAll(context.getGhostStates());
aliases.addAll(context.getAliases());
}

public void saveGhost(CtElement element, GhostState ghost) {
String file = getFile(element);
if (file == null)
return;
ghosts.putIfAbsent(file, new HashSet<>());
ghosts.get(file).add(ghost);
}

private String getFile(CtElement element) {
SourcePosition pos = element.getPosition();
if (pos == null || pos.getFile() == null)
return null;

return pos.getFile().getAbsolutePath();
}

public String getScopePosition(CtElement element) {
CtElement startElement = element instanceof CtParameter<?> ? element.getParent() : element;
SourcePosition annPosition = Utils.getFirstLJAnnotationPosition(startElement);
SourcePosition pos = element.getPosition();
SourcePosition innerPosition = pos;
if (element instanceof CtExecutable<?> executable) {
if (executable.getBody() != null)
innerPosition = executable.getBody().getPosition();
}
return String.format("%d:%d-%d:%d", innerPosition.getLine(), innerPosition.getColumn() + 1, pos.getEndLine(),
return String.format("%d:%d-%d:%d", annPosition.getLine(), annPosition.getColumn() + 1, pos.getEndLine(),
pos.getEndColumn());
}

Expand All @@ -81,7 +95,7 @@ public Set<RefinedVariable> getGlobalVars() {
return globalVars;
}

public Set<GhostState> getGhosts() {
public Map<String, Set<GhostState>> getGhosts() {
return ghosts;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,21 @@
import java.util.List;
import java.util.Set;
import liquidjava.rj_language.Predicate;
import liquidjava.utils.Utils;
import spoon.reflect.cu.SourcePosition;
import spoon.reflect.declaration.CtElement;
import spoon.reflect.reference.CtTypeReference;

public abstract class RefinedVariable extends Refined {
private final List<CtTypeReference<?>> supertypes;
private PlacementInCode placementInCode;
private boolean isParameter;
private SourcePosition annPosition;

public RefinedVariable(String name, CtTypeReference<?> type, Predicate c) {
super(name, type, c);
supertypes = new ArrayList<>();
isParameter = false;
}

public abstract Predicate getMainRefinement();
Expand All @@ -34,14 +40,23 @@ public void addSuperTypes(CtTypeReference<?> ts, Set<CtTypeReference<?>> sts) {
supertypes.add(ct);
}

public void addPlacementInCode(PlacementInCode s) {
placementInCode = s;
public void addPlacementInCode(CtElement element) {
placementInCode = PlacementInCode.createPlacement(element);
annPosition = Utils.getFirstLJAnnotationPosition(element);
}

public void addPlacementInCode(PlacementInCode placement) {
placementInCode = placement;
}

public PlacementInCode getPlacementInCode() {
return placementInCode;
}

public SourcePosition getAnnPosition() {
return annPosition;
}

@Override
public int hashCode() {
final int prime = 31;
Expand All @@ -65,4 +80,12 @@ public boolean equals(Object obj) {
return supertypes.equals(other.supertypes);
}
}

public void setIsParameter(boolean b) {
isParameter = b;
}

public boolean isParameter() {
return isParameter;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import liquidjava.diagnostics.warnings.ExternalClassNotFoundWarning;
import liquidjava.diagnostics.warnings.ExternalMethodNotFoundWarning;
import liquidjava.processor.context.Context;
import liquidjava.processor.context.ContextHistory;
import liquidjava.processor.context.GhostFunction;
import liquidjava.processor.facade.GhostDTO;
import liquidjava.processor.refinement_checker.general_checkers.MethodsFunctionsChecker;
Expand All @@ -28,8 +29,9 @@
import spoon.reflect.reference.CtTypeReference;

public class ExternalRefinementTypeChecker extends TypeChecker {
String prefix;
Diagnostics diagnostics = Diagnostics.getInstance();
private String prefix;
private final Diagnostics diagnostics = Diagnostics.getInstance();
private final ContextHistory contextHistory = ContextHistory.getInstance();

public ExternalRefinementTypeChecker(Context context, Factory factory) {
super(context, factory);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import liquidjava.diagnostics.errors.*;
import liquidjava.processor.context.AliasWrapper;
import liquidjava.processor.context.Context;
import liquidjava.processor.context.ContextHistory;
import liquidjava.processor.context.GhostFunction;
import liquidjava.processor.context.GhostState;
import liquidjava.processor.context.RefinedVariable;
Expand Down Expand Up @@ -40,6 +41,7 @@ public abstract class TypeChecker extends CtScanner {
protected final Context context;
protected final Factory factory;
protected final VCChecker vcChecker;
private final ContextHistory contextHistory = ContextHistory.getInstance();

public TypeChecker(Context context, Factory factory) {
this.context = context;
Expand Down Expand Up @@ -81,7 +83,7 @@ public Optional<Predicate> getRefinementFromAnnotation(CtElement element) throws
if (ref.isPresent()) {
Predicate p = new Predicate(ref.get(), element);
if (!p.getExpression().isBooleanExpression()) {
SourcePosition position = Utils.getAnnotationPosition(element, ref.get());
SourcePosition position = Utils.getLJAnnotationPosition(element, ref.get());
throw new InvalidRefinementError(position, "Refinement predicate must be a boolean expression",
ref.get());
}
Expand Down Expand Up @@ -157,6 +159,7 @@ private void createStateSet(CtNewArray<String> e, int set, CtElement element) th
gs.setRefinement(Predicate.createEquals(ip, Predicate.createLit(Integer.toString(order), Types.INT)));
// open(THIS) -> state1(THIS) == 1
context.addToGhostClass(g.getParentClassName(), gs);
contextHistory.saveGhost(element, gs);
}
order++;
}
Expand Down Expand Up @@ -191,6 +194,7 @@ private void createStateGhost(String string, CtElement element, SourcePosition p
CtTypeReference<?> r = factory.Type().createReference(gd.returnType());
GhostState gs = new GhostState(gd.name(), param, r, qn);
context.addToGhostClass(sn, gs);
contextHistory.saveGhost(element, gs);
}

protected String getQualifiedClassName(CtElement element) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ private Predicate handleFunctionRefinements(RefinedFunction f, CtElement method,
c = oc.get().substituteVariable(Keys.WILDCARD, paramName);
param.putMetadata(Keys.REFINEMENT, c);
RefinedVariable v = rtc.getContext().addVarToContext(param.getSimpleName(), param.getType(), c, param);
v.setIsParameter(true);
rtc.getMessageFromAnnotation(param).ifPresent(v::setMessage);
if (v instanceof Variable)
f.addArgRefinements((Variable) v);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ private static Predicate createStatePredicate(String value, String targetClass,
boolean isTo, String prefix) throws LJError {
Predicate p = new Predicate(value, e, prefix);
if (!p.getExpression().isBooleanExpression()) {
SourcePosition position = Utils.getAnnotationPosition(e, value);
SourcePosition position = Utils.getLJAnnotationPosition(e, value);
throw new InvalidRefinementError(position, "State refinement transition must be a boolean expression",
value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ protected Expression parse(String ref, CtElement element) throws LJError {
return RefinementsParser.createAST(ref, prefix);
} catch (LJError e) {
// add location info to error
SourcePosition pos = Utils.getAnnotationPosition(element, ref);
SourcePosition pos = Utils.getLJAnnotationPosition(element, ref);
e.setPosition(pos);
throw e;
}
Expand Down
11 changes: 10 additions & 1 deletion liquidjava-verifier/src/main/java/liquidjava/utils/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,21 @@ public static String qualifyName(String prefix, String name) {
return String.format("%s.%s", prefix, name);
}

public static SourcePosition getAnnotationPosition(CtElement element, String refinement) {
public static SourcePosition getLJAnnotationPosition(CtElement element, String refinement) {
return element.getAnnotations().stream()
.filter(a -> isLiquidJavaAnnotation(a) && hasRefinementValue(a, "\"" + refinement + "\"")).findFirst()
.map(CtElement::getPosition).orElse(element.getPosition());
}

public static SourcePosition getFirstLJAnnotationPosition(CtElement element) {
return element.getAnnotations().stream().filter(Utils::isLiquidJavaAnnotation).map(CtElement::getPosition)
.min((p1, p2) -> {
if (p1.getLine() != p2.getLine())
return Integer.compare(p1.getLine(), p2.getLine());
return Integer.compare(p1.getColumn(), p2.getColumn());
}).orElse(element.getPosition());
}

private static boolean isLiquidJavaAnnotation(CtAnnotation<?> annotation) {
return annotation.getAnnotationType().getQualifiedName().startsWith("liquidjava.specification");
}
Expand Down