diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebugger.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebugger.java index c238e711c..47f274812 100644 --- a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebugger.java +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebugger.java @@ -107,7 +107,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { } @Override - public JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) { + public synchronized JavaScriptBreakpoint createBreakpoint(JavaScriptLocation location) { RDPBreakpoint breakpoint = new RDPBreakpoint(this, location); breakpoints.add(breakpoint); if (endpoint != null) { @@ -116,7 +116,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { return breakpoint; } - void destroyBreakpoint(RDPBreakpoint breakpoint) { + synchronized void destroyBreakpoint(RDPBreakpoint breakpoint) { breakpoints.remove(breakpoint); if (endpoint != null) { endpoint.destroyBreakpoint(breakpoint); @@ -140,4 +140,10 @@ public class ChromeRDPDebugger implements JavaScriptDebugger { listener.scriptAdded(script); } } + + void fireBreakpointStatusChanged(RDPBreakpoint breakpoint) { + for (JavaScriptDebuggerListener listener : listeners) { + listener.breakpointChanged(breakpoint); + } + } } diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebuggerEndpoint.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebuggerEndpoint.java index 85b33ddeb..11c7f0848 100644 --- a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebuggerEndpoint.java +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/ChromeRDPDebuggerEndpoint.java @@ -18,7 +18,10 @@ package org.teavm.chromerpd; import java.io.IOException; import java.util.HashMap; import java.util.Map; -import javax.websocket.*; +import javax.websocket.OnClose; +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.map.ObjectMapper; @@ -27,7 +30,9 @@ import org.teavm.chromerpd.data.LocationDTO; import org.teavm.chromerpd.data.Message; import org.teavm.chromerpd.data.Response; import org.teavm.chromerpd.messages.*; -import org.teavm.debugging.*; +import org.teavm.debugging.JavaScriptBreakpoint; +import org.teavm.debugging.JavaScriptCallFrame; +import org.teavm.debugging.JavaScriptLocation; /** * @@ -39,9 +44,9 @@ public class ChromeRDPDebuggerEndpoint { private RDPCallFrame[] callStack = new RDPCallFrame[0]; private Map scripts = new HashMap<>(); private Map scriptIds = new HashMap<>(); - private boolean suspended; + private boolean suspended = false; private ObjectMapper mapper = new ObjectMapper(); - private Map deferredResponses = new HashMap<>(); + private Map responseHandlers = new HashMap<>(); private int messageIdGenerator; boolean closed; private ChromeRDPDebugger debugger; @@ -70,7 +75,7 @@ public class ChromeRDPDebuggerEndpoint { JsonNode jsonMessage = mapper.readTree(messageText); if (jsonMessage.has("result")) { Response response = mapper.reader(Response.class).readValue(jsonMessage); - deferredResponses.remove(response.getId()).set(response.getResult()); + responseHandlers.remove(response.getId()).received(response.getResult()); } else { Message message = mapper.reader(Message.class).readValue(messageText); switch (message.getMethod()) { @@ -91,13 +96,14 @@ public class ChromeRDPDebuggerEndpoint { return mapper.reader(type).readValue(node); } - private void firePaused(SuspendedNotification params) { + private synchronized 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]); + RDPCallFrame[] callStack = new RDPCallFrame[callFrameDTOs.length]; + for (int i = 0; i < callStack.length; ++i) { + callStack[i] = map(callFrameDTOs[i]); } + this.callStack = callStack; debugger.firePaused(); } @@ -117,12 +123,13 @@ public class ChromeRDPDebuggerEndpoint { return dto; } - private void fireResumed() { + private synchronized void fireResumed() { suspended = false; + callStack = null; debugger.fireResumed(); } - private void scriptParsed(ScriptParsedNotification params) { + private synchronized void scriptParsed(ScriptParsedNotification params) { if (scripts.containsKey(params.getScriptId())) { return; } @@ -188,7 +195,7 @@ public class ChromeRDPDebuggerEndpoint { sendMessage(message); } - public void continueToLocation(JavaScriptLocation location) { + public synchronized void continueToLocation(JavaScriptLocation location) { Message message = new Message(); message.setMethod("Debugger.continueToLocation"); ContinueToLocationCommand params = new ContinueToLocationCommand(); @@ -197,35 +204,34 @@ public class ChromeRDPDebuggerEndpoint { sendMessage(message); } - public boolean isSuspended() { + public synchronized boolean isSuspended() { return suspended; } - public JavaScriptCallFrame[] getCallStack() { + public synchronized JavaScriptCallFrame[] getCallStack() { return callStack; } - public JavaScriptBreakpoint getCurrentBreakpoint() { + public synchronized JavaScriptBreakpoint getCurrentBreakpoint() { return null; } - public void updateBreakpoint(RDPBreakpoint breakpoint) { + public synchronized void updateBreakpoint(final RDPBreakpoint breakpoint) { Message message = new Message(); - message.setId(messageIdGenerator++); + message.setId(++messageIdGenerator); message.setMethod("Debugger.setBreakpoint"); SetBreakpointCommand params = new SetBreakpointCommand(); params.setLocation(unmap(breakpoint.getLocation())); message.setParams(mapper.valueToTree(params)); - Deferred deferred = new Deferred(); - deferredResponses.put(message.getId(), deferred); + ResponseHandler handler = new ResponseHandler() { + @Override public void received(JsonNode node) throws IOException { + SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class).readValue(node); + breakpoint.chromeId = response.getBreakpointId(); + debugger.fireBreakpointStatusChanged(breakpoint); + } + }; + responseHandlers.put(message.getId(), handler); sendMessage(message); - try { - SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class) - .readValue((JsonNode)deferred.get()); - breakpoint.chromeId = response.getBreakpointId(); - } catch (IOException e) { - throw new RuntimeException(e); - } } void destroyBreakpoint(RDPBreakpoint breakpoint) { @@ -238,4 +244,8 @@ public class ChromeRDPDebuggerEndpoint { sendMessage(message); } } + + interface ResponseHandler { + void received(JsonNode node) throws IOException; + } } diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/Deferred.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/Deferred.java deleted file mode 100644 index e5a5479f2..000000000 --- a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/Deferred.java +++ /dev/null @@ -1,48 +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; - -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * - * @author Alexey Andreev - */ -public class Deferred { - private volatile Object calculatedValue; - private AtomicBoolean calculated = new AtomicBoolean(); - private CountDownLatch latch = new CountDownLatch(1); - - public Object get() { - while (latch.getCount() > 0) { - try { - latch.await(); - } catch (InterruptedException e) { - throw new RuntimeException("Interrupted while awaiting for value"); - } - } - return calculatedValue; - } - - public void set(Object value) { - if (!calculated.compareAndSet(false, true)) { - throw new IllegalStateException("Future already calculated"); - } - calculatedValue = value; - latch.countDown(); - } -} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/RDPBreakpoint.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/RDPBreakpoint.java index b4e0c37f8..bd9670eff 100644 --- a/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/RDPBreakpoint.java +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerpd/RDPBreakpoint.java @@ -23,7 +23,7 @@ import org.teavm.debugging.JavaScriptLocation; * @author Alexey Andreev */ public class RDPBreakpoint implements JavaScriptBreakpoint { - String chromeId; + volatile String chromeId; private ChromeRDPDebugger debugger; private JavaScriptLocation location; @@ -41,12 +41,13 @@ public class RDPBreakpoint implements JavaScriptBreakpoint { public void destroy() { if (debugger != null) { debugger.destroyBreakpoint(this); + chromeId = null; debugger = null; } } @Override public boolean isValid() { - return false; + return chromeId != null; } } diff --git a/teavm-chrome-rdp/src/main/js/chrome/manifest.json b/teavm-chrome-rdp/src/main/js/chrome/manifest.json index 22d5280e3..3eb71000c 100644 --- a/teavm-chrome-rdp/src/main/js/chrome/manifest.json +++ b/teavm-chrome-rdp/src/main/js/chrome/manifest.json @@ -3,7 +3,7 @@ "name": "TeaVM debugger agent", "description": "TeaVM debugger agent, that sends RDP commands over WebSocket", - "version": "1.0", + "version": "0.2", "permissions" : ["debugger", "activeTab", "tabs"], diff --git a/teavm-core/src/main/java/org/teavm/debugging/Breakpoint.java b/teavm-core/src/main/java/org/teavm/debugging/Breakpoint.java index e3bb0d29a..30120d81c 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/Breakpoint.java +++ b/teavm-core/src/main/java/org/teavm/debugging/Breakpoint.java @@ -37,13 +37,8 @@ public class Breakpoint { return location; } - public void destroy() { - for (JavaScriptBreakpoint jsBreakpoint : jsBreakpoints) { - jsBreakpoint.destroy(); - debugger.breakpointMap.remove(jsBreakpoint); - } - debugger.breakpoints.remove(this); - jsBreakpoints.clear(); + public synchronized void destroy() { + debugger.destroyBreakpoint(this); debugger = null; } @@ -51,7 +46,7 @@ public class Breakpoint { return valid; } - public boolean isDestroyed() { + public synchronized boolean isDestroyed() { return debugger == null; } diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java index b47a7cf5b..5d9eb681f 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java @@ -61,8 +61,9 @@ public class DebugInformation { int end = description.generatedLocationStart[line + 1]; GeneratedLocation[] resultArray = new GeneratedLocation[(end - start) / 2]; for (int i = 0; i < resultArray.length; ++i) { - resultArray[i] = new GeneratedLocation(description.generatedLocationData[i * 2], - description.generatedLocationData[i * 2 + 1]); + int genLine = description.generatedLocationData[start++]; + int genColumn = description.generatedLocationData[start++]; + resultArray[i] = new GeneratedLocation(genLine, genColumn); } return Arrays.asList(resultArray); } diff --git a/teavm-core/src/main/java/org/teavm/debugging/Debugger.java b/teavm-core/src/main/java/org/teavm/debugging/Debugger.java index c5abc91f2..1b03e2d77 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/Debugger.java +++ b/teavm-core/src/main/java/org/teavm/debugging/Debugger.java @@ -79,7 +79,7 @@ public class Debugger { continueToLocation(location.getFileName(), location.getLine()); } - public void continueToLocation(String fileName, int line) { + public synchronized void continueToLocation(String fileName, int line) { if (!javaScriptDebugger.isSuspended()) { return; } @@ -105,7 +105,7 @@ public class Debugger { return createBreakpoint(new SourceLocation(file, line)); } - public Breakpoint createBreakpoint(SourceLocation location) { + public synchronized Breakpoint createBreakpoint(SourceLocation location) { Breakpoint breakpoint = new Breakpoint(this, location); breakpoints.add(breakpoint); updateInternalBreakpoints(breakpoint); @@ -113,8 +113,8 @@ public class Debugger { return breakpoint; } - public Set getBreakpoints() { - return Collections.unmodifiableSet(breakpoints); + public synchronized Set getBreakpoints() { + return new HashSet<>(breakpoints); } void updateInternalBreakpoints(Breakpoint breakpoint) { @@ -122,6 +122,7 @@ public class Debugger { breakpointMap.remove(jsBreakpoint); jsBreakpoint.destroy(); } + breakpoint.jsBreakpoints.clear(); SourceLocation location = breakpoint.getLocation(); for (DebugInformation debugInformation : debugInformationBySource(location.getFileName())) { Collection locations = debugInformation.getGeneratedLocations(location); @@ -130,6 +131,7 @@ public class Debugger { genLocation.getLine(), genLocation.getColumn()); JavaScriptBreakpoint jsBreakpoint = javaScriptDebugger.createBreakpoint(jsLocation); breakpoint.jsBreakpoints.add(jsBreakpoint); + breakpointMap.put(jsBreakpoint, breakpoint); } } } @@ -151,8 +153,8 @@ public class Debugger { } } - public CallFrame[] getCallStack() { - if (isSuspended()) { + public synchronized CallFrame[] getCallStack() { + if (!isSuspended()) { return null; } if (callStack == null) { @@ -182,7 +184,7 @@ public class Debugger { return callStack.clone(); } - private void addScript(String name) { + private synchronized void addScript(String name) { if (debugInformationMap.containsKey(name)) { return; } @@ -210,20 +212,60 @@ public class Debugger { return javaScriptDebugger.isAttached(); } + synchronized void destroyBreakpoint(Breakpoint breakpoint) { + for (JavaScriptBreakpoint jsBreakpoint : breakpoint.jsBreakpoints) { + jsBreakpoint.destroy(); + breakpointMap.remove(jsBreakpoint); + } + breakpoint.jsBreakpoints.clear(); + breakpoints.remove(this); + } + + private synchronized void fireResumed() { + for (JavaScriptBreakpoint jsBreakpoint : temporaryJsBreakpoints) { + jsBreakpoint.destroy(); + } + temporaryJsBreakpoints.clear(); + for (DebuggerListener listener : listeners) { + listener.resumed(); + } + } + + private synchronized void fireAttached() { + for (Breakpoint breakpoint : breakpoints) { + updateInternalBreakpoints(breakpoint); + updateBreakpointStatus(breakpoint, false); + } + for (DebuggerListener listener : listeners) { + listener.attached(); + } + } + + private synchronized void fireDetached() { + for (Breakpoint breakpoint : breakpoints) { + updateBreakpointStatus(breakpoint, false); + } + for (DebuggerListener listener : listeners) { + listener.detached(); + } + } + + private synchronized void fireBreakpointChanged(JavaScriptBreakpoint jsBreakpoint) { + Breakpoint breakpoint = breakpointMap.get(jsBreakpoint); + if (breakpoint != null) { + updateBreakpointStatus(breakpoint, true); + } + } + private JavaScriptDebuggerListener javaScriptListener = new JavaScriptDebuggerListener() { @Override public void resumed() { - for (JavaScriptBreakpoint jsBreakpoint : temporaryJsBreakpoints) { - jsBreakpoint.destroy(); - } - temporaryJsBreakpoints.clear(); - for (DebuggerListener listener : listeners) { - listener.resumed(); - } + fireResumed(); } @Override public void paused() { + callStack = null; for (DebuggerListener listener : listeners) { listener.paused(); } @@ -236,32 +278,17 @@ public class Debugger { @Override public void attached() { - for (Breakpoint breakpoint : breakpoints) { - updateInternalBreakpoints(breakpoint); - updateBreakpointStatus(breakpoint, false); - } - for (DebuggerListener listener : listeners) { - listener.attached(); - } + fireAttached(); } @Override public void detached() { - for (Breakpoint breakpoint : breakpoints) { - updateBreakpointStatus(breakpoint, true); - } - for (DebuggerListener listener : listeners) { - listener.detached(); - } + fireDetached(); } @Override public void breakpointChanged(JavaScriptBreakpoint jsBreakpoint) { - Breakpoint breakpoint = breakpointMap.get(jsBreakpoint); - if (breakpoint != null) { - updateInternalBreakpoints(breakpoint); - updateBreakpointStatus(breakpoint, false); - } + fireBreakpointChanged(jsBreakpoint); } }; }