1 /*
  2  * Copyright (c) 1998, 2021, 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.jdi;
 27 
 28 import java.util.ArrayList;
 29 import java.util.List;
 30 
 31 import com.sun.jdi.AbsentInformationException;
 32 import com.sun.jdi.ArrayReference;
 33 import com.sun.jdi.ArrayType;
 34 import com.sun.jdi.ClassNotLoadedException;
 35 import com.sun.jdi.InterfaceType;
 36 import com.sun.jdi.InvalidTypeException;
 37 import com.sun.jdi.Location;
 38 import com.sun.jdi.Method;
 39 import com.sun.jdi.Type;
 40 import com.sun.jdi.Value;
 41 import com.sun.jdi.VirtualMachine;
 42 
 43 public abstract class MethodImpl extends TypeComponentImpl
 44                                  implements Method
 45 {
 46     private JNITypeParser signatureParser;
 47 
 48     abstract int argSlotCount() throws AbsentInformationException;
 49 
 50     abstract List<Location> allLineLocations(SDE.Stratum stratum,
 51                                              String sourceName)
 52                             throws AbsentInformationException;
 53 
 54     abstract List<Location> locationsOfLine(SDE.Stratum stratum,
 55                                             String sourceName,
 56                                             int lineNumber)
 57                             throws AbsentInformationException;
 58 
 59     MethodImpl(VirtualMachine vm, ReferenceTypeImpl declaringType,
 60                long ref, String name, String signature,
 61                String genericSignature, int modifiers) {
 62         super(vm, declaringType, ref, name, signature,
 63               genericSignature, modifiers);
 64         signatureParser = new JNITypeParser(signature);
 65     }
 66 
 67     static MethodImpl createMethodImpl(VirtualMachine vm,
 68                                        ReferenceTypeImpl declaringType,
 69                                        long ref,
 70                                        String name,
 71                                        String signature,
 72                                        String genericSignature,
 73                                        int modifiers) {
 74         if ((modifiers & (VMModifiers.NATIVE | VMModifiers.ABSTRACT)) != 0) {
 75             return new NonConcreteMethodImpl(vm, declaringType, ref,
 76                                              name, signature,
 77                                              genericSignature,
 78                                              modifiers);
 79         } else {
 80             return new ConcreteMethodImpl(vm, declaringType, ref,
 81                                           name, signature,
 82                                           genericSignature,
 83                                           modifiers);
 84         }
 85     }
 86 
 87     public boolean equals(Object obj) {
 88         if (obj instanceof MethodImpl other) {
 89             return (declaringType().equals(other.declaringType())) &&
 90                    (ref() == other.ref()) &&
 91                    super.equals(obj);
 92         } else {
 93             return false;
 94         }
 95     }
 96 
 97     @Override
 98     public int hashCode() {
 99         return Long.hashCode(ref());
100     }
101 
102     public final List<Location> allLineLocations()
103                                 throws AbsentInformationException {
104         return allLineLocations(vm.getDefaultStratum(), null);
105     }
106 
107     public List<Location> allLineLocations(String stratumID,
108                                            String sourceName)
109                           throws AbsentInformationException {
110         return allLineLocations(declaringType.stratum(stratumID), sourceName);
111     }
112 
113     public final List<Location> locationsOfLine(int lineNumber)
114                                 throws AbsentInformationException {
115         return locationsOfLine(vm.getDefaultStratum(),
116                                null, lineNumber);
117     }
118 
119     public List<Location> locationsOfLine(String stratumID,
120                                           String sourceName,
121                                           int lineNumber)
122                           throws AbsentInformationException {
123         return locationsOfLine(declaringType.stratum(stratumID),
124                                sourceName, lineNumber);
125     }
126 
127     LineInfo codeIndexToLineInfo(SDE.Stratum stratum,
128                                  long codeIndex) {
129         if (stratum.isJava()) {
130             return new BaseLineInfo(-1, declaringType);
131         } else {
132             return new StratumLineInfo(stratum.id(), -1, null, null);
133         }
134     }
135 
136     /**
137      * @return a text representation of the declared return type
138      * of this method.
139      */
140     public String returnTypeName() {
141         return signatureParser.typeName();
142     }
143 
144     private String returnSignature() {
145         return signatureParser.signature();
146     }
147 
148     public Type returnType() throws ClassNotLoadedException {
149         return findType(returnSignature());
150     }
151 
152     public Type findType(String signature) throws ClassNotLoadedException {
153         ReferenceTypeImpl enclosing = (ReferenceTypeImpl)declaringType();
154         return enclosing.findType(signature);
155     }
156 
157     public List<String> argumentTypeNames() {
158         return signatureParser.argumentTypeNames();
159     }
160 
161     public List<String> argumentSignatures() {
162         return signatureParser.argumentSignatures();
163     }
164 
165     Type argumentType(int index) throws ClassNotLoadedException {
166         ReferenceTypeImpl enclosing = (ReferenceTypeImpl)declaringType();
167         String signature = argumentSignatures().get(index);
168         return enclosing.findType(signature);
169     }
170 
171     public List<Type> argumentTypes() throws ClassNotLoadedException {
172         int size = argumentSignatures().size();
173         List<Type> types = new ArrayList<>(size);
174         for (int i = 0; i < size; i++) {
175             Type type = argumentType(i);
176             types.add(type);
177         }
178 
179         return types;
180     }
181 
182     public int compareTo(Method method) {
183         ReferenceTypeImpl declaringType = (ReferenceTypeImpl)declaringType();
184         int rc = declaringType.compareTo(method.declaringType());
185         if (rc == 0) {
186             rc = declaringType.indexOf(this) - declaringType.indexOf(method);
187         }
188         return rc;
189     }
190 
191     public boolean isAbstract() {
192         return isModifierSet(VMModifiers.ABSTRACT);
193     }
194 
195     public boolean isDefault() {
196         return !isModifierSet(VMModifiers.ABSTRACT) &&
197                !isModifierSet(VMModifiers.STATIC) &&
198                !isModifierSet(VMModifiers.PRIVATE) &&
199                declaringType() instanceof InterfaceType;
200     }
201 
202     public boolean isSynchronized() {
203         return isModifierSet(VMModifiers.SYNCHRONIZED);
204     }
205 
206     public boolean isNative() {
207         return isModifierSet(VMModifiers.NATIVE);
208     }
209 
210     public boolean isVarArgs() {
211         return isModifierSet(VMModifiers.VARARGS);
212     }
213 
214     public boolean isBridge() {
215         return isModifierSet(VMModifiers.BRIDGE);
216     }
217 
218     public boolean isConstructor() {
219         return name().equals("<init>") || name().equals("<vnew>");
220     }
221 
222     public boolean isStaticInitializer() {
223         return name().equals("<clinit>");
224     }
225 
226     public boolean isObsolete() {
227         try {
228             return JDWP.Method.IsObsolete.process(vm,
229                                     declaringType, ref).isObsolete;
230         } catch (JDWPException exc) {
231             throw exc.toJDIException();
232         }
233     }
234 
235     /*
236      * A container class for the return value to allow
237      * proper type-checking.
238      */
239     class ReturnContainer implements ValueContainer {
240         ReturnContainer() {
241         }
242         public Type type() throws ClassNotLoadedException {
243             return returnType();
244         }
245         public String typeName(){
246             return returnTypeName();
247         }
248         public String signature() {
249             return returnSignature(); //type().signature();
250         }
251         public Type findType(String signature) throws ClassNotLoadedException {
252             return MethodImpl.this.findType(signature);
253         }
254     }
255     ReturnContainer retValContainer = null;
256     ReturnContainer getReturnValueContainer() {
257         if (retValContainer == null) {
258             retValContainer = new ReturnContainer();
259         }
260         return retValContainer;
261     }
262 
263     /*
264      * A container class for the argument to allow
265      * proper type-checking.
266      */
267     class ArgumentContainer implements ValueContainer {
268         int index;
269 
270         ArgumentContainer(int index) {
271             this.index = index;
272         }
273         public Type type() throws ClassNotLoadedException {
274             return argumentType(index);
275         }
276         public String typeName(){
277             return argumentTypeNames().get(index);
278         }
279         public String signature() {
280             return argumentSignatures().get(index);
281         }
282         public Type findType(String signature) throws ClassNotLoadedException {
283             return MethodImpl.this.findType(signature);
284         }
285     }
286 
287     /*
288      * This is a var args method.  Thus, its last param is an
289      * array. If the method has n params, then:
290      * 1.  If there are n args and the last is the same type as the type of
291      *     the last param, do nothing.  IE, a String[]
292      *     can be passed to a String...
293      * 2.  If there are >= n arguments and for each arg whose number is >= n,
294      *     the arg type is 'compatible' with the component type of
295      *     the last param, then do
296      *     - create an array of the type of the last param
297      *     - put the n, ... args into this array.
298      *       We might have to do conversions here.
299      *     - put this array into arguments(n)
300      *     - delete arguments(n+1), ...
301      * NOTE that this might modify the input list.
302      */
303     void handleVarArgs(List<Value> arguments)
304         throws ClassNotLoadedException, InvalidTypeException {
305         List<Type> paramTypes = this.argumentTypes();
306         ArrayType lastParamType = (ArrayType)paramTypes.get(paramTypes.size() - 1);
307         int argCount = arguments.size();
308         int paramCount = paramTypes.size();
309         if (argCount < paramCount - 1) {
310             // Error; will be caught later.
311             return;
312         }
313         if (argCount == paramCount - 1) {
314             // It is ok to pass 0 args to the var arg.
315             // We have to gen a 0 length array.
316             ArrayReference argArray = lastParamType.newInstance(0);
317             arguments.add(argArray);
318             return;
319         }
320         Value nthArgValue = arguments.get(paramCount - 1);
321         if (nthArgValue == null && argCount == paramCount) {
322             // We have one varargs parameter and it is null
323             // so we don't have to do anything.
324             return;
325         }
326         // If the first varargs parameter is null, then don't
327         // access its type since it can't be an array.
328         Type nthArgType = (nthArgValue == null) ? null : nthArgValue.type();
329         if (nthArgType instanceof ArrayTypeImpl) {
330             if (argCount == paramCount &&
331                 ((ArrayTypeImpl)nthArgType).isAssignableTo(lastParamType)) {
332                 /*
333                  * This is case 1.  A compatible array is being passed to the
334                  * var args array param.  We don't have to do anything.
335                  */
336                 return;
337             }
338         }
339 
340         /*
341          * Case 2.  We have to verify that the n, n+1, ... args are compatible
342          * with componentType, and do conversions if necessary and create
343          * an array of componentType to hold these possibly converted values.
344          */
345         int count = argCount - paramCount + 1;
346         ArrayReference argArray = lastParamType.newInstance(count);
347 
348         /*
349          * This will copy arguments(paramCount - 1) ... to argArray(0) ...
350          * doing whatever conversions are needed!  It will throw an
351          * exception if an incompatible arg is encountered
352          */
353         argArray.setValues(0, arguments, paramCount - 1, count);
354         arguments.set(paramCount - 1, argArray);
355 
356         /*
357          * Remove the excess args
358          */
359         for (int ii = paramCount; ii < argCount; ii++) {
360             arguments.remove(paramCount);
361         }
362         return;
363     }
364 
365     /*
366      * The output list will be different than the input list.
367      */
368     List<Value> validateAndPrepareArgumentsForInvoke(List<? extends Value> origArguments)
369                          throws ClassNotLoadedException, InvalidTypeException {
370 
371         List<Value> arguments = new ArrayList<>(origArguments);
372         if (isVarArgs()) {
373             handleVarArgs(arguments);
374         }
375 
376         int argSize = arguments.size();
377 
378         JNITypeParser parser = new JNITypeParser(signature());
379         List<String> signatures = parser.argumentSignatures();
380 
381         if (signatures.size() != argSize) {
382             throw new IllegalArgumentException("Invalid argument count: expected " +
383                                                signatures.size() + ", received " +
384                                                arguments.size());
385         }
386 
387         for (int i = 0; i < argSize; i++) {
388             Value value = arguments.get(i);
389             value = ValueImpl.prepareForAssignment(value,
390                                                    new ArgumentContainer(i));
391             arguments.set(i, value);
392         }
393         return arguments;
394     }
395 
396     public String toString() {
397         StringBuilder sb = new StringBuilder();
398         sb.append(declaringType().name());
399         sb.append(".");
400         sb.append(name());
401         sb.append("(");
402         boolean first = true;
403         for (String name : argumentTypeNames()) {
404             if (!first) {
405                 sb.append(", ");
406             }
407             sb.append(name);
408             first = false;
409         }
410         sb.append(")");
411         return sb.toString();
412     }
413 }