1 /*
  2  * Copyright (c) 1996, 2020, 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.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 package org.openjdk.asmtools.jdis;
 24 
 25 import org.openjdk.asmtools.jasm.Modifiers;
 26 
 27 import java.io.DataInputStream;
 28 import java.io.IOException;
 29 import java.util.ArrayList;
 30 
 31 import static org.openjdk.asmtools.jasm.JasmTokens.Token;
 32 import static org.openjdk.asmtools.jasm.Tables.AttrTag;
 33 import static org.openjdk.asmtools.jasm.Tables.CF_Context;
 34 
 35 /**
 36  * Method data for method members in a class of the Java Disassembler
 37  */
 38 public class MethodData extends MemberData {
 39 
 40     /**
 41      * CP index to the method name
 42      */
 43     protected int name_cpx;
 44 
 45     /**
 46      * CP index to the method type
 47      */
 48     protected int sig_cpx;
 49     protected String lP;        // labelPrefix
 50     /**
 51      * The parameter names for this method
 52      */
 53     protected ArrayList<ParamNameData> paramNames;
 54     /**
 55      * The visible parameter annotations for this method
 56      */
 57     protected ParameterAnnotationData visibleParameterAnnotations;
 58     /**
 59      * The invisible parameter annotations for this method
 60      */
 61     protected ParameterAnnotationData invisibleParameterAnnotations;
 62     /**
 63      * The invisible parameter annotations for this method
 64      */
 65     protected AnnotationElement.AnnotValue defaultAnnotation;
 66     /**
 67      * The code data for this method. May be null
 68      */
 69     private CodeData code;
 70     /**
 71      * The exception table (thrown exceptions) for this method. May be null
 72      */
 73     private int[] exc_table = null;
 74 
 75     public MethodData(ClassData cls) {
 76         super(cls);
 77         memberType = "MethodData";
 78         lP = (options.contains(Options.PR.LABS)) ? "L" : "";
 79         paramNames = null;
 80     }
 81 
 82     /*========================================================*/
 83     /* Read Methods */
 84     @Override
 85     protected boolean handleAttributes(DataInputStream in, AttrTag attrtag, int attrlen) throws IOException {
 86         // Read the Attributes
 87         boolean handled = true;
 88         switch (attrtag) {
 89             case ATT_Code:
 90                 code = new CodeData(this);
 91                 code.read(in, attrlen);
 92                 break;
 93             case ATT_Exceptions:
 94                 readExceptions(in);
 95                 break;
 96             case ATT_MethodParameters:
 97                 readMethodParameters(in);
 98                 break;
 99             case ATT_RuntimeVisibleParameterAnnotations:
100             case ATT_RuntimeInvisibleParameterAnnotations:
101                 boolean invisible = (attrtag == AttrTag.ATT_RuntimeInvisibleParameterAnnotations);
102                 ParameterAnnotationData pannots = new ParameterAnnotationData(cls, invisible);
103                 pannots.read(in);
104                 if (invisible) {
105                     invisibleParameterAnnotations = pannots;
106                 } else {
107                     visibleParameterAnnotations = pannots;
108                 }
109                 break;
110             case ATT_AnnotationDefault:
111                 defaultAnnotation = AnnotationElement.readValue(in, cls, false);
112                 break;
113             default:
114                 handled = false;
115                 break;
116         }
117         return handled;
118     }
119 
120     /**
121      * read
122      * read and resolve the method data called from ClassData.
123      * Precondition: NumFields has already been read from the stream.
124      */
125     public void read(DataInputStream in) throws IOException {
126         // read the Methods CP indexes
127         access = in.readUnsignedShort(); // & MM_METHOD; // Q
128         name_cpx = in.readUnsignedShort();
129         sig_cpx = in.readUnsignedShort();
130         TraceUtils.traceln(2,"MethodData: {modifiers}: " + Modifiers.toString(access, CF_Context.CTX_METHOD),
131                             "      MethodData: name[" + name_cpx + "]=" + cls.pool.getString(name_cpx) + " sig[" + sig_cpx + "]=" + cls.pool.getString(sig_cpx));
132         // Read the attributes
133         readAttributes(in);
134     }
135 
136     private void readExceptions(DataInputStream in) throws IOException {
137         // this is not really a CodeAttr attribute, it's part of the CodeAttr
138         int exc_table_len = in.readUnsignedShort();
139         TraceUtils.traceln(3,"ExceptionsAttr[" + exc_table_len + "]");
140         exc_table = new int[exc_table_len];
141         for (int l = 0; l < exc_table_len; l++) {
142             int exc = in.readShort();
143             TraceUtils.traceln(4,"throws:#" + exc);
144             exc_table[l] = exc;
145         }
146     }
147 
148     private void readMethodParameters(DataInputStream in) throws IOException {
149         // this is not really a CodeAttr attribute, it's part of the CodeAttr
150         int num_params = in.readUnsignedByte();
151         TraceUtils.traceln(3,"MethodParametersAttr[" + num_params + "]");
152         paramNames = new ArrayList<>(num_params);
153         for (int l = 0; l < num_params; l++) {
154             short pname_cpx = (short) in.readUnsignedShort();
155             int paccess = in.readUnsignedShort();
156             TraceUtils.traceln(4,"P[" + l + "] ={ name[" + pname_cpx + "]: " + cls.pool.getString(pname_cpx)
157                     + " modifiers [" + paccess + "]: " + Modifiers.toString(paccess, CF_Context.CTX_METHOD) + "}");
158             paramNames.add(l, new ParamNameData(pname_cpx, paccess));
159         }
160     }
161 
162     /**
163      * printPAnnotations
164      * <p>
165      * prints the parameter annotations for this method. called from CodeAttr (since JASM
166      * code integrates the PAnnotation Syntax inside the method body).
167      */
168     // This is called from the CodeAttr
169     public void printPAnnotations() throws IOException {
170         int visSize = 0;
171         int invisSize = 0;
172         int pNumSize = 0;
173 
174         if (visibleParameterAnnotations != null) {
175             visSize = visibleParameterAnnotations.numParams();
176         }
177         if (invisibleParameterAnnotations != null) {
178             invisSize = invisibleParameterAnnotations.numParams();
179         }
180         if (paramNames != null) {
181             pNumSize = paramNames.size();
182         }
183 
184         int maxParams;
185         maxParams = (pNumSize > invisSize) ? pNumSize : invisSize;
186         maxParams = (visSize > maxParams) ? visSize : maxParams;
187 
188         for (int paramNum = 0; paramNum < maxParams; paramNum++) {
189             ArrayList<AnnotationData> visAnnots = null;
190             if (visibleParameterAnnotations != null && paramNum < visSize) {
191                 visAnnots = visibleParameterAnnotations.get(paramNum);
192             }
193             ArrayList<AnnotationData> invisAnnots = null;
194             if (invisibleParameterAnnotations != null && paramNum < invisSize) {
195                 invisAnnots = invisibleParameterAnnotations.get(paramNum);
196             }
197             ParamNameData pname = (paramNames == null) ? null : paramNames.get(paramNum);
198 
199             boolean nullAnnots = ((visAnnots == null) && (invisAnnots == null));
200             if (pname != null && pname.name_cpx == 0) {
201                 pname = null;
202             }
203 
204             // Print the Param number (header)
205             if ((pname != null) || !nullAnnots) {
206                 out.print("\t" + paramNum + ": ");
207             } else {
208                 continue;
209             }
210 
211             boolean firstTime = true;
212 
213             // Print the Parameter name
214             if (pname != null) {
215                 out.print(Token.PARAM_NAME.parseKey());
216                 out.print(Token.LBRACE.parseKey());
217                 out.print(cls.pool.getString(pname.name_cpx));
218                 out.print(" ");
219                 out.print(Modifiers.toString(pname.access, CF_Context.CTX_METHOD));
220                 out.print(Token.RBRACE.parseKey());
221                 out.print(" ");
222             }
223 
224             // Print any visible param annotations
225             if (visAnnots != null) {
226                 for (AnnotationData annot : visAnnots) {
227                     if (!firstTime) {
228                         out.print("\t   ");
229                     }
230                     annot.print(out, getIndentString());
231 //                    out.println();
232                     firstTime = false;
233                 }
234             }
235 
236             // Print any invisible param annotations
237             if (invisAnnots != null) {
238                 for (AnnotationData annot : invisAnnots) {
239                     if (!firstTime) {
240                         out.print("\t   ");
241                     }
242                     annot.print(out, getIndentString());
243 //                    out.println();
244                     firstTime = false;
245                 }
246             }
247 
248             // Reset the line, if there were parameters
249             if ((pname != null) || !nullAnnots) {
250                 out.println();
251             }
252 
253         }
254 
255     }
256 
257     /**
258      * Prints the method data to the current output stream. called from ClassData.
259      */
260     @Override
261     public void print() throws IOException {
262 
263         printAnnotations(getIndentString());
264 
265         out.print(getIndentString() + Modifiers.accessString(access, CF_Context.CTX_METHOD));
266 
267         if (isSynthetic) {
268             out.print(Token.SYNTHETIC.parseKey() + " ");
269         }
270         if (isDeprecated) {
271             out.print(Token.DEPRECATED.parseKey() + " ");
272         }
273         out.print(Token.METHODREF.parseKey() + " ");
274 
275         if (pr_cpx) {
276             // print the CPX method descriptor
277             out.print("#" + name_cpx + ":#" + sig_cpx +
278                     ((code == null && exc_table == null && defaultAnnotation == null) ? ";" : "") +
279                     "\t // " + cls.pool.getName(name_cpx) + ":" + cls.pool.getName(sig_cpx));
280         } else {
281             out.print(cls.pool.getName(name_cpx) + ":" + cls.pool.getName(sig_cpx) +
282                     ((code == null && exc_table == null && defaultAnnotation == null) ? ";" : ""));
283         }
284         // followed by default annotation
285         if (defaultAnnotation != null) {
286             out.print(" default { ");
287             defaultAnnotation.print(out, getIndentString());
288             out.print(" }" + ((code == null && exc_table == null) ? ";" : " "));
289         }
290         // followed by exception table
291         printExceptionTable();
292 
293         if (code != null) {
294             code.print();
295         } else {
296             if( exc_table != null ) {
297                 out.print(';');
298             }
299             out.println();
300         }
301     }
302 
303     private void printExceptionTable() {
304         if (exc_table != null) {
305             out.print("\n\tthrows ");
306             int len = exc_table.length;
307             for (int exceptNum = 0; exceptNum < len; exceptNum++) {
308                 out.print(cls.pool.getClassName(exc_table[exceptNum]));
309                 if (exceptNum < len - 1) {
310                     out.print(", ");
311                 }
312             }
313         }
314     }
315 
316     /**
317      * MethodParamData
318      */
319     class ParamNameData {
320 
321         public int access;
322         public int name_cpx;
323 
324         public ParamNameData(int name, int access) {
325             this.access = access;
326             this.name_cpx = name;
327         }
328     }
329 } // end MethodData