src/jdk.jdi/share/classes/com/sun/tools/example/debug/tty/BreakpointSpec.java
changeset 47216 71c04702a3d5
parent 25859 3317bb8137f4
child 53918 616a32d6b463
equal deleted inserted replaced
47215:4ebc2e2fb97c 47216:71c04702a3d5
       
     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 /*
       
    27  * This source code is provided to illustrate the usage of a given feature
       
    28  * or technique and has been deliberately simplified. Additional steps
       
    29  * required for a production-quality application, such as security checks,
       
    30  * input validation and proper error handling, might not be present in
       
    31  * this sample code.
       
    32  */
       
    33 
       
    34 
       
    35 package com.sun.tools.example.debug.tty;
       
    36 
       
    37 import com.sun.jdi.*;
       
    38 import com.sun.jdi.request.*;
       
    39 
       
    40 import java.util.ArrayList;
       
    41 import java.util.List;
       
    42 
       
    43 class BreakpointSpec extends EventRequestSpec {
       
    44     String methodId;
       
    45     List<String> methodArgs;
       
    46     int lineNumber;
       
    47 
       
    48     BreakpointSpec(ReferenceTypeSpec refSpec, int lineNumber) {
       
    49         super(refSpec);
       
    50         this.methodId = null;
       
    51         this.methodArgs = null;
       
    52         this.lineNumber = lineNumber;
       
    53     }
       
    54 
       
    55     BreakpointSpec(ReferenceTypeSpec refSpec, String methodId,
       
    56                    List<String> methodArgs) throws MalformedMemberNameException {
       
    57         super(refSpec);
       
    58         this.methodId = methodId;
       
    59         this.methodArgs = methodArgs;
       
    60         this.lineNumber = 0;
       
    61         if (!isValidMethodName(methodId)) {
       
    62             throw new MalformedMemberNameException(methodId);
       
    63         }
       
    64     }
       
    65 
       
    66     /**
       
    67      * The 'refType' is known to match, return the EventRequest.
       
    68      */
       
    69     @Override
       
    70     EventRequest resolveEventRequest(ReferenceType refType)
       
    71                            throws AmbiguousMethodException,
       
    72                                   AbsentInformationException,
       
    73                                   InvalidTypeException,
       
    74                                   NoSuchMethodException,
       
    75                                   LineNotFoundException {
       
    76         Location location = location(refType);
       
    77         if (location == null) {
       
    78             throw new InvalidTypeException();
       
    79         }
       
    80         EventRequestManager em = refType.virtualMachine().eventRequestManager();
       
    81         EventRequest bp = em.createBreakpointRequest(location);
       
    82         bp.setSuspendPolicy(suspendPolicy);
       
    83         bp.enable();
       
    84         return bp;
       
    85     }
       
    86 
       
    87     String methodName() {
       
    88         return methodId;
       
    89     }
       
    90 
       
    91     int lineNumber() {
       
    92         return lineNumber;
       
    93     }
       
    94 
       
    95     List<String> methodArgs() {
       
    96         return methodArgs;
       
    97     }
       
    98 
       
    99     boolean isMethodBreakpoint() {
       
   100         return (methodId != null);
       
   101     }
       
   102 
       
   103     @Override
       
   104     public int hashCode() {
       
   105         return refSpec.hashCode() + lineNumber +
       
   106             ((methodId != null) ? methodId.hashCode() : 0) +
       
   107             ((methodArgs != null) ? methodArgs.hashCode() : 0);
       
   108     }
       
   109 
       
   110     @Override
       
   111     public boolean equals(Object obj) {
       
   112         if (obj instanceof BreakpointSpec) {
       
   113             BreakpointSpec breakpoint = (BreakpointSpec)obj;
       
   114 
       
   115             return ((methodId != null) ?
       
   116                         methodId.equals(breakpoint.methodId)
       
   117                       : methodId == breakpoint.methodId) &&
       
   118                    ((methodArgs != null) ?
       
   119                         methodArgs.equals(breakpoint.methodArgs)
       
   120                       : methodArgs == breakpoint.methodArgs) &&
       
   121                    refSpec.equals(breakpoint.refSpec) &&
       
   122                    (lineNumber == breakpoint.lineNumber);
       
   123         } else {
       
   124             return false;
       
   125         }
       
   126     }
       
   127 
       
   128     @Override
       
   129     String errorMessageFor(Exception e) {
       
   130         if (e instanceof AmbiguousMethodException) {
       
   131             return (MessageOutput.format("Method is overloaded; specify arguments",
       
   132                                          methodName()));
       
   133             /*
       
   134              * TO DO: list the methods here
       
   135              */
       
   136         } else if (e instanceof NoSuchMethodException) {
       
   137             return (MessageOutput.format("No method in",
       
   138                                          new Object [] {methodName(),
       
   139                                                         refSpec.toString()}));
       
   140         } else if (e instanceof AbsentInformationException) {
       
   141             return (MessageOutput.format("No linenumber information for",
       
   142                                          refSpec.toString()));
       
   143         } else if (e instanceof LineNotFoundException) {
       
   144             return (MessageOutput.format("No code at line",
       
   145                                          new Object [] {Long.valueOf(lineNumber()),
       
   146                                                  refSpec.toString()}));
       
   147         } else if (e instanceof InvalidTypeException) {
       
   148             return (MessageOutput.format("Breakpoints can be located only in classes.",
       
   149                                          refSpec.toString()));
       
   150         } else {
       
   151             return super.errorMessageFor( e);
       
   152         }
       
   153     }
       
   154 
       
   155     @Override
       
   156     public String toString() {
       
   157         StringBuilder sb = new StringBuilder(refSpec.toString());
       
   158         if (isMethodBreakpoint()) {
       
   159             sb.append('.');
       
   160             sb.append(methodId);
       
   161             if (methodArgs != null) {
       
   162                 boolean first = true;
       
   163                 sb.append('(');
       
   164                 for (String arg : methodArgs) {
       
   165                     if (!first) {
       
   166                         sb.append(',');
       
   167                     }
       
   168                     sb.append(arg);
       
   169                     first = false;
       
   170                 }
       
   171                 sb.append(")");
       
   172             }
       
   173         } else {
       
   174             sb.append(':');
       
   175             sb.append(lineNumber);
       
   176         }
       
   177         return MessageOutput.format("breakpoint", sb.toString());
       
   178     }
       
   179 
       
   180     private Location location(ReferenceType refType) throws
       
   181                                     AmbiguousMethodException,
       
   182                                     AbsentInformationException,
       
   183                                     NoSuchMethodException,
       
   184                                     LineNotFoundException {
       
   185         Location location = null;
       
   186         if (isMethodBreakpoint()) {
       
   187             Method method = findMatchingMethod(refType);
       
   188             location = method.location();
       
   189         } else {
       
   190             // let AbsentInformationException be thrown
       
   191             List<Location> locs = refType.locationsOfLine(lineNumber());
       
   192             if (locs.size() == 0) {
       
   193                 throw new LineNotFoundException();
       
   194             }
       
   195             // TO DO: handle multiple locations
       
   196             location = locs.get(0);
       
   197             if (location.method() == null) {
       
   198                 throw new LineNotFoundException();
       
   199             }
       
   200         }
       
   201         return location;
       
   202     }
       
   203 
       
   204     private boolean isValidMethodName(String s) {
       
   205         return isJavaIdentifier(s) ||
       
   206                s.equals("<init>") ||
       
   207                s.equals("<clinit>");
       
   208     }
       
   209 
       
   210     /*
       
   211      * Compare a method's argument types with a Vector of type names.
       
   212      * Return true if each argument type has a name identical to the
       
   213      * corresponding string in the vector (allowing for varars)
       
   214      * and if the number of arguments in the method matches the
       
   215      * number of names passed
       
   216      */
       
   217     private boolean compareArgTypes(Method method, List<String> nameList) {
       
   218         List<String> argTypeNames = method.argumentTypeNames();
       
   219 
       
   220         // If argument counts differ, we can stop here
       
   221         if (argTypeNames.size() != nameList.size()) {
       
   222             return false;
       
   223         }
       
   224 
       
   225         // Compare each argument type's name
       
   226         int nTypes = argTypeNames.size();
       
   227         for (int i = 0; i < nTypes; ++i) {
       
   228             String comp1 = argTypeNames.get(i);
       
   229             String comp2 = nameList.get(i);
       
   230             if (! comp1.equals(comp2)) {
       
   231                 /*
       
   232                  * We have to handle varargs.  EG, the
       
   233                  * method's last arg type is xxx[]
       
   234                  * while the nameList contains xxx...
       
   235                  * Note that the nameList can also contain
       
   236                  * xxx[] in which case we don't get here.
       
   237                  */
       
   238                 if (i != nTypes - 1 ||
       
   239                     !method.isVarArgs()  ||
       
   240                     !comp2.endsWith("...")) {
       
   241                     return false;
       
   242                 }
       
   243                 /*
       
   244                  * The last types differ, it is a varargs
       
   245                  * method and the nameList item is varargs.
       
   246                  * We just have to compare the type names, eg,
       
   247                  * make sure we don't have xxx[] for the method
       
   248                  * arg type and yyy... for the nameList item.
       
   249                  */
       
   250                 int comp1Length = comp1.length();
       
   251                 if (comp1Length + 1 != comp2.length()) {
       
   252                     // The type names are different lengths
       
   253                     return false;
       
   254                 }
       
   255                 // We know the two type names are the same length
       
   256                 if (!comp1.regionMatches(0, comp2, 0, comp1Length - 2)) {
       
   257                     return false;
       
   258                 }
       
   259                 // We do have xxx[] and xxx... as the last param type
       
   260                 return true;
       
   261             }
       
   262         }
       
   263 
       
   264         return true;
       
   265     }
       
   266 
       
   267 
       
   268     /*
       
   269      * Remove unneeded spaces and expand class names to fully
       
   270      * qualified names, if necessary and possible.
       
   271      */
       
   272     private String normalizeArgTypeName(String name) {
       
   273         /*
       
   274          * Separate the type name from any array modifiers,
       
   275          * stripping whitespace after the name ends
       
   276          */
       
   277         int i = 0;
       
   278         StringBuilder typePart = new StringBuilder();
       
   279         StringBuilder arrayPart = new StringBuilder();
       
   280         name = name.trim();
       
   281         int nameLength = name.length();
       
   282         /*
       
   283          * For varargs, there can be spaces before the ... but not
       
   284          * within the ...  So, we will just ignore the ...
       
   285          * while stripping blanks.
       
   286          */
       
   287         boolean isVarArgs = name.endsWith("...");
       
   288         if (isVarArgs) {
       
   289             nameLength -= 3;
       
   290         }
       
   291         while (i < nameLength) {
       
   292             char c = name.charAt(i);
       
   293             if (Character.isWhitespace(c) || c == '[') {
       
   294                 break;      // name is complete
       
   295             }
       
   296             typePart.append(c);
       
   297             i++;
       
   298         }
       
   299         while (i < nameLength) {
       
   300             char c = name.charAt(i);
       
   301             if ( (c == '[') || (c == ']')) {
       
   302                 arrayPart.append(c);
       
   303             } else if (!Character.isWhitespace(c)) {
       
   304                 throw new IllegalArgumentException
       
   305                     (MessageOutput.format("Invalid argument type name"));
       
   306             }
       
   307             i++;
       
   308         }
       
   309         name = typePart.toString();
       
   310 
       
   311         /*
       
   312          * When there's no sign of a package name already, try to expand the
       
   313          * the name to a fully qualified class name
       
   314          */
       
   315         if ((name.indexOf('.') == -1) || name.startsWith("*.")) {
       
   316             try {
       
   317                 ReferenceType argClass = Env.getReferenceTypeFromToken(name);
       
   318                 if (argClass != null) {
       
   319                     name = argClass.name();
       
   320                 }
       
   321             } catch (IllegalArgumentException e) {
       
   322                 // We'll try the name as is
       
   323             }
       
   324         }
       
   325         name += arrayPart.toString();
       
   326         if (isVarArgs) {
       
   327             name += "...";
       
   328         }
       
   329         return name;
       
   330     }
       
   331 
       
   332     /*
       
   333      * Attempt an unambiguous match of the method name and
       
   334      * argument specification to a method. If no arguments
       
   335      * are specified, the method must not be overloaded.
       
   336      * Otherwise, the argument types much match exactly
       
   337      */
       
   338     private Method findMatchingMethod(ReferenceType refType)
       
   339                                         throws AmbiguousMethodException,
       
   340                                                NoSuchMethodException {
       
   341 
       
   342         // Normalize the argument string once before looping below.
       
   343         List<String> argTypeNames = null;
       
   344         if (methodArgs() != null) {
       
   345             argTypeNames = new ArrayList<String>(methodArgs().size());
       
   346             for (String name : methodArgs()) {
       
   347                 name = normalizeArgTypeName(name);
       
   348                 argTypeNames.add(name);
       
   349             }
       
   350         }
       
   351 
       
   352         // Check each method in the class for matches
       
   353         Method firstMatch = null;  // first method with matching name
       
   354         Method exactMatch = null;  // (only) method with same name & sig
       
   355         int matchCount = 0;        // > 1 implies overload
       
   356         for (Method candidate : refType.methods()) {
       
   357             if (candidate.name().equals(methodName())) {
       
   358                 matchCount++;
       
   359 
       
   360                 // Remember the first match in case it is the only one
       
   361                 if (matchCount == 1) {
       
   362                     firstMatch = candidate;
       
   363                 }
       
   364 
       
   365                 // If argument types were specified, check against candidate
       
   366                 if ((argTypeNames != null)
       
   367                         && compareArgTypes(candidate, argTypeNames) == true) {
       
   368                     exactMatch = candidate;
       
   369                     break;
       
   370                 }
       
   371             }
       
   372         }
       
   373 
       
   374         // Determine method for breakpoint
       
   375         Method method = null;
       
   376         if (exactMatch != null) {
       
   377             // Name and signature match
       
   378             method = exactMatch;
       
   379         } else if ((argTypeNames == null) && (matchCount > 0)) {
       
   380             // At least one name matched and no arg types were specified
       
   381             if (matchCount == 1) {
       
   382                 method = firstMatch;       // Only one match; safe to use it
       
   383             } else {
       
   384                 throw new AmbiguousMethodException();
       
   385             }
       
   386         } else {
       
   387             throw new NoSuchMethodException(methodName());
       
   388         }
       
   389         return method;
       
   390     }
       
   391 }