moving things back off the branch, and into full disaster mode

This commit is contained in:
benfry
2011-01-20 19:29:39 +00:00
parent 4922d95e59
commit 5ecf3fdf36
67 changed files with 6032 additions and 3645 deletions

View File

@@ -0,0 +1,404 @@
/*
* @(#)EventThread.java 1.4 03/01/23
*
* Copyright 2003 Sun Microsystems, Inc. All rights reserved.
* SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
*/
/*
* Copyright (c) 1997-2001 by Sun Microsystems, Inc. All Rights Reserved.
*
* Sun grants you ("Licensee") a non-exclusive, royalty free, license to use,
* modify and redistribute this software in source and binary code form,
* provided that i) this copyright notice and license appear on all copies of
* the software; and ii) Licensee does not utilize the software in a manner
* which is disparaging to Sun.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING ANY
* IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE OR
* NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN AND ITS LICENSORS SHALL NOT BE
* LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING
* OR DISTRIBUTING THE SOFTWARE OR ITS DERIVATIVES. IN NO EVENT WILL SUN OR ITS
* LICENSORS BE LIABLE FOR ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT,
* INDIRECT, SPECIAL, CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER
* CAUSED AND REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF
* OR INABILITY TO USE SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGES.
*
* This software is not designed or intended for use in on-line control of
* aircraft, air traffic, aircraft navigation or aircraft communications; or in
* the design, construction, operation or maintenance of any nuclear
* facility. Licensee represents and warrants that it will not use or
* redistribute the Software for such purposes.
*/
package processing.mode.java.runner;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.sun.jdi.*;
import com.sun.jdi.event.*;
import com.sun.jdi.request.*;
/**
* This class processes incoming JDI events and displays them
*
* @version @(#) EventThread.java 1.4 03/01/23 23:33:38
* @author Robert Field
*/
public class EventThread extends Thread {
private final Runner parent;
private final VirtualMachine vm; // Running VM
private final String[] excludes; // Packages to exclude
private final PrintWriter writer; // Where output goes
static String nextBaseIndent = ""; // Starting indent for next thread
private boolean connected = true; // Connected to VM
private boolean vmDied = true; // VMDeath occurred
// Maps ThreadReference to ThreadTrace instances
private Map traceMap = new HashMap();
public EventThread(Runner parent, VirtualMachine vm, String[] excludes, PrintWriter writer) {
super("event-handler");
this.parent = parent;
this.vm = vm;
this.excludes = excludes;
this.writer = writer;
}
/**
* Run the event handling thread.
* As long as we are connected, get event sets off
* the queue and dispatch the events within them.
*/
public void run() {
EventQueue queue = vm.eventQueue();
while (connected) {
try {
EventSet eventSet = queue.remove();
EventIterator it = eventSet.eventIterator();
while (it.hasNext()) {
handleEvent(it.nextEvent());
}
eventSet.resume();
} catch (InterruptedException exc) {
// Ignore
} catch (VMDisconnectedException discExc) {
handleDisconnectedException();
break;
}
}
}
/**
* Create the desired event requests, and enable
* them so that we will get events.
* @param excludes Class patterns for which we don't want events
* @param watchFields Do we want to watch assignments to fields
*/
public void setEventRequests(boolean watchFields) {
EventRequestManager mgr = vm.eventRequestManager();
// VMDeathRequest deathReq = mgr.createVMDeathRequest();
// deathReq.setSuspendPolicy(EventRequest.SUSPEND_ALL);
// deathReq.enable();
// get only the uncaught exceptions
ExceptionRequest excReq = mgr.createExceptionRequest(null, false, true);
// this version reports all exceptions, caught or uncaught
//ExceptionRequest excReq = mgr.createExceptionRequest(null, true, true);
// suspend so we can step
excReq.setSuspendPolicy(EventRequest.SUSPEND_ALL);
excReq.enable();
/*
MethodEntryRequest menr = mgr.createMethodEntryRequest();
for (int i=0; i<excludes.length; ++i) {
menr.addClassExclusionFilter(excludes[i]);
}
menr.setSuspendPolicy(EventRequest.SUSPEND_NONE);
menr.enable();
MethodExitRequest mexr = mgr.createMethodExitRequest();
for (int i=0; i<excludes.length; ++i) {
mexr.addClassExclusionFilter(excludes[i]);
}
mexr.setSuspendPolicy(EventRequest.SUSPEND_NONE);
mexr.enable();
ThreadDeathRequest tdr = mgr.createThreadDeathRequest();
// Make sure we sync on thread death
tdr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
tdr.enable();
*/
// turn on field watching (waaay slow)
/*
// if (watchFields) {
ClassPrepareRequest cpr = mgr.createClassPrepareRequest();
for (int i=0; i<excludes.length; ++i) {
cpr.addClassExclusionFilter(excludes[i]);
}
cpr.setSuspendPolicy(EventRequest.SUSPEND_ALL);
cpr.enable();
// }
*/
}
/**
* This class keeps context on events in one thread.
* In this implementation, context is the indentation prefix.
*/
class ThreadTrace {
final ThreadReference thread;
final String baseIndent;
static final String threadDelta = " ";
StringBuffer indent;
ThreadTrace(ThreadReference thread) {
this.thread = thread;
this.baseIndent = nextBaseIndent;
indent = new StringBuffer(baseIndent);
nextBaseIndent += threadDelta;
println("====== " + thread.name() + " ======");
}
private void println(String str) {
if (writer != null) {
writer.print(indent);
writer.println(str);
writer.flush();
}
}
void methodEntryEvent(MethodEntryEvent event) {
println(event.method().name() + " -- "
+ event.method().declaringType().name());
indent.append("| ");
}
void methodExitEvent(MethodExitEvent event) {
indent.setLength(indent.length()-2);
}
void fieldWatchEvent(ModificationWatchpointEvent event) {
Field field = event.field();
Value value = event.valueToBe();
println(" " + field.name() + " = " + value);
}
void exceptionEvent(ExceptionEvent event) {
println("Exception: " + event.exception() +
" catch: " + event.catchLocation());
// System.out.println("Exception: " + event.exception() +
// " catch: " + event.catchLocation());
// Step to the catch
EventRequestManager mgr = vm.eventRequestManager();
StepRequest req = mgr.createStepRequest(thread,
StepRequest.STEP_MIN,
StepRequest.STEP_INTO);
req.addCountFilter(1); // next step only
req.setSuspendPolicy(EventRequest.SUSPEND_ALL);
req.enable();
}
// Step to exception catch
void stepEvent(StepEvent event) {
// Adjust call depth
int cnt = 0;
indent = new StringBuffer(baseIndent);
try {
cnt = thread.frameCount();
} catch (IncompatibleThreadStateException exc) {
}
while (cnt-- > 0) {
indent.append("| ");
}
EventRequestManager mgr = vm.eventRequestManager();
mgr.deleteEventRequest(event.request());
}
void threadDeathEvent(ThreadDeathEvent event) {
indent = new StringBuffer(baseIndent);
println("====== " + thread.name() + " end ======");
}
}
/**
* Returns the ThreadTrace instance for the specified thread,
* creating one if needed.
*/
ThreadTrace threadTrace(ThreadReference thread) {
ThreadTrace trace = (ThreadTrace)traceMap.get(thread);
if (trace == null) {
trace = new ThreadTrace(thread);
traceMap.put(thread, trace);
}
return trace;
}
/**
* Dispatch incoming events
*/
private void handleEvent(Event event) {
if (event instanceof ExceptionEvent) {
exceptionEvent((ExceptionEvent)event);
} else if (event instanceof ModificationWatchpointEvent) {
fieldWatchEvent((ModificationWatchpointEvent)event);
} else if (event instanceof MethodEntryEvent) {
methodEntryEvent((MethodEntryEvent)event);
} else if (event instanceof MethodExitEvent) {
methodExitEvent((MethodExitEvent)event);
} else if (event instanceof StepEvent) {
stepEvent((StepEvent)event);
} else if (event instanceof ThreadDeathEvent) {
threadDeathEvent((ThreadDeathEvent)event);
} else if (event instanceof ClassPrepareEvent) {
classPrepareEvent((ClassPrepareEvent)event);
} else if (event instanceof VMStartEvent) {
vmStartEvent((VMStartEvent)event);
} else if (event instanceof VMDeathEvent) {
vmDeathEvent((VMDeathEvent)event);
} else if (event instanceof VMDisconnectEvent) {
vmDisconnectEvent((VMDisconnectEvent)event);
} else {
throw new Error("Unexpected event type");
}
}
/***
* A VMDisconnectedException has happened while dealing with
* another event. We need to flush the event queue, dealing only
* with exit events (VMDeath, VMDisconnect) so that we terminate
* correctly.
*/
synchronized void handleDisconnectedException() {
EventQueue queue = vm.eventQueue();
while (connected) {
try {
EventSet eventSet = queue.remove();
EventIterator iter = eventSet.eventIterator();
while (iter.hasNext()) {
Event event = iter.nextEvent();
if (event instanceof VMDeathEvent) {
vmDeathEvent((VMDeathEvent)event);
} else if (event instanceof VMDisconnectEvent) {
vmDisconnectEvent((VMDisconnectEvent)event);
}
}
eventSet.resume(); // Resume the VM
} catch (InterruptedException exc) {
// ignore
}
}
}
private void vmStartEvent(VMStartEvent event) {
if (writer != null) writer.println("-- VM Started --");
}
// Forward event for thread specific processing
private void methodEntryEvent(MethodEntryEvent event) {
threadTrace(event.thread()).methodEntryEvent(event);
}
// Forward event for thread specific processing
private void methodExitEvent(MethodExitEvent event) {
threadTrace(event.thread()).methodExitEvent(event);
}
// Forward event for thread specific processing
private void stepEvent(StepEvent event) {
threadTrace(event.thread()).stepEvent(event);
}
// Forward event for thread specific processing
private void fieldWatchEvent(ModificationWatchpointEvent event) {
threadTrace(event.thread()).fieldWatchEvent(event);
}
void threadDeathEvent(ThreadDeathEvent event) {
ThreadTrace trace = (ThreadTrace)traceMap.get(event.thread());
if (trace != null) { // only want threads we care about
trace.threadDeathEvent(event); // Forward event
}
}
/**
* A new class has been loaded.
* Set watchpoints on each of its fields
*/
private void classPrepareEvent(ClassPrepareEvent event) {
// System.out.println(event);
// List list = event.referenceType().methodsByName("stop");
// Object o = list.get(0);
// System.out.println("stop methods = " + list);
// System.out.println(o.getClass());
EventRequestManager mgr = vm.eventRequestManager();
List fields = event.referenceType().visibleFields();
for (Iterator it = fields.iterator(); it.hasNext(); ) {
Field field = (Field)it.next();
ModificationWatchpointRequest req =
mgr.createModificationWatchpointRequest(field);
for (int i=0; i<excludes.length; ++i) {
req.addClassExclusionFilter(excludes[i]);
}
req.setSuspendPolicy(EventRequest.SUSPEND_NONE);
req.enable();
}
}
private void exceptionEvent(ExceptionEvent event) {
// com.sun.jdi.ObjectReference or = event.exception();
// System.out.println("exceptionEvent() fired " + or);
// System.out.println("catch location " + event.catchLocation());
parent.exception(event);
/*
ObjectReference or = event.exception();
ThreadReference thread = event.thread();
ThreadTrace trace = (ThreadTrace)traceMap.get(thread);
if (trace != null) { // only want threads we care about
trace.exceptionEvent(event); // Forward event
}
try {
List frames = thread.frames();
for (Object item : frames) {
System.out.println("got " + item);
}
//System.out.println(frames);
} catch (IncompatibleThreadStateException e) {
e.printStackTrace();
}
*/
}
public void vmDeathEvent(VMDeathEvent event) {
//System.err.println("vm is dead! dead!");
vmDied = true;
if (writer != null) {
writer.println("-- The application exited --");
}
}
public void vmDisconnectEvent(VMDisconnectEvent event) {
connected = false;
if (!vmDied) {
if (writer != null) {
writer.println("-- The application has been disconnected --");
}
}
}
}

View File

@@ -0,0 +1,42 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-06 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.mode.java.runner;
/**
* Interface for dealing with parser/compiler output.
* <P>
* Different instances of MessageStream need to do different things with
* messages. In particular, a stream instance used for parsing output from
* the compiler compiler has to interpret its messages differently than one
* parsing output from the runtime.
* <P>
* Classes which consume messages and do something with them
* should implement this interface.
*/
public interface MessageConsumer {
public void message(String s);
}

View File

@@ -0,0 +1,92 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-06 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.mode.java.runner;
import java.io.*;
/**
* Slurps up messages from compiler.
*/
public class MessageSiphon implements Runnable {
BufferedReader streamReader;
Thread thread;
MessageConsumer consumer;
public MessageSiphon(InputStream stream, MessageConsumer consumer) {
this.streamReader = new BufferedReader(new InputStreamReader(stream));
this.consumer = consumer;
thread = new Thread(this);
// don't set priority too low, otherwise exceptions won't
// bubble up in time (i.e. compile errors have a weird delay)
//thread.setPriority(Thread.MIN_PRIORITY);
thread.setPriority(Thread.MAX_PRIORITY-1);
//thread.start();
}
public void run() {
try {
// process data until we hit EOF; this will happily block
// (effectively sleeping the thread) until new data comes in.
// when the program is finally done, null will come through.
//
String currentLine;
while ((currentLine = streamReader.readLine()) != null) {
// \n is added again because readLine() strips it out
//EditorConsole.systemOut.println("messaging in");
consumer.message(currentLine + "\n");
//EditorConsole.systemOut.println("messaging out");
}
//EditorConsole.systemOut.println("messaging thread done");
thread = null;
} catch (NullPointerException npe) {
// Fairly common exception during shutdown
thread = null;
} catch (Exception e) {
// On Linux and sometimes on Mac OS X, a "bad file descriptor"
// message comes up when closing an applet that's run externally.
// That message just gets supressed here..
String mess = e.getMessage();
if ((mess != null) &&
(mess.indexOf("Bad file descriptor") != -1)) {
//if (e.getMessage().indexOf("Bad file descriptor") == -1) {
//System.err.println("MessageSiphon err " + e);
//e.printStackTrace();
} else {
e.printStackTrace();
}
thread = null;
}
}
public Thread getThread() {
return thread;
}
}

View File

@@ -0,0 +1,62 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-08 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.mode.java.runner;
import java.io.*;
/**
* OutputStream to handle stdout/stderr messages.
* <P>
* This is used by Editor, System.err is set to
* new PrintStream(new MessageStream()).
* It's also used by Compiler.
*/
class MessageStream extends OutputStream {
MessageConsumer messageConsumer;
public MessageStream(MessageConsumer messageConsumer) {
this.messageConsumer = messageConsumer;
}
public void close() { }
public void flush() { }
public void write(byte b[]) {
// this never seems to get called
System.out.println("leech1: " + new String(b));
}
public void write(byte b[], int offset, int length) {
//System.out.println("leech2: " + new String(b));
this.messageConsumer.message(new String(b, offset, length));
}
public void write(int b) {
// this never seems to get called
System.out.println("leech3: '" + ((char)b) + "'");
}
}

View File

@@ -0,0 +1,678 @@
/* -*- mode: java; c-basic-offset: 2; indent-tabs-mode: nil -*- */
/*
Part of the Processing project - http://processing.org
Copyright (c) 2004-09 Ben Fry and Casey Reas
Copyright (c) 2001-04 Massachusetts Institute of Technology
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program 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 for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software Foundation,
Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
package processing.mode.java.runner;
import processing.app.*;
import processing.app.exec.StreamRedirectThread;
import processing.core.*;
import processing.mode.java.Build;
import java.awt.Point;
import java.io.*;
import java.util.*;
import com.sun.jdi.*;
import com.sun.jdi.connect.*;
import com.sun.jdi.event.ExceptionEvent;
/**
* Runs a compiled sketch. As of release 0136, all sketches are run externally
* to the environment so that a debugging interface can be used. This opens up
* future options for a decent debugger, but in the meantime fixes several
* problems with output and error streams, messages getting lost on Mac OS X,
* the run/stop buttons not working, libraries not shutting down, exceptions
* not coming through, exceptions being printed twice, having to force quit
* if you make a bad while() loop, and so on.
*/
public class Runner implements MessageConsumer {
private boolean presenting;
// Object that listens for error messages or exceptions.
protected RunnerListener listener;
// Running remote VM
protected VirtualMachine vm;
// Thread transferring remote error stream to our error stream
protected Thread errThread = null;
// Thread transferring remote output stream to our output stream
protected Thread outThread = null;
// Mode for tracing the Trace program (default= 0 off)
protected int debugTraceMode = 0;
// Do we want to watch assignments to fields
protected boolean watchFields = false;
// Class patterns for which we don't want events
protected String[] excludes = {
"java.*", "javax.*", "sun.*", "com.sun.*",
"apple.*",
"processing.*"
};
protected SketchException exception;
//private PrintStream leechErr;
protected Editor editor;
// protected Sketch sketch;
protected Build build;
// private String appletClassName;
public Runner(Build build, RunnerListener listener) {
this.listener = listener;
// this.sketch = sketch;
this.build = build;
if (listener instanceof Editor) {
this.editor = (Editor) listener;
// } else {
// System.out.println("actually it's a " + listener.getClass().getName());
}
}
// public void launch(String appletClassName, boolean presenting) {
// this.appletClassName = appletClassName;
public void launch(boolean presenting) {
this.presenting = presenting;
// all params have to be stored as separate items,
// so a growable array needs to be used. i.e. -Xms128m -Xmx1024m
// will throw an error if it's shoved into a single array element
//Vector params = new Vector();
// get around Apple's Java 1.5 bugs
//params.add("/System/Library/Frameworks/JavaVM.framework/Versions/1.4.2/Commands/java");
//params.add("java");
//System.out.println("0");
String[] machineParamList = getMachineParams();
String[] sketchParamList = getSketchParams();
vm = launchVirtualMachine(machineParamList, sketchParamList);
if (vm != null) {
generateTrace(null);
// try {
// generateTrace(new PrintWriter("/Users/fry/Desktop/output.txt"));
// } catch (Exception e) {
// e.printStackTrace();
// }
}
}
protected String[] getMachineParams() {
ArrayList<String> params = new ArrayList<String>();
//params.add("-Xint"); // interpreted mode
//params.add("-Xprof"); // profiler
//params.add("-Xaprof"); // allocation profiler
//params.add("-Xrunhprof:cpu=samples"); // old-style profiler
// TODO change this to use run.args = true, run.args.0, run.args.1, etc.
// so that spaces can be included in the arg names
String options = Preferences.get("run.options");
if (options.length() > 0) {
String pieces[] = PApplet.split(options, ' ');
for (int i = 0; i < pieces.length; i++) {
String p = pieces[i].trim();
if (p.length() > 0) {
params.add(p);
}
}
}
// params.add("-Djava.ext.dirs=nuffing");
if (Preferences.getBoolean("run.options.memory")) {
params.add("-Xms" + Preferences.get("run.options.memory.initial") + "m");
params.add("-Xmx" + Preferences.get("run.options.memory.maximum") + "m");
}
if (Base.isMacOS()) {
params.add("-Xdock:name=" + build.getSketchClassName());
// params.add("-Dcom.apple.mrj.application.apple.menu.about.name=" +
// sketch.getMainClassName());
}
// sketch.libraryPath might be ""
// librariesClassPath will always have sep char prepended
params.add("-Djava.library.path=" +
build.getJavaLibraryPath() +
File.pathSeparator +
System.getProperty("java.library.path"));
params.add("-cp");
params.add(build.getClassPath());
// params.add(sketch.getClassPath() +
// File.pathSeparator +
// Base.librariesClassPath);
// enable assertions - http://dev.processing.org/bugs/show_bug.cgi?id=1188
params.add("-ea");
//PApplet.println(PApplet.split(sketch.classPath, ':'));
String outgoing[] = new String[params.size()];
params.toArray(outgoing);
//PApplet.println(outgoing);
// PApplet.println(PApplet.split(outgoing[0], ":"));
// PApplet.println();
// PApplet.println("class path");
// PApplet.println(PApplet.split(outgoing[2], ":"));
return outgoing;
//return (String[]) params.toArray();
// System.out.println("sketch class path");
// PApplet.println(PApplet.split(sketch.classPath, ';'));
// System.out.println();
// System.out.println("libraries class path");
// PApplet.println(PApplet.split(Base.librariesClassPath, ';'));
// System.out.println();
}
protected String[] getSketchParams() {
ArrayList<String> params = new ArrayList<String>();
// It's dangerous to add your own main() to your code,
// but if you've done it, we'll respect your right to hang yourself.
// http://dev.processing.org/bugs/show_bug.cgi?id=1446
if (build.getFoundMain()) {
params.add(build.getSketchClassName());
} else {
params.add("processing.core.PApplet");
// If there was a saved location (this guy has been run more than once)
// then the location will be set to the last position of the sketch window.
// This will be passed to the PApplet runner using something like
// --location=30,20
// Otherwise, the editor location will be passed, and the applet will
// figure out where to place itself based on the editor location.
// --editor-location=150,20
if (editor != null) { // if running processing-cmd, don't do placement
Point windowLocation = editor.getSketchLocation();
if (windowLocation != null) {
params.add(PApplet.ARGS_LOCATION + "=" +
windowLocation.x + "," + windowLocation.y);
} else {
Point editorLocation = editor.getLocation();
params.add(PApplet.ARGS_EDITOR_LOCATION + "=" +
editorLocation.x + "," + editorLocation.y);
}
params.add(PApplet.ARGS_EXTERNAL);
}
params.add(PApplet.ARGS_DISPLAY + "=" +
Preferences.get("run.display"));
params.add(PApplet.ARGS_SKETCH_FOLDER + "=" +
build.getSketchPath());
if (presenting) {
params.add(PApplet.ARGS_PRESENT);
if (Preferences.getBoolean("run.present.exclusive")) {
params.add(PApplet.ARGS_EXCLUSIVE);
}
params.add(PApplet.ARGS_STOP_COLOR + "=" +
Preferences.get("run.present.stop.color"));
params.add(PApplet.ARGS_BGCOLOR + "=" +
Preferences.get("run.present.bgcolor"));
}
params.add(build.getSketchClassName());
}
// String outgoing[] = new String[params.size()];
// params.toArray(outgoing);
// return outgoing;
return (String[]) params.toArray(new String[0]);
}
protected VirtualMachine launchVirtualMachine(String[] vmParams,
String[] classParams) {
//vm = launchTarget(sb.toString());
LaunchingConnector connector = (LaunchingConnector)
findConnector("com.sun.jdi.RawCommandLineLaunch");
//PApplet.println(connector); // gets the defaults
//Map arguments = connectorArguments(connector, mainArgs);
Map arguments = connector.defaultArguments();
Connector.Argument commandArg =
(Connector.Argument)arguments.get("command");
// Using localhost instead of 127.0.0.1 sometimes causes a
// "Transport Error 202" error message when trying to run.
// http://dev.processing.org/bugs/show_bug.cgi?id=895
String addr = "127.0.0.1:" + (8000 + (int) (Math.random() * 1000));
//String addr = "localhost:" + (8000 + (int) (Math.random() * 1000));
//String addr = "" + (8000 + (int) (Math.random() * 1000));
String commandArgs =
"java -Xrunjdwp:transport=dt_socket,address=" + addr + ",suspend=y ";
if (Base.isWindows()) {
commandArgs =
"java -Xrunjdwp:transport=dt_shmem,address=" + addr + ",suspend=y ";
} else if (Base.isMacOS()) {
if (System.getProperty("os.version").startsWith("10.4")) {
// -d32 not understood by 10.4 (and not needed)
commandArgs =
"java -Xrunjdwp:transport=dt_socket,address=" + addr + ",suspend=y ";
} else {
commandArgs =
// "java -Xrunjdwp:transport=dt_socket,address=" + addr + ",suspend=y ";
"java -d32 -Xrunjdwp:transport=dt_socket,address=" + addr + ",suspend=y ";
}
}
for (int i = 0; i < vmParams.length; i++) {
commandArgs = addArgument(commandArgs, vmParams[i], ' ');
}
if (classParams != null) {
for (int i = 0; i < classParams.length; i++) {
commandArgs = addArgument(commandArgs, classParams[i], ' ');
}
}
commandArg.setValue(commandArgs);
Connector.Argument addressArg =
(Connector.Argument)arguments.get("address");
addressArg.setValue(addr);
//PApplet.println(connector); // prints the current
//com.sun.tools.jdi.AbstractLauncher al;
//com.sun.tools.jdi.RawCommandLineLauncher rcll;
//System.out.println(PApplet.javaVersion);
// http://java.sun.com/j2se/1.5.0/docs/guide/jpda/conninv.html#sunlaunch
try {
return connector.launch(arguments);
} catch (IOException exc) {
throw new Error("Unable to launch target VM: " + exc);
} catch (IllegalConnectorArgumentsException exc) {
throw new Error("Internal error: " + exc);
} catch (VMStartException exc) {
Process p = exc.process();
//System.out.println(p);
String[] errorStrings = PApplet.loadStrings(p.getErrorStream());
/*String[] inputStrings =*/ PApplet.loadStrings(p.getInputStream());
if (errorStrings != null && errorStrings.length > 1) {
if (errorStrings[0].indexOf("Invalid maximum heap size") != -1) {
Base.showWarning("Way Too High",
"Please lower the value for \u201Cmaximum available memory\u201D in the\n" +
"Preferences window. For more information, read Help \u2192 Troubleshooting.",
exc);
} else {
PApplet.println(errorStrings);
System.err.println("Using startup command:");
PApplet.println(arguments);
}
} else {
exc.printStackTrace();
System.err.println("Could not run the sketch (Target VM failed to initialize).");
if (Preferences.getBoolean("run.options.memory")) {
// Only mention this if they've even altered the memory setup
System.err.println("Make sure that you haven't set the maximum available memory too high.");
}
System.err.println("For more information, read revisions.txt and Help \u2192 Troubleshooting.");
}
// changing this to separate editor and listener [091124]
//if (editor != null) {
listener.statusError("Could not run the sketch.");
//}
return null;
}
}
private static boolean hasWhitespace(String string) {
int length = string.length();
for (int i = 0; i < length; i++) {
if (Character.isWhitespace(string.charAt(i))) {
return true;
}
}
return false;
}
private static String addArgument(String string, String argument, char sep) {
if (hasWhitespace(argument) || argument.indexOf(',') != -1) {
// Quotes were stripped out for this argument, add 'em back.
StringBuffer buffer = new StringBuffer(string);
buffer.append('"');
for (int i = 0; i < argument.length(); i++) {
char c = argument.charAt(i);
if (c == '"') {
buffer.append('\\');
}
buffer.append(c);
}
buffer.append('\"');
buffer.append(sep);
return buffer.toString();
} else {
return string + argument + String.valueOf(sep);
}
}
/**
* Generate the trace.
* Enable events, start thread to display events,
* start threads to forward remote error and output streams,
* resume the remote VM, wait for the final event, and shutdown.
*/
protected void generateTrace(PrintWriter writer) {
vm.setDebugTraceMode(debugTraceMode);
EventThread eventThread = null;
//if (writer != null) {
eventThread = new EventThread(this, vm, excludes, writer);
eventThread.setEventRequests(watchFields);
eventThread.start();
//}
//redirectOutput();
Process process = vm.process();
// processInput = new SystemOutSiphon(process.getInputStream());
// processError = new MessageSiphon(process.getErrorStream(), this);
// Copy target's output and error to our output and error.
// errThread = new StreamRedirectThread("error reader",
// process.getErrorStream(),
// System.err);
MessageSiphon ms = new MessageSiphon(process.getErrorStream(), this);
errThread = ms.getThread();
outThread = new StreamRedirectThread("output reader",
process.getInputStream(),
System.out);
errThread.start();
outThread.start();
vm.resume();
//System.out.println("done with resume");
// Shutdown begins when event thread terminates
try {
if (eventThread != null) eventThread.join();
// System.out.println("in here");
// Bug #852 tracked to this next line in the code.
// http://dev.processing.org/bugs/show_bug.cgi?id=852
errThread.join(); // Make sure output is forwarded
// System.out.println("and then");
outThread.join(); // before we exit
// System.out.println("out of it");
// At this point, disable the run button.
// This happens when the sketch is exited by hitting ESC,
// or the user manually closes the sketch window.
// TODO this should be handled better, should it not?
if (editor != null) {
editor.deactivateRun();
}
} catch (InterruptedException exc) {
// we don't interrupt
}
//System.out.println("and leaving");
if (writer != null) writer.close();
}
protected Connector findConnector(String connectorName) {
List connectors = Bootstrap.virtualMachineManager().allConnectors();
// debug: code to list available connectors
// Iterator iter2 = connectors.iterator();
// while (iter2.hasNext()) {
// Connector connector = (Connector)iter2.next();
// System.out.println("connector name is " + connector.name());
// }
Iterator iter = connectors.iterator();
while (iter.hasNext()) {
Connector connector = (Connector)iter.next();
if (connector.name().equals(connectorName)) {
return connector;
}
}
throw new Error("No connector");
}
public void exception(ExceptionEvent event) {
// System.out.println(event);
ObjectReference or = event.exception();
ReferenceType rt = or.referenceType();
String exceptionName = rt.name();
//Field messageField = Throwable.class.getField("detailMessage");
Field messageField = rt.fieldByName("detailMessage");
// System.out.println("field " + messageField);
Value messageValue = or.getValue(messageField);
// System.out.println("mess val " + messageValue);
int last = exceptionName.lastIndexOf('.');
String message = exceptionName.substring(last + 1);
if (messageValue != null) {
String messageStr = messageValue.toString();
if (messageStr.startsWith("\"")) {
messageStr = messageStr.substring(1, messageStr.length() - 1);
}
message += ": " + messageStr;
}
// System.out.println("mess type " + messageValue.type());
//StringReference messageReference = (StringReference) messageValue.type();
// System.out.println(or.referenceType().fields());
// if (name.startsWith("java.lang.")) {
// name = name.substring(10);
if (!handleCommonErrors(exceptionName, message, listener)) {
reportException(message, event.thread());
}
if (editor != null) {
editor.deactivateRun();
}
}
public static boolean handleCommonErrors(final String exceptionClass,
final String message,
final RunnerListener listener) {
if (exceptionClass.equals("java.lang.OutOfMemoryError")) {
if (message.contains("exceeds VM budget")) {
// TODO this is a kludge for Android, since there's no memory preference
listener.statusError("OutOfMemoryError: This code attempts to use more memory than available.");
System.err.println("An OutOfMemoryError means that your code is either using up too much memory");
System.err.println("because of a bug (e.g. creating an array that's too large, or unintentionally");
System.err.println("loading thousands of images), or simply that it's trying to use more memory");
System.err.println("than what is supported by the current device.");
} else {
listener.statusError("OutOfMemoryError: You may need to increase the memory setting in Preferences.");
System.err.println("An OutOfMemoryError means that your code is either using up too much memory");
System.err.println("because of a bug (e.g. creating an array that's too large, or unintentionally");
System.err.println("loading thousands of images), or that your sketch may need more memory to run.");
System.err.println("If your sketch uses a lot of memory (for instance if it loads a lot of data files)");
System.err.println("you can increase the memory available to your sketch using the Preferences window.");
}
} else if (exceptionClass.equals("java.lang.StackOverflowError")) {
listener.statusError("StackOverflowError: This sketch is attempting too much recursion.");
System.err.println("A StackOverflowError means that you have a bug that's causing a function");
System.err.println("to be called recursively (it's calling itself and going in circles),");
System.err.println("or you're intentionally calling a recursive function too much,");
System.err.println("and your code should be rewritten in a more efficient manner.");
} else if (exceptionClass.equals("java.lang.UnsupportedClassVersionError")) {
listener.statusError("UnsupportedClassVersionError: A library is using code compiled with an unsupported version of Java.");
System.err.println("This version of Processing only supports libraries and JAR files compiled for Java 1.5.");
System.err.println("A library used by this sketch was compiled for Java 1.6 or later, ");
System.err.println("and needs to be recompiled to be compatible with Java 1.5.");
} else if (exceptionClass.equals("java.lang.NoSuchMethodError") ||
exceptionClass.equals("java.lang.NoSuchFieldError")) {
listener.statusError(exceptionClass.substring(10) + ": " +
"You may be using a library that's incompatible " +
"with this version of Processing.");
} else if (message != null &&
message.equals("ClassNotFoundException: quicktime.std.StdQTException")) {
listener.statusError("Could not find QuickTime, please reinstall QuickTime 7 or later.");
} else {
return false;
}
return true;
}
// TODO: This may be called more than one time per error in the VM,
// presumably because exceptions might be wrapped inside others,
// and this will fire for both.
protected void reportException(String message, ThreadReference thread) {
listener.statusError(findException(message, thread));
}
/**
* Move through a list of stack frames, searching for references to code
* found in the current sketch. Return with a RunnerException that contains
* the location of the error, or if nothing is found, just return with a
* RunnerException that wraps the error message itself.
*/
SketchException findException(String message, ThreadReference thread) {
try {
// use to dump the stack for debugging
// for (StackFrame frame : thread.frames()) {
// System.out.println("frame: " + frame);
// }
List<StackFrame> frames = thread.frames();
for (StackFrame frame : frames) {
try {
Location location = frame.location();
String filename = null;
filename = location.sourceName();
int lineNumber = location.lineNumber() - 1;
SketchException rex =
build.placeException(message, filename, lineNumber);
if (rex != null) {
return rex;
}
} catch (AbsentInformationException e) {
// Any of the thread.blah() methods can throw an AbsentInformationEx
// if that bit of data is missing. If so, just write out the error
// message to the console.
//e.printStackTrace(); // not useful
exception = new SketchException(message);
exception.hideStackTrace();
listener.statusError(exception);
}
}
} catch (IncompatibleThreadStateException e) {
// This shouldn't happen, but if it does, print the exception in case
// it's something that needs to be debugged separately.
e.printStackTrace();
}
// Give up, nothing found inside the pile of stack frames
SketchException rex = new SketchException(message);
// exception is being created /here/, so stack trace is not useful
rex.hideStackTrace();
return rex;
}
public void close() {
// TODO make sure stop() has already been called to exit the sketch
// TODO actually kill off the vm here
if (vm != null) {
try {
vm.exit(0);
} catch (com.sun.jdi.VMDisconnectedException vmde) {
// if the vm has disconnected on its own, ignore message
//System.out.println("harmless disconnect " + vmde.getMessage());
// TODO shouldn't need to do this, need to do more cleanup
}
vm = null;
}
}
// made synchronized for rev 87
// attempted to remove synchronized for 0136 to fix bug #775 (no luck tho)
// http://dev.processing.org/bugs/show_bug.cgi?id=775
synchronized public void message(String s) {
//System.out.println("M" + s.length() + ":" + s.trim()); // + "MMM" + s.length());
// this eats the CRLFs on the lines.. oops.. do it later
//if (s.trim().length() == 0) return;
// this is PApplet sending a message (via System.out.println)
// that signals that the applet has been quit.
if (s.indexOf(PApplet.EXTERNAL_STOP) == 0) {
//System.out.println("external: quit");
if (editor != null) {
// editor.internalCloseRunner(); // [091124]
// editor.handleStop(); // prior to 0192
editor.internalCloseRunner(); // 0192
}
return;
}
// this is the PApplet sending us a message that the applet
// is being moved to a new window location
if (s.indexOf(PApplet.EXTERNAL_MOVE) == 0) {
String nums = s.substring(s.indexOf(' ') + 1).trim();
int space = nums.indexOf(' ');
int left = Integer.parseInt(nums.substring(0, space));
int top = Integer.parseInt(nums.substring(space + 1));
// this is only fired when connected to an editor
editor.setSketchLocation(new Point(left, top));
//System.out.println("external: move to " + left + " " + top);
return;
}
// these are used for debugging, in case there are concerns
// that some errors aren't coming through properly
// if (s.length() > 2) {
// System.err.println(newMessage);
// System.err.println("message " + s.length() + ":" + s);
// }
// always shove out the mesage, since it might not fall under
// the same setup as we're expecting
System.err.print(s);
//System.err.println("[" + s.length() + "] " + s);
System.err.flush();
}
}