package org.overture.codegen.vdm2jml.predgen; import java.util.LinkedList; import java.util.List; import org.apache.commons.lang.BooleanUtils; import org.apache.log4j.Logger; import org.overture.ast.util.ClonableString; import org.overture.codegen.assistant.TypeAssistantIR; import org.overture.codegen.ir.INode; import org.overture.codegen.ir.STypeIR; import org.overture.codegen.ir.declarations.ADefaultClassDeclIR; import org.overture.codegen.ir.declarations.ANamedTypeDeclIR; import org.overture.codegen.ir.expressions.SVarExpIR; import org.overture.codegen.ir.statements.AMetaStmIR; import org.overture.codegen.ir.types.AClassTypeIR; import org.overture.codegen.ir.types.AExternalTypeIR; import org.overture.codegen.ir.types.AMapMapTypeIR; import org.overture.codegen.ir.types.ARecordTypeIR; import org.overture.codegen.ir.types.ASeqSeqTypeIR; import org.overture.codegen.ir.types.ASetSetTypeIR; import org.overture.codegen.ir.types.ATupleTypeIR; import org.overture.codegen.ir.types.AUnionTypeIR; import org.overture.codegen.ir.types.AUnknownTypeIR; import org.overture.codegen.vdm2jml.JmlGenUtil; import org.overture.codegen.vdm2jml.JmlGenerator; import org.overture.codegen.vdm2jml.data.RecClassInfo; import org.overture.codegen.vdm2jml.predgen.info.AbstractTypeInfo; import org.overture.codegen.vdm2jml.predgen.info.LeafTypeInfo; import org.overture.codegen.vdm2jml.predgen.info.MapInfo; import org.overture.codegen.vdm2jml.predgen.info.NamedTypeInfo; import org.overture.codegen.vdm2jml.predgen.info.NamedTypeInvDepCalculator; import org.overture.codegen.vdm2jml.predgen.info.SeqInfo; import org.overture.codegen.vdm2jml.predgen.info.SetInfo; import org.overture.codegen.vdm2jml.predgen.info.TupleInfo; import org.overture.codegen.vdm2jml.predgen.info.UnionInfo; import org.overture.codegen.vdm2jml.predgen.info.UnknownLeaf; import org.overture.codegen.vdm2jml.util.NameGen; public class TypePredUtil { private TypePredHandler handler; private Logger log = Logger.getLogger(this.getClass().getName()); public TypePredUtil(TypePredHandler handler) { this.handler = handler; } public List<String> consJmlCheck(ADefaultClassDeclIR encClass, String jmlVisibility, String annotationType, boolean invChecksGuard, AbstractTypeInfo typeInfo, SVarExpIR var) { List<String> predStrs = new LinkedList<>(); if (handler.getDecorator().buildRecValidChecks()) { appendRecValidChecks(invChecksGuard, typeInfo, var, predStrs, encClass); } StringBuilder inv = new StringBuilder(); inv.append("//@ "); if (jmlVisibility != null) { inv.append(jmlVisibility); inv.append(' '); } inv.append(annotationType); inv.append(' '); if (invChecksGuard) { inv.append(consInvChecksGuard(encClass)); inv.append('('); } String javaPackage = handler.getJmlGen().getJavaSettings().getJavaRootPackage(); NameGen nameGen = new NameGen(encClass); String consCheckExp = typeInfo.consCheckExp(encClass.getName(), javaPackage, var.getName(), nameGen); if (consCheckExp != null) { inv.append(consCheckExp); } else { log.error("Expression could not be checked"); // TODO: Consider better handling inv.append("true"); } if (invChecksGuard) { inv.append(')'); } inv.append(';'); predStrs.add(inv.toString()); return predStrs; } private String consInvChecksGuard(ADefaultClassDeclIR encClass) { StringBuilder sb = new StringBuilder(); sb.append(handler.getJmlGen().getAnnotator().consInvChecksOnNameEncClass(encClass)); sb.append(JmlGenerator.JML_IMPLIES); return sb.toString(); } private void appendRecValidChecks(boolean invChecksGuard, AbstractTypeInfo typeInfo, SVarExpIR var, List<String> predStrs, ADefaultClassDeclIR encClass) { List<ARecordTypeIR> recordTypes = getRecTypes(typeInfo); if (!recordTypes.isEmpty()) { for (ARecordTypeIR rt : recordTypes) { StringBuilder inv = new StringBuilder(); String fullyQualifiedRecType = fullyQualifiedRecType(rt); inv.append("//@ "); inv.append(JmlGenerator.JML_ASSERT_ANNOTATION); inv.append(' '); if (invChecksGuard) { inv.append(consInvChecksGuard(encClass)); } if (var.getType() instanceof ARecordTypeIR) { if (handler.getJmlGen().getJmlSettings().genInvariantFor()) { inv.append(JmlGenerator.JML_INVARIANT_FOR); inv.append('('); inv.append(var.getName()); inv.append(')'); // e.g. invariant_for(r1) } else { inv.append(var.getName()); inv.append('.'); inv.append(JmlGenerator.REC_VALID_METHOD_CALL); // e.g. r1.valid() } } else { inv.append(var.getName()); inv.append(JmlGenerator.JAVA_INSTANCEOF); inv.append(fullyQualifiedRecType); inv.append(JmlGenerator.JML_IMPLIES); // So far we have: // e.g. r1 instanceof project.Entrytypes.R3 if (handler.getJmlGen().getJmlSettings().genInvariantFor()) { inv.append(JmlGenerator.JML_INVARIANT_FOR); inv.append('('); inv.append(consRecVarCast(var, fullyQualifiedRecType)); ; inv.append(')'); // e.g. r1 instanceof project.Entrytypes.R3 ==> \invariant_for((project.Entrytypes.R3) r1); } else { inv.append(consRecVarCast(var, fullyQualifiedRecType)); inv.append('.'); inv.append(JmlGenerator.REC_VALID_METHOD_CALL); // e.g. r1 instanceof project.Entrytypes.R3 ==> ((project.Entrytypes.R3) r1).valid() } } inv.append(';'); predStrs.add(inv.toString()); } } } private String consRecVarCast(SVarExpIR var, String fullyQualifiedRecType) { return "((" + fullyQualifiedRecType + ") " + var.getName() + ")"; } public String fullyQualifiedRecType(ARecordTypeIR rt) { String defClass = rt.getName().getDefiningClass(); String recPackage = JmlGenUtil.consRecPackage(defClass, handler.getJmlGen().getJavaGen().getJavaSettings().getJavaRootPackage()); String fullyQualifiedRecType = recPackage + "." + rt.getName().getName(); return fullyQualifiedRecType; } private List<ARecordTypeIR> getRecTypes(AbstractTypeInfo typeInfo) { List<ARecordTypeIR> recTypes = new LinkedList<>(); List<LeafTypeInfo> leaves = typeInfo.getLeafTypesRecursively(); for (LeafTypeInfo leaf : leaves) { if (leaf.getType() instanceof ARecordTypeIR) { recTypes.add((ARecordTypeIR) leaf.getType()); } } return recTypes; } public List<AMetaStmIR> consAssertStm(AbstractTypeInfo invTypes, ADefaultClassDeclIR encClass, SVarExpIR var, INode node, RecClassInfo recInfo) { boolean inAccessor = node != null && recInfo != null && recInfo.inAccessor(node); List<AMetaStmIR> asserts = new LinkedList<>(); List<String> assertStrs = consJmlCheck(encClass, null, JmlGenerator.JML_ASSERT_ANNOTATION, inAccessor, invTypes, var); for (String a : assertStrs) { AMetaStmIR assertStm = new AMetaStmIR(); List<ClonableString> assertMetaData = handler.getJmlGen().getAnnotator().consMetaData(a); handler.getJmlGen().getAnnotator().appendMetaData(assertStm, assertMetaData); asserts.add(assertStm); } return asserts; } public AMetaStmIR consVarNotNullAssert(String varName) { AMetaStmIR assertStm = new AMetaStmIR(); List<ClonableString> assertMetaData = handler.getJmlGen().getAnnotator().consMetaData("//@ assert " + varName + " != null;"); handler.getJmlGen().getAnnotator().appendMetaData(assertStm, assertMetaData); return assertStm; } public AbstractTypeInfo findTypeInfo(STypeIR type) { TypeAssistantIR assist = handler.getJmlGen().getJavaGen().getInfo().getTypeAssistant(); if (type.getNamedInvType() != null) { ANamedTypeDeclIR namedInv = type.getNamedInvType(); String defModule = namedInv.getName().getDefiningClass(); String typeName = namedInv.getName().getName(); NamedTypeInfo info = NamedTypeInvDepCalculator.findTypeInfo(handler.getJmlGen().getTypeInfoList(), defModule, typeName); if (assist.isOptional(type)) { info = new NamedTypeInfo(info.getTypeName(), info.getDefModule(), info.hasInv(), true, info.getDomainType()); } if (info == null) { log.error("Could not find info for named type '" + typeName + "' defined in module '" + defModule); } return info; // We do not need to collect sub named invariant types } else { if (type instanceof AUnionTypeIR) { List<AbstractTypeInfo> types = new LinkedList<>(); for (STypeIR t : ((AUnionTypeIR) type).getTypes()) { types.add(findTypeInfo(t)); } return new UnionInfo(assist.isOptional(type), types); } else if (type instanceof ATupleTypeIR) { List<AbstractTypeInfo> types = new LinkedList<>(); for (STypeIR t : ((ATupleTypeIR) type).getTypes()) { types.add(findTypeInfo(t)); } return new TupleInfo(assist.isOptional(type), types); } else if (type instanceof ASeqSeqTypeIR) { ASeqSeqTypeIR seqType = (ASeqSeqTypeIR) type; STypeIR elementType = seqType.getSeqOf(); return new SeqInfo(assist.isOptional(seqType), findTypeInfo(elementType), BooleanUtils.isTrue(seqType.getSeq1())); } else if (type instanceof ASetSetTypeIR) { ASetSetTypeIR setType = (ASetSetTypeIR) type; STypeIR elementType = setType.getSetOf(); return new SetInfo(assist.isOptional(setType), findTypeInfo(elementType)); } else if (type instanceof AMapMapTypeIR) { AMapMapTypeIR mapType = (AMapMapTypeIR) type; AbstractTypeInfo domInfo = findTypeInfo(mapType.getFrom()); AbstractTypeInfo rngInfo = findTypeInfo(mapType.getTo()); boolean injective = BooleanUtils.isTrue(mapType.getInjective()); return new MapInfo(assist.isOptional(mapType), domInfo, rngInfo, injective); } else if (type instanceof AUnknownTypeIR || type instanceof AClassTypeIR || type instanceof AExternalTypeIR) { // Iterators are class types for instance return new UnknownLeaf(); } else { return new LeafTypeInfo(type, assist.isOptional(type)); } } } }