mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Great progress toward "step over" operation
This commit is contained in:
parent
b05d1a7524
commit
4115785823
|
@ -6,6 +6,8 @@ import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
import java.util.concurrent.locks.Lock;
|
||||||
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
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.*;
|
import org.teavm.chromerdp.data.*;
|
||||||
|
@ -20,6 +22,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
private static final Object dummy = new Object();
|
private static final Object dummy = new Object();
|
||||||
private ChromeRDPExchange exchange;
|
private ChromeRDPExchange exchange;
|
||||||
private ConcurrentMap<JavaScriptDebuggerListener, Object> listeners = new ConcurrentHashMap<>();
|
private ConcurrentMap<JavaScriptDebuggerListener, Object> listeners = new ConcurrentHashMap<>();
|
||||||
|
private ConcurrentMap<JavaScriptLocation, RDPBreakpoint> breakpointLocationMap = new ConcurrentHashMap<>();
|
||||||
private ConcurrentMap<RDPBreakpoint, Object> breakpoints = new ConcurrentHashMap<>();
|
private ConcurrentMap<RDPBreakpoint, Object> breakpoints = new ConcurrentHashMap<>();
|
||||||
private volatile RDPCallFrame[] callStack = new RDPCallFrame[0];
|
private volatile RDPCallFrame[] callStack = new RDPCallFrame[0];
|
||||||
private ConcurrentMap<String, String> scripts = new ConcurrentHashMap<>();
|
private ConcurrentMap<String, String> scripts = new ConcurrentHashMap<>();
|
||||||
|
@ -28,6 +31,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
private ObjectMapper mapper = new ObjectMapper();
|
private ObjectMapper mapper = new ObjectMapper();
|
||||||
private ConcurrentMap<Integer, ResponseHandler> responseHandlers = new ConcurrentHashMap<>();
|
private ConcurrentMap<Integer, ResponseHandler> responseHandlers = new ConcurrentHashMap<>();
|
||||||
private AtomicInteger messageIdGenerator = new AtomicInteger();
|
private AtomicInteger messageIdGenerator = new AtomicInteger();
|
||||||
|
private Lock breakpointLock = new ReentrantLock();
|
||||||
|
|
||||||
private List<JavaScriptDebuggerListener> getListeners() {
|
private List<JavaScriptDebuggerListener> getListeners() {
|
||||||
return new ArrayList<>(listeners.keySet());
|
return new ArrayList<>(listeners.keySet());
|
||||||
|
@ -74,7 +78,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
if (jsonMessage.has("id")) {
|
if (jsonMessage.has("id")) {
|
||||||
Response response = mapper.reader(Response.class).readValue(jsonMessage);
|
Response response = mapper.reader(Response.class).readValue(jsonMessage);
|
||||||
if (response.getError() != null) {
|
if (response.getError() != null) {
|
||||||
System.err.println(response.getError().toString());
|
System.err.println("#" + jsonMessage.get("id") + ": " + response.getError().toString());
|
||||||
}
|
}
|
||||||
responseHandlers.remove(response.getId()).received(response.getResult());
|
responseHandlers.remove(response.getId()).received(response.getResult());
|
||||||
} else {
|
} else {
|
||||||
|
@ -235,15 +239,38 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) {
|
public JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) {
|
||||||
RDPBreakpoint breakpoint = new RDPBreakpoint(this, location);
|
RDPBreakpoint breakpoint;
|
||||||
breakpoints.put(breakpoint, dummy);
|
|
||||||
|
breakpointLock.lock();
|
||||||
|
try {
|
||||||
|
breakpoint = breakpointLocationMap.get(location);
|
||||||
|
if (breakpoint == null) {
|
||||||
|
breakpoint = new RDPBreakpoint(this, location);
|
||||||
|
breakpointLocationMap.put(location, breakpoint);
|
||||||
updateBreakpoint(breakpoint);
|
updateBreakpoint(breakpoint);
|
||||||
|
}
|
||||||
|
breakpoint.referenceCount.incrementAndGet();
|
||||||
|
breakpoints.put(breakpoint, dummy);
|
||||||
|
} finally {
|
||||||
|
breakpointLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
return breakpoint;
|
return breakpoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
void destroyBreakpoint(RDPBreakpoint breakpoint) {
|
void destroyBreakpoint(RDPBreakpoint breakpoint) {
|
||||||
|
if (breakpoint.referenceCount.decrementAndGet() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
breakpointLock.lock();
|
||||||
|
try {
|
||||||
|
if (breakpoint.referenceCount.get() > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
breakpointLocationMap.remove(breakpoint.getLocation());
|
||||||
breakpoints.remove(breakpoint);
|
breakpoints.remove(breakpoint);
|
||||||
if (breakpoint.chromeId != null) {
|
if (breakpoint.chromeId != null) {
|
||||||
|
System.out.println("Removing breakpoint at " + breakpoint.getLocation());
|
||||||
Message message = new Message();
|
Message message = new Message();
|
||||||
message.setMethod("Debugger.removeBreakpoint");
|
message.setMethod("Debugger.removeBreakpoint");
|
||||||
RemoveBreakpointCommand params = new RemoveBreakpointCommand();
|
RemoveBreakpointCommand params = new RemoveBreakpointCommand();
|
||||||
|
@ -251,6 +278,11 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
message.setParams(mapper.valueToTree(params));
|
message.setParams(mapper.valueToTree(params));
|
||||||
sendMessage(message);
|
sendMessage(message);
|
||||||
}
|
}
|
||||||
|
breakpoint.debugger = null;
|
||||||
|
breakpoint.chromeId = null;
|
||||||
|
} finally {
|
||||||
|
breakpointLock.unlock();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void fireScriptAdded(String script) {
|
void fireScriptAdded(String script) {
|
||||||
|
@ -260,21 +292,24 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
|
||||||
}
|
}
|
||||||
|
|
||||||
void updateBreakpoint(final RDPBreakpoint breakpoint) {
|
void updateBreakpoint(final RDPBreakpoint breakpoint) {
|
||||||
if (exchange == null) {
|
if (exchange == null || breakpoint.chromeId != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Message message = new Message();
|
final Message message = new Message();
|
||||||
message.setId(messageIdGenerator.incrementAndGet());
|
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()));
|
||||||
message.setParams(mapper.valueToTree(params));
|
message.setParams(mapper.valueToTree(params));
|
||||||
|
System.out.println("Setting breakpoint at: " + breakpoint.getLocation());
|
||||||
ResponseHandler handler = new ResponseHandler() {
|
ResponseHandler handler = new ResponseHandler() {
|
||||||
@Override public void received(JsonNode node) throws IOException {
|
@Override public void received(JsonNode node) throws IOException {
|
||||||
if (node != null) {
|
if (node != null) {
|
||||||
SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class).readValue(node);
|
SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class).readValue(node);
|
||||||
breakpoint.chromeId = response.getBreakpointId();
|
breakpoint.chromeId = response.getBreakpointId();
|
||||||
} else {
|
} else {
|
||||||
|
System.err.println("Error setting breakpoint at " + breakpoint.getLocation() +
|
||||||
|
", message id is " + message.getId());
|
||||||
breakpoint.chromeId = null;
|
breakpoint.chromeId = null;
|
||||||
}
|
}
|
||||||
for (JavaScriptDebuggerListener listener : getListeners()) {
|
for (JavaScriptDebuggerListener listener : getListeners()) {
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.chromerdp;
|
package org.teavm.chromerdp;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import org.teavm.debugging.JavaScriptBreakpoint;
|
import org.teavm.debugging.JavaScriptBreakpoint;
|
||||||
import org.teavm.debugging.JavaScriptLocation;
|
import org.teavm.debugging.JavaScriptLocation;
|
||||||
|
|
||||||
|
@ -24,8 +25,9 @@ import org.teavm.debugging.JavaScriptLocation;
|
||||||
*/
|
*/
|
||||||
public class RDPBreakpoint implements JavaScriptBreakpoint {
|
public class RDPBreakpoint implements JavaScriptBreakpoint {
|
||||||
volatile String chromeId;
|
volatile String chromeId;
|
||||||
private ChromeRDPDebugger debugger;
|
ChromeRDPDebugger debugger;
|
||||||
private JavaScriptLocation location;
|
private JavaScriptLocation location;
|
||||||
|
AtomicInteger referenceCount = new AtomicInteger();
|
||||||
|
|
||||||
RDPBreakpoint(ChromeRDPDebugger debugger, JavaScriptLocation location) {
|
RDPBreakpoint(ChromeRDPDebugger debugger, JavaScriptLocation location) {
|
||||||
this.debugger = debugger;
|
this.debugger = debugger;
|
||||||
|
@ -41,8 +43,6 @@ public class RDPBreakpoint implements JavaScriptBreakpoint {
|
||||||
public void destroy() {
|
public void destroy() {
|
||||||
if (debugger != null) {
|
if (debugger != null) {
|
||||||
debugger.destroyBreakpoint(this);
|
debugger.destroyBreakpoint(this);
|
||||||
chromeId = null;
|
|
||||||
debugger = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ public class Breakpoint {
|
||||||
return valid;
|
return valid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean isDestroyed() {
|
public boolean isDestroyed() {
|
||||||
return debugger == null;
|
return debugger == null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,7 @@ public class DebugInformation {
|
||||||
if (description == null) {
|
if (description == null) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
if (line > description.generatedLocationStart.length) {
|
if (line >= description.generatedLocationStart.length - 1) {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
int start = description.generatedLocationStart[line];
|
int start = description.generatedLocationStart[line];
|
||||||
|
|
|
@ -136,9 +136,9 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
||||||
}
|
}
|
||||||
for (SourceLocation succ : successors) {
|
for (SourceLocation succ : successors) {
|
||||||
if (succ == null) {
|
if (succ == null) {
|
||||||
cfg.add(location.getLine(), fileIndex, -1);
|
cfg.add(location.getLine(), -1, fileIndex);
|
||||||
} else {
|
} else {
|
||||||
cfg.add(location.getLine(), files.index(succ.getFileName()), succ.getLine());
|
cfg.add(location.getLine(), succ.getLine(), files.index(succ.getFileName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -183,6 +183,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
||||||
cfgs[i] = this.cfgs.get(i).build();
|
cfgs[i] = this.cfgs.get(i).build();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
debugInformation.controlFlowGraphs = cfgs;
|
||||||
|
|
||||||
debugInformation.rebuildFileDescriptions();
|
debugInformation.rebuildFileDescriptions();
|
||||||
debugInformation.rebuildMaps();
|
debugInformation.rebuildMaps();
|
||||||
|
@ -354,7 +355,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
||||||
}
|
}
|
||||||
long[] pairs = new long[linesChunk.size()];
|
long[] pairs = new long[linesChunk.size()];
|
||||||
for (int j = 0; j < pairs.length; ++j) {
|
for (int j = 0; j < pairs.length; ++j) {
|
||||||
pairs[j] = (filesChunk.get(j) << 32) | linesChunk.get(j);
|
pairs[j] = (((long)filesChunk.get(j)) << 32) | linesChunk.get(j);
|
||||||
}
|
}
|
||||||
Arrays.sort(pairs);
|
Arrays.sort(pairs);
|
||||||
int distinctSize = 0;
|
int distinctSize = 0;
|
||||||
|
@ -362,16 +363,16 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
|
||||||
long pair = pairs[j];
|
long pair = pairs[j];
|
||||||
if (distinctSize == 0 || pair != pairs[distinctSize]) {
|
if (distinctSize == 0 || pair != pairs[distinctSize]) {
|
||||||
pairs[distinctSize++] = pair;
|
pairs[distinctSize++] = pair;
|
||||||
linesData.add((int)(pair >>> 32));
|
filesData.add((int)(pair >>> 32));
|
||||||
filesData.add((int)pair);
|
linesData.add((int)pair);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
offsets[i + 1] = linesData.size();
|
offsets[i + 1] = linesData.size();
|
||||||
}
|
}
|
||||||
DebugInformation.CFG cfg = new DebugInformation.CFG();
|
DebugInformation.CFG cfg = new DebugInformation.CFG();
|
||||||
cfg.offsets = offsets;
|
cfg.offsets = offsets;
|
||||||
cfg.lines = lines.getAll();
|
cfg.lines = linesData.getAll();
|
||||||
cfg.files = files.getAll();
|
cfg.files = filesData.getAll();
|
||||||
return cfg;
|
return cfg;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,24 +54,62 @@ public class Debugger {
|
||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void suspend() {
|
public void suspend() {
|
||||||
javaScriptDebugger.suspend();
|
javaScriptDebugger.suspend();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void resume() {
|
public void resume() {
|
||||||
javaScriptDebugger.resume();
|
javaScriptDebugger.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void stepInto() {
|
public void stepInto() {
|
||||||
javaScriptDebugger.stepInto();
|
javaScriptDebugger.stepInto();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void stepOut() {
|
public void stepOut() {
|
||||||
javaScriptDebugger.stepOut();
|
javaScriptDebugger.stepOut();
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void stepOver() {
|
public void stepOver() {
|
||||||
|
CallFrame[] callStack = getCallStack();
|
||||||
|
if (callStack == null || callStack.length == 0) {
|
||||||
javaScriptDebugger.stepOver();
|
javaScriptDebugger.stepOver();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CallFrame recentFrame = callStack[0];
|
||||||
|
if (recentFrame.getLocation() == null || recentFrame.getLocation().getFileName() == null ||
|
||||||
|
recentFrame.getLocation().getLine() < 0) {
|
||||||
|
javaScriptDebugger.stepOver();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Set<JavaScriptLocation> successors = new HashSet<>();
|
||||||
|
for (int i = 0; i < callStack.length; ++i) {
|
||||||
|
CallFrame frame = callStack[i];
|
||||||
|
boolean exits = false;
|
||||||
|
for (Map.Entry<String, DebugInformation> entry : debugInformationMap.entrySet()) {
|
||||||
|
DebugInformation debugInfo = entry.getValue();
|
||||||
|
SourceLocation[] following = debugInfo.getFollowingLines(frame.getLocation());
|
||||||
|
if (following == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (SourceLocation successor : debugInfo.getFollowingLines(frame.getLocation())) {
|
||||||
|
if (successor == null) {
|
||||||
|
exits = true;
|
||||||
|
} else {
|
||||||
|
for (GeneratedLocation loc : debugInfo.getGeneratedLocations(successor)) {
|
||||||
|
successors.add(new JavaScriptLocation(entry.getKey(), loc.getLine(), loc.getColumn()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!exits) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (JavaScriptLocation successor : successors) {
|
||||||
|
temporaryBreakpoints.add(javaScriptDebugger.createBreakpoint(successor));
|
||||||
|
}
|
||||||
|
javaScriptDebugger.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<DebugInformation> debugInformationBySource(String sourceFile) {
|
private List<DebugInformation> debugInformationBySource(String sourceFile) {
|
||||||
|
@ -249,8 +287,8 @@ public class Debugger {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void fireResumed() {
|
private void fireResumed() {
|
||||||
List<JavaScriptBreakpoint> termporaryBreakpoints = new ArrayList<>();
|
List<JavaScriptBreakpoint> temporaryBreakpoints = new ArrayList<>();
|
||||||
this.temporaryBreakpoints.drainTo(termporaryBreakpoints);
|
this.temporaryBreakpoints.drainTo(temporaryBreakpoints);
|
||||||
for (JavaScriptBreakpoint jsBreakpoint : temporaryBreakpoints) {
|
for (JavaScriptBreakpoint jsBreakpoint : temporaryBreakpoints) {
|
||||||
jsBreakpoint.destroy();
|
jsBreakpoint.destroy();
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.debugging;
|
package org.teavm.debugging;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @author Alexey Andreev
|
* @author Alexey Andreev
|
||||||
|
@ -42,6 +44,23 @@ public class JavaScriptLocation {
|
||||||
return column;
|
return column;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (this == obj) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (!(obj instanceof JavaScriptLocation)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
JavaScriptLocation other = (JavaScriptLocation)obj;
|
||||||
|
return Objects.equals(other.script, script) && other.line == line;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
return (31 + line) * 31 + Objects.hashCode(script);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return script + ":(" + line + ";" + column + ")";
|
return script + ":(" + line + ";" + column + ")";
|
||||||
|
|
|
@ -58,4 +58,9 @@ public class InstructionLocation {
|
||||||
InstructionLocation other = (InstructionLocation)obj;
|
InstructionLocation other = (InstructionLocation)obj;
|
||||||
return Objects.equals(fileName, other.fileName) && line == other.line;
|
return Objects.equals(fileName, other.fileName) && line == other.line;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return fileName + ":" + line;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,139 @@
|
||||||
|
/*
|
||||||
|
* 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.model.util;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
import org.teavm.common.Graph;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
|
import org.teavm.model.InstructionLocation;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @author Alexey Andreev
|
||||||
|
*/
|
||||||
|
class LocationGraphBuilder {
|
||||||
|
private Map<InstructionLocation, Set<InstructionLocation>> graphBuilder;
|
||||||
|
private List<Set<InstructionLocation>> startLocations;
|
||||||
|
private List<AdditionalConnection> additionalConnections;
|
||||||
|
|
||||||
|
public Map<InstructionLocation, InstructionLocation[]> build(Program program) {
|
||||||
|
graphBuilder = new HashMap<>();
|
||||||
|
Graph graph = ProgramUtils.buildControlFlowGraph(program);
|
||||||
|
dfs(graph, program);
|
||||||
|
return assemble();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void dfs(Graph graph, Program program) {
|
||||||
|
startLocations = new ArrayList<>(Collections.<Set<InstructionLocation>>nCopies(graph.size(), null));
|
||||||
|
additionalConnections = new ArrayList<>();
|
||||||
|
Deque<Step> stack = new ArrayDeque<>();
|
||||||
|
for (int i = 0; i < graph.size(); ++i) {
|
||||||
|
if (graph.incomingEdgesCount(i) == 0) {
|
||||||
|
stack.push(new Step(null, new HashSet<InstructionLocation>(), i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
boolean[] visited = new boolean[graph.size()];
|
||||||
|
InstructionLocation[] blockLocations = new InstructionLocation[graph.size()];
|
||||||
|
|
||||||
|
while (!stack.isEmpty()) {
|
||||||
|
Step step = stack.pop();
|
||||||
|
if (visited[step.block]) {
|
||||||
|
if (step.location != null) {
|
||||||
|
additionalConnections.add(new AdditionalConnection(step.location, startLocations.get(step.block)));
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
visited[step.block] = true;
|
||||||
|
startLocations.set(step.block, step.startLocations);
|
||||||
|
BasicBlock block = program.basicBlockAt(step.block);
|
||||||
|
InstructionLocation location = step.location;
|
||||||
|
boolean started = false;
|
||||||
|
for (Instruction insn : block.getInstructions()) {
|
||||||
|
if (insn.getLocation() != null) {
|
||||||
|
if (!started) {
|
||||||
|
step.startLocations.add(location);
|
||||||
|
}
|
||||||
|
started = true;
|
||||||
|
if (blockLocations[step.block] == null) {
|
||||||
|
blockLocations[step.block] = insn.getLocation();
|
||||||
|
}
|
||||||
|
if (location != null && !Objects.equals(location, insn.getLocation())) {
|
||||||
|
addEdge(location, insn.getLocation());
|
||||||
|
}
|
||||||
|
location = insn.getLocation();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (graph.outgoingEdgesCount(step.block) == 0) {
|
||||||
|
addEdge(location, new InstructionLocation(null, -1));
|
||||||
|
} else {
|
||||||
|
for (int next : graph.outgoingEdges(step.block)) {
|
||||||
|
stack.push(new Step(location, started ? new HashSet<InstructionLocation>() : step.startLocations,
|
||||||
|
next));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map<InstructionLocation, InstructionLocation[]> assemble() {
|
||||||
|
for (AdditionalConnection additionalConn : additionalConnections) {
|
||||||
|
for (InstructionLocation succ : additionalConn.successors) {
|
||||||
|
addEdge(additionalConn.location, succ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Map<InstructionLocation, InstructionLocation[]> locationGraph = new HashMap<>();
|
||||||
|
for (Map.Entry<InstructionLocation, Set<InstructionLocation>> entry : graphBuilder.entrySet()) {
|
||||||
|
InstructionLocation[] successors = entry.getValue().toArray(new InstructionLocation[0]);
|
||||||
|
for (int i = 0; i < successors.length; ++i) {
|
||||||
|
if (successors[i].getLine() < 0) {
|
||||||
|
successors[i] = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
locationGraph.put(entry.getKey(), successors);
|
||||||
|
}
|
||||||
|
return locationGraph;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addEdge(InstructionLocation source, InstructionLocation dest) {
|
||||||
|
Set<InstructionLocation> successors = graphBuilder.get(source);
|
||||||
|
if (successors == null) {
|
||||||
|
successors = new HashSet<>();
|
||||||
|
graphBuilder.put(source, successors);
|
||||||
|
}
|
||||||
|
successors.add(dest);
|
||||||
|
}
|
||||||
|
|
||||||
|
static class Step {
|
||||||
|
InstructionLocation location;
|
||||||
|
Set<InstructionLocation> startLocations;
|
||||||
|
int block;
|
||||||
|
public Step(InstructionLocation location, Set<InstructionLocation> startLocations, int block) {
|
||||||
|
this.location = location;
|
||||||
|
this.startLocations = startLocations;
|
||||||
|
this.block = block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AdditionalConnection {
|
||||||
|
InstructionLocation location;
|
||||||
|
Set<InstructionLocation> successors;
|
||||||
|
public AdditionalConnection(InstructionLocation location, Set<InstructionLocation> successors) {
|
||||||
|
this.location = location;
|
||||||
|
this.successors = successors;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -69,64 +69,7 @@ public final class ProgramUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Map<InstructionLocation, InstructionLocation[]> getLocationCFG(Program program) {
|
public static Map<InstructionLocation, InstructionLocation[]> getLocationCFG(Program program) {
|
||||||
Graph graph = buildControlFlowGraph(program);
|
return new LocationGraphBuilder().build(program);
|
||||||
class Step {
|
|
||||||
InstructionLocation location;
|
|
||||||
int block;
|
|
||||||
}
|
|
||||||
Deque<Step> stack = new ArrayDeque<>();
|
|
||||||
for (int i = 0; i < graph.size(); ++i) {
|
|
||||||
if (graph.incomingEdgesCount(i) == 0) {
|
|
||||||
Step step = new Step();
|
|
||||||
step.block = i;
|
|
||||||
step.location = null;
|
|
||||||
stack.push(step);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
boolean[] visited = new boolean[graph.size()];
|
|
||||||
Map<InstructionLocation, Set<InstructionLocation>> locationGraphBuilder = new HashMap<>();
|
|
||||||
while (!stack.isEmpty()) {
|
|
||||||
Step step = stack.pop();
|
|
||||||
if (visited[step.block]) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
visited[step.block] = true;
|
|
||||||
BasicBlock block = program.basicBlockAt(step.block);
|
|
||||||
InstructionLocation location = step.location;
|
|
||||||
for (Instruction insn : block.getInstructions()) {
|
|
||||||
if (insn.getLocation() != null) {
|
|
||||||
if (location != null) {
|
|
||||||
Set<InstructionLocation> successors = locationGraphBuilder.get(location);
|
|
||||||
if (successors == null) {
|
|
||||||
successors = new HashSet<>();
|
|
||||||
locationGraphBuilder.put(location, successors);
|
|
||||||
}
|
|
||||||
successors.add(insn.getLocation());
|
|
||||||
}
|
|
||||||
location = insn.getLocation();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (graph.outgoingEdgesCount(step.block) == 0) {
|
|
||||||
Set<InstructionLocation> successors = locationGraphBuilder.get(location);
|
|
||||||
if (successors == null) {
|
|
||||||
successors = new HashSet<>();
|
|
||||||
locationGraphBuilder.put(location, successors);
|
|
||||||
}
|
|
||||||
successors.add(new InstructionLocation(null, -1));
|
|
||||||
} else {
|
|
||||||
for (int next : graph.outgoingEdges(step.block)) {
|
|
||||||
step = new Step();
|
|
||||||
step.location = location;
|
|
||||||
step.block = next;
|
|
||||||
stack.push(step);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Map<InstructionLocation, InstructionLocation[]> locationGraph = new HashMap<>();
|
|
||||||
for (Map.Entry<InstructionLocation, Set<InstructionLocation>> entry : locationGraphBuilder.entrySet()) {
|
|
||||||
locationGraph.put(entry.getKey(), entry.getValue().toArray(new InstructionLocation[0]));
|
|
||||||
}
|
|
||||||
return locationGraph;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Program copy(ProgramReader program) {
|
public static Program copy(ProgramReader program) {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.teavm.codegen.*;
|
||||||
import org.teavm.common.FiniteExecutor;
|
import org.teavm.common.FiniteExecutor;
|
||||||
import org.teavm.common.ServiceRepository;
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.debugging.DebugInformationEmitter;
|
import org.teavm.debugging.DebugInformationEmitter;
|
||||||
|
import org.teavm.debugging.SourceLocation;
|
||||||
import org.teavm.dependency.*;
|
import org.teavm.dependency.*;
|
||||||
import org.teavm.javascript.Decompiler;
|
import org.teavm.javascript.Decompiler;
|
||||||
import org.teavm.javascript.Renderer;
|
import org.teavm.javascript.Renderer;
|
||||||
|
@ -349,6 +350,14 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
SourceWriter sourceWriter = builder.build(writer);
|
SourceWriter sourceWriter = builder.build(writer);
|
||||||
Renderer renderer = new Renderer(sourceWriter, classSet, classLoader, this);
|
Renderer renderer = new Renderer(sourceWriter, classSet, classLoader, this);
|
||||||
if (debugEmitter != null) {
|
if (debugEmitter != null) {
|
||||||
|
for (String className : classSet.getClassNames()) {
|
||||||
|
ClassHolder cls = classSet.get(className);
|
||||||
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
|
if (method.getProgram() != null) {
|
||||||
|
emitCFG(debugEmitter, method.getProgram());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
renderer.setDebugEmitter(debugEmitter);
|
renderer.setDebugEmitter(debugEmitter);
|
||||||
}
|
}
|
||||||
renderer.getDebugEmitter().setLocationProvider(sourceWriter);
|
renderer.getDebugEmitter().setLocationProvider(sourceWriter);
|
||||||
|
@ -387,6 +396,25 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void emitCFG(DebugInformationEmitter emitter, Program program) {
|
||||||
|
Map<InstructionLocation, InstructionLocation[]> cfg = ProgramUtils.getLocationCFG(program);
|
||||||
|
for (Map.Entry<InstructionLocation, InstructionLocation[]> entry : cfg.entrySet()) {
|
||||||
|
SourceLocation location = map(entry.getKey());
|
||||||
|
SourceLocation[] successors = new SourceLocation[entry.getValue().length];
|
||||||
|
for (int i = 0; i < entry.getValue().length; ++i) {
|
||||||
|
successors[i] = map(entry.getValue()[i]);
|
||||||
|
}
|
||||||
|
emitter.addSuccessors(location, successors);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SourceLocation map(InstructionLocation location) {
|
||||||
|
if (location == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new SourceLocation(location.getFileName(), location.getLine());
|
||||||
|
}
|
||||||
|
|
||||||
private void devirtualize(ListableClassHolderSource classes, DependencyInfo dependency) {
|
private void devirtualize(ListableClassHolderSource classes, DependencyInfo dependency) {
|
||||||
final Devirtualization devirtualization = new Devirtualization(dependency, classes);
|
final Devirtualization devirtualization = new Devirtualization(dependency, classes);
|
||||||
for (String className : classes.getClassNames()) {
|
for (String className : classes.getClassNames()) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user