View Javadoc
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  
19  import java.io.PrintWriter;
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.List;
23  import java.util.Locale;
24  import java.util.Map;
25  
26  import org.apache.bcel.Const;
27  import org.apache.bcel.classfile.Utility;
28  import org.apache.bcel.generic.AllocationInstruction;
29  import org.apache.bcel.generic.ArrayInstruction;
30  import org.apache.bcel.generic.ArrayType;
31  import org.apache.bcel.generic.BranchHandle;
32  import org.apache.bcel.generic.BranchInstruction;
33  import org.apache.bcel.generic.CHECKCAST;
34  import org.apache.bcel.generic.CPInstruction;
35  import org.apache.bcel.generic.CodeExceptionGen;
36  import org.apache.bcel.generic.ConstantPoolGen;
37  import org.apache.bcel.generic.ConstantPushInstruction;
38  import org.apache.bcel.generic.EmptyVisitor;
39  import org.apache.bcel.generic.FieldInstruction;
40  import org.apache.bcel.generic.IINC;
41  import org.apache.bcel.generic.INSTANCEOF;
42  import org.apache.bcel.generic.Instruction;
43  import org.apache.bcel.generic.InstructionConst;
44  import org.apache.bcel.generic.InstructionHandle;
45  import org.apache.bcel.generic.InvokeInstruction;
46  import org.apache.bcel.generic.LDC;
47  import org.apache.bcel.generic.LDC2_W;
48  import org.apache.bcel.generic.LocalVariableInstruction;
49  import org.apache.bcel.generic.MULTIANEWARRAY;
50  import org.apache.bcel.generic.MethodGen;
51  import org.apache.bcel.generic.NEWARRAY;
52  import org.apache.bcel.generic.ObjectType;
53  import org.apache.bcel.generic.RET;
54  import org.apache.bcel.generic.ReturnInstruction;
55  import org.apache.bcel.generic.Select;
56  import org.apache.bcel.generic.Type;
57  
58  /**
59   * Factory creates il.append() statements, and sets instruction targets. A helper class for BCELifier.
60   *
61   * @see BCELifier
62   */
63  final class BCELFactory extends EmptyVisitor {
64  
65      private static final String CONSTANT_PREFIX = Const.class.getSimpleName() + ".";
66      private final MethodGen methodGen;
67      private final PrintWriter printWriter;
68      private final ConstantPoolGen constantPoolGen;
69  
70      private final Map<Instruction, InstructionHandle> branchMap = new HashMap<>();
71  
72      // Memorize BranchInstructions that need an update
73      private final List<BranchInstruction> branches = new ArrayList<>();
74  
75      BCELFactory(final MethodGen mg, final PrintWriter out) {
76          methodGen = mg;
77          constantPoolGen = mg.getConstantPool();
78          printWriter = out;
79      }
80  
81      private void createConstant(final Object value) {
82          String embed = value.toString();
83          if (value instanceof String) {
84              embed = '"' + Utility.convertString(embed) + '"';
85          } else if (value instanceof Character) {
86              embed = "(char)0x" + Integer.toHexString(((Character) value).charValue());
87          } else if (value instanceof Float) {
88              final Float f = (Float) value;
89              if (Float.isNaN(f)) {
90                  embed = "Float.NaN";
91              } else if (f == Float.POSITIVE_INFINITY) {
92                  embed = "Float.POSITIVE_INFINITY";
93              } else if (f == Float.NEGATIVE_INFINITY) {
94                  embed = "Float.NEGATIVE_INFINITY";
95              } else {
96                  embed += "f";
97              }
98          }  else if (value instanceof Double) {
99              final Double d = (Double) value;
100             if (Double.isNaN(d)) {
101                 embed = "Double.NaN";
102             } else if (d == Double.POSITIVE_INFINITY) {
103                 embed = "Double.POSITIVE_INFINITY";
104             } else if (d == Double.NEGATIVE_INFINITY) {
105                 embed = "Double.NEGATIVE_INFINITY";
106             } else {
107                 embed += "d";
108             }
109         } else if (value instanceof Long) {
110             embed += "L";
111         } else if (value instanceof ObjectType) {
112             final ObjectType ot = (ObjectType) value;
113             embed = "new ObjectType(\"" + ot.getClassName() + "\")";
114         } else if (value instanceof ArrayType) {
115             final ArrayType at = (ArrayType) value;
116             embed = "new ArrayType(" + BCELifier.printType(at.getBasicType()) + ", " + at.getDimensions() + ")";
117         }
118 
119         printWriter.println("il.append(new PUSH(_cp, " + embed + "));");
120     }
121 
122     public void start() {
123         if (!methodGen.isAbstract() && !methodGen.isNative()) {
124             for (InstructionHandle ih = methodGen.getInstructionList().getStart(); ih != null; ih = ih.getNext()) {
125                 final Instruction i = ih.getInstruction();
126                 if (i instanceof BranchInstruction) {
127                     branchMap.put(i, ih); // memorize container
128                 }
129                 if (ih.hasTargeters()) {
130                     if (i instanceof BranchInstruction) {
131                         printWriter.println("    InstructionHandle ih_" + ih.getPosition() + ";");
132                     } else {
133                         printWriter.print("    InstructionHandle ih_" + ih.getPosition() + " = ");
134                     }
135                 } else {
136                     printWriter.print("    ");
137                 }
138                 if (!visitInstruction(i)) {
139                     i.accept(this);
140                 }
141             }
142             updateBranchTargets();
143             updateExceptionHandlers();
144         }
145     }
146 
147     private void updateBranchTargets() {
148         branches.forEach(bi -> {
149             final BranchHandle bh = (BranchHandle) branchMap.get(bi);
150             final int pos = bh.getPosition();
151             final String name = bi.getName() + "_" + pos;
152             int targetPos = bh.getTarget().getPosition();
153             printWriter.println("    " + name + ".setTarget(ih_" + targetPos + ");");
154             if (bi instanceof Select) {
155                 final InstructionHandle[] ihs = ((Select) bi).getTargets();
156                 for (int j = 0; j < ihs.length; j++) {
157                     targetPos = ihs[j].getPosition();
158                     printWriter.println("    " + name + ".setTarget(" + j + ", ih_" + targetPos + ");");
159                 }
160             }
161         });
162     }
163 
164     private void updateExceptionHandlers() {
165         final CodeExceptionGen[] handlers = methodGen.getExceptionHandlers();
166         for (final CodeExceptionGen h : handlers) {
167             final String type = h.getCatchType() == null ? "null" : BCELifier.printType(h.getCatchType());
168             printWriter.println("    method.addExceptionHandler(" + "ih_" + h.getStartPC().getPosition() + ", " + "ih_" + h.getEndPC().getPosition() + ", "
169                 + "ih_" + h.getHandlerPC().getPosition() + ", " + type + ");");
170         }
171     }
172 
173     @Override
174     public void visitAllocationInstruction(final AllocationInstruction i) {
175         Type type;
176         if (i instanceof CPInstruction) {
177             type = ((CPInstruction) i).getType(constantPoolGen);
178         } else {
179             type = ((NEWARRAY) i).getType();
180         }
181         final short opcode = ((Instruction) i).getOpcode();
182         int dim = 1;
183         switch (opcode) {
184         case Const.NEW:
185             printWriter.println("il.append(_factory.createNew(\"" + ((ObjectType) type).getClassName() + "\"));");
186             break;
187         case Const.MULTIANEWARRAY:
188             dim = ((MULTIANEWARRAY) i).getDimensions();
189             //$FALL-THROUGH$
190         case Const.NEWARRAY:
191             if (type instanceof ArrayType) {
192                 type = ((ArrayType) type).getBasicType();
193             }
194             //$FALL-THROUGH$
195         case Const.ANEWARRAY:
196             printWriter.println("il.append(_factory.createNewArray(" + BCELifier.printType(type) + ", (short) " + dim + "));");
197             break;
198         default:
199             throw new IllegalArgumentException("Unhandled opcode: " + opcode);
200         }
201     }
202 
203     @Override
204     public void visitArrayInstruction(final ArrayInstruction i) {
205         final short opcode = i.getOpcode();
206         final Type type = i.getType(constantPoolGen);
207         final String kind = opcode < Const.IASTORE ? "Load" : "Store";
208         printWriter.println("il.append(_factory.createArray" + kind + "(" + BCELifier.printType(type) + "));");
209     }
210 
211     @Override
212     public void visitBranchInstruction(final BranchInstruction bi) {
213         final BranchHandle bh = (BranchHandle) branchMap.get(bi);
214         final int pos = bh.getPosition();
215         final String name = bi.getName() + "_" + pos;
216         if (bi instanceof Select) {
217             final Select s = (Select) bi;
218             branches.add(bi);
219             final StringBuilder args = new StringBuilder("new int[] { ");
220             final int[] matchs = s.getMatchs();
221             for (int i = 0; i < matchs.length; i++) {
222                 args.append(matchs[i]);
223                 if (i < matchs.length - 1) {
224                     args.append(", ");
225                 }
226             }
227             args.append(" }");
228             printWriter.print("Select " + name + " = new " + bi.getName().toUpperCase(Locale.ENGLISH) + "(" + args + ", new InstructionHandle[] { ");
229             for (int i = 0; i < matchs.length; i++) {
230                 printWriter.print("null");
231                 if (i < matchs.length - 1) {
232                     printWriter.print(", ");
233                 }
234             }
235             printWriter.println(" }, null);");
236         } else {
237             final int tPos = bh.getTarget().getPosition();
238             String target;
239             if (pos > tPos) {
240                 target = "ih_" + tPos;
241             } else {
242                 branches.add(bi);
243                 target = "null";
244             }
245             printWriter.println("    BranchInstruction " + name + " = _factory.createBranchInstruction(" + CONSTANT_PREFIX
246                 + bi.getName().toUpperCase(Locale.ENGLISH) + ", " + target + ");");
247         }
248         if (bh.hasTargeters()) {
249             printWriter.println("    ih_" + pos + " = il.append(" + name + ");");
250         } else {
251             printWriter.println("    il.append(" + name + ");");
252         }
253     }
254 
255     @Override
256     public void visitCHECKCAST(final CHECKCAST i) {
257         final Type type = i.getType(constantPoolGen);
258         printWriter.println("il.append(_factory.createCheckCast(" + BCELifier.printType(type) + "));");
259     }
260 
261     @Override
262     public void visitConstantPushInstruction(final ConstantPushInstruction i) {
263         createConstant(i.getValue());
264     }
265 
266     @Override
267     public void visitFieldInstruction(final FieldInstruction i) {
268         final short opcode = i.getOpcode();
269         final String className = i.getClassName(constantPoolGen);
270         final String fieldName = i.getFieldName(constantPoolGen);
271         final Type type = i.getFieldType(constantPoolGen);
272         printWriter.println("il.append(_factory.createFieldAccess(\"" + className + "\", \"" + fieldName + "\", " + BCELifier.printType(type) + ", "
273             + CONSTANT_PREFIX + Const.getOpcodeName(opcode).toUpperCase(Locale.ENGLISH) + "));");
274     }
275 
276     @Override
277     public void visitINSTANCEOF(final INSTANCEOF i) {
278         final Type type = i.getType(constantPoolGen);
279         printWriter.println("il.append(_factory.createInstanceOf(" + BCELifier.printType(type) + "));");
280     }
281 
282     private boolean visitInstruction(final Instruction i) {
283         final short opcode = i.getOpcode();
284         if (InstructionConst.getInstruction(opcode) != null && !(i instanceof ConstantPushInstruction) && !(i instanceof ReturnInstruction)) { // Handled below
285             printWriter.println("il.append(InstructionConst." + i.getName().toUpperCase(Locale.ENGLISH) + ");");
286             return true;
287         }
288         return false;
289     }
290 
291     @Override
292     public void visitInvokeInstruction(final InvokeInstruction i) {
293         final short opcode = i.getOpcode();
294         final String className = i.getClassName(constantPoolGen);
295         final String methodName = i.getMethodName(constantPoolGen);
296         final Type type = i.getReturnType(constantPoolGen);
297         final Type[] argTypes = i.getArgumentTypes(constantPoolGen);
298         printWriter.println("il.append(_factory.createInvoke(\"" + className + "\", \"" + methodName + "\", " + BCELifier.printType(type) + ", "
299             + BCELifier.printArgumentTypes(argTypes) + ", " + CONSTANT_PREFIX + Const.getOpcodeName(opcode).toUpperCase(Locale.ENGLISH) + "));");
300     }
301 
302     @Override
303     public void visitLDC(final LDC i) {
304         createConstant(i.getValue(constantPoolGen));
305     }
306 
307     @Override
308     public void visitLDC2_W(final LDC2_W i) {
309         createConstant(i.getValue(constantPoolGen));
310     }
311 
312     @Override
313     public void visitLocalVariableInstruction(final LocalVariableInstruction i) {
314         final short opcode = i.getOpcode();
315         final Type type = i.getType(constantPoolGen);
316         if (opcode == Const.IINC) {
317             printWriter.println("il.append(new IINC(" + i.getIndex() + ", " + ((IINC) i).getIncrement() + "));");
318         } else {
319             final String kind = opcode < Const.ISTORE ? "Load" : "Store";
320             printWriter.println("il.append(_factory.create" + kind + "(" + BCELifier.printType(type) + ", " + i.getIndex() + "));");
321         }
322     }
323 
324     @Override
325     public void visitRET(final RET i) {
326         printWriter.println("il.append(new RET(" + i.getIndex() + "));");
327     }
328 
329     @Override
330     public void visitReturnInstruction(final ReturnInstruction i) {
331         final Type type = i.getType(constantPoolGen);
332         printWriter.println("il.append(_factory.createReturn(" + BCELifier.printType(type) + "));");
333     }
334 }