Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.strobel.decompiler.languages.java.ast.LocalClassHelper Maven / Gradle / Ivy
/*
* LocalClassHelper.java
*
* Copyright (c) 2013 Mike Strobel
*
* This source code is based on Mono.Cecil from Jb Evain, Copyright (c) Jb Evain;
* and ILSpy/ICSharpCode from SharpDevelop, Copyright (c) AlphaSierraPapa.
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0.
* A copy of the license can be found in the License.html file at the root of this distribution.
* By using this source code in any fashion, you are agreeing to be bound by the terms of the
* Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*/
package com.strobel.decompiler.languages.java.ast;
import com.strobel.assembler.metadata.FieldDefinition;
import com.strobel.assembler.metadata.FieldReference;
import com.strobel.assembler.metadata.MemberReference;
import com.strobel.assembler.metadata.MetadataHelper;
import com.strobel.assembler.metadata.MethodDefinition;
import com.strobel.assembler.metadata.ParameterDefinition;
import com.strobel.assembler.metadata.TypeDefinition;
import com.strobel.core.VerifyArgument;
import com.strobel.decompiler.DecompilerContext;
import com.strobel.decompiler.ast.Variable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.strobel.core.CollectionUtilities.firstOrDefault;
import static com.strobel.core.CollectionUtilities.getOrDefault;
public final class LocalClassHelper {
private static final ConvertTypeOptions OUTER_TYPE_CONVERT_OPTIONS;
static {
OUTER_TYPE_CONVERT_OPTIONS = new ConvertTypeOptions(false, false);
OUTER_TYPE_CONVERT_OPTIONS.setIncludeTypeArguments(false);
}
public static void replaceClosureMembers(final DecompilerContext context, final AnonymousObjectCreationExpression node) {
replaceClosureMembers(context, node.getTypeDeclaration(), Collections.singletonList(node));
}
public static void replaceClosureMembers(
final DecompilerContext context,
final TypeDeclaration declaration,
final List extends ObjectCreationExpression> instantiations) {
VerifyArgument.notNull(context, "context");
VerifyArgument.notNull(declaration, "declaration");
VerifyArgument.notNull(instantiations, "instantiations");
final Map initializers = new HashMap<>();
final Map replacements = new HashMap<>();
final List nodesToRemove = new ArrayList<>();
final List parametersToRemove = new ArrayList<>();
final List originalArguments;
if (instantiations.isEmpty()) {
originalArguments = Collections.emptyList();
}
else {
originalArguments = new ArrayList<>(instantiations.get(0).getArguments());
}
new ClosureRewriterPhaseOneVisitor(context, originalArguments, replacements, initializers, parametersToRemove, nodesToRemove).run(declaration);
rewriteThisReferences(context, declaration, initializers);
new ClosureRewriterPhaseTwoVisitor(context, replacements, initializers).run(declaration);
for (final ObjectCreationExpression instantiation : instantiations) {
for (final ParameterDefinition p : parametersToRemove) {
final Expression argumentToRemove = getOrDefault(instantiation.getArguments(), p.getPosition());
if (argumentToRemove != null) {
instantiation.getArguments().remove(argumentToRemove);
}
}
}
for (final AstNode n : nodesToRemove) {
if (n instanceof Expression) {
final int argumentIndex = originalArguments.indexOf(n);
if (argumentIndex >= 0) {
for (final ObjectCreationExpression instantiation : instantiations) {
final Expression argumentToRemove = getOrDefault(instantiation.getArguments(), argumentIndex);
if (argumentToRemove != null) {
argumentToRemove.remove();
}
}
}
}
n.remove();
}
}
public static void introduceInitializerBlocks(final DecompilerContext context, final AstNode node) {
VerifyArgument.notNull(context, "context");
VerifyArgument.notNull(node, "node");
new IntroduceInitializersVisitor(context).run(node);
}
private static void rewriteThisReferences(
final DecompilerContext context,
final TypeDeclaration declaration,
final Map initializers) {
final TypeDefinition innerClass = declaration.getUserData(Keys.TYPE_DEFINITION);
if (innerClass != null) {
final ContextTrackingVisitor thisRewriter = new ThisReferenceReplacingVisitor(context, innerClass);
for (final Expression e : initializers.values()) {
thisRewriter.run(e);
}
}
}
private final static class ClosureRewriterPhaseOneVisitor extends ContextTrackingVisitor {
private final Map _replacements;
private final List _originalArguments;
private final List _parametersToRemove;
private final Map _initializers;
private final List _nodesToRemove;
private boolean _baseConstructorCalled;
public ClosureRewriterPhaseOneVisitor(
final DecompilerContext context,
final List originalArguments,
final Map replacements,
final Map initializers,
final List parametersToRemove,
final List nodesToRemove) {
super(context);
_originalArguments = VerifyArgument.notNull(originalArguments, "originalArguments");
_replacements = VerifyArgument.notNull(replacements, "replacements");
_initializers = VerifyArgument.notNull(initializers, "initializers");
_parametersToRemove = VerifyArgument.notNull(parametersToRemove, "parametersToRemove");
_nodesToRemove = VerifyArgument.notNull(nodesToRemove, "nodesToRemove");
}
@Override
public Void visitConstructorDeclaration(final ConstructorDeclaration node, final Void _) {
final boolean wasDone = _baseConstructorCalled;
_baseConstructorCalled = false;
try {
return super.visitConstructorDeclaration(node, _);
}
finally {
_baseConstructorCalled = wasDone;
}
}
@Override
protected Void visitChildren(final AstNode node, final Void _) {
final MethodDefinition currentMethod = context.getCurrentMethod();
if (currentMethod != null && !(currentMethod.isConstructor()/* && currentMethod.isSynthetic()*/)) {
return null;
}
return super.visitChildren(node, _);
}
@Override
public Void visitSuperReferenceExpression(final SuperReferenceExpression node, final Void _) {
super.visitSuperReferenceExpression(node, _);
if (context.getCurrentMethod() != null &&
context.getCurrentMethod().isConstructor() &&
node.getParent() instanceof InvocationExpression) {
//
// We only care about field initializations that occur before the base constructor call.
//
_baseConstructorCalled = true;
}
return null;
}
@Override
public Void visitAssignmentExpression(final AssignmentExpression node, final Void _) {
super.visitAssignmentExpression(node, _);
if (context.getCurrentMethod() == null || !context.getCurrentMethod().isConstructor()) {
return null;
}
final Expression left = node.getLeft();
final Expression right = node.getRight();
if (left instanceof MemberReferenceExpression) {
if (right instanceof IdentifierExpression) {
final Variable variable = right.getUserData(Keys.VARIABLE);
if (variable == null || !variable.isParameter()) {
return null;
}
final MemberReferenceExpression memberReference = (MemberReferenceExpression) left;
final MemberReference member = memberReference.getUserData(Keys.MEMBER_REFERENCE);
if (member instanceof FieldReference &&
memberReference.getTarget() instanceof ThisReferenceExpression) {
final FieldDefinition resolvedField = ((FieldReference) member).resolve();
if (resolvedField != null && resolvedField.isSynthetic()) {
final ParameterDefinition parameter = variable.getOriginalParameter();
int parameterIndex = parameter.getPosition();
if (parameter.getMethod().getParameters().size() > _originalArguments.size()) {
parameterIndex -= (parameter.getMethod().getParameters().size() - _originalArguments.size());
}
if (parameterIndex >= 0 && parameterIndex < _originalArguments.size()) {
final Expression argument = _originalArguments.get(parameterIndex);
if (argument == null) {
return null;
}
_nodesToRemove.add(argument);
if (argument instanceof ThisReferenceExpression) {
//
// Don't replace outer class references; they will be rewritten later.
//
markConstructorParameterForRemoval(node, parameter);
return null;
}
_parametersToRemove.add(parameter);
final String fullName = member.getFullName();
if (!hasSideEffects(argument)) {
_replacements.put(fullName, argument);
}
else {
context.getForcedVisibleMembers().add(resolvedField);
_initializers.put(fullName, argument);
}
if (node.getParent() instanceof ExpressionStatement) {
_nodesToRemove.add(node.getParent());
}
markConstructorParameterForRemoval(node, parameter);
}
}
else if (_baseConstructorCalled &&
resolvedField != null &&
context.getCurrentMethod().isConstructor() &&
(!context.getCurrentMethod().isSynthetic() ||
context.getSettings().getShowSyntheticMembers())) {
final MemberReferenceExpression leftMemberReference = (MemberReferenceExpression) left;
final MemberReference leftMember = leftMemberReference.getUserData(Keys.MEMBER_REFERENCE);
final Variable rightVariable = right.getUserData(Keys.VARIABLE);
if (rightVariable.isParameter()) {
final ParameterDefinition parameter = variable.getOriginalParameter();
final int parameterIndex = parameter.getPosition();
if (parameterIndex >= 0 && parameterIndex < _originalArguments.size()) {
final Expression argument = _originalArguments.get(parameterIndex);
if (parameterIndex == 0 &&
argument instanceof ThisReferenceExpression &&
isLocalOrAnonymous(context.getCurrentType())) {
//
// Don't replace outer class references; they will be rewritten later.
//
return null;
}
final FieldDefinition resolvedTargetField = ((FieldReference) leftMember).resolve();
if (resolvedTargetField != null && !resolvedTargetField.isSynthetic()) {
_parametersToRemove.add(parameter);
_initializers.put(resolvedTargetField.getFullName(), argument);
if (node.getParent() instanceof ExpressionStatement) {
_nodesToRemove.add(node.getParent());
}
}
}
}
}
}
}
else if (_baseConstructorCalled && right instanceof MemberReferenceExpression) {
final MemberReferenceExpression leftMemberReference = (MemberReferenceExpression) left;
final MemberReference leftMember = leftMemberReference.getUserData(Keys.MEMBER_REFERENCE);
final MemberReferenceExpression rightMemberReference = (MemberReferenceExpression) right;
final MemberReference rightMember = right.getUserData(Keys.MEMBER_REFERENCE);
if (rightMember instanceof FieldReference &&
rightMemberReference.getTarget() instanceof ThisReferenceExpression) {
final FieldDefinition resolvedTargetField = ((FieldReference) leftMember).resolve();
final FieldDefinition resolvedSourceField = ((FieldReference) rightMember).resolve();
if (resolvedSourceField != null &&
resolvedTargetField != null &&
resolvedSourceField.isSynthetic() &&
!resolvedTargetField.isSynthetic()) {
final Expression initializer = _replacements.get(rightMember.getFullName());
if (initializer != null) {
_initializers.put(resolvedTargetField.getFullName(), initializer);
if (node.getParent() instanceof ExpressionStatement) {
_nodesToRemove.add(node.getParent());
}
}
}
}
}
}
return null;
}
private void markConstructorParameterForRemoval(final AssignmentExpression node, final ParameterDefinition parameter) {
final ConstructorDeclaration constructorDeclaration = node.getParent(ConstructorDeclaration.class);
if (constructorDeclaration != null) {
final AstNodeCollection parameters = constructorDeclaration.getParameters();
for (final ParameterDeclaration p : parameters) {
if (p.getUserData(Keys.PARAMETER_DEFINITION) == parameter) {
_nodesToRemove.add(p);
break;
}
}
}
}
}
private static boolean isLocalOrAnonymous(final TypeDefinition type) {
return type != null && (type.isLocalClass() || type.isAnonymous());
}
private static boolean hasSideEffects(final Expression e) {
return !(e instanceof IdentifierExpression ||
e instanceof PrimitiveExpression ||
e instanceof ThisReferenceExpression ||
e instanceof SuperReferenceExpression ||
e instanceof NullReferenceExpression ||
e instanceof ClassOfExpression);
}
private final static class ClosureRewriterPhaseTwoVisitor extends ContextTrackingVisitor {
private final Map _replacements;
private final Map _initializers;
protected ClosureRewriterPhaseTwoVisitor(
final DecompilerContext context,
final Map replacements,
final Map initializers) {
super(context);
_replacements = VerifyArgument.notNull(replacements, "replacements");
_initializers = VerifyArgument.notNull(initializers, "initializers");
}
@Override
public Void visitFieldDeclaration(final FieldDeclaration node, final Void data) {
super.visitFieldDeclaration(node, data);
final FieldDefinition field = node.getUserData(Keys.FIELD_DEFINITION);
if (field != null &&
!_initializers.isEmpty() &&
node.getVariables().size() == 1 &&
node.getVariables().firstOrNullObject().getInitializer().isNull()) {
final Expression initializer = _initializers.get(field.getFullName());
if (initializer != null) {
node.getVariables().firstOrNullObject().setInitializer(initializer.clone());
}
}
return null;
}
@Override
public Void visitMemberReferenceExpression(final MemberReferenceExpression node, final Void _) {
super.visitMemberReferenceExpression(node, _);
if (node.getParent() instanceof AssignmentExpression &&
node.getRole() == AssignmentExpression.LEFT_ROLE) {
return null;
}
final MemberReference member = node.getUserData(Keys.MEMBER_REFERENCE);
if (member instanceof FieldReference) {
final Expression replacement = _replacements.get(member.getFullName());
if (replacement != null) {
node.replaceWith(replacement.clone());
}
}
return null;
}
}
private static class ThisReferenceReplacingVisitor extends ContextTrackingVisitor {
private final TypeDefinition _innerClass;
public ThisReferenceReplacingVisitor(final DecompilerContext context, final TypeDefinition innerClass) {
super(context);
_innerClass = innerClass;
}
@Override
public Void visitMemberReferenceExpression(final MemberReferenceExpression node, final Void data) {
super.visitMemberReferenceExpression(node, data);
if (node.getTarget() instanceof ThisReferenceExpression) {
final ThisReferenceExpression thisReference = (ThisReferenceExpression) node.getTarget();
final Expression target = thisReference.getTarget();
if (target == null || target.isNull()) {
MemberReference member = node.getUserData(Keys.MEMBER_REFERENCE);
if (member == null && node.getParent() instanceof InvocationExpression) {
member = node.getParent().getUserData(Keys.MEMBER_REFERENCE);
}
if (member != null &&
MetadataHelper.isEnclosedBy(_innerClass, member.getDeclaringType())) {
final AstBuilder astBuilder = context.getUserData(Keys.AST_BUILDER);
if (astBuilder != null) {
thisReference.setTarget(
new TypeReferenceExpression(
thisReference.getOffset(),
astBuilder.convertType(
member.getDeclaringType(),
OUTER_TYPE_CONVERT_OPTIONS
)
)
);
}
}
}
}
return null;
}
}
private final static class IntroduceInitializersVisitor extends ContextTrackingVisitor {
public IntroduceInitializersVisitor(final DecompilerContext context) {
super(context);
}
@Override
public Void visitSuperReferenceExpression(final SuperReferenceExpression node, final Void _) {
super.visitSuperReferenceExpression(node, _);
if (context.getCurrentMethod() != null &&
context.getCurrentMethod().isConstructor() &&
context.getCurrentMethod().getDeclaringType().isAnonymous() &&
node.getParent() instanceof InvocationExpression &&
node.getRole() == Roles.TARGET_EXPRESSION) {
//
// For anonymous classes, take all statements after the base constructor call and move them
// into an instance initializer block.
//
final Statement parentStatement = firstOrDefault(node.getAncestors(Statement.class));
final ConstructorDeclaration constructor = firstOrDefault(node.getAncestors(ConstructorDeclaration.class));
if (parentStatement == null ||
constructor == null ||
constructor.getParent() == null ||
parentStatement.getNextStatement() == null) {
return null;
}
final InstanceInitializer initializer = new InstanceInitializer();
final BlockStatement initializerBody = new BlockStatement();
for (Statement current = parentStatement.getNextStatement(); current != null;) {
final Statement next = current.getNextStatement();
current.remove();
initializerBody.addChild(current, current.getRole());
current = next;
}
initializer.setBody(initializerBody);
constructor.getParent().insertChildAfter(constructor, initializer, Roles.TYPE_MEMBER);
}
return null;
}
}
}