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.Set;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.jdt.annotation.NonNullByDefault;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.common.types.JvmAnyTypeReference;
import org.eclipse.xtext.common.types.JvmArrayType;
import org.eclipse.xtext.common.types.JvmFormalParameter;
import org.eclipse.xtext.common.types.JvmIdentifiableElement;
import org.eclipse.xtext.common.types.JvmPrimitiveType;
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.util.Primitives;
import org.eclipse.xtext.common.types.util.Primitives.Primitive;
import org.eclipse.xtext.common.types.util.TypeConformanceComputer;
import org.eclipse.xtext.common.types.util.TypeReferences;
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.typing.ITypeProvider;
import org.eclipse.xtext.xbase.typing.JvmExceptions;
import org.eclipse.xtext.xbase.typing.JvmOnlyTypeConformanceComputer;
import com.google.common.collect.Iterables;
import com.google.inject.Inject;
/**
* @author Sven Efftinge - Initial contribution and API
*/
@SuppressWarnings("deprecation")
@NonNullByDefault
public abstract class AbstractXbaseCompiler {
@Inject
private TypeReferences typeReferences;
@Inject
private TypeReferenceSerializer referenceSerializer;
@Inject
private JavaKeywords javaUtils;
protected TypeReferences getTypeReferences() {
return typeReferences;
}
/**
* Public for testing purpose.
* @noreference This method is not intended to be referenced by clients.
*/
public void setTypeReferences(TypeReferences typeReferences) {
this.typeReferences = typeReferences;
}
@Inject
private ITypeProvider typeProvider;
protected ITypeProvider getTypeProvider() {
return typeProvider;
}
@Inject
private IEarlyExitComputer exitComputer;
@Inject
private JvmOnlyTypeConformanceComputer typeConformanceComputer;
@Inject
private Primitives primitives;
@Inject
private JvmExceptions jvmExceptions;
protected Primitives getPrimitives() {
return primitives;
}
public ITreeAppendable compile(XExpression obj, ITreeAppendable appendable, JvmTypeReference expectedReturnType) {
compile(obj, appendable, expectedReturnType, null);
return appendable;
}
public ITreeAppendable compileAsJavaExpression(XExpression obj, ITreeAppendable parentAppendable, JvmTypeReference expectedType) {
ITreeAppendable appendable = parentAppendable.trace(obj, true);
final boolean isPrimitiveVoidExpected = typeReferences.is(expectedType, Void.TYPE);
final boolean isPrimitiveVoid = isPrimitiveVoid(obj);
final boolean earlyExit = exitComputer.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 ");
JvmTypeReference procedureOrFunction = null;
if (isPrimitiveVoidExpected) {
procedureOrFunction = typeReferences.getTypeForName(Procedures.Procedure0.class, obj);
} else {
procedureOrFunction = typeReferences.getTypeForName(Functions.Function0.class, obj, expectedType);
}
if (procedureOrFunction != null)
referenceSerializer.serialize(procedureOrFunction, obj, appendable, false, false, true, false);
else
appendable.append("Object");
appendable.append("() {").increaseIndentation();
appendable.newLine().append("public ");
referenceSerializer.serialize(primitives.asWrapperTypeIfPrimitive(expectedType), obj, appendable);
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(obj, appendable);
}
appendable.decreaseIndentation().newLine().append("}");
appendable.decreaseIndentation().newLine().append("}.apply()");
} finally {
appendable.closeScope();
}
} else {
internalToJavaExpression(obj, appendable);
}
return parentAppendable;
}
protected void appendDefaultLiteral(ITreeAppendable b, @Nullable JvmTypeReference type) {
if (type != null && getPrimitives().isPrimitive(type)) {
Primitive primitiveKind = getPrimitives().primitiveKind((JvmPrimitiveType) type.getType());
switch (primitiveKind) {
case Boolean:
b.append("false");
break;
default:
b.append("0");
break;
}
} else {
b.append("null");
}
}
protected void generateCheckedExceptionHandling(XExpression obj, ITreeAppendable appendable) {
String name = appendable.declareSyntheticVariable(new Object(), "_e");
appendable.decreaseIndentation().newLine().append("} catch (Throwable "+name+") {").increaseIndentation();
final JvmType findDeclaredType = typeReferences.findDeclaredType(Exceptions.class, obj);
if (findDeclaredType == null) {
appendable.append("COMPILE ERROR : '"+Exceptions.class.getCanonicalName()+"' could not be found on the classpath!");
} else {
appendable.newLine().append("throw ");
appendable.append(findDeclaredType);
appendable.append(".sneakyThrow(");
appendable.append(name);
appendable.append(");");
}
appendable.decreaseIndentation().newLine().append("}");
}
protected boolean canCompileToJavaExpression(XExpression expression, ITreeAppendable appendable) {
TreeIterator iterator = EcoreUtil2.eAll(expression);
while (iterator.hasNext()) {
EObject next = iterator.next();
if (next instanceof XExpression) {
if (!internalCanCompileToJavaExpression((XExpression) next, appendable))
return false;
}
}
return true;
}
protected boolean internalCanCompileToJavaExpression(XExpression expression, ITreeAppendable appendable) {
return getReferenceName(expression, appendable) != null || !isVariableDeclarationRequired(expression, appendable);
}
public ITreeAppendable compile(XExpression obj, ITreeAppendable parentAppendable, @Nullable JvmTypeReference expectedReturnType, @Nullable Set declaredExceptions) {
ITreeAppendable appendable = parentAppendable.trace(obj, true);
if (declaredExceptions == null) {
declaredExceptions = newHashSet();
assert declaredExceptions != null;
}
final boolean isPrimitiveVoidExpected = typeReferences.is(expectedReturnType, Void.TYPE);
final boolean isPrimitiveVoid = isPrimitiveVoid(obj);
final boolean earlyExit = exitComputer.isEarlyExit(obj);
boolean needsSneakyThrow = needsSneakyThrow(obj, declaredExceptions);
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 {
internalToJavaExpression(obj, appendable);
}
appendable.append(";");
}
if (needsSneakyThrow) {
generateCheckedExceptionHandling(obj, appendable);
}
return parentAppendable;
}
protected boolean needsSneakyThrow(XExpression obj, Collection declaredExceptions) {
Iterable types = typeProvider.getThrownExceptionTypes(obj);
Iterable exceptions = jvmExceptions.findUnhandledExceptions(obj, types, declaredExceptions);
return ! Iterables.isEmpty(exceptions);
}
/**
* this one trims the outer block
*/
public ITreeAppendable compile(XBlockExpression expr, ITreeAppendable b, JvmTypeReference expectedReturnType) {
final boolean isPrimitiveVoidExpected = typeReferences.is(expectedReturnType, Void.TYPE);
final boolean isPrimitiveVoid = isPrimitiveVoid(expr);
final boolean earlyExit = exitComputer.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, getType(expr));
b.append(");");
}
}
}
return b;
}
protected JvmTypeReference getType(XExpression expr) {
return getTypeProvider().getType(expr);
}
protected abstract void internalToConvertedExpression(final XExpression obj, final ITreeAppendable appendable,
@Nullable JvmTypeReference toBeConvertedTo);
protected boolean isPrimitiveVoid(XExpression xExpression) {
JvmTypeReference type = getType(xExpression);
return typeReferences.is(type, Void.TYPE);
}
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());
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 void declareSyntheticVariable(final XExpression expr, ITreeAppendable b) {
declareFreshLocalVariable(expr, b, new Later() {
public void exec(ITreeAppendable appendable) {
appendable.append(getDefaultValueLiteral(expr));
}
});
}
protected String getDefaultValueLiteral(XExpression expr) {
JvmTypeReference type = getType(expr);
if (primitives.isPrimitive(type)) {
if (primitives.primitiveKind((JvmPrimitiveType) type.getType()) == Primitive.Boolean) {
return "false";
} else {
return "(" + type.getQualifiedName() + ") 0";
}
}
return "null";
}
protected void declareFreshLocalVariable(XExpression expr, ITreeAppendable b, Later expression) {
JvmTypeReference type = getTypeForVariableDeclaration(expr);
final String proposedName = makeJavaIdentifier(getFavoriteVariableName(expr));
final String varName = b.declareSyntheticVariable(expr, proposedName);
b.newLine();
serialize(type,expr,b);
b.append(" ").append(varName).append(" = ");
expression.exec(b);
b.append(";");
}
protected JvmTypeReference getTypeForVariableDeclaration(XExpression expr) {
JvmTypeReference type = getType(expr);
//TODO we need to replace any occurrence of JvmAnyTypeReference with a better match from the expected type
if (type instanceof JvmAnyTypeReference) {
JvmTypeReference expectedType = getTypeProvider().getExpectedType(expr);
if (expectedType == null) {
expectedType = getTypeProvider().getExpectedReturnType(expr, false);
if (expectedType == null) {
expectedType = getTypeProvider().getCommonReturnType(expr, true);
}
}
if (expectedType!=null && !typeReferences.is(expectedType, Void.TYPE))
type = expectedType;
}
return type;
}
/**
* 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;
}
protected TypeConformanceComputer getTypeConformanceComputer() {
return typeConformanceComputer;
}
/**
* @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