BCELFactory.java

  1. /*
  2.  * Licensed to the Apache Software Foundation (ASF) under one or more
  3.  * contributor license agreements.  See the NOTICE file distributed with
  4.  * this work for additional information regarding copyright ownership.
  5.  * The ASF licenses this file to You under the Apache License, Version 2.0
  6.  * (the "License"); you may not use this file except in compliance with
  7.  * the License.  You may obtain a copy of the License at
  8.  *
  9.  *      http://www.apache.org/licenses/LICENSE-2.0
  10.  *
  11.  *  Unless required by applicable law or agreed to in writing, software
  12.  *  distributed under the License is distributed on an "AS IS" BASIS,
  13.  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14.  *  See the License for the specific language governing permissions and
  15.  *  limitations under the License.
  16.  */
  17. package org.apache.bcel.util;

  18. import java.io.PrintWriter;
  19. import java.util.ArrayList;
  20. import java.util.HashMap;
  21. import java.util.List;
  22. import java.util.Locale;
  23. import java.util.Map;

  24. import org.apache.bcel.Const;
  25. import org.apache.bcel.classfile.Utility;
  26. import org.apache.bcel.generic.AllocationInstruction;
  27. import org.apache.bcel.generic.ArrayInstruction;
  28. import org.apache.bcel.generic.ArrayType;
  29. import org.apache.bcel.generic.BranchHandle;
  30. import org.apache.bcel.generic.BranchInstruction;
  31. import org.apache.bcel.generic.CHECKCAST;
  32. import org.apache.bcel.generic.CPInstruction;
  33. import org.apache.bcel.generic.CodeExceptionGen;
  34. import org.apache.bcel.generic.ConstantPoolGen;
  35. import org.apache.bcel.generic.ConstantPushInstruction;
  36. import org.apache.bcel.generic.EmptyVisitor;
  37. import org.apache.bcel.generic.FieldInstruction;
  38. import org.apache.bcel.generic.IINC;
  39. import org.apache.bcel.generic.INSTANCEOF;
  40. import org.apache.bcel.generic.Instruction;
  41. import org.apache.bcel.generic.InstructionConst;
  42. import org.apache.bcel.generic.InstructionHandle;
  43. import org.apache.bcel.generic.InvokeInstruction;
  44. import org.apache.bcel.generic.LDC;
  45. import org.apache.bcel.generic.LDC2_W;
  46. import org.apache.bcel.generic.LocalVariableInstruction;
  47. import org.apache.bcel.generic.MULTIANEWARRAY;
  48. import org.apache.bcel.generic.MethodGen;
  49. import org.apache.bcel.generic.NEWARRAY;
  50. import org.apache.bcel.generic.ObjectType;
  51. import org.apache.bcel.generic.RET;
  52. import org.apache.bcel.generic.ReturnInstruction;
  53. import org.apache.bcel.generic.Select;
  54. import org.apache.bcel.generic.Type;

  55. /**
  56.  * Factory creates il.append() statements, and sets instruction targets. A helper class for BCELifier.
  57.  *
  58.  * @see BCELifier
  59.  */
  60. final class BCELFactory extends EmptyVisitor {

  61.     private static final String CONSTANT_PREFIX = Const.class.getSimpleName() + ".";
  62.     private final MethodGen methodGen;
  63.     private final PrintWriter printWriter;
  64.     private final ConstantPoolGen constantPoolGen;

  65.     private final Map<Instruction, InstructionHandle> branchMap = new HashMap<>();

  66.     // Memorize BranchInstructions that need an update
  67.     private final List<BranchInstruction> branches = new ArrayList<>();

  68.     BCELFactory(final MethodGen mg, final PrintWriter out) {
  69.         methodGen = mg;
  70.         constantPoolGen = mg.getConstantPool();
  71.         printWriter = out;
  72.     }

  73.     private void createConstant(final Object value) {
  74.         String embed = value.toString();
  75.         if (value instanceof String) {
  76.             embed = '"' + Utility.convertString(embed) + '"';
  77.         } else if (value instanceof Character) {
  78.             embed = "(char) 0x" + Integer.toHexString(((Character) value).charValue());
  79.         } else if (value instanceof Float) {
  80.             final Float f = (Float) value;
  81.             if (Float.isNaN(f)) {
  82.                 embed = "Float.NaN";
  83.             } else if (f == Float.POSITIVE_INFINITY) {
  84.                 embed = "Float.POSITIVE_INFINITY";
  85.             } else if (f == Float.NEGATIVE_INFINITY) {
  86.                 embed = "Float.NEGATIVE_INFINITY";
  87.             } else {
  88.                 embed += "f";
  89.             }
  90.         }  else if (value instanceof Double) {
  91.             final Double d = (Double) value;
  92.             if (Double.isNaN(d)) {
  93.                 embed = "Double.NaN";
  94.             } else if (d == Double.POSITIVE_INFINITY) {
  95.                 embed = "Double.POSITIVE_INFINITY";
  96.             } else if (d == Double.NEGATIVE_INFINITY) {
  97.                 embed = "Double.NEGATIVE_INFINITY";
  98.             } else {
  99.                 embed += "d";
  100.             }
  101.         } else if (value instanceof Long) {
  102.             embed += "L";
  103.         } else if (value instanceof ObjectType) {
  104.             final ObjectType ot = (ObjectType) value;
  105.             embed = "new ObjectType(\"" + ot.getClassName() + "\")";
  106.         } else if (value instanceof ArrayType) {
  107.             final ArrayType at = (ArrayType) value;
  108.             embed = "new ArrayType(" + BCELifier.printType(at.getBasicType()) + ", " + at.getDimensions() + ")";
  109.         }

  110.         printWriter.println("il.append(new PUSH(_cp, " + embed + "));");
  111.     }

  112.     public void start() {
  113.         if (!methodGen.isAbstract() && !methodGen.isNative()) {
  114.             for (InstructionHandle ih = methodGen.getInstructionList().getStart(); ih != null; ih = ih.getNext()) {
  115.                 final Instruction i = ih.getInstruction();
  116.                 if (i instanceof BranchInstruction) {
  117.                     branchMap.put(i, ih); // memorize container
  118.                 }
  119.                 if (ih.hasTargeters()) {
  120.                     if (i instanceof BranchInstruction) {
  121.                         printWriter.println("    InstructionHandle ih_" + ih.getPosition() + ";");
  122.                     } else {
  123.                         printWriter.print("    InstructionHandle ih_" + ih.getPosition() + " = ");
  124.                     }
  125.                 } else {
  126.                     printWriter.print("    ");
  127.                 }
  128.                 if (!visitInstruction(i)) {
  129.                     i.accept(this);
  130.                 }
  131.             }
  132.             updateBranchTargets();
  133.             updateExceptionHandlers();
  134.         }
  135.     }

  136.     private void updateBranchTargets() {
  137.         branches.forEach(bi -> {
  138.             final BranchHandle bh = (BranchHandle) branchMap.get(bi);
  139.             final int pos = bh.getPosition();
  140.             final String name = bi.getName() + "_" + pos;
  141.             int targetPos = bh.getTarget().getPosition();
  142.             printWriter.println("    " + name + ".setTarget(ih_" + targetPos + ");");
  143.             if (bi instanceof Select) {
  144.                 final InstructionHandle[] ihs = ((Select) bi).getTargets();
  145.                 for (int j = 0; j < ihs.length; j++) {
  146.                     targetPos = ihs[j].getPosition();
  147.                     printWriter.println("    " + name + ".setTarget(" + j + ", ih_" + targetPos + ");");
  148.                 }
  149.             }
  150.         });
  151.     }

  152.     private void updateExceptionHandlers() {
  153.         final CodeExceptionGen[] handlers = methodGen.getExceptionHandlers();
  154.         for (final CodeExceptionGen h : handlers) {
  155.             final String type = h.getCatchType() == null ? "null" : BCELifier.printType(h.getCatchType());
  156.             printWriter.println("    method.addExceptionHandler(" + "ih_" + h.getStartPC().getPosition() + ", " + "ih_" + h.getEndPC().getPosition() + ", "
  157.                 + "ih_" + h.getHandlerPC().getPosition() + ", " + type + ");");
  158.         }
  159.     }

  160.     @Override
  161.     public void visitAllocationInstruction(final AllocationInstruction i) {
  162.         Type type;
  163.         if (i instanceof CPInstruction) {
  164.             type = ((CPInstruction) i).getType(constantPoolGen);
  165.         } else {
  166.             type = ((NEWARRAY) i).getType();
  167.         }
  168.         final short opcode = ((Instruction) i).getOpcode();
  169.         int dim = 1;
  170.         switch (opcode) {
  171.         case Const.NEW:
  172.             printWriter.println("il.append(_factory.createNew(\"" + ((ObjectType) type).getClassName() + "\"));");
  173.             break;
  174.         case Const.MULTIANEWARRAY:
  175.             dim = ((MULTIANEWARRAY) i).getDimensions();
  176.             //$FALL-THROUGH$
  177.         case Const.NEWARRAY:
  178.             if (type instanceof ArrayType) {
  179.                 type = ((ArrayType) type).getBasicType();
  180.             }
  181.             //$FALL-THROUGH$
  182.         case Const.ANEWARRAY:
  183.             printWriter.println("il.append(_factory.createNewArray(" + BCELifier.printType(type) + ", (short) " + dim + "));");
  184.             break;
  185.         default:
  186.             throw new IllegalArgumentException("Unhandled opcode: " + opcode);
  187.         }
  188.     }

  189.     @Override
  190.     public void visitArrayInstruction(final ArrayInstruction i) {
  191.         final short opcode = i.getOpcode();
  192.         final Type type = i.getType(constantPoolGen);
  193.         final String kind = opcode < Const.IASTORE ? "Load" : "Store";
  194.         printWriter.println("il.append(_factory.createArray" + kind + "(" + BCELifier.printType(type) + "));");
  195.     }

  196.     @Override
  197.     public void visitBranchInstruction(final BranchInstruction bi) {
  198.         final BranchHandle bh = (BranchHandle) branchMap.get(bi);
  199.         final int pos = bh.getPosition();
  200.         final String name = bi.getName() + "_" + pos;
  201.         if (bi instanceof Select) {
  202.             final Select s = (Select) bi;
  203.             branches.add(bi);
  204.             final StringBuilder args = new StringBuilder("new int[] { ");
  205.             final int[] matchs = s.getMatchs();
  206.             for (int i = 0; i < matchs.length; i++) {
  207.                 args.append(matchs[i]);
  208.                 if (i < matchs.length - 1) {
  209.                     args.append(", ");
  210.                 }
  211.             }
  212.             args.append(" }");
  213.             printWriter.print("Select " + name + " = new " + bi.getName().toUpperCase(Locale.ENGLISH) + "(" + args + ", new InstructionHandle[] { ");
  214.             for (int i = 0; i < matchs.length; i++) {
  215.                 printWriter.print("null");
  216.                 if (i < matchs.length - 1) {
  217.                     printWriter.print(", ");
  218.                 }
  219.             }
  220.             printWriter.println(" }, null);");
  221.         } else {
  222.             final int tPos = bh.getTarget().getPosition();
  223.             String target;
  224.             if (pos > tPos) {
  225.                 target = "ih_" + tPos;
  226.             } else {
  227.                 branches.add(bi);
  228.                 target = "null";
  229.             }
  230.             printWriter.println("    BranchInstruction " + name + " = _factory.createBranchInstruction(" + CONSTANT_PREFIX
  231.                 + bi.getName().toUpperCase(Locale.ENGLISH) + ", " + target + ");");
  232.         }
  233.         if (bh.hasTargeters()) {
  234.             printWriter.println("    ih_" + pos + " = il.append(" + name + ");");
  235.         } else {
  236.             printWriter.println("    il.append(" + name + ");");
  237.         }
  238.     }

  239.     @Override
  240.     public void visitCHECKCAST(final CHECKCAST i) {
  241.         final Type type = i.getType(constantPoolGen);
  242.         printWriter.println("il.append(_factory.createCheckCast(" + BCELifier.printType(type) + "));");
  243.     }

  244.     @Override
  245.     public void visitConstantPushInstruction(final ConstantPushInstruction i) {
  246.         createConstant(i.getValue());
  247.     }

  248.     @Override
  249.     public void visitFieldInstruction(final FieldInstruction i) {
  250.         final short opcode = i.getOpcode();
  251.         final String className = i.getClassName(constantPoolGen);
  252.         final String fieldName = i.getFieldName(constantPoolGen);
  253.         final Type type = i.getFieldType(constantPoolGen);
  254.         printWriter.println("il.append(_factory.createFieldAccess(\"" + className + "\", \"" + fieldName + "\", " + BCELifier.printType(type) + ", "
  255.             + CONSTANT_PREFIX + Const.getOpcodeName(opcode).toUpperCase(Locale.ENGLISH) + "));");
  256.     }

  257.     @Override
  258.     public void visitINSTANCEOF(final INSTANCEOF i) {
  259.         final Type type = i.getType(constantPoolGen);
  260.         printWriter.println("il.append(_factory.createInstanceOf(" + BCELifier.printType(type) + "));");
  261.     }

  262.     private boolean visitInstruction(final Instruction i) {
  263.         final short opcode = i.getOpcode();
  264.         if (InstructionConst.getInstruction(opcode) != null && !(i instanceof ConstantPushInstruction) && !(i instanceof ReturnInstruction)) { // Handled below
  265.             printWriter.println("il.append(InstructionConst." + i.getName().toUpperCase(Locale.ENGLISH) + ");");
  266.             return true;
  267.         }
  268.         return false;
  269.     }

  270.     @Override
  271.     public void visitInvokeInstruction(final InvokeInstruction i) {
  272.         final short opcode = i.getOpcode();
  273.         final String className = i.getClassName(constantPoolGen);
  274.         final String methodName = i.getMethodName(constantPoolGen);
  275.         final Type type = i.getReturnType(constantPoolGen);
  276.         final Type[] argTypes = i.getArgumentTypes(constantPoolGen);
  277.         printWriter.println("il.append(_factory.createInvoke(\"" + className + "\", \"" + methodName + "\", " + BCELifier.printType(type) + ", "
  278.             + BCELifier.printArgumentTypes(argTypes) + ", " + CONSTANT_PREFIX + Const.getOpcodeName(opcode).toUpperCase(Locale.ENGLISH) + "));");
  279.     }

  280.     @Override
  281.     public void visitLDC(final LDC i) {
  282.         createConstant(i.getValue(constantPoolGen));
  283.     }

  284.     @Override
  285.     public void visitLDC2_W(final LDC2_W i) {
  286.         createConstant(i.getValue(constantPoolGen));
  287.     }

  288.     @Override
  289.     public void visitLocalVariableInstruction(final LocalVariableInstruction i) {
  290.         final short opcode = i.getOpcode();
  291.         final Type type = i.getType(constantPoolGen);
  292.         if (opcode == Const.IINC) {
  293.             printWriter.println("il.append(new IINC(" + i.getIndex() + ", " + ((IINC) i).getIncrement() + "));");
  294.         } else {
  295.             final String kind = opcode < Const.ISTORE ? "Load" : "Store";
  296.             printWriter.println("il.append(_factory.create" + kind + "(" + BCELifier.printType(type) + ", " + i.getIndex() + "));");
  297.         }
  298.     }

  299.     @Override
  300.     public void visitRET(final RET i) {
  301.         printWriter.println("il.append(new RET(" + i.getIndex() + "));");
  302.     }

  303.     @Override
  304.     public void visitReturnInstruction(final ReturnInstruction i) {
  305.         final Type type = i.getType(constantPoolGen);
  306.         printWriter.println("il.append(_factory.createReturn(" + BCELifier.printType(type) + "));");
  307.     }
  308. }