IDEA-151950 Decompiler doesn't work for classes from JDK 9 - support java 9 string concatenation

This commit is contained in:
Egor.Ushakov
2016-03-01 18:04:29 +03:00
parent f128515325
commit 4724fd78a6
11 changed files with 206 additions and 25 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,11 +17,15 @@ package org.jetbrains.java.decompiler.modules.decompiler;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.modules.decompiler.exps.*;
import org.jetbrains.java.decompiler.struct.consts.PooledConstant;
import org.jetbrains.java.decompiler.struct.consts.PrimitiveConstant;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
public class ConcatenationHelper {
@@ -52,6 +56,12 @@ public class ConcatenationHelper {
exprTmp = iex.getInstance();
}
}
else if ("makeConcatWithConstants".equals(iex.getName())) { // java 9 style
List<Exprent> parameters = extractParameters(iex.getBootstrapArguments(), iex);
if (parameters.size() >= 2) {
return createConcatExprent(parameters, expr.bytecode);
}
}
}
if (exprTmp == null) {
@@ -125,20 +135,69 @@ public class ConcatenationHelper {
lstOperands.set(i, rep);
}
}
return createConcatExprent(lstOperands, expr.bytecode);
}
private static Exprent createConcatExprent(List<Exprent> lstOperands, Set<Integer> bytecode) {
// build exprent to return
Exprent func = lstOperands.get(0);
for (int i = 1; i < lstOperands.size(); i++) {
List<Exprent> lstTmp = new ArrayList<Exprent>();
lstTmp.add(func);
lstTmp.add(lstOperands.get(i));
func = new FunctionExprent(FunctionExprent.FUNCTION_STR_CONCAT, lstTmp, expr.bytecode);
func = new FunctionExprent(FunctionExprent.FUNCTION_STR_CONCAT, Arrays.asList(func, lstOperands.get(i)), bytecode);
}
return func;
}
// See StringConcatFactory in jdk sources
private static final char TAG_ARG = '\u0001';
private static final char TAG_CONST = '\u0002';
private static List<Exprent> extractParameters(List<PooledConstant> bootstrapArguments, InvocationExprent expr) {
List<Exprent> parameters = expr.getLstParameters();
if (bootstrapArguments != null) {
PooledConstant constant = bootstrapArguments.get(0);
if (constant.type == CodeConstants.CONSTANT_String) {
String recipe = ((PrimitiveConstant)constant).getString();
List<Exprent> res = new ArrayList<>();
StringBuilder acc = new StringBuilder();
int parameterId = 0;
for (int i = 0; i < recipe.length(); i++) {
char c = recipe.charAt(i);
if (c == TAG_CONST || c == TAG_ARG) {
// Detected a special tag, flush all accumulated characters
// as a constant first:
if (acc.length() > 0) {
res.add(new ConstExprent(VarType.VARTYPE_STRING, acc.toString(), expr.bytecode));
acc.setLength(0);
}
if (c == TAG_CONST) {
// skip for now
}
if (c == TAG_ARG) {
res.add(parameters.get(parameterId++));
}
}
else {
// Not a special characters, this is a constant embedded into
// the recipe itself.
acc.append(c);
}
}
// Flush the remaining characters as constant:
if (acc.length() > 0) {
res.add(new ConstExprent(VarType.VARTYPE_STRING, acc.toString(), expr.bytecode));
}
return res;
}
}
return new ArrayList<>(parameters);
}
private static boolean isAppendConcat(InvocationExprent expr, VarType cltype) {
if ("append".equals(expr.getName())) {

View File

@@ -567,21 +567,14 @@ public class ExprProcessor implements CodeConstants {
case opc_invokeinterface:
case opc_invokedynamic:
if (instr.opcode != opc_invokedynamic || instr.bytecode_version >= CodeConstants.BYTECODE_JAVA_7) {
LinkConstant invoke_constant = pool.getLinkConstant(instr.getOperand(0));
int dynamic_invokation_type = -1;
List<PooledConstant> bootstrap_arguments = null;
if (instr.opcode == opc_invokedynamic && bootstrap != null) {
List<PooledConstant> bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1);
if (bootstrap_arguments.size() > 1) { // INVOKEDYNAMIC is used not only for lambdas
PooledConstant link = bootstrap_arguments.get(1);
if (link instanceof LinkConstant) {
dynamic_invokation_type = ((LinkConstant)link).index1;
}
}
bootstrap_arguments = bootstrap.getMethodArguments(invoke_constant.index1);
}
InvocationExprent exprinv = new InvocationExprent(instr.opcode, invoke_constant, stack, dynamic_invokation_type, bytecode_offsets);
InvocationExprent exprinv = new InvocationExprent(instr.opcode, invoke_constant, bootstrap_arguments, stack, bytecode_offsets);
if (exprinv.getDescriptor().ret.type == CodeConstants.TYPE_VOID) {
exprlist.add(exprinv);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2000-2015 JetBrains s.r.o.
* Copyright 2000-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,13 +15,6 @@
*/
package org.jetbrains.java.decompiler.modules.decompiler.exps;
import java.util.ArrayList;
import java.util.BitSet;
import java.util.Collections;
import java.util.List;
import java.util.Map.Entry;
import java.util.Set;
import org.jetbrains.java.decompiler.code.CodeConstants;
import org.jetbrains.java.decompiler.main.ClassesProcessor.ClassNode;
import org.jetbrains.java.decompiler.main.DecompilerContext;
@@ -36,6 +29,7 @@ import org.jetbrains.java.decompiler.modules.decompiler.vars.VarVersionPair;
import org.jetbrains.java.decompiler.struct.StructClass;
import org.jetbrains.java.decompiler.struct.StructMethod;
import org.jetbrains.java.decompiler.struct.consts.LinkConstant;
import org.jetbrains.java.decompiler.struct.consts.PooledConstant;
import org.jetbrains.java.decompiler.struct.gen.MethodDescriptor;
import org.jetbrains.java.decompiler.struct.gen.VarType;
import org.jetbrains.java.decompiler.struct.match.MatchEngine;
@@ -45,6 +39,9 @@ import org.jetbrains.java.decompiler.util.InterpreterUtil;
import org.jetbrains.java.decompiler.util.ListStack;
import org.jetbrains.java.decompiler.util.TextUtil;
import java.util.*;
import java.util.Map.Entry;
public class InvocationExprent extends Exprent {
public static final int INVOKE_SPECIAL = 1;
@@ -69,16 +66,22 @@ public class InvocationExprent extends Exprent {
private String invokeDynamicClassSuffix;
private int invocationTyp = INVOKE_VIRTUAL;
private List<Exprent> lstParameters = new ArrayList<Exprent>();
private List<PooledConstant> bootstrapArguments;
public InvocationExprent() {
super(EXPRENT_INVOCATION);
}
public InvocationExprent(int opcode, LinkConstant cn, ListStack<Exprent> stack, int dynamicInvocationType, Set<Integer> bytecodeOffsets) {
public InvocationExprent(int opcode,
LinkConstant cn,
List<PooledConstant> bootstrapArguments,
ListStack<Exprent> stack,
Set<Integer> bytecodeOffsets) {
this();
name = cn.elementname;
classname = cn.classname;
this.bootstrapArguments = bootstrapArguments;
switch (opcode) {
case CodeConstants.opc_invokestatic:
@@ -115,6 +118,15 @@ public class InvocationExprent extends Exprent {
}
if (opcode == CodeConstants.opc_invokedynamic) {
int dynamicInvocationType = -1;
if (bootstrapArguments != null) {
if (bootstrapArguments.size() > 1) { // INVOKEDYNAMIC is used not only for lambdas
PooledConstant link = bootstrapArguments.get(1);
if (link instanceof LinkConstant) {
dynamicInvocationType = ((LinkConstant)link).index1;
}
}
}
if (dynamicInvocationType == CodeConstants.CONSTANT_MethodHandle_REF_invokeStatic) {
isStatic = true;
}
@@ -154,6 +166,7 @@ public class InvocationExprent extends Exprent {
ExprProcessor.copyEntries(lstParameters);
addBytecodeOffsets(expr.bytecode);
bootstrapArguments = expr.getBootstrapArguments();
}
@Override
@@ -492,6 +505,10 @@ public class InvocationExprent extends Exprent {
return invokeDynamicClassSuffix;
}
public List<PooledConstant> getBootstrapArguments() {
return bootstrapArguments;
}
// *****************************************************************************
// IMatchable implementation
// *****************************************************************************