/* * #%~ * VDM Code Generator * %% * Copyright (C) 2008 - 2014 Overture * %% * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program. If not, see * <http://www.gnu.org/licenses/gpl-3.0.html>. * #~% */ package org.overture.codegen.vdm2java; import java.util.LinkedList; import java.util.List; import org.overture.codegen.assistant.AssistantManager; import org.overture.codegen.ir.INode; import org.overture.codegen.ir.SExpIR; import org.overture.codegen.ir.STypeIR; import org.overture.codegen.ir.declarations.ADefaultClassDeclIR; import org.overture.codegen.ir.declarations.AFieldDeclIR; import org.overture.codegen.ir.declarations.AMethodDeclIR; import org.overture.codegen.ir.declarations.SClassDeclIR; import org.overture.codegen.ir.expressions.AAddrEqualsBinaryExpIR; import org.overture.codegen.ir.expressions.AAddrNotEqualsBinaryExpIR; import org.overture.codegen.ir.expressions.AApplyExpIR; import org.overture.codegen.ir.expressions.ACardUnaryExpIR; import org.overture.codegen.ir.expressions.ACastUnaryExpIR; import org.overture.codegen.ir.expressions.AEqualsBinaryExpIR; import org.overture.codegen.ir.expressions.AExplicitVarExpIR; import org.overture.codegen.ir.expressions.AFieldExpIR; import org.overture.codegen.ir.expressions.AFieldNumberExpIR; import org.overture.codegen.ir.expressions.AHeadUnaryExpIR; import org.overture.codegen.ir.expressions.AInSetBinaryExpIR; import org.overture.codegen.ir.expressions.AIndicesUnaryExpIR; import org.overture.codegen.ir.expressions.AInstanceofExpIR; import org.overture.codegen.ir.expressions.ALenUnaryExpIR; import org.overture.codegen.ir.expressions.AMapSeqGetExpIR; import org.overture.codegen.ir.expressions.ANewExpIR; import org.overture.codegen.ir.expressions.ANotEqualsBinaryExpIR; import org.overture.codegen.ir.expressions.ASetProperSubsetBinaryExpIR; import org.overture.codegen.ir.expressions.ASetSubsetBinaryExpIR; import org.overture.codegen.ir.expressions.ATupleCompatibilityExpIR; import org.overture.codegen.ir.expressions.ATupleSizeExpIR; import org.overture.codegen.ir.expressions.SIsExpIR; import org.overture.codegen.ir.statements.AAssignToExpStmIR; import org.overture.codegen.ir.statements.ACallObjectExpStmIR; import org.overture.codegen.ir.statements.AForAllStmIR; import org.overture.codegen.ir.statements.AMapCompAddStmIR; import org.overture.codegen.ir.statements.AMapSeqUpdateStmIR; import org.overture.codegen.ir.statements.ASeqCompAddStmIR; import org.overture.codegen.ir.statements.ASetCompAddStmIR; import org.overture.codegen.ir.types.AExternalTypeIR; import org.overture.codegen.ir.types.AMethodTypeIR; import org.overture.codegen.ir.types.ARecordTypeIR; import org.overture.codegen.ir.types.ATupleTypeIR; import org.overture.codegen.ir.types.AUnionTypeIR; import org.overture.codegen.ir.types.SMapTypeIR; import org.overture.codegen.ir.types.SSeqTypeIR; import org.overture.codegen.ir.types.SSetTypeIR; public class JavaValueSemantics { private JavaFormat javaFormat; private JavaSettings javaSettings; private List<INode> cloneFreeNodes; public JavaValueSemantics(JavaFormat javaFormat) { this.javaFormat = javaFormat; this.javaSettings = new JavaSettings(); this.cloneFreeNodes = new LinkedList<>(); } public void clear() { cloneFreeNodes.clear(); } public void addCloneFreeNode(INode node) { cloneFreeNodes.add(node); } public List<INode> getCloneFreeNodes() { return cloneFreeNodes; } public void setJavaSettings(JavaSettings javaSettings) { this.javaSettings = javaSettings; } public JavaSettings getJavaSettings() { return javaSettings; } public boolean cloneMember(AFieldNumberExpIR exp) { if (javaSettings.getDisableCloning()) { return false; } if (isCloneFree(exp)) { return false; } // Generally tuples need to be cloned, for example, if they // contain a record field (that must be cloned) if (exp.parent() instanceof AFieldNumberExpIR) { return false; } if (cloneNotNeededMapPutGet(exp)) { return false; } if (cloneNotNeededAssign(exp)) { return false; } List<ATupleTypeIR> tupleTypes = getTypes(exp.getTuple().getType(), ATupleTypeIR.class); final int idx = (int) (exp.getField() - 1); for (ATupleTypeIR tupleType : tupleTypes) { STypeIR fieldType = tupleType.getTypes().get(idx); if (mayBeValueType(fieldType)) { return true; } } return false; } public boolean cloneMember(AFieldExpIR exp) { if (javaSettings.getDisableCloning()) { return false; } if (isCloneFree(exp)) { return false; } INode parent = exp.parent(); if (cloneNotNeeded(parent)) { return false; } if (cloneNotNeededMapPutGet(exp)) { return false; } if (cloneNotNeededAssign(exp)) { return false; } List<ARecordTypeIR> recTypes = getTypes(exp.getObject().getType(), ARecordTypeIR.class); String memberName = exp.getMemberName(); List<SClassDeclIR> classes = javaFormat.getIrInfo().getClasses(); AssistantManager man = javaFormat.getIrInfo().getAssistantManager(); for (ARecordTypeIR r : recTypes) { AFieldDeclIR field = man.getDeclAssistant().getFieldDecl(classes, r, memberName); if (field != null && mayBeValueType(field.getType())) { return true; } } return false; } private <T extends STypeIR> List<T> getTypes(STypeIR type, Class<T> filter) { List<T> filteredTypes = new LinkedList<>(); if (filter.isInstance(type)) { filteredTypes.add(filter.cast(type)); } else if (type instanceof AUnionTypeIR) { List<STypeIR> types = ((AUnionTypeIR) type).getTypes(); for (STypeIR t : types) { filteredTypes.addAll(getTypes(t, filter)); } } return filteredTypes; } public boolean shouldClone(SExpIR exp) { if (javaSettings.getDisableCloning()) { return false; } if (isCloneFree(exp)) { return false; } if (inRecClassNonConstructor(exp)) { return false; } if (compAdd(exp)) { return false; } INode parent = exp.parent(); if (cloneNotNeeded(parent)) { return false; } if (parent instanceof AAssignToExpStmIR) { AAssignToExpStmIR assignment = (AAssignToExpStmIR) parent; if (assignment.getTarget() == exp) { return false; } } if (parent instanceof ACallObjectExpStmIR) { ACallObjectExpStmIR callObjStm = (ACallObjectExpStmIR) parent; if (callObjStm.getObj() == exp) { return false; } } if (cloneNotNeededMapPutGet(exp)) { return false; } if (isPrePostArgument(exp)) { return false; } STypeIR type = exp.getType(); if (mayBeValueType(type)) { if (parent instanceof ANewExpIR) { ANewExpIR newExp = (ANewExpIR) parent; STypeIR newExpType = newExp.getType(); if (mayBeValueType(newExpType)) { return false; } } return true; } return false; } private boolean compAdd(SExpIR exp) { INode parent = exp.parent(); if (parent instanceof ASeqCompAddStmIR) { ASeqCompAddStmIR add = (ASeqCompAddStmIR) parent; return add.getSeq() == exp; } if (parent instanceof ASetCompAddStmIR) { ASetCompAddStmIR add = (ASetCompAddStmIR) parent; return add.getSet() == exp; } if (parent instanceof AMapCompAddStmIR) { AMapCompAddStmIR add = (AMapCompAddStmIR) parent; return add.getMap() == exp; } return false; } private boolean inRecClassNonConstructor(SExpIR exp) { ADefaultClassDeclIR encClass = exp.getAncestor(ADefaultClassDeclIR.class); if (encClass != null) { LinkedList<AMethodDeclIR> methods = encClass.getMethods(); boolean isRec = false; for (AMethodDeclIR m : methods) { if (m.getIsConstructor() && m.getMethodType().getResult() instanceof ARecordTypeIR) { isRec = true; break; } } if (!isRec) { return false; } else { AMethodDeclIR encMethod = exp.getAncestor(AMethodDeclIR.class); if (encMethod != null) { return !encMethod.getIsConstructor(); } else { return false; } } } else { return false; } } private boolean cloneNotNeededMapPutGet(SExpIR exp) { INode parent = exp.parent(); if (parent instanceof AMapSeqUpdateStmIR) { AMapSeqUpdateStmIR mapSeqUpd = (AMapSeqUpdateStmIR) parent; if (mapSeqUpd.getCol() == exp) { return true; } } if (parent instanceof AMapSeqGetExpIR) { AMapSeqGetExpIR mapSeqGet = (AMapSeqGetExpIR) parent; if (mapSeqGet.getCol() == exp) { return true; } } return false; } private boolean cloneNotNeeded(INode parent) { while (parent instanceof ACastUnaryExpIR) { parent = parent.parent(); } if (parent instanceof AApplyExpIR) { // Cloning is not needed if the expression is // used to look up a value in a sequence or a map SExpIR root = ((AApplyExpIR) parent).getRoot(); if (!(root.getType() instanceof AMethodTypeIR)) { return true; } } return parent instanceof AFieldExpIR || parent instanceof AFieldNumberExpIR || parent instanceof ATupleSizeExpIR || parent instanceof ATupleCompatibilityExpIR || parent instanceof AEqualsBinaryExpIR || parent instanceof ANotEqualsBinaryExpIR || parent instanceof AAddrEqualsBinaryExpIR || parent instanceof AAddrNotEqualsBinaryExpIR || parent instanceof AForAllStmIR || parent instanceof AInstanceofExpIR || parent instanceof SIsExpIR || cloneNotNeededCollectionOperator(parent) || cloneNotNeededUtilCall(parent); } private boolean isPrePostArgument(SExpIR exp) { INode parent = exp.parent(); if (!(parent instanceof AApplyExpIR)) { return false; } AApplyExpIR applyExp = (AApplyExpIR) parent; Object tag = applyExp.getTag(); if (!(tag instanceof JavaValueSemanticsTag)) { return false; } JavaValueSemanticsTag javaTag = (JavaValueSemanticsTag) tag; if (javaTag.mustClone()) { return false; } return applyExp.getArgs().contains(exp); } private boolean cloneNotNeededCollectionOperator(INode parent) { return cloneNotNeededSeqOperators(parent) || cloneNotNeededSetOperators(parent); } private boolean cloneNotNeededSeqOperators(INode parent) { return parent instanceof ALenUnaryExpIR || parent instanceof AIndicesUnaryExpIR || parent instanceof AHeadUnaryExpIR; } private boolean cloneNotNeededSetOperators(INode parent) { return parent instanceof ACardUnaryExpIR || parent instanceof AInSetBinaryExpIR || parent instanceof ASetSubsetBinaryExpIR || parent instanceof ASetProperSubsetBinaryExpIR; } private boolean cloneNotNeededUtilCall(INode node) { if (!(node instanceof AApplyExpIR)) { return false; } AApplyExpIR applyExp = (AApplyExpIR) node; SExpIR root = applyExp.getRoot(); if (!(root instanceof AExplicitVarExpIR)) { return false; } AExplicitVarExpIR explicitVar = (AExplicitVarExpIR) root; STypeIR classType = explicitVar.getClassType(); return classType instanceof AExternalTypeIR && ((AExternalTypeIR) classType).getName().equals(JavaFormat.UTILS_FILE); } public boolean mayBeValueType(STypeIR type) { if (type instanceof AUnionTypeIR) { LinkedList<STypeIR> types = ((AUnionTypeIR) type).getTypes(); for (STypeIR t : types) { if (mayBeValueType(t)) { return true; } } return false; } else { return type instanceof ARecordTypeIR || type instanceof ATupleTypeIR || type instanceof SSeqTypeIR || type instanceof SSetTypeIR || type instanceof SMapTypeIR; } } private boolean cloneNotNeededAssign(SExpIR exp) { INode parent = exp.parent(); if (parent instanceof AAssignToExpStmIR) { AAssignToExpStmIR assignment = (AAssignToExpStmIR) parent; if (assignment.getTarget() == exp) { return true; } } return false; } public boolean isCloneFree(SExpIR exp) { if (exp == null) { return false; } INode next = exp; while (next != null) { if (contains(next)) { return true; } else { next = next.parent(); } } return false; } private boolean contains(INode node) { for (INode n : cloneFreeNodes) { if (n == node) { return true; } } return false; } }