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 38980bd54..d41c827b0 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 @@ -1,16 +1,16 @@ package org.teavm.chromerdp; import java.io.IOException; -import java.util.*; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.Exchanger; import java.util.concurrent.atomic.AtomicInteger; import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.map.ObjectMapper; -import org.teavm.chromerdp.data.CallFrameDTO; -import org.teavm.chromerdp.data.LocationDTO; -import org.teavm.chromerdp.data.Message; -import org.teavm.chromerdp.data.Response; +import org.teavm.chromerdp.data.*; import org.teavm.chromerdp.messages.*; import org.teavm.debugging.*; @@ -266,6 +266,37 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC sendMessage(message); } + List getScope(String scopeId) { + if (exchange == null) { + return Collections.emptyList(); + } + Message message = new Message(); + message.setId(messageIdGenerator.incrementAndGet()); + message.setMethod("Runtime.getProperties"); + GetPropertiesCommand params = new GetPropertiesCommand(); + params.setObjectId(scopeId); + params.setOwnProperties(true); + message.setParams(mapper.valueToTree(params)); + final Exchanger> exchanger = new Exchanger<>(); + responseHandlers.put(message.getId(), new ResponseHandler() { + @Override public void received(JsonNode node) throws IOException { + GetPropertiesResponse response = mapper.reader(GetPropertiesResponse.class).readValue(node); + // TODO: parse response + try { + exchanger.exchange(new ArrayList()); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + }); + sendMessage(message); + try { + return exchanger.exchange(null); + } catch (InterruptedException e) { + return Collections.emptyList(); + } + } + private T parseJson(Class type, JsonNode node) throws IOException { return mapper.reader(type).readValue(node); } @@ -282,7 +313,14 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC } RDPCallFrame map(CallFrameDTO dto) { - return new RDPCallFrame(dto.getCallFrameId(), map(dto.getLocation())); + String scopeId = null; + for (ScopeDTO scope : dto.getScopeChain()) { + if (scope.getType().equals("local")) { + scopeId = scope.getObject(); + break; + } + } + return new RDPCallFrame(dto.getCallFrameId(), map(dto.getLocation()), new RDPScope(this, scopeId)); } JavaScriptLocation map(LocationDTO dto) { diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPCallFrame.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPCallFrame.java index d89ef718d..3ed96bfc6 100644 --- a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPCallFrame.java +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPCallFrame.java @@ -15,8 +15,11 @@ */ package org.teavm.chromerdp; +import java.util.Collections; +import java.util.Map; import org.teavm.debugging.JavaScriptCallFrame; import org.teavm.debugging.JavaScriptLocation; +import org.teavm.debugging.JavaScriptVariable; /** * @@ -25,10 +28,13 @@ import org.teavm.debugging.JavaScriptLocation; public class RDPCallFrame implements JavaScriptCallFrame { private String chromeId; private JavaScriptLocation location; + private Map variables; - public RDPCallFrame(String chromeId, JavaScriptLocation location) { + public RDPCallFrame(String chromeId, JavaScriptLocation location, + Map variables) { this.chromeId = chromeId; this.location = location; + this.variables = Collections.unmodifiableMap(variables); } public String getChromeId() { @@ -39,4 +45,9 @@ public class RDPCallFrame implements JavaScriptCallFrame { public JavaScriptLocation getLocation() { return location; } + + @Override + public Map getVariables() { + return variables; + } } diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPLocalVariable.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPLocalVariable.java new file mode 100644 index 000000000..4bf308ae6 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPLocalVariable.java @@ -0,0 +1,28 @@ +package org.teavm.chromerdp; + +import org.teavm.debugging.JavaScriptValue; +import org.teavm.debugging.JavaScriptVariable; + +/** + * + * @author Alexey Andreev + */ +public class RDPLocalVariable implements JavaScriptVariable { + private String name; + private RDPValue value; + + public RDPLocalVariable(String name, RDPValue value) { + this.name = name; + this.value = value; + } + + @Override + public String getName() { + return name; + } + + @Override + public JavaScriptValue getValue() { + return value; + } +} 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 new file mode 100644 index 000000000..e024ffa8e --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPScope.java @@ -0,0 +1,53 @@ +package org.teavm.chromerdp; + +import java.util.AbstractMap; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * + * @author Alexey Andreev + */ +public class RDPScope extends AbstractMap { + private volatile Map backingMap; + private ChromeRDPDebugger debugger; + private String id; + + public RDPScope(ChromeRDPDebugger debugger, String id) { + this.debugger = debugger; + this.id = id; + } + + @Override + public Set> entrySet() { + initBackingMap(); + return backingMap.entrySet(); + } + + @Override + public int size() { + initBackingMap(); + return backingMap.size(); + } + + @Override + public RDPLocalVariable get(Object key) { + initBackingMap(); + return backingMap.get(key); + } + + private void initBackingMap() { + if (backingMap != null) { + return; + } + if (id == null) { + backingMap = new HashMap<>(); + } + Map newBackingMap = new HashMap<>(); + for (RDPLocalVariable variable : debugger.getScope(id)) { + newBackingMap.put(variable.getName(), variable); + } + backingMap = 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 new file mode 100644 index 000000000..6d1743406 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/RDPValue.java @@ -0,0 +1,28 @@ +package org.teavm.chromerdp; + +import java.util.Collections; +import java.util.Map; +import org.teavm.debugging.JavaScriptValue; +import org.teavm.debugging.JavaScriptVariable; + +/** + * + * @author Alexey Andreev + */ +public class RDPValue implements JavaScriptValue { + private String representation; + + public RDPValue(String representation) { + this.representation = representation; + } + + @Override + public String getRepresentation() { + return representation; + } + + @Override + public Map getProperties() { + return Collections.emptyMap(); + } +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallFrameDTO.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallFrameDTO.java index 584a50c99..e32b66191 100644 --- a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallFrameDTO.java +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/CallFrameDTO.java @@ -25,6 +25,7 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties; public class CallFrameDTO { private String callFrameId; private LocationDTO location; + private ScopeDTO[] scopeChain; public String getCallFrameId() { return callFrameId; @@ -41,4 +42,12 @@ public class CallFrameDTO { public void setLocation(LocationDTO location) { this.location = location; } + + public ScopeDTO[] getScopeChain() { + return scopeChain; + } + + public void setScopeChain(ScopeDTO[] scopeChain) { + this.scopeChain = scopeChain; + } } diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/PropertyDescriptorDTO.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/PropertyDescriptorDTO.java new file mode 100644 index 000000000..af4d81169 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/PropertyDescriptorDTO.java @@ -0,0 +1,29 @@ +package org.teavm.chromerdp.data; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; + +/** + * + * @author Alexey Andreev + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class PropertyDescriptorDTO { + private String name; + private RemoteObjectDTO value; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public RemoteObjectDTO getValue() { + return value; + } + + public void setValue(RemoteObjectDTO value) { + this.value = value; + } +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/RemoteObjectDTO.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/RemoteObjectDTO.java new file mode 100644 index 000000000..7b07c5630 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/RemoteObjectDTO.java @@ -0,0 +1,57 @@ +package org.teavm.chromerdp.data; + +import org.codehaus.jackson.JsonNode; +import org.codehaus.jackson.annotate.JsonIgnoreProperties; + +/** + * + * @author Alexey Andreev + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class RemoteObjectDTO { + private String className; + private String description; + private String objectId; + private String type; + private JsonNode value; + + public String getClassName() { + return className; + } + + public void setClassName(String className) { + this.className = className; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getObjectId() { + return objectId; + } + + public void setObjectId(String objectId) { + this.objectId = objectId; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + 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/data/ScopeDTO.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/ScopeDTO.java new file mode 100644 index 000000000..fda09c677 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/data/ScopeDTO.java @@ -0,0 +1,29 @@ +package org.teavm.chromerdp.data; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; + +/** + * + * @author Alexey Andreev + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class ScopeDTO { + private String object; + private String type; + + public String getObject() { + return object; + } + + public void setObject(String object) { + this.object = object; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesCommand.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesCommand.java new file mode 100644 index 000000000..5b7baf27c --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesCommand.java @@ -0,0 +1,29 @@ +package org.teavm.chromerdp.messages; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; + +/** + * + * @author Alexey Andreev + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class GetPropertiesCommand { + private String objectId; + private boolean ownProperties; + + public String getObjectId() { + return objectId; + } + + public void setObjectId(String objectId) { + this.objectId = objectId; + } + + public boolean isOwnProperties() { + return ownProperties; + } + + public void setOwnProperties(boolean ownProperties) { + this.ownProperties = ownProperties; + } +} diff --git a/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesResponse.java b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesResponse.java new file mode 100644 index 000000000..509cc4324 --- /dev/null +++ b/teavm-chrome-rdp/src/main/java/org/teavm/chromerdp/messages/GetPropertiesResponse.java @@ -0,0 +1,21 @@ +package org.teavm.chromerdp.messages; + +import org.codehaus.jackson.annotate.JsonIgnoreProperties; +import org.teavm.chromerdp.data.PropertyDescriptorDTO; + +/** + * + * @author Alexey Andreev + */ +@JsonIgnoreProperties(ignoreUnknown = true) +public class GetPropertiesResponse { + private PropertyDescriptorDTO[] properties; + + public PropertyDescriptorDTO[] getProperties() { + return properties; + } + + public void setProperties(PropertyDescriptorDTO[] properties) { + this.properties = properties; + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JavacSupport.java b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JavacSupport.java index def1bef59..f2bc94084 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/impl/JavacSupport.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/impl/JavacSupport.java @@ -33,8 +33,8 @@ public class JavacSupport implements ClassHolderTransformer { ValueType.object("javax.tools.JavaCompiler"))); Program program = new Program(); BasicBlock block = program.createBasicBlock(); - program.createVariable(); - Variable var = program.createVariable(); + program.createVariable(null); + Variable var = program.createVariable(null); ConstructInstruction construct = new ConstructInstruction(); construct.setReceiver(var); construct.setType("com.sun.tools.javac.api.JavacTool"); diff --git a/teavm-core/src/main/java/org/teavm/debugging/CallFrame.java b/teavm-core/src/main/java/org/teavm/debugging/CallFrame.java index 20edbe8c4..80a51b764 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/CallFrame.java +++ b/teavm-core/src/main/java/org/teavm/debugging/CallFrame.java @@ -15,6 +15,8 @@ */ package org.teavm.debugging; +import java.util.Collections; +import java.util.Map; import org.teavm.model.MethodReference; /** @@ -24,10 +26,12 @@ import org.teavm.model.MethodReference; public class CallFrame { private SourceLocation location; private MethodReference method; + private Map variables; - CallFrame(SourceLocation location, MethodReference method) { + CallFrame(SourceLocation location, MethodReference method, Map variables) { this.location = location; this.method = method; + this.variables = Collections.unmodifiableMap(variables); } public SourceLocation getLocation() { @@ -37,4 +41,8 @@ public class CallFrame { public MethodReference getMethod() { return method; } + + public Map getVariables() { + return variables; + } } 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 5d9eb681f..3997798c5 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformation.java @@ -35,11 +35,14 @@ public class DebugInformation { Map classNameMap; String[] methods; Map methodMap; + String[] variableNames; + Map variableNameMap; FileDescription[] fileDescriptions; Mapping fileMapping; Mapping classMapping; Mapping methodMapping; Mapping lineMapping; + Mapping[] variableMappings; public String[] getCoveredSourceFiles() { return fileNames.clone(); @@ -99,6 +102,22 @@ public class DebugInformation { return getMethodAt(new GeneratedLocation(line, column)); } + public String getVariableMeaningAt(int line, int column, String variable) { + return getVariableMeaningAt(new GeneratedLocation(line, column), variable); + } + + public String getVariableMeaningAt(GeneratedLocation location, String variable) { + Integer varIndex = variableNameMap.get(variable); + if (varIndex == null) { + return null; + } + Mapping mapping = variableMappings[varIndex]; + if (mapping == null) { + return null; + } + return componentByKey(mapping, variableNames, location); + } + private T componentByKey(Mapping mapping, T[] values, GeneratedLocation location) { int keyIndex = indexByKey(mapping, location); int valueIndex = keyIndex >= 0 ? mapping.values[keyIndex] : -1; @@ -124,6 +143,7 @@ public class DebugInformation { fileNameMap = mapArray(fileNames); classNameMap = mapArray(classNames); methodMap = mapArray(methods); + variableNameMap = mapArray(variableNames); } private Map mapArray(String[] array) { 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 2d3350418..c4a8ff96a 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationBuilder.java @@ -30,10 +30,12 @@ public class DebugInformationBuilder implements DebugInformationEmitter { private MappedList files = new MappedList(); private MappedList classes = new MappedList(); private MappedList methods = new MappedList(); + private MappedList variableNames = new MappedList(); private Mapping fileMapping = new Mapping(); private Mapping lineMapping = new Mapping(); private Mapping classMapping = new Mapping(); private Mapping methodMapping = new Mapping(); + private Map variableMappings = new HashMap<>(); private MethodDescriptor currentMethod; private String currentClass; private String currentFileName; @@ -82,6 +84,18 @@ public class DebugInformationBuilder implements DebugInformationEmitter { } } + @Override + public void emitVariable(String sourceName, String generatedName) { + int sourceIndex = variableNames.index(sourceName); + int generatedIndex = variableNames.index(generatedName); + Mapping mapping = variableMappings.get(generatedIndex); + if (mapping == null) { + mapping = new Mapping(); + variableMappings.put(generatedIndex, mapping); + } + mapping.add(locationProvider, sourceIndex); + } + public DebugInformation getDebugInformation() { if (debugInformation == null) { debugInformation = new DebugInformation(); @@ -92,11 +106,18 @@ public class DebugInformationBuilder implements DebugInformationEmitter { debugInformation.classNameMap = classes.getIndexes(); debugInformation.methods = methods.getItems(); debugInformation.methodMap = methods.getIndexes(); + debugInformation.variableNames = variableNames.getItems(); + debugInformation.variableNameMap = variableNames.getIndexes(); debugInformation.fileMapping = fileMapping.build(); debugInformation.lineMapping = lineMapping.build(); debugInformation.classMapping = classMapping.build(); debugInformation.methodMapping = methodMapping.build(); + debugInformation.variableMappings = new DebugInformation.Mapping[variableNames.list.size()]; + for (int var : variableMappings.keySet()) { + Mapping mapping = variableMappings.get(var); + debugInformation.variableMappings[var] = mapping.build(); + } debugInformation.rebuildFileDescriptions(); } 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 b88ad22b0..0e794be15 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationEmitter.java @@ -30,4 +30,6 @@ public interface DebugInformationEmitter { void emitMethod(MethodDescriptor method); void emitClass(String className); + + void emitVariable(String sourceName, String generatedName); } \ 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 59ecb4d86..d37249026 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationReader.java @@ -36,15 +36,30 @@ class DebugInformationReader { debugInfo.fileNames = readStrings(); debugInfo.classNames = readStrings(); debugInfo.methods = readStrings(); + debugInfo.variableNames = readStrings(); debugInfo.fileMapping = readMapping(); debugInfo.lineMapping = readMapping(); debugInfo.classMapping = readMapping(); debugInfo.methodMapping = readMapping(); + debugInfo.variableMappings = readVariableMappings(debugInfo.variableNames.length); debugInfo.rebuildFileDescriptions(); debugInfo.rebuildMaps(); return debugInfo; } + private DebugInformation.Mapping[] readVariableMappings(int count) throws IOException { + DebugInformation.Mapping[] mappings = new DebugInformation.Mapping[count]; + int varCount = readUnsignedNumber(); + while (varCount-- > 0) { + int lastVar = 0; + for (int i = 0; i < mappings.length; ++i) { + lastVar += readUnsignedNumber(); + mappings[lastVar] = readMapping(); + } + } + return mappings; + } + 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 f50d6ba1f..d1402c39d 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DebugInformationWriter.java @@ -34,11 +34,37 @@ class DebugInformationWriter { writeStringArray(debugInfo.fileNames); writeStringArray(debugInfo.classNames); writeStringArray(debugInfo.methods); + writeStringArray(debugInfo.variableNames); writeMapping(debugInfo.fileMapping); writeMapping(debugInfo.lineMapping); writeMapping(debugInfo.classMapping); writeMapping(debugInfo.methodMapping); + writeUnsignedNumber(nonNullVariableMappings(debugInfo)); + writeVariableMappings(debugInfo); + } + + private void writeVariableMappings(DebugInformation debugInfo) throws IOException { + int lastVar = 0; + for (int i = 0; i < debugInfo.variableMappings.length; ++i) { + DebugInformation.Mapping mapping = debugInfo.variableMappings[i]; + if (mapping == null) { + continue; + } + writeUnsignedNumber(i - lastVar); + lastVar = i; + writeMapping(mapping); + } + } + + private int nonNullVariableMappings(DebugInformation debugInfo) { + int count = 0; + for (int i = 0; i < debugInfo.variableMappings.length; ++i) { + if (debugInfo.variableMappings[i] != null) { + ++count; + } + } + return count; } private void writeStringArray(String[] array) throws IOException { 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 48dd77c78..eabab3b1b 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/Debugger.java +++ b/teavm-core/src/main/java/org/teavm/debugging/Debugger.java @@ -186,7 +186,7 @@ public class Debugger { MethodReference method = !empty ? debugInformation.getMethodAt(jsFrame.getLocation().getLine(), jsFrame.getLocation().getColumn()) : null; if (!empty || !wasEmpty) { - frames.add(new CallFrame(loc, method)); + frames.add(new CallFrame(loc, method, new HashMap())); } wasEmpty = empty; } 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 49d6e2c98..de84a96a2 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java +++ b/teavm-core/src/main/java/org/teavm/debugging/DummyDebugInformationEmitter.java @@ -36,6 +36,10 @@ public class DummyDebugInformationEmitter implements DebugInformationEmitter { public void emitClass(String className) { } + @Override + public void emitVariable(String sourceName, String generatedName) { + } + @Override public void setLocationProvider(LocationProvider locationProvider) { } diff --git a/teavm-core/src/main/java/org/teavm/debugging/JavaScriptCallFrame.java b/teavm-core/src/main/java/org/teavm/debugging/JavaScriptCallFrame.java index a0a6d4c4a..a80e8aab2 100644 --- a/teavm-core/src/main/java/org/teavm/debugging/JavaScriptCallFrame.java +++ b/teavm-core/src/main/java/org/teavm/debugging/JavaScriptCallFrame.java @@ -15,10 +15,14 @@ */ package org.teavm.debugging; +import java.util.Map; + /** * * @author Alexey Andreev */ public interface JavaScriptCallFrame { JavaScriptLocation getLocation(); + + Map getVariables(); } diff --git a/teavm-core/src/main/java/org/teavm/debugging/JavaScriptValue.java b/teavm-core/src/main/java/org/teavm/debugging/JavaScriptValue.java new file mode 100644 index 000000000..7541f781b --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/debugging/JavaScriptValue.java @@ -0,0 +1,13 @@ +package org.teavm.debugging; + +import java.util.Map; + +/** + * + * @author Alexey Andreev + */ +public interface JavaScriptValue { + String getRepresentation(); + + Map getProperties(); +} diff --git a/teavm-core/src/main/java/org/teavm/debugging/JavaScriptVariable.java b/teavm-core/src/main/java/org/teavm/debugging/JavaScriptVariable.java new file mode 100644 index 000000000..511216f39 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/debugging/JavaScriptVariable.java @@ -0,0 +1,11 @@ +package org.teavm.debugging; + +/** + * + * @author Alexey Andreev + */ +public interface JavaScriptVariable { + String getName(); + + JavaScriptValue getValue(); +} diff --git a/teavm-core/src/main/java/org/teavm/debugging/LocalVariable.java b/teavm-core/src/main/java/org/teavm/debugging/LocalVariable.java new file mode 100644 index 000000000..e98029b55 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/debugging/LocalVariable.java @@ -0,0 +1,23 @@ +package org.teavm.debugging; + +/** + * + * @author Alexey Andreev + */ +public class LocalVariable { + private String name; + private WatchedValue value; + + LocalVariable(String name, WatchedValue value) { + this.name = name; + this.value = value; + } + + public String getName() { + return name; + } + + public WatchedValue getValue() { + return value; + } +} diff --git a/teavm-core/src/main/java/org/teavm/debugging/WatchedValue.java b/teavm-core/src/main/java/org/teavm/debugging/WatchedValue.java new file mode 100644 index 000000000..bf44a8410 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/debugging/WatchedValue.java @@ -0,0 +1,13 @@ +package org.teavm.debugging; + +import java.util.Map; + +/** + * + * @author Alexey Andreev + */ +public abstract class WatchedValue { + public abstract String getRepresentation(); + + public abstract Map getProperties(); +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java index a7e186502..74f104fc2 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Decompiler.java @@ -268,6 +268,11 @@ public class Decompiler { Optimizer optimizer = new Optimizer(); optimizer.optimize(methodNode, method.getProgram()); methodNode.getModifiers().addAll(mapModifiers(method.getModifiers())); + int paramCount = method.getSignature().length; + for (int i = 0; i < paramCount; ++i) { + Variable var = program.variableAt(i); + methodNode.getParameterDebugNames().add(var.getDebugName()); + } return methodNode; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/NullPointerExceptionTransformer.java b/teavm-core/src/main/java/org/teavm/javascript/NullPointerExceptionTransformer.java index f915886d9..59d872a8f 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/NullPointerExceptionTransformer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/NullPointerExceptionTransformer.java @@ -49,7 +49,7 @@ public class NullPointerExceptionTransformer implements ClassHolderTransformer { } NullCheckInstruction nullCheck = new NullCheckInstruction(); nullCheck.setValue(invoke.getInstance()); - Variable var = block.getProgram().createVariable(); + Variable var = block.getProgram().createVariable(null); nullCheck.setReceiver(var); invoke.setInstance(var); block.getInstructions().add(i++, nullCheck); 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 695c90b1d..e5fafbd71 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/Renderer.java +++ b/teavm-core/src/main/java/org/teavm/javascript/Renderer.java @@ -518,6 +518,9 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext public void visit(RegularMethodNode method) { try { MethodReference ref = method.getReference(); + for (int i = 0; i < method.getParameterDebugNames().size(); ++i) { + debugEmitter.emitVariable(method.getParameterDebugNames().get(i), variableName(i)); + } int variableCount = 0; for (int var : method.getVariables()) { variableCount = Math.max(variableCount, var + 1); @@ -601,6 +604,10 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext if (statement.getLocation() != null) { popLocation(); } + if (statement.getLeftValue() instanceof VariableExpr) { + VariableExpr receiver = (VariableExpr)statement.getLeftValue(); + debugEmitter.emitVariable(statement.getDebugName(), variableName(receiver.getIndex())); + } } catch (IOException e) { throw new RenderingException("IO error occured", e); } diff --git a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java index aaf3e8b2f..20444591f 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java +++ b/teavm-core/src/main/java/org/teavm/javascript/StatementGenerator.java @@ -48,44 +48,44 @@ class StatementGenerator implements InstructionVisitor { @Override public void visit(ClassConstantInstruction insn) { - assign(Expr.constant(insn.getConstant()), insn.getReceiver().getIndex()); + assign(Expr.constant(insn.getConstant()), insn.getReceiver()); } @Override public void visit(NullConstantInstruction insn) { - assign(Expr.constant(null), insn.getReceiver().getIndex()); + assign(Expr.constant(null), insn.getReceiver()); } @Override public void visit(IntegerConstantInstruction insn) { - assign(Expr.constant(insn.getConstant()), insn.getReceiver().getIndex()); + assign(Expr.constant(insn.getConstant()), insn.getReceiver()); } @Override public void visit(LongConstantInstruction insn) { - assign(Expr.constant(insn.getConstant()), insn.getReceiver().getIndex()); + assign(Expr.constant(insn.getConstant()), insn.getReceiver()); } @Override public void visit(FloatConstantInstruction insn) { - assign(Expr.constant(insn.getConstant()), insn.getReceiver().getIndex()); + assign(Expr.constant(insn.getConstant()), insn.getReceiver()); } @Override public void visit(DoubleConstantInstruction insn) { - assign(Expr.constant(insn.getConstant()), insn.getReceiver().getIndex()); + assign(Expr.constant(insn.getConstant()), insn.getReceiver()); } @Override public void visit(StringConstantInstruction insn) { - assign(Expr.constant(insn.getConstant()), insn.getReceiver().getIndex()); + assign(Expr.constant(insn.getConstant()), insn.getReceiver()); } @Override public void visit(BinaryInstruction insn) { int first = insn.getFirstOperand().getIndex(); int second = insn.getSecondOperand().getIndex(); - int result = insn.getReceiver().getIndex(); + Variable result = insn.getReceiver(); switch (insn.getOperation()) { case ADD: switch (insn.getOperandType()) { @@ -227,15 +227,15 @@ class StatementGenerator implements InstructionVisitor { switch (insn.getOperandType()) { case INT: assign(castToInteger(Expr.unary(UnaryOperation.NEGATE, Expr.var(insn.getOperand().getIndex()))), - insn.getReceiver().getIndex()); + insn.getReceiver()); break; case LONG: assign(Expr.unary(UnaryOperation.NEGATE_LONG, Expr.var(insn.getOperand().getIndex())), - insn.getReceiver().getIndex()); + insn.getReceiver()); break; default: assign(Expr.unary(UnaryOperation.NEGATE, Expr.var(insn.getOperand().getIndex())), - insn.getReceiver().getIndex()); + insn.getReceiver()); break; } } @@ -244,13 +244,14 @@ class StatementGenerator implements InstructionVisitor { public void visit(AssignInstruction insn) { AssignmentStatement stmt = Statement.assign(Expr.var(insn.getReceiver().getIndex()), Expr.var(insn.getAssignee().getIndex())); + stmt.setDebugName(insn.getReceiver().getDebugName()); stmt.setLocation(currentLocation); statements.add(stmt); } @Override public void visit(CastInstruction insn) { - assign(Expr.var(insn.getValue().getIndex()), insn.getReceiver().getIndex()); + assign(Expr.var(insn.getValue().getIndex()), insn.getReceiver()); } @Override @@ -292,7 +293,7 @@ class StatementGenerator implements InstructionVisitor { default: break; } - assign(value, insn.getReceiver().getIndex()); + assign(value, insn.getReceiver()); } @Override @@ -323,7 +324,7 @@ class StatementGenerator implements InstructionVisitor { } break; } - assign(value, insn.getReceiver().getIndex()); + assign(value, insn.getReceiver()); } @Override @@ -459,13 +460,12 @@ class StatementGenerator implements InstructionVisitor { @Override public void visit(ConstructArrayInstruction insn) { - assign(Expr.createArray(insn.getItemType(), Expr.var(insn.getSize().getIndex())), - insn.getReceiver().getIndex()); + assign(Expr.createArray(insn.getItemType(), Expr.var(insn.getSize().getIndex())), insn.getReceiver()); } @Override public void visit(ConstructInstruction insn) { - assign(Expr.createObject(insn.getType()), insn.getReceiver().getIndex()); + assign(Expr.createObject(insn.getType()), insn.getReceiver()); } @Override @@ -474,7 +474,7 @@ class StatementGenerator implements InstructionVisitor { for (int i = 0; i < dimensionExprs.length; ++i) { dimensionExprs[i] = Expr.var(insn.getDimensions().get(i).getIndex()); } - assign(Expr.createArray(insn.getItemType(), dimensionExprs), insn.getReceiver().getIndex()); + assign(Expr.createArray(insn.getItemType(), dimensionExprs), insn.getReceiver()); } @Override @@ -509,28 +509,27 @@ class StatementGenerator implements InstructionVisitor { @Override public void visit(ArrayLengthInstruction insn) { - assign(Expr.unary(UnaryOperation.LENGTH, Expr.var(insn.getArray().getIndex())), insn.getReceiver().getIndex()); + assign(Expr.unary(UnaryOperation.LENGTH, Expr.var(insn.getArray().getIndex())), insn.getReceiver()); } @Override public void visit(UnwrapArrayInstruction insn) { UnwrapArrayExpr unwrapExpr = new UnwrapArrayExpr(insn.getElementType()); unwrapExpr.setArray(Expr.var(insn.getArray().getIndex())); - assign(unwrapExpr, insn.getReceiver().getIndex()); + assign(unwrapExpr, insn.getReceiver()); } @Override public void visit(CloneArrayInstruction insn) { MethodDescriptor cloneMethodDesc = new MethodDescriptor("clone", ValueType.object("java.lang.Object")); MethodReference cloneMethod = new MethodReference("java.lang.Object", cloneMethodDesc); - assign(Expr.invoke(cloneMethod, Expr.var(insn.getArray().getIndex()), new Expr[0]), - insn.getReceiver().getIndex()); + assign(Expr.invoke(cloneMethod, Expr.var(insn.getArray().getIndex()), new Expr[0]), insn.getReceiver()); } @Override public void visit(GetElementInstruction insn) { assign(Expr.subscript(Expr.var(insn.getArray().getIndex()), Expr.var(insn.getIndex().getIndex())), - insn.getReceiver().getIndex()); + insn.getReceiver()); } @Override @@ -559,7 +558,7 @@ class StatementGenerator implements InstructionVisitor { invocationExpr = Expr.invokeStatic(insn.getMethod(), exprArgs); } if (insn.getReceiver() != null) { - assign(invocationExpr, insn.getReceiver().getIndex()); + assign(invocationExpr, insn.getReceiver()); } else { AssignmentStatement stmt = Statement.assign(null, invocationExpr); stmt.setLocation(currentLocation); @@ -569,13 +568,13 @@ class StatementGenerator implements InstructionVisitor { @Override public void visit(IsInstanceInstruction insn) { - assign(Expr.instanceOf(Expr.var(insn.getValue().getIndex()), insn.getType()), - insn.getReceiver().getIndex()); + assign(Expr.instanceOf(Expr.var(insn.getValue().getIndex()), insn.getType()), insn.getReceiver()); } - private void assign(Expr source, int target) { - AssignmentStatement stmt = Statement.assign(Expr.var(target), source); + private void assign(Expr source, Variable target) { + AssignmentStatement stmt = Statement.assign(Expr.var(target.getIndex()), source); stmt.setLocation(currentLocation); + stmt.setDebugName(target.getDebugName()); statements.add(stmt); } @@ -599,11 +598,11 @@ class StatementGenerator implements InstructionVisitor { return Expr.unary(UnaryOperation.LONG_TO_NUM, value); } - private void binary(int first, int second, int result, BinaryOperation op) { + private void binary(int first, int second, Variable result, BinaryOperation op) { assign(Expr.binary(op, Expr.var(first), Expr.var(second)), result); } - private void intBinary(int first, int second, int result, BinaryOperation op) { + private void intBinary(int first, int second, Variable result, BinaryOperation op) { assign(castToInteger(Expr.binary(op, Expr.var(first), Expr.var(second))), result); } @@ -657,7 +656,6 @@ class StatementGenerator implements InstructionVisitor { @Override public void visit(NullCheckInstruction insn) { - assign(Expr.unary(UnaryOperation.NULL_CHECK, Expr.var(insn.getValue().getIndex())), - insn.getReceiver().getIndex()); + assign(Expr.unary(UnaryOperation.NULL_CHECK, Expr.var(insn.getValue().getIndex())), insn.getReceiver()); } } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/AssignmentStatement.java b/teavm-core/src/main/java/org/teavm/javascript/ast/AssignmentStatement.java index 49f670f8b..f76b6465e 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/AssignmentStatement.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/AssignmentStatement.java @@ -23,6 +23,7 @@ public class AssignmentStatement extends Statement { private Expr leftValue; private Expr rightValue; private NodeLocation location; + private String debugName; public Expr getLeftValue() { return leftValue; @@ -48,6 +49,14 @@ public class AssignmentStatement extends Statement { this.location = location; } + public String getDebugName() { + return debugName; + } + + public void setDebugName(String debugName) { + this.debugName = debugName; + } + @Override public void acceptVisitor(StatementVisitor visitor) { visitor.visit(this); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/RegularMethodNode.java b/teavm-core/src/main/java/org/teavm/javascript/ast/RegularMethodNode.java index 8184d8fac..f37a9ccb4 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/RegularMethodNode.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/RegularMethodNode.java @@ -26,6 +26,7 @@ import org.teavm.model.MethodReference; public class RegularMethodNode extends MethodNode { private Statement body; private List variables = new ArrayList<>(); + private List parameterDebugNames = new ArrayList<>(); public RegularMethodNode(MethodReference reference) { super(reference); @@ -43,6 +44,10 @@ public class RegularMethodNode extends MethodNode { return variables; } + public List getParameterDebugNames() { + return parameterDebugNames; + } + @Override public void acceptVisitor(MethodNodeVisitor visitor) { visitor.visit(this); diff --git a/teavm-core/src/main/java/org/teavm/model/Program.java b/teavm-core/src/main/java/org/teavm/model/Program.java index 9c3cab944..ffb05aa15 100644 --- a/teavm-core/src/main/java/org/teavm/model/Program.java +++ b/teavm-core/src/main/java/org/teavm/model/Program.java @@ -35,8 +35,8 @@ public class Program implements ProgramReader { return block; } - public Variable createVariable() { - Variable variable = new Variable(this); + public Variable createVariable(String debugName) { + Variable variable = new Variable(this, debugName); variable.setIndex(variables.size()); variables.add(variable); variable.setRegister(lastUsedRegister++); diff --git a/teavm-core/src/main/java/org/teavm/model/Variable.java b/teavm-core/src/main/java/org/teavm/model/Variable.java index ceb63fc01..2708f68a0 100644 --- a/teavm-core/src/main/java/org/teavm/model/Variable.java +++ b/teavm-core/src/main/java/org/teavm/model/Variable.java @@ -23,9 +23,11 @@ public class Variable implements VariableReader { private Program program; private int index; private int register; + private String debugName; - Variable(Program program) { + Variable(Program program, String debugName) { this.program = program; + this.debugName = debugName; } @Override @@ -54,4 +56,13 @@ public class Variable implements VariableReader { public void setRegister(int register) { this.register = register; } + + @Override + public String getDebugName() { + return debugName; + } + + public void setDebugName(String debugName) { + this.debugName = debugName; + } } diff --git a/teavm-core/src/main/java/org/teavm/model/VariableReader.java b/teavm-core/src/main/java/org/teavm/model/VariableReader.java index d93ed618e..a62aaa3d7 100644 --- a/teavm-core/src/main/java/org/teavm/model/VariableReader.java +++ b/teavm-core/src/main/java/org/teavm/model/VariableReader.java @@ -24,5 +24,7 @@ public interface VariableReader { ProgramReader getProgram(); + String getDebugName(); + int getRegister(); } diff --git a/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java b/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java index 7ec26790e..ec2bffac6 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java +++ b/teavm-core/src/main/java/org/teavm/model/util/ProgramUtils.java @@ -73,7 +73,7 @@ public final class ProgramUtils { InstructionCopyReader insnCopier = new InstructionCopyReader(); insnCopier.programCopy = copy; for (int i = 0; i < program.variableCount(); ++i) { - copy.createVariable(); + copy.createVariable(program.variableAt(i).getDebugName()); } for (int i = 0; i < program.basicBlockCount(); ++i) { copy.createBasicBlock(); diff --git a/teavm-core/src/main/java/org/teavm/model/util/RegisterAllocator.java b/teavm-core/src/main/java/org/teavm/model/util/RegisterAllocator.java index 946fdf740..5d74f3c5a 100644 --- a/teavm-core/src/main/java/org/teavm/model/util/RegisterAllocator.java +++ b/teavm-core/src/main/java/org/teavm/model/util/RegisterAllocator.java @@ -97,7 +97,7 @@ public class RegisterAllocator { final Phi phi = incoming.getPhi(); Program program = phi.getBasicBlock().getProgram(); AssignInstruction copyInstruction = new AssignInstruction(); - Variable firstCopy = program.createVariable(); + Variable firstCopy = program.createVariable(incoming.getValue().getDebugName()); copyInstruction.setReceiver(firstCopy); copyInstruction.setAssignee(incoming.getValue()); BasicBlock source = blockMap.get(incoming.getSource()); diff --git a/teavm-core/src/main/java/org/teavm/optimization/LoopInvariantMotion.java b/teavm-core/src/main/java/org/teavm/optimization/LoopInvariantMotion.java index 32b7da2c7..4cc8c979e 100644 --- a/teavm-core/src/main/java/org/teavm/optimization/LoopInvariantMotion.java +++ b/teavm-core/src/main/java/org/teavm/optimization/LoopInvariantMotion.java @@ -179,7 +179,7 @@ public class LoopInvariantMotion implements MethodOptimization { phi.getIncomings().remove(j--); if (preheaderPhi == null) { preheaderPhi = new Phi(); - preheaderPhi.setReceiver(program.createVariable()); + preheaderPhi.setReceiver(program.createVariable(null)); preheader.getPhis().add(preheaderPhi); } preheaderPhi.getIncomings().add(incoming); @@ -391,7 +391,7 @@ public class LoopInvariantMotion implements MethodOptimization { @Override public void visit(ClassConstantInstruction insn) { - var = program.createVariable(); + var = program.createVariable(insn.getReceiver().getDebugName()); ClassConstantInstruction copy = new ClassConstantInstruction(); copy.setConstant(insn.getConstant()); copy.setReceiver(var); @@ -400,7 +400,7 @@ public class LoopInvariantMotion implements MethodOptimization { @Override public void visit(NullConstantInstruction insn) { - var = program.createVariable(); + var = program.createVariable(insn.getReceiver().getDebugName()); NullConstantInstruction copy = new NullConstantInstruction(); copy.setReceiver(var); this.copy = copy; @@ -408,7 +408,7 @@ public class LoopInvariantMotion implements MethodOptimization { @Override public void visit(IntegerConstantInstruction insn) { - var = program.createVariable(); + var = program.createVariable(insn.getReceiver().getDebugName()); IntegerConstantInstruction copy = new IntegerConstantInstruction(); copy.setConstant(insn.getConstant()); copy.setReceiver(var); @@ -417,7 +417,7 @@ public class LoopInvariantMotion implements MethodOptimization { @Override public void visit(LongConstantInstruction insn) { - var = program.createVariable(); + var = program.createVariable(insn.getReceiver().getDebugName()); LongConstantInstruction copy = new LongConstantInstruction(); copy.setConstant(insn.getConstant()); copy.setReceiver(var); @@ -426,7 +426,7 @@ public class LoopInvariantMotion implements MethodOptimization { @Override public void visit(FloatConstantInstruction insn) { - var = program.createVariable(); + var = program.createVariable(insn.getReceiver().getDebugName()); FloatConstantInstruction copy = new FloatConstantInstruction(); copy.setConstant(insn.getConstant()); copy.setReceiver(var); @@ -435,7 +435,7 @@ public class LoopInvariantMotion implements MethodOptimization { @Override public void visit(DoubleConstantInstruction insn) { - var = program.createVariable(); + var = program.createVariable(insn.getReceiver().getDebugName()); DoubleConstantInstruction copy = new DoubleConstantInstruction(); copy.setConstant(insn.getConstant()); copy.setReceiver(var); @@ -444,7 +444,7 @@ public class LoopInvariantMotion implements MethodOptimization { @Override public void visit(StringConstantInstruction insn) { - var = program.createVariable(); + var = program.createVariable(insn.getReceiver().getDebugName()); StringConstantInstruction copy = new StringConstantInstruction(); copy.setConstant(insn.getConstant()); copy.setReceiver(var); diff --git a/teavm-core/src/main/java/org/teavm/parsing/Parser.java b/teavm-core/src/main/java/org/teavm/parsing/Parser.java index e8d647ed8..e01940cda 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/Parser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/Parser.java @@ -40,11 +40,11 @@ public final class Parser { Program program = programParser.parse(node, className); new UnreachableBasicBlockEliminator().optimize(program); SSATransformer ssaProducer = new SSATransformer(); - ssaProducer.transformToSSA(program, method.getParameterTypes()); + ssaProducer.transformToSSA(program, programParser, method.getParameterTypes()); method.setProgram(program); parseAnnotations(method.getAnnotations(), node.visibleAnnotations, node.invisibleAnnotations); while (program.variableCount() <= method.parameterCount()) { - program.createVariable(); + program.createVariable(null); } return method; } diff --git a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java index 43842868c..c18cb3fee 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java +++ b/teavm-core/src/main/java/org/teavm/parsing/ProgramParser.java @@ -20,13 +20,14 @@ import org.objectweb.asm.*; import org.objectweb.asm.tree.*; import org.teavm.model.*; import org.teavm.model.instructions.*; +import org.teavm.model.util.DefinitionExtractor; import org.teavm.model.util.InstructionTransitionExtractor; /** * * @author Alexey Andreev */ -public class ProgramParser { +public class ProgramParser implements VariableDebugInformation { static final byte ROOT = 0; static final byte SINGLE = 1; static final byte DOUBLE_FIRST_HALF = 2; @@ -48,6 +49,9 @@ public class ProgramParser { private int currentLineNumber; private boolean lineNumberChanged; private InstructionLocation lastInsnLocation; + private Map> localVariableMap = new HashMap<>(); + private Map variableDebugNames = new HashMap<>(); + private Map parameterNames = new HashMap<>(); private static class Step { public final int source; @@ -113,11 +117,29 @@ public class ProgramParser { } int signatureVars = countSignatureVariables(method.desc); while (program.variableCount() <= signatureVars) { - program.createVariable(); + program.createVariable(getVariableDebugName(program.variableCount(), 0)); + } + for (int i = 0; i < signatureVars; ++i) { + parameterNames.put(i, getVariableDebugName(i, 0)); } return program; } + private String getVariableDebugName(int var, int location) { + List nodes = localVariableMap.get(var); + if (nodes == null) { + return null; + } + for (LocalVariableNode node : nodes) { + int start = labelIndexes.get(node.start.getLabel()); + int end = labelIndexes.get(node.end.getLabel()); + if (location >= start && location < end) { + return node.name; + } + } + return null; + } + private int countSignatureVariables(String desc) { int count = 1; for (Type paramType : Type.getArgumentTypes(desc)) { @@ -159,6 +181,16 @@ public class ProgramParser { return depth; } + @Override + public String getDefinitionDebugName(Instruction insn) { + return variableDebugNames.get(insn); + } + + @Override + public String getParameterDebugName(int index) { + return parameterNames.get(index); + } + private void prepare(MethodNode method) { InsnList instructions = method.instructions; minLocal = 0; @@ -177,6 +209,14 @@ public class ProgramParser { lineNumbers.put(lineNumberNode.start.getLabel(), lineNumberNode.line); } } + for (LocalVariableNode localVar : method.localVariables) { + List vars = localVariableMap.get(localVar.index); + if (vars == null) { + vars = new ArrayList<>(); + localVariableMap.put(localVar.index, vars); + } + vars.add(localVar); + } targetInstructions = new ArrayList<>(instructions.size()); targetInstructions.addAll(Collections.>nCopies(instructions.size(), null)); basicBlocks.addAll(Collections.nCopies(instructions.size(), null)); @@ -250,6 +290,20 @@ public class ProgramParser { } private void assemble() { + DefinitionExtractor defExtractor = new DefinitionExtractor(); + for (int i = 0; i < targetInstructions.size(); ++i) { + List instructionList = targetInstructions.get(i); + for (Instruction insn : instructionList) { + insn.acceptVisitor(defExtractor); + for (Variable var : defExtractor.getDefinedVariables()) { + String debugName = getVariableDebugName(var.getIndex(), i); + if (debugName != null) { + variableDebugNames.put(insn, debugName); + } + } + } + } + BasicBlock basicBlock = null; for (int i = 0; i < basicBlocks.size(); ++i) { BasicBlock newBasicBlock = basicBlocks.get(i); @@ -300,7 +354,7 @@ public class ProgramParser { private Variable getVariable(int index) { while (index >= program.variableCount()) { - program.createVariable(); + program.createVariable(null); } return program.variableAt(index); } diff --git a/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java b/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java index 46d93dbc0..af0e333c0 100644 --- a/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java +++ b/teavm-core/src/main/java/org/teavm/parsing/SSATransformer.java @@ -38,12 +38,14 @@ public class SSATransformer { private Phi[][] phiMap; private int[][] phiIndexMap; private ValueType[] arguments; + private VariableDebugInformation variableDebugInfo; - public void transformToSSA(Program program, ValueType[] arguments) { + public void transformToSSA(Program program, VariableDebugInformation variableDebugInfo, ValueType[] arguments) { if (program.basicBlockCount() == 0) { return; } this.program = program; + this.variableDebugInfo = variableDebugInfo; this.arguments = arguments; cfg = ProgramUtils.buildControlFlowGraphWithoutTryCatch(program); domTree = GraphUtils.buildDominatorTree(cfg); @@ -55,6 +57,9 @@ public class SSATransformer { phiMap[i] = new Phi[program.variableCount()]; phiIndexMap[i] = new int[program.variableCount()]; } + for (int i = 0; i < program.variableCount(); ++i) { + program.variableAt(i).setDebugName(variableDebugInfo.getParameterDebugName(i)); + } applySignature(); domFrontiers = GraphUtils.findDominanceFrontiers(cfg, domTree); estimatePhis(); @@ -136,16 +141,17 @@ public class SSATransformer { processed[currentBlock.getIndex()] = true; variableMap = Arrays.copyOf(task.variables, task.variables.length); for (Phi phi : currentBlock.getPhis()) { - Variable var = program.createVariable(); + Variable var = program.createVariable(phi.getReceiver().getDebugName()); variableMap[phi.getReceiver().getIndex()] = var; phi.setReceiver(var); } if (!caughtBlocks.get(currentBlock.getIndex()).isEmpty()) { Phi phi = new Phi(); - phi.setReceiver(program.createVariable()); + phi.setReceiver(program.createVariable(null)); for (TryCatchBlock tryCatch : caughtBlocks.get(currentBlock.getIndex())) { variableMap[tryCatch.getExceptionVariable().getIndex()] = phi.getReceiver(); - tryCatch.setExceptionVariable(program.createVariable()); + tryCatch.setExceptionVariable(program.createVariable( + tryCatch.getExceptionVariable().getDebugName())); Incoming incoming = new Incoming(); incoming.setSource(tryCatch.getProtectedBlock()); incoming.setValue(tryCatch.getExceptionVariable()); @@ -210,8 +216,8 @@ public class SSATransformer { } } - private Variable define(Variable var) { - Variable result = program.createVariable(); + private Variable define(Variable var, String debugName) { + Variable result = program.createVariable(debugName); variableMap[var.getIndex()] = result; return result; } @@ -231,56 +237,56 @@ public class SSATransformer { @Override public void visit(ClassConstantInstruction insn) { - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(NullConstantInstruction insn) { - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(IntegerConstantInstruction insn) { - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(LongConstantInstruction insn) { - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(FloatConstantInstruction insn) { - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(DoubleConstantInstruction insn) { - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(StringConstantInstruction insn) { - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(BinaryInstruction insn) { insn.setFirstOperand(use(insn.getFirstOperand())); insn.setSecondOperand(use(insn.getSecondOperand())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(NegateInstruction insn) { insn.setOperand(use(insn.getOperand())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(AssignInstruction insn) { insn.setAssignee(use(insn.getAssignee())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override @@ -318,12 +324,12 @@ public class SSATransformer { @Override public void visit(ConstructArrayInstruction insn) { insn.setSize(use(insn.getSize())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(ConstructInstruction insn) { - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override @@ -332,7 +338,7 @@ public class SSATransformer { for (int i = 0; i < dimensions.size(); ++i) { dimensions.set(i, use(dimensions.get(i))); } - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override @@ -340,7 +346,7 @@ public class SSATransformer { if (insn.getInstance() != null) { insn.setInstance(use(insn.getInstance())); } - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override @@ -355,7 +361,7 @@ public class SSATransformer { public void visit(GetElementInstruction insn) { insn.setArray(use(insn.getArray())); insn.setIndex(use(insn.getIndex())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override @@ -375,50 +381,50 @@ public class SSATransformer { insn.setInstance(use(insn.getInstance())); } if (insn.getReceiver() != null) { - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } } @Override public void visit(IsInstanceInstruction insn) { insn.setValue(use(insn.getValue())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(CastInstruction insn) { insn.setValue(use(insn.getValue())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(CastNumberInstruction insn) { insn.setValue(use(insn.getValue())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(CastIntegerInstruction insn) { insn.setValue(use(insn.getValue())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(ArrayLengthInstruction insn) { insn.setArray(use(insn.getArray())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(UnwrapArrayInstruction insn) { insn.setArray(use(insn.getArray())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override public void visit(CloneArrayInstruction insn) { insn.setArray(use(insn.getArray())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } @Override @@ -428,7 +434,7 @@ public class SSATransformer { @Override public void visit(NullCheckInstruction insn) { insn.setValue(use(insn.getValue())); - insn.setReceiver(define(insn.getReceiver())); + insn.setReceiver(define(insn.getReceiver(), variableDebugInfo.getDefinitionDebugName(insn))); } }; } diff --git a/teavm-core/src/main/java/org/teavm/parsing/VariableDebugInformation.java b/teavm-core/src/main/java/org/teavm/parsing/VariableDebugInformation.java new file mode 100644 index 000000000..db11c723e --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/parsing/VariableDebugInformation.java @@ -0,0 +1,13 @@ +package org.teavm.parsing; + +import org.teavm.model.Instruction; + +/** + * + * @author Alexey Andreev + */ +public interface VariableDebugInformation { + String getDefinitionDebugName(Instruction insn); + + String getParameterDebugName(int index); +} diff --git a/teavm-html4j/src/main/java/org/teavm/html4j/JCLHacks.java b/teavm-html4j/src/main/java/org/teavm/html4j/JCLHacks.java index 7a0cbd850..2c4557a3f 100644 --- a/teavm-html4j/src/main/java/org/teavm/html4j/JCLHacks.java +++ b/teavm-html4j/src/main/java/org/teavm/html4j/JCLHacks.java @@ -44,13 +44,13 @@ public class JCLHacks implements ClassHolderTransformer { MethodHolder method = new MethodHolder(desc); Program program = new Program(); for (int i = 0; i < desc.parameterCount(); ++i) { - program.createVariable(); + program.createVariable(null); } if (!staticMethod) { - program.createVariable(); + program.createVariable(null); } - program.createVariable(); - Variable var = program.createVariable(); + program.createVariable(null); + Variable var = program.createVariable(null); BasicBlock block = program.createBasicBlock(); ConstructInstruction cons = new ConstructInstruction(); cons.setType("java.lang.SecurityException"); @@ -75,8 +75,8 @@ public class JCLHacks implements ClassHolderTransformer { private MethodHolder createThreadSleep() { MethodHolder method = new MethodHolder("sleep", ValueType.LONG, ValueType.VOID); Program program = new Program(); - program.createVariable(); - program.createVariable(); + program.createVariable(null); + program.createVariable(null); BasicBlock block = program.createBasicBlock(); ExitInstruction exit = new ExitInstruction(); block.getInstructions().add(exit); diff --git a/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java b/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java index 73e6a0971..1f50781c6 100644 --- a/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java +++ b/teavm-jso/src/main/java/org/teavm/jso/plugin/JavascriptNativeProcessor.java @@ -81,7 +81,7 @@ class JavascriptNativeProcessor { if (isProperGetter(method.getDescriptor())) { String propertyName = method.getName().charAt(0) == 'i' ? cutPrefix(method.getName(), 2) : cutPrefix(method.getName(), 3); - Variable result = invoke.getReceiver() != null ? program.createVariable() : null; + Variable result = invoke.getReceiver() != null ? program.createVariable(null) : null; addPropertyGet(propertyName, invoke.getInstance(), result); if (result != null) { result = unwrap(result, method.getResultType()); @@ -96,7 +96,7 @@ class JavascriptNativeProcessor { } } else if (method.getAnnotations().get(JSIndexer.class.getName()) != null) { if (isProperGetIndexer(method.getDescriptor())) { - Variable result = invoke.getReceiver() != null ? program.createVariable() : null; + Variable result = invoke.getReceiver() != null ? program.createVariable(null) : null; addIndexerGet(invoke.getInstance(), wrap(invoke.getArguments().get(0), method.parameterType(0)), result); if (result != null) { @@ -143,7 +143,7 @@ class JavascriptNativeProcessor { "a proper native JavaScript method or constructor declaration"); } } - Variable result = invoke.getReceiver() != null ? program.createVariable() : null; + Variable result = invoke.getReceiver() != null ? program.createVariable(null) : null; InvokeInstruction newInvoke = new InvokeInstruction(); ValueType[] signature = new ValueType[method.parameterCount() + 3]; Arrays.fill(signature, ValueType.object(JSObject.class.getName())); @@ -226,7 +226,7 @@ class JavascriptNativeProcessor { } private Variable addString(String str) { - Variable var = program.createVariable(); + Variable var = program.createVariable(null); StringConstantInstruction nameInsn = new StringConstantInstruction(); nameInsn.setReceiver(var); nameInsn.setConstant(str); @@ -261,7 +261,7 @@ class JavascriptNativeProcessor { } else if (className.equals("java.lang.String")) { return unwrap(var, "unwrapString", ValueType.object("java.lang.String")); } else { - Variable result = program.createVariable(); + Variable result = program.createVariable(null); CastInstruction castInsn = new CastInstruction(); castInsn.setReceiver(result); castInsn.setValue(var); @@ -274,7 +274,7 @@ class JavascriptNativeProcessor { } private Variable unwrap(Variable var, String methodName, ValueType resultType) { - Variable result = program.createVariable(); + Variable result = program.createVariable(null); InvokeInstruction insn = new InvokeInstruction(); insn.setMethod(new MethodReference(JS.class.getName(), methodName, ValueType.object(JSObject.class.getName()), resultType)); @@ -301,7 +301,7 @@ class JavascriptNativeProcessor { throw new RuntimeException("Wrong functor: " + type.getName()); } String name = type.getMethods().iterator().next().getName(); - Variable functor = program.createVariable(); + Variable functor = program.createVariable(null); Variable nameVar = addStringWrap(addString(name)); InvokeInstruction insn = new InvokeInstruction(); insn.setMethod(new MethodReference(JS.class.getName(), "function", @@ -321,7 +321,7 @@ class JavascriptNativeProcessor { return var; } } - Variable result = program.createVariable(); + Variable result = program.createVariable(null); InvokeInstruction insn = new InvokeInstruction(); insn.setMethod(new MethodReference(JS.class.getName(), "wrap", type, ValueType.object(JSObject.class.getName()))); diff --git a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java index 6d71fb628..42070e4a8 100644 --- a/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java +++ b/teavm-platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java @@ -131,7 +131,7 @@ class ResourceProgramTransformer { } else if (type instanceof ValueType.Object) { switch (((ValueType.Object)type).getClassName()) { case "java.lang.String": { - Variable resultVar = insn.getProgram().createVariable(); + Variable resultVar = insn.getProgram().createVariable(null); getProperty(insn, property, instructions, resultVar); InvokeInstruction castInvoke = new InvokeInstruction(); castInvoke.setType(InvocationType.SPECIAL); @@ -143,7 +143,7 @@ class ResourceProgramTransformer { return instructions; } default: { - Variable resultVar = insn.getProgram().createVariable(); + Variable resultVar = insn.getProgram().createVariable(null); getProperty(insn, property, instructions, resultVar); CastInstruction castInsn = new CastInstruction(); castInsn.setReceiver(insn.getReceiver()); @@ -159,7 +159,7 @@ class ResourceProgramTransformer { private void getProperty(InvokeInstruction insn, String property, List instructions, Variable resultVar) { - Variable nameVar = program.createVariable(); + Variable nameVar = program.createVariable(null); StringConstantInstruction nameInsn = new StringConstantInstruction(); nameInsn.setConstant(property); nameInsn.setReceiver(nameVar); @@ -176,7 +176,7 @@ class ResourceProgramTransformer { private void getAndCastProperty(InvokeInstruction insn, String property, List instructions, Class primitive) { - Variable resultVar = program.createVariable(); + Variable resultVar = program.createVariable(null); getProperty(insn, property, instructions, resultVar); InvokeInstruction castInvoke = new InvokeInstruction(); castInvoke.setType(InvocationType.SPECIAL); @@ -220,7 +220,7 @@ class ResourceProgramTransformer { } else if (type instanceof ValueType.Object) { switch (((ValueType.Object)type).getClassName()) { case "java.lang.String": { - Variable castVar = insn.getProgram().createVariable(); + Variable castVar = insn.getProgram().createVariable(null); InvokeInstruction castInvoke = new InvokeInstruction(); castInvoke.setType(InvocationType.SPECIAL); castInvoke.setMethod(new MethodReference(ResourceAccessor.class, "castFromString", @@ -242,7 +242,7 @@ class ResourceProgramTransformer { private void setProperty(InvokeInstruction insn, String property, List instructions, Variable valueVar) { - Variable nameVar = program.createVariable(); + Variable nameVar = program.createVariable(null); StringConstantInstruction nameInsn = new StringConstantInstruction(); nameInsn.setConstant(property); nameInsn.setReceiver(nameVar); @@ -259,7 +259,7 @@ class ResourceProgramTransformer { private void castAndSetProperty(InvokeInstruction insn, String property, List instructions, Class primitive) { - Variable castVar = program.createVariable(); + Variable castVar = program.createVariable(null); InvokeInstruction castInvoke = new InvokeInstruction(); castInvoke.setType(InvocationType.SPECIAL); String primitiveCapitalized = primitive.getName();