382 lines · 13.7 KB · Java
RawDownload
/* * Copyright (c) 1998, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package com.sun.tools.example.debug.tty; import com.sun.jdi.*;import com.sun.jdi.request.*; import java.util.ArrayList;import java.util.List; class BreakpointSpec extends EventRequestSpec {    String methodId;    List<String> methodArgs;    int lineNumber;     BreakpointSpec(ReferenceTypeSpec refSpec, int lineNumber) {        super(refSpec);        this.methodId = null;        this.methodArgs = null;        this.lineNumber = lineNumber;    }     BreakpointSpec(ReferenceTypeSpec refSpec, String methodId,                   List<String> methodArgs) throws MalformedMemberNameException {        super(refSpec);        this.methodId = methodId;        this.methodArgs = methodArgs;        this.lineNumber = 0;        if (!isValidMethodName(methodId)) {            throw new MalformedMemberNameException(methodId);        }    }     /**     * The 'refType' is known to match, return the EventRequest.     */    @Override    EventRequest resolveEventRequest(ReferenceType refType)                           throws AmbiguousMethodException,                                  AbsentInformationException,                                  InvalidTypeException,                                  NoSuchMethodException,                                  LineNotFoundException {        Location location = location(refType);        if (location == null) {            throw new InvalidTypeException();        }        EventRequestManager em = refType.virtualMachine().eventRequestManager();        EventRequest bp = em.createBreakpointRequest(location);        bp.setSuspendPolicy(suspendPolicy);        bp.enable();        return bp;    }     String methodName() {        return methodId;    }     int lineNumber() {        return lineNumber;    }     List<String> methodArgs() {        return methodArgs;    }     boolean isMethodBreakpoint() {        return (methodId != null);    }     @Override    public int hashCode() {        return refSpec.hashCode() + lineNumber +            ((methodId != null) ? methodId.hashCode() : 0) +            ((methodArgs != null) ? methodArgs.hashCode() : 0);    }     @Override    public boolean equals(Object obj) {        if (obj instanceof BreakpointSpec) {            BreakpointSpec breakpoint = (BreakpointSpec)obj;             return ((methodId != null) ?                        methodId.equals(breakpoint.methodId)                      : methodId == breakpoint.methodId) &&                   ((methodArgs != null) ?                        methodArgs.equals(breakpoint.methodArgs)                      : methodArgs == breakpoint.methodArgs) &&                   refSpec.equals(breakpoint.refSpec) &&                   (lineNumber == breakpoint.lineNumber);        } else {            return false;        }    }     @Override    String errorMessageFor(Exception e) {        if (e instanceof AmbiguousMethodException) {            return (MessageOutput.format("Method is overloaded; specify arguments",                                         methodName()));            /*             * TO DO: list the methods here             */        } else if (e instanceof NoSuchMethodException) {            return (MessageOutput.format("No method in",                                         new Object [] {methodName(),                                                        refSpec.toString()}));        } else if (e instanceof AbsentInformationException) {            return (MessageOutput.format("No linenumber information for",                                         refSpec.toString()));        } else if (e instanceof LineNotFoundException) {            return (MessageOutput.format("No code at line",                                         new Object [] {new Long (lineNumber()),                                                        refSpec.toString()}));        } else if (e instanceof InvalidTypeException) {            return (MessageOutput.format("Breakpoints can be located only in classes.",                                         refSpec.toString()));        } else {            return super.errorMessageFor( e);        }    }     @Override    public String toString() {        StringBuffer buffer = new StringBuffer(refSpec.toString());        if (isMethodBreakpoint()) {            buffer.append('.');            buffer.append(methodId);            if (methodArgs != null) {                boolean first = true;                buffer.append('(');                for (String arg : methodArgs) {                    if (!first) {                        buffer.append(',');                    }                    buffer.append(arg);                    first = false;                }                buffer.append(")");            }        } else {            buffer.append(':');            buffer.append(lineNumber);        }        return MessageOutput.format("breakpoint", buffer.toString());    }     private Location location(ReferenceType refType) throws                                    AmbiguousMethodException,                                    AbsentInformationException,                                    NoSuchMethodException,                                    LineNotFoundException {        Location location = null;        if (isMethodBreakpoint()) {            Method method = findMatchingMethod(refType);            location = method.location();        } else {            // let AbsentInformationException be thrown            List<Location> locs = refType.locationsOfLine(lineNumber());            if (locs.size() == 0) {                throw new LineNotFoundException();            }            // TO DO: handle multiple locations            location = locs.get(0);            if (location.method() == null) {                throw new LineNotFoundException();            }        }        return location;    }     private boolean isValidMethodName(String s) {        return isJavaIdentifier(s) ||               s.equals("<init>") ||               s.equals("<clinit>");    }     /*     * Compare a method's argument types with a Vector of type names.     * Return true if each argument type has a name identical to the     * corresponding string in the vector (allowing for varars)     * and if the number of arguments in the method matches the     * number of names passed     */    private boolean compareArgTypes(Method method, List<String> nameList) {        List<String> argTypeNames = method.argumentTypeNames();         // If argument counts differ, we can stop here        if (argTypeNames.size() != nameList.size()) {            return false;        }         // Compare each argument type's name        int nTypes = argTypeNames.size();        for (int i = 0; i < nTypes; ++i) {            String comp1 = argTypeNames.get(i);            String comp2 = nameList.get(i);            if (! comp1.equals(comp2)) {                /*                 * We have to handle varargs.  EG, the                 * method's last arg type is xxx[]                 * while the nameList contains xxx...                 * Note that the nameList can also contain                 * xxx[] in which case we don't get here.                 */                if (i != nTypes - 1 ||                    !method.isVarArgs()  ||                    !comp2.endsWith("...")) {                    return false;                }                /*                 * The last types differ, it is a varargs                 * method and the nameList item is varargs.                 * We just have to compare the type names, eg,                 * make sure we don't have xxx[] for the method                 * arg type and yyy... for the nameList item.                 */                int comp1Length = comp1.length();                if (comp1Length + 1 != comp2.length()) {                    // The type names are different lengths                    return false;                }                // We know the two type names are the same length                if (!comp1.regionMatches(0, comp2, 0, comp1Length - 2)) {                    return false;                }                // We do have xxx[] and xxx... as the last param type                return true;            }        }         return true;    }      /*     * Remove unneeded spaces and expand class names to fully     * qualified names, if necessary and possible.     */    private String normalizeArgTypeName(String name) {        /*         * Separate the type name from any array modifiers,         * stripping whitespace after the name ends         */        int i = 0;        StringBuffer typePart = new StringBuffer();        StringBuffer arrayPart = new StringBuffer();        name = name.trim();        int nameLength = name.length();        /*         * For varargs, there can be spaces before the ... but not         * within the ...  So, we will just ignore the ...         * while stripping blanks.         */        boolean isVarArgs = name.endsWith("...");        if (isVarArgs) {            nameLength -= 3;        }        while (i < nameLength) {            char c = name.charAt(i);            if (Character.isWhitespace(c) || c == '[') {                break;      // name is complete            }            typePart.append(c);            i++;        }        while (i < nameLength) {            char c = name.charAt(i);            if ( (c == '[') || (c == ']')) {                arrayPart.append(c);            } else if (!Character.isWhitespace(c)) {                throw new IllegalArgumentException                    (MessageOutput.format("Invalid argument type name"));            }            i++;        }        name = typePart.toString();         /*         * When there's no sign of a package name already, try to expand the         * the name to a fully qualified class name         */        if ((name.indexOf('.') == -1) || name.startsWith("*.")) {            try {                ReferenceType argClass = Env.getReferenceTypeFromToken(name);                if (argClass != null) {                    name = argClass.name();                }            } catch (IllegalArgumentException e) {                // We'll try the name as is            }        }        name += arrayPart.toString();        if (isVarArgs) {            name += "...";        }        return name;    }     /*     * Attempt an unambiguous match of the method name and     * argument specification to a method. If no arguments     * are specified, the method must not be overloaded.     * Otherwise, the argument types much match exactly     */    private Method findMatchingMethod(ReferenceType refType)                                        throws AmbiguousMethodException,                                               NoSuchMethodException {         // Normalize the argument string once before looping below.        List<String> argTypeNames = null;        if (methodArgs() != null) {            argTypeNames = new ArrayList<String>(methodArgs().size());            for (String name : methodArgs()) {                name = normalizeArgTypeName(name);                argTypeNames.add(name);            }        }         // Check each method in the class for matches        Method firstMatch = null;  // first method with matching name        Method exactMatch = null;  // (only) method with same name & sig        int matchCount = 0;        // > 1 implies overload        for (Method candidate : refType.methods()) {            if (candidate.name().equals(methodName())) {                matchCount++;                 // Remember the first match in case it is the only one                if (matchCount == 1) {                    firstMatch = candidate;                }                 // If argument types were specified, check against candidate                if ((argTypeNames != null)                        && compareArgTypes(candidate, argTypeNames) == true) {                    exactMatch = candidate;                    break;                }            }        }         // Determine method for breakpoint        Method method = null;        if (exactMatch != null) {            // Name and signature match            method = exactMatch;        } else if ((argTypeNames == null) && (matchCount > 0)) {            // At least one name matched and no arg types were specified            if (matchCount == 1) {                method = firstMatch;       // Only one match; safe to use it            } else {                throw new AmbiguousMethodException();            }        } else {            throw new NoSuchMethodException(methodName());        }        return method;    }}