1 /*
  2  * Copyright (c) 2009, 2025, 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.lang.classfile.constantpool.NameAndTypeEntry;
 29 import java.util.HashMap;
 30 import java.util.List;
 31 import java.util.Map;
 32 import java.lang.classfile.Attributes;
 33 import java.lang.classfile.ClassFile;
 34 
 35 import java.lang.classfile.Instruction;
 36 import java.lang.classfile.attribute.CodeAttribute;
 37 import java.lang.classfile.attribute.StackMapFrameInfo;
 38 import java.lang.classfile.attribute.StackMapTableAttribute;
 39 
 40 /**
 41  * Annotate instructions with stack map.
 42  *
 43  *  <p><b>This is NOT part of any supported API.
 44  *  If you write code that depends on this, you do so at your own risk.
 45  *  This code and its internal interfaces are subject to change or
 46  *  deletion without notice.</b>
 47  */
 48 public class StackMapWriter extends InstructionDetailWriter {
 49     static StackMapWriter instance(Context context) {
 50         StackMapWriter instance = context.get(StackMapWriter.class);
 51         if (instance == null)
 52             instance = new StackMapWriter(context);
 53         return instance;
 54     }
 55 
 56     protected StackMapWriter(Context context) {
 57         super(context);
 58         context.put(StackMapWriter.class, this);
 59     }
 60 
 61     public void reset(CodeAttribute code) {
 62         setStackMap(code);
 63     }
 64 
 65     void setStackMap(CodeAttribute code) {
 66         StackMapTableAttribute attr = code.findAttribute(Attributes.stackMapTable())
 67                 .orElse(null);
 68         if (attr == null) {
 69             map = null;
 70             return;
 71         }
 72         var m = code.parent().get();
 73         if ((m.flags().flagsMask() & ClassFile.ACC_STATIC) == 0) {
 74             thisClassName =  m.parent().get().thisClass().asInternalName();
 75         } else {
 76             thisClassName = null;
 77         }
 78 
 79         map = new HashMap<>();
 80         this.code = code;
 81         for (var fr : attr.entries())
 82             map.put(code.labelToBci(fr.target()), fr);
 83     }
 84 
 85     public void writeInitialDetails() {
 86         writeDetails(-1);
 87     }
 88 
 89     @Override
 90     public void writeDetails(int pc, Instruction instr) {
 91         writeDetails(pc);
 92     }
 93 
 94     private void writeDetails(int pc) {
 95         if (map == null)
 96             return;
 97 
 98         var m = map.get(pc);
 99         if (m != null) {
100             print("StackMap locals: ", m.locals(), true);
101             print("StackMap stack: ", m.stack(), false);
102             if (!m.unsetFields().isEmpty()) {
103                 printFields("StackMap unset fields: ", m.unsetFields());
104             }
105         }
106 
107     }
108 
109     void printFields(String label, List<NameAndTypeEntry> entries) {
110         print(label);
111         boolean first = true;
112         for (var e : entries) {
113             if (!first) {
114                 print(", ");
115             } else {
116                 first = false;
117             }
118             print(e::name);
119             print(":");
120             print(e::type);
121         }
122         println();
123     }
124 
125     void print(String label, List<StackMapFrameInfo.VerificationTypeInfo> entries,
126             boolean firstThis) {
127         print(label);
128         for (var e : entries) {
129             print(" ");
130             print(e, firstThis);
131             firstThis = false;
132         }
133         println();
134     }
135 
136     void print(StackMapFrameInfo.VerificationTypeInfo entry, boolean firstThis) {
137         if (entry == null) {
138             print("ERROR");
139             return;
140         }
141 
142         switch (entry) {
143             case StackMapFrameInfo.SimpleVerificationTypeInfo s -> {
144                 switch (s) {
145                     case TOP ->
146                         print("top");
147 
148                     case INTEGER ->
149                         print("int");
150 
151                     case FLOAT ->
152                         print("float");
153 
154                     case LONG ->
155                         print("long");
156 
157                     case DOUBLE ->
158                         print("double");
159 
160                     case NULL ->
161                         print("null");
162 
163                     case UNINITIALIZED_THIS ->
164                         print("uninit_this");
165                 }
166             }
167 
168             case StackMapFrameInfo.ObjectVerificationTypeInfo o -> {
169                 String cln = o.className().asInternalName();
170                 print(firstThis && cln.equals(thisClassName) ? "this" : cln);
171             }
172 
173             case StackMapFrameInfo.UninitializedVerificationTypeInfo u ->
174                 print(code.labelToBci(u.newTarget()));
175         }
176 
177     }
178 
179     private Map<Integer, StackMapFrameInfo> map;
180     private String thisClassName;
181     private CodeAttribute code;
182 }