From 461528b51ff64d3627307ae47e7e0f7e0f9a999b Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Wed, 6 Aug 2014 23:03:50 +0400 Subject: [PATCH] Adds support of object's fields in debugger watches --- .../teavm/chromerdp/ChromeRDPDebugger.java | 47 +++++++++++++-- .../java/org/teavm/chromerdp/RDPScope.java | 22 +++---- .../java/org/teavm/chromerdp/RDPValue.java | 24 +++++++- .../teavm/chromerdp/data/CallArgumentDTO.java | 30 ++++++++++ .../messages/CallFunctionCommand.java | 48 +++++++++++++++ .../messages/CallFunctionResponse.java | 30 ++++++++++ teavm-chrome-rdp/src/main/js/chrome/main.js | 3 +- .../org/teavm/debugging/DebugInformation.java | 17 ++++++ .../debugging/DebugInformationBuilder.java | 43 ++++++++++++-- .../debugging/DebugInformationEmitter.java | 4 ++ .../debugging/DebugInformationReader.java | 22 +++++++ .../debugging/DebugInformationWriter.java | 20 +++++++ .../java/org/teavm/debugging/Debugger.java | 10 ++++ .../DummyDebugInformationEmitter.java | 8 +++ .../org/teavm/debugging/JavaScriptValue.java | 2 + .../java/org/teavm/debugging/PropertyMap.java | 59 +++++++++++++++++++ .../java/org/teavm/debugging/SimpleValue.java | 41 ------------- .../main/java/org/teavm/debugging/Value.java | 27 ++++++++- .../java/org/teavm/debugging/VariableMap.java | 2 +- .../java/org/teavm/javascript/Renderer.java | 11 ++-- .../resources/org/teavm/javascript/runtime.js | 3 +- 21 files changed, 400 insertions(+), 73 deletions(-) create mode 100644 teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallArgumentDTO.java create mode 100644 teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionCommand.java create mode 100644 teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionResponse.java create mode 100644 teavm-core/src/main/java/org/teavm/debugging/PropertyMap.java delete mode 100644 teavm-core/src/main/java/org/teavm/debugging/SimpleValue.java diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/ChromeRDPDebugger.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/ChromeRDPDebugger.java index 54d69c634..2c0814d8d 100644 --- a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/ChromeRDPDebugger.java +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/ChromeRDPDebugger.java @@ -63,11 +63,14 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC private ChromeRDPExchangeListener exchangeListener = new ChromeRDPExchangeListener() { @Override public void received(String messageText) throws IOException { JsonNode jsonMessage = mapper.readTree(messageText); - if (jsonMessage.has("result")) { + if (jsonMessage.has("id")) { Response response = mapper.reader(Response.class).readValue(jsonMessage); responseHandlers.remove(response.getId()).received(response.getResult()); } else { Message message = mapper.reader(Message.class).readValue(messageText); + if (message.getMethod() == null) { + return; + } switch (message.getMethod()) { case "Debugger.paused": firePaused(parseJson(SuspendedNotification.class, message.getParams())); @@ -290,6 +293,40 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC } } + String getClassName(String objectId) { + if (exchange == null) { + return null; + } + Message message = new Message(); + message.setId(messageIdGenerator.incrementAndGet()); + message.setMethod("Runtime.callFunctionOn"); + CallFunctionCommand params = new CallFunctionCommand(); + CallArgumentDTO arg = new CallArgumentDTO(); + arg.setObjectId(objectId); + params.setObjectId(objectId); + params.setArguments(new CallArgumentDTO[] { arg }); + params.setFunctionDeclaration("$dbg_class"); + message.setParams(mapper.valueToTree(params)); + final BlockingQueue sync = new LinkedTransferQueue<>(); + responseHandlers.put(message.getId(), new ResponseHandler() { + @Override public void received(JsonNode node) throws IOException { + if (node == null) { + sync.add(""); + } else { + CallFunctionResponse response = mapper.reader(CallFunctionResponse.class).readValue(node); + sync.add(response.getResult().getValue().getTextValue()); + } + } + }); + sendMessage(message); + try { + String result = sync.take(); + return result.isEmpty() ? null : result; + } catch (InterruptedException e) { + return null; + } + } + private List parseProperties(PropertyDescriptorDTO[] properties) { List variables = new ArrayList<>(); if (properties != null) { @@ -298,14 +335,16 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC RDPValue value; switch (remoteValue.getType()) { case "undefined": - value = new RDPValue("undefined"); + value = new RDPValue(this, "undefined", "undefined", null); break; case "object": case "function": - value = new RDPValue(remoteValue.getObjectId()); + value = new RDPValue(this, remoteValue.getDescription(), remoteValue.getType(), + remoteValue.getObjectId()); break; default: - value = new RDPValue(remoteValue.getValue().asText()); + value = new RDPValue(this, remoteValue.getValue().asText(), remoteValue.getType(), + remoteValue.getObjectId()); break; } diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPScope.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPScope.java index e024ffa8e..92d150df8 100644 --- a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPScope.java +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPScope.java @@ -4,13 +4,14 @@ import java.util.AbstractMap; import java.util.HashMap; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; /** * * @author Alexey Andreev */ public class RDPScope extends AbstractMap { - private volatile Map backingMap; + private AtomicReference> backingMap = new AtomicReference<>(); private ChromeRDPDebugger debugger; private String id; @@ -22,32 +23,31 @@ public class RDPScope extends AbstractMap { @Override public Set> entrySet() { initBackingMap(); - return backingMap.entrySet(); + return backingMap.get().entrySet(); } @Override public int size() { initBackingMap(); - return backingMap.size(); + return backingMap.get().size(); } @Override public RDPLocalVariable get(Object key) { initBackingMap(); - return backingMap.get(key); + return backingMap.get().get(key); } private void initBackingMap() { - if (backingMap != null) { + if (backingMap.get() != null) { return; } - if (id == null) { - backingMap = new HashMap<>(); - } Map newBackingMap = new HashMap<>(); - for (RDPLocalVariable variable : debugger.getScope(id)) { - newBackingMap.put(variable.getName(), variable); + if (id != null) { + for (RDPLocalVariable variable : debugger.getScope(id)) { + newBackingMap.put(variable.getName(), variable); + } } - backingMap = newBackingMap; + backingMap.compareAndSet(null, newBackingMap); } } diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPValue.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPValue.java index 6d1743406..d959f8429 100644 --- a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPValue.java +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPValue.java @@ -11,9 +11,18 @@ import org.teavm.debugging.JavaScriptVariable; */ public class RDPValue implements JavaScriptValue { private String representation; + private String typeName; + private ChromeRDPDebugger debugger; + private String objectId; + private Map properties; - public RDPValue(String representation) { + public RDPValue(ChromeRDPDebugger debugger, String representation, String typeName, String objectId) { this.representation = representation; + this.typeName = typeName; + this.debugger = debugger; + this.objectId = objectId; + properties = objectId != null ? new RDPScope(debugger, objectId) : + Collections.emptyMap(); } @Override @@ -21,8 +30,19 @@ public class RDPValue implements JavaScriptValue { return representation; } + @Override + public String getClassName() { + if (objectId != null) { + String className = debugger.getClassName(objectId); + return className != null ? className : "object"; + } else { + return typeName; + } + } + + @SuppressWarnings("unchecked") @Override public Map getProperties() { - return Collections.emptyMap(); + return (Map)properties; } } diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallArgumentDTO.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallArgumentDTO.java new file mode 100644 index 000000000..6235a7aed --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallArgumentDTO.java @@ -0,0 +1,30 @@ +package org.teavm.chromerdp.data; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.annotate.JsonIgnoreProperties; + +/** + * + * @author Alexey Andreev + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class CallArgumentDTO { + private String objectId; + private JsonNode value; + + public String getObjectId() { + return objectId; + } + + public void setObjectId(String objectId) { + this.objectId = objectId; + } + + public JsonNode getValue() { + return value; + } + + public void setValue(JsonNode value) { + this.value = value; + } +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionCommand.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionCommand.java new file mode 100644 index 000000000..556581515 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionCommand.java @@ -0,0 +1,48 @@ +package org.teavm.chromerdp.messages; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.teavm.chromerdp.data.CallArgumentDTO; + +/** + * + * @author Alexey Andreev + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class CallFunctionCommand { + private String objectId; + private String functionDeclaration; + private CallArgumentDTO[] arguments; + private boolean returnByValue; + + public String getObjectId() { + return objectId; + } + + public void setObjectId(String objectId) { + this.objectId = objectId; + } + + public String getFunctionDeclaration() { + return functionDeclaration; + } + + public void setFunctionDeclaration(String functionDeclaration) { + this.functionDeclaration = functionDeclaration; + } + + public CallArgumentDTO[] getArguments() { + return arguments; + } + + public void setArguments(CallArgumentDTO[] arguments) { + this.arguments = arguments; + } + + public boolean isReturnByValue() { + return returnByValue; + } + + public void setReturnByValue(boolean returnByValue) { + this.returnByValue = returnByValue; + } +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionResponse.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionResponse.java new file mode 100644 index 000000000..36255827c --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/CallFunctionResponse.java @@ -0,0 +1,30 @@ +package org.teavm.chromerdp.messages; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.teavm.chromerdp.data.RemoteObjectDTO; + +/** + * + * @author Alexey Andreev + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class CallFunctionResponse { + private RemoteObjectDTO result; + private boolean wasThrown; + + public RemoteObjectDTO getResult() { + return result; + } + + public void setResult(RemoteObjectDTO result) { + this.result = result; + } + + public boolean isWasThrown() { + return wasThrown; + } + + public void setWasThrown(boolean wasThrown) { + this.wasThrown = wasThrown; + } +} diff --git a/teavm-chrome-rdp/src/main/js/chrome/main.js b/teavm-chrome-rdp/src/main/js/chrome/main.js index 684e31042..d03c61e78 100644 --- a/teavm-chrome-rdp/src/main/js/chrome/main.js +++ b/teavm-chrome-rdp/src/main/js/chrome/main.js @@ -38,7 +38,8 @@ DebuggerAgent.prototype.connectToServer = function() { DebuggerAgent.prototype.receiveMessage = function(message) { chrome.debugger.sendCommand(this.debuggee, message.method, message.params, function(response) { if (message.id) { - var responseToServer = { id : message.id, result : response }; + var responseToServer = { id : message.id, result : response, + error : response ? undefined : chrome.runtime.lastError }; this.connection.send(JSON.stringify(responseToServer)); } }.bind(this)); 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 3997798c5..cf953196f 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java @@ -33,6 +33,8 @@ public class DebugInformation { Map fileNameMap; String[] classNames; Map classNameMap; + String[] fields; + Map fieldMap; String[] methods; Map methodMap; String[] variableNames; @@ -43,6 +45,7 @@ public class DebugInformation { Mapping methodMapping; Mapping lineMapping; Mapping[] variableMappings; + List> classesMetadata; public String[] getCoveredSourceFiles() { return fileNames.clone(); @@ -118,6 +121,19 @@ public class DebugInformation { return componentByKey(mapping, variableNames, location); } + public String getFieldMeaning(String className, String jsName) { + Integer classIndex = classNameMap.get(className); + if (classIndex == null) { + return null; + } + Integer jsIndex = fieldMap.get(jsName); + if (jsIndex == null) { + return null; + } + Integer fieldIndex = classesMetadata.get(classIndex).get(jsIndex); + return fieldIndex != null ? fields[fieldIndex] : null; + } + private T componentByKey(Mapping mapping, T[] values, GeneratedLocation location) { int keyIndex = indexByKey(mapping, location); int valueIndex = keyIndex >= 0 ? mapping.values[keyIndex] : -1; @@ -142,6 +158,7 @@ public class DebugInformation { void rebuildMaps() { fileNameMap = mapArray(fileNames); classNameMap = mapArray(classNames); + fieldMap = mapArray(fields); methodMap = mapArray(methods); variableNameMap = mapArray(variableNames); } diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java index c4a8ff96a..75041a2c6 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java @@ -29,6 +29,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter { private DebugInformation debugInformation; private MappedList files = new MappedList(); private MappedList classes = new MappedList(); + private MappedList fields = new MappedList(); private MappedList methods = new MappedList(); private MappedList variableNames = new MappedList(); private Mapping fileMapping = new Mapping(); @@ -39,6 +40,8 @@ public class DebugInformationBuilder implements DebugInformationEmitter { private MethodDescriptor currentMethod; private String currentClass; private String currentFileName; + private int currentClassMetadata = -1; + private List classesMetadata = new ArrayList<>(); private int currentLine; public LocationProvider getLocationProvider() { @@ -96,18 +99,32 @@ public class DebugInformationBuilder implements DebugInformationEmitter { mapping.add(locationProvider, sourceIndex); } + @Override + public void addClass(String className) { + int classIndex = classes.index(className); + while (classIndex >= classesMetadata.size()) { + classesMetadata.add(new ClassMetadata()); + } + currentClassMetadata = classIndex; + } + + @Override + public void addField(String fieldName, String jsName) { + ClassMetadata metadata = classesMetadata.get(currentClassMetadata); + int fieldIndex = fields.index(fieldName); + int jsIndex = fields.index(jsName); + metadata.fieldMap.put(jsIndex, fieldIndex); + } + public DebugInformation getDebugInformation() { if (debugInformation == null) { debugInformation = new DebugInformation(); - debugInformation.fileNames = files.getItems(); - debugInformation.fileNameMap = files.getIndexes(); + debugInformation.fileNames = files.getItems();; debugInformation.classNames = classes.getItems(); - debugInformation.classNameMap = classes.getIndexes(); + debugInformation.fields = fields.getItems(); debugInformation.methods = methods.getItems(); - debugInformation.methodMap = methods.getIndexes(); debugInformation.variableNames = variableNames.getItems(); - debugInformation.variableNameMap = variableNames.getIndexes(); debugInformation.fileMapping = fileMapping.build(); debugInformation.lineMapping = lineMapping.build(); @@ -119,7 +136,19 @@ public class DebugInformationBuilder implements DebugInformationEmitter { debugInformation.variableMappings[var] = mapping.build(); } + List> builtMetadata = new ArrayList<>(classes.list.size()); + for (int i = 0; i < classes.list.size(); ++i) { + if (i >= classesMetadata.size()) { + builtMetadata.add(new HashMap()); + } else { + Map map = new HashMap<>(classesMetadata.get(i).fieldMap); + builtMetadata.add(map); + } + } + debugInformation.classesMetadata = builtMetadata; + debugInformation.rebuildFileDescriptions(); + debugInformation.rebuildMaps(); } return debugInformation; } @@ -172,4 +201,8 @@ public class DebugInformationBuilder implements DebugInformationEmitter { return new HashMap<>(map); } } + + static class ClassMetadata { + Map fieldMap = new HashMap<>(); + } } diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java index 0e794be15..b0a495d63 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java @@ -32,4 +32,8 @@ public interface DebugInformationEmitter { void emitClass(String className); void emitVariable(String sourceName, String generatedName); + + void addClass(String className); + + void addField(String fieldName, String jsName); } \ No newline at end of file diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java index 649189794..a5fc45ffd 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java @@ -18,6 +18,10 @@ package org.teavm.debugging; import java.io.EOFException; import java.io.IOException; import java.io.InputStream; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** * @@ -35,6 +39,7 @@ class DebugInformationReader { DebugInformation debugInfo = new DebugInformation(); debugInfo.fileNames = readStrings(); debugInfo.classNames = readStrings(); + debugInfo.fields = readStrings(); debugInfo.methods = readStrings(); debugInfo.variableNames = readStrings(); debugInfo.fileMapping = readMapping(); @@ -42,6 +47,7 @@ class DebugInformationReader { debugInfo.classMapping = readMapping(); debugInfo.methodMapping = readMapping(); debugInfo.variableMappings = readVariableMappings(debugInfo.variableNames.length); + debugInfo.classesMetadata = readClassesMetadata(debugInfo.classNames.length); debugInfo.rebuildFileDescriptions(); debugInfo.rebuildMaps(); return debugInfo; @@ -58,6 +64,22 @@ class DebugInformationReader { return mappings; } + private List> readClassesMetadata(int count) throws IOException { + List> classes = new ArrayList<>(count); + for (int i = 0; i < count; ++i) { + Map cls = new HashMap<>(); + classes.add(cls); + int entryCount = readUnsignedNumber(); + resetRelativeNumber(); + for (int j = 0; j < entryCount; ++j) { + int key = readRelativeNumber(); + int value = readUnsignedNumber(); + cls.put(key, value); + } + } + return classes; + } + private int processSign(int number) { boolean negative = (number & 1) != 0; number >>>= 1; diff --git a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java index c876ff0ad..1d7e68a0c 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java @@ -17,6 +17,10 @@ package org.teavm.debugging; import java.io.DataOutput; import java.io.IOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; /** * @@ -33,6 +37,7 @@ class DebugInformationWriter { public void write(DebugInformation debugInfo) throws IOException { writeStringArray(debugInfo.fileNames); writeStringArray(debugInfo.classNames); + writeStringArray(debugInfo.fields); writeStringArray(debugInfo.methods); writeStringArray(debugInfo.variableNames); @@ -41,6 +46,7 @@ class DebugInformationWriter { writeMapping(debugInfo.classMapping); writeMapping(debugInfo.methodMapping); writeVariableMappings(debugInfo); + writeClassMetadata(debugInfo.classesMetadata); } private void writeVariableMappings(DebugInformation debugInfo) throws IOException { @@ -57,6 +63,20 @@ class DebugInformationWriter { } } + private void writeClassMetadata(List> classes) throws IOException { + for (int i = 0; i < classes.size(); ++i) { + Map cls = classes.get(i); + writeUnsignedNumber(cls.size()); + List keys = new ArrayList<>(cls.keySet()); + Collections.sort(keys); + resetRelativeNumber(); + for (int key : keys) { + writeRelativeNumber(key); + writeUnsignedNumber(cls.get(key)); + } + } + } + private int nonNullVariableMappings(DebugInformation debugInfo) { int count = 0; for (int i = 0; i < debugInfo.variableMappings.length; ++i) { 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 c944f6c22..1d42f6349 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/Debugger.java +++ b/teavm-core/src/main/java/org/teavm/debugging/Debugger.java @@ -288,6 +288,16 @@ public class Debugger { return debugInfo.getVariableMeaningAt(location.getLine(), location.getColumn(), variable); } + public String mapField(String className, String jsField) { + for (DebugInformation debugInfo : debugInformationMap.values()) { + String meaning = debugInfo.getFieldMeaning(className, jsField); + if (meaning != null) { + return meaning; + } + } + return null; + } + private JavaScriptDebuggerListener javaScriptListener = new JavaScriptDebuggerListener() { @Override public void resumed() { diff --git a/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java b/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java index de84a96a2..9bef35c97 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java @@ -43,4 +43,12 @@ public class DummyDebugInformationEmitter implements DebugInformationEmitter { @Override public void setLocationProvider(LocationProvider locationProvider) { } + + @Override + public void addClass(String className) { + } + + @Override + public void addField(String fieldName, String jsName) { + } } diff --git a/teavm-core/src/main/java/org/teavm/debugging/JavaScriptValue.java b/teavm-core/src/main/java/org/teavm/debugging/JavaScriptValue.java index e23989495..aac497887 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/JavaScriptValue.java +++ b/teavm-core/src/main/java/org/teavm/debugging/JavaScriptValue.java @@ -24,5 +24,7 @@ import java.util.Map; public interface JavaScriptValue { String getRepresentation(); + String getClassName(); + Map getProperties(); } diff --git a/teavm-core/src/main/java/org/teavm/debugging/PropertyMap.java b/teavm-core/src/main/java/org/teavm/debugging/PropertyMap.java new file mode 100644 index 000000000..595d5ba1a --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/debugging/PropertyMap.java @@ -0,0 +1,59 @@ +package org.teavm.debugging; + +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; + +/** + * + * @author Alexey Andreev + */ +class PropertyMap extends AbstractMap{ + private String className; + private AtomicReference> backingMap = new AtomicReference<>(); + private Map jsVariables; + private Debugger debugger; + + public PropertyMap(String className, Map jsVariables, Debugger debugger) { + this.className = className; + this.jsVariables = jsVariables; + this.debugger = debugger; + } + + @Override + public int size() { + updateBackingMap(); + return backingMap.get().size(); + } + + @Override + public Variable get(Object key) { + updateBackingMap(); + return backingMap.get().get(key); + } + + @Override + public Set> entrySet() { + updateBackingMap(); + return backingMap.get().entrySet(); + } + + private void updateBackingMap() { + if (backingMap.get() != null) { + return; + } + Map vars = new HashMap<>(); + for (Map.Entry entry : jsVariables.entrySet()) { + JavaScriptVariable jsVar = entry.getValue(); + String name = debugger.mapField(className, entry.getKey()); + if (name == null) { + continue; + } + Value value = new Value(debugger, jsVar.getValue()); + vars.put(entry.getKey(), new Variable(name, value)); + } + backingMap.compareAndSet(null, vars); + } +} diff --git a/teavm-core/src/main/java/org/teavm/debugging/SimpleValue.java b/teavm-core/src/main/java/org/teavm/debugging/SimpleValue.java deleted file mode 100644 index e977361ee..000000000 --- a/teavm-core/src/main/java/org/teavm/debugging/SimpleValue.java +++ /dev/null @@ -1,41 +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.debugging; - -import java.util.Collections; -import java.util.Map; - -/** - * - * @author Alexey Andreev - */ -class SimpleValue extends Value { - private String representation; - - public SimpleValue(String representation) { - this.representation = representation; - } - - @Override - public String getRepresentation() { - return representation; - } - - @Override - public Map getProperties() { - return Collections.emptyMap(); - } -} diff --git a/teavm-core/src/main/java/org/teavm/debugging/Value.java b/teavm-core/src/main/java/org/teavm/debugging/Value.java index 3a768a141..a0968476b 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/Value.java +++ b/teavm-core/src/main/java/org/teavm/debugging/Value.java @@ -16,13 +16,34 @@ package org.teavm.debugging; import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; /** * * @author Alexey Andreev */ -public abstract class Value { - public abstract String getRepresentation(); +public class Value { + private Debugger debugger; + private JavaScriptValue jsValue; + private AtomicReference properties = new AtomicReference<>(); - public abstract Map getProperties(); + Value(Debugger debugger, JavaScriptValue jsValue) { + this.debugger = debugger; + this.jsValue = jsValue; + } + + public String getRepresentation() { + return jsValue.getRepresentation(); + } + + public String getType() { + return jsValue.getClassName(); + } + + public Map getProperties() { + if (properties.get() == null) { + properties.compareAndSet(null, new PropertyMap(jsValue.getClassName(), jsValue.getProperties(), debugger)); + } + return properties.get(); + } } diff --git a/teavm-core/src/main/java/org/teavm/debugging/VariableMap.java b/teavm-core/src/main/java/org/teavm/debugging/VariableMap.java index cc53aa26e..cb5fb1ec5 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/VariableMap.java +++ b/teavm-core/src/main/java/org/teavm/debugging/VariableMap.java @@ -66,7 +66,7 @@ class VariableMap extends AbstractMap { if (name == null) { continue; } - SimpleValue value = new SimpleValue(jsVar.getValue().getRepresentation()); + Value value = new Value(debugger, jsVar.getValue()); vars.put(entry.getKey(), new Variable(name, value)); } backingMap.compareAndSet(null, vars); diff --git a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java index e5fafbd71..f976b318f 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -243,6 +243,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext public void render(ClassNode cls) throws RenderingException { debugEmitter.emitClass(cls.getName()); + debugEmitter.addClass(cls.getName()); try { writer.append("function ").appendClass(cls.getName()).append("()").ws().append("{") .indent().softNewLine(); @@ -254,8 +255,10 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext if (value == null) { value = getDefaultValue(field.getType()); } - writer.append("this.").appendField(new FieldReference(cls.getName(), field.getName())).ws() - .append("=").ws().append(constantToString(value)).append(";").softNewLine(); + FieldReference fieldRef = new FieldReference(cls.getName(), field.getName()); + writer.append("this.").appendField(fieldRef).ws().append("=").ws().append(constantToString(value)) + .append(";").softNewLine(); + debugEmitter.addField(field.getName(), naming.getNameFor(fieldRef)); } writer.outdent().append("}").newLine(); @@ -267,8 +270,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext if (value == null) { value = getDefaultValue(field.getType()); } - writer.appendClass(cls.getName()).append('.') - .appendField(new FieldReference(cls.getName(), field.getName())).ws().append("=").ws() + FieldReference fieldRef = new FieldReference(cls.getName(), field.getName()); + writer.appendClass(cls.getName()).append('.').appendField(fieldRef).ws().append("=").ws() .append(constantToString(value)).append(";").softNewLine(); } diff --git a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js index 831bd1377..5a395f015 100644 --- a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js +++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js @@ -398,7 +398,8 @@ function $dbg_class(obj) { if (obj instanceof Long) { return "long"; } - return obj.constructor.$meta.name; + var meta = obj.constructor.$meta; + return meta ? meta.name : ""; } Long = function(lo, hi) {