blob: 49cfae60df55dc6ecdcaac5169c123dde7a90ac1 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.netbeans.modules.debugger.jpda.jdi;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
/**
* Generates wrapper methods for JDI calls.
* Use "ant generate" to run this class and generate JDI wrapper classes.
*/
public class Generate {
private static final String JDI_CLASSES_PATH = "com/sun/jdi";
private static final Class[] RUNTIME_EXCEPTIONS = new Class[]
{ com.sun.jdi.ClassNotPreparedException.class, com.sun.jdi.InconsistentDebugInfoException.class,
com.sun.jdi.InternalException.class, com.sun.jdi.InvalidStackFrameException.class,
com.sun.jdi.NativeMethodException.class, com.sun.jdi.ObjectCollectedException.class,
com.sun.jdi.VMCannotBeModifiedException.class, com.sun.jdi.VMDisconnectedException.class,
com.sun.jdi.VMOutOfMemoryException.class,
com.sun.jdi.request.DuplicateRequestException.class,
com.sun.jdi.request.InvalidRequestStateException.class };
private static final String PACKAGE = "org.netbeans.modules.debugger.jpda.jdi";
private static final Map<Class, String> EXCEPTION_WRAPPERS = new LinkedHashMap<Class, String>();
private static final Map<String/*class name*/, Map<String/*method*/, Set<Class/*exception*/>>> EXCEPTIONS_BY_METHODS = new LinkedHashMap<String, Map<String, Set<Class>>>();
// Fake values can be returned if these exceptions are thrown:
private static final Set<Class> SILENT_EXCEPTIONS = Collections.unmodifiableSet(new LinkedHashSet<Class>(Arrays.asList(new Class[] {
com.sun.jdi.InternalException.class, com.sun.jdi.ObjectCollectedException.class, com.sun.jdi.VMDisconnectedException.class })));
private static final Set<String> NOT_USED_CLASSES = Collections.unmodifiableSet(new HashSet<String>(Arrays.asList(new String[] {
com.sun.jdi.Accessible.class.getName(), com.sun.jdi.Bootstrap.class.getName(),
com.sun.jdi.ClassLoaderReference.class.getName(),
com.sun.jdi.PathSearchingVirtualMachine.class.getName(),
com.sun.jdi.VoidValue.class.getName(),
// Connectors are used in API and UI modules.
// Classes starting with "com.sun.jdi.connect" are not generated
com.sun.jdi.event.EventIterator.class.getName(),
})));
private static final String METHODS_BY_JDK = "MethodsByJDK";
private static String license = null;
// Runtime exceptions:
// VMDisconnectedException on all JDI calls
// InternalException on all JDI calls
// ObjectCollectedException on all calls on ObjectReference
// Other exceptions on selected methods
static {
Map<String, Set<Class>> AllClassExceptions = new LinkedHashMap<String, Set<Class>>();
AllClassExceptions.put("*", new LinkedHashSet<Class>(Arrays.asList(
new Class[] { com.sun.jdi.InternalException.class,
com.sun.jdi.VMDisconnectedException.class })));
EXCEPTIONS_BY_METHODS.put("*", AllClassExceptions);
Map<String, Set<Class>> ReferenceTypeExceptions = new LinkedHashMap<String, Set<Class>>();
ReferenceTypeExceptions.put("fields", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ReferenceTypeExceptions.put("visibleFields", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ReferenceTypeExceptions.put("allFields", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ReferenceTypeExceptions.put("fieldByName", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ReferenceTypeExceptions.put("methods", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ReferenceTypeExceptions.put("visibleMethods", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ReferenceTypeExceptions.put("allMethods", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ReferenceTypeExceptions.put("methodsByName", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ReferenceTypeExceptions.put("allLineLocations", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ReferenceTypeExceptions.put("locationsOfLine", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ReferenceTypeExceptions.put("classObject", Collections.singleton((Class) java.lang.UnsupportedOperationException.class));
ReferenceTypeExceptions.put("*", Collections.singleton((Class) com.sun.jdi.ObjectCollectedException.class));
EXCEPTIONS_BY_METHODS.put(com.sun.jdi.ReferenceType.class.getName(), ReferenceTypeExceptions);
Map<String, Set<Class>> ClassTypeExceptions = new LinkedHashMap<String, Set<Class>>();
ClassTypeExceptions.put("interfaces", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ClassTypeExceptions.put("allInterfaces", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ClassTypeExceptions.put("setValue", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class)); // JDWP protocol says that this can be thrown!
ClassTypeExceptions.put("concreteMethodByName", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
ClassTypeExceptions.put("invokeMethod", Collections.singleton((Class) com.sun.jdi.ObjectCollectedException.class));
ClassTypeExceptions.put("newInstance", Collections.singleton((Class) com.sun.jdi.ObjectCollectedException.class));
EXCEPTIONS_BY_METHODS.put(com.sun.jdi.ClassType.class.getName(), ClassTypeExceptions);
Map<String, Set<Class>> InterfaceTypeExceptions = new LinkedHashMap<String, Set<Class>>();
InterfaceTypeExceptions.put("superinterfaces", Collections.singleton((Class) com.sun.jdi.ClassNotPreparedException.class));
EXCEPTIONS_BY_METHODS.put(com.sun.jdi.InterfaceType.class.getName(), InterfaceTypeExceptions);
Map<String, Set<Class>> TypeExceptions = new LinkedHashMap<String, Set<Class>>();
TypeExceptions.put("*", Collections.singleton((Class) com.sun.jdi.ObjectCollectedException.class));
EXCEPTIONS_BY_METHODS.put(com.sun.jdi.Type.class.getName(), TypeExceptions);
Map<String, Set<Class>> ObjectReferenceExceptions = new LinkedHashMap<String, Set<Class>>();
ObjectReferenceExceptions.put("setValue", Collections.singleton((Class) java.lang.IllegalArgumentException.class));
ObjectReferenceExceptions.put("disableCollection", Collections.singleton((Class) java.lang.UnsupportedOperationException.class));
ObjectReferenceExceptions.put("enableCollection", Collections.singleton((Class) java.lang.UnsupportedOperationException.class));
EXCEPTIONS_BY_METHODS.put(com.sun.jdi.ObjectReference.class.getName(), ObjectReferenceExceptions);
Map<String, Set<Class>> FieldExceptions = new LinkedHashMap<String, Set<Class>>();
FieldExceptions.put("type", Collections.singleton((Class) com.sun.jdi.ObjectCollectedException.class));
EXCEPTIONS_BY_METHODS.put(com.sun.jdi.Field.class.getName(), FieldExceptions);
Map<String, Set<Class>> ThreadReferenceExceptions = new LinkedHashMap<String, Set<Class>>();
// IllegalThreadStateException is thrown through JDWPException when INVALID_THREAD is received from JDWP.
ThreadReferenceExceptions.put("*", Collections.singleton((Class) IllegalThreadStateException.class));
ThreadReferenceExceptions.put("popFrames", new LinkedHashSet<Class>(Arrays.asList(
new Class[] { com.sun.jdi.NativeMethodException.class,
com.sun.jdi.InvalidStackFrameException.class })));
ThreadReferenceExceptions.put("forceEarlyReturn", new LinkedHashSet<Class>(Arrays.asList(
new Class[] { com.sun.jdi.NativeMethodException.class,
com.sun.jdi.InvalidStackFrameException.class })));
ThreadReferenceExceptions.put("frameCount", new LinkedHashSet<Class>(Arrays.asList(
new Class[] { com.sun.jdi.InvalidStackFrameException.class })));// #233070
ThreadReferenceExceptions.put("threadGroup", new LinkedHashSet<Class>(Arrays.asList(
new Class[] { com.sun.jdi.VMOutOfMemoryException.class })));
EXCEPTIONS_BY_METHODS.put(com.sun.jdi.ThreadReference.class.getName(), ThreadReferenceExceptions);
Map<String, Set<Class>> StackFrameExceptions = new LinkedHashMap<String, Set<Class>>();
StackFrameExceptions.put("*", Collections.singleton((Class) com.sun.jdi.InvalidStackFrameException.class));
StackFrameExceptions.put("setValue", Collections.singleton((Class) com.sun.jdi.ObjectCollectedException.class));
StackFrameExceptions.put("visibleVariableByName", Collections.singleton((Class) com.sun.jdi.NativeMethodException.class));
StackFrameExceptions.put("visibleVariables", Collections.singleton((Class) com.sun.jdi.NativeMethodException.class));
EXCEPTIONS_BY_METHODS.put(com.sun.jdi.StackFrame.class.getName(), StackFrameExceptions);
Map<String, Set<Class>> MonitorInfoExceptions = new LinkedHashMap<String, Set<Class>>();
MonitorInfoExceptions.put("*", Collections.singleton((Class) com.sun.jdi.InvalidStackFrameException.class));
EXCEPTIONS_BY_METHODS.put("com.sun.jdi.MonitorInfo", MonitorInfoExceptions);
Map<String, Set<Class>> VirtualMachineExceptions = new LinkedHashMap<String, Set<Class>>();
// UnsupportedOperationException can be thrown on J2ME:
VirtualMachineExceptions.put("mirrorOf(java.lang.String)", Collections.singleton((Class) java.lang.UnsupportedOperationException.class));
EXCEPTIONS_BY_METHODS.put(com.sun.jdi.VirtualMachine.class.getName(), VirtualMachineExceptions);
Map<String, Set<Class>> EventRequestExceptions = new LinkedHashMap<String, Set<Class>>();
// ObjectCollectedException can be thrown
EventRequestExceptions.put("disable", new LinkedHashSet<Class>(Arrays.asList(
new Class [] { com.sun.jdi.ObjectCollectedException.class,
com.sun.jdi.request.InvalidRequestStateException.class })));
EventRequestExceptions.put("enable", new LinkedHashSet<Class>(Arrays.asList(
new Class [] { com.sun.jdi.ObjectCollectedException.class,
com.sun.jdi.request.InvalidRequestStateException.class })));
EventRequestExceptions.put("setEnabled", new LinkedHashSet<Class>(Arrays.asList(
new Class [] { com.sun.jdi.ObjectCollectedException.class,
com.sun.jdi.request.InvalidRequestStateException.class })));
EXCEPTIONS_BY_METHODS.put(com.sun.jdi.request.EventRequest.class.getName(), EventRequestExceptions);
Map<String, Set<Class>> EventRequestManagerExceptions = new LinkedHashMap<String, Set<Class>>();
EventRequestManagerExceptions.put("deleteEventRequest", Collections.singleton((Class)
com.sun.jdi.request.InvalidRequestStateException.class));
EventRequestManagerExceptions.put("deleteEventRequests", Collections.singleton((Class)
com.sun.jdi.request.InvalidRequestStateException.class));
EXCEPTIONS_BY_METHODS.put(com.sun.jdi.request.EventRequestManager.class.getName(), EventRequestManagerExceptions);
Map<String, Set<Class>> EventSetExceptions = new LinkedHashMap<String, Set<Class>>();
// IllegalThreadStateException is thrown through JDWPException when INVALID_THREAD is received from JDWP.
// ObjectCollectedException can be thrown when the thread is collected.
EventSetExceptions.put("resume", new LinkedHashSet<Class>(Arrays.asList(
new Class [] { IllegalThreadStateException.class,
com.sun.jdi.ObjectCollectedException.class })));
EXCEPTIONS_BY_METHODS.put(com.sun.jdi.event.EventSet.class.getName(), EventSetExceptions);
}
private static String readLicense() throws IOException {
StringBuilder sb = new StringBuilder();
File f = new File(System.getProperty("user.dir"));
f = new File(f, "gensrc/"+Generate.class.getName().replace('.', '/')+".java");
BufferedReader r = new BufferedReader(new FileReader(f));
while (true) {
String line = r.readLine();
sb.append(line);
sb.append(System.getProperty("line.separator"));
if (line.trim().indexOf("*/") >= 0) {
break;
}
}
r.close();
return sb.toString();
}
private static String getLicense() {
if (license == null) {
try {
license = readLicense();
} catch (IOException ioex) {
ioex.printStackTrace();
}
}
return license;
}
private static String generateWrapperException(File dir, Class jdiException) throws IOException {
String name = jdiException.getSimpleName();
String cName = name + "Wrapper";
String eName = jdiException.getName();
File cf = new File(dir, cName+".java");
Writer w = new BufferedWriter(new FileWriter(cf));
w.write(getLicense());
w.write("\npackage "+PACKAGE+";\n\n");
w.write("/**\n * Wrapper for "+name+" JDI exception.\n * The calling code must count with this exception being thrown.\n */\n");
w.write("public final class "+cName+" extends Exception {\n");
w.write("\n public "+cName+"("+eName+" ex) {\n");
w.write(" super(ex);\n");
w.write(" }\n\n");
w.write(" @Override\n");
w.write(" public "+eName+" getCause() {\n");
w.write(" return ("+eName+") super.getCause();\n");
w.write(" }\n\n");
w.write("}\n");
w.close();
return PACKAGE+"."+cName;
}
private static List<Class> getJDIClasses() throws IOException {
List<Class> classes = new ArrayList<Class>();
URL resource = ClassLoader.getSystemClassLoader().getResource("com/sun/jdi/VirtualMachine.class");
String jarFileName = resource.getFile();
jarFileName = jarFileName.substring("file:".length(), jarFileName.indexOf('!'));
ZipFile jar = new ZipFile(jarFileName);
Enumeration<? extends ZipEntry> classEntries = jar.entries();
while (classEntries.hasMoreElements()) {
ZipEntry classEntry = classEntries.nextElement();
String name = classEntry.getName();
if (name.startsWith(JDI_CLASSES_PATH) && name.endsWith(".class")) {
String className = name.substring(0, name.length() - ".class".length()).replace('/', '.');
//className = className.replace('$', '.');
//System.err.println("Have class from JAR: '"+className+"'");
Class c;
try {
c = Class.forName(className);
} catch (ClassNotFoundException ex) {
Logger.getLogger(Generate.class.getName()).log(Level.SEVERE, null, ex);
continue;
}
if (Throwable.class.isAssignableFrom(c)) {
continue;
}
classes.add(c);
}
}
Collections.sort(classes, new Comparator<Class>() {
public int compare(Class o1, Class o2) {
return o1.getName().compareTo(o2.getName());
}
});
return classes;
}
private static void generate(File dir) throws IOException {
List<Class> classes = getJDIClasses();
for (Map<String, Set<Class>> map : EXCEPTIONS_BY_METHODS.values()) {
for (Set<Class> set : map.values()) {
for (Class ex : set) {
if (!EXCEPTION_WRAPPERS.containsKey(ex)) {
EXCEPTION_WRAPPERS.put(ex, generateWrapperException(dir, ex));
}
}
}
}
EXCEPTION_WRAPPERS.put(com.sun.jdi.ObjectCollectedException.class, generateWrapperException(dir, com.sun.jdi.ObjectCollectedException.class));
// Add classes and methods that are in JDK 1.6 and higher versions and generate reflection calls
String jdkVersion = System.getProperty("java.version");
System.err.println("jdkVersion = "+jdkVersion);
File rootResource;
try {
rootResource = new File(Generate.class.getResource("").toURI());
} catch (Exception ex) {
IOException ioex = new IOException();
ioex.initCause(ex);
throw ioex;
}
File jdkLogFile = new File(rootResource, METHODS_BY_JDK+jdkVersion);
Writer log = new BufferedWriter(new FileWriter(jdkLogFile));
// Information about all JDI methods of the current JDK version are stored
// in the following form:
// <class name>:<wrapper class simple name> [isObjectReference]
// <method name>(<generic parameter types>):<generic return type>:<default return value> throws <exception classes>
Map<String, List<String>> higherVersionMethods = getHigherVersionMethods(rootResource, jdkVersion);
int[] higherVersionMethodIndexes = new int[higherVersionMethods.size()];
Set<String> higherVersionClasses = new HashSet<String>();
int[] indexes = new int[higherVersionMethods.size()];
for (Class c : classes) {
if (c.getDeclaredMethods().length == 0) {
continue;
}
int ii = 0;
for (Iterator<String> it = higherVersionMethods.keySet().iterator(); it.hasNext(); ii++) {
String version = it.next();
List<String> methodsLog = higherVersionMethods.get(version);
int i;
for (i = indexes[ii]; i < methodsLog.size(); i++) {
String loggedClass = methodsLog.get(i);
if (loggedClass.startsWith(" ")) continue;
int colonIndex = loggedClass.indexOf(':');
if (!loggedClass.substring(0, colonIndex).equals(c.getName())) {
higherVersionClasses.add(loggedClass.substring(0, colonIndex).replace('$', '.'));
} else {
i++;
break;
}
}
indexes[ii] = i;
}
}
System.out.println("\nHigher version classes: "+higherVersionClasses+"\n");
for (Class c : classes) {
String name = c.getSimpleName();
String className = c.getName().replace('$', '.');
String classPackage = c.getPackage().getName();
String cName;
Class enclosingClass = c.getEnclosingClass();
if (enclosingClass != null) {
cName = enclosingClass.getSimpleName() + name + "Wrapper";
name = enclosingClass.getSimpleName() + "." + name;
} else {
cName = name + "Wrapper";
}
Method[] methods = c.getDeclaredMethods();
if (methods.length == 0) {
System.err.println("Class: "+c.getName());
System.err.println(" - ignored, have no methods.");
continue;
}
writeHigherVersionClasses(dir, c, higherVersionMethods, higherVersionMethodIndexes, higherVersionClasses);
System.err.println("Class: "+c.getName());
Arrays.sort(methods, new Comparator<Method>() {
public int compare(Method m1, Method m2) {
int c = m1.getName().compareTo(m2.getName());
if (c == 0) {
StringBuilder p1 = new StringBuilder();
for (Class pt : m1.getParameterTypes()) {
p1.append(pt.getName());
p1.append(" ");
}
StringBuilder p2 = new StringBuilder();
for (Class pt : m2.getParameterTypes()) {
p2.append(pt.getName());
p2.append(" ");
}
c = p1.toString().compareTo(p2.toString());
}
return c;
}
});
log.write(c.getName()+":"+cName+"\n");
Writer w;
if (NOT_USED_CLASSES.contains(c.getName()) || c.getName().startsWith("com.sun.jdi.connect")) {
w = null;
} else {
w = writeClassHeader(dir, name, classPackage, cName, null);
}
for (Method m : methods) {
writeHigherVersionMethods(w, m, className, higherVersionMethods, higherVersionMethodIndexes, higherVersionClasses);
String mName = m.getName();
Type[] paramTypes = m.getGenericParameterTypes();
String rType = translateType(m.getGenericReturnType());
String defaultReturn = getDefaultReturn(m.getReturnType());
Class[] exceptionTypes = m.getExceptionTypes();
System.err.println(" Method: "+mName);
logMethod(log, mName, paramTypes, exceptionTypes, rType, defaultReturn);
if (w != null) {
if (defaultReturn != null) {
writeMethod(w, c, className, mName, mName+"0", paramTypes, exceptionTypes, rType, defaultReturn);
}
writeMethod(w, c, className, mName, mName, paramTypes, exceptionTypes, rType, null);
}
}
writeHigherVersionMethods(w, null, className, higherVersionMethods, higherVersionMethodIndexes, higherVersionClasses);
if (w != null) {
w.write("}\n");
w.close();
}
}
writeHigherVersionClasses(dir, null, higherVersionMethods, higherVersionMethodIndexes, higherVersionClasses);
log.close();
}
private static Writer writeClassHeader(File dir, String name, String classPackage, String cName, String jdkVersion) throws IOException {
String classDir = classPackage.replace('.', '/').substring(JDI_CLASSES_PATH.length());
if (classDir.startsWith("/")) classDir = classDir.substring(1);
File cDir = (classDir.length() == 0) ? dir : new File(dir, classDir);
cDir.mkdirs();
File cf = new File(cDir, cName+".java");
Writer w = new BufferedWriter(new FileWriter(cf));
String cPackage = classPackage.substring(JDI_CLASSES_PATH.length());
w.write(getLicense());
w.write("\npackage "+PACKAGE+cPackage+";\n\n");
w.write("// DO NOT MODIFY THIS CODE, GENERATED AUTOMATICALLY\n");
w.write("// Generated by "+Generate.class.getName()+" class located in 'gensrc' folder,\n// perform the desired modifications there and re-generate by \"ant generate\".\n\n");
w.write("/**\n * Wrapper for "+name+" JDI class");
if (jdkVersion != null) {
w.write(" from JDK ");
w.write(jdkVersion);
}
w.write(".\n * Use methods of this class instead of direct calls on JDI objects.\n"+
" * These methods assure that exceptions thrown from JDI calls are handled appropriately.\n"+
" *\n * @author Martin Entlicher\n */\n");
w.write("public final class "+cName+" {\n");
w.write("\n private "+cName+"() {}\n\n");
return w;
}
private static void logMethod(Writer log, String mName,
Type[] paramTypes, Class[] exceptionTypes,
String rType, String defaultReturn) throws IOException {
log.write(" "+mName+"(");
for (int i = 0; i < paramTypes.length; i++) {
String paramType = translateType(paramTypes[i]);
if (i > 0) log.write(", ");
log.write(paramType);
}
log.write("):"+rType+":"+((defaultReturn != null) ? defaultReturn : ""));
if (exceptionTypes.length > 0) {
log.write(" throws ");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i > 0) log.write(", ");
log.write(exceptionTypes[i].getName());
}
}
log.write("\n");
}
private static void writeMethod(Writer w, Class c, String className,
String mName, String mGenName, Type[] paramTypes, Class[] exceptionTypes,
String rType, String defaultReturn) throws IOException {
w.write(" // DO NOT MODIFY THIS CODE, GENERATED AUTOMATICALLY\n");
w.write(" public static "+rType+" "+mGenName+"("+className+" a");
String[] paramNames = new String[paramTypes.length];
StringBuilder paramTypesList = new StringBuilder("(");
for (int i = 0; i < paramTypes.length; i++) {
//Class t = paramTypes[i];
paramNames[i] = Character.toString((char) ('a'+(i+1)));
String paramType = translateType(paramTypes[i]);
w.write(", "+paramType+" "+paramNames[i]);
if (i > 0) paramTypesList.append(", ");
paramTypesList.append(paramType);
}
w.write(")");
paramTypesList.append(")");
String mNameWithParamTypes = mName + paramTypesList.toString();
// Add wrappers of JDI runtime exceptions...
Set<Class> thrownExceptions = new LinkedHashSet<Class>();
thrownExceptions.addAll(EXCEPTIONS_BY_METHODS.get("*").get("*"));
if (com.sun.jdi.Value.class.isAssignableFrom(c) &&
!com.sun.jdi.PrimitiveValue.class.isAssignableFrom(c)) {
thrownExceptions.add(com.sun.jdi.ObjectCollectedException.class);
}
Map<String, Set<Class>> excByMethods = EXCEPTIONS_BY_METHODS.get(c.getName());
if (excByMethods != null) {
Set<Class> excs = excByMethods.get("*");
if (excs != null) {
thrownExceptions.addAll(excs);
}
excs = excByMethods.get(mNameWithParamTypes);
if (excs == null) {
excs = excByMethods.get(mName);
}
if (excs != null) {
thrownExceptions.addAll(excs);
}
}
Set<Class> caughtExceptions = new LinkedHashSet<Class>(thrownExceptions);
if (defaultReturn != null) thrownExceptions.removeAll(SILENT_EXCEPTIONS);
if (exceptionTypes.length > 0 || thrownExceptions.size() > 0) {
w.write(" throws ");
for (int i = 0; i < exceptionTypes.length; i++) {
if (i > 0) w.write(", ");
w.write(exceptionTypes[i].getName());
}
if (exceptionTypes.length > 0 && thrownExceptions.size() > 0) {
w.write(", ");
}
int i = 0;
for (Iterator it = thrownExceptions.iterator(); it.hasNext(); i++) {
if (i > 0) w.write(", ");
w.write(EXCEPTION_WRAPPERS.get(it.next()));
}
}
w.write(" {\n");
String preCode = methodPreCode(className, mName);
if (preCode != null) {
w.write(preCode);
}
w.write(" if (org.netbeans.modules.debugger.jpda.JDIExceptionReporter.isLoggable()) {\n");
w.write(" org.netbeans.modules.debugger.jpda.JDIExceptionReporter.logCallStart(\n"+
" \""+className+"\",\n"+
" \""+mName+"\",\n"+
" \"JDI CALL: "+className+"({0})."+mName+"(");
for (int i = 0; i < paramNames.length; i++) {
if (i > 0) {
w.write(", ");
}
w.write("{"+Integer.toString(i+1)+"}");
}
w.write(")\",\n"+
" new Object[] {a");
for (int i = 0; i < paramNames.length; i++) {
w.write(", ");
w.write(paramNames[i]);
}
w.write("});\n");
w.write(" }\n"); // if
boolean isVoidReturn = "void".equals(rType);
if (!isVoidReturn) {
w.write(" Object retValue = null;\n");
}
w.write(" try {\n");
StringBuffer exec = new StringBuffer();
if (!isVoidReturn) {
exec.append(" ").append(rType).append(" ret;\n");
exec.append(" ret = ");
} else {
exec.append(" ");
}
exec.append("a."+mName+"(");
for (int i = 0; i < paramNames.length; i++) {
if (i > 0) {
exec.append(", ");
}
exec.append(paramNames[i]);
}
exec.append(");\n");
if (!isVoidReturn) {
exec.append(" retValue = ret;\n");
exec.append(" return ret;\n");
}
w.write(methodImpl(className, mName, exec.toString()));
w.write(" }");
/*// First re-throw the checked exceptions:
for (int i = 0; i < exceptionTypes.length; i++) {
w.write(exceptionTypes[i].getName());
w.write(" ex) {\n");
w.write(" throw ex;\n");
w.write(" } catch (");
}*/
for (Iterator<Class> it = caughtExceptions.iterator(); it.hasNext(); ) {
Class cex = it.next();
w.write(" catch (");
w.write(cex.getName());
w.write(" ex) {\n");
if (!isVoidReturn) {
w.write(" retValue = ex;\n");
}
if (com.sun.jdi.InternalException.class.equals(cex)) {
w.write(reportOfJDIException(className, mName, "ex"));
}
if (com.sun.jdi.VMDisconnectedException.class.equals(cex)) {
w.write(disposeVMOnVMDisconnectedException(className, mName));
}
if (defaultReturn != null && SILENT_EXCEPTIONS.contains(cex)) {
w.write(" return "+defaultReturn+";\n");
} else {
w.write(" throw new "+EXCEPTION_WRAPPERS.get(cex)+"(ex);\n");
}
w.write(" }");
}
if (!isVoidReturn) {
for (int i = 0; i < exceptionTypes.length; i++) {
w.write(" catch (");
w.write(exceptionTypes[i].getName());
w.write(" ex) {\n");
w.write(" retValue = ex;\n");
w.write(" throw ex;\n }");
}
w.write(" catch (Error err) {\n");
w.write(" retValue = err;\n");
w.write(" throw err;\n");
w.write(" } catch (RuntimeException rex) {\n");
w.write(" retValue = rex;\n");
w.write(" throw rex;\n }");
}
w.write(" finally {\n");
w.write(" if (org.netbeans.modules.debugger.jpda.JDIExceptionReporter.isLoggable()) {\n");
w.write(" org.netbeans.modules.debugger.jpda.JDIExceptionReporter.logCallEnd(\n"+
" \""+className+"\",\n"+
" \""+mName+"\",\n"+
(isVoidReturn ?
" org.netbeans.modules.debugger.jpda.JDIExceptionReporter.RET_VOID);\n" :
" retValue);\n"));
//w.write(" logger.log(java.util.logging.Level.FINER, \" returned after {0} ns\", (t2 - t1));\n");
w.write(" }\n");
w.write(" }\n");
w.write(" }\n\n");
}
public static void writeHigherVersionClasses(File dir, Class c,
Map<String, List<String>> higherVersionMethods,
int[] higherVersionMethodIndexes,
Set<String> higherVersionClasses) throws IOException {
int versionIndex = 0;
for (Iterator<String> it = higherVersionMethods.keySet().iterator(); it.hasNext(); versionIndex++) {
String version = it.next();
List<String> methodsLog = higherVersionMethods.get(version);
while (higherVersionMethodIndexes[versionIndex] < methodsLog.size()) {
String loggedClass = methodsLog.get(higherVersionMethodIndexes[versionIndex]++);
int colonIndex = loggedClass.indexOf(':');
if (c == null || !loggedClass.substring(0, colonIndex).equals(c.getName())) {
String loggedClassBinaryName = loggedClass.substring(0, colonIndex);
int lastDotIndex = loggedClassBinaryName.lastIndexOf('.');
String loggedClassPackage = loggedClassBinaryName.substring(0, lastDotIndex);
String loggedClassName = loggedClassBinaryName.replace('$', '.');
String loggedName = loggedClassName.substring(loggedClassName.lastIndexOf('.') + 1);
System.err.println("Have class: "+loggedName+" from JDK "+version);
Writer w;
if (NOT_USED_CLASSES.contains(loggedClassBinaryName) || loggedClassBinaryName.startsWith("com.sun.jdi.connect")) {
w = null;
} else {
w = writeClassHeader(dir, loggedName, loggedClassPackage, loggedClass.substring(colonIndex + 1), version);
}
int i;
for (i = higherVersionMethodIndexes[versionIndex]; i < methodsLog.size(); i++) {
String method = methodsLog.get(i);
if (!method.startsWith(" ")) {
break;
}
if (w != null) {
writeHigherVersionMethod(w, loggedClassName, method, version, higherVersionClasses);
}
}
higherVersionMethodIndexes[versionIndex] = i;
if (w != null) {
w.write("}\n");
w.close();
}
} else {
break;
}
}
}
}
private static void writeHigherVersionMethods(Writer w, Method m,
String className,
Map<String, List<String>> higherVersionMethods,
int[] higherVersionMethodIndexes,
Set<String> higherVersionClasses) throws IOException {
//System.err.println("writeHigherVersionMethods("+m+")");
int versionIndex = 0;
for (Iterator<String> it = higherVersionMethods.keySet().iterator(); it.hasNext(); versionIndex++) {
String version = it.next();
List<String> methodsLog = higherVersionMethods.get(version);
while (higherVersionMethodIndexes[versionIndex] < methodsLog.size()) {
String loggedMethod = methodsLog.get(higherVersionMethodIndexes[versionIndex]);
if (loggedMethod.startsWith(" ")) {
higherVersionMethodIndexes[versionIndex]++;
if (w != null && (m == null || !isLoggedMethod(m, loggedMethod))) {
//System.out.println(" Method "+loggedMethod.substring(1, loggedMethod.indexOf("):") + 1)+" from JDK "+version);
writeHigherVersionMethod(w, className, loggedMethod, version, higherVersionClasses);
} else {
break;
}
} else {
break;
}
};
}
}
private static boolean isLoggedMethod(Method m, String loggedMethod) {
String mName = m.getName();
Type[] paramTypes = m.getGenericParameterTypes();
StringBuilder mLog = new StringBuilder(" ");
mLog.append(mName);
mLog.append("(");
for (int i = 0; i < paramTypes.length; i++) {
String paramType = translateType(paramTypes[i]);
if (i > 0) mLog.append(", ");
mLog.append(paramType);
}
mLog.append("):");
return loggedMethod.startsWith(mLog.toString());
}
private static void writeHigherVersionMethod(Writer w, String className,
String methodLine, String jdkVersion,
Set<String> higherVersionClasses) throws IOException {
methodLine = methodLine.trim();
System.err.println(" Method: "+methodLine+" from JDK "+jdkVersion);
int index = methodLine.indexOf('(');
String mName = methodLine.substring(0, index);
List<String> paramTypes = new ArrayList<String>();
index++;
int index2 = index;
char c;
while ((c = methodLine.charAt(index2)) != ')') {
if (c == ',') {
paramTypes.add(substituteHigherClasses(methodLine.substring(index, index2), higherVersionClasses));
index = index2 + 2; // ", "
index2 = index;
} else if (c == '<') {
index2 = findPair(methodLine, index2 + 1, '<', '>');
} else {
index2++;
}
//System.err.println(" c = "+c+", index = "+index+", index2 = "+index2);
}
if (index2 > index) {
paramTypes.add(substituteHigherClasses(methodLine.substring(index, index2), higherVersionClasses));
}
String mNameWithParamTypes = methodLine.substring(0, index2 + 1);
index = index2 + 2; // "):"
index2 = methodLine.indexOf(":", index);
String rType = methodLine.substring(index, index2);
rType = substituteHigherClasses(rType, higherVersionClasses);
index = index2 + 1; // ":"
index2 = methodLine.indexOf(" throws ", index);
String defaultReturn;
List<String> exceptionTypes;
if (index2 < 0) {
defaultReturn = methodLine.substring(index).trim();
exceptionTypes = Collections.emptyList();
} else {
defaultReturn = methodLine.substring(index, index2).trim();
index = index2 + " throws ".length();
exceptionTypes = new ArrayList<String>();
for (index2 = index; index2 < methodLine.length(); index2++) {
if (methodLine.charAt(index2) == ',') {
exceptionTypes.add(methodLine.substring(index, index2).trim());
index = index2 + 2; // ", "
index2 = index;
}
}
exceptionTypes.add(methodLine.substring(index).trim());
}
if (defaultReturn.length() == 0) {
defaultReturn = null;
}
Set<Class> thrownExceptions = new LinkedHashSet<Class>();
thrownExceptions.addAll(EXCEPTIONS_BY_METHODS.get("*").get("*"));
//if (com.sun.jdi.ObjectReference.class.isAssignableFrom(c)) {
// thrownExceptions.add(com.sun.jdi.ObjectCollectedException.class);
//}
Map<String, Set<Class>> excByMethods = EXCEPTIONS_BY_METHODS.get(className);
if (excByMethods != null) {
Set<Class> excs = excByMethods.get("*");
if (excs != null) {
thrownExceptions.addAll(excs);
}
excs = excByMethods.get(mNameWithParamTypes);
if (excs == null) {
excs = excByMethods.get(mName);
}
if (excs != null) {
thrownExceptions.addAll(excs);
}
}
className = substituteHigherClasses(className, higherVersionClasses);
Set<Class> caughtExceptions = new LinkedHashSet<Class>(thrownExceptions);
writeHigherVersionMethod(w, className, mName, mName, paramTypes, exceptionTypes, thrownExceptions, caughtExceptions, rType, null, jdkVersion);
if (defaultReturn != null) {
thrownExceptions.removeAll(SILENT_EXCEPTIONS);
writeHigherVersionMethod(w, className, mName, mName+"0", paramTypes, exceptionTypes, thrownExceptions, caughtExceptions, rType, defaultReturn, jdkVersion);
}
}
private static void writeHigherVersionMethod(Writer w, String className,
String mName, String mGenName,
List<String> paramTypes, List<String> exceptionTypes,
Set<Class> thrownExceptions, Set<Class> caughtExceptions,
String rType, String defaultReturn,
String jdkVersion) throws IOException {
w.write(" // DO NOT MODIFY THIS CODE, GENERATED AUTOMATICALLY\n");
w.write(" /** Wrapper for method "+mName+" from JDK "+jdkVersion+". */\n");
w.write(" public static "+rType+" "+mGenName+"("+className+" a");
String[] paramNames = new String[paramTypes.size()];
for (int i = 0; i < paramTypes.size(); i++) {
paramNames[i] = Character.toString((char) ('a'+(i+1)));
String paramType = paramTypes.get(i);
w.write(", "+paramType+" "+paramNames[i]);
}
w.write(")");
if (exceptionTypes.size() > 0 || thrownExceptions.size() > 0) {
w.write(" throws ");
for (int i = 0; i < exceptionTypes.size(); i++) {
if (i > 0) w.write(", ");
w.write(exceptionTypes.get(i));
}
if (exceptionTypes.size() > 0 && thrownExceptions.size() > 0) {
w.write(", ");
}
int i = 0;
for (Iterator it = thrownExceptions.iterator(); it.hasNext(); i++) {
if (i > 0) w.write(", ");
w.write(EXCEPTION_WRAPPERS.get(it.next()));
}
}
w.write(" {\n");
String higherVersionClass;
String higherVersionClassName;
int higherVersionClassIndex = className.indexOf("/*");
if (higherVersionClassIndex > 0) {
higherVersionClassIndex += 2;
higherVersionClassName = className.substring(higherVersionClassIndex, className.indexOf("*/"));
higherVersionClass = higherVersionClassName.substring(higherVersionClassName.lastIndexOf('.') + 1);
higherVersionClass = Character.toLowerCase(higherVersionClass.charAt(0)) + higherVersionClass.substring(1) + "Class";
w.write(" Class "+higherVersionClass+";\n"+
" try {\n"+
" "+higherVersionClass+" = org.openide.util.Lookup.getDefault().lookup(ClassLoader.class).loadClass(\""+higherVersionClassName+"\");\n"+
" } catch (ClassNotFoundException ex) {\n"+
" throw new IllegalStateException(ex);\n"+
" }\n");
} else {
higherVersionClass = null;
higherVersionClassName = null;
}
w.write(" if (org.netbeans.modules.debugger.jpda.JDIExceptionReporter.isLoggable()) {\n");
w.write(" org.netbeans.modules.debugger.jpda.JDIExceptionReporter.logCallStart(\n"+
" \""+className+"\",\n"+
" \""+mName+"\",\n"+
" \"JDI CALL: "+className+"({0})."+mName+"(");
for (int i = 0; i < paramNames.length; i++) {
if (i > 0) {
w.write(", ");
}
w.write("{"+Integer.toString(i+1)+"}");
}
w.write(")\",\n"+
" new Object[] {a");
for (int i = 0; i < paramNames.length; i++) {
w.write(", ");
w.write(paramNames[i]);
}
w.write("});\n");
w.write(" }\n"); // if
boolean isVoidReturn = "void".equals(rType);
if (!isVoidReturn) {
w.write(" Object retValue = null;\n");
}
w.write(" try {\n");
StringBuffer exec = new StringBuffer();
if (!isVoidReturn) {
if ("boolean".equals(rType)) rType = "Boolean";
if ("int".equals(rType)) rType = "Integer";
if ("long".equals(rType)) rType = "Long";
exec.append(" ").append(rType).append(" ret;\n");
exec.append(" ret = (").append(rType).append(") ");
} else {
exec.append(" ");
}
// Use reflection to invoke method from higher JDK version
/* instead of:
w.write("a."+mName+"(");
for (int i = 0; i < paramNames.length; i++) {
if (i > 0) {
w.write(", ");
}
w.write(paramNames[i]);
}
w.write(");\n");
use reflection: */
if (higherVersionClass != null) {
exec.append(higherVersionClass+".getMethod(\""+mName+"\"");
} else {
exec.append(className+".class.getMethod(\""+mName+"\"");
}
for (int i = 0; i < paramNames.length; i++) {
exec.append(", ");
String type = paramTypes.get(i);
if (type.indexOf('<') > 0) type = type.substring(0, type.indexOf('<'));
exec.append(type);
exec.append(".class");
}
exec.append(").invoke(a");
for (int i = 0; i < paramNames.length; i++) {
exec.append(", ");
exec.append(paramNames[i]);
}
exec.append(");\n");
if (!isVoidReturn) {
exec.append(" retValue = ret;\n");
exec.append(" return ret;\n");
}
w.write(methodImpl(className, mName, exec.toString()));
w.write(" } catch (NoSuchMethodException ex) {\n");
if (!isVoidReturn) {
w.write(" retValue = ex;\n");
}
w.write(" throw new IllegalStateException(ex);\n");
w.write(" } catch (SecurityException ex) {\n");
if (!isVoidReturn) {
w.write(" retValue = ex;\n");
}
w.write(" throw new IllegalStateException(ex);\n");
w.write(" } catch (IllegalAccessException ex) {\n");
if (!isVoidReturn) {
w.write(" retValue = ex;\n");
}
w.write(" throw new IllegalStateException(ex);\n");
w.write(" } catch (IllegalArgumentException ex) {\n");
if (!isVoidReturn) {
w.write(" retValue = ex;\n");
}
w.write(" throw new IllegalStateException(ex);\n");
w.write(" } catch (java.lang.reflect.InvocationTargetException ex) {\n");
w.write(" Throwable t = ex.getTargetException();\n");
if (!isVoidReturn) {
w.write(" retValue = t;\n");
}
// First re-throw the checked exceptions:
for (int i = 0; i < exceptionTypes.size(); i++) {
w.write(" if (t instanceof "+exceptionTypes.get(i)+") {\n");
//w.write(" ex) {\n");
w.write(" throw ("+exceptionTypes.get(i)+") t;\n");
w.write(" }\n");
//w.write(" } catch (");
}
for (Iterator<Class> it = caughtExceptions.iterator(); it.hasNext(); ) {
Class cex = it.next();
w.write(" if (t instanceof "+cex.getName()+") {\n");
if (com.sun.jdi.InternalException.class.equals(cex)) {
String c = higherVersionClassName;
if (c == null) c = className;
w.write(" "+reportOfJDIException(c, mName, "("+com.sun.jdi.InternalException.class.getName()+") t"));
}
if (defaultReturn != null && SILENT_EXCEPTIONS.contains(cex)) {
w.write(" return "+defaultReturn+";\n");
} else {
w.write(" throw new "+EXCEPTION_WRAPPERS.get(cex)+"(("+cex.getName()+") t);\n");
}
w.write(" }\n");
//w.write(" } catch (");
}
w.write(" throw new IllegalStateException(t);\n");
w.write(" }");
w.write(" finally {\n");
w.write(" if (org.netbeans.modules.debugger.jpda.JDIExceptionReporter.isLoggable()) {\n");
w.write(" org.netbeans.modules.debugger.jpda.JDIExceptionReporter.logCallEnd(\n"+
" \""+className+"\",\n"+
" \""+mName+"\",\n"+
(isVoidReturn ?
" org.netbeans.modules.debugger.jpda.JDIExceptionReporter.RET_VOID);\n" :
" retValue);\n"));
w.write(" }\n");
w.write(" }\n");
w.write(" }\n\n");
}
private static Map<String, List<String>> getHigherVersionMethods(File rootResource, String jdkVersion) throws IOException {
Map<String, List<String>> map = new LinkedHashMap<String, List<String>>();
for (String resourceName : rootResource.list()) {
if (resourceName.startsWith(METHODS_BY_JDK)) {
String version = resourceName.substring(METHODS_BY_JDK.length());
if (version.compareTo(jdkVersion) > 0) {
map.put(version, readMethodsLog(new File(rootResource, resourceName)));
}
}
}
return map;
}
private static List<String> readMethodsLog(File resource) throws IOException {
List<String> list = new ArrayList<String>();
BufferedReader log = new BufferedReader(new FileReader(resource));
String line;
while ((line = log.readLine()) != null) {
list.add(line);
}
log.close();
return list;
}
private static String substituteHigherClasses(String type, Set<String> higherVersionClasses) {
int l = type.length();
for (String higherVersionClass : higherVersionClasses) {
int i0 = 0;
int i1;
StringBuilder sb = null;
while ((i1 = type.indexOf(higherVersionClass, i0)) >= 0) {
int i2 = i1 + higherVersionClass.length();
if (i2 == l || type.charAt(i2) == ',' || type.charAt(i2) == '>') {
if (sb == null) {
sb = new StringBuilder(type.substring(i0, i1));
}
sb.append("Object/*");
sb.append(higherVersionClass);
sb.append("*/");
}
i0 = i2;
}
if (sb != null) {
sb.append(type.substring(i0));
type = sb.toString();
}
}
return type;
}
private static String translateType(Type t) {
if (t instanceof Class) {
Class ct = ((Class) t).getComponentType();
if (ct != null) {
return translateType(ct)+"[]";
}
return ((Class) t).getName().replace('$', '.');
}
return t.toString().replace('$', '.');
}
private static String getDefaultReturn(Class returnType) {
if (Integer.TYPE.equals(returnType)) {
return "0";
}
if (Boolean.TYPE.equals(returnType)) {
return "false";
}
if (java.util.List.class.equals(returnType)) {
return "java.util.Collections.emptyList()";
}
if (java.util.Set.class.equals(returnType)) {
return "java.util.Collections.emptySet()";
}
if (java.util.Map.class.equals(returnType)) {
return "java.util.Collections.emptyMap()";
}
return null;
}
private static int findPair(String text, int index, char co, char cc) {
int l = text.length();
int ci = 1; // Expecting that opening character was already
while (index < l) {
char c = text.charAt(index);
if (c == co) ci++;
if (c == cc) ci--;
if (ci != 0) index++;
else break;
}
if (index < l) return index;
else return -1;
}
private static String methodPreCode(String className, String methodName) {
if (com.sun.jdi.ThreadReference.class.getName().equals(className)) {
if (methodName.equals("currentContendedMonitor")) {
String statusCheck = " int status = status0(a);\n"+
" if (status == com.sun.jdi.ThreadReference.THREAD_STATUS_NOT_STARTED ||\n" +
" status == com.sun.jdi.ThreadReference.THREAD_STATUS_ZOMBIE ||\n" +
" status == com.sun.jdi.ThreadReference.THREAD_STATUS_UNKNOWN) {\n" +
" \n" +
" // #253058, JDWP Error: 15\n" +
" return null;\n" +
" }\n";
return statusCheck;
}
}
return null;
}
// Custom code can be provided here to override the original invocation
private static String methodImpl(String className, String methodName, String exec) {
if (com.sun.jdi.ThreadReference.class.getName().equals(className)) {
if (methodName.equals("popFrames")) {
String catchJDWPException = " try {\n"+
" "+exec+
" } catch ("+com.sun.jdi.InternalException.class.getName()+" iex) {\n"+
" if (iex.errorCode() == 32) { // OPAQUE_FRAME\n"+
" // "+com.sun.jdi.NativeMethodException.class.getSimpleName()+" should be thrown here!\n"+
" throw new "+com.sun.jdi.NativeMethodException.class.getName()+"(iex.getMessage());\n"+
" } else {\n"+
" throw iex; // re-throw the original\n"+
" }\n"+
" }\n";
return catchJDWPException;
}
if (methodName.equals("currentContendedMonitor")) {
String catchJDWPException = " try {\n"+
" "+exec+
" } catch ("+com.sun.jdi.InternalException.class.getName()+" iex) {\n"+
" if (iex.errorCode() == 13) { // THREAD_NOT_SUSPENDED\n"+
" // "+com.sun.jdi.IncompatibleThreadStateException.class.getSimpleName()+" should be thrown here!\n"+
" throw new "+com.sun.jdi.IncompatibleThreadStateException.class.getName()+"(iex.getMessage());\n"+
" } else {\n"+
" throw iex; // re-throw the original\n"+
" }\n"+
" }\n";
return catchJDWPException;
}
if (methodName.equals("frame")) {
String catchNPE = " try {\n"+
" "+exec+
" } catch ("+NullPointerException.class.getName()+" npex) {\n"+
" // See http://www.netbeans.org/issues/show_bug.cgi?id=159887\n"+
" throw new "+com.sun.jdi.IncompatibleThreadStateException.class.getName()+"(npex.getMessage());\n"+
" }\n";
return catchNPE;
}
if (methodName.equals("resume")) {
String catchJDWPException = " try {\n"+
" "+exec+
" } catch ("+com.sun.jdi.InternalException.class.getName()+" iex) {\n"+
" if (iex.errorCode() == 13) { // THREAD_NOT_SUSPENDED\n"+
" // Ignore, as we're resuming the thread.\n"+
" } else {\n"+
" throw iex; // re-throw the original\n"+
" }\n"+
" }\n";
return catchJDWPException;
}
}
if (com.sun.jdi.ReferenceType.class.getName().equals(className) && methodName.equals("constantPool")) {
String catchNPE = " try {\n"+
" "+exec+
" } catch (NullPointerException ex) {\n"+
" // JDI defect http://bugs.sun.com/view_bug.do?bug_id=6822627\n"+
" return null;\n"+
" }\n";
return catchNPE;
}
if (com.sun.jdi.StackFrame.class.getName().equals(className) && methodName.equals("thisObject")) {
String catchJDWPException = " try {\n"+
" "+exec+
" } catch ("+com.sun.jdi.InternalException.class.getName()+" iex) {\n"+
" if (iex.errorCode() == 35) { // INVALID_SLOT, see http://www.netbeans.org/issues/show_bug.cgi?id=163652\n"+
" return null;\n"+
" } else {\n"+
" throw iex; // re-throw the original\n"+
" }\n"+
" }\n";
return catchJDWPException;
}
if (com.sun.jdi.Location.class.getName().equals(className)) {
if (methodName.equals("sourcePath") || methodName.equals("sourceName")) {
String catchJDWPException = " try {\n"+
" "+exec+
" } catch ("+com.sun.jdi.InternalException.class.getName()+" iex) {\n"+
" if (iex.errorCode() == 101) { // ABSENT_INFORMATION\n"+
" throw new com.sun.jdi.AbsentInformationException(iex.getMessage());\n"+
" } else {\n"+
" throw iex; // re-throw the original\n"+
" }\n"+
" }\n";
return catchJDWPException;
}
if (methodName.equals("lineNumber")) {
String catchJDWPException = " try {\n"+
" "+exec+
" } catch ("+com.sun.jdi.InternalException.class.getName()+" iex) {\n"+
" if (iex.errorCode() == 101) { // ABSENT_INFORMATION\n"+
" return -1;\n"+
" } else {\n"+
" throw iex; // re-throw the original\n"+
" }\n"+
" }\n";
return catchJDWPException;
}
}
if (com.sun.jdi.ClassType.class.getName().equals(className) || com.sun.jdi.ObjectReference.class.getName().equals(className)) {
if (methodName.equals("invokeMethod")) {
String catchJDWPException = " try {\n"+
" "+exec+
" } catch ("+com.sun.jdi.InternalException.class.getName()+" iex) {\n"+
" if (iex.errorCode() == 502) { // ALREADY_INVOKING\n"+
" iex = ("+com.sun.jdi.InternalException.class.getName()+") org.openide.util.Exceptions.attachLocalizedMessage(iex, org.openide.util.NbBundle.getMessage(org.netbeans.modules.debugger.jpda.JPDADebuggerImpl.class, \"JDWPError502\"));\n"+
" org.openide.util.Exceptions.printStackTrace(iex);\n"+
" return null;\n"+
" } else {\n"+
" throw iex; // re-throw the original\n"+
" }\n"+
" }\n";
return catchJDWPException;
}
}
if (com.sun.jdi.Method.class.getName().equals(className)) {
if (methodName.equals("bytecodes")) {
String catchJDWPException = " try {\n"+
" "+exec+
" } catch ("+com.sun.jdi.InternalException.class.getName()+" iex) {\n"+
" if (iex.errorCode() == 113) { // INTERNAL, see https://netbeans.org/bugzilla/show_bug.cgi?id=255298\n"+
" throw new InternalExceptionWrapper(iex);\n"+
" } else {\n"+
" throw iex; // re-throw the original\n"+
" }\n"+
" }\n";
return catchJDWPException;
}
}
return exec;
}
private static final String JDI_EXC_REPORT1 =
" org.netbeans.modules.debugger.jpda.JDIExceptionReporter.report(";
private static final String JDI_EXC_REPORT2 =
");\n";
private static String reportOfJDIException(String className, String methodName, String ex) {
if (com.sun.jdi.Method.class.getName().equals(className) && methodName.equals("bytecodes")) {
return " if (ex.errorCode() != 101) { // Ignore ABSENT_INFORMATION\n"+
" "+JDI_EXC_REPORT1+ex+JDI_EXC_REPORT2+
" }\n";
} else {
return JDI_EXC_REPORT1+ex+JDI_EXC_REPORT2;
}
}
private static String disposeVMOnVMDisconnectedException(String className, String methodName) {
if (com.sun.jdi.VirtualMachine.class.getName().equals(className) &&
(methodName.equals("exit") || methodName.equals("dispose"))) {
return ""; // Ignore
}
// We should not have to do this. See #243837
return " if (a instanceof com.sun.jdi.Mirror) {\n" +
" com.sun.jdi.VirtualMachine vm = ((com.sun.jdi.Mirror) a).virtualMachine();\n" +
" try {\n"+
" vm.dispose();\n" +
" } catch (com.sun.jdi.VMDisconnectedException vmdex) {}\n"+
" }\n";
}
public static void main(String[] args) {
/*if (args.length == 0) {
System.err.println("Usage: Generate <output dir>");
}*/
File dir;
if (args.length > 0) {
dir = new File(args[0]);
} else {
dir = new File(System.getProperty("user.dir"));
}
dir = new File(dir, "src/"+PACKAGE.replace('.', '/'));
dir.mkdirs();
System.err.println("Generating into "+dir+" for JDK "+System.getProperty("java.version"));
try {
generate(dir);
} catch (IOException ex) {
Logger.getLogger(Generate.class.getName()).log(Level.SEVERE, null, ex);
}
}
}