1 /*
   2  * Copyright (c) 2017, 2020, Oracle and/or its affiliates. All rights reserved.
   3  */
   4 /*
   5  * Licensed to the Apache Software Foundation (ASF) under one or more
   6  * contributor license agreements.  See the NOTICE file distributed with
   7  * this work for additional information regarding copyright ownership.
   8  * The ASF licenses this file to You under the Apache License, Version 2.0
   9  * (the "License"); you may not use this file except in compliance with
  10  * the License.  You may obtain a copy of the License at
  11  *
  12  *      http://www.apache.org/licenses/LICENSE-2.0
  13  *
  14  * Unless required by applicable law or agreed to in writing, software
  15  * distributed under the License is distributed on an "AS IS" BASIS,
  16  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17  * See the License for the specific language governing permissions and
  18  * limitations under the License.
  19  */
  20 
  21 package com.sun.org.apache.bcel.internal.util;
  22 
  23 import java.io.PrintWriter;
  24 import java.util.ArrayList;
  25 import java.util.HashMap;
  26 import java.util.List;
  27 import java.util.Locale;
  28 import java.util.Map;
  29 
  30 import com.sun.org.apache.bcel.internal.Const;
  31 import com.sun.org.apache.bcel.internal.classfile.Utility;
  32 import com.sun.org.apache.bcel.internal.generic.AllocationInstruction;
  33 import com.sun.org.apache.bcel.internal.generic.ArrayInstruction;
  34 import com.sun.org.apache.bcel.internal.generic.ArrayType;
  35 import com.sun.org.apache.bcel.internal.generic.BranchHandle;
  36 import com.sun.org.apache.bcel.internal.generic.BranchInstruction;
  37 import com.sun.org.apache.bcel.internal.generic.CHECKCAST;
  38 import com.sun.org.apache.bcel.internal.generic.CPInstruction;
  39 import com.sun.org.apache.bcel.internal.generic.CodeExceptionGen;
  40 import com.sun.org.apache.bcel.internal.generic.ConstantPoolGen;
  41 import com.sun.org.apache.bcel.internal.generic.ConstantPushInstruction;
  42 import com.sun.org.apache.bcel.internal.generic.EmptyVisitor;
  43 import com.sun.org.apache.bcel.internal.generic.FieldInstruction;
  44 import com.sun.org.apache.bcel.internal.generic.IINC;
  45 import com.sun.org.apache.bcel.internal.generic.INSTANCEOF;
  46 import com.sun.org.apache.bcel.internal.generic.Instruction;
  47 import com.sun.org.apache.bcel.internal.generic.InstructionConst;
  48 import com.sun.org.apache.bcel.internal.generic.InstructionHandle;
  49 import com.sun.org.apache.bcel.internal.generic.InvokeInstruction;
  50 import com.sun.org.apache.bcel.internal.generic.LDC;
  51 import com.sun.org.apache.bcel.internal.generic.LDC2_W;
  52 import com.sun.org.apache.bcel.internal.generic.LocalVariableInstruction;
  53 import com.sun.org.apache.bcel.internal.generic.MULTIANEWARRAY;
  54 import com.sun.org.apache.bcel.internal.generic.MethodGen;
  55 import com.sun.org.apache.bcel.internal.generic.NEWARRAY;
  56 import com.sun.org.apache.bcel.internal.generic.ObjectType;
  57 import com.sun.org.apache.bcel.internal.generic.RET;
  58 import com.sun.org.apache.bcel.internal.generic.ReturnInstruction;
  59 import com.sun.org.apache.bcel.internal.generic.Select;
  60 import com.sun.org.apache.bcel.internal.generic.Type;
  61 
  62 /**
  63  * Factory creates il.append() statements, and sets instruction targets.
  64  * A helper class for BCELifier.
  65  *
  66  * @see BCELifier
  67  * @LastModified: Jan 2020
  68  */
  69 class BCELFactory extends EmptyVisitor {
  70 
  71     private static final String CONSTANT_PREFIX = Const.class.getSimpleName()+".";
  72     private final MethodGen _mg;
  73     private final PrintWriter _out;
  74     private final ConstantPoolGen _cp;
  75 
  76 
  77     BCELFactory(final MethodGen mg, final PrintWriter out) {
  78         _mg = mg;
  79         _cp = mg.getConstantPool();
  80         _out = out;
  81     }
  82 
  83     private final Map<Instruction, InstructionHandle> branch_map = new HashMap<>();
  84 
  85 
  86     public void start() {
  87         if (!_mg.isAbstract() && !_mg.isNative()) {
  88             for (InstructionHandle ih = _mg.getInstructionList().getStart(); ih != null; ih = ih
  89                     .getNext()) {
  90                 final Instruction i = ih.getInstruction();
  91                 if (i instanceof BranchInstruction) {
  92                     branch_map.put(i, ih); // memorize container
  93                 }
  94                 if (ih.hasTargeters()) {
  95                     if (i instanceof BranchInstruction) {
  96                         _out.println("    InstructionHandle ih_" + ih.getPosition() + ";");
  97                     } else {
  98                         _out.print("    InstructionHandle ih_" + ih.getPosition() + " = ");
  99                     }
 100                 } else {
 101                     _out.print("    ");
 102                 }
 103                 if (!visitInstruction(i)) {
 104                     i.accept(this);
 105                 }
 106             }
 107             updateBranchTargets();
 108             updateExceptionHandlers();
 109         }
 110     }
 111 
 112 
 113     private boolean visitInstruction( final Instruction i ) {
 114         final short opcode = i.getOpcode();
 115         if ((InstructionConst.getInstruction(opcode) != null)
 116                 && !(i instanceof ConstantPushInstruction) && !(i instanceof ReturnInstruction)) { // Handled below
 117             _out.println("il.append(InstructionConst."
 118                     + i.getName().toUpperCase(Locale.ENGLISH) + ");");
 119             return true;
 120         }
 121         return false;
 122     }
 123 
 124 
 125     @Override
 126     public void visitLocalVariableInstruction( final LocalVariableInstruction i ) {
 127         final short opcode = i.getOpcode();
 128         final Type type = i.getType(_cp);
 129         if (opcode == Const.IINC) {
 130             _out.println("il.append(new IINC(" + i.getIndex() + ", " + ((IINC) i).getIncrement()
 131                     + "));");
 132         } else {
 133             final String kind = (opcode < Const.ISTORE) ? "Load" : "Store";
 134             _out.println("il.append(_factory.create" + kind + "(" + BCELifier.printType(type)
 135                     + ", " + i.getIndex() + "));");
 136         }
 137     }
 138 
 139 
 140     @Override
 141     public void visitArrayInstruction( final ArrayInstruction i ) {
 142         final short opcode = i.getOpcode();
 143         final Type type = i.getType(_cp);
 144         final String kind = (opcode < Const.IASTORE) ? "Load" : "Store";
 145         _out.println("il.append(_factory.createArray" + kind + "(" + BCELifier.printType(type)
 146                 + "));");
 147     }
 148 
 149 
 150     @Override
 151     public void visitFieldInstruction( final FieldInstruction i ) {
 152         final short opcode = i.getOpcode();
 153         final String class_name = i.getReferenceType(_cp).getSignature();
 154         final String field_name = i.getFieldName(_cp);
 155         final Type type = i.getFieldType(_cp);
 156         _out.println("il.append(_factory.createFieldAccess(\"" + class_name + "\", \"" + field_name
 157                 + "\", " + BCELifier.printType(type) + ", " + CONSTANT_PREFIX
 158                 + Const.getOpcodeName(opcode).toUpperCase(Locale.ENGLISH) + "));");
 159     }
 160 
 161 
 162     @Override
 163     public void visitInvokeInstruction( final InvokeInstruction i ) {
 164         final short opcode = i.getOpcode();
 165         final String class_name = i.getReferenceType(_cp).getSignature();
 166         final String method_name = i.getMethodName(_cp);
 167         final Type type = i.getReturnType(_cp);
 168         final Type[] arg_types = i.getArgumentTypes(_cp);
 169         _out.println("il.append(_factory.createInvoke(\"" + class_name + "\", \"" + method_name
 170                 + "\", " + BCELifier.printType(type) + ", "
 171                 + BCELifier.printArgumentTypes(arg_types) + ", " + CONSTANT_PREFIX
 172                 + Const.getOpcodeName(opcode).toUpperCase(Locale.ENGLISH) + "));");
 173     }
 174 
 175 
 176     @Override
 177     @SuppressWarnings("fallthrough") // by design for case Const.ANEWARRAY
 178     public void visitAllocationInstruction( final AllocationInstruction i ) {
 179         Type type;
 180         if (i instanceof CPInstruction) {
 181             type = ((CPInstruction) i).getType(_cp);
 182         } else {
 183             type = ((NEWARRAY) i).getType();
 184         }
 185         final short opcode = ((Instruction) i).getOpcode();
 186         int dim = 1;
 187         switch (opcode) {
 188             case Const.NEW:
 189                 _out.println("il.append(_factory.createNew(\"" + ((ObjectType) type).getClassName()
 190                         + "\"));");
 191                 break;
 192             case Const.MULTIANEWARRAY:
 193                 dim = ((MULTIANEWARRAY) i).getDimensions();
 194                 //$FALL-THROUGH$
 195             case Const.ANEWARRAY:
 196             case Const.NEWARRAY:
 197                 if (type instanceof ArrayType) {
 198                     type = ((ArrayType) type).getBasicType();
 199                 }
 200                 _out.println("il.append(_factory.createNewArray(" + BCELifier.printType(type)
 201                         + ", (short) " + dim + "));");
 202                 break;
 203             default:
 204                 throw new RuntimeException("Oops: " + opcode);
 205         }
 206     }
 207 
 208 
 209     private void createConstant( final Object value ) {
 210         String embed = value.toString();
 211         if (value instanceof String) {
 212             embed = '"' + Utility.convertString(embed) + '"';
 213         } else if (value instanceof Character) {
 214             embed = "(char)0x" + Integer.toHexString(((Character) value).charValue());
 215         } else if (value instanceof Float) {
 216             embed += "f";
 217         } else if (value instanceof Long) {
 218             embed += "L";
 219         } else if (value instanceof ObjectType) {
 220             final ObjectType ot = (ObjectType) value;
 221             embed = "new ObjectType(\""+ot.getClassName()+"\")";
 222         }
 223 
 224         _out.println("il.append(new PUSH(_cp, " + embed + "));");
 225     }
 226 
 227 
 228     @Override
 229     public void visitLDC( final LDC i ) {
 230         createConstant(i.getValue(_cp));
 231     }
 232 
 233 
 234     @Override
 235     public void visitLDC2_W( final LDC2_W i ) {
 236         createConstant(i.getValue(_cp));
 237     }
 238 
 239 
 240     @Override
 241     public void visitConstantPushInstruction( final ConstantPushInstruction i ) {
 242         createConstant(i.getValue());
 243     }
 244 
 245 
 246     @Override
 247     public void visitINSTANCEOF( final INSTANCEOF i ) {
 248         final Type type = i.getType(_cp);
 249         _out.println("il.append(new INSTANCEOF(_cp.addClass(" + BCELifier.printType(type) + ")));");
 250     }
 251 
 252 
 253     @Override
 254     public void visitCHECKCAST( final CHECKCAST i ) {
 255         final Type type = i.getType(_cp);
 256         _out.println("il.append(_factory.createCheckCast(" + BCELifier.printType(type) + "));");
 257     }
 258 
 259 
 260     @Override
 261     public void visitReturnInstruction( final ReturnInstruction i ) {
 262         final Type type = i.getType(_cp);
 263         _out.println("il.append(_factory.createReturn(" + BCELifier.printType(type) + "));");
 264     }
 265 
 266     // Memorize BranchInstructions that need an update
 267     private final List<BranchInstruction> branches = new ArrayList<>();
 268 
 269 
 270     @Override
 271     public void visitBranchInstruction( final BranchInstruction bi ) {
 272         final BranchHandle bh = (BranchHandle) branch_map.get(bi);
 273         final int pos = bh.getPosition();
 274         final String name = bi.getName() + "_" + pos;
 275         if (bi instanceof Select) {
 276             final Select s = (Select) bi;
 277             branches.add(bi);
 278             final StringBuilder args = new StringBuilder("new int[] { ");
 279             final int[] matchs = s.getMatchs();
 280             for (int i = 0; i < matchs.length; i++) {
 281                 args.append(matchs[i]);
 282                 if (i < matchs.length - 1) {
 283                     args.append(", ");
 284                 }
 285             }
 286             args.append(" }");
 287             _out.print("Select " + name + " = new " + bi.getName().toUpperCase(Locale.ENGLISH)
 288                     + "(" + args + ", new InstructionHandle[] { ");
 289             for (int i = 0; i < matchs.length; i++) {
 290                 _out.print("null");
 291                 if (i < matchs.length - 1) {
 292                     _out.print(", ");
 293                 }
 294             }
 295             _out.println(" }, null);");
 296         } else {
 297             final int t_pos = bh.getTarget().getPosition();
 298             String target;
 299             if (pos > t_pos) {
 300                 target = "ih_" + t_pos;
 301             } else {
 302                 branches.add(bi);
 303                 target = "null";
 304             }
 305             _out.println("    BranchInstruction " + name + " = _factory.createBranchInstruction("
 306                     + CONSTANT_PREFIX + bi.getName().toUpperCase(Locale.ENGLISH) + ", " + target
 307                     + ");");
 308         }
 309         if (bh.hasTargeters()) {
 310             _out.println("    ih_" + pos + " = il.append(" + name + ");");
 311         } else {
 312             _out.println("    il.append(" + name + ");");
 313         }
 314     }
 315 
 316 
 317     @Override
 318     public void visitRET( final RET i ) {
 319         _out.println("il.append(new RET(" + i.getIndex() + ")));");
 320     }
 321 
 322 
 323     private void updateBranchTargets() {
 324         for (final BranchInstruction bi : branches) {
 325             final BranchHandle bh = (BranchHandle) branch_map.get(bi);
 326             final int pos = bh.getPosition();
 327             final String name = bi.getName() + "_" + pos;
 328             int t_pos = bh.getTarget().getPosition();
 329             _out.println("    " + name + ".setTarget(ih_" + t_pos + ");");
 330             if (bi instanceof Select) {
 331                 final InstructionHandle[] ihs = ((Select) bi).getTargets();
 332                 for (int j = 0; j < ihs.length; j++) {
 333                     t_pos = ihs[j].getPosition();
 334                     _out.println("    " + name + ".setTarget(" + j + ", ih_" + t_pos + ");");
 335                 }
 336             }
 337         }
 338     }
 339 
 340 
 341     private void updateExceptionHandlers() {
 342         final CodeExceptionGen[] handlers = _mg.getExceptionHandlers();
 343         for (final CodeExceptionGen h : handlers) {
 344             final String type = (h.getCatchType() == null) ? "null" : BCELifier.printType(h
 345                     .getCatchType());
 346             _out.println("    method.addExceptionHandler(" + "ih_" + h.getStartPC().getPosition()
 347                     + ", " + "ih_" + h.getEndPC().getPosition() + ", " + "ih_"
 348                     + h.getHandlerPC().getPosition() + ", " + type + ");");
 349         }
 350     }
 351 }