Begin implementation of google chrome debugger backend

This commit is contained in:
Alexey Andreev 2014-07-28 23:54:33 +04:00
parent 444c599b17
commit 0cb0bd469b
20 changed files with 810 additions and 23 deletions

25
pom.xml
View File

@ -65,8 +65,10 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<html4j.version>0.7.5</html4j.version>
<sonatypeOssDistMgmtSnapshotsUrl>https://oss.sonatype.org/content/repositories/snapshots/</sonatypeOssDistMgmtSnapshotsUrl> <sonatypeOssDistMgmtSnapshotsUrl>https://oss.sonatype.org/content/repositories/snapshots/</sonatypeOssDistMgmtSnapshotsUrl>
<html4j.version>0.7.5</html4j.version>
<jetty.version>9.2.1.v20140609</jetty.version>
<slf4j.version>1.7.7</slf4j.version>
</properties> </properties>
<modules> <modules>
@ -79,6 +81,7 @@
<module>teavm-samples</module> <module>teavm-samples</module>
<module>teavm-platform</module> <module>teavm-platform</module>
<module>teavm-cli</module> <module>teavm-cli</module>
<module>teavm-chrome-rdp</module>
</modules> </modules>
<dependencyManagement> <dependencyManagement>
@ -134,6 +137,26 @@
<artifactId>maven-core</artifactId> <artifactId>maven-core</artifactId>
<version>3.0</version> <version>3.0</version>
</dependency> </dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
<version>${jetty.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
</dependencies> </dependencies>
</dependencyManagement> </dependencyManagement>

4
teavm-chrome-rdp/.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
/target
/.settings
/.classpath
/.project

88
teavm-chrome-rdp/pom.xml Normal file
View File

@ -0,0 +1,88 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.teavm</groupId>
<artifactId>teavm</artifactId>
<version>0.2-SNAPSHOT</version>
</parent>
<artifactId>teavm-chrome-rdp</artifactId>
<name>TeaVM debugging backend for Google Chrome RDP</name>
<description>TeaVM debugging backend for Google Chrome RDP</description>
<dependencies>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
</dependency>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-core</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>javax.websocket</groupId>
<artifactId>javax.websocket-api</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
</dependency>
<dependency>
<groupId>org.eclipse.jetty.websocket</groupId>
<artifactId>javax-websocket-server-impl</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<version>1.9.13</version>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,202 @@
/*
* 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;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.JsonProcessingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.teavm.chromerpd.data.CallFrameDTO;
import org.teavm.chromerpd.data.LocationDTO;
import org.teavm.chromerpd.data.Message;
import org.teavm.chromerpd.messages.ContinueToLocationCommand;
import org.teavm.chromerpd.messages.ScriptParsedNotification;
import org.teavm.chromerpd.messages.SuspendedNotification;
import org.teavm.debugging.*;
/**
*
* @author Alexey Andreev
*/
@ServerEndpoint("/")
public class ChromeRDPDebugger implements JavaScriptDebugger {
private List<JavaScriptDebuggerListener> listeners = new ArrayList<>();
private Session session;
private RDPCallFrame[] callStack = new RDPCallFrame[0];
private Map<String, String> scripts = new HashMap<>();
private Map<String, String> scriptIds = new HashMap<>();
private boolean suspended;
private ObjectMapper mapper = new ObjectMapper();
@OnOpen
public void open(Session session) {
this.session = session;
}
@OnMessage
public void receive(String messageText) throws IOException {
Message message = mapper.reader(Message.class).readValue(messageText);
switch (message.getMethod()) {
case "Debugger.paused":
firePaused(parseJson(SuspendedNotification.class, message.getParams()));
break;
case "Debugger.resumed":
fireResumed();
break;
case "Debugger.scriptParsed":
scriptParsed(parseJson(ScriptParsedNotification.class, message.getParams()));
break;
}
}
private <T> T parseJson(Class<T> type, JsonNode node) throws IOException {
return mapper.reader(type).readValue(node);
}
private void firePaused(SuspendedNotification params) {
suspended = true;
CallFrameDTO[] callFrameDTOs = params.getCallFrames();
RDPCallFrame[] callFrames = new RDPCallFrame[callFrameDTOs.length];
for (int i = 0; i < callFrames.length; ++i) {
callFrames[i] = map(callFrameDTOs[i]);
}
for (JavaScriptDebuggerListener listener : listeners) {
listener.paused();
}
}
RDPCallFrame map(CallFrameDTO dto) {
return new RDPCallFrame(dto.getCallFrameId(), map(dto.getLocation()));
}
JavaScriptLocation map(LocationDTO dto) {
return new JavaScriptLocation(scripts.get(dto.getScriptId()), dto.getLineNumber(), dto.getColumnNumber());
}
private void fireResumed() {
suspended = false;
for (JavaScriptDebuggerListener listener : listeners) {
listener.resumed();
}
}
private void scriptParsed(ScriptParsedNotification params) {
if (scripts.containsKey(params.getScriptId())) {
return;
}
scripts.put(params.getScriptId(), params.getUrl());
scriptIds.put(params.getUrl(), params.getScriptId());
for (JavaScriptDebuggerListener listener : listeners) {
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) {
try {
String messageText = mapper.writer().writeValueAsString(message);
session.getAsyncRemote().sendText(messageText);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void suspend() {
Message message = new Message();
message.setMethod("Debugger.pause");
sendMessage(message);
}
@Override
public void resume() {
Message message = new Message();
message.setMethod("Debugger.resume");
sendMessage(message);
}
@Override
public void stepInto() {
Message message = new Message();
message.setMethod("Debugger.stepInto");
sendMessage(message);
}
@Override
public void stepOut() {
Message message = new Message();
message.setMethod("Debugger.stepOut");
sendMessage(message);
}
@Override
public void stepOver() {
Message message = new Message();
message.setMethod("Debugger.stepOver");
sendMessage(message);
}
@Override
public void continueToLocation(JavaScriptLocation location) {
Message message = new Message();
message.setMethod("Debugger.continueToLocation");
ContinueToLocationCommand params = new ContinueToLocationCommand();
LocationDTO locationDTO = new LocationDTO();
locationDTO.setScriptId(scriptIds.get(location.getScript()));
locationDTO.setLineNumber(location.getLine());
locationDTO.setColumnNumber(location.getColumn());
params.setLocation(locationDTO);
message.setParams(mapper.valueToTree(params));
sendMessage(message);
}
@Override
public boolean isSuspended() {
return suspended;
}
@Override
public JavaScriptCallFrame[] getCallStack() {
return callStack;
}
@Override
public JavaScriptBreakpoint getCurrentBreakpoint() {
return null;
}
@Override
public JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) {
return null;
}
}

View File

@ -0,0 +1,42 @@
/*
* 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;
import org.teavm.debugging.JavaScriptCallFrame;
import org.teavm.debugging.JavaScriptLocation;
/**
*
* @author Alexey Andreev
*/
public class RDPCallFrame implements JavaScriptCallFrame {
private String chromeId;
private JavaScriptLocation location;
public RDPCallFrame(String chromeId, JavaScriptLocation location) {
this.chromeId = chromeId;
this.location = location;
}
public String getChromeId() {
return chromeId;
}
@Override
public JavaScriptLocation getLocation() {
return location;
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.data;
/**
*
* @author Alexey Andreev
*/
public class CallFrameDTO {
private String callFrameId;
private LocationDTO location;
public String getCallFrameId() {
return callFrameId;
}
public void setCallFrameId(String callFrameId) {
this.callFrameId = callFrameId;
}
public LocationDTO getLocation() {
return location;
}
public void setLocation(LocationDTO location) {
this.location = location;
}
}

View File

@ -0,0 +1,50 @@
/*
* 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.data;
/**
*
* @author Alexey Andreev
*/
public class LocationDTO {
private int columnNumber;
private int lineNumber;
private String scriptId;
public int getColumnNumber() {
return columnNumber;
}
public void setColumnNumber(int columnNumber) {
this.columnNumber = columnNumber;
}
public int getLineNumber() {
return lineNumber;
}
public void setLineNumber(int lineNumber) {
this.lineNumber = lineNumber;
}
public String getScriptId() {
return scriptId;
}
public void setScriptId(String scriptId) {
this.scriptId = scriptId;
}
}

View File

@ -0,0 +1,43 @@
/*
* 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.data;
import org.codehaus.jackson.JsonNode;
/**
*
* @author Alexey Andreev
*/
public class Message {
private String method;
private JsonNode params;
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public JsonNode getParams() {
return params;
}
public void setParams(JsonNode params) {
this.params = params;
}
}

View File

@ -0,0 +1,34 @@
/*
* 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.messages;
import org.teavm.chromerpd.data.LocationDTO;
/**
*
* @author Alexey Andreev
*/
public class ContinueToLocationCommand {
private LocationDTO location;
public LocationDTO getLocation() {
return location;
}
public void setLocation(LocationDTO location) {
this.location = location;
}
}

View File

@ -0,0 +1,41 @@
/*
* 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.messages;
/**
*
* @author Alexey Andreev
*/
public class ScriptParsedNotification {
private String scriptId;
private String url;
public String getScriptId() {
return scriptId;
}
public void setScriptId(String scriptId) {
this.scriptId = scriptId;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}

View File

@ -0,0 +1,53 @@
/*
* 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.messages;
import org.codehaus.jackson.JsonNode;
import org.teavm.chromerpd.data.CallFrameDTO;
/**
*
* @author Alexey Andreev
*/
public class SuspendedNotification {
private CallFrameDTO[] callFrames;
private String reason;
private JsonNode data;
public CallFrameDTO[] getCallFrames() {
return callFrames;
}
public void setCallFrames(CallFrameDTO[] callFrames) {
this.callFrames = callFrames;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public JsonNode getData() {
return data;
}
public void setData(JsonNode data) {
this.data = data;
}
}

View File

@ -41,6 +41,10 @@ public class DebugInformation {
Mapping methodMapping; Mapping methodMapping;
Mapping lineMapping; Mapping lineMapping;
public String[] getCoveredSourceFiles() {
return fileNames.clone();
}
public Collection<GeneratedLocation> getGeneratedLocations(String fileName, int line) { public Collection<GeneratedLocation> getGeneratedLocations(String fileName, int line) {
Integer fileIndex = fileNameMap.get(fileName); Integer fileIndex = fileNameMap.get(fileName);
if (fileIndex == null) { if (fileIndex == null) {
@ -90,6 +94,10 @@ public class DebugInformation {
return new MethodReference(className, MethodDescriptor.parse(method)); return new MethodReference(className, MethodDescriptor.parse(method));
} }
public MethodReference getMethodAt(int line, int column) {
return getMethodAt(new GeneratedLocation(line, column));
}
private <T> T componentByKey(Mapping mapping, T[] values, GeneratedLocation location) { private <T> T componentByKey(Mapping mapping, T[] values, GeneratedLocation location) {
int keyIndex = indexByKey(mapping, location); int keyIndex = indexByKey(mapping, location);
int valueIndex = keyIndex >= 0 ? mapping.values[keyIndex] : -1; int valueIndex = keyIndex >= 0 ? mapping.values[keyIndex] : -1;
@ -106,7 +114,7 @@ public class DebugInformation {
writer.write(this); writer.write(this);
} }
public DebugInformation read(InputStream input) throws IOException { public static DebugInformation read(InputStream input) throws IOException {
DebugInformationReader reader = new DebugInformationReader(input); DebugInformationReader reader = new DebugInformationReader(input);
return reader.read(); return reader.read();
} }

View File

@ -0,0 +1,24 @@
/*
* 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.debugging;
/**
*
* @author Alexey Andreev
*/
public interface DebugInformationProvider {
DebugInformation getDebugInformation(String script);
}

View File

@ -27,14 +27,17 @@ import org.teavm.model.MethodReference;
public class Debugger { public class Debugger {
private List<DebuggerListener> listeners = new ArrayList<>(); private List<DebuggerListener> listeners = new ArrayList<>();
private JavaScriptDebugger javaScriptDebugger; private JavaScriptDebugger javaScriptDebugger;
private DebugInformation debugInformation; private DebugInformationProvider debugInformationProvider;
private List<JavaScriptBreakpoint> temporaryJsBreakpoints = new ArrayList<>(); private List<JavaScriptBreakpoint> temporaryJsBreakpoints = new ArrayList<>();
private Map<String, DebugInformation> debugInformationMap = new HashMap<>();
private Map<String, List<DebugInformation>> debugInformationFileMap = new HashMap<>();
private Map<DebugInformation, String> scriptMap = new HashMap<>();
Map<String, Breakpoint> breakpointMap = new HashMap<>(); Map<String, Breakpoint> breakpointMap = new HashMap<>();
private CallFrame[] callStack; private CallFrame[] callStack;
public Debugger(JavaScriptDebugger javaScriptDebugger, DebugInformation debugInformation) { public Debugger(JavaScriptDebugger javaScriptDebugger, DebugInformationProvider debugInformationProvider) {
this.javaScriptDebugger = javaScriptDebugger; this.javaScriptDebugger = javaScriptDebugger;
this.debugInformation = debugInformation; this.debugInformationProvider = debugInformationProvider;
javaScriptDebugger.addListener(javaScriptListener); javaScriptDebugger.addListener(javaScriptListener);
} }
@ -65,7 +68,6 @@ public class Debugger {
if (!javaScriptDebugger.isSuspended()) { if (!javaScriptDebugger.isSuspended()) {
return; return;
} }
// TODO: lookup all locations that are "out of" the current location and create temporary breakpoints
javaScriptDebugger.stepOut(); javaScriptDebugger.stepOut();
} }
@ -74,20 +76,28 @@ public class Debugger {
return; return;
} }
javaScriptDebugger.stepOver(); javaScriptDebugger.stepOver();
// TODO: lookup all locations that are "after" the current location and create temporary breakpoints }
private List<DebugInformation> debugInformationBySource(String sourceFile) {
List<DebugInformation> list = debugInformationFileMap.get(sourceFile);
return list != null ? list : Collections.<DebugInformation>emptyList();
} }
public void continueToLocation(String fileName, int line) { public void continueToLocation(String fileName, int line) {
if (!javaScriptDebugger.isSuspended()) { if (!javaScriptDebugger.isSuspended()) {
return; return;
} }
for (DebugInformation debugInformation : debugInformationBySource(fileName)) {
Collection<GeneratedLocation> locations = debugInformation.getGeneratedLocations(fileName, line); Collection<GeneratedLocation> locations = debugInformation.getGeneratedLocations(fileName, line);
for (GeneratedLocation location : locations) { for (GeneratedLocation location : locations) {
JavaScriptBreakpoint jsBreakpoint = javaScriptDebugger.createBreakpoint(location); JavaScriptLocation jsLocation = new JavaScriptLocation(scriptMap.get(debugInformation),
location.getLine(), location.getColumn());
JavaScriptBreakpoint jsBreakpoint = javaScriptDebugger.createBreakpoint(jsLocation);
if (jsBreakpoint != null) { if (jsBreakpoint != null) {
temporaryJsBreakpoints.add(jsBreakpoint); temporaryJsBreakpoints.add(jsBreakpoint);
} }
} }
}
javaScriptDebugger.resume(); javaScriptDebugger.resume();
} }
@ -96,14 +106,18 @@ public class Debugger {
} }
public Breakpoint createBreakpoint(String fileName, int line) { public Breakpoint createBreakpoint(String fileName, int line) {
Collection<GeneratedLocation> locations = debugInformation.getGeneratedLocations(fileName, line);
List<JavaScriptBreakpoint> jsBreakpoints = new ArrayList<>(); List<JavaScriptBreakpoint> jsBreakpoints = new ArrayList<>();
for (DebugInformation debugInformation : debugInformationBySource(fileName)) {
Collection<GeneratedLocation> locations = debugInformation.getGeneratedLocations(fileName, line);
for (GeneratedLocation location : locations) { for (GeneratedLocation location : locations) {
JavaScriptBreakpoint jsBreakpoint = javaScriptDebugger.createBreakpoint(location); JavaScriptLocation jsLocation = new JavaScriptLocation(scriptMap.get(debugInformation),
location.getLine(), location.getColumn());
JavaScriptBreakpoint jsBreakpoint = javaScriptDebugger.createBreakpoint(jsLocation);
if (jsBreakpoint != null) { if (jsBreakpoint != null) {
jsBreakpoints.add(jsBreakpoint); jsBreakpoints.add(jsBreakpoint);
} }
} }
}
return !jsBreakpoints.isEmpty() ? new Breakpoint(this, jsBreakpoints, fileName, line) : null; return !jsBreakpoints.isEmpty() ? new Breakpoint(this, jsBreakpoints, fileName, line) : null;
} }
@ -117,9 +131,17 @@ public class Debugger {
List<CallFrame> frames = new ArrayList<>(); List<CallFrame> frames = new ArrayList<>();
boolean wasEmpty = false; boolean wasEmpty = false;
for (JavaScriptCallFrame jsFrame : javaScriptDebugger.getCallStack()) { for (JavaScriptCallFrame jsFrame : javaScriptDebugger.getCallStack()) {
SourceLocation loc = debugInformation.getSourceLocation(jsFrame.getLocation()); DebugInformation debugInformation = debugInformationMap.get(jsFrame.getLocation().getScript());
SourceLocation loc;
if (debugInformation != null) {
loc = debugInformation.getSourceLocation(jsFrame.getLocation().getLine(),
jsFrame.getLocation().getColumn());
} else {
loc = null;
}
boolean empty = loc == null || (loc.getFileName() == null && loc.getLine() < 0); boolean empty = loc == null || (loc.getFileName() == null && loc.getLine() < 0);
MethodReference method = !empty ? debugInformation.getMethodAt(jsFrame.getLocation()) : null; MethodReference method = !empty ? debugInformation.getMethodAt(jsFrame.getLocation().getLine(),
jsFrame.getLocation().getColumn()) : null;
if (!empty || !wasEmpty) { if (!empty || !wasEmpty) {
frames.add(new CallFrame(loc, method)); frames.add(new CallFrame(loc, method));
} }
@ -130,6 +152,23 @@ public class Debugger {
return callStack.clone(); return callStack.clone();
} }
private void addScript(String name) {
if (debugInformationMap.containsKey(name)) {
return;
}
DebugInformation debugInfo = debugInformationProvider.getDebugInformation(name);
debugInformationMap.put(name, debugInfo);
for (String sourceFile : debugInfo.getCoveredSourceFiles()) {
List<DebugInformation> list = debugInformationFileMap.get(sourceFile);
if (list == null) {
list = new ArrayList<>();
debugInformationFileMap.put(sourceFile, list);
}
list.add(debugInfo);
}
scriptMap.put(debugInfo, name);
}
private JavaScriptDebuggerListener javaScriptListener = new JavaScriptDebuggerListener() { private JavaScriptDebuggerListener javaScriptListener = new JavaScriptDebuggerListener() {
@Override @Override
public void resumed() { public void resumed() {
@ -148,5 +187,10 @@ public class Debugger {
listener.paused(); listener.paused();
} }
} }
@Override
public void scriptAdded(String name) {
addScript(name);
}
}; };
} }

View File

@ -28,7 +28,7 @@ public interface JavaScriptBreakpoint {
String getFileName(); String getFileName();
GeneratedLocation getLocation(); JavaScriptLocation getLocation();
void destroy(); void destroy();
} }

View File

@ -20,5 +20,5 @@ package org.teavm.debugging;
* @author Alexey Andreev * @author Alexey Andreev
*/ */
public interface JavaScriptCallFrame { public interface JavaScriptCallFrame {
GeneratedLocation getLocation(); JavaScriptLocation getLocation();
} }

View File

@ -34,7 +34,7 @@ public interface JavaScriptDebugger {
void stepOver(); void stepOver();
void continueToLocation(GeneratedLocation location); void continueToLocation(JavaScriptLocation location);
boolean isSuspended(); boolean isSuspended();
@ -42,5 +42,5 @@ public interface JavaScriptDebugger {
JavaScriptBreakpoint getCurrentBreakpoint(); JavaScriptBreakpoint getCurrentBreakpoint();
JavaScriptBreakpoint createBreakpoint(GeneratedLocation location); JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location);
} }

View File

@ -23,4 +23,6 @@ public interface JavaScriptDebuggerListener {
void paused(); void paused();
void resumed(); void resumed();
void scriptAdded(String name);
} }

View File

@ -0,0 +1,44 @@
/*
* 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.debugging;
/**
*
* @author Alexey Andreev
*/
public class JavaScriptLocation {
private String script;
private int line;
private int column;
public JavaScriptLocation(String script, int line, int column) {
this.script = script;
this.line = line;
this.column = column;
}
public String getScript() {
return script;
}
public int getLine() {
return line;
}
public int getColumn() {
return column;
}
}

View File

@ -0,0 +1,44 @@
/*
* 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.debugging;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
/**
*
* @author Alexey Andreev
*/
public class URLDebugInformationProvider implements DebugInformationProvider {
private String baseURL;
public URLDebugInformationProvider(String baseURL) {
this.baseURL = baseURL;
}
@Override
public DebugInformation getDebugInformation(String script) {
try {
URL url = new URL(baseURL + script);
try (InputStream input = url.openStream()) {
return DebugInformation.read(input);
}
} catch (IOException e) {
return null;
}
}
}