1 /*
   2  * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.javap;
  27 
  28 import java.util.Arrays;
  29 import java.util.HashMap;
  30 import java.util.Map;
  31 
  32 import com.sun.tools.classfile.AccessFlags;
  33 import com.sun.tools.classfile.Attribute;
  34 import com.sun.tools.classfile.Code_attribute;
  35 import com.sun.tools.classfile.ConstantPool;
  36 import com.sun.tools.classfile.ConstantPoolException;
  37 import com.sun.tools.classfile.Descriptor;
  38 import com.sun.tools.classfile.Descriptor.InvalidDescriptor;
  39 import com.sun.tools.classfile.Instruction;
  40 import com.sun.tools.classfile.Method;
  41 import com.sun.tools.classfile.StackMapTable_attribute;
  42 import com.sun.tools.classfile.StackMapTable_attribute.*;
  43 
  44 import static com.sun.tools.classfile.StackMapTable_attribute.verification_type_info.*;
  45 
  46 /**
  47  * Annotate instructions with stack map.
  48  *
  49  *  <p><b>This is NOT part of any supported API.
  50  *  If you write code that depends on this, you do so at your own risk.
  51  *  This code and its internal interfaces are subject to change or
  52  *  deletion without notice.</b>
  53  */
  54 public class StackMapWriter extends InstructionDetailWriter {
  55     static StackMapWriter instance(Context context) {
  56         StackMapWriter instance = context.get(StackMapWriter.class);
  57         if (instance == null)
  58             instance = new StackMapWriter(context);
  59         return instance;
  60     }
  61 
  62     protected StackMapWriter(Context context) {
  63         super(context);
  64         context.put(StackMapWriter.class, this);
  65         classWriter = ClassWriter.instance(context);
  66     }
  67 
  68     public void reset(Code_attribute attr) {
  69         setStackMap((StackMapTable_attribute) attr.attributes.get(Attribute.StackMapTable));
  70     }
  71 
  72     void setStackMap(StackMapTable_attribute attr) {
  73         if (attr == null) {
  74             map = null;
  75             return;
  76         }
  77 
  78         Method m = classWriter.getMethod();
  79         Descriptor d = m.descriptor;
  80         String[] args;
  81         try {
  82             ConstantPool cp = classWriter.getClassFile().constant_pool;
  83             String argString = d.getParameterTypes(cp);
  84             args = argString.substring(1, argString.length() - 1).split("[, ]+");
  85         } catch (ConstantPoolException | InvalidDescriptor e) {
  86             return;
  87         }
  88         boolean isStatic = m.access_flags.is(AccessFlags.ACC_STATIC);
  89 
  90         verification_type_info[] initialLocals = new verification_type_info[(isStatic ? 0 : 1) + args.length];
  91         if (!isStatic)
  92             initialLocals[0] = new CustomVerificationTypeInfo("this");
  93         for (int i = 0; i < args.length; i++) {
  94             initialLocals[(isStatic ? 0 : 1) + i] =
  95                     new CustomVerificationTypeInfo(args[i].replace(".", "/"));
  96         }
  97 
  98         map = new HashMap<>();
  99         StackMapBuilder builder = new StackMapBuilder();
 100 
 101         // using -1 as the pc for the initial frame effectively compensates for
 102         // the difference in behavior for the first stack map frame (where the
 103         // pc offset is just offset_delta) compared to subsequent frames (where
 104         // the pc offset is always offset_delta+1).
 105         int pc = -1;
 106 
 107         map.put(pc, new StackMap(initialLocals, empty));
 108 
 109         for (int i = 0; i < attr.entries.length; i++)
 110             pc = attr.entries[i].accept(builder, pc);
 111     }
 112 
 113     public void writeInitialDetails() {
 114         writeDetails(-1);
 115     }
 116 
 117     public void writeDetails(Instruction instr) {
 118         writeDetails(instr.getPC());
 119     }
 120 
 121     private void writeDetails(int pc) {
 122         if (map == null)
 123             return;
 124 
 125         StackMap m = map.get(pc);
 126         if (m != null) {
 127             print("StackMap locals: ", m.locals);
 128             print("StackMap stack: ", m.stack);
 129         }
 130 
 131     }
 132 
 133     void print(String label, verification_type_info[] entries) {
 134         print(label);
 135         for (int i = 0; i < entries.length; i++) {
 136             print(" ");
 137             print(entries[i]);
 138         }
 139         println();
 140     }
 141 
 142     void print(verification_type_info entry) {
 143         if (entry == null) {
 144             print("ERROR");
 145             return;
 146         }
 147 
 148         switch (entry.tag) {
 149             case -1:
 150                 print(((CustomVerificationTypeInfo) entry).text);
 151                 break;
 152 
 153             case ITEM_Top:
 154                 print("top");
 155                 break;
 156 
 157             case ITEM_Integer:
 158                 print("int");
 159                 break;
 160 
 161             case ITEM_Float:
 162                 print("float");
 163                 break;
 164 
 165             case ITEM_Long:
 166                 print("long");
 167                 break;
 168 
 169             case ITEM_Double:
 170                 print("double");
 171                 break;
 172 
 173             case ITEM_Null:
 174                 print("null");
 175                 break;
 176 
 177             case ITEM_UninitializedThis:
 178                 print("uninit_this");
 179                 break;
 180 
 181             case ITEM_Object:
 182                 try {
 183                     ConstantPool cp = classWriter.getClassFile().constant_pool;
 184                     ConstantPool.CONSTANT_Class_info class_info = cp.getClassInfo(((Object_variable_info) entry).cpool_index);
 185                     print(cp.getUTF8Value(class_info.name_index));
 186                 } catch (ConstantPoolException e) {
 187                     print("??");
 188                 }
 189                 break;
 190 
 191             case ITEM_Uninitialized:
 192                 print(((Uninitialized_variable_info) entry).offset);
 193                 break;
 194         }
 195 
 196     }
 197 
 198     private Map<Integer, StackMap> map;
 199     private ClassWriter classWriter;
 200 
 201     class StackMapBuilder
 202             implements StackMapTable_attribute.stack_map_frame.Visitor<Integer, Integer> {
 203 
 204         public Integer visit_same_frame(same_frame frame, Integer pc) {
 205             int new_pc = pc + frame.getOffsetDelta() + 1;
 206             StackMap m = map.get(pc);
 207             assert (m != null);
 208             map.put(new_pc, m);
 209             return new_pc;
 210         }
 211 
 212         public Integer visit_same_locals_1_stack_item_frame(same_locals_1_stack_item_frame frame, Integer pc) {
 213             int new_pc = pc + frame.getOffsetDelta() + 1;
 214             StackMap prev = map.get(pc);
 215             assert (prev != null);
 216             StackMap m = new StackMap(prev.locals, frame.stack);
 217             map.put(new_pc, m);
 218             return new_pc;
 219         }
 220 
 221         public Integer visit_same_locals_1_stack_item_frame_extended(same_locals_1_stack_item_frame_extended frame, Integer pc) {
 222             int new_pc = pc + frame.getOffsetDelta() + 1;
 223             StackMap prev = map.get(pc);
 224             assert (prev != null);
 225             StackMap m = new StackMap(prev.locals, frame.stack);
 226             map.put(new_pc, m);
 227             return new_pc;
 228         }
 229 
 230         public Integer visit_chop_frame(chop_frame frame, Integer pc) {
 231             int new_pc = pc + frame.getOffsetDelta() + 1;
 232             StackMap prev = map.get(pc);
 233             assert (prev != null);
 234             int k = 251 - frame.frame_type;
 235             verification_type_info[] new_locals = Arrays.copyOf(prev.locals, prev.locals.length - k);
 236             StackMap m = new StackMap(new_locals, empty);
 237             map.put(new_pc, m);
 238             return new_pc;
 239         }
 240 
 241         public Integer visit_same_frame_extended(same_frame_extended frame, Integer pc) {
 242             int new_pc = pc + frame.getOffsetDelta();
 243             StackMap m = map.get(pc);
 244             assert (m != null);
 245             map.put(new_pc, m);
 246             return new_pc;
 247         }
 248 
 249         public Integer visit_append_frame(append_frame frame, Integer pc) {
 250             int new_pc = pc + frame.getOffsetDelta() + 1;
 251             StackMap prev = map.get(pc);
 252             assert (prev != null);
 253             verification_type_info[] new_locals = new verification_type_info[prev.locals.length + frame.locals.length];
 254             System.arraycopy(prev.locals, 0, new_locals, 0, prev.locals.length);
 255             System.arraycopy(frame.locals, 0, new_locals, prev.locals.length, frame.locals.length);
 256             StackMap m = new StackMap(new_locals, empty);
 257             map.put(new_pc, m);
 258             return new_pc;
 259         }
 260 
 261         public Integer visit_full_frame(full_frame frame, Integer pc) {
 262             int new_pc = pc + frame.getOffsetDelta() + 1;
 263             StackMap m = new StackMap(frame.locals, frame.stack);
 264             map.put(new_pc, m);
 265             return new_pc;
 266         }
 267 
 268     }
 269 
 270     static class StackMap {
 271         StackMap(verification_type_info[] locals, verification_type_info[] stack) {
 272             this.locals = locals;
 273             this.stack = stack;
 274         }
 275 
 276         private final verification_type_info[] locals;
 277         private final verification_type_info[] stack;
 278     }
 279 
 280     static class CustomVerificationTypeInfo extends verification_type_info {
 281         public CustomVerificationTypeInfo(String text) {
 282             super(-1);
 283             this.text = text;
 284         }
 285         private String text;
 286     }
 287 
 288     private final verification_type_info[] empty = { };
 289 }