All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.eclipse.xtext.xbase.compiler.AbstractXbaseCompiler Maven / Gradle / Ivy

/*******************************************************************************
 * Copyright (c) 2011 itemis AG (http://www.itemis.eu) and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *******************************************************************************/
package org.eclipse.xtext.xbase.compiler;

import static com.google.common.collect.Sets.*;

import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.common.types.JvmArrayType;
import org.eclipse.xtext.common.types.JvmConstructor;
import org.eclipse.xtext.common.types.JvmFormalParameter;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmType;
import org.eclipse.xtext.common.types.JvmTypeParameter;
import org.eclipse.xtext.common.types.JvmTypeReference;
import org.eclipse.xtext.common.types.JvmVoid;
import org.eclipse.xtext.common.types.access.impl.URIHelperConstants;
import org.eclipse.xtext.common.types.util.Primitives;
import org.eclipse.xtext.common.types.util.Primitives.Primitive;
import org.eclipse.xtext.util.Strings;
import org.eclipse.xtext.xbase.XAbstractFeatureCall;
import org.eclipse.xtext.xbase.XBlockExpression;
import org.eclipse.xtext.xbase.XConstructorCall;
import org.eclipse.xtext.xbase.XExpression;
import org.eclipse.xtext.xbase.XFeatureCall;
import org.eclipse.xtext.xbase.XVariableDeclaration;
import org.eclipse.xtext.xbase.compiler.output.ITreeAppendable;
import org.eclipse.xtext.xbase.controlflow.IEarlyExitComputer;
import org.eclipse.xtext.xbase.featurecalls.IdentifiableSimpleNameProvider;
import org.eclipse.xtext.xbase.lib.Exceptions;
import org.eclipse.xtext.xbase.lib.Functions;
import org.eclipse.xtext.xbase.lib.Procedures;
import org.eclipse.xtext.xbase.scoping.featurecalls.OperatorMapping;
import org.eclipse.xtext.xbase.typesystem.IBatchTypeResolver;
import org.eclipse.xtext.xbase.typesystem.IResolvedTypes;
import org.eclipse.xtext.xbase.typesystem.conformance.RawTypeConformanceComputer;
import org.eclipse.xtext.xbase.typesystem.references.ITypeReferenceOwner;
import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference;
import org.eclipse.xtext.xbase.typesystem.references.StandardTypeReferenceOwner;
import org.eclipse.xtext.xbase.typesystem.util.CommonTypeComputationServices;

import com.google.inject.Inject;

/**
 * @author Sven Efftinge - Initial contribution and API
 */
public abstract class AbstractXbaseCompiler {

	@Inject
	private TypeReferenceSerializer referenceSerializer;
	
	public TypeReferenceSerializer getTypeReferenceSerializer() {
		return referenceSerializer;
	}
	
	@Inject
	private CommonTypeComputationServices services;

	@Inject
	private JavaKeywords javaUtils;

	@Inject 
	private IBatchTypeResolver typeResolver;

	// TODO doublecheck usage of this one
	@Inject
	private IEarlyExitComputer exitComputer;
	
	private IBatchTypeResolver getTypeResolver() {
		return typeResolver;
	}
	
	/* @Nullable */
	protected JvmType findKnownTopLevelType(Class rawType, Notifier context) {
		if (rawType.isArray()) {
			throw new IllegalArgumentException(rawType.getCanonicalName());
		}
		if (rawType.isPrimitive()) {
			throw new IllegalArgumentException(rawType.getName());
		}
		ResourceSet resourceSet = EcoreUtil2.getResourceSet(context);
		if (resourceSet == null) {
			return null;
		}
		Resource typeResource = resourceSet.getResource(URIHelperConstants.OBJECTS_URI.appendSegment(rawType.getName()), true);
		List resourceContents = typeResource.getContents();
		if (resourceContents.isEmpty())
			return null;
		JvmType type = (JvmType) resourceContents.get(0);
		return type;
	}
	
	/* @Nullable */
	protected JvmType findKnownType(Class rawType, Notifier context) {
		if (rawType.isArray()) {
			throw new IllegalArgumentException(rawType.getCanonicalName());
		}
		if (rawType.isPrimitive()) {
			throw new IllegalArgumentException(rawType.getName());
		}		ResourceSet resourceSet = EcoreUtil2.getResourceSet(context);
		if (resourceSet == null) {
			return null;
		}
		Class declaringClass = rawType.getDeclaringClass();
		if (declaringClass == null) {
			return findKnownTopLevelType(rawType, resourceSet);
		}
		JvmType result = (JvmType) resourceSet.getEObject(URIHelperConstants.OBJECTS_URI.appendSegment(declaringClass.getName()).appendFragment(rawType.getName()), true);
		return result;
	}
	
	protected CommonTypeComputationServices getTypeComputationServices() {
		return services;
	}
	
	protected ITypeReferenceOwner newTypeReferenceOwner(EObject context) {
		return new StandardTypeReferenceOwner(services, context);
	}
	
	protected LightweightTypeReference toLightweight(JvmTypeReference reference, EObject context) {
		return newTypeReferenceOwner(context).toLightweightTypeReference(reference);
	}
	
	public ITreeAppendable compile(XExpression obj, ITreeAppendable appendable, LightweightTypeReference expectedReturnType) {
		compile(obj, appendable, expectedReturnType, null);
		return appendable;
	}
	
	public ITreeAppendable compileAsJavaExpression(XExpression obj, ITreeAppendable parentAppendable, JvmTypeReference expectedType) {
		LightweightTypeReference converted = null;
		if (expectedType != null) {
			converted = newTypeReferenceOwner(obj).toLightweightTypeReference(expectedType);
		}
		return compileAsJavaExpression(obj, parentAppendable, converted);
	}
	
	public ITreeAppendable compileAsJavaExpression(XExpression obj, ITreeAppendable parentAppendable, LightweightTypeReference expectedType) {
		ITreeAppendable appendable = parentAppendable.trace(obj, true);
		if (expectedType == null) {
			expectedType = getLightweightReturnType(obj);
			if (expectedType == null) {
				expectedType = getLightweightType(obj);
			}
		}
		final boolean isPrimitiveVoidExpected = expectedType.isPrimitiveVoid(); 
		final boolean isPrimitiveVoid = isPrimitiveVoid(obj);
		final boolean earlyExit = isEarlyExit(obj);
		boolean needsSneakyThrow = needsSneakyThrow(obj, Collections.emptySet());
		boolean needsToBeWrapped = earlyExit || needsSneakyThrow || !canCompileToJavaExpression(obj, appendable);
		if (needsToBeWrapped) {
			appendable.openScope();
			try {
				if (appendable.hasObject("this")) {
					Object thisElement = appendable.getObject("this");
					if (thisElement instanceof JvmType) {
						appendable.declareVariable(thisElement, ((JvmType) thisElement).getSimpleName()+".this");
						if (appendable.hasObject("super")) {
							Object superElement = appendable.getObject("super");
							if (superElement instanceof JvmType) {
								appendable.declareVariable(superElement, ((JvmType) thisElement).getSimpleName()+".super");
							}
						}
					}
				}
				appendable.append("new ");
				JvmType procedureOrFunction = null;
				if (isPrimitiveVoidExpected) {
					procedureOrFunction = findKnownType(Procedures.Procedure0.class, obj);
				} else {
					procedureOrFunction = findKnownType(Functions.Function0.class, obj);
				}
				if (procedureOrFunction != null) {
					appendable.append(procedureOrFunction);
					if (!isPrimitiveVoidExpected) {
						appendable.append("<");
						appendable.append(expectedType.getWrapperTypeIfPrimitive());
						appendable.append(">");
					}
				} else {
					appendable.append("Object");
				}
				appendable.append("() {").increaseIndentation();
				appendable.newLine().append("public ");
				appendable.append(expectedType.getWrapperTypeIfPrimitive());
				appendable.append(" apply() {").increaseIndentation();
				if (needsSneakyThrow) {
					appendable.newLine().append("try {").increaseIndentation();
				}
				internalToJavaStatement(obj, appendable, !isPrimitiveVoidExpected && !isPrimitiveVoid && !earlyExit);
				if (!isPrimitiveVoidExpected && !earlyExit) {
						appendable.newLine().append("return ");
						if (isPrimitiveVoid && !isPrimitiveVoidExpected) {
							appendDefaultLiteral(appendable, expectedType);
						} else {
							internalToJavaExpression(obj, appendable);
						}
						appendable.append(";");
				}
				if (needsSneakyThrow) {
					generateCheckedExceptionHandling(appendable);
				}
				appendable.decreaseIndentation().newLine().append("}");
				appendable.decreaseIndentation().newLine().append("}.apply()");
				if (expectedType.isPrimitive()) {
					appendable.append(".").append(expectedType.getSimpleName()).append("Value()");
				}
			} finally {
				appendable.closeScope();
			}
		} else {
			internalToJavaExpression(obj, appendable);
		}
		return parentAppendable;
	}
	
	protected void appendDefaultLiteral(ITreeAppendable b, /* @Nullable */ LightweightTypeReference type) {
		if (type != null && type.isPrimitive()) {
			Primitive primitiveKind = type.getPrimitiveKind();
			switch (primitiveKind) {
				case Boolean:
					b.append("false");
					break;
				default:
					b.append("0");
					break;
			}
		} else {
			b.append("null");
		}
	}
	
	protected void generateCheckedExceptionHandling(ITreeAppendable appendable) {
		String name = appendable.declareSyntheticVariable(new Object(), "_e");
		appendable.decreaseIndentation().newLine().append("} catch (").append(Throwable.class).append(" ").append(name).append(") {").increaseIndentation();
		appendable.newLine().append("throw ");
		appendable.append(Exceptions.class);
		appendable.append(".sneakyThrow(");
		appendable.append(name);
		appendable.append(");");
		appendable.decreaseIndentation().newLine().append("}");
	}
	
	protected boolean canCompileToJavaExpression(XExpression expression, ITreeAppendable appendable) {
		return internalCanCompileToJavaExpression(expression, appendable);
	}
	
	/**
	 * @param expression to be used by subtypes 
	 * @param appendable to be used by subtypes
	 */
	protected boolean internalCanCompileToJavaExpression(XExpression expression, ITreeAppendable appendable) {
		return true;
	}
	
	public ITreeAppendable compile(XExpression obj, ITreeAppendable parentAppendable, /* @Nullable */ JvmTypeReference expectedReturnType, /* @Nullable */ Set declaredExceptions) {
		LightweightTypeReference converted = null;
		if (expectedReturnType != null) {
			converted = newTypeReferenceOwner(obj).toLightweightTypeReference(expectedReturnType);
		}
		return compile(obj, parentAppendable, converted, declaredExceptions);
	}
	
	public ITreeAppendable compile(XExpression obj, ITreeAppendable parentAppendable, /* @Nullable */ LightweightTypeReference expectedReturnType, /* @Nullable */ Set declaredExceptions) {
		if (declaredExceptions == null) {
			declaredExceptions = newHashSet();
			assert declaredExceptions != null;
		}
		ITreeAppendable appendable = parentAppendable.trace(obj, true);
		final boolean isPrimitiveVoidExpected = expectedReturnType.isPrimitiveVoid(); 
		final boolean isPrimitiveVoid = isPrimitiveVoid(obj);
		final boolean earlyExit = isEarlyExit(obj);
		boolean needsSneakyThrow = needsSneakyThrow(obj, declaredExceptions);
		if (needsSneakyThrow && isPrimitiveVoidExpected && hasJvmConstructorCall(obj)) {
			compileWithJvmConstructorCall((XBlockExpression) obj, appendable);
			return parentAppendable;
		}
		if (needsSneakyThrow) {
			appendable.newLine().append("try {").increaseIndentation();
		}
		internalToJavaStatement(obj, appendable, !isPrimitiveVoidExpected && !isPrimitiveVoid && !earlyExit);
		if (!isPrimitiveVoidExpected && !earlyExit) {
			appendable.newLine().append("return ");
			if (isPrimitiveVoid && !isPrimitiveVoidExpected) {
				appendDefaultLiteral(appendable, expectedReturnType);
			} else {
				internalToConvertedExpression(obj, appendable, expectedReturnType);
			}
			appendable.append(";");
		}
		if (needsSneakyThrow) {
			generateCheckedExceptionHandling(appendable);
		}
		return parentAppendable;
	}
	
	protected void compileWithJvmConstructorCall(XBlockExpression obj, ITreeAppendable apendable) {
		EList expressions = obj.getExpressions();
		internalToJavaStatement(expressions.get(0), apendable.trace(obj, false), false);
		if (expressions.size() == 1) {
			return;
		}
		
		apendable.newLine().append("try {").increaseIndentation();
		
		ITreeAppendable b = apendable.trace(obj, false);
		for (int i = 1; i < expressions.size(); i++) {
			XExpression ex = expressions.get(i);
			internalToJavaStatement(ex, b, false);
		}
		
		generateCheckedExceptionHandling(apendable);
	}
	
	protected boolean hasJvmConstructorCall(XExpression obj) {
		if (!(obj instanceof XBlockExpression)) {
			return false;
		}
		XBlockExpression blockExpression = (XBlockExpression) obj;
		EList expressions = blockExpression.getExpressions();
		if (expressions.isEmpty()) {
			return false;
		}
		XExpression expr = expressions.get(0);
		if (!(expr instanceof XFeatureCall)) {
			return false;
		}
		XFeatureCall featureCall = (XFeatureCall) expr;
		return featureCall.getFeature() instanceof JvmConstructor;
	}

	protected boolean needsSneakyThrow(XExpression obj, Collection declaredExceptions) {
		IResolvedTypes resolvedTypes = getResolvedTypes(obj);
		List thrownExceptions = resolvedTypes.getThrownExceptions(obj);
		return hasUnhandledException(thrownExceptions, declaredExceptions);
	}
	
	protected boolean hasUnhandledException(List thrownExceptions, Collection declaredExceptions) {
		for(LightweightTypeReference thrownException: thrownExceptions) {
			if (!thrownException.isSubtypeOf(RuntimeException.class) && !thrownException.isSubtypeOf(Error.class)) {
				if (isUnhandledException(thrownException, declaredExceptions)) {
					return true;
				}
			}
		}
		return false;
	}
	
	protected boolean isUnhandledException(LightweightTypeReference thrownException, Collection declaredExceptions) {
		for(JvmTypeReference declaredException: declaredExceptions) {
			if (thrownException.isSubtypeOf(declaredException.getType())) {
				return false;
			}
		}
		return true;
	}

	/**
	 * this one trims the outer block
	 */
	public ITreeAppendable compile(XBlockExpression expr, ITreeAppendable b, LightweightTypeReference expectedReturnType) {
		final boolean isPrimitiveVoidExpected = expectedReturnType.isPrimitiveVoid(); 
		final boolean isPrimitiveVoid = isPrimitiveVoid(expr);
		final boolean earlyExit = isEarlyExit(expr);
		final boolean isImplicitReturn = !isPrimitiveVoidExpected && !isPrimitiveVoid && !earlyExit;
		final EList expressions = expr.getExpressions();
		for (int i = 0; i < expressions.size(); i++) {
			XExpression ex = expressions.get(i);
			if (i < expressions.size() - 1) {
				internalToJavaStatement(ex, b.trace(ex, true), false);
			} else {
				internalToJavaStatement(ex, b.trace(ex, true), isImplicitReturn);
				if (isImplicitReturn) {
					b.newLine().append("return (");
					internalToConvertedExpression(ex, b, expectedReturnType);
					b.append(");");
				}
			}
		}
		return b;
	}

	protected boolean isEarlyExit(XExpression expr) {
		return exitComputer.isEarlyExit(expr);
	}

	protected boolean isPrimitiveVoid(JvmTypeReference typeRef) {
		JvmType type = typeRef.getType();
		if (type instanceof JvmVoid) {
			return !type.eIsProxy();
		}
		return false;
	}

	protected JvmTypeReference getType(XExpression expr) {
		LightweightTypeReference actualType = getLightweightType(expr);
		if (actualType != null)
			return actualType.toTypeReference();
		return null;
	}
	
	protected JvmTypeReference getType(JvmIdentifiableElement identifiable) {
		LightweightTypeReference actualType = getLightweightType(identifiable);
		if (actualType != null)
			return actualType.toTypeReference();
		return null;
	}

	protected LightweightTypeReference getLightweightType(XExpression expr) {
		IResolvedTypes resolvedTypes = getResolvedTypes(expr);
		LightweightTypeReference actualType = resolvedTypes.getActualType(expr);
		return actualType;
	}
	
	protected LightweightTypeReference getLightweightType(JvmIdentifiableElement identifiable) {
		IResolvedTypes resolvedTypes = getResolvedTypes(identifiable);
		LightweightTypeReference actualType = resolvedTypes.getActualType(identifiable);
		return actualType;
	}
	
//	protected JvmTypeReference getDeclaredType(JvmIdentifiableElement identifiable) {
//		IResolvedTypes resolvedTypes = getResolvedTypes(identifiable);
//		JvmTypeReference result = resolvedTypes.getDeclaredType(identifiable);
//		return result;
//	}
	
	protected LightweightTypeReference getLightweightReturnType(XExpression expr) {
		IResolvedTypes resolvedTypes = getResolvedTypes(expr);
		LightweightTypeReference returnType = resolvedTypes.getReturnType(expr);
		return returnType;
	}

	protected IResolvedTypes getResolvedTypes(EObject obj) {
		return getTypeResolver().resolveTypes(obj);
	}
	
	protected JvmTypeReference getExpectedType(XExpression expr) {
		LightweightTypeReference expectedType = getLightweightExpectedType(expr);
		if (expectedType != null)
			return expectedType.toTypeReference();
		return null;
	}
	
	protected LightweightTypeReference getLightweightExpectedType(XExpression expr) {
		IResolvedTypes resolvedTypes = getResolvedTypes(expr);
		LightweightTypeReference expectedType = resolvedTypes.getExpectedType(expr);
		return expectedType;
	}
	
	protected abstract void internalToConvertedExpression(final XExpression obj, final ITreeAppendable appendable,
			/* @Nullable */ LightweightTypeReference toBeConvertedTo);
	
	protected boolean isPrimitiveVoid(XExpression xExpression) {
		LightweightTypeReference type = getLightweightType(xExpression);
		return type != null && type.isPrimitiveVoid();
	}

	protected final void internalToJavaStatement(XExpression obj, ITreeAppendable builder, boolean isReferenced) {
		final ITreeAppendable trace = builder.trace(obj, true);
		doInternalToJavaStatement(obj, trace, isReferenced);
	}

	protected void doInternalToJavaStatement(XExpression obj, ITreeAppendable builder, boolean isReferenced) {
		_toJavaStatement(obj, builder, isReferenced);
	}
	
	public void toJavaExpression(final XExpression obj, final ITreeAppendable appendable) {
		internalToJavaExpression(obj, appendable.trace(obj, true));
	}
	
	public void toJavaStatement(final XExpression obj, final ITreeAppendable appendable, boolean isReferenced) {
		internalToJavaStatement(obj, appendable.trace(obj, true), isReferenced);
	}

	protected void internalToJavaExpression(final XExpression obj, final ITreeAppendable appendable) {
		_toJavaExpression(obj, appendable);
	}

	/**
	 * @param b the appendable, unused, but necessary for dispatching purpose
	 * @param isReferenced unused, but necessary for dispatching purpose
	 */
	public void _toJavaStatement(XExpression func, ITreeAppendable b, boolean isReferenced) {
		throw new UnsupportedOperationException("Coudn't find a compilation strategy for expressions of type "
				+ func.getClass().getCanonicalName());
	}

	/**
	 * @param b the appendable, unused, but necessary for dispatching purpose
	 */
	public void _toJavaExpression(XExpression func, ITreeAppendable b) {
		throw new UnsupportedOperationException("Coudn't find a compilation strategy for expressions of type "
				+ func.getClass().getCanonicalName());
	}

	protected void serialize(final JvmTypeReference type, EObject context, ITreeAppendable appendable) {
		serialize(type, context, appendable, false, true);
	}
	
	protected void serialize(final JvmTypeReference type, EObject context, ITreeAppendable appendable, boolean withoutConstraints, boolean paramsToWildcard) {
		serialize(type, context, appendable, withoutConstraints, paramsToWildcard, false, true);
	}
	
	protected void serialize(final JvmTypeReference type, EObject context, ITreeAppendable appendable, boolean withoutConstraints, boolean paramsToWildcard, boolean paramsToObject, boolean allowPrimitives) {
		referenceSerializer.serialize(type, context, appendable, withoutConstraints, paramsToWildcard, paramsToObject, allowPrimitives);
	}
	
	protected boolean isReferenceToForeignTypeParameter(final JvmTypeReference reference, EObject context) {
		JvmType type = reference.getType();
		if (type instanceof JvmTypeParameter) {
			return !referenceSerializer.isLocalTypeParameter(context, (JvmTypeParameter) type);
		}
		return false;
	}

	protected JvmTypeReference resolveMultiType(JvmTypeReference typeRef, EObject context) {
		return referenceSerializer.resolveMultiType(typeRef, context);
	}
	
	protected String getVarName(Object ex, ITreeAppendable appendable) {
		String name = appendable.getName(ex);
		return name;
	}

	@Inject
	private IdentifiableSimpleNameProvider nameProvider;

	public void setNameProvider(IdentifiableSimpleNameProvider nameProvider) {
		this.nameProvider = nameProvider;
	}

	protected IdentifiableSimpleNameProvider getNameProvider() {
		return nameProvider;
	}

	protected String getFavoriteVariableName(EObject ex) {
		if (ex instanceof XVariableDeclaration) {
			return ((XVariableDeclaration) ex).getName();
		}
		if (ex instanceof JvmFormalParameter) {
			return ((JvmFormalParameter) ex).getName();
		}
		if(ex instanceof JvmArrayType) {
			return getFavoriteVariableName(((JvmArrayType) ex).getComponentType());
		}
		if(ex instanceof JvmType) {
			return "_" + Strings.toFirstLower(((JvmType) ex).getSimpleName());
		}
		if (ex instanceof JvmIdentifiableElement) {
			return ((JvmIdentifiableElement) ex).getSimpleName();
		}
		if (ex instanceof XAbstractFeatureCall) {
			String name = nameProvider.getSimpleName(((XAbstractFeatureCall) ex).getFeature());
			if (name == null) {
				throw new IllegalStateException("name may not be null");
			}
			int indexOf = name.indexOf('(');
			if (indexOf != -1) {
				name = name.substring(0, indexOf);
			}
			indexOf = name.lastIndexOf('.');
			if (indexOf != -1) {
				name = name.substring(indexOf + 1);
			}
			if (name.startsWith(OperatorMapping.OP_PREFIX))
				name = Strings.toFirstLower(name.substring(OperatorMapping.OP_PREFIX.length()));
			else if (name.startsWith("get") && name.length() > 3)
				name = Strings.toFirstLower(name.substring(3));
			else if (name.startsWith("to") && name.length() > 2)
				name = Strings.toFirstLower(name.substring(2));
			return "_"+name;
		}
		if (ex instanceof XConstructorCall) {
			String name = ((XConstructorCall) ex).getConstructor().getSimpleName();
			return "_"+Strings.toFirstLower(name);
		}
		return "_"+Strings.toFirstLower(ex.eClass().getName().toLowerCase());
	}

	protected String makeJavaIdentifier(String name) {
		return javaUtils.isJavaKeyword(name) ? name+"_" : name;
	}
	
	protected boolean isJavaConformant(LightweightTypeReference left, LightweightTypeReference right) {
		boolean result = (services.getTypeConformanceComputer().isConformant(
				left, right,
				RawTypeConformanceComputer.ALLOW_PRIMITIVE_WIDENING | RawTypeConformanceComputer.ALLOW_RAW_TYPE_CONVERSION | RawTypeConformanceComputer.ALLOW_BOXING | RawTypeConformanceComputer.ALLOW_UNBOXING) & RawTypeConformanceComputer.SUCCESS) != 0;
		return result;
	}
	
	protected void declareSyntheticVariable(final XExpression expr, ITreeAppendable b) {
		declareFreshLocalVariable(expr, b, new Later() {
			@Override
			public void exec(ITreeAppendable appendable) {
				appendable.append(getDefaultValueLiteral(expr));
			}
		});
	}

	protected String getDefaultValueLiteral(XExpression expr) {
		LightweightTypeReference type = getTypeForVariableDeclaration(expr);
		if (type.isPrimitive()) {
			if (type.getPrimitiveKind() == Primitives.Primitive.Boolean) {
				return "false";
			} else {
				return "(" + type.getSimpleName() + ") 0";
			}
		}
		return "null";
	}

	protected void declareFreshLocalVariable(XExpression expr, ITreeAppendable b, Later expression) {
		LightweightTypeReference type = getTypeForVariableDeclaration(expr);
		final String proposedName = makeJavaIdentifier(getFavoriteVariableName(expr));
		final String varName = b.declareSyntheticVariable(expr, proposedName);
		b.newLine();
		b.append(type);
		b.append(" ").append(varName).append(" = ");
		expression.exec(b);
		b.append(";");
	}

	protected LightweightTypeReference getTypeForVariableDeclaration(XExpression expr) {
		IResolvedTypes resolvedTypes = getResolvedTypes(expr);
		LightweightTypeReference actualType = resolvedTypes.getActualType(expr);
		if (actualType.isPrimitiveVoid()) {
			LightweightTypeReference expectedType = resolvedTypes.getExpectedType(expr);
			if (expectedType == null) {
				expectedType = resolvedTypes.getExpectedReturnType(expr);
				if (expectedType == null) {
					expectedType = resolvedTypes.getReturnType(expr);
				}
			}
			if (expectedType != null && !expectedType.isPrimitiveVoid()) {
				actualType = expectedType;
			}
		}
		return actualType;
	}

	/**
	 * whether an expression needs to be declared in a statement
	 * If an expression has side effects this method must return true for it.
	 * @param expr the checked expression
	 * @param b the appendable which represents the current compiler state
	 */
	protected boolean isVariableDeclarationRequired(XExpression expr, ITreeAppendable b) {
		return true;
	}
	
	/**
	 * @return the variable name under which the result of the expression is stored. Returns null if the
	 *          expression hasn't been assigned to a local variable before.
	 */
	/* @Nullable */
	protected String getReferenceName(XExpression expr, ITreeAppendable b) {
		if (b.hasName(expr))
			return b.getName(expr);
		if (expr instanceof XFeatureCall) {
			XFeatureCall featureCall = (XFeatureCall) expr;
			if (b.hasName(featureCall.getFeature()))
				return b.getName(featureCall.getFeature());
		}
		return null;
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy