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