Fixes bugs with variable mapping

This commit is contained in:
konsoletyper 2014-08-05 18:08:13 +04:00
parent bc911a661a
commit cbe2ccb499
17 changed files with 290 additions and 68 deletions

View File

@ -4,9 +4,7 @@ import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Exchanger;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import org.codehaus.jackson.JsonNode; import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper; import org.codehaus.jackson.map.ObjectMapper;
@ -277,26 +275,47 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
params.setObjectId(scopeId); params.setObjectId(scopeId);
params.setOwnProperties(true); params.setOwnProperties(true);
message.setParams(mapper.valueToTree(params)); message.setParams(mapper.valueToTree(params));
final Exchanger<List<RDPLocalVariable>> exchanger = new Exchanger<>(); final BlockingQueue<List<RDPLocalVariable>> sync = new LinkedTransferQueue<>();
responseHandlers.put(message.getId(), new ResponseHandler() { responseHandlers.put(message.getId(), new ResponseHandler() {
@Override public void received(JsonNode node) throws IOException { @Override public void received(JsonNode node) throws IOException {
GetPropertiesResponse response = mapper.reader(GetPropertiesResponse.class).readValue(node); GetPropertiesResponse response = mapper.reader(GetPropertiesResponse.class).readValue(node);
// TODO: parse response sync.add(parseProperties(response.getResult()));
try {
exchanger.exchange(new ArrayList<RDPLocalVariable>());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
} }
}); });
sendMessage(message); sendMessage(message);
try { try {
return exchanger.exchange(null); return sync.take();
} catch (InterruptedException e) { } catch (InterruptedException e) {
return Collections.emptyList(); return Collections.emptyList();
} }
} }
private List<RDPLocalVariable> parseProperties(PropertyDescriptorDTO[] properties) {
List<RDPLocalVariable> variables = new ArrayList<>();
if (properties != null) {
for (PropertyDescriptorDTO property : properties) {
RemoteObjectDTO remoteValue = property.getValue();
RDPValue value;
switch (remoteValue.getType()) {
case "undefined":
value = new RDPValue("undefined");
break;
case "object":
case "function":
value = new RDPValue(remoteValue.getObjectId());
break;
default:
value = new RDPValue(remoteValue.getValue().asText());
break;
}
RDPLocalVariable var = new RDPLocalVariable(property.getName(), value);
variables.add(var);
}
}
return variables;
}
private <T> T parseJson(Class<T> type, JsonNode node) throws IOException { private <T> T parseJson(Class<T> type, JsonNode node) throws IOException {
return mapper.reader(type).readValue(node); return mapper.reader(type).readValue(node);
} }
@ -316,7 +335,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
String scopeId = null; String scopeId = null;
for (ScopeDTO scope : dto.getScopeChain()) { for (ScopeDTO scope : dto.getScopeChain()) {
if (scope.getType().equals("local")) { if (scope.getType().equals("local")) {
scopeId = scope.getObject(); scopeId = scope.getObject().getObjectId();
break; break;
} }
} }

View File

@ -8,14 +8,14 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class ScopeDTO { public class ScopeDTO {
private String object; private RemoteObjectDTO object;
private String type; private String type;
public String getObject() { public RemoteObjectDTO getObject() {
return object; return object;
} }
public void setObject(String object) { public void setObject(RemoteObjectDTO object) {
this.object = object; this.object = object;
} }

View File

@ -9,13 +9,13 @@ import org.teavm.chromerdp.data.PropertyDescriptorDTO;
*/ */
@JsonIgnoreProperties(ignoreUnknown = true) @JsonIgnoreProperties(ignoreUnknown = true)
public class GetPropertiesResponse { public class GetPropertiesResponse {
private PropertyDescriptorDTO[] properties; private PropertyDescriptorDTO[] result;
public PropertyDescriptorDTO[] getProperties() { public PropertyDescriptorDTO[] getResult() {
return properties; return result;
} }
public void setProperties(PropertyDescriptorDTO[] properties) { public void setResult(PropertyDescriptorDTO[] properties) {
this.properties = properties; this.result = properties;
} }
} }

View File

@ -26,9 +26,9 @@ import org.teavm.model.MethodReference;
public class CallFrame { public class CallFrame {
private SourceLocation location; private SourceLocation location;
private MethodReference method; private MethodReference method;
private Map<String, LocalVariable> variables; private Map<String, Variable> variables;
CallFrame(SourceLocation location, MethodReference method, Map<String, LocalVariable> variables) { CallFrame(SourceLocation location, MethodReference method, Map<String, Variable> variables) {
this.location = location; this.location = location;
this.method = method; this.method = method;
this.variables = Collections.unmodifiableMap(variables); this.variables = Collections.unmodifiableMap(variables);
@ -42,7 +42,7 @@ public class CallFrame {
return method; return method;
} }
public Map<String, LocalVariable> getVariables() { public Map<String, Variable> getVariables() {
return variables; return variables;
} }
} }

View File

@ -50,13 +50,11 @@ class DebugInformationReader {
private DebugInformation.Mapping[] readVariableMappings(int count) throws IOException { private DebugInformation.Mapping[] readVariableMappings(int count) throws IOException {
DebugInformation.Mapping[] mappings = new DebugInformation.Mapping[count]; DebugInformation.Mapping[] mappings = new DebugInformation.Mapping[count];
int varCount = readUnsignedNumber(); int varCount = readUnsignedNumber();
while (varCount-- > 0) {
int lastVar = 0; int lastVar = 0;
for (int i = 0; i < mappings.length; ++i) { while (varCount-- > 0) {
lastVar += readUnsignedNumber(); lastVar += readUnsignedNumber();
mappings[lastVar] = readMapping(); mappings[lastVar] = readMapping();
} }
}
return mappings; return mappings;
} }

View File

@ -40,12 +40,12 @@ class DebugInformationWriter {
writeMapping(debugInfo.lineMapping); writeMapping(debugInfo.lineMapping);
writeMapping(debugInfo.classMapping); writeMapping(debugInfo.classMapping);
writeMapping(debugInfo.methodMapping); writeMapping(debugInfo.methodMapping);
writeUnsignedNumber(nonNullVariableMappings(debugInfo));
writeVariableMappings(debugInfo); writeVariableMappings(debugInfo);
} }
private void writeVariableMappings(DebugInformation debugInfo) throws IOException { private void writeVariableMappings(DebugInformation debugInfo) throws IOException {
int lastVar = 0; int lastVar = 0;
writeUnsignedNumber(nonNullVariableMappings(debugInfo));
for (int i = 0; i < debugInfo.variableMappings.length; ++i) { for (int i = 0; i < debugInfo.variableMappings.length; ++i) {
DebugInformation.Mapping mapping = debugInfo.variableMappings[i]; DebugInformation.Mapping mapping = debugInfo.variableMappings[i];
if (mapping == null) { if (mapping == null) {

View File

@ -186,7 +186,8 @@ public class Debugger {
MethodReference method = !empty ? debugInformation.getMethodAt(jsFrame.getLocation().getLine(), MethodReference method = !empty ? debugInformation.getMethodAt(jsFrame.getLocation().getLine(),
jsFrame.getLocation().getColumn()) : null; jsFrame.getLocation().getColumn()) : null;
if (!empty || !wasEmpty) { if (!empty || !wasEmpty) {
frames.add(new CallFrame(loc, method, new HashMap<String, LocalVariable>())); VariableMap vars = new VariableMap(jsFrame.getVariables(), this, jsFrame.getLocation());
frames.add(new CallFrame(loc, method, vars));
} }
wasEmpty = empty; wasEmpty = empty;
} }
@ -279,6 +280,14 @@ public class Debugger {
} }
} }
public String mapVariable(String variable, JavaScriptLocation location) {
DebugInformation debugInfo = debugInformationMap.get(location.getScript());
if (debugInfo == null) {
return null;
}
return debugInfo.getVariableMeaningAt(location.getLine(), location.getColumn(), variable);
}
private JavaScriptDebuggerListener javaScriptListener = new JavaScriptDebuggerListener() { private JavaScriptDebuggerListener javaScriptListener = new JavaScriptDebuggerListener() {
@Override @Override
public void resumed() { public void resumed() {

View File

@ -1,3 +1,18 @@
/*
* 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; package org.teavm.debugging;
import java.util.Map; import java.util.Map;

View File

@ -1,3 +1,18 @@
/*
* 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; package org.teavm.debugging;
/** /**

View File

@ -1,23 +0,0 @@
package org.teavm.debugging;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
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;
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.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<String, Variable> getProperties() {
return Collections.emptyMap();
}
}

View File

@ -0,0 +1,28 @@
/*
* 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.Map;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public abstract class Value {
public abstract String getRepresentation();
public abstract Map<String, Variable> getProperties();
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2014 Alexey Andreev.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.teavm.debugging;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class Variable {
private String name;
private Value value;
Variable(String name, Value value) {
this.name = name;
this.value = value;
}
public String getName() {
return name;
}
public Value getValue() {
return value;
}
}

View File

@ -0,0 +1,74 @@
/*
* 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.AbstractMap;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;
/**
*
* @author Alexey Andreev
*/
class VariableMap extends AbstractMap<String, Variable> {
private AtomicReference<Map<String, Variable>> backingMap = new AtomicReference<>();
private Map<String, JavaScriptVariable> jsVariables;
private Debugger debugger;
private JavaScriptLocation location;
public VariableMap(Map<String, JavaScriptVariable> jsVariables, Debugger debugger, JavaScriptLocation location) {
this.jsVariables = jsVariables;
this.debugger = debugger;
this.location = location;
}
@Override
public Set<Entry<String, Variable>> entrySet() {
updateBackingMap();
return backingMap.get().entrySet();
}
@Override
public Variable get(Object key) {
updateBackingMap();
return backingMap.get().get(key);
}
@Override
public int size() {
updateBackingMap();
return backingMap.get().size();
}
private void updateBackingMap() {
if (backingMap.get() != null) {
return;
}
Map<String, Variable> vars = new HashMap<>();
for (Map.Entry<String, JavaScriptVariable> entry : jsVariables.entrySet()) {
JavaScriptVariable jsVar = entry.getValue();
String name = debugger.mapVariable(entry.getKey(), location);
if (name == null) {
continue;
}
SimpleValue value = new SimpleValue(jsVar.getValue().getRepresentation());
vars.put(entry.getKey(), new Variable(name, value));
}
backingMap.compareAndSet(null, vars);
}
}

View File

@ -1,13 +0,0 @@
package org.teavm.debugging;
import java.util.Map;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public abstract class WatchedValue {
public abstract String getRepresentation();
public abstract Map<String, LocalVariable> getProperties();
}

View File

@ -119,13 +119,16 @@ public class ProgramParser implements VariableDebugInformation {
while (program.variableCount() <= signatureVars) { while (program.variableCount() <= signatureVars) {
program.createVariable(getVariableDebugName(program.variableCount(), 0)); program.createVariable(getVariableDebugName(program.variableCount(), 0));
} }
for (int i = 0; i < signatureVars; ++i) { for (int i = 0; i <= signatureVars; ++i) {
parameterNames.put(i, getVariableDebugName(i, 0)); parameterNames.put(i, getVariableDebugName(i, 0));
} }
return program; return program;
} }
private String getVariableDebugName(int var, int location) { private String getVariableDebugName(int var, int location) {
if (var < 0) {
return null;
}
List<LocalVariableNode> nodes = localVariableMap.get(var); List<LocalVariableNode> nodes = localVariableMap.get(var);
if (nodes == null) { if (nodes == null) {
return null; return null;
@ -293,10 +296,13 @@ public class ProgramParser implements VariableDebugInformation {
DefinitionExtractor defExtractor = new DefinitionExtractor(); DefinitionExtractor defExtractor = new DefinitionExtractor();
for (int i = 0; i < targetInstructions.size(); ++i) { for (int i = 0; i < targetInstructions.size(); ++i) {
List<Instruction> instructionList = targetInstructions.get(i); List<Instruction> instructionList = targetInstructions.get(i);
if (instructionList == null) {
continue;
}
for (Instruction insn : instructionList) { for (Instruction insn : instructionList) {
insn.acceptVisitor(defExtractor); insn.acceptVisitor(defExtractor);
for (Variable var : defExtractor.getDefinedVariables()) { for (Variable var : defExtractor.getDefinedVariables()) {
String debugName = getVariableDebugName(var.getIndex(), i); String debugName = getVariableDebugName(var.getIndex() - minLocal, i);
if (debugName != null) { if (debugName != null) {
variableDebugNames.put(insn, debugName); variableDebugNames.put(insn, debugName);
} }

View File

@ -1,3 +1,18 @@
/*
* 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.parsing; package org.teavm.parsing;
import org.teavm.model.Instruction; import org.teavm.model.Instruction;