[500803] Suppress inlining if invoking a non-final operation
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/analyzer/AS2CGVisitor.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/analyzer/AS2CGVisitor.java
index adb2c3f..84eb6c9 100644
--- a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/analyzer/AS2CGVisitor.java
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/analyzer/AS2CGVisitor.java
@@ -937,7 +937,7 @@
 	 * Return all final operations directly referenced by opaqueExpression, or null if none.
 	 * @since 1.3
 	 */
-	protected @Nullable Set<@NonNull Operation> getReferencedFinalOperations(@NonNull FinalAnalysis finalAnalysis, @NonNull LanguageExpression specification) {
+	protected @Nullable Iterable<@NonNull Operation> getReferencedFinalOperations(@NonNull FinalAnalysis finalAnalysis, @NonNull LanguageExpression specification) {
 		ExpressionInOCL prototype = null;
 		try {
 			prototype = metamodelManager.parseSpecification(specification);
@@ -964,6 +964,33 @@
 		return referencedOperations;
 	}
 
+	protected @Nullable Iterable<@NonNull Operation> getReferencedNonFinalOperations(@NonNull FinalAnalysis finalAnalysis, @NonNull LanguageExpression specification) {
+		ExpressionInOCL prototype = null;
+		try {
+			prototype = metamodelManager.parseSpecification(specification);
+		}
+		catch (ParserException e) {
+			// FIXME log error
+			e.printStackTrace();
+		}
+		if (prototype == null) {
+			return null;
+		}
+		Set<@NonNull Operation> referencedOperations = null;
+		for (EObject crossReference : EcoreUtil.ExternalCrossReferencer.find(prototype).keySet()) {
+			if (crossReference instanceof Operation) {
+				Operation operation = (Operation) crossReference;
+				if (!finalAnalysis.isFinal(operation)) {
+					if (referencedOperations == null) {
+						referencedOperations = new HashSet<>();
+					}
+					referencedOperations.add(operation);
+				}
+			}
+		}
+		return referencedOperations;
+	}
+
 	@Deprecated
 	public @NonNull CGParameter getSelfParameter(@NonNull Variable aParameter) {
 		return getParameter(aParameter, null);
@@ -973,8 +1000,8 @@
 	 * Return all final operations transitively referenced by opaqueExpression, or null if none.
 	 * @since 1.3
 	 */
-	protected void getTransitivelyReferencedFinalOperations(@NonNull Set<Operation> alreadyReferencedFinalOperations, @NonNull FinalAnalysis finalAnalysis, @NonNull LanguageExpression expressionInOCL) {
-		Set<@NonNull Operation> newlyReferencedFinalOperations = getReferencedFinalOperations(finalAnalysis, expressionInOCL);
+	protected void getTransitivelyReferencedFinalOperations(@NonNull Set<@NonNull Operation> alreadyReferencedFinalOperations, @NonNull FinalAnalysis finalAnalysis, @NonNull LanguageExpression expressionInOCL) {
+		Iterable<@NonNull Operation> newlyReferencedFinalOperations = getReferencedFinalOperations(finalAnalysis, expressionInOCL);
 		if (newlyReferencedFinalOperations != null) {
 			for (@NonNull Operation newlyReferencedFinalOperation : newlyReferencedFinalOperations) {
 				if (alreadyReferencedFinalOperations.add(newlyReferencedFinalOperation)) {
@@ -1023,6 +1050,10 @@
 		if (referencedFinalOperations.contains(callExp.getReferredOperation())) {
 			return null;	// Avoid an infinite inlining recursion.
 		}
+		Iterable<@NonNull Operation> referencedNonFinalOperations = getReferencedNonFinalOperations(finalAnalysis, specification);
+		if (referencedNonFinalOperations != null) {
+			return null;	// Simple heavy heuristic
+		}
 		ExpressionInOCL asClone = createCopy(prototype);
 		OCLExpression asExpression = ClassUtil.nonNullState(asClone.getOwnedBody());
 		List<@NonNull OCLExpression> asArguments = ClassUtil.nullFree(callExp.getOwnedArguments());
diff --git a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/CG2JavaVisitor.java b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/CG2JavaVisitor.java
index 27f82f6..0fb3e47 100644
--- a/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/CG2JavaVisitor.java
+++ b/examples/org.eclipse.ocl.examples.codegen/src/org/eclipse/ocl/examples/codegen/java/CG2JavaVisitor.java
@@ -500,64 +500,12 @@
 
 	protected void doCachedOperationBasicEvaluate(@NonNull CGOperation cgOperation) {
 		List<@NonNull CGParameter> cgParameters = ClassUtil.nullFree(cgOperation.getParameters());
-		js.append("public ");
-		//				boolean cgOperationIsInvalid = cgOperation.getInvalidValue() != null;
-		//				js.appendIsCaught(!cgOperationIsInvalid, cgOperationIsInvalid);
-		//				js.append(" ");
-		js.appendClassReference(cgOperation.isRequired() ? true : null, cgOperation);
-		js.append(" evaluate(");
-		boolean isFirst = true;
-		for (@NonNull CGParameter cgParameter : cgParameters) {
-			if (!isFirst) {
-				js.append(", ");
-			}
-			js.appendDeclaration(cgParameter);
-			isFirst = false;
-		}
-		js.append(") {\n");
-		js.pushIndentation(null);
-		js.append("return (");
-		js.appendClassReference(null, cgOperation);
-		js.append(")");
-		js.append(JavaConstants.EVALUATION_CACHE_NAME);
-		js.append(".getCachedEvaluationResult(this, caller, new ");
-		js.appendIsRequired(false);
-		js.append(" ");
-		js.appendClassReference(Object.class);
-		js.append("[]{");
-		isFirst = true;
-		for (@NonNull CGParameter cgParameter : cgParameters) {
-			if (!isFirst) {
-				js.append(", ");
-			}
-			js.appendValueName(cgParameter);
-			isFirst = false;
-		}
-		js.append("});\n");
-		js.popIndentation();
-		js.append("}\n");
-	}
-
-	protected void doCachedOperationClassInstance(@NonNull Operation asOperation) {
-		String name = getNativeOperationClassName(asOperation);
-		js.append("protected final ");
-		js.appendIsRequired(true);
-		js.append(" ");
-		js.append(name);
-		js.append(" ");
-		js.append(getNativeOperationInstanceName(asOperation));
-		js.append(" = new ");
-		js.append(name);
-		js.append("();\n");
-	}
-
-	protected void doCachedOperationEvaluate(@NonNull CGOperation cgOperation) {
-		List<@NonNull CGParameter> cgParameters = ClassUtil.nullFree(cgOperation.getParameters());
 		CGValuedElement body = getExpression(cgOperation.getBody());
 		appendAtOverride(cgOperation);
 		js.append("public ");
 		//				boolean cgOperationIsInvalid = cgOperation.getInvalidValue() != null;
 		//				js.appendIsCaught(!cgOperationIsInvalid, cgOperationIsInvalid);
+		js.appendIsRequired(false);
 		js.append(" ");
 		js.appendClassReference(Object.class);
 		js.append(" basicEvaluate(");
@@ -587,7 +535,7 @@
 			if (cgParameter.getASTypeId() instanceof CollectionTypeId) {
 				js.append("@SuppressWarnings(\"unchecked\") ");
 			}
-			else {
+			else if (cgParameter.isRequired()) {
 				js.append("@SuppressWarnings(\"null\") ");
 			}
 			js.appendDeclaration(cgParameter);
@@ -600,6 +548,66 @@
 		js.append("}\n");
 	}
 
+	protected void doCachedOperationClassInstance(@NonNull Operation asOperation) {
+		String name = getNativeOperationClassName(asOperation);
+		js.append("protected final ");
+		js.appendIsRequired(true);
+		js.append(" ");
+		js.append(name);
+		js.append(" ");
+		js.append(getNativeOperationInstanceName(asOperation));
+		js.append(" = new ");
+		js.append(name);
+		js.append("();\n");
+	}
+
+	protected void doCachedOperationEvaluate(@NonNull CGOperation cgOperation) {
+		List<@NonNull CGParameter> cgParameters = ClassUtil.nullFree(cgOperation.getParameters());
+		Boolean isRequiredReturn = cgOperation.isRequired() ? true : null;
+		if (cgOperation.getASTypeId() instanceof CollectionTypeId) {
+			js.append("@SuppressWarnings(\"unchecked\")\n");
+		}
+		else if ((isRequiredReturn == Boolean.TRUE) && js.isUseNullAnnotations()) {
+			js.append("@SuppressWarnings(\"null\")\n");
+		}
+		js.append("public ");
+		//				boolean cgOperationIsInvalid = cgOperation.getInvalidValue() != null;
+		//				js.appendIsCaught(!cgOperationIsInvalid, cgOperationIsInvalid);
+		//				js.append(" ");
+		js.appendClassReference(isRequiredReturn, cgOperation);
+		js.append(" evaluate(");
+		boolean isFirst = true;
+		for (@NonNull CGParameter cgParameter : cgParameters) {
+			if (!isFirst) {
+				js.append(", ");
+			}
+			js.appendDeclaration(cgParameter);
+			isFirst = false;
+		}
+		js.append(") {\n");
+		js.pushIndentation(null);
+		js.append("return (");
+		js.appendClassReference(isRequiredReturn, cgOperation);
+		js.append(")");
+		js.append(JavaConstants.EVALUATION_CACHE_NAME);
+		js.append(".getCachedEvaluationResult(this, caller, new ");
+		js.appendIsRequired(false);
+		js.append(" ");
+		js.appendClassReference(Object.class);
+		js.append("[]{");
+		isFirst = true;
+		for (@NonNull CGParameter cgParameter : cgParameters) {
+			if (!isFirst) {
+				js.append(", ");
+			}
+			js.appendValueName(cgParameter);
+			isFirst = false;
+		}
+		js.append("});\n");
+		js.popIndentation();
+		js.append("}\n");
+	}
+
 	protected boolean doClassMethods(@NonNull CGClass cgClass, boolean needsBlankLine) {
 		for (CGOperation cgOperation : cgClass.getOperations()) {
 			if (needsBlankLine) {
@@ -2149,9 +2157,9 @@
 				js.append("\n");
 				js.append("{\n");
 				js.pushIndentation(null);
-				doCachedOperationEvaluate(cgOperation);
-				js.append("\n");
 				doCachedOperationBasicEvaluate(cgOperation);
+				js.append("\n");
+				doCachedOperationEvaluate(cgOperation);
 				js.popIndentation();
 				js.append("}\n");
 				//