}
@SuppressWarnings("ConstantConditions")
private AstNode transformByteCode(final com.strobel.decompiler.ast.Expression byteCode, final boolean isTopLevel) {
final Object operand = byteCode.getOperand();
final Label label = operand instanceof Label ? (Label) operand : null;
final AstType operandType = operand instanceof TypeReference ? _astBuilder.convertType((TypeReference) operand) : AstType.NULL;
final Variable variableOperand = operand instanceof Variable ? (Variable) operand : null;
final FieldReference fieldOperand = operand instanceof FieldReference ? (FieldReference) operand : null;
final List<Expression> arguments = new ArrayList<>();
for (final com.strobel.decompiler.ast.Expression e : byteCode.getArguments()) {
arguments.add((Expression) transformExpression(e, false));
}
final Expression arg1 = arguments.size() >= 1 ? arguments.get(0) : null;
final Expression arg2 = arguments.size() >= 2 ? arguments.get(1) : null;
final Expression arg3 = arguments.size() >= 3 ? arguments.get(2) : null;
switch (byteCode.getCode()) {
case Nop:
return null;
case AConstNull:
return new NullReferenceExpression( byteCode.getOffset());
case LdC: {
if (operand instanceof TypeReference) {
operandType.getChildrenByRole(Roles.TYPE_ARGUMENT).clear();
return new ClassOfExpression(byteCode.getOffset(), operandType);
}
final TypeReference type = byteCode.getInferredType() != null ? byteCode.getInferredType()
: byteCode.getExpectedType();
if (type != null) {
switch (type.getSimpleType()) {
case Byte:
case Short:
return new PrimitiveExpression(
byteCode.getOffset(),
JavaPrimitiveCast.cast(JvmType.Integer, operand)
);
default:
return new PrimitiveExpression(
byteCode.getOffset(),
JavaPrimitiveCast.cast(type.getSimpleType(), operand)
);
}
}
return new PrimitiveExpression(byteCode.getOffset(), operand);
}
case Pop:
case Pop2:
case Dup:
case DupX1:
case DupX2:
case Dup2:
case Dup2X1:
case Dup2X2:
return arg1;
case Swap:
return arg1;
case I2L:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Long), arg1);
case I2F:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Float), arg1);
case I2D:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Double), arg1);
case L2I:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Integer), arg1);
case L2F:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Float), arg1);
case L2D:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Double), arg1);
case F2I:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Integer), arg1);
case F2L:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Long), arg1);
case F2D:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Double), arg1);
case D2I:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Integer), arg1);
case D2L:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Long), arg1);
case D2F:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Float), arg1);
case I2B:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Byte), arg1);
case I2C:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Character), arg1);
case I2S:
return new CastExpression(_astBuilder.convertType(BuiltinTypes.Short), arg1);
case Goto:
return new GotoStatement(byteCode.getOffset(), ((Label) operand).getName());
case GetStatic: {
final ConvertTypeOptions options = new ConvertTypeOptions();
options.setIncludeTypeParameterDefinitions(false);
final MemberReferenceExpression fieldReference = _astBuilder.convertType(fieldOperand.getDeclaringType(), options)
.member(fieldOperand.getName());
fieldReference.putUserData(Keys.MEMBER_REFERENCE, fieldOperand);
return fieldReference;
}
case PutStatic: {
final ConvertTypeOptions options = new ConvertTypeOptions();
options.setIncludeTypeParameterDefinitions(false);
final FieldDefinition resolvedField = fieldOperand.resolve();
final Expression fieldReference;
if (resolvedField != null &&
resolvedField.isFinal() &&
StringUtilities.equals(resolvedField.getDeclaringType().getInternalName(), _context.getCurrentType().getInternalName())) {
//
// Fields marked 'static final' cannot be initialized using a fully qualified name.
//
fieldReference = new IdentifierExpression( byteCode.getOffset(), fieldOperand.getName());
}
else {
fieldReference = _astBuilder.convertType(fieldOperand.getDeclaringType(), options)
.member(fieldOperand.getName());
}
fieldReference.putUserData(Keys.MEMBER_REFERENCE, fieldOperand);
return new AssignmentExpression(fieldReference, arg1);
}
case GetField: {
final MemberReferenceExpression fieldReference;
if (arg1 instanceof ThisReferenceExpression &&
MetadataHelper.isSubType(_context.getCurrentType(), fieldOperand.getDeclaringType()) &&
!StringUtilities.equals(fieldOperand.getDeclaringType().getInternalName(), _context.getCurrentType().getInternalName())) {
fieldReference = new SuperReferenceExpression(arg1.getOffset()).member(fieldOperand.getName());
}
else {
fieldReference = arg1.member(fieldOperand.getName());
}
fieldReference.putUserData(Keys.MEMBER_REFERENCE, fieldOperand);
return fieldReference;
}
case PutField: {
final MemberReferenceExpression fieldReference;
if (arg1 instanceof ThisReferenceExpression &&
MetadataHelper.isSubType(_context.getCurrentType(), fieldOperand.getDeclaringType()) &&
!StringUtilities.equals(fieldOperand.getDeclaringType().getInternalName(), _context.getCurrentType().getInternalName())) {
fieldReference = new SuperReferenceExpression(arg1.getOffset()).member(fieldOperand.getName());
}
else {
fieldReference = arg1.member(fieldOperand.getName());
}
fieldReference.putUserData(Keys.MEMBER_REFERENCE, fieldOperand);
return new AssignmentExpression(fieldReference, arg2);
}
case InvokeVirtual:
return transformCall(true, byteCode, arguments);
case InvokeSpecial:
case InvokeStatic:
return transformCall(false, byteCode, arguments);
case InvokeInterface:
return transformCall(false, byteCode, arguments);
case InvokeDynamic: {
final DynamicCallSite callSite = (DynamicCallSite) operand;
final MethodReference bootstrapMethod = callSite.getBootstrapMethod();
if ("java/lang/invoke/LambdaMetafactory".equals(bootstrapMethod.getDeclaringType().getInternalName()) &&
(StringUtilities.equals("metafactory", bootstrapMethod.getName(), StringComparison.OrdinalIgnoreCase) ||
StringUtilities.equals("altMetafactory", bootstrapMethod.getName(), StringComparison.OrdinalIgnoreCase)) &&
callSite.getBootstrapArguments().size() >= 3 &&
callSite.getBootstrapArguments().get(1) instanceof MethodHandle) {
final MethodHandle targetMethodHandle = (MethodHandle) callSite.getBootstrapArguments().get(1);
final MethodReference targetMethod = targetMethodHandle.getMethod();
final TypeReference declaringType = targetMethod.getDeclaringType();
final String methodName = targetMethod.isConstructor() ? "new" : targetMethod.getName();
final boolean hasInstanceArgument;
switch (targetMethodHandle.getHandleType()) {
case GetField:
case PutField:
case InvokeVirtual:
case InvokeInterface:
case InvokeSpecial:
// assert arg1 != null;
hasInstanceArgument = arg1 != null;
break;
default:
hasInstanceArgument = false;
break;
}
final MethodGroupExpression methodGroup = new MethodGroupExpression(
byteCode.getOffset(),
hasInstanceArgument ? arg1
: new TypeReferenceExpression( byteCode.getOffset(), _astBuilder.convertType(declaringType)),
methodName
);
methodGroup.getClosureArguments().addAll(
hasInstanceArgument ? arguments.subList(1, arguments.size()) : arguments
);
methodGroup.putUserData(Keys.DYNAMIC_CALL_SITE, callSite);
methodGroup.putUserData(Keys.MEMBER_REFERENCE, targetMethod);
if (byteCode.getInferredType() != null) {
methodGroup.putUserData(Keys.TYPE_REFERENCE, byteCode.getInferredType());
}
return methodGroup;
}
break;
}
case Bind: {
final Lambda lambda = (Lambda) byteCode.getOperand();
final LambdaExpression lambdaExpression = new LambdaExpression(byteCode.getOffset());
final AstNodeCollection<ParameterDeclaration> declarations = lambdaExpression.getParameters();
for (final Variable v : lambda.getParameters()) {
final ParameterDefinition p = v.getOriginalParameter();
final ParameterDeclaration d = new ParameterDeclaration(v.getName(), null);
d.putUserData(Keys.PARAMETER_DEFINITION, p);
d.putUserData(Keys.VARIABLE, v);
for (final CustomAnnotation annotation : p.getAnnotations()) {
d.getAnnotations().add(_astBuilder.createAnnotation(annotation));
}
declarations.add(d);
if (p.isFinal()) {
EntityDeclaration.addModifier(d, Modifier.FINAL);
}
}
final BlockStatement body = transformBlock(lambda.getBody());
final Match m = LAMBDA_BODY_PATTERN.match(body);
if (m.success()) {
final AstNode bodyNode = first(m.<AstNode>get("body"));
bodyNode.remove();
lambdaExpression.setBody(bodyNode);
if (EMPTY_LAMBDA_BODY_PATTERN.matches(bodyNode)) {
bodyNode.getChildrenByRole(BlockStatement.STATEMENT_ROLE).clear();
}
}
else {
lambdaExpression.setBody(body);
}
lambdaExpression.putUserData(Keys.TYPE_REFERENCE, byteCode.getInferredType());
final DynamicCallSite callSite = lambda.getCallSite();
if (callSite != null) {
lambdaExpression.putUserData(Keys.DYNAMIC_CALL_SITE, callSite);
}
return lambdaExpression;
}
case ArrayLength:
final MemberReferenceExpression length = arg1.member("length");
final TypeReference arrayType = single(byteCode.getArguments()).getInferredType();
if (arrayType != null) {
length.putUserData(
Keys.MEMBER_REFERENCE,
_parser.parseField(arrayType, "length", "I")
);
}
return length;
case AThrow:
return new ThrowStatement(arg1);
case CheckCast:
return new CastExpression(operandType, arg1);
case InstanceOf:
return new InstanceOfExpression( byteCode.getOffset(), arg1, operandType);
case MonitorEnter:
case MonitorExit:
break;
case MultiANewArray: {
final ArrayCreationExpression arrayCreation =
new ArrayCreationExpression( byteCode.getOffset());
int rank = 0;
AstType elementType = operandType;
while (elementType instanceof ComposedType) {
rank += ((ComposedType) elementType).getArraySpecifiers().size();
elementType = ((ComposedType) elementType).getBaseType();
}
arrayCreation.setType(elementType.clone());
for (int i = 0; i < arguments.size(); i++) {
arrayCreation.getDimensions().add(arguments.get(i));
--rank;
}
for (int i = 0; i < rank; i++) {
arrayCreation.getAdditionalArraySpecifiers().add(new ArraySpecifier());
}
return arrayCreation;
}
case Breakpoint:
return null;
case Load: {
if (!variableOperand.isParameter()) {
_localVariablesToDefine.add(variableOperand);
}
if (variableOperand.isParameter() && variableOperand.getOriginalParameter().getPosition() < 0) {
final ThisReferenceExpression self = new ThisReferenceExpression( byteCode.getOffset());
self.putUserData(Keys.TYPE_REFERENCE, _context.getCurrentType());
return self;
}
final IdentifierExpression name = new IdentifierExpression( byteCode.getOffset(),
variableOperand.getName());
name.putUserData(Keys.VARIABLE, variableOperand);
return name;
}
case Store: {
if (!variableOperand.isParameter()) {
_localVariablesToDefine.add(variableOperand);
}
final IdentifierExpression name = new IdentifierExpression( byteCode.getOffset(),
variableOperand.getName());
name.putUserData(Keys.VARIABLE, variableOperand);
return new AssignmentExpression(name, arg1);
}
case LoadElement: {
return new IndexerExpression( byteCode.getOffset(), arg1, arg2);
}
case StoreElement: {
return new AssignmentExpression(
new IndexerExpression( byteCode.getOffset(), arg1, arg2),
arg3
);
}
case Add:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.ADD, arg2);
case Sub:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.SUBTRACT, arg2);
case Mul:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.MULTIPLY, arg2);
case Div:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.DIVIDE, arg2);
case Rem:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.MODULUS, arg2);
case Neg:
return new UnaryOperatorExpression(UnaryOperatorType.MINUS, arg1);
case Shl:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.SHIFT_LEFT, arg2);
case Shr:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.SHIFT_RIGHT, arg2);
case UShr:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.UNSIGNED_SHIFT_RIGHT, arg2);
case And:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.BITWISE_AND, arg2);
case Or:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.BITWISE_OR, arg2);
case Not:
return new UnaryOperatorExpression(UnaryOperatorType.NOT, arg1);
case Xor:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.EXCLUSIVE_OR, arg2);
case Inc: {
if (!variableOperand.isParameter()) {
_localVariablesToDefine.add(variableOperand);
}
final IdentifierExpression name = new IdentifierExpression( byteCode.getOffset(),
variableOperand.getName());
name.getIdentifierToken().putUserData(Keys.VARIABLE, variableOperand);
name.putUserData(Keys.VARIABLE, variableOperand);
final PrimitiveExpression deltaExpression = (PrimitiveExpression) arg1;
final int delta = (int) JavaPrimitiveCast.cast(JvmType.Integer, deltaExpression.getValue());
switch (delta) {
case -1:
return new UnaryOperatorExpression(UnaryOperatorType.DECREMENT, name);
case 1:
return new UnaryOperatorExpression(UnaryOperatorType.INCREMENT, name);
default:
return new AssignmentExpression(name, AssignmentOperatorType.ADD, arg1);
}
}
case CmpEq:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.EQUALITY, arg2);
case CmpNe:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.INEQUALITY, arg2);
case CmpLt:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.LESS_THAN, arg2);
case CmpGe:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.GREATER_THAN_OR_EQUAL, arg2);
case CmpGt:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.GREATER_THAN, arg2);
case CmpLe:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.LESS_THAN_OR_EQUAL, arg2);
case Return:
return new ReturnStatement(byteCode.getOffset(), arg1);
case NewArray: {
final ArrayCreationExpression arrayCreation =
new ArrayCreationExpression( byteCode.getOffset());
TypeReference elementType = operandType.getUserData(Keys.TYPE_REFERENCE);
while (elementType.isArray()) {
arrayCreation.getAdditionalArraySpecifiers().add(new ArraySpecifier());
elementType = elementType.getElementType();
}
arrayCreation.setType(_astBuilder.convertType(elementType));
arrayCreation.getDimensions().add(arg1);
return arrayCreation;
}
case LogicalNot:
return new UnaryOperatorExpression(UnaryOperatorType.NOT, arg1);
case LogicalAnd:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.LOGICAL_AND, arg2);
case LogicalOr:
return new BinaryOperatorExpression(arg1, BinaryOperatorType.LOGICAL_OR, arg2);
case InitObject:
return transformCall(false, byteCode, arguments);
case InitArray: {
final ArrayCreationExpression arrayCreation =
new ArrayCreationExpression( byteCode.getOffset());
TypeReference elementType = operandType.getUserData(Keys.TYPE_REFERENCE);
while (elementType.isArray()) {
arrayCreation.getAdditionalArraySpecifiers().add(new ArraySpecifier());
elementType = elementType.getElementType();
}
arrayCreation.setType(_astBuilder.convertType(elementType));
arrayCreation.setInitializer(new ArrayInitializerExpression(arguments));
return arrayCreation;
}
case Wrap:
return null;
case TernaryOp:
return new ConditionalExpression(arg1, arg2, arg3);
case LoopOrSwitchBreak:
return label != null ? new GotoStatement(byteCode.getOffset(), label.getName()) : new BreakStatement(byteCode.getOffset());
case LoopContinue:
return label != null ? new ContinueStatement(byteCode.getOffset(), label.getName()) : new ContinueStatement( byteCode.getOffset());
case CompoundAssignment:
throw ContractUtils.unreachable();
case PreIncrement: {