mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Improvement of debugger plugin. Debugger itself became multithreaded
This commit is contained in:
parent
930d2087ab
commit
c490e2f9f8
|
@ -2,6 +2,9 @@ package org.teavm.chromerdp;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.codehaus.jackson.JsonNode;
|
import org.codehaus.jackson.JsonNode;
|
||||||
import org.codehaus.jackson.map.ObjectMapper;
|
import org.codehaus.jackson.map.ObjectMapper;
|
||||||
import org.teavm.chromerdp.data.CallFrameDTO;
|
import org.teavm.chromerdp.data.CallFrameDTO;
|
||||||
|
@ -16,16 +19,21 @@ import org.teavm.debugging.*;
|
||||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeConsumer {
|
public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeConsumer {
|
||||||
|
private static final Object dummy = new Object();
|
||||||
private ChromeRDPExchange exchange;
|
private ChromeRDPExchange exchange;
|
||||||
private List<JavaScriptDebuggerListener> listeners = new ArrayList<>();
|
private ConcurrentMap<JavaScriptDebuggerListener, Object> listeners = new ConcurrentHashMap<>();
|
||||||
private Set<RDPBreakpoint> breakpoints = new HashSet<>();
|
private ConcurrentMap<RDPBreakpoint, Object> breakpoints = new ConcurrentHashMap<>();
|
||||||
private RDPCallFrame[] callStack = new RDPCallFrame[0];
|
private volatile RDPCallFrame[] callStack = new RDPCallFrame[0];
|
||||||
private Map<String, String> scripts = new HashMap<>();
|
private ConcurrentMap<String, String> scripts = new ConcurrentHashMap<>();
|
||||||
private Map<String, String> scriptIds = new HashMap<>();
|
private ConcurrentMap<String, String> scriptIds = new ConcurrentHashMap<>();
|
||||||
private boolean suspended = false;
|
private boolean suspended = false;
|
||||||
private ObjectMapper mapper = new ObjectMapper();
|
private ObjectMapper mapper = new ObjectMapper();
|
||||||
private Map<Integer, ResponseHandler> responseHandlers = new HashMap<>();
|
private ConcurrentMap<Integer, ResponseHandler> responseHandlers = new ConcurrentHashMap<>();
|
||||||
private int messageIdGenerator;
|
private AtomicInteger messageIdGenerator = new AtomicInteger();
|
||||||
|
|
||||||
|
private List<JavaScriptDebuggerListener> getListeners() {
|
||||||
|
return new ArrayList<>(listeners.keySet());
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setExchange(ChromeRDPExchange exchange) {
|
public void setExchange(ChromeRDPExchange exchange) {
|
||||||
|
@ -37,15 +45,15 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
}
|
}
|
||||||
this.exchange = exchange;
|
this.exchange = exchange;
|
||||||
if (exchange != null) {
|
if (exchange != null) {
|
||||||
for (RDPBreakpoint breakpoint : breakpoints) {
|
for (RDPBreakpoint breakpoint : breakpoints.keySet().toArray(new RDPBreakpoint[0])) {
|
||||||
updateBreakpoint(breakpoint);
|
updateBreakpoint(breakpoint);
|
||||||
}
|
}
|
||||||
for (JavaScriptDebuggerListener listener : listeners) {
|
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||||
listener.attached();
|
listener.attached();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
suspended = false;
|
suspended = false;
|
||||||
for (JavaScriptDebuggerListener listener : listeners) {
|
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||||
listener.detached();
|
listener.detached();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +94,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
callStack[i] = map(callFrameDTOs[i]);
|
callStack[i] = map(callFrameDTOs[i]);
|
||||||
}
|
}
|
||||||
this.callStack = callStack;
|
this.callStack = callStack;
|
||||||
for (JavaScriptDebuggerListener listener : listeners) {
|
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||||
listener.paused();
|
listener.paused();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,18 +102,17 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
private synchronized void fireResumed() {
|
private synchronized void fireResumed() {
|
||||||
suspended = false;
|
suspended = false;
|
||||||
callStack = null;
|
callStack = null;
|
||||||
for (JavaScriptDebuggerListener listener : listeners) {
|
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||||
listener.resumed();
|
listener.resumed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private synchronized void scriptParsed(ScriptParsedNotification params) {
|
private synchronized void scriptParsed(ScriptParsedNotification params) {
|
||||||
if (scripts.containsKey(params.getScriptId())) {
|
if (scripts.putIfAbsent(params.getScriptId(), params.getUrl()) != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
scripts.put(params.getScriptId(), params.getUrl());
|
|
||||||
scriptIds.put(params.getUrl(), params.getScriptId());
|
scriptIds.put(params.getUrl(), params.getScriptId());
|
||||||
for (JavaScriptDebuggerListener listener : listeners) {
|
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||||
listener.scriptAdded(params.getUrl());
|
listener.scriptAdded(params.getUrl());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,7 +120,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addListener(JavaScriptDebuggerListener listener) {
|
public void addListener(JavaScriptDebuggerListener listener) {
|
||||||
listeners.add(listener);
|
listeners.put(listener, dummy);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -194,23 +201,32 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
return exchange != null;
|
return exchange != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void detach() {
|
||||||
|
if (exchange != null) {
|
||||||
|
exchange.disconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JavaScriptCallFrame[] getCallStack() {
|
public JavaScriptCallFrame[] getCallStack() {
|
||||||
if (exchange == null) {
|
if (exchange == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
JavaScriptCallFrame[] callStack = this.callStack;
|
||||||
return callStack != null ? callStack.clone() : null;
|
return callStack != null ? callStack.clone() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) {
|
public JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) {
|
||||||
RDPBreakpoint breakpoint = new RDPBreakpoint(this, location);
|
RDPBreakpoint breakpoint = new RDPBreakpoint(this, location);
|
||||||
breakpoints.add(breakpoint);
|
breakpoints.put(breakpoint, dummy);
|
||||||
updateBreakpoint(breakpoint);
|
updateBreakpoint(breakpoint);
|
||||||
return breakpoint;
|
return breakpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroyBreakpoint(RDPBreakpoint breakpoint) {
|
void destroyBreakpoint(RDPBreakpoint breakpoint) {
|
||||||
|
breakpoints.remove(breakpoint);
|
||||||
if (breakpoint.chromeId != null) {
|
if (breakpoint.chromeId != null) {
|
||||||
Message message = new Message();
|
Message message = new Message();
|
||||||
message.setMethod("Debugger.removeBreakpoint");
|
message.setMethod("Debugger.removeBreakpoint");
|
||||||
|
@ -222,7 +238,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
}
|
}
|
||||||
|
|
||||||
void fireScriptAdded(String script) {
|
void fireScriptAdded(String script) {
|
||||||
for (JavaScriptDebuggerListener listener : listeners) {
|
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||||
listener.scriptAdded(script);
|
listener.scriptAdded(script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -232,7 +248,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Message message = new Message();
|
Message message = new Message();
|
||||||
message.setId(++messageIdGenerator);
|
message.setId(messageIdGenerator.incrementAndGet());
|
||||||
message.setMethod("Debugger.setBreakpoint");
|
message.setMethod("Debugger.setBreakpoint");
|
||||||
SetBreakpointCommand params = new SetBreakpointCommand();
|
SetBreakpointCommand params = new SetBreakpointCommand();
|
||||||
params.setLocation(unmap(breakpoint.getLocation()));
|
params.setLocation(unmap(breakpoint.getLocation()));
|
||||||
|
@ -241,7 +257,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
@Override public void received(JsonNode node) throws IOException {
|
@Override public void received(JsonNode node) throws IOException {
|
||||||
SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class).readValue(node);
|
SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class).readValue(node);
|
||||||
breakpoint.chromeId = response.getBreakpointId();
|
breakpoint.chromeId = response.getBreakpointId();
|
||||||
for (JavaScriptDebuggerListener listener : listeners) {
|
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||||
listener.breakpointChanged(breakpoint);
|
listener.breakpointChanged(breakpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,15 @@ public class ChromeRDPDebuggerEndpoint implements ChromeRDPExchange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void disconnect() {
|
||||||
|
try {
|
||||||
|
session.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OnMessage
|
@OnMessage
|
||||||
public void receive(String message) throws IOException {
|
public void receive(String message) throws IOException {
|
||||||
for (ChromeRDPExchangeListener listener : listeners) {
|
for (ChromeRDPExchangeListener listener : listeners) {
|
||||||
|
|
|
@ -7,6 +7,8 @@ package org.teavm.chromerdp;
|
||||||
public interface ChromeRDPExchange {
|
public interface ChromeRDPExchange {
|
||||||
void send(String message);
|
void send(String message);
|
||||||
|
|
||||||
|
void disconnect();
|
||||||
|
|
||||||
void addListener(ChromeRDPExchangeListener listener);
|
void addListener(ChromeRDPExchangeListener listener);
|
||||||
|
|
||||||
void removeListener(ChromeRDPExchangeListener listener);
|
void removeListener(ChromeRDPExchangeListener listener);
|
||||||
|
|
|
@ -48,6 +48,6 @@ public class RDPBreakpoint implements JavaScriptBreakpoint {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isValid() {
|
public boolean isValid() {
|
||||||
return chromeId != null;
|
return chromeId != null && debugger != null && debugger.isAttached();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class Breakpoint {
|
public class Breakpoint {
|
||||||
private Debugger debugger;
|
private Debugger debugger;
|
||||||
List<JavaScriptBreakpoint> jsBreakpoints = new ArrayList<>();
|
volatile List<JavaScriptBreakpoint> jsBreakpoints = new ArrayList<>();
|
||||||
private SourceLocation location;
|
private SourceLocation location;
|
||||||
boolean valid;
|
boolean valid;
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,10 @@
|
||||||
package org.teavm.debugging;
|
package org.teavm.debugging;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
import java.util.concurrent.BlockingQueue;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
import java.util.concurrent.ConcurrentMap;
|
||||||
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -25,16 +29,18 @@ import org.teavm.model.MethodReference;
|
||||||
// TODO: variable name handling
|
// TODO: variable name handling
|
||||||
// TODO: class fields handling
|
// TODO: class fields handling
|
||||||
public class Debugger {
|
public class Debugger {
|
||||||
private List<DebuggerListener> listeners = new ArrayList<>();
|
private static final Object dummyObject = new Object();
|
||||||
|
private ConcurrentMap<DebuggerListener, Object> listeners = new ConcurrentHashMap<>();
|
||||||
private JavaScriptDebugger javaScriptDebugger;
|
private JavaScriptDebugger javaScriptDebugger;
|
||||||
private DebugInformationProvider debugInformationProvider;
|
private DebugInformationProvider debugInformationProvider;
|
||||||
private List<JavaScriptBreakpoint> temporaryJsBreakpoints = new ArrayList<>();
|
private BlockingQueue<JavaScriptBreakpoint> temporaryBreakpoints = new LinkedBlockingQueue<>();
|
||||||
private Map<String, DebugInformation> debugInformationMap = new HashMap<>();
|
private ConcurrentMap<String, DebugInformation> debugInformationMap = new ConcurrentHashMap<>();
|
||||||
private Map<String, List<DebugInformation>> debugInformationFileMap = new HashMap<>();
|
private ConcurrentMap<String, ConcurrentMap<DebugInformation, Object>> debugInformationFileMap =
|
||||||
private Map<DebugInformation, String> scriptMap = new HashMap<>();
|
new ConcurrentHashMap<>();
|
||||||
Map<JavaScriptBreakpoint, Breakpoint> breakpointMap = new HashMap<>();
|
private ConcurrentMap<DebugInformation, String> scriptMap = new ConcurrentHashMap<>();
|
||||||
Set<Breakpoint> breakpoints = new HashSet<>();
|
ConcurrentMap<JavaScriptBreakpoint, Breakpoint> breakpointMap = new ConcurrentHashMap<>();
|
||||||
private CallFrame[] callStack;
|
ConcurrentMap<Breakpoint, Object> breakpoints = new ConcurrentHashMap<>();
|
||||||
|
private volatile CallFrame[] callStack;
|
||||||
|
|
||||||
public Debugger(JavaScriptDebugger javaScriptDebugger, DebugInformationProvider debugInformationProvider) {
|
public Debugger(JavaScriptDebugger javaScriptDebugger, DebugInformationProvider debugInformationProvider) {
|
||||||
this.javaScriptDebugger = javaScriptDebugger;
|
this.javaScriptDebugger = javaScriptDebugger;
|
||||||
|
@ -43,36 +49,36 @@ public class Debugger {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addListener(DebuggerListener listener) {
|
public void addListener(DebuggerListener listener) {
|
||||||
listeners.add(listener);
|
listeners.put(listener, dummyObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void removeListener(DebuggerListener listener) {
|
public void removeListener(DebuggerListener listener) {
|
||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void suspend() {
|
public synchronized void suspend() {
|
||||||
javaScriptDebugger.suspend();
|
javaScriptDebugger.suspend();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void resume() {
|
public synchronized void resume() {
|
||||||
javaScriptDebugger.resume();
|
javaScriptDebugger.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stepInto() {
|
public synchronized void stepInto() {
|
||||||
javaScriptDebugger.stepInto();
|
javaScriptDebugger.stepInto();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stepOut() {
|
public synchronized void stepOut() {
|
||||||
javaScriptDebugger.stepOut();
|
javaScriptDebugger.stepOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stepOver() {
|
public synchronized void stepOver() {
|
||||||
javaScriptDebugger.stepOver();
|
javaScriptDebugger.stepOver();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DebugInformation> debugInformationBySource(String sourceFile) {
|
private List<DebugInformation> debugInformationBySource(String sourceFile) {
|
||||||
List<DebugInformation> list = debugInformationFileMap.get(sourceFile);
|
Map<DebugInformation, Object> list = debugInformationFileMap.get(sourceFile);
|
||||||
return list != null ? list : Collections.<DebugInformation>emptyList();
|
return list != null ? new ArrayList<>(list.keySet()) : Collections.<DebugInformation>emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void continueToLocation(SourceLocation location) {
|
public void continueToLocation(SourceLocation location) {
|
||||||
|
@ -90,7 +96,7 @@ public class Debugger {
|
||||||
location.getLine(), location.getColumn());
|
location.getLine(), location.getColumn());
|
||||||
JavaScriptBreakpoint jsBreakpoint = javaScriptDebugger.createBreakpoint(jsLocation);
|
JavaScriptBreakpoint jsBreakpoint = javaScriptDebugger.createBreakpoint(jsLocation);
|
||||||
if (jsBreakpoint != null) {
|
if (jsBreakpoint != null) {
|
||||||
temporaryJsBreakpoints.add(jsBreakpoint);
|
temporaryBreakpoints.add(jsBreakpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -107,14 +113,14 @@ public class Debugger {
|
||||||
|
|
||||||
public Breakpoint createBreakpoint(SourceLocation location) {
|
public Breakpoint createBreakpoint(SourceLocation location) {
|
||||||
Breakpoint breakpoint = new Breakpoint(this, location);
|
Breakpoint breakpoint = new Breakpoint(this, location);
|
||||||
breakpoints.add(breakpoint);
|
breakpoints.put(breakpoint, dummyObject);
|
||||||
updateInternalBreakpoints(breakpoint);
|
updateInternalBreakpoints(breakpoint);
|
||||||
updateBreakpointStatus(breakpoint, false);
|
updateBreakpointStatus(breakpoint, false);
|
||||||
return breakpoint;
|
return breakpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Set<Breakpoint> getBreakpoints() {
|
public Set<Breakpoint> getBreakpoints() {
|
||||||
return new HashSet<>(breakpoints);
|
return new HashSet<>(breakpoints.keySet());
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateInternalBreakpoints(Breakpoint breakpoint) {
|
void updateInternalBreakpoints(Breakpoint breakpoint) {
|
||||||
|
@ -122,7 +128,7 @@ public class Debugger {
|
||||||
breakpointMap.remove(jsBreakpoint);
|
breakpointMap.remove(jsBreakpoint);
|
||||||
jsBreakpoint.destroy();
|
jsBreakpoint.destroy();
|
||||||
}
|
}
|
||||||
breakpoint.jsBreakpoints.clear();
|
List<JavaScriptBreakpoint> jsBreakpoints = new ArrayList<>();
|
||||||
SourceLocation location = breakpoint.getLocation();
|
SourceLocation location = breakpoint.getLocation();
|
||||||
for (DebugInformation debugInformation : debugInformationBySource(location.getFileName())) {
|
for (DebugInformation debugInformation : debugInformationBySource(location.getFileName())) {
|
||||||
Collection<GeneratedLocation> locations = debugInformation.getGeneratedLocations(location);
|
Collection<GeneratedLocation> locations = debugInformation.getGeneratedLocations(location);
|
||||||
|
@ -130,10 +136,15 @@ public class Debugger {
|
||||||
JavaScriptLocation jsLocation = new JavaScriptLocation(scriptMap.get(debugInformation),
|
JavaScriptLocation jsLocation = new JavaScriptLocation(scriptMap.get(debugInformation),
|
||||||
genLocation.getLine(), genLocation.getColumn());
|
genLocation.getLine(), genLocation.getColumn());
|
||||||
JavaScriptBreakpoint jsBreakpoint = javaScriptDebugger.createBreakpoint(jsLocation);
|
JavaScriptBreakpoint jsBreakpoint = javaScriptDebugger.createBreakpoint(jsLocation);
|
||||||
breakpoint.jsBreakpoints.add(jsBreakpoint);
|
jsBreakpoints.add(jsBreakpoint);
|
||||||
breakpointMap.put(jsBreakpoint, breakpoint);
|
breakpointMap.put(jsBreakpoint, breakpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
breakpoint.jsBreakpoints = jsBreakpoints;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DebuggerListener[] getListeners() {
|
||||||
|
return listeners.keySet().toArray(new DebuggerListener[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateBreakpointStatus(Breakpoint breakpoint, boolean fireEvent) {
|
void updateBreakpointStatus(Breakpoint breakpoint, boolean fireEvent) {
|
||||||
|
@ -146,7 +157,7 @@ public class Debugger {
|
||||||
if (breakpoint.valid != valid) {
|
if (breakpoint.valid != valid) {
|
||||||
breakpoint.valid = valid;
|
breakpoint.valid = valid;
|
||||||
if (fireEvent) {
|
if (fireEvent) {
|
||||||
for (DebuggerListener listener : listeners) {
|
for (DebuggerListener listener : getListeners()) {
|
||||||
listener.breakpointStatusChanged(breakpoint);
|
listener.breakpointStatusChanged(breakpoint);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -192,17 +203,23 @@ public class Debugger {
|
||||||
if (debugInfo == null) {
|
if (debugInfo == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
debugInformationMap.put(name, debugInfo);
|
if (debugInformationMap.putIfAbsent(name, debugInfo) != null) {
|
||||||
for (String sourceFile : debugInfo.getCoveredSourceFiles()) {
|
return;
|
||||||
List<DebugInformation> list = debugInformationFileMap.get(sourceFile);
|
|
||||||
if (list == null) {
|
|
||||||
list = new ArrayList<>();
|
|
||||||
debugInformationFileMap.put(sourceFile, list);
|
|
||||||
}
|
}
|
||||||
list.add(debugInfo);
|
for (String sourceFile : debugInfo.getCoveredSourceFiles()) {
|
||||||
|
ConcurrentMap<DebugInformation, Object> list = debugInformationFileMap.get(sourceFile);
|
||||||
|
if (list == null) {
|
||||||
|
list = new ConcurrentHashMap<>();
|
||||||
|
ConcurrentMap<DebugInformation, Object> existing = debugInformationFileMap.putIfAbsent(
|
||||||
|
sourceFile, list);
|
||||||
|
if (existing != null) {
|
||||||
|
list = existing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
list.put(debugInfo, dummyObject);
|
||||||
}
|
}
|
||||||
scriptMap.put(debugInfo, name);
|
scriptMap.put(debugInfo, name);
|
||||||
for (Breakpoint breakpoint : breakpoints) {
|
for (Breakpoint breakpoint : breakpoints.keySet()) {
|
||||||
updateInternalBreakpoints(breakpoint);
|
updateInternalBreakpoints(breakpoint);
|
||||||
updateBreakpointStatus(breakpoint, true);
|
updateBreakpointStatus(breakpoint, true);
|
||||||
}
|
}
|
||||||
|
@ -212,40 +229,45 @@ public class Debugger {
|
||||||
return javaScriptDebugger.isAttached();
|
return javaScriptDebugger.isAttached();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void detach() {
|
||||||
|
javaScriptDebugger.detach();
|
||||||
|
}
|
||||||
|
|
||||||
void destroyBreakpoint(Breakpoint breakpoint) {
|
void destroyBreakpoint(Breakpoint breakpoint) {
|
||||||
for (JavaScriptBreakpoint jsBreakpoint : breakpoint.jsBreakpoints) {
|
for (JavaScriptBreakpoint jsBreakpoint : breakpoint.jsBreakpoints) {
|
||||||
jsBreakpoint.destroy();
|
jsBreakpoint.destroy();
|
||||||
breakpointMap.remove(jsBreakpoint);
|
breakpointMap.remove(jsBreakpoint);
|
||||||
}
|
}
|
||||||
breakpoint.jsBreakpoints.clear();
|
breakpoint.jsBreakpoints = new ArrayList<>();
|
||||||
breakpoints.remove(this);
|
breakpoints.remove(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireResumed() {
|
private void fireResumed() {
|
||||||
for (JavaScriptBreakpoint jsBreakpoint : temporaryJsBreakpoints) {
|
List<JavaScriptBreakpoint> termporaryBreakpoints = new ArrayList<>();
|
||||||
|
this.temporaryBreakpoints.drainTo(termporaryBreakpoints);
|
||||||
|
for (JavaScriptBreakpoint jsBreakpoint : temporaryBreakpoints) {
|
||||||
jsBreakpoint.destroy();
|
jsBreakpoint.destroy();
|
||||||
}
|
}
|
||||||
temporaryJsBreakpoints.clear();
|
for (DebuggerListener listener : getListeners()) {
|
||||||
for (DebuggerListener listener : listeners) {
|
|
||||||
listener.resumed();
|
listener.resumed();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireAttached() {
|
private void fireAttached() {
|
||||||
for (Breakpoint breakpoint : breakpoints) {
|
for (Breakpoint breakpoint : breakpoints.keySet()) {
|
||||||
updateInternalBreakpoints(breakpoint);
|
updateInternalBreakpoints(breakpoint);
|
||||||
updateBreakpointStatus(breakpoint, false);
|
updateBreakpointStatus(breakpoint, false);
|
||||||
}
|
}
|
||||||
for (DebuggerListener listener : listeners) {
|
for (DebuggerListener listener : getListeners()) {
|
||||||
listener.attached();
|
listener.attached();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireDetached() {
|
private void fireDetached() {
|
||||||
for (Breakpoint breakpoint : breakpoints) {
|
for (Breakpoint breakpoint : breakpoints.keySet()) {
|
||||||
updateBreakpointStatus(breakpoint, false);
|
updateBreakpointStatus(breakpoint, false);
|
||||||
}
|
}
|
||||||
for (DebuggerListener listener : listeners) {
|
for (DebuggerListener listener : getListeners()) {
|
||||||
listener.detached();
|
listener.detached();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -266,7 +288,7 @@ public class Debugger {
|
||||||
@Override
|
@Override
|
||||||
public void paused() {
|
public void paused() {
|
||||||
callStack = null;
|
callStack = null;
|
||||||
for (DebuggerListener listener : listeners) {
|
for (DebuggerListener listener : getListeners()) {
|
||||||
listener.paused();
|
listener.paused();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,6 +40,8 @@ public interface JavaScriptDebugger {
|
||||||
|
|
||||||
boolean isAttached();
|
boolean isAttached();
|
||||||
|
|
||||||
|
void detach();
|
||||||
|
|
||||||
JavaScriptCallFrame[] getCallStack();
|
JavaScriptCallFrame[] getCallStack();
|
||||||
|
|
||||||
JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location);
|
JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location);
|
||||||
|
|
|
@ -14,6 +14,7 @@ import org.teavm.chromerdp.ChromeRDPServer;
|
||||||
import org.teavm.debugging.Breakpoint;
|
import org.teavm.debugging.Breakpoint;
|
||||||
import org.teavm.debugging.Debugger;
|
import org.teavm.debugging.Debugger;
|
||||||
import org.teavm.debugging.DebuggerListener;
|
import org.teavm.debugging.DebuggerListener;
|
||||||
|
import org.teavm.debugging.JavaScriptDebugger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
|
@ -23,6 +24,7 @@ import org.teavm.debugging.DebuggerListener;
|
||||||
public class TeaVMDebugTarget implements IDebugTarget, IStep {
|
public class TeaVMDebugTarget implements IDebugTarget, IStep {
|
||||||
ILaunch launch;
|
ILaunch launch;
|
||||||
Debugger teavmDebugger;
|
Debugger teavmDebugger;
|
||||||
|
JavaScriptDebugger jsDebugger;
|
||||||
private ChromeRDPServer server;
|
private ChromeRDPServer server;
|
||||||
private boolean terminated;
|
private boolean terminated;
|
||||||
private TeaVMDebugProcess process;
|
private TeaVMDebugProcess process;
|
||||||
|
@ -90,6 +92,7 @@ public class TeaVMDebugTarget implements IDebugTarget, IStep {
|
||||||
terminated = true;
|
terminated = true;
|
||||||
server.stop();
|
server.stop();
|
||||||
fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
|
fireEvent(new DebugEvent(this, DebugEvent.TERMINATE));
|
||||||
|
fireEvent(new DebugEvent(thread, DebugEvent.TERMINATE));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -187,6 +190,7 @@ public class TeaVMDebugTarget implements IDebugTarget, IStep {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void disconnect() throws DebugException {
|
public void disconnect() throws DebugException {
|
||||||
|
teavmDebugger.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
package org.teavm.eclipse.debugger;
|
package org.teavm.eclipse.debugger;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import org.eclipse.core.runtime.CoreException;
|
import org.eclipse.core.runtime.CoreException;
|
||||||
import org.eclipse.core.runtime.IProgressMonitor;
|
import org.eclipse.core.runtime.IProgressMonitor;
|
||||||
import org.eclipse.debug.core.ILaunch;
|
import org.eclipse.debug.core.ILaunch;
|
||||||
import org.eclipse.debug.core.ILaunchConfiguration;
|
import org.eclipse.debug.core.ILaunchConfiguration;
|
||||||
import org.eclipse.debug.core.ILaunchManager;
|
import org.eclipse.debug.core.ILaunchManager;
|
||||||
import org.eclipse.debug.core.model.ILaunchConfigurationDelegate;
|
import org.eclipse.debug.core.model.ILaunchConfigurationDelegate;
|
||||||
import org.eclipse.ui.PlatformUI;
|
import org.teavm.chromerdp.ChromeRDPDebugger;
|
||||||
import org.teavm.chromerdp.*;
|
import org.teavm.chromerdp.ChromeRDPServer;
|
||||||
import org.teavm.debugging.Debugger;
|
import org.teavm.debugging.Debugger;
|
||||||
import org.teavm.debugging.URLDebugInformationProvider;
|
import org.teavm.debugging.URLDebugInformationProvider;
|
||||||
|
|
||||||
|
@ -29,64 +26,15 @@ public class TeaVMLaunchConfigurationDelegate implements ILaunchConfigurationDel
|
||||||
final ChromeRDPServer server = new ChromeRDPServer();
|
final ChromeRDPServer server = new ChromeRDPServer();
|
||||||
server.setPort(port);
|
server.setPort(port);
|
||||||
ChromeRDPDebugger jsDebugger = new ChromeRDPDebugger();
|
ChromeRDPDebugger jsDebugger = new ChromeRDPDebugger();
|
||||||
server.setExchangeConsumer(new SynchronousMessageExchange(jsDebugger));
|
server.setExchangeConsumer(jsDebugger);
|
||||||
Debugger debugger = new Debugger(jsDebugger, new URLDebugInformationProvider(""));
|
Debugger debugger = new Debugger(jsDebugger, new URLDebugInformationProvider(""));
|
||||||
new Thread() {
|
new Thread() {
|
||||||
@Override public void run() {
|
@Override public void run() {
|
||||||
server.start();
|
server.start();
|
||||||
}
|
}
|
||||||
}.start();
|
}.start();
|
||||||
launch.addDebugTarget(new TeaVMDebugTarget(launch, debugger, server));
|
TeaVMDebugTarget target = new TeaVMDebugTarget(launch, debugger, server);
|
||||||
}
|
launch.addDebugTarget(target);
|
||||||
|
launch.addProcess(target.getProcess());
|
||||||
private static class SynchronousMessageExchange implements ChromeRDPExchangeConsumer,
|
|
||||||
ChromeRDPExchange {
|
|
||||||
private List<ChromeRDPExchangeListener> listeners = new ArrayList<>();
|
|
||||||
private ChromeRDPDebugger debugger;
|
|
||||||
private ChromeRDPExchange exchange;
|
|
||||||
|
|
||||||
public SynchronousMessageExchange(ChromeRDPDebugger debugger) {
|
|
||||||
this.debugger = debugger;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setExchange(ChromeRDPExchange exchange) {
|
|
||||||
this.exchange = exchange;
|
|
||||||
debugger.setExchange(this);
|
|
||||||
exchange.addListener(new ChromeRDPExchangeListener() {
|
|
||||||
@Override public void received(final String message) throws IOException {
|
|
||||||
postToUIThread(message);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void postToUIThread(final String message) {
|
|
||||||
PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() {
|
|
||||||
@Override public void run() {
|
|
||||||
try {
|
|
||||||
for (ChromeRDPExchangeListener listener : listeners) {
|
|
||||||
listener.received(message);
|
|
||||||
}
|
|
||||||
} catch (IOException e) {
|
|
||||||
// TODO: add logging
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void send(String message) {
|
|
||||||
exchange.send(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addListener(ChromeRDPExchangeListener listener) {
|
|
||||||
listeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeListener(ChromeRDPExchangeListener listener) {
|
|
||||||
listeners.remove(listener);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class TeaVMStackFrame implements IStackFrame {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canResume() {
|
public boolean canResume() {
|
||||||
return false;
|
return thread.getTopStackFrame() == this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -29,11 +29,13 @@ public class TeaVMThread implements IThread {
|
||||||
@Override
|
@Override
|
||||||
public void resumed() {
|
public void resumed() {
|
||||||
updateStackTrace();
|
updateStackTrace();
|
||||||
|
fireEvent(new DebugEvent(TeaVMThread.this, DebugEvent.RESUME));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void paused() {
|
public void paused() {
|
||||||
updateStackTrace();
|
updateStackTrace();
|
||||||
|
fireEvent(new DebugEvent(TeaVMThread.this, DebugEvent.SUSPEND));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -70,16 +72,17 @@ public class TeaVMThread implements IThread {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTerminate() {
|
public boolean canTerminate() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTerminated() {
|
public boolean isTerminated() {
|
||||||
return false;
|
return debugTarget.isTerminated();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void terminate() throws DebugException {
|
public void terminate() throws DebugException {
|
||||||
|
debugTarget.terminate();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
@SuppressWarnings("rawtypes")
|
||||||
|
@ -190,6 +193,6 @@ public class TeaVMThread implements IThread {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasStackFrames() throws DebugException {
|
public boolean hasStackFrames() throws DebugException {
|
||||||
return true;
|
return stackTrace != null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user