Refactor Debugger interface for attaching/detaching. Fix bugs with line

mapping generation
This commit is contained in:
konsoletyper 2014-07-30 14:43:36 +04:00
parent 81fff7a4a6
commit f6853eda80
18 changed files with 296 additions and 141 deletions

View File

@ -0,0 +1,143 @@
package org.teavm.chromerpd;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.teavm.debugging.*;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class ChromeRDPDebugger implements JavaScriptDebugger {
private ChromeRDPDebuggerEndpoint endpoint;
private List<JavaScriptDebuggerListener> listeners = new ArrayList<>();
private Set<RDPBreakpoint> breakpoints = new HashSet<>();
void setEndpoint(ChromeRDPDebuggerEndpoint endpoint) {
if (this.endpoint == endpoint) {
return;
}
this.endpoint = endpoint;
if (endpoint != null) {
for (RDPBreakpoint breakpoint : breakpoints) {
endpoint.updateBreakpoint(breakpoint);
}
for (JavaScriptDebuggerListener listener : listeners) {
listener.attached();
}
} else {
for (JavaScriptDebuggerListener listener : listeners) {
listener.detached();
}
}
}
@Override
public void addListener(JavaScriptDebuggerListener listener) {
listeners.add(listener);
}
@Override
public void removeListener(JavaScriptDebuggerListener listener) {
listeners.remove(listener);
}
@Override
public void suspend() {
if (endpoint != null) {
endpoint.suspend();
}
}
@Override
public void resume() {
if (endpoint != null) {
endpoint.resume();
}
}
@Override
public void stepInto() {
if (endpoint != null) {
endpoint.stepInto();
}
}
@Override
public void stepOut() {
if (endpoint != null) {
endpoint.stepOut();
}
}
@Override
public void stepOver() {
if (endpoint != null) {
endpoint.stepOver();
}
}
@Override
public void continueToLocation(JavaScriptLocation location) {
if (endpoint != null) {
endpoint.continueToLocation(location);
}
}
@Override
public boolean isSuspended() {
return endpoint != null && endpoint.isSuspended();
}
@Override
public boolean isAttached() {
return endpoint != null;
}
@Override
public JavaScriptCallFrame[] getCallStack() {
return endpoint != null ? endpoint.getCallStack() : null;
}
@Override
public JavaScriptBreakpoint getCurrentBreakpoint() {
return endpoint != null ? endpoint.getCurrentBreakpoint() : null;
}
@Override
public JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) {
RDPBreakpoint breakpoint = new RDPBreakpoint(this, location);
breakpoints.add(breakpoint);
if (endpoint != null) {
endpoint.updateBreakpoint(breakpoint);
}
return breakpoint;
}
void destroyBreakpoint(RDPBreakpoint breakpoint) {
breakpoints.remove(breakpoint);
if (endpoint != null) {
endpoint.destroyBreakpoint(breakpoint);
}
}
void fireResumed() {
for (JavaScriptDebuggerListener listener : listeners) {
listener.resumed();
}
}
void firePaused() {
for (JavaScriptDebuggerListener listener : listeners) {
listener.paused();
}
}
void fireScriptAdded(String script) {
for (JavaScriptDebuggerListener listener : listeners) {
listener.scriptAdded(script);
}
}
}

View File

@ -16,14 +16,9 @@
package org.teavm.chromerpd; package org.teavm.chromerpd;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import javax.websocket.OnClose; import javax.websocket.*;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint; import javax.websocket.server.ServerEndpoint;
import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ObjectMapper;
@ -39,8 +34,7 @@ import org.teavm.debugging.*;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
@ServerEndpoint("/") @ServerEndpoint("/")
public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger { public class ChromeRDPDebuggerEndpoint {
private List<JavaScriptDebuggerListener> listeners = new ArrayList<>();
private Session session; private Session session;
private RDPCallFrame[] callStack = new RDPCallFrame[0]; private RDPCallFrame[] callStack = new RDPCallFrame[0];
private Map<String, String> scripts = new HashMap<>(); private Map<String, String> scripts = new HashMap<>();
@ -50,24 +44,24 @@ public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
private Map<Integer, Deferred> deferredResponses = new HashMap<>(); private Map<Integer, Deferred> deferredResponses = new HashMap<>();
private int messageIdGenerator; private int messageIdGenerator;
boolean closed; boolean closed;
private ChromeRDPContainer container; private ChromeRDPDebugger debugger;
@OnOpen @OnOpen
public void open(Session session) { public void open(Session session) {
this.session = session; this.session = session;
Object container = session.getUserProperties().get("rdp.container"); Object debugger = session.getUserProperties().get("chrome.rdp");
if (container instanceof ChromeRDPContainer) { if (debugger instanceof ChromeRDPDebugger) {
this.container = (ChromeRDPContainer)container; this.debugger = (ChromeRDPDebugger)debugger;
this.container.setDebugger(this); this.debugger.setEndpoint(this);
} }
} }
@OnClose @OnClose
public void close() { public void close() {
closed = true; closed = true;
if (this.container != null) { if (this.debugger != null) {
this.container.setDebugger(null); this.debugger.setEndpoint(null);
this.container = null; this.debugger = null;
} }
} }
@ -104,9 +98,7 @@ public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
for (int i = 0; i < callFrames.length; ++i) { for (int i = 0; i < callFrames.length; ++i) {
callFrames[i] = map(callFrameDTOs[i]); callFrames[i] = map(callFrameDTOs[i]);
} }
for (JavaScriptDebuggerListener listener : listeners) { debugger.firePaused();
listener.paused();
}
} }
RDPCallFrame map(CallFrameDTO dto) { RDPCallFrame map(CallFrameDTO dto) {
@ -127,9 +119,7 @@ public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
private void fireResumed() { private void fireResumed() {
suspended = false; suspended = false;
for (JavaScriptDebuggerListener listener : listeners) { debugger.fireResumed();
listener.resumed();
}
} }
private void scriptParsed(ScriptParsedNotification params) { private void scriptParsed(ScriptParsedNotification params) {
@ -138,19 +128,7 @@ public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
} }
scripts.put(params.getScriptId(), params.getUrl()); scripts.put(params.getScriptId(), params.getUrl());
scriptIds.put(params.getUrl(), params.getScriptId()); scriptIds.put(params.getUrl(), params.getScriptId());
for (JavaScriptDebuggerListener listener : listeners) { debugger.fireScriptAdded(params.getUrl());
listener.scriptAdded(params.getUrl());
}
}
@Override
public void addListener(JavaScriptDebuggerListener listener) {
listeners.add(listener);
}
@Override
public void removeListener(JavaScriptDebuggerListener listener) {
listeners.remove(listener);
} }
private void sendMessage(Message message) { private void sendMessage(Message message) {
@ -165,7 +143,6 @@ public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
} }
} }
@Override
public void suspend() { public void suspend() {
if (closed) { if (closed) {
return; return;
@ -175,7 +152,6 @@ public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
sendMessage(message); sendMessage(message);
} }
@Override
public void resume() { public void resume() {
if (closed) { if (closed) {
return; return;
@ -185,7 +161,6 @@ public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
sendMessage(message); sendMessage(message);
} }
@Override
public void stepInto() { public void stepInto() {
if (closed) { if (closed) {
return; return;
@ -195,7 +170,6 @@ public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
sendMessage(message); sendMessage(message);
} }
@Override
public void stepOut() { public void stepOut() {
if (closed) { if (closed) {
return; return;
@ -205,7 +179,6 @@ public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
sendMessage(message); sendMessage(message);
} }
@Override
public void stepOver() { public void stepOver() {
if (closed) { if (closed) {
return; return;
@ -215,7 +188,6 @@ public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
sendMessage(message); sendMessage(message);
} }
@Override
public void continueToLocation(JavaScriptLocation location) { public void continueToLocation(JavaScriptLocation location) {
Message message = new Message(); Message message = new Message();
message.setMethod("Debugger.continueToLocation"); message.setMethod("Debugger.continueToLocation");
@ -225,28 +197,24 @@ public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
sendMessage(message); sendMessage(message);
} }
@Override
public boolean isSuspended() { public boolean isSuspended() {
return suspended; return suspended;
} }
@Override
public JavaScriptCallFrame[] getCallStack() { public JavaScriptCallFrame[] getCallStack() {
return callStack; return callStack;
} }
@Override
public JavaScriptBreakpoint getCurrentBreakpoint() { public JavaScriptBreakpoint getCurrentBreakpoint() {
return null; return null;
} }
@Override public void updateBreakpoint(RDPBreakpoint breakpoint) {
public JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) {
Message message = new Message(); Message message = new Message();
message.setId(messageIdGenerator++); message.setId(messageIdGenerator++);
message.setMethod("Debugger.setBreakpoint"); message.setMethod("Debugger.setBreakpoint");
SetBreakpointCommand params = new SetBreakpointCommand(); SetBreakpointCommand params = new SetBreakpointCommand();
params.setLocation(unmap(location)); params.setLocation(unmap(breakpoint.getLocation()));
message.setParams(mapper.valueToTree(params)); message.setParams(mapper.valueToTree(params));
Deferred deferred = new Deferred(); Deferred deferred = new Deferred();
deferredResponses.put(message.getId(), deferred); deferredResponses.put(message.getId(), deferred);
@ -254,18 +222,20 @@ public class ChromeRDPDebuggerEndpoint implements JavaScriptDebugger {
try { try {
SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class) SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class)
.readValue((JsonNode)deferred.get()); .readValue((JsonNode)deferred.get());
return new RDPBreakpoint(response.getBreakpointId(), this, map(response.getActualLocation())); breakpoint.chromeId = response.getBreakpointId();
} catch (IOException e) { } catch (IOException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} }
void destroyBreakpoint(RDPBreakpoint breakpoint) { void destroyBreakpoint(RDPBreakpoint breakpoint) {
Message message = new Message(); if (breakpoint.chromeId != null) {
message.setMethod("Debugger.removeBreakpoint"); Message message = new Message();
RemoveBreakpointCommand params = new RemoveBreakpointCommand(); message.setMethod("Debugger.removeBreakpoint");
params.setBreakpointId(breakpoint.getChromeId()); RemoveBreakpointCommand params = new RemoveBreakpointCommand();
message.setParams(mapper.valueToTree(params)); params.setBreakpointId(breakpoint.chromeId);
sendMessage(message); message.setParams(mapper.valueToTree(params));
sendMessage(message);
}
} }
} }

View File

@ -16,7 +16,6 @@
package org.teavm.chromerpd; package org.teavm.chromerpd;
import java.util.*; import java.util.*;
import java.util.concurrent.atomic.AtomicReference;
import javax.websocket.Decoder; import javax.websocket.Decoder;
import javax.websocket.Encoder; import javax.websocket.Encoder;
import javax.websocket.Extension; import javax.websocket.Extension;
@ -35,8 +34,7 @@ import org.teavm.debugging.JavaScriptDebugger;
public class ChromeRDPServer { public class ChromeRDPServer {
private int port = 2357; private int port = 2357;
private Appendable output = System.err; private Appendable output = System.err;
private AtomicReference<JavaScriptDebugger> debugger = new AtomicReference<>(); private ChromeRDPDebugger debugger = new ChromeRDPDebugger();
private List<ChromeRDPServerListener> listeners = new ArrayList<>();
public int getPort() { public int getPort() {
return port; return port;
@ -54,14 +52,6 @@ public class ChromeRDPServer {
this.output = output; this.output = output;
} }
public void addListener(ChromeRDPServerListener listener) {
listeners.add(listener);
}
public void removeListener(ChromeRDPServerListener listener) {
listeners.remove(listener);
}
public void start() { public void start() {
final Server server = new Server(); final Server server = new Server();
ServerConnector connector = new ServerConnector(server); ServerConnector connector = new ServerConnector(server);
@ -88,11 +78,7 @@ public class ChromeRDPServer {
private Map<String, Object> userProperties = new HashMap<>(); private Map<String, Object> userProperties = new HashMap<>();
public RPDEndpointConfig() { public RPDEndpointConfig() {
userProperties.put("rdp.container", new ChromeRDPContainer() { userProperties.put("chrome.rdp", debugger);
@Override public void setDebugger(ChromeRDPDebuggerEndpoint debugger) {
ChromeRDPServer.this.setDebugger(debugger);
}
});
} }
@Override @Override
@ -137,19 +123,6 @@ public class ChromeRDPServer {
} }
public JavaScriptDebugger getDebugger() { public JavaScriptDebugger getDebugger() {
return debugger.get(); return debugger;
}
void setDebugger(JavaScriptDebugger debugger) {
if (debugger != null) {
if (!this.debugger.compareAndSet(null, debugger)) {
throw new IllegalStateException("Can't handle more than one connection");
}
for (ChromeRDPServerListener listener : listeners) {
listener.connected(this);
}
} else {
this.debugger.set(null);
}
} }
} }

View File

@ -1,24 +0,0 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed 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.teavm.chromerpd;
/**
*
* @author Alexey Andreev
*/
public interface ChromeRDPServerListener {
void connected(ChromeRDPServer server);
}

View File

@ -23,20 +23,15 @@ import org.teavm.debugging.JavaScriptLocation;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public class RDPBreakpoint implements JavaScriptBreakpoint { public class RDPBreakpoint implements JavaScriptBreakpoint {
private String chromeId; String chromeId;
private ChromeRDPDebuggerEndpoint debugger; private ChromeRDPDebugger debugger;
private JavaScriptLocation location; private JavaScriptLocation location;
public RDPBreakpoint(String chromeId, ChromeRDPDebuggerEndpoint debugger, JavaScriptLocation location) { RDPBreakpoint(ChromeRDPDebugger debugger, JavaScriptLocation location) {
this.chromeId = chromeId;
this.debugger = debugger; this.debugger = debugger;
this.location = location; this.location = location;
} }
public String getChromeId() {
return chromeId;
}
@Override @Override
public JavaScriptLocation getLocation() { public JavaScriptLocation getLocation() {
return location; return location;
@ -44,8 +39,14 @@ public class RDPBreakpoint implements JavaScriptBreakpoint {
@Override @Override
public void destroy() { public void destroy() {
if (!debugger.closed) { if (debugger != null) {
debugger.destroyBreakpoint(this); debugger.destroyBreakpoint(this);
debugger = null;
} }
} }
@Override
public boolean isValid() {
return false;
}
} }

View File

@ -13,7 +13,6 @@
}, },
"background": { "background": {
"scripts": ["main.js"], "scripts": ["main.js"]
"persistent": false
} }
} }

View File

@ -169,6 +169,7 @@ public class SourceWriter implements Appendable, LocationProvider {
if (!minified) { if (!minified) {
innerWriter.append('\n'); innerWriter.append('\n');
column = 0; column = 0;
++line;
lineStart = true; lineStart = true;
} }
return this; return this;

View File

@ -15,6 +15,7 @@
*/ */
package org.teavm.debugging; package org.teavm.debugging;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
@ -23,16 +24,13 @@ import java.util.List;
*/ */
public class Breakpoint { public class Breakpoint {
private Debugger debugger; private Debugger debugger;
private List<JavaScriptBreakpoint> jsBreakpoints; List<JavaScriptBreakpoint> jsBreakpoints = new ArrayList<>();
private SourceLocation location; private SourceLocation location;
boolean valid;
Breakpoint(Debugger debugger, List<JavaScriptBreakpoint> jsBreakpoints, SourceLocation location) { Breakpoint(Debugger debugger, SourceLocation location) {
this.debugger = debugger; this.debugger = debugger;
this.jsBreakpoints = jsBreakpoints;
this.location = location; this.location = location;
for (JavaScriptBreakpoint jsBreakpoint : jsBreakpoints) {
debugger.breakpointMap.put(jsBreakpoint, this);
}
} }
public SourceLocation getLocation() { public SourceLocation getLocation() {
@ -44,11 +42,17 @@ public class Breakpoint {
jsBreakpoint.destroy(); jsBreakpoint.destroy();
debugger.breakpointMap.remove(jsBreakpoint); debugger.breakpointMap.remove(jsBreakpoint);
} }
debugger.breakpoints.remove(this);
jsBreakpoints.clear(); jsBreakpoints.clear();
debugger = null;
}
public boolean isValid() {
return valid;
} }
public boolean isDestroyed() { public boolean isDestroyed() {
return jsBreakpoints.isEmpty(); return debugger == null;
} }
public Debugger getDebugger() { public Debugger getDebugger() {

View File

@ -203,8 +203,8 @@ public class DebugInformation {
ensureLine(sourceLine); ensureLine(sourceLine);
generatedLocationSize.set(sourceLine, generatedLocationSize.get(sourceLine) + 1); generatedLocationSize.set(sourceLine, generatedLocationSize.get(sourceLine) + 1);
int slot = generatedLocationStart.get(sourceLine); int slot = generatedLocationStart.get(sourceLine);
slot = addData(slot, column);
slot = addData(slot, line); slot = addData(slot, line);
slot = addData(slot, column);
generatedLocationStart.set(sourceLine, slot); generatedLocationStart.set(sourceLine, slot);
} }

View File

@ -109,6 +109,13 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
IntegerArray values = new IntegerArray(1); IntegerArray values = new IntegerArray(1);
public void add(LocationProvider location, int value) { public void add(LocationProvider location, int value) {
if (lines.size() > 1) {
int last = lines.size() - 1;
if (lines.get(last) == location.getLine() && columns.get(last) == location.getColumn()) {
values.set(last, value);
return;
}
}
lines.add(location.getLine()); lines.add(location.getLine());
columns.add(location.getColumn()); columns.add(location.getColumn());
values.add(value); values.add(value);

View File

@ -41,6 +41,7 @@ class DebugInformationReader {
debugInfo.classMapping = readMapping(); debugInfo.classMapping = readMapping();
debugInfo.methodMapping = readMapping(); debugInfo.methodMapping = readMapping();
debugInfo.rebuildFileDescriptions(); debugInfo.rebuildFileDescriptions();
debugInfo.rebuildMaps();
return debugInfo; return debugInfo;
} }

View File

@ -52,8 +52,9 @@ class DebugInformationWriter {
int[] lines = mapping.lines.clone(); int[] lines = mapping.lines.clone();
int last = 0; int last = 0;
for (int i = 0; i < lines.length; ++i) { for (int i = 0; i < lines.length; ++i) {
last = lines[i]; int next = lines[i];
lines[i] -= last; lines[i] -= last;
last = next;
} }
writeRle(lines); writeRle(lines);
resetRelativeNumber(); resetRelativeNumber();

View File

@ -33,6 +33,7 @@ public class Debugger {
private Map<String, List<DebugInformation>> debugInformationFileMap = new HashMap<>(); private Map<String, List<DebugInformation>> debugInformationFileMap = new HashMap<>();
private Map<DebugInformation, String> scriptMap = new HashMap<>(); private Map<DebugInformation, String> scriptMap = new HashMap<>();
Map<JavaScriptBreakpoint, Breakpoint> breakpointMap = new HashMap<>(); Map<JavaScriptBreakpoint, Breakpoint> breakpointMap = new HashMap<>();
Set<Breakpoint> breakpoints = new HashSet<>();
private CallFrame[] callStack; private CallFrame[] callStack;
public Debugger(JavaScriptDebugger javaScriptDebugger, DebugInformationProvider debugInformationProvider) { public Debugger(JavaScriptDebugger javaScriptDebugger, DebugInformationProvider debugInformationProvider) {
@ -58,23 +59,14 @@ public class Debugger {
} }
public void stepInto() { public void stepInto() {
if (!javaScriptDebugger.isSuspended()) {
return;
}
javaScriptDebugger.stepInto(); javaScriptDebugger.stepInto();
} }
public void stepOut() { public void stepOut() {
if (!javaScriptDebugger.isSuspended()) {
return;
}
javaScriptDebugger.stepOut(); javaScriptDebugger.stepOut();
} }
public void stepOver() { public void stepOver() {
if (!javaScriptDebugger.isSuspended()) {
return;
}
javaScriptDebugger.stepOver(); javaScriptDebugger.stepOver();
} }
@ -114,19 +106,49 @@ public class Debugger {
} }
public Breakpoint createBreakpoint(SourceLocation location) { public Breakpoint createBreakpoint(SourceLocation location) {
List<JavaScriptBreakpoint> jsBreakpoints = new ArrayList<>(); Breakpoint breakpoint = new Breakpoint(this, location);
breakpoints.add(breakpoint);
updateInternalBreakpoints(breakpoint);
updateBreakpointStatus(breakpoint, false);
return breakpoint;
}
public Set<Breakpoint> getBreakpoints() {
return Collections.unmodifiableSet(breakpoints);
}
void updateInternalBreakpoints(Breakpoint breakpoint) {
for (JavaScriptBreakpoint jsBreakpoint : breakpoint.jsBreakpoints) {
breakpointMap.remove(jsBreakpoint);
jsBreakpoint.destroy();
}
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);
for (GeneratedLocation genLocation : locations) { for (GeneratedLocation genLocation : locations) {
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);
if (jsBreakpoint != null) { breakpoint.jsBreakpoints.add(jsBreakpoint);
jsBreakpoints.add(jsBreakpoint); }
}
}
void updateBreakpointStatus(Breakpoint breakpoint, boolean fireEvent) {
boolean valid = false;
for (JavaScriptBreakpoint jsBreakpoint : breakpoint.jsBreakpoints) {
if (jsBreakpoint.isValid()) {
valid = true;
}
}
if (breakpoint.valid != valid) {
breakpoint.valid = valid;
if (fireEvent) {
for (DebuggerListener listener : listeners) {
listener.breakpointStatusChanged(breakpoint);
} }
} }
} }
return !jsBreakpoints.isEmpty() ? new Breakpoint(this, jsBreakpoints, location) : null;
} }
public CallFrame[] getCallStack() { public CallFrame[] getCallStack() {
@ -178,6 +200,14 @@ public class Debugger {
list.add(debugInfo); list.add(debugInfo);
} }
scriptMap.put(debugInfo, name); scriptMap.put(debugInfo, name);
for (Breakpoint breakpoint : breakpoints) {
updateInternalBreakpoints(breakpoint);
updateBreakpointStatus(breakpoint, true);
}
}
public boolean isAttached() {
return javaScriptDebugger.isAttached();
} }
private JavaScriptDebuggerListener javaScriptListener = new JavaScriptDebuggerListener() { private JavaScriptDebuggerListener javaScriptListener = new JavaScriptDebuggerListener() {
@ -203,5 +233,35 @@ public class Debugger {
public void scriptAdded(String name) { public void scriptAdded(String name) {
addScript(name); addScript(name);
} }
@Override
public void attached() {
for (Breakpoint breakpoint : breakpoints) {
updateInternalBreakpoints(breakpoint);
updateBreakpointStatus(breakpoint, false);
}
for (DebuggerListener listener : listeners) {
listener.attached();
}
}
@Override
public void detached() {
for (Breakpoint breakpoint : breakpoints) {
updateBreakpointStatus(breakpoint, true);
}
for (DebuggerListener listener : listeners) {
listener.detached();
}
}
@Override
public void breakpointChanged(JavaScriptBreakpoint jsBreakpoint) {
Breakpoint breakpoint = breakpointMap.get(jsBreakpoint);
if (breakpoint != null) {
updateInternalBreakpoints(breakpoint);
updateBreakpointStatus(breakpoint, false);
}
}
}; };
} }

View File

@ -23,4 +23,10 @@ public interface DebuggerListener {
void resumed(); void resumed();
void paused(); void paused();
void breakpointStatusChanged(Breakpoint breakpoint);
void attached();
void detached();
} }

View File

@ -22,5 +22,7 @@ package org.teavm.debugging;
public interface JavaScriptBreakpoint { public interface JavaScriptBreakpoint {
JavaScriptLocation getLocation(); JavaScriptLocation getLocation();
boolean isValid();
void destroy(); void destroy();
} }

View File

@ -38,6 +38,8 @@ public interface JavaScriptDebugger {
boolean isSuspended(); boolean isSuspended();
boolean isAttached();
JavaScriptCallFrame[] getCallStack(); JavaScriptCallFrame[] getCallStack();
JavaScriptBreakpoint getCurrentBreakpoint(); JavaScriptBreakpoint getCurrentBreakpoint();

View File

@ -24,5 +24,11 @@ public interface JavaScriptDebuggerListener {
void resumed(); void resumed();
void attached();
void detached();
void breakpointChanged(JavaScriptBreakpoint breakpoint);
void scriptAdded(String name); void scriptAdded(String name);
} }

View File

@ -1498,6 +1498,9 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append("("); writer.append("(");
expr.getExpr().acceptVisitor(this); expr.getExpr().acceptVisitor(this);
writer.append(" instanceof ").appendClass(clsName).append(")"); writer.append(" instanceof ").appendClass(clsName).append(")");
if (expr.getLocation() != null) {
popLocation();
}
return; return;
} }
} }