package org.overture.codegen.vdm2jml.trans; import java.util.LinkedList; import java.util.List; import org.overture.codegen.ir.IRConstants; import org.overture.codegen.ir.SExpIR; import org.overture.codegen.ir.analysis.AnalysisException; import org.overture.codegen.ir.analysis.DepthFirstAnalysisAdaptor; import org.overture.codegen.ir.declarations.AFieldDeclIR; import org.overture.codegen.ir.declarations.AFormalParamLocalParamIR; import org.overture.codegen.ir.declarations.AMethodDeclIR; import org.overture.codegen.ir.declarations.ARecordDeclIR; import org.overture.codegen.ir.declarations.AVarDeclIR; import org.overture.codegen.ir.expressions.AApplyExpIR; import org.overture.codegen.ir.expressions.AFieldExpIR; import org.overture.codegen.ir.expressions.AMapSeqGetExpIR; import org.overture.codegen.ir.statements.AAssignToExpStmIR; import org.overture.codegen.ir.statements.ACallObjectExpStmIR; import org.overture.codegen.ir.statements.AMapSeqUpdateStmIR; import org.overture.codegen.ir.statements.AReturnStmIR; import org.overture.codegen.ir.types.ABoolBasicTypeIR; import org.overture.codegen.ir.types.AMethodTypeIR; import org.overture.codegen.ir.types.ARecordTypeIR; import org.overture.codegen.ir.types.AVoidTypeIR; import org.overture.codegen.vdm2java.JavaValueSemantics; import org.overture.codegen.vdm2jml.JmlGenerator; import org.overture.codegen.vdm2jml.data.RecClassInfo; public class RecAccessorTrans extends DepthFirstAnalysisAdaptor { public static final String GET_PREFIX = "get_"; public static final String SET_PREFIX = "set_"; private JmlGenerator jmlGen; private RecClassInfo recInfo; private boolean inTarget = false; public RecAccessorTrans(JmlGenerator jmlGen) { this.jmlGen = jmlGen; this.recInfo = new RecClassInfo(); } // private void privatizeFields(ARecordDeclIR node) // { // for (AFieldDeclIR f : node.getFields()) // { // f.setAccess(IRConstants.PRIVATE); // } // } @Override public void caseARecordDeclIR(ARecordDeclIR node) throws AnalysisException { // TODO: Privatise record fields? List<AMethodDeclIR> accessors = consAccessors(node); registerAccessors(accessors); node.getMethods().addAll(accessors); if (!this.jmlGen.getJmlSettings().genInvariantFor()) { node.getMethods().add(consValidMethod()); } } @Override public void caseAAssignToExpStmIR(AAssignToExpStmIR node) throws AnalysisException { if (node.getTarget() instanceof AFieldExpIR) { AFieldExpIR target = (AFieldExpIR) node.getTarget(); if (target.getObject().getType() instanceof ARecordTypeIR) { ACallObjectExpStmIR setCall = new ACallObjectExpStmIR(); setCall.setFieldName(consSetCallName(target.getMemberName())); setCall.getArgs().add(node.getExp().clone()); setCall.setType(new AVoidTypeIR()); SExpIR obj = target.getObject().clone(); jmlGen.getJavaGen().getJavaFormat().getValueSemantics().addCloneFreeNode(obj); setCall.setObj(obj); setCall.setSourceNode(node.getSourceNode()); /** * Replacing the assignment statement with a setter call makes the setter call the new state designator * owner */ jmlGen.getStateDesInfo().replaceStateDesOwner(node, setCall); jmlGen.getJavaGen().getTransAssistant().replaceNodeWith(node, setCall); inTarget = true; setCall.getObj().apply(this); inTarget = false; setCall.getArgs().getFirst().apply(this); } } else { inTarget = true; node.getTarget().apply(this); inTarget = false; node.getExp().apply(this); } } @Override public void caseAFieldExpIR(AFieldExpIR node) throws AnalysisException { node.getObject().apply(this); if (node.getObject().getType() instanceof ARecordTypeIR && !(node.parent() instanceof AApplyExpIR)) { AMethodTypeIR getterType = new AMethodTypeIR(); getterType.setResult(node.getType().clone()); AFieldExpIR getterField = node.clone(); getterField.setType(getterType); AApplyExpIR getCall = new AApplyExpIR(); getCall.setRoot(getterField); getCall.setType(node.getType().clone()); getCall.setSourceNode(node.getSourceNode()); getterField.setMemberName(consGetCallName(node.getMemberName())); /** * The getters added to the record classes do not copy object references representing values. Therefore we * need to take it into account when we do the field read call */ if (cloneFieldRead(node)) { getCall = makeCopy(getCall); } jmlGen.getJavaGen().getTransAssistant().replaceNodeWith(node, getCall); } } private AApplyExpIR makeCopy(AApplyExpIR getCall) { AApplyExpIR copyCall = jmlGen.getJavaGen().getJavaFormat().getJavaFormatAssistant().consUtilCopyCall(); copyCall.getArgs().add(getCall); return copyCall; } private boolean cloneFieldRead(AFieldExpIR node) { if (jmlGen.getJavaSettings().getDisableCloning()) { return false; } AVarDeclIR decl = node.getAncestor(AVarDeclIR.class); /* * Normalized state designators do not need cloning */ if (decl != null && jmlGen.getStateDesInfo().isStateDesDecl(decl)) { return false; } if (jmlGen.getJavaGen().getJavaFormat().getValueSemantics().isCloneFree(node)) { return false; } JavaValueSemantics valSem = jmlGen.getJavaGen().getJavaFormat().getValueSemantics(); return !inTarget && !isObjOfFieldExp(node) && !isColOfMapSeq(node) && valSem.mayBeValueType(node.getType()); } private boolean isObjOfFieldExp(AFieldExpIR node) { return node.parent() instanceof AFieldExpIR && ((AFieldExpIR) node.parent()).getObject() == node; } private boolean isColOfMapSeq(AFieldExpIR node) { return node.parent() instanceof AMapSeqGetExpIR && ((AMapSeqGetExpIR) node.parent()).getCol() == node || node.parent() instanceof AMapSeqUpdateStmIR && ((AMapSeqUpdateStmIR) node.parent()).getCol() == node; } private List<AMethodDeclIR> consAccessors(ARecordDeclIR node) { List<AMethodDeclIR> accessors = new LinkedList<AMethodDeclIR>(); for (AFieldDeclIR f : node.getFields()) { accessors.add(consGetter(f)); accessors.add(consSetter(f)); } return accessors; } private AMethodDeclIR consSetter(AFieldDeclIR f) { AMethodDeclIR setter = new AMethodDeclIR(); setter.setAbstract(false); setter.setAccess(IRConstants.PUBLIC); setter.setAsync(false); setter.setImplicit(false); setter.setIsConstructor(false); setter.setName(consSetCallName(f.getName())); setter.setStatic(false); setter.setSourceNode(f.getSourceNode()); String paramName = consParamName(f); AFormalParamLocalParamIR param = new AFormalParamLocalParamIR(); param.setType(f.getType().clone()); param.setPattern(jmlGen.getJavaGen().getInfo().getPatternAssistant().consIdPattern(paramName)); setter.getFormalParams().add(param); AMethodTypeIR methodType = new AMethodTypeIR(); methodType.setResult(new AVoidTypeIR()); methodType.getParams().add(f.getType().clone()); setter.setMethodType(methodType); AAssignToExpStmIR fieldUpdate = new AAssignToExpStmIR(); fieldUpdate.setTarget(jmlGen.getJavaGen().getInfo().getExpAssistant().consIdVar(f.getName(), f.getType().clone())); fieldUpdate.setExp(jmlGen.getJavaGen().getInfo().getExpAssistant().consIdVar(paramName, f.getType().clone())); setter.setBody(fieldUpdate); return setter; } private AMethodDeclIR consGetter(AFieldDeclIR f) { AMethodDeclIR getter = new AMethodDeclIR(); getter.setAbstract(false); getter.setAccess(IRConstants.PUBLIC); getter.setAsync(false); getter.setImplicit(false); getter.setIsConstructor(false); getter.setName(consGetCallName(f.getName())); getter.setStatic(false); getter.setSourceNode(f.getSourceNode()); AMethodTypeIR methodType = new AMethodTypeIR(); methodType.setResult(f.getType().clone()); getter.setMethodType(methodType); AReturnStmIR returnField = new AReturnStmIR(); returnField.setExp(jmlGen.getJavaGen().getInfo().getExpAssistant().consIdVar(f.getName(), f.getType().clone())); getter.setBody(returnField); jmlGen.getAnnotator().makePure(getter); return getter; } public AMethodDeclIR consValidMethod() { AMethodDeclIR validMethod = new AMethodDeclIR(); validMethod.setAbstract(false); validMethod.setAccess(IRConstants.PUBLIC); validMethod.setAsync(false); validMethod.setImplicit(false); validMethod.setIsConstructor(false); validMethod.setName(JmlGenerator.REC_VALID_METHOD_NAMEVALID); validMethod.setStatic(false); AMethodTypeIR methodType = new AMethodTypeIR(); methodType.setResult(new ABoolBasicTypeIR()); validMethod.setMethodType(methodType); AReturnStmIR body = new AReturnStmIR(); body.setExp(jmlGen.getJavaGen().getInfo().getExpAssistant().consBoolLiteral(true)); validMethod.setBody(body); jmlGen.getAnnotator().makePure(validMethod); return validMethod; } private void registerAccessors(List<AMethodDeclIR> accessors) { for (AMethodDeclIR a : accessors) { recInfo.register(a); } } public String consGetCallName(String fieldName) { return GET_PREFIX + fieldName; } public String consSetCallName(String fieldName) { return SET_PREFIX + fieldName; } private String consParamName(AFieldDeclIR f) { return "_" + f.getName(); } public RecClassInfo getRecInfo() { return recInfo; } }