415N/A/*
1259N/A * Copyright (c) 1999, 2008, Oracle and/or its affiliates. All rights reserved.
415N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
415N/A *
415N/A * This code is free software; you can redistribute it and/or modify it
415N/A * under the terms of the GNU General Public License version 2 only, as
553N/A * published by the Free Software Foundation. Oracle designates this
415N/A * particular file as subject to the "Classpath" exception as provided
553N/A * by Oracle in the LICENSE file that accompanied this code.
415N/A *
415N/A * This code is distributed in the hope that it will be useful, but WITHOUT
415N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
415N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
415N/A * version 2 for more details (a copy is included in the LICENSE file that
415N/A * accompanied this code).
415N/A *
415N/A * You should have received a copy of the GNU General Public License version
415N/A * 2 along with this work; if not, write to the Free Software Foundation,
415N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
415N/A *
553N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
553N/A * or visit www.oracle.com if you need additional information or have any
553N/A * questions.
415N/A */
415N/A
415N/A/*
415N/A * This source code is provided to illustrate the usage of a given feature
415N/A * or technique and has been deliberately simplified. Additional steps
727N/A * required for a production-quality application, such as security checks,
415N/A * input validation and proper error handling, might not be present in
415N/A * this sample code.
415N/A */
415N/A
415N/A
415N/Apackage com.sun.tools.example.debug.bdi;
415N/A
415N/Aimport com.sun.jdi.*;
415N/Aimport java.util.ArrayList;
415N/Aimport java.util.List;
415N/A
415N/Apublic class MethodBreakpointSpec extends BreakpointSpec {
415N/A String methodId;
415N/A List<String> methodArgs;
415N/A
415N/A MethodBreakpointSpec(EventRequestSpecList specs,
415N/A ReferenceTypeSpec refSpec,
415N/A String methodId, List<String> methodArgs) {
415N/A super(specs, refSpec);
415N/A this.methodId = methodId;
711N/A this.methodArgs = methodArgs;
415N/A }
415N/A
415N/A /**
415N/A * The 'refType' is known to match.
415N/A */
415N/A @Override
415N/A void resolve(ReferenceType refType) throws MalformedMemberNameException,
415N/A AmbiguousMethodException,
415N/A InvalidTypeException,
415N/A NoSuchMethodException,
415N/A NoSessionException {
415N/A if (!isValidMethodName(methodId)) {
574N/A throw new MalformedMemberNameException(methodId);
415N/A }
415N/A if (!(refType instanceof ClassType)) {
415N/A throw new InvalidTypeException();
415N/A }
415N/A Location location = location((ClassType)refType);
415N/A setRequest(refType.virtualMachine().eventRequestManager()
415N/A .createBreakpointRequest(location));
415N/A }
415N/A
415N/A private Location location(ClassType clazz) throws
415N/A AmbiguousMethodException,
711N/A NoSuchMethodException,
711N/A NoSessionException {
711N/A Method method = findMatchingMethod(clazz);
727N/A Location location = method.location();
415N/A return location;
415N/A }
415N/A
415N/A public String methodName() {
415N/A return methodId;
580N/A }
415N/A
415N/A public List<String> methodArgs() {
415N/A return methodArgs;
415N/A }
415N/A
415N/A @Override
415N/A public int hashCode() {
415N/A return refSpec.hashCode() +
415N/A ((methodId != null) ? methodId.hashCode() : 0) +
415N/A ((methodArgs != null) ? methodArgs.hashCode() : 0);
415N/A }
415N/A
415N/A @Override
415N/A public boolean equals(Object obj) {
415N/A if (obj instanceof MethodBreakpointSpec) {
415N/A MethodBreakpointSpec breakpoint = (MethodBreakpointSpec)obj;
415N/A
415N/A return methodId.equals(breakpoint.methodId) &&
415N/A methodArgs.equals(breakpoint.methodArgs) &&
415N/A refSpec.equals(breakpoint.refSpec);
415N/A } else {
415N/A return false;
415N/A }
415N/A }
415N/A
415N/A @Override
415N/A public String errorMessageFor(Exception e) {
415N/A if (e instanceof AmbiguousMethodException) {
415N/A return ("Method " + methodName() + " is overloaded; specify arguments");
415N/A /*
415N/A * TO DO: list the methods here
415N/A */
415N/A } else if (e instanceof NoSuchMethodException) {
415N/A return ("No method " + methodName() + " in " + refSpec);
415N/A } else if (e instanceof InvalidTypeException) {
415N/A return ("Breakpoints can be located only in classes. " +
415N/A refSpec + " is an interface or array");
415N/A } else {
415N/A return super.errorMessageFor( e);
415N/A }
415N/A }
415N/A
415N/A @Override
415N/A public String toString() {
415N/A StringBuffer buffer = new StringBuffer("breakpoint ");
415N/A buffer.append(refSpec.toString());
415N/A buffer.append('.');
415N/A buffer.append(methodId);
415N/A if (methodArgs != null) {
415N/A boolean first = true;
415N/A buffer.append('(');
415N/A for (String name : methodArgs) {
415N/A if (!first) {
415N/A buffer.append(',');
415N/A }
415N/A buffer.append(name);
415N/A first = false;
415N/A }
415N/A buffer.append(")");
415N/A }
415N/A buffer.append(" (");
415N/A buffer.append(getStatusString());
415N/A buffer.append(')');
415N/A return buffer.toString();
415N/A }
415N/A
415N/A private boolean isValidMethodName(String s) {
415N/A return isJavaIdentifier(s) ||
415N/A s.equals("<init>") ||
415N/A s.equals("<clinit>");
415N/A }
415N/A
415N/A /*
415N/A * Compare a method's argument types with a Vector of type names.
415N/A * Return true if each argument type has a name identical to the
415N/A * corresponding string in the vector (allowing for varargs)
415N/A * and if the number of arguments in the method matches the
415N/A * number of names passed
415N/A */
415N/A private boolean compareArgTypes(Method method, List<String> nameList) {
415N/A List<String> argTypeNames = method.argumentTypeNames();
415N/A
415N/A // If argument counts differ, we can stop here
415N/A if (argTypeNames.size() != nameList.size()) {
415N/A return false;
415N/A }
415N/A
415N/A // Compare each argument type's name
415N/A int nTypes = argTypeNames.size();
415N/A for (int i = 0; i < nTypes; ++i) {
415N/A String comp1 = argTypeNames.get(i);
415N/A String comp2 = nameList.get(i);
415N/A if (! comp1.equals(comp2)) {
415N/A /*
415N/A * We have to handle varargs. EG, the
415N/A * method's last arg type is xxx[]
706N/A * while the nameList contains xxx...
415N/A * Note that the nameList can also contain
415N/A * xxx[] in which case we don't get here.
415N/A */
415N/A if (i != nTypes - 1 ||
415N/A !method.isVarArgs() ||
415N/A !comp2.endsWith("...")) {
415N/A return false;
415N/A }
415N/A /*
415N/A * The last types differ, it is a varargs
415N/A * method and the nameList item is varargs.
415N/A * We just have to compare the type names, eg,
415N/A * make sure we don't have xxx[] for the method
415N/A * arg type and yyy... for the nameList item.
415N/A */
415N/A int comp1Length = comp1.length();
415N/A if (comp1Length + 1 != comp2.length()) {
415N/A // The type names are different lengths
415N/A return false;
415N/A }
415N/A // We know the two type names are the same length
415N/A if (!comp1.regionMatches(0, comp2, 0, comp1Length - 2)) {
415N/A return false;
415N/A }
415N/A // We do have xxx[] and xxx... as the last param type
415N/A return true;
415N/A }
415N/A }
415N/A
415N/A return true;
415N/A }
415N/A
415N/A private VirtualMachine vm() {
415N/A return request.virtualMachine();
415N/A }
415N/A
415N/A /**
415N/A * Remove unneeded spaces and expand class names to fully
415N/A * qualified names, if necessary and possible.
415N/A */
415N/A private String normalizeArgTypeName(String name) throws NoSessionException {
415N/A /*
415N/A * Separate the type name from any array modifiers,
415N/A * stripping whitespace after the name ends.
415N/A */
415N/A int i = 0;
415N/A StringBuffer typePart = new StringBuffer();
415N/A StringBuffer arrayPart = new StringBuffer();
415N/A name = name.trim();
415N/A int nameLength = name.length();
415N/A /*
415N/A * For varargs, there can be spaces before the ... but not
415N/A * within the ... So, we will just ignore the ...
415N/A * while stripping blanks.
415N/A */
415N/A boolean isVarArgs = name.endsWith("...");
415N/A if (isVarArgs) {
415N/A nameLength -= 3;
415N/A }
711N/A
711N/A while (i < nameLength) {
711N/A char c = name.charAt(i);
711N/A if (Character.isWhitespace(c) || c == '[') {
711N/A break; // name is complete
711N/A }
711N/A typePart.append(c);
711N/A i++;
711N/A }
415N/A while (i < nameLength) {
415N/A char c = name.charAt(i);
415N/A if ( (c == '[') || (c == ']')) {
415N/A arrayPart.append(c);
415N/A } else if (!Character.isWhitespace(c)) {
415N/A throw new IllegalArgumentException(
415N/A "Invalid argument type name");
415N/A
415N/A }
415N/A i++;
415N/A }
415N/A
415N/A name = typePart.toString();
415N/A
415N/A /*
415N/A * When there's no sign of a package name already,
415N/A * try to expand the
415N/A * the name to a fully qualified class name
415N/A */
415N/A if ((name.indexOf('.') == -1) || name.startsWith("*.")) {
415N/A try {
415N/A List<?> refs = specs.runtime.findClassesMatchingPattern(name);
507N/A if (refs.size() > 0) { //### ambiguity???
507N/A name = ((ReferenceType)(refs.get(0))).name();
507N/A }
507N/A } catch (IllegalArgumentException e) {
507N/A // We'll try the name as is
415N/A }
415N/A }
415N/A name += arrayPart.toString();
415N/A if (isVarArgs) {
415N/A name += "...";
415N/A }
415N/A return name;
415N/A }
415N/A
415N/A /*
415N/A * Attempt an unambiguous match of the method name and
415N/A * argument specification to a method. If no arguments
415N/A * are specified, the method must not be overloaded.
415N/A * Otherwise, the argument types much match exactly
415N/A */
415N/A private Method findMatchingMethod(ClassType clazz)
415N/A throws AmbiguousMethodException,
415N/A NoSuchMethodException,
415N/A NoSessionException {
415N/A
415N/A // Normalize the argument string once before looping below.
415N/A List<String> argTypeNames = null;
415N/A if (methodArgs() != null) {
415N/A argTypeNames = new ArrayList<String>(methodArgs().size());
415N/A for (String name : methodArgs()) {
415N/A name = normalizeArgTypeName(name);
415N/A argTypeNames.add(name);
415N/A }
415N/A }
415N/A
415N/A // Check each method in the class for matches
415N/A Method firstMatch = null; // first method with matching name
415N/A Method exactMatch = null; // (only) method with same name & sig
415N/A int matchCount = 0; // > 1 implies overload
415N/A for (Method candidate : clazz.methods()) {
415N/A if (candidate.name().equals(methodName())) {
415N/A matchCount++;
415N/A
415N/A // Remember the first match in case it is the only one
415N/A if (matchCount == 1) {
415N/A firstMatch = candidate;
415N/A }
415N/A
415N/A // If argument types were specified, check against candidate
415N/A if ((argTypeNames != null)
415N/A && compareArgTypes(candidate, argTypeNames) == true) {
415N/A exactMatch = candidate;
415N/A break;
415N/A }
415N/A }
415N/A }
415N/A
415N/A // Determine method for breakpoint
415N/A Method method = null;
415N/A if (exactMatch != null) {
415N/A // Name and signature match
415N/A method = exactMatch;
415N/A } else if ((argTypeNames == null) && (matchCount > 0)) {
415N/A // At least one name matched and no arg types were specified
415N/A if (matchCount == 1) {
415N/A method = firstMatch; // Only one match; safe to use it
415N/A } else {
415N/A throw new AmbiguousMethodException();
415N/A }
415N/A } else {
415N/A throw new NoSuchMethodException(methodName());
711N/A }
711N/A return method;
415N/A }
415N/A}
415N/A