IDEA-151950 Decompiler doesn't work for classes from JDK 9 - support java 9 string concatenation
This commit is contained in:
@@ -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())) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
// *****************************************************************************
|
||||
|
||||
Reference in New Issue
Block a user