com.google.j2cl.transpiler.frontend.jdt.CompilationUnitBuilder Maven / Gradle / Ivy
/*
* Copyright 2015 Google Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.j2cl.transpiler.frontend.jdt;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.common.collect.MoreCollectors.onlyElement;
import static java.util.Arrays.stream;
import static java.util.stream.Collectors.toCollection;
import static java.util.stream.Collectors.toList;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterables;
import com.google.j2cl.common.FilePosition;
import com.google.j2cl.common.SourcePosition;
import com.google.j2cl.transpiler.ast.ArrayAccess;
import com.google.j2cl.transpiler.ast.ArrayCreationReference;
import com.google.j2cl.transpiler.ast.ArrayLiteral;
import com.google.j2cl.transpiler.ast.ArrayTypeDescriptor;
import com.google.j2cl.transpiler.ast.AssertStatement;
import com.google.j2cl.transpiler.ast.AstUtils;
import com.google.j2cl.transpiler.ast.BinaryExpression;
import com.google.j2cl.transpiler.ast.BinaryOperator;
import com.google.j2cl.transpiler.ast.Block;
import com.google.j2cl.transpiler.ast.BooleanLiteral;
import com.google.j2cl.transpiler.ast.BreakStatement;
import com.google.j2cl.transpiler.ast.CastExpression;
import com.google.j2cl.transpiler.ast.CatchClause;
import com.google.j2cl.transpiler.ast.CompilationUnit;
import com.google.j2cl.transpiler.ast.ConditionalExpression;
import com.google.j2cl.transpiler.ast.ContinueStatement;
import com.google.j2cl.transpiler.ast.DeclaredTypeDescriptor;
import com.google.j2cl.transpiler.ast.DoWhileStatement;
import com.google.j2cl.transpiler.ast.Expression;
import com.google.j2cl.transpiler.ast.Expression.Associativity;
import com.google.j2cl.transpiler.ast.ExpressionStatement;
import com.google.j2cl.transpiler.ast.Field;
import com.google.j2cl.transpiler.ast.FieldAccess;
import com.google.j2cl.transpiler.ast.FieldDescriptor;
import com.google.j2cl.transpiler.ast.ForEachStatement;
import com.google.j2cl.transpiler.ast.ForStatement;
import com.google.j2cl.transpiler.ast.FunctionExpression;
import com.google.j2cl.transpiler.ast.IfStatement;
import com.google.j2cl.transpiler.ast.InstanceOfExpression;
import com.google.j2cl.transpiler.ast.Label;
import com.google.j2cl.transpiler.ast.LabelReference;
import com.google.j2cl.transpiler.ast.LabeledStatement;
import com.google.j2cl.transpiler.ast.Literal;
import com.google.j2cl.transpiler.ast.LocalClassDeclarationStatement;
import com.google.j2cl.transpiler.ast.Method;
import com.google.j2cl.transpiler.ast.MethodCall;
import com.google.j2cl.transpiler.ast.MethodDescriptor;
import com.google.j2cl.transpiler.ast.MethodReference;
import com.google.j2cl.transpiler.ast.NewArray;
import com.google.j2cl.transpiler.ast.NewInstance;
import com.google.j2cl.transpiler.ast.NumberLiteral;
import com.google.j2cl.transpiler.ast.PostfixExpression;
import com.google.j2cl.transpiler.ast.PrefixExpression;
import com.google.j2cl.transpiler.ast.PrimitiveTypeDescriptor;
import com.google.j2cl.transpiler.ast.ReturnStatement;
import com.google.j2cl.transpiler.ast.Statement;
import com.google.j2cl.transpiler.ast.StringLiteral;
import com.google.j2cl.transpiler.ast.SuperReference;
import com.google.j2cl.transpiler.ast.SwitchCase;
import com.google.j2cl.transpiler.ast.SwitchStatement;
import com.google.j2cl.transpiler.ast.SynchronizedStatement;
import com.google.j2cl.transpiler.ast.ThisReference;
import com.google.j2cl.transpiler.ast.ThrowStatement;
import com.google.j2cl.transpiler.ast.TryStatement;
import com.google.j2cl.transpiler.ast.Type;
import com.google.j2cl.transpiler.ast.TypeDeclaration;
import com.google.j2cl.transpiler.ast.TypeDescriptor;
import com.google.j2cl.transpiler.ast.TypeDescriptors;
import com.google.j2cl.transpiler.ast.TypeLiteral;
import com.google.j2cl.transpiler.ast.UnaryExpression;
import com.google.j2cl.transpiler.ast.UnionTypeDescriptor;
import com.google.j2cl.transpiler.ast.Variable;
import com.google.j2cl.transpiler.ast.VariableDeclarationExpression;
import com.google.j2cl.transpiler.ast.VariableDeclarationFragment;
import com.google.j2cl.transpiler.ast.WhileStatement;
import com.google.j2cl.transpiler.frontend.common.AbstractCompilationUnitBuilder;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.annotation.Nullable;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.AnnotationTypeMemberDeclaration;
import org.eclipse.jdt.core.dom.AnonymousClassDeclaration;
import org.eclipse.jdt.core.dom.ArrayType;
import org.eclipse.jdt.core.dom.BodyDeclaration;
import org.eclipse.jdt.core.dom.CreationReference;
import org.eclipse.jdt.core.dom.EnhancedForStatement;
import org.eclipse.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.jdt.core.dom.EnumDeclaration;
import org.eclipse.jdt.core.dom.ExpressionMethodReference;
import org.eclipse.jdt.core.dom.FieldDeclaration;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Initializer;
import org.eclipse.jdt.core.dom.LambdaExpression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.SuperMethodReference;
import org.eclipse.jdt.core.dom.TypeMethodReference;
import org.eclipse.jdt.core.dom.VariableDeclaration;
/** Creates a J2CL Java AST from the AST provided by JDT. */
public class CompilationUnitBuilder extends AbstractCompilationUnitBuilder {
private final JdtEnvironment environment;
private class ASTConverter {
private org.eclipse.jdt.core.dom.CompilationUnit jdtCompilationUnit;
private final Map variableByJdtBinding = new HashMap<>();
// Keeps track of labels that are currently in scope. Even though labels cannot have the
// same name if they are nested in the same method body, labels with the same name could
// be lexically nested by being in different methods bodies, e.g. from local or anonymous
// classes or lambdas.
private final Map> labelsInScope = new HashMap<>();
private CompilationUnit convert(
String sourceFilePath, org.eclipse.jdt.core.dom.CompilationUnit jdtCompilationUnit) {
this.jdtCompilationUnit = jdtCompilationUnit;
setCurrentSourceFile(sourceFilePath);
PackageDeclaration packageDeclaration = jdtCompilationUnit.getPackage();
String packageName =
packageDeclaration == null ? "" : packageDeclaration.getName().getFullyQualifiedName();
setCurrentCompilationUnit(CompilationUnit.createForFile(sourceFilePath, packageName));
for (Object object : jdtCompilationUnit.types()) {
AbstractTypeDeclaration abstractTypeDeclaration = (AbstractTypeDeclaration) object;
getCurrentCompilationUnit().addType(convert(abstractTypeDeclaration));
}
return getCurrentCompilationUnit();
}
private Type convert(AbstractTypeDeclaration typeDeclaration) {
switch (typeDeclaration.getNodeType()) {
case ASTNode.ANNOTATION_TYPE_DECLARATION:
case ASTNode.TYPE_DECLARATION:
return convertType(typeDeclaration);
case ASTNode.ENUM_DECLARATION:
return convert((EnumDeclaration) typeDeclaration);
default:
throw internalCompilerError(
"Unexpected node type for AbstractTypeDeclaration: %s type name: %s ",
typeDeclaration.getClass().getName(), typeDeclaration.getName().toString());
}
}
private Type convert(EnumDeclaration enumDeclaration) {
Type enumType = convertType(enumDeclaration);
checkState(enumType.isEnum());
processEnclosedBy(
enumType,
() -> {
int ordinal = 0;
for (EnumConstantDeclaration enumConstantDeclaration :
JdtEnvironment.asTypedList(
enumDeclaration.enumConstants())) {
enumType.addMember(ordinal, convert(enumConstantDeclaration));
ordinal++;
}
return null;
});
return enumType;
}
private Type convertType(AbstractTypeDeclaration typeDeclaration) {
return convertType(
typeDeclaration.resolveBinding(),
JdtEnvironment.asTypedList(typeDeclaration.bodyDeclarations()),
typeDeclaration.getName());
}
/**
* Constructs a type, maintains the type stack and let's the caller to do additional work by
* supplying a {@code typeProcessor}.
*
* @return {T} the value returned by {@code typeProcessor}
*/
private Type convertType(
ITypeBinding typeBinding,
List bodyDeclarations,
ASTNode sourcePositionNode) {
Type type = createType(typeBinding, sourcePositionNode);
processEnclosedBy(
type,
() -> {
convertTypeBody(type, bodyDeclarations);
return null;
});
return type;
}
private void convertTypeBody(Type type, List bodyDeclarations) {
for (BodyDeclaration bodyDeclaration : bodyDeclarations) {
if (bodyDeclaration instanceof FieldDeclaration) {
FieldDeclaration fieldDeclaration = (FieldDeclaration) bodyDeclaration;
type.addMembers(convert(fieldDeclaration));
} else if (bodyDeclaration instanceof MethodDeclaration) {
MethodDeclaration methodDeclaration = (MethodDeclaration) bodyDeclaration;
type.addMember(convert(methodDeclaration));
} else if (bodyDeclaration instanceof AnnotationTypeMemberDeclaration) {
AnnotationTypeMemberDeclaration memberDeclaration =
(AnnotationTypeMemberDeclaration) bodyDeclaration;
type.addMember(convert(memberDeclaration));
} else if (bodyDeclaration instanceof Initializer) {
Initializer initializer = (Initializer) bodyDeclaration;
Block block = convert(initializer.getBody());
if (JdtEnvironment.isStatic(initializer)) {
type.addStaticInitializerBlock(block);
} else {
type.addInstanceInitializerBlock(block);
}
} else if (bodyDeclaration instanceof AbstractTypeDeclaration) {
// Nested class
AbstractTypeDeclaration nestedTypeDeclaration = (AbstractTypeDeclaration) bodyDeclaration;
type.addType(convert(nestedTypeDeclaration));
} else {
throw internalCompilerError(
"Unexpected type for BodyDeclaration: %s, in type: %s",
bodyDeclaration.getClass().getName(), type.getDeclaration().getQualifiedSourceName());
}
}
}
private Field convert(EnumConstantDeclaration enumConstantDeclaration) {
IMethodBinding enumConstructorBinding = enumConstantDeclaration.resolveConstructorBinding();
Type anonymousInnerClass =
enumConstantDeclaration.getAnonymousClassDeclaration() != null
? convertAnonymousClassDeclaration(
enumConstantDeclaration.getAnonymousClassDeclaration(),
enumConstructorBinding,
null)
: null;
FieldDescriptor fieldDescriptor =
environment.createFieldDescriptor(enumConstantDeclaration.resolveVariable());
// Since initializing custom values for JsEnum requires literals, we fold constant expressions
// to give more options to the user. E.g. -1 is a unary expression but the expression is a
// constant that can be evaluated at compile time, hence it makes sense to allow it.
boolean foldConstantArguments = fieldDescriptor.getEnclosingTypeDescriptor().isJsEnum();
MethodDescriptor methodDescriptor =
environment.createMethodDescriptor(enumConstructorBinding);
Expression initializer =
NewInstance.Builder.from(methodDescriptor)
.setArguments(
convertArguments(
enumConstructorBinding,
JdtEnvironment.asTypedList(enumConstantDeclaration.arguments()),
foldConstantArguments))
.setAnonymousInnerClass(anonymousInnerClass)
.build();
checkArgument(fieldDescriptor.isEnumConstant());
return Field.Builder.from(fieldDescriptor)
.setInitializer(initializer)
.setSourcePosition(getSourcePosition(enumConstantDeclaration))
.setNameSourcePosition(getSourcePosition(enumConstantDeclaration.getName()))
.build();
}
private List convert(FieldDeclaration fieldDeclaration) {
List fields = new ArrayList<>();
for (Object object : fieldDeclaration.fragments()) {
org.eclipse.jdt.core.dom.VariableDeclarationFragment fragment =
(org.eclipse.jdt.core.dom.VariableDeclarationFragment) object;
Expression initializer;
IVariableBinding variableBinding = fragment.resolveBinding();
if (variableBinding.getConstantValue() == null) {
initializer = convertOrNull(fragment.getInitializer());
} else {
initializer =
Literal.fromValue(
variableBinding.getConstantValue(),
environment.createTypeDescriptor(variableBinding.getType()));
}
Field field =
Field.Builder.from(environment.createFieldDescriptor(variableBinding))
.setInitializer(initializer)
.setSourcePosition(getSourcePosition(fieldDeclaration))
.setNameSourcePosition(getSourcePosition(fragment.getName()))
.build();
fields.add(field);
}
return fields;
}
private Method convert(MethodDeclaration methodDeclaration) {
boolean inNullMarkedScope = getCurrentType().getDeclaration().isNullMarked();
List parameters = new ArrayList<>();
for (SingleVariableDeclaration parameter :
JdtEnvironment.asTypedList(methodDeclaration.parameters())) {
parameters.add(createVariable(parameter, inNullMarkedScope));
}
MethodDescriptor methodDescriptor =
environment.createMethodDescriptor(methodDeclaration.resolveBinding());
// If a method has no body, initialize the body with an empty list of statements.
Block body =
methodDeclaration.getBody() == null
? Block.newBuilder().setSourcePosition(getSourcePosition(methodDeclaration)).build()
: convert(methodDeclaration.getBody());
return newMethodBuilder(methodDescriptor)
.setBodySourcePosition(body.getSourcePosition())
.setSourcePosition(getSourcePosition(methodDeclaration.getName()))
.setParameters(parameters)
.addStatements(body.getStatements())
.build();
}
private Method convert(AnnotationTypeMemberDeclaration memberDeclaration) {
MethodDescriptor methodDescriptor =
environment.createMethodDescriptor(memberDeclaration.resolveBinding());
return newMethodBuilder(methodDescriptor)
.setSourcePosition(getSourcePosition(memberDeclaration))
.build();
}
private Method.Builder newMethodBuilder(MethodDescriptor methodDescriptor) {
return Method.newBuilder().setMethodDescriptor(methodDescriptor);
}
private ArrayAccess convert(org.eclipse.jdt.core.dom.ArrayAccess expression) {
return ArrayAccess.newBuilder()
.setArrayExpression(convert(expression.getArray()))
.setIndexExpression(convert(expression.getIndex()))
.build();
}
private NewArray convert(org.eclipse.jdt.core.dom.ArrayCreation expression) {
ArrayType arrayType = expression.getType();
List dimensionExpressions =
convertExpressions(JdtEnvironment.asTypedList(expression.dimensions()));
// Pad the dimension expressions with null values to denote omitted dimensions.
AstUtils.addNullPadding(dimensionExpressions, arrayType.getDimensions());
ArrayLiteral arrayLiteral =
expression.getInitializer() == null ? null : convert(expression.getInitializer());
ArrayTypeDescriptor typeDescriptor =
(ArrayTypeDescriptor)
environment.createTypeDescriptor(
expression.resolveTypeBinding(),
getCurrentType().getDeclaration().isNullMarked());
return NewArray.newBuilder()
.setTypeDescriptor(typeDescriptor)
.setDimensionExpressions(dimensionExpressions)
.setInitializer(arrayLiteral)
.build();
}
private ArrayLiteral convert(org.eclipse.jdt.core.dom.ArrayInitializer expression) {
return new ArrayLiteral(
(ArrayTypeDescriptor)
environment.createTypeDescriptor(
expression.resolveTypeBinding(),
getCurrentType().getDeclaration().isNullMarked()),
convertExpressions(JdtEnvironment.asTypedList(expression.expressions())));
}
private BooleanLiteral convert(org.eclipse.jdt.core.dom.BooleanLiteral literal) {
return literal.booleanValue() ? BooleanLiteral.get(true) : BooleanLiteral.get(false);
}
private CastExpression convert(org.eclipse.jdt.core.dom.CastExpression expression) {
// Resolve the cast type descriptor in the proper @NullMarked scope so that type arguments
// are inferred with the correct nullability. The nullability of cast type itself will be
// inferred from the expression.
TypeDescriptor castTypeDescriptor =
environment.createTypeDescriptor(
expression.getType().resolveBinding(),
getCurrentType().getDeclaration().isNullMarked());
Expression castExpression = convert(expression.getExpression());
if (!castExpression.canBeNull()) {
castTypeDescriptor = castTypeDescriptor.toNonNullable();
} else if (castExpression.getTypeDescriptor().isNullable()) {
castTypeDescriptor = castTypeDescriptor.toNullable();
}
return CastExpression.newBuilder()
.setExpression(castExpression)
.setCastTypeDescriptor(castTypeDescriptor)
.build();
}
private NumberLiteral convert(org.eclipse.jdt.core.dom.CharacterLiteral literal) {
return NumberLiteral.fromChar(literal.charValue());
}
private Expression convert(org.eclipse.jdt.core.dom.ClassInstanceCreation expression) {
IMethodBinding constructorBinding = expression.resolveConstructorBinding();
Expression qualifier = convertOrNull(expression.getExpression());
List arguments =
convertArguments(constructorBinding, JdtEnvironment.asTypedList(expression.arguments()));
MethodDescriptor constructorDescriptor =
environment.createMethodDescriptor(constructorBinding);
Type anonymousInnerClass = null;
if (expression.getAnonymousClassDeclaration() != null) {
anonymousInnerClass =
convertAnonymousClassDeclaration(
expression.getAnonymousClassDeclaration(), constructorBinding, qualifier);
constructorDescriptor =
Iterables.getOnlyElement(anonymousInnerClass.getConstructors()).getDescriptor();
qualifier = null;
}
return NewInstance.Builder.from(constructorDescriptor)
.setQualifier(qualifier)
.setArguments(arguments)
.setAnonymousInnerClass(anonymousInnerClass)
.build();
}
private Type convertAnonymousClassDeclaration(
AnonymousClassDeclaration typeDeclaration,
IMethodBinding constructorBinding,
Expression superQualifier) {
Type type =
convertType(
typeDeclaration.resolveBinding(),
JdtEnvironment.asTypedList(typeDeclaration.bodyDeclarations()),
typeDeclaration);
// The initial constructor descriptor does not include the super call qualifier.
MethodDescriptor constructorDescriptor =
environment.createMethodDescriptor(constructorBinding);
// Find the corresponding superconstructor, the ClassInstanceCreation construct does not
// have a reference the super constructor that needs to be called. But the synthetic
// anonymous class constructor is a subsignature of the corresponding super constructor.
IMethodBinding superConstructorBinding =
stream(typeDeclaration.resolveBinding().getSuperclass().getDeclaredMethods())
.filter(IMethodBinding::isConstructor)
.filter(constructorBinding::isSubsignature)
.findFirst()
.get();
MethodDescriptor superConstructorDescriptor =
environment.createMethodDescriptor(superConstructorBinding);
type.addMember(
0,
AstUtils.createImplicitAnonymousClassConstructor(
type.getSourcePosition(),
constructorDescriptor,
superConstructorDescriptor,
superQualifier));
return type;
}
@Nullable
private Expression convertOrNull(org.eclipse.jdt.core.dom.Expression expression) {
return expression != null ? convert(expression) : null;
}
private Expression convert(org.eclipse.jdt.core.dom.Expression expression) {
switch (expression.getNodeType()) {
case ASTNode.ARRAY_ACCESS:
return convert((org.eclipse.jdt.core.dom.ArrayAccess) expression);
case ASTNode.ARRAY_CREATION:
return convert((org.eclipse.jdt.core.dom.ArrayCreation) expression);
case ASTNode.ARRAY_INITIALIZER:
return convert((org.eclipse.jdt.core.dom.ArrayInitializer) expression);
case ASTNode.ASSIGNMENT:
return convert((org.eclipse.jdt.core.dom.Assignment) expression);
case ASTNode.BOOLEAN_LITERAL:
return convert((org.eclipse.jdt.core.dom.BooleanLiteral) expression);
case ASTNode.CAST_EXPRESSION:
return convert((org.eclipse.jdt.core.dom.CastExpression) expression);
case ASTNode.CHARACTER_LITERAL:
return convert((org.eclipse.jdt.core.dom.CharacterLiteral) expression);
case ASTNode.CLASS_INSTANCE_CREATION:
return convert((org.eclipse.jdt.core.dom.ClassInstanceCreation) expression);
case ASTNode.CONDITIONAL_EXPRESSION:
return convert((org.eclipse.jdt.core.dom.ConditionalExpression) expression);
case ASTNode.EXPRESSION_METHOD_REFERENCE:
return convert((org.eclipse.jdt.core.dom.ExpressionMethodReference) expression);
case ASTNode.CREATION_REFERENCE:
return convert((org.eclipse.jdt.core.dom.CreationReference) expression);
case ASTNode.TYPE_METHOD_REFERENCE:
return convert((org.eclipse.jdt.core.dom.TypeMethodReference) expression);
case ASTNode.SUPER_METHOD_REFERENCE:
return convert((org.eclipse.jdt.core.dom.SuperMethodReference) expression);
case ASTNode.FIELD_ACCESS:
return convert((org.eclipse.jdt.core.dom.FieldAccess) expression);
case ASTNode.INFIX_EXPRESSION:
return convert((org.eclipse.jdt.core.dom.InfixExpression) expression);
case ASTNode.INSTANCEOF_EXPRESSION:
return convert((org.eclipse.jdt.core.dom.InstanceofExpression) expression);
case ASTNode.LAMBDA_EXPRESSION:
return convert((org.eclipse.jdt.core.dom.LambdaExpression) expression);
case ASTNode.METHOD_INVOCATION:
return convert((org.eclipse.jdt.core.dom.MethodInvocation) expression);
case ASTNode.NULL_LITERAL:
return environment.createTypeDescriptor(expression.resolveTypeBinding()).getNullValue();
case ASTNode.NUMBER_LITERAL:
return convert((org.eclipse.jdt.core.dom.NumberLiteral) expression);
case ASTNode.PARENTHESIZED_EXPRESSION:
return convert((org.eclipse.jdt.core.dom.ParenthesizedExpression) expression);
case ASTNode.POSTFIX_EXPRESSION:
return convert((org.eclipse.jdt.core.dom.PostfixExpression) expression);
case ASTNode.PREFIX_EXPRESSION:
return convert((org.eclipse.jdt.core.dom.PrefixExpression) expression);
case ASTNode.QUALIFIED_NAME:
return convert((org.eclipse.jdt.core.dom.QualifiedName) expression);
case ASTNode.SIMPLE_NAME:
return convert((org.eclipse.jdt.core.dom.SimpleName) expression);
case ASTNode.STRING_LITERAL:
return convert((org.eclipse.jdt.core.dom.StringLiteral) expression);
case ASTNode.SUPER_FIELD_ACCESS:
return convert((org.eclipse.jdt.core.dom.SuperFieldAccess) expression);
case ASTNode.SUPER_METHOD_INVOCATION:
return convert((org.eclipse.jdt.core.dom.SuperMethodInvocation) expression);
case ASTNode.THIS_EXPRESSION:
return convert((org.eclipse.jdt.core.dom.ThisExpression) expression);
case ASTNode.TYPE_LITERAL:
return convert((org.eclipse.jdt.core.dom.TypeLiteral) expression);
case ASTNode.VARIABLE_DECLARATION_EXPRESSION:
return convert((org.eclipse.jdt.core.dom.VariableDeclarationExpression) expression);
default:
throw internalCompilerError(
"Unexpected type for Expression: %s", expression.getClass().getName());
}
}
private VariableDeclarationExpression convert(
org.eclipse.jdt.core.dom.VariableDeclarationExpression expression) {
List fragments =
JdtEnvironment.asTypedList(expression.fragments());
return VariableDeclarationExpression.newBuilder()
.setVariableDeclarationFragments(
fragments.stream().map(this::convert).collect(toImmutableList()))
.build();
}
private List convertExpressions(
List expressions) {
return expressions.stream().map(this::convert).collect(toCollection(ArrayList::new));
}
private ConditionalExpression convert(
org.eclipse.jdt.core.dom.ConditionalExpression conditionalExpression) {
TypeDescriptor conditionalTypeDescriptor =
environment.createTypeDescriptor(
conditionalExpression.resolveTypeBinding(),
getCurrentType().getDeclaration().isNullMarked());
Expression condition = convert(conditionalExpression.getExpression());
Expression trueExpression = convert(conditionalExpression.getThenExpression());
Expression falseExpression = convert(conditionalExpression.getElseExpression());
return ConditionalExpression.newBuilder()
.setTypeDescriptor(
trueExpression.getTypeDescriptor().canBeNull()
|| falseExpression.getTypeDescriptor().canBeNull()
? conditionalTypeDescriptor.toNullable()
: conditionalTypeDescriptor)
.setConditionExpression(condition)
.setTrueExpression(trueExpression)
.setFalseExpression(falseExpression)
.build();
}
private Statement convertStatement(org.eclipse.jdt.core.dom.Statement statement) {
switch (statement.getNodeType()) {
case ASTNode.ASSERT_STATEMENT:
return convert((org.eclipse.jdt.core.dom.AssertStatement) statement);
case ASTNode.BLOCK:
return convert((org.eclipse.jdt.core.dom.Block) statement);
case ASTNode.BREAK_STATEMENT:
return convert((org.eclipse.jdt.core.dom.BreakStatement) statement);
case ASTNode.CONSTRUCTOR_INVOCATION:
return convert((org.eclipse.jdt.core.dom.ConstructorInvocation) statement);
case ASTNode.CONTINUE_STATEMENT:
return convert((org.eclipse.jdt.core.dom.ContinueStatement) statement);
case ASTNode.DO_STATEMENT:
return convert((org.eclipse.jdt.core.dom.DoStatement) statement);
case ASTNode.EMPTY_STATEMENT:
return Statement.createNoopStatement();
case ASTNode.EXPRESSION_STATEMENT:
return convert((org.eclipse.jdt.core.dom.ExpressionStatement) statement);
case ASTNode.FOR_STATEMENT:
return convert((org.eclipse.jdt.core.dom.ForStatement) statement);
case ASTNode.ENHANCED_FOR_STATEMENT:
return convert((EnhancedForStatement) statement);
case ASTNode.IF_STATEMENT:
return convert((org.eclipse.jdt.core.dom.IfStatement) statement);
case ASTNode.LABELED_STATEMENT:
return convert((org.eclipse.jdt.core.dom.LabeledStatement) statement);
case ASTNode.RETURN_STATEMENT:
return convert((org.eclipse.jdt.core.dom.ReturnStatement) statement);
case ASTNode.SUPER_CONSTRUCTOR_INVOCATION:
return convert((org.eclipse.jdt.core.dom.SuperConstructorInvocation) statement);
case ASTNode.SWITCH_STATEMENT:
return convert((org.eclipse.jdt.core.dom.SwitchStatement) statement);
case ASTNode.SYNCHRONIZED_STATEMENT:
return convert((org.eclipse.jdt.core.dom.SynchronizedStatement) statement);
case ASTNode.THROW_STATEMENT:
return convert((org.eclipse.jdt.core.dom.ThrowStatement) statement);
case ASTNode.TRY_STATEMENT:
return convert((org.eclipse.jdt.core.dom.TryStatement) statement);
case ASTNode.TYPE_DECLARATION_STATEMENT:
return convert((org.eclipse.jdt.core.dom.TypeDeclarationStatement) statement);
case ASTNode.VARIABLE_DECLARATION_STATEMENT:
return convert((org.eclipse.jdt.core.dom.VariableDeclarationStatement) statement);
case ASTNode.WHILE_STATEMENT:
return convert((org.eclipse.jdt.core.dom.WhileStatement) statement);
default:
throw internalCompilerError(
"Unexpected type for Statement: %s", statement.getClass().getName());
}
}
public SourcePosition getSourcePosition(ASTNode node) {
return getSourcePosition(null, node);
}
public SourcePosition getSourcePosition(String name, ASTNode node) {
int startCharacterPosition = node.getStartPosition();
int startLine = jdtCompilationUnit.getLineNumber(startCharacterPosition) - 1;
int startColumn = jdtCompilationUnit.getColumnNumber(startCharacterPosition);
int endCharacterPosition = startCharacterPosition + node.getLength() - 1;
int endLine = jdtCompilationUnit.getLineNumber(endCharacterPosition) - 1;
int endColumn = jdtCompilationUnit.getColumnNumber(endCharacterPosition) + 1;
return SourcePosition.newBuilder()
.setFilePath(getCurrentCompilationUnit().getFilePath())
.setPackageRelativePath(getCurrentCompilationUnit().getPackageRelativePath())
.setName(name)
.setStartFilePosition(
FilePosition.newBuilder()
.setLine(startLine)
.setColumn(startColumn)
.setByteOffset(startCharacterPosition)
.build())
.setEndFilePosition(
FilePosition.newBuilder()
.setLine(endLine)
// TODO(b/92372836): Document which character this should point to
.setColumn(endColumn)
.setByteOffset(endCharacterPosition + 1)
.build())
.build();
}
@SuppressWarnings({"unchecked", "TypeParameterUnusedInFormals"})
private T convertOrNull(org.eclipse.jdt.core.dom.Statement statement) {
return statement != null ? (T) convert(statement) : null;
}
private Statement convert(org.eclipse.jdt.core.dom.Statement statement) {
return convertStatement(statement);
}
private LabeledStatement convert(org.eclipse.jdt.core.dom.LabeledStatement statement) {
Label label = Label.newBuilder().setName(statement.getLabel().getIdentifier()).build();
labelsInScope.computeIfAbsent(label.getName(), n -> new ArrayDeque<>()).push(label);
LabeledStatement labeledStatement =
LabeledStatement.newBuilder()
.setSourcePosition(getSourcePosition(statement))
.setLabel(label)
.setStatement(convert(statement.getBody()))
.build();
labelsInScope.get(label.getName()).pop();
return labeledStatement;
}
private BreakStatement convert(org.eclipse.jdt.core.dom.BreakStatement statement) {
return BreakStatement.newBuilder()
.setSourcePosition(getSourcePosition(statement))
.setLabelReference(getLabelReferenceOrNull(statement.getLabel()))
.build();
}
private ContinueStatement convert(org.eclipse.jdt.core.dom.ContinueStatement statement) {
return ContinueStatement.newBuilder()
.setSourcePosition(getSourcePosition(statement))
.setLabelReference(getLabelReferenceOrNull(statement.getLabel()))
.build();
}
@Nullable
private LabelReference getLabelReferenceOrNull(SimpleName label) {
return label == null
? null
: labelsInScope.get(label.getIdentifier()).peek().createReference();
}
private ForStatement convert(org.eclipse.jdt.core.dom.ForStatement statement) {
return ForStatement.newBuilder()
// The order here is important since initializers can define new variables
// These can be used in the expression, updaters or the body
// This is why we need to process initializers first
.setInitializers(convertExpressions(JdtEnvironment.asTypedList(statement.initializers())))
.setConditionExpression(
statement.getExpression() == null
? BooleanLiteral.get(true)
: convert(statement.getExpression()))
.setBody(convert(statement.getBody()))
.setUpdates(convertExpressions(JdtEnvironment.asTypedList(statement.updaters())))
.setSourcePosition(getSourcePosition(statement))
.build();
}
private Statement convert(EnhancedForStatement statement) {
return ForEachStatement.newBuilder()
.setLoopVariable(convert(statement.getParameter()))
.setIterableExpression(convert(statement.getExpression()))
.setBody(convert(statement.getBody()))
.setSourcePosition(getSourcePosition(statement))
.build();
}
private DoWhileStatement convert(org.eclipse.jdt.core.dom.DoStatement statement) {
return DoWhileStatement.newBuilder()
.setSourcePosition(getSourcePosition(statement))
.setConditionExpression(convert(statement.getExpression()))
.setBody(convert(statement.getBody()))
.build();
}
private Statement convert(
org.eclipse.jdt.core.dom.TypeDeclarationStatement typeDeclarationStatement) {
return new LocalClassDeclarationStatement(
convert(typeDeclarationStatement.getDeclaration()),
getSourcePosition(typeDeclarationStatement));
}
private WhileStatement convert(org.eclipse.jdt.core.dom.WhileStatement statement) {
return WhileStatement.newBuilder()
.setSourcePosition(getSourcePosition(statement))
.setConditionExpression(convert(statement.getExpression()))
.setBody(convert(statement.getBody()))
.build();
}
private IfStatement convert(org.eclipse.jdt.core.dom.IfStatement statement) {
return IfStatement.newBuilder()
.setSourcePosition(getSourcePosition(statement))
.setConditionExpression(convert(statement.getExpression()))
.setThenStatement(convert(statement.getThenStatement()))
.setElseStatement(convertOrNull(statement.getElseStatement()))
.build();
}
private InstanceOfExpression convert(org.eclipse.jdt.core.dom.InstanceofExpression expression) {
return InstanceOfExpression.newBuilder()
.setSourcePosition(getSourcePosition(expression))
.setExpression(convert(expression.getLeftOperand()))
.setTestTypeDescriptor(
environment.createTypeDescriptor(expression.getRightOperand().resolveBinding()))
.build();
}
private Expression convert(LambdaExpression expression) {
MethodDescriptor functionalMethodDescriptor =
environment.createMethodDescriptor(expression.resolveMethodBinding());
return FunctionExpression.newBuilder()
.setTypeDescriptor(
environment.createTypeDescriptor(
expression.resolveTypeBinding(),
getCurrentType().getDeclaration().isNullMarked()))
.setJsAsync(functionalMethodDescriptor.isJsAsync())
.setParameters(
JdtEnvironment.asTypedList(expression.parameters()).stream()
.map(this::convert)
.collect(toImmutableList()))
.setStatements(
convertLambdaBody(
expression.getBody(), functionalMethodDescriptor.getReturnTypeDescriptor())
.getStatements())
.setSourcePosition(getSourcePosition(expression))
.build();
}
// Lambda expression bodies can be either an Expression or a Statement
private Block convertLambdaBody(ASTNode lambdaBody, TypeDescriptor returnTypeDescriptor) {
Block body;
if (lambdaBody.getNodeType() == ASTNode.BLOCK) {
body = convert((org.eclipse.jdt.core.dom.Block) lambdaBody);
} else {
checkArgument(lambdaBody instanceof org.eclipse.jdt.core.dom.Expression);
Expression lambdaMethodBody = convert((org.eclipse.jdt.core.dom.Expression) lambdaBody);
Statement statement =
AstUtils.createReturnOrExpressionStatement(
getSourcePosition(lambdaBody), lambdaMethodBody, returnTypeDescriptor);
body =
Block.newBuilder()
.setSourcePosition(getSourcePosition(lambdaBody))
.setStatements(statement)
.build();
}
return body;
}
/**
* Converts method reference expressions of the form:
*
*
*
*
* q::m into (par1, ..., parN) -> q.m(par1, ..., parN) or
* (let $q = q, (par1, ..., parN) -> $q.m(par1, ..., parN))
*
*
* Depending on whether the qualifier can be evaluated inside the functional expression
* preserving semantics.
*/
private Expression convert(ExpressionMethodReference expression) {
SourcePosition sourcePosition = getSourcePosition(expression);
TypeDescriptor expressionTypeDescriptor =
environment.createTypeDescriptor(
expression.resolveTypeBinding(), getCurrentType().getDeclaration().isNullMarked());
// MethodDescriptor target of the method reference.
MethodDescriptor referencedMethodDescriptor = resolveMethodReferenceTarget(expression);
// Functional interface method that the expression implements.
MethodDescriptor functionalMethodDescriptor =
environment.createMethodDescriptor(
expression.resolveTypeBinding().getFunctionalInterfaceMethod());
Expression qualifier = convert(expression.getExpression());
return MethodReference.newBuilder()
.setTypeDescriptor(expressionTypeDescriptor)
.setReferencedMethodDescriptor(referencedMethodDescriptor)
.setInterfaceMethodDescriptor(functionalMethodDescriptor)
.setQualifier(qualifier)
.setSourcePosition(sourcePosition)
.build();
}
private MethodDescriptor resolveMethodReferenceTarget(ExpressionMethodReference expression) {
IMethodBinding methodBinding = expression.resolveMethodBinding();
if (methodBinding == null) {
// JDT did not resolve the method binding but it was not a compilation error. This situation
// seems to happen only for method references on array objects.
checkArgument(expression.getExpression().resolveTypeBinding().isArray());
// Array methods are provided by java.lang.Object and are matched here by name. This is safe
// because there is only a handful of method in java.lang.Object and if methods are added
// in the future they will be caught be the MoreCollectors.onlyElement. Resolving the target
// correctly if there were many overloads is extra complexity that can be left out until
// it is really needed.
String targetMethodName = expression.getName().getIdentifier();
return TypeDescriptors.get().javaLangObject.getDeclaredMethodDescriptors().stream()
.filter(m -> m.getName().equals(targetMethodName))
.collect(onlyElement());
}
return environment.createMethodDescriptor(methodBinding);
}
/**
* Converts method reference expressions of the forms:
*
*
*
*
* A[]::new into (size) -> new A[size]
* A:new into (par1, ..., parN) -> new A(par1, ..., parN)
*
*/
private Expression convert(CreationReference expression) {
ITypeBinding expressionTypeBinding = expression.getType().resolveBinding();
TypeDescriptor expressionTypeDescriptor =
environment.createTypeDescriptor(
expressionTypeBinding, getCurrentType().getDeclaration().isNullMarked());
MethodDescriptor functionalMethodDescriptor =
environment.createMethodDescriptor(
expression.resolveTypeBinding().getFunctionalInterfaceMethod());
// There are 3 flavors for CreationReferences: 1) array creations, 2) unqualified
// constructors, and 3) implicitly qualified constructors.
// If the expression does not resolve, it is an array creation.
SourcePosition sourcePosition = getSourcePosition(expression);
if (expression.resolveMethodBinding() == null) {
return ArrayCreationReference.newBuilder()
.setTargetTypeDescriptor(
(ArrayTypeDescriptor) environment.createTypeDescriptor(expressionTypeBinding))
.setInterfaceMethodDescriptor(functionalMethodDescriptor)
.setSourcePosition(sourcePosition)
.build();
}
MethodDescriptor targetConstructorMethodDescriptor =
environment.createMethodDescriptor(expression.resolveMethodBinding());
return MethodReference.newBuilder()
.setTypeDescriptor(expressionTypeDescriptor)
.setReferencedMethodDescriptor(targetConstructorMethodDescriptor)
.setInterfaceMethodDescriptor(functionalMethodDescriptor)
.setSourcePosition(sourcePosition)
.build();
}
/**
* Converts method reference expressions of the form:
*
*
*
*
* A::m into (par1, ..., parN) -> A.m(par1, ..., parN)
*
*/
private Expression convert(TypeMethodReference expression) {
ITypeBinding expressionTypeBinding = expression.resolveTypeBinding();
return MethodReference.newBuilder()
.setTypeDescriptor(
environment.createDeclaredTypeDescriptor(
expressionTypeBinding, getCurrentType().getDeclaration().isNullMarked()))
.setReferencedMethodDescriptor(
environment.createMethodDescriptor(expression.resolveMethodBinding()))
.setInterfaceMethodDescriptor(
environment.createMethodDescriptor(
expressionTypeBinding.getFunctionalInterfaceMethod()))
.setSourcePosition(getSourcePosition(expression))
.build();
}
/**
* Converts method reference expressions of the form:
*
*
*
*
* super::m into (par1, ..., parN) -> super.m(par1, ..., parN)
*
*/
private Expression convert(SuperMethodReference expression) {
MethodDescriptor methodDescriptor =
environment.createMethodDescriptor(expression.resolveMethodBinding());
Expression qualifier = createSuperReference(expression.getQualifier());
return MethodReference.newBuilder()
.setTypeDescriptor(environment.createTypeDescriptor(expression.resolveTypeBinding()))
.setReferencedMethodDescriptor(methodDescriptor)
.setInterfaceMethodDescriptor(
environment.createMethodDescriptor(
expression.resolveTypeBinding().getFunctionalInterfaceMethod()))
.setQualifier(qualifier)
.setSourcePosition(getSourcePosition(expression))
.build();
}
private SuperReference createSuperReference(Name qualifier) {
ITypeBinding qualifierTypeBinding =
qualifier != null ? (ITypeBinding) qualifier.resolveBinding() : null;
// Don't consider calls that select a default method in the super hierarchy as qualified
// (to have the same representation as in kotlin). Only consider calls to be qualified if
// they are targeting a method in an enclosing class. Note that enclosing classes can never
// be interfaces.
boolean isQualified =
qualifierTypeBinding != null && !qualifierTypeBinding.getErasure().isInterface();
if (isQualified) {
// This is a qualified super call, targeting an outer class method;
return new SuperReference(
environment.createDeclaredTypeDescriptor(qualifierTypeBinding), true);
}
// Call targeting a method in the super types.
return new SuperReference(getCurrentType().getTypeDescriptor());
}
private AssertStatement convert(org.eclipse.jdt.core.dom.AssertStatement statement) {
return AssertStatement.newBuilder()
.setSourcePosition(getSourcePosition(statement))
.setExpression(convert(statement.getExpression()))
.setMessage(convertOrNull(statement.getMessage()))
.build();
}
private BinaryExpression convert(org.eclipse.jdt.core.dom.Assignment expression) {
return BinaryExpression.newBuilder()
.setLeftOperand(convert(expression.getLeftHandSide()))
.setOperator(JdtEnvironment.getBinaryOperator(expression.getOperator()))
.setRightOperand(convert(expression.getRightHandSide()))
.build();
}
private Block convert(org.eclipse.jdt.core.dom.Block block) {
List statements =
JdtEnvironment.asTypedList(block.statements());
return Block.newBuilder()
.setSourcePosition(getSourcePosition(block))
.setStatements(
statements.stream()
.map(this::convert)
.filter(Predicates.notNull())
.collect(toImmutableList()))
.build();
}
private CatchClause convert(org.eclipse.jdt.core.dom.CatchClause catchClause) {
// Order is important here, exception declaration must be converted before body.
return CatchClause.newBuilder()
.setExceptionVariable(convert(catchClause.getException()))
.setBody(convert(catchClause.getBody()))
.build();
}
private ExpressionStatement convert(org.eclipse.jdt.core.dom.ConstructorInvocation statement) {
IMethodBinding constructorBinding = statement.resolveConstructorBinding();
MethodDescriptor methodDescriptor = environment.createMethodDescriptor(constructorBinding);
return MethodCall.Builder.from(methodDescriptor)
.setArguments(
convertArguments(
constructorBinding, JdtEnvironment.asTypedList(statement.arguments())))
.setSourcePosition(getSourcePosition(statement))
.build()
.makeStatement(getSourcePosition(statement));
}
private Statement convert(org.eclipse.jdt.core.dom.ExpressionStatement statement) {
return convert(statement.getExpression()).makeStatement(getSourcePosition(statement));
}
private FieldAccess convert(org.eclipse.jdt.core.dom.SuperFieldAccess expression) {
IVariableBinding variableBinding = expression.resolveFieldBinding();
FieldDescriptor fieldDescriptor = environment.createFieldDescriptor(variableBinding);
SuperReference qualifier = createSuperReference(expression.getQualifier());
return FieldAccess.Builder.from(fieldDescriptor).setQualifier(qualifier).build();
}
private Expression convert(org.eclipse.jdt.core.dom.FieldAccess expression) {
Expression qualifier = convert(expression.getExpression());
return environment.createFieldAccess(qualifier, expression.resolveFieldBinding());
}
private BinaryExpression convert(org.eclipse.jdt.core.dom.InfixExpression expression) {
Expression leftOperand = convert(expression.getLeftOperand());
Expression rightOperand = convert(expression.getRightOperand());
BinaryOperator operator = JdtEnvironment.getBinaryOperator(expression.getOperator());
checkArgument(
!expression.hasExtendedOperands()
|| operator.getPrecedence().getAssociativity() == Associativity.LEFT);
BinaryExpression binaryExpression =
BinaryExpression.newBuilder()
.setLeftOperand(leftOperand)
.setOperator(operator)
.setRightOperand(rightOperand)
.build();
for (Object object : expression.extendedOperands()) {
org.eclipse.jdt.core.dom.Expression extendedOperand =
(org.eclipse.jdt.core.dom.Expression) object;
binaryExpression =
BinaryExpression.newBuilder()
.setLeftOperand(binaryExpression)
.setOperator(operator)
.setRightOperand(convert(extendedOperand))
.build();
}
return binaryExpression;
}
private Expression convert(org.eclipse.jdt.core.dom.MethodInvocation methodInvocation) {
Expression qualifier =
methodInvocation.getExpression() != null
? convert(methodInvocation.getExpression())
: null;
IMethodBinding methodBinding = methodInvocation.resolveMethodBinding();
MethodDescriptor methodDescriptor = environment.createMethodDescriptor(methodBinding);
List arguments =
convertArguments(methodBinding, JdtEnvironment.asTypedList(methodInvocation.arguments()));
return MethodCall.Builder.from(methodDescriptor)
.setQualifier(qualifier)
.setArguments(arguments)
.setSourcePosition(getSourcePosition(methodInvocation))
.build();
}
private MethodCall convert(SuperMethodInvocation expression) {
IMethodBinding methodBinding = expression.resolveMethodBinding();
MethodDescriptor methodDescriptor = environment.createMethodDescriptor(methodBinding);
return MethodCall.Builder.from(methodDescriptor)
.setQualifier(createSuperReference(expression.getQualifier()))
.setArguments(
convertArguments(methodBinding, JdtEnvironment.asTypedList(expression.arguments())))
.setSourcePosition(getSourcePosition(expression))
.build();
}
private List convertArguments(
IMethodBinding methodBinding,
List argumentExpressions) {
return convertArguments(methodBinding, argumentExpressions, false);
}
private List convertArguments(
IMethodBinding methodBinding,
List argumentExpressions,
boolean foldConstants) {
MethodDescriptor methodDescriptor = environment.createMethodDescriptor(methodBinding);
List arguments =
argumentExpressions.stream()
.map(
expression ->
foldConstants ? convertAndFoldExpression(expression) : convert(expression))
.collect(toList());
return AstUtils.maybePackageVarargs(methodDescriptor, arguments);
}
private NumberLiteral convert(org.eclipse.jdt.core.dom.NumberLiteral literal) {
Number constantValue = (Number) literal.resolveConstantExpressionValue();
PrimitiveTypeDescriptor typeDescriptor =
(PrimitiveTypeDescriptor) environment.createTypeDescriptor(literal.resolveTypeBinding());
return new NumberLiteral(typeDescriptor, constantValue);
}
private Expression convert(org.eclipse.jdt.core.dom.ParenthesizedExpression expression) {
return convert(expression.getExpression());
}
private UnaryExpression convert(org.eclipse.jdt.core.dom.PostfixExpression expression) {
return PostfixExpression.newBuilder()
.setOperand(convert(expression.getOperand()))
.setOperator(JdtEnvironment.getPostfixOperator(expression.getOperator()))
.build();
}
private UnaryExpression convert(org.eclipse.jdt.core.dom.PrefixExpression expression) {
return PrefixExpression.newBuilder()
.setOperand(convert(expression.getOperand()))
.setOperator(JdtEnvironment.getPrefixOperator(expression.getOperator()))
.build();
}
@Nullable
private Expression convert(org.eclipse.jdt.core.dom.QualifiedName expression) {
IBinding binding = expression.resolveBinding();
if (binding instanceof IVariableBinding) {
IVariableBinding variableBinding = (IVariableBinding) binding;
checkArgument(
variableBinding.isField(),
internalCompilerErrorMessage("Unexpected QualifiedName that is not a field"));
Expression qualifier = convert(expression.getQualifier());
return environment.createFieldAccess(qualifier, variableBinding);
}
if (binding instanceof ITypeBinding) {
return null;
}
throw internalCompilerError(
"Unexpected type for QualifiedName binding: %s ", binding.getClass().getName());
}
private ReturnStatement convert(org.eclipse.jdt.core.dom.ReturnStatement statement) {
// Grab the type of the return statement from the method declaration, not from the expression.
return ReturnStatement.newBuilder()
.setExpression(convertOrNull(statement.getExpression()))
.setSourcePosition(getSourcePosition(statement))
.build();
}
@Nullable
private Expression convert(org.eclipse.jdt.core.dom.SimpleName expression) {
IBinding binding = expression.resolveBinding();
if (binding instanceof IVariableBinding) {
IVariableBinding variableBinding = (IVariableBinding) binding;
if (variableBinding.isField()) {
// It refers to a field.
FieldDescriptor fieldDescriptor = environment.createFieldDescriptor(variableBinding);
return FieldAccess.Builder.from(fieldDescriptor).build();
} else {
// It refers to a local variable or parameter in a method or block.
return variableByJdtBinding.get(variableBinding).createReference();
}
}
if (binding instanceof ITypeBinding) {
return null;
}
throw internalCompilerError(
"Unexpected binding class for SimpleName: %s", expression.getClass().getName());
}
private Variable convert(
org.eclipse.jdt.core.dom.SingleVariableDeclaration variableDeclaration) {
boolean inNullMarkedScope = getCurrentType().getDeclaration().isNullMarked();
Variable variable = createVariable(variableDeclaration, inNullMarkedScope);
if (variableDeclaration.getType() instanceof org.eclipse.jdt.core.dom.UnionType) {
// Union types are only relevant in multi catch variable declarations, which appear in the
// AST as a SingleVariableDeclaration.
variable.setTypeDescriptor(
convert((org.eclipse.jdt.core.dom.UnionType) variableDeclaration.getType()));
}
return variable;
}
private Variable createVariable(
VariableDeclaration variableDeclaration, boolean inNullMarkedScope) {
IVariableBinding variableBinding = variableDeclaration.resolveBinding();
Variable variable =
environment.createVariable(
getSourcePosition(variableBinding.getName(), variableDeclaration.getName()),
variableBinding,
inNullMarkedScope);
variableByJdtBinding.put(variableBinding, variable);
return variable;
}
private StringLiteral convert(org.eclipse.jdt.core.dom.StringLiteral literal) {
return new StringLiteral(literal.getLiteralValue());
}
private SwitchStatement convert(org.eclipse.jdt.core.dom.SwitchStatement switchStatement) {
Expression switchExpression = convert(switchStatement.getExpression());
List caseBuilders = new ArrayList<>();
for (org.eclipse.jdt.core.dom.Statement statement :
JdtEnvironment.asTypedList(
switchStatement.statements())) {
if (statement instanceof org.eclipse.jdt.core.dom.SwitchCase) {
caseBuilders.add(convert((org.eclipse.jdt.core.dom.SwitchCase) statement));
} else {
Iterables.getLast(caseBuilders).addStatement(convertStatement(statement));
}
}
return SwitchStatement.newBuilder()
.setSourcePosition(getSourcePosition(switchStatement))
.setSwitchExpression(switchExpression)
.setCases(caseBuilders.stream().map(SwitchCase.Builder::build).collect(toImmutableList()))
.build();
}
private SwitchCase.Builder convert(org.eclipse.jdt.core.dom.SwitchCase statement) {
return statement.isDefault()
? SwitchCase.newBuilder()
: SwitchCase.newBuilder()
// Fold the constant in the switch case to avoid complex expressions. Otherwise JDT
// would represent negative values as unary expressions, e.g - . The Wasm
// backend relies on switch case constant for switch on integral values to be
// literals.
.setCaseExpression(convertAndFoldExpression(statement.getExpression()));
}
private SynchronizedStatement convert(
org.eclipse.jdt.core.dom.SynchronizedStatement statement) {
return SynchronizedStatement.newBuilder()
.setSourcePosition(getSourcePosition(statement))
.setExpression(convert(statement.getExpression()))
.setBody(convert(statement.getBody()))
.build();
}
private ExpressionStatement convert(
org.eclipse.jdt.core.dom.SuperConstructorInvocation expression) {
IMethodBinding superConstructorBinding = expression.resolveConstructorBinding();
MethodDescriptor methodDescriptor =
environment.createMethodDescriptor(superConstructorBinding);
return MethodCall.Builder.from(methodDescriptor)
.setQualifier(convertOrNull(expression.getExpression()))
.setArguments(
convertArguments(
superConstructorBinding, JdtEnvironment.asTypedList(expression.arguments())))
.setSourcePosition(getSourcePosition(expression))
.build()
.makeStatement(getSourcePosition(expression));
}
private Expression convert(org.eclipse.jdt.core.dom.ThisExpression expression) {
Name qualifier = expression.getQualifier();
boolean isQualified = qualifier != null;
DeclaredTypeDescriptor typeDescriptor =
isQualified
? environment.createDeclaredTypeDescriptor(qualifier.resolveTypeBinding())
: getCurrentType().getTypeDescriptor();
return new ThisReference(typeDescriptor, isQualified);
}
private Expression convert(org.eclipse.jdt.core.dom.TypeLiteral literal) {
ITypeBinding typeBinding = literal.getType().resolveBinding();
TypeDescriptor literalTypeDescriptor = environment.createTypeDescriptor(typeBinding);
return new TypeLiteral(getSourcePosition(literal), literalTypeDescriptor);
}
private ThrowStatement convert(org.eclipse.jdt.core.dom.ThrowStatement statement) {
return ThrowStatement.newBuilder()
.setSourcePosition(getSourcePosition(statement))
.setExpression(convert(statement.getExpression()))
.build();
}
private TryStatement convert(org.eclipse.jdt.core.dom.TryStatement statement) {
List resources =
JdtEnvironment.asTypedList(statement.resources());
List catchClauses =
JdtEnvironment.asTypedList(statement.catchClauses());
return TryStatement.newBuilder()
.setSourcePosition(getSourcePosition(statement))
.setResourceDeclarations(
resources.stream()
.map(this::convert)
.map(CompilationUnitBuilder::toResource)
.collect(toImmutableList()))
.setBody(convert(statement.getBody()))
.setCatchClauses(catchClauses.stream().map(this::convert).collect(toImmutableList()))
.setFinallyBlock(convertOrNull(statement.getFinally()))
.build();
}
private TypeDescriptor convert(org.eclipse.jdt.core.dom.UnionType unionType) {
return UnionTypeDescriptor.newBuilder()
.setUnionTypeDescriptors(
JdtEnvironment.asTypedList(unionType.types()).stream()
.map(org.eclipse.jdt.core.dom.Type::resolveBinding)
.map(environment::createTypeDescriptor)
.collect(toImmutableList()))
.build();
}
private VariableDeclarationFragment convert(
org.eclipse.jdt.core.dom.VariableDeclarationFragment variableDeclarationFragment) {
boolean inNullMarkedScope = getCurrentType().getDeclaration().isNullMarked();
Variable variable = createVariable(variableDeclarationFragment, inNullMarkedScope);
return VariableDeclarationFragment.newBuilder()
.setVariable(variable)
.setInitializer(convertOrNull(variableDeclarationFragment.getInitializer()))
.build();
}
private Variable convert(org.eclipse.jdt.core.dom.VariableDeclaration variableDeclaration) {
if (variableDeclaration instanceof org.eclipse.jdt.core.dom.SingleVariableDeclaration) {
return convert((org.eclipse.jdt.core.dom.SingleVariableDeclaration) variableDeclaration);
} else {
return convert((org.eclipse.jdt.core.dom.VariableDeclarationFragment) variableDeclaration)
.getVariable();
}
}
private ExpressionStatement convert(
org.eclipse.jdt.core.dom.VariableDeclarationStatement statement) {
List fragments =
JdtEnvironment.asTypedList(statement.fragments());
return VariableDeclarationExpression.newBuilder()
.setVariableDeclarationFragments(
fragments.stream().map(this::convert).collect(toImmutableList()))
.build()
.makeStatement(getSourcePosition(statement));
}
private Expression convertAndFoldExpression(org.eclipse.jdt.core.dom.Expression expression) {
Object constantValue = expression.resolveConstantExpressionValue();
return constantValue != null
? Literal.fromValue(
constantValue, environment.createTypeDescriptor(expression.resolveTypeBinding()))
: convert(expression);
}
@Nullable
private Type createType(ITypeBinding typeBinding, ASTNode sourcePositionNode) {
if (typeBinding == null) {
return null;
}
TypeDeclaration typeDeclaration = environment.createDeclarationForType(typeBinding);
return new Type(getSourcePosition(sourcePositionNode), typeDeclaration);
}
}
private CompilationUnit buildCompilationUnit(
String sourceFilePath, org.eclipse.jdt.core.dom.CompilationUnit compilationUnit) {
ASTConverter converter = new ASTConverter();
return converter.convert(sourceFilePath, compilationUnit);
}
public static List build(
CompilationUnitsAndTypeBindings compilationUnitsAndTypeBindings, JdtParser jdtParser) {
JdtEnvironment environment =
new JdtEnvironment(
PackageAnnotationsResolver.create(
compilationUnitsAndTypeBindings.getCompilationUnitsByFilePath().entrySet().stream()
.filter(e -> e.getKey().endsWith("package-info.java"))
.map(Entry::getValue),
jdtParser));
Map jdtUnitsByFilePath =
compilationUnitsAndTypeBindings.getCompilationUnitsByFilePath();
List wellKnownTypeBindings = compilationUnitsAndTypeBindings.getTypeBindings();
CompilationUnitBuilder compilationUnitBuilder =
new CompilationUnitBuilder(wellKnownTypeBindings, environment);
return jdtUnitsByFilePath.entrySet().stream()
.map(entry -> compilationUnitBuilder.buildCompilationUnit(entry.getKey(), entry.getValue()))
.collect(toImmutableList());
}
private CompilationUnitBuilder(
List wellKnownTypeBindings, JdtEnvironment environment) {
this.environment = environment;
environment.initWellKnownTypes(wellKnownTypeBindings);
}
}