1/*
2 * Copyright (c) 1998, 2017, 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
26package com.sun.tools.jdi;
27
28import java.util.ArrayList;
29import java.util.List;
30
31import com.sun.jdi.AbsentInformationException;
32import com.sun.jdi.ArrayReference;
33import com.sun.jdi.ArrayType;
34import com.sun.jdi.ClassNotLoadedException;
35import com.sun.jdi.InterfaceType;
36import com.sun.jdi.InvalidTypeException;
37import com.sun.jdi.Location;
38import com.sun.jdi.Method;
39import com.sun.jdi.Type;
40import com.sun.jdi.Value;
41import com.sun.jdi.VirtualMachine;
42
43public 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 != null) && (obj instanceof MethodImpl)) {
89            MethodImpl other = (MethodImpl)obj;
90            return (declaringType().equals(other.declaringType())) &&
91                   (ref() == other.ref()) &&
92                   super.equals(obj);
93        } else {
94            return false;
95        }
96    }
97
98    public int hashCode() {
99        return (int)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>");
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}
414