package org.overture.codegen.vdm2jml; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import org.apache.log4j.Logger; import org.overture.ast.util.ClonableString; import org.overture.codegen.ir.INode; import org.overture.codegen.ir.IRConstants; import org.overture.codegen.ir.IRStatus; import org.overture.codegen.ir.PIR; import org.overture.codegen.ir.SPatternIR; import org.overture.codegen.ir.STypeIR; import org.overture.codegen.ir.VdmNodeInfo; import org.overture.codegen.ir.declarations.ADefaultClassDeclIR; import org.overture.codegen.ir.declarations.AFieldDeclIR; import org.overture.codegen.ir.declarations.AFormalParamLocalParamIR; import org.overture.codegen.ir.declarations.AInterfaceDeclIR; import org.overture.codegen.ir.declarations.AMethodDeclIR; import org.overture.codegen.ir.declarations.ANamedTypeDeclIR; import org.overture.codegen.ir.declarations.ARecordDeclIR; import org.overture.codegen.ir.declarations.ATypeDeclIR; import org.overture.codegen.ir.expressions.AIdentifierVarExpIR; import org.overture.codegen.ir.patterns.AIdentifierPatternIR; import org.overture.codegen.ir.statements.AReturnStmIR; import org.overture.codegen.ir.types.ABoolBasicTypeIR; import org.overture.codegen.ir.types.AExternalTypeIR; import org.overture.codegen.ir.types.AMethodTypeIR; import org.overture.codegen.vdm2java.JavaCodeGen; import org.overture.codegen.vdm2java.JavaCodeGenUtil; import org.overture.codegen.vdm2java.JavaFormat; import org.overture.codegen.vdm2jml.data.RecClassInfo; import org.overture.codegen.vdm2jml.util.NameGen; public class JmlGenUtil { private JmlGenerator jmlGen; private Logger log = Logger.getLogger(this.getClass().getName()); public JmlGenUtil(JmlGenerator jmlGen) { this.jmlGen = jmlGen; } public AIdentifierVarExpIR getInvParamVar(AMethodDeclIR invMethod) { AFormalParamLocalParamIR formalParam = getInvFormalParam(invMethod); if (formalParam == null) { return null; } String paramName = getName(formalParam.getPattern()); if (paramName == null) { return null; } STypeIR paramType = formalParam.getType().clone(); return jmlGen.getJavaGen().getInfo().getExpAssistant().consIdVar(paramName, paramType); } public String getName(SPatternIR id) { if (!(id instanceof AIdentifierPatternIR)) { log.error("Expected identifier pattern " + "to be an identifier pattern at this point. Got: " + id); return null; } return ((AIdentifierPatternIR) id).getName(); } public AFormalParamLocalParamIR getInvFormalParam(AMethodDeclIR invMethod) { if (invMethod.getFormalParams().size() == 1) { return invMethod.getFormalParams().get(0); } else { log.error("Expected only a single formal parameter " + "for named invariant type method " + invMethod.getName() + " but got " + invMethod.getFormalParams().size()); if (!invMethod.getFormalParams().isEmpty()) { return invMethod.getFormalParams().get(0); } else { return null; } } } public AMethodDeclIR getInvMethod(ATypeDeclIR typeDecl) { if (typeDecl.getInv() instanceof AMethodDeclIR) { return (AMethodDeclIR) typeDecl.getInv(); } else if (typeDecl.getInv() != null) { log.error("Expected named type invariant function " + "to be a method declaration at this point. Got: " + typeDecl.getDecl()); } return null; } public List<AMethodDeclIR> getNamedTypeInvMethods(ADefaultClassDeclIR clazz) { List<AMethodDeclIR> invDecls = new LinkedList<AMethodDeclIR>(); for (ATypeDeclIR typeDecl : clazz.getTypeDecls()) { if (typeDecl.getDecl() instanceof ANamedTypeDeclIR) { AMethodDeclIR m = getInvMethod(typeDecl); if (m != null) { invDecls.add(m); } } } return invDecls; } public List<String> getRecFieldNames(ARecordDeclIR r) { List<String> args = new LinkedList<String>(); for (AFieldDeclIR f : r.getFields()) { args.add(f.getName()); } return args; } public List<ARecordDeclIR> getRecords(List<IRStatus<PIR>> ast) { List<ARecordDeclIR> records = new LinkedList<ARecordDeclIR>(); for (IRStatus<ADefaultClassDeclIR> classStatus : IRStatus.extract(ast, ADefaultClassDeclIR.class)) { for (ATypeDeclIR typeDecl : classStatus.getIrNode().getTypeDecls()) { if (typeDecl.getDecl() instanceof ARecordDeclIR) { records.add((ARecordDeclIR) typeDecl.getDecl()); } } } return records; } public String getMethodCondArgName(SPatternIR pattern) { // By now all patterns should be identifier patterns if (pattern instanceof AIdentifierPatternIR) { String paramName = ((AIdentifierPatternIR) pattern).getName(); if (jmlGen.getJavaGen().getInfo().getExpAssistant().isOld(paramName)) { paramName = toJmlOldExp(paramName); } else if (jmlGen.getJavaGen().getInfo().getExpAssistant().isResult(paramName)) { // The type checker prohibits use of 'RESULT' as name of a user specified identifier paramName = JmlGenerator.JML_RESULT; } return paramName; } else { log.error("Expected formal parameter pattern to be an indentifier pattern. Got: " + pattern); return "UNKNOWN"; } } public String toJmlOldExp(String paramName) { // Convert old name to current name (e.g. _St to St) String currentArg = jmlGen.getJavaGen().getInfo().getExpAssistant().oldNameToCurrentName(paramName); // Note that invoking the copy method on the state should be okay // because the state should never be a null pointer currentArg += ".copy()"; // Convert current name to JML old expression (e.g. \old(St) return String.format("%s(%s)", JmlGenerator.JML_OLD_PREFIX, currentArg); } /** * This change to the IR is really only to circumvent a bug in OpenJML with loading of inner classes. The only thing * that the Java code generator uses inner classes for is records. If this problem gets fixed the workaround * introduced by this method should be removed. * * @param ast * The IR passed by the Java code generator after the transformation process * @param recInfo * Since the new record classes are deep copies we need to update the record info too */ public List<IRStatus<PIR>> makeRecsOuterClasses(List<IRStatus<PIR>> ast, RecClassInfo recInfo) { List<IRStatus<PIR>> extraClasses = new LinkedList<IRStatus<PIR>>(); for (IRStatus<ADefaultClassDeclIR> status : IRStatus.extract(ast, ADefaultClassDeclIR.class)) { ADefaultClassDeclIR clazz = status.getIrNode(); List<ARecordDeclIR> recDecls = new LinkedList<ARecordDeclIR>(); for (ATypeDeclIR d : clazz.getTypeDecls()) { if (d.getDecl() instanceof ARecordDeclIR) { recDecls.add((ARecordDeclIR) d.getDecl()); } } // Note that we do not remove the type declarations (or records) from the class. // For each of the records we will make a top-level class for (ARecordDeclIR recDecl : recDecls) { ADefaultClassDeclIR recClass = new ADefaultClassDeclIR(); recInfo.registerRecClass(recClass); recClass.setMetaData(recDecl.getMetaData()); recClass.setAbstract(false); recClass.setAccess(IRConstants.PUBLIC); recClass.setSourceNode(recDecl.getSourceNode()); recClass.setStatic(false); recClass.setName(recDecl.getName()); if (recDecl.getInvariant() != null) { recClass.setInvariant(recDecl.getInvariant().clone()); } AInterfaceDeclIR recInterface = new AInterfaceDeclIR(); recInterface.setPackage("org.overture.codegen.runtime"); final String RECORD_NAME = "Record"; recInterface.setName(RECORD_NAME); AExternalTypeIR recInterfaceType = new AExternalTypeIR(); recInterfaceType.setName(RECORD_NAME); AMethodTypeIR copyMethodType = new AMethodTypeIR(); copyMethodType.setResult(recInterfaceType); AMethodDeclIR copyMethod = jmlGen.getJavaGen().getJavaFormat().getRecCreator().consCopySignature(copyMethodType); copyMethod.setAbstract(true); recInterface.getMethodSignatures().add(copyMethod); recClass.getInterfaces().add(recInterface); // Copy the record methods to the class List<AMethodDeclIR> methods = new LinkedList<AMethodDeclIR>(); for (AMethodDeclIR m : recDecl.getMethods()) { AMethodDeclIR newMethod = m.clone(); methods.add(newMethod); recInfo.updateAccessor(m, newMethod); } recClass.setMethods(methods); // Copy the record fields to the class List<AFieldDeclIR> fields = new LinkedList<AFieldDeclIR>(); for (AFieldDeclIR f : recDecl.getFields()) { AFieldDeclIR newField = f.clone(); fields.add(newField); recInfo.register(newField); } recClass.setFields(fields); // Put the record classes of module M in package <userpackage>.<modulename>types // Examples: my.pack.Mtypes if (JavaCodeGenUtil.isValidJavaPackage(jmlGen.getJavaSettings().getJavaRootPackage())) { String recPackage = consRecPackage(clazz.getName(), jmlGen.getJavaSettings().getJavaRootPackage()); recClass.setPackage(recPackage); } else { recClass.setPackage(clazz.getName() + JavaFormat.TYPE_DECL_PACKAGE_SUFFIX); } List<ClonableString> imports = new LinkedList<>(); imports.add(new ClonableString(JavaCodeGen.JAVA_UTIL)); imports.add(new ClonableString(JavaCodeGen.RUNTIME_IMPORT)); recClass.setDependencies(imports); extraClasses.add(new IRStatus<PIR>(recClass.getSourceNode().getVdmNode(), recClass.getName(), recClass, new HashSet<VdmNodeInfo>())); } } return extraClasses; } public static String consRecPackage(String defClass, String javaRootPackage) { String recPackage = ""; if (JavaCodeGenUtil.isValidJavaPackage(javaRootPackage)) { recPackage += javaRootPackage + "."; } recPackage += defClass + JavaFormat.TYPE_DECL_PACKAGE_SUFFIX; return recPackage; } public AMethodDeclIR genInvMethod(ADefaultClassDeclIR clazz, ANamedTypeDeclIR namedTypeDecl) { AReturnStmIR body = new AReturnStmIR(); body.setExp(jmlGen.getJavaGen().getInfo().getExpAssistant().consBoolLiteral(true)); STypeIR paramType = namedTypeDecl.getType(); AMethodTypeIR invMethodType = new AMethodTypeIR(); invMethodType.setResult(new ABoolBasicTypeIR()); invMethodType.getParams().add(paramType.clone()); String formalParamName = new NameGen(clazz).getName(JmlGenerator.GEN_INV_METHOD_PARAM_NAME); AFormalParamLocalParamIR formalParam = new AFormalParamLocalParamIR(); formalParam.setType(paramType.clone()); formalParam.setPattern(jmlGen.getJavaGen().getInfo().getPatternAssistant().consIdPattern(formalParamName)); AMethodDeclIR method = new AMethodDeclIR(); method.setImplicit(false); method.setAbstract(false); method.setAccess(IRConstants.PUBLIC); method.setAsync(false); method.setBody(body); method.getFormalParams().add(formalParam); method.setIsConstructor(false); method.setMethodType(invMethodType); method.setName("inv_" + namedTypeDecl.getName()); method.setStatic(true); return method; } public AIdentifierPatternIR consInvParamReplacementId( ADefaultClassDeclIR encClass, String originalParamName) { NameGen nameGen = new NameGen(encClass); nameGen.addName(originalParamName); String newParamName = nameGen.getName(JmlGenerator.INV_METHOD_REPLACEMENT_NAME_PREFIX + originalParamName); return jmlGen.getJavaGen().getInfo().getPatternAssistant().consIdPattern(newParamName); } public ADefaultClassDeclIR getEnclosingClass(INode node) { ADefaultClassDeclIR enclosingClass = node.getAncestor(ADefaultClassDeclIR.class); if (enclosingClass != null) { return enclosingClass; } else { log.error("Could not find enclosing class of node: " + node); return null; } } public AMethodDeclIR getEnclosingMethod(INode node) { AMethodDeclIR enclosingMethod = node.getAncestor(AMethodDeclIR.class); if (enclosingMethod != null) { return enclosingMethod; } else { log.error("Could not find enclosing method of node: " + node); return null; } } /** * There are problems with OpenJML when you invoke named type invariant methods across classes. Until these bugs are * fixed the workaround is simply to make sure that all generated classes have a local copy of a named invariant * method. TODO: Currently invariant method are named on the form "module_typename" although this does not truly * garuantee uniqueness. For example if module A defines type B_C the invariant method name is A_B_C. However if * module A_B defines type C then the invariant method will also be named A_B_C. So something needs to be done about * this. * * @param newAst */ public void distributeNamedTypeInvs(List<IRStatus<PIR>> newAst) { // Collect all named type invariants List<ATypeDeclIR> allNamedTypeInvTypeDecls = new LinkedList<ATypeDeclIR>(); for (IRStatus<ADefaultClassDeclIR> status : IRStatus.extract(newAst, ADefaultClassDeclIR.class)) { ADefaultClassDeclIR clazz = status.getIrNode(); if (jmlGen.getJavaGen().getInfo().getDeclAssistant().isLibraryName(clazz.getName())) { continue; } for (ATypeDeclIR typeDecl : clazz.getTypeDecls()) { if (typeDecl.getDecl() instanceof ANamedTypeDeclIR) { allNamedTypeInvTypeDecls.add(typeDecl); } } } for (IRStatus<ADefaultClassDeclIR> status : IRStatus.extract(newAst, ADefaultClassDeclIR.class)) { ADefaultClassDeclIR clazz = status.getIrNode(); if (jmlGen.getJavaGen().getInfo().getDeclAssistant().isLibraryName(clazz.getName())) { continue; } List<ATypeDeclIR> classTypeDecls = new LinkedList<ATypeDeclIR>(clazz.getTypeDecls()); for (ATypeDeclIR namedTypeInv : allNamedTypeInvTypeDecls) { if (!classTypeDecls.contains(namedTypeInv)) { classTypeDecls.add(namedTypeInv.clone()); } } clazz.setTypeDecls(classTypeDecls); } } }