Multiple small improvements

This commit is contained in:
konsoletyper 2014-08-07 14:12:08 +04:00
parent 1b374c7466
commit 569d6fa3e8
14 changed files with 287 additions and 62 deletions

View File

@ -62,30 +62,45 @@ 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("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()));
break;
case "Debugger.resumed":
fireResumed();
break;
case "Debugger.scriptParsed":
scriptParsed(parseJson(ScriptParsedNotification.class, message.getParams()));
break;
}
}
receiveMessage(messageText);
}
};
private void receiveMessage(final String messageText) {
new Thread() {
@Override public void run() {
try {
JsonNode jsonMessage = mapper.readTree(messageText);
if (jsonMessage.has("id")) {
Response response = mapper.reader(Response.class).readValue(jsonMessage);
if (response.getError() != null) {
System.err.println(response.getError().toString());
}
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()));
break;
case "Debugger.resumed":
fireResumed();
break;
case "Debugger.scriptParsed":
scriptParsed(parseJson(ScriptParsedNotification.class, message.getParams()));
break;
}
}
} catch (Exception e) {
// TODO: logging
e.printStackTrace();
}
}
}.start();
}
private synchronized void firePaused(SuspendedNotification params) {
suspended = true;
@ -256,8 +271,12 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
message.setParams(mapper.valueToTree(params));
ResponseHandler handler = new ResponseHandler() {
@Override public void received(JsonNode node) throws IOException {
SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class).readValue(node);
breakpoint.chromeId = response.getBreakpointId();
if (node != null) {
SetBreakpointResponse response = mapper.reader(SetBreakpointResponse.class).readValue(node);
breakpoint.chromeId = response.getBreakpointId();
} else {
breakpoint.chromeId = null;
}
for (JavaScriptDebuggerListener listener : getListeners()) {
listener.breakpointChanged(breakpoint);
}
@ -314,7 +333,8 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
sync.add("");
} else {
CallFunctionResponse response = mapper.reader(CallFunctionResponse.class).readValue(node);
sync.add(response.getResult().getValue().getTextValue());
RemoteObjectDTO result = response.getResult();
sync.add(result.getValue() != null ? result.getValue().getTextValue() : "");
}
}
});
@ -327,6 +347,51 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
}
}
String getRepresentation(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_repr");
message.setParams(mapper.valueToTree(params));
final BlockingQueue<RepresentationWrapper> sync = new LinkedTransferQueue<>();
responseHandlers.put(message.getId(), new ResponseHandler() {
@Override public void received(JsonNode node) throws IOException {
if (node == null) {
sync.add(new RepresentationWrapper(null));
} else {
CallFunctionResponse response = mapper.reader(CallFunctionResponse.class).readValue(node);
RemoteObjectDTO result = response.getResult();
sync.add(new RepresentationWrapper(result.getValue() != null ?
result.getValue().getTextValue() : null));
}
}
});
sendMessage(message);
try {
RepresentationWrapper result = sync.take();
return result.repr;
} catch (InterruptedException e) {
return null;
}
}
private static class RepresentationWrapper {
String repr;
public RepresentationWrapper(String repr) {
super();
this.repr = repr;
}
}
private List<RDPLocalVariable> parseProperties(PropertyDescriptorDTO[] properties) {
List<RDPLocalVariable> variables = new ArrayList<>();
if (properties != null) {
@ -339,8 +404,7 @@ public class ChromeRDPDebugger implements JavaScriptDebugger, ChromeRDPExchangeC
break;
case "object":
case "function":
value = new RDPValue(this, remoteValue.getDescription(), remoteValue.getType(),
remoteValue.getObjectId());
value = new RDPValue(this, null, remoteValue.getType(), remoteValue.getObjectId());
break;
default:
value = new RDPValue(this, remoteValue.getValue().asText(), remoteValue.getType(),

View File

@ -2,6 +2,7 @@ package org.teavm.chromerdp;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import org.teavm.debugging.JavaScriptValue;
import org.teavm.debugging.JavaScriptVariable;
@ -10,14 +11,15 @@ import org.teavm.debugging.JavaScriptVariable;
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class RDPValue implements JavaScriptValue {
private String representation;
private AtomicReference<String> representation = new AtomicReference<>();
private AtomicReference<String> className = new AtomicReference<>();
private String typeName;
private ChromeRDPDebugger debugger;
private String objectId;
private Map<String, ? extends JavaScriptVariable> properties;
public RDPValue(ChromeRDPDebugger debugger, String representation, String typeName, String objectId) {
this.representation = representation;
this.representation.set(representation == null && objectId == null ? "" : representation);
this.typeName = typeName;
this.debugger = debugger;
this.objectId = objectId;
@ -27,17 +29,23 @@ public class RDPValue implements JavaScriptValue {
@Override
public String getRepresentation() {
return representation;
if (representation.get() == null) {
representation.compareAndSet(null, debugger.getRepresentation(objectId));
}
return representation.get();
}
@Override
public String getClassName() {
if (objectId != null) {
String className = debugger.getClassName(objectId);
return className != null ? className : "object";
} else {
return typeName;
if (className.get() == null) {
if (objectId != null) {
String computedClassName = debugger.getClassName(objectId);
className.compareAndSet(null, computedClassName != null ? computedClassName : "@Object");
} else {
className.compareAndSet(null, "@" + typeName);
}
}
return className.get();
}
@SuppressWarnings("unchecked")

View File

@ -26,6 +26,7 @@ import org.codehaus.jackson.annotate.JsonIgnoreProperties;
public class Response {
private int id;
private JsonNode result;
private JsonNode error;
public int getId() {
return id;
@ -42,4 +43,12 @@ public class Response {
public void setResult(JsonNode result) {
this.result = result;
}
public JsonNode getError() {
return error;
}
public void setError(JsonNode error) {
this.error = error;
}
}

View File

@ -33,6 +33,7 @@ public class JCLPlugin implements TeaVMPlugin {
host.add(new EnumTransformer());
host.add(new ClassLookupDependencySupport());
host.add(new NewInstanceDependencySupport());
host.add(new ObjectEnrichRenderer());
ServiceLoaderSupport serviceLoaderSupp = new ServiceLoaderSupport(host.getClassLoader());
host.add(serviceLoaderSupp);
MethodReference loadServicesMethod = new MethodReference("java.util.ServiceLoader", new MethodDescriptor(

View File

@ -0,0 +1,62 @@
/*
* 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.classlib.impl;
import java.io.IOException;
import org.teavm.javascript.RenderingContext;
import org.teavm.model.ClassReader;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReader;
import org.teavm.vm.BuildTarget;
import org.teavm.vm.spi.RendererListener;
/**
*
* @author Alexey Andreev
*/
public class ObjectEnrichRenderer implements RendererListener {
private RenderingContext context;
@Override
public void begin(RenderingContext context, BuildTarget buildTarget) throws IOException {
this.context = context;
}
@Override
public void beforeClass(ClassReader cls) throws IOException {
}
@Override
public void afterClass(ClassReader cls) throws IOException {
if (cls.getName().equals("java.lang.Object")) {
MethodReader toString = cls.getMethod(new MethodDescriptor("toString", String.class));
if (toString != null) {
String clsName = context.getNaming().getNameFor(cls.getName());
String toStringName = context.getNaming().getNameFor(toString.getReference());
context.getWriter().append(clsName).append(".prototype.toString").ws().append('=').ws()
.append("function()").ws().append('{').indent().softNewLine();
context.getWriter().append("return this.").append(toStringName).ws().append('?').ws()
.append("$rt_ustr(this.").append(toStringName).append("())").ws().append(':')
.append("Object.prototype.toString.call(this);").softNewLine();
context.getWriter().outdent().append("}").newLine();
}
}
}
@Override
public void complete() throws IOException {
}
}

View File

@ -45,7 +45,7 @@ public class DebugInformation {
Mapping methodMapping;
Mapping lineMapping;
MultiMapping[] variableMappings;
List<Map<Integer, Integer>> classesMetadata;
List<ClassMetadata> classesMetadata;
public String[] getCoveredSourceFiles() {
return fileNames.clone();
@ -130,8 +130,15 @@ public class DebugInformation {
if (jsIndex == null) {
return null;
}
Integer fieldIndex = classesMetadata.get(classIndex).get(jsIndex);
return fieldIndex != null ? fields[fieldIndex] : null;
while (classIndex != null) {
ClassMetadata cls = classesMetadata.get(classIndex);
Integer fieldIndex = cls.fieldMap.get(jsIndex);
if (fieldIndex != null) {
return fields[fieldIndex];
}
classIndex = cls.parentId;
}
return null;
}
private <T> T componentByKey(Mapping mapping, T[] values, GeneratedLocation location) {
@ -372,4 +379,9 @@ public class DebugInformation {
return lines.length;
}
}
static class ClassMetadata {
Integer parentId;
Map<Integer, Integer> fieldMap = new HashMap<>();
}
}

View File

@ -104,12 +104,14 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
}
@Override
public void addClass(String className) {
public void addClass(String className, String parentName) {
int classIndex = classes.index(className);
int parentIndex = classes.index(parentName);
while (classIndex >= classesMetadata.size()) {
classesMetadata.add(new ClassMetadata());
}
currentClassMetadata = classIndex;
classesMetadata.get(currentClassMetadata).parentIndex = parentIndex;
}
@Override
@ -140,13 +142,16 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
debugInformation.variableMappings[var] = mapping.build();
}
List<Map<Integer, Integer>> builtMetadata = new ArrayList<>(classes.list.size());
List<DebugInformation.ClassMetadata> builtMetadata = new ArrayList<>(classes.list.size());
for (int i = 0; i < classes.list.size(); ++i) {
if (i >= classesMetadata.size()) {
builtMetadata.add(new HashMap<Integer, Integer>());
builtMetadata.add(new DebugInformation.ClassMetadata());
} else {
Map<Integer, Integer> map = new HashMap<>(classesMetadata.get(i).fieldMap);
builtMetadata.add(map);
ClassMetadata origMetadata = classesMetadata.get(i);
DebugInformation.ClassMetadata mappedMetadata = new DebugInformation.ClassMetadata();
mappedMetadata.fieldMap.putAll(origMetadata.fieldMap);
mappedMetadata.parentId = origMetadata.parentIndex >= 0 ? origMetadata.parentIndex : null;
builtMetadata.add(mappedMetadata);
}
}
debugInformation.classesMetadata = builtMetadata;
@ -285,6 +290,7 @@ public class DebugInformationBuilder implements DebugInformationEmitter {
}
static class ClassMetadata {
int parentIndex;
Map<Integer, Integer> fieldMap = new HashMap<>();
}
}

View File

@ -33,7 +33,7 @@ public interface DebugInformationEmitter {
void emitVariable(String[] sourceNames, String generatedName);
void addClass(String className);
void addClass(String className, String parentName);
void addField(String fieldName, String jsName);
}

View File

@ -19,9 +19,7 @@ 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;
/**
*
@ -64,17 +62,21 @@ class DebugInformationReader {
return mappings;
}
private List<Map<Integer, Integer>> readClassesMetadata(int count) throws IOException {
List<Map<Integer, Integer>> classes = new ArrayList<>(count);
private List<DebugInformation.ClassMetadata> readClassesMetadata(int count) throws IOException {
List<DebugInformation.ClassMetadata> classes = new ArrayList<>(count);
for (int i = 0; i < count; ++i) {
Map<Integer, Integer> cls = new HashMap<>();
DebugInformation.ClassMetadata cls = new DebugInformation.ClassMetadata();
classes.add(cls);
cls.parentId = readUnsignedNumber() - 1;
if (cls.parentId.equals(-1)) {
cls.parentId = null;
}
int entryCount = readUnsignedNumber();
resetRelativeNumber();
for (int j = 0; j < entryCount; ++j) {
int key = readRelativeNumber();
int value = readUnsignedNumber();
cls.put(key, value);
cls.fieldMap.put(key, value);
}
}
return classes;

View File

@ -20,7 +20,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.teavm.debugging.DebugInformation.ClassMetadata;
/**
*
@ -63,16 +63,17 @@ class DebugInformationWriter {
}
}
private void writeClassMetadata(List<Map<Integer, Integer>> classes) throws IOException {
private void writeClassMetadata(List<ClassMetadata> classes) throws IOException {
for (int i = 0; i < classes.size(); ++i) {
Map<Integer, Integer> cls = classes.get(i);
writeUnsignedNumber(cls.size());
List<Integer> keys = new ArrayList<>(cls.keySet());
ClassMetadata cls = classes.get(i);
writeUnsignedNumber(cls.parentId != null ? cls.parentId + 1 : 0);
writeUnsignedNumber(cls.fieldMap.size());
List<Integer> keys = new ArrayList<>(cls.fieldMap.keySet());
Collections.sort(keys);
resetRelativeNumber();
for (int key : keys) {
writeRelativeNumber(key);
writeUnsignedNumber(cls.get(key));
writeUnsignedNumber(cls.fieldMap.get(key));
}
}
}

View File

@ -26,8 +26,6 @@ import org.teavm.model.MethodReference;
*
* @author Alexey Andreev
*/
// TODO: variable name handling
// TODO: class fields handling
public class Debugger {
private static final Object dummyObject = new Object();
private ConcurrentMap<DebuggerListener, Object> listeners = new ConcurrentHashMap<>();
@ -198,13 +196,16 @@ public class Debugger {
private void addScript(String name) {
if (debugInformationMap.containsKey(name)) {
updateBreakpoints();
return;
}
DebugInformation debugInfo = debugInformationProvider.getDebugInformation(name);
if (debugInfo == null) {
updateBreakpoints();
return;
}
if (debugInformationMap.putIfAbsent(name, debugInfo) != null) {
updateBreakpoints();
return;
}
for (String sourceFile : debugInfo.getCoveredSourceFiles()) {
@ -220,6 +221,10 @@ public class Debugger {
list.put(debugInfo, dummyObject);
}
scriptMap.put(debugInfo, name);
updateBreakpoints();
}
private void updateBreakpoints() {
for (Breakpoint breakpoint : breakpoints.keySet()) {
updateInternalBreakpoints(breakpoint);
updateBreakpointStatus(breakpoint, true);

View File

@ -45,7 +45,7 @@ public class DummyDebugInformationEmitter implements DebugInformationEmitter {
}
@Override
public void addClass(String className) {
public void addClass(String className, String parentName) {
}
@Override

View File

@ -243,7 +243,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
public void render(ClassNode cls) throws RenderingException {
debugEmitter.emitClass(cls.getName());
debugEmitter.addClass(cls.getName());
debugEmitter.addClass(cls.getName(), cls.getParentName());
try {
writer.append("function ").appendClass(cls.getName()).append("()").ws().append("{")
.indent().softNewLine();

View File

@ -108,6 +108,17 @@ $rt_arraycls = function(cls) {
};
arraycls.prototype = new ($rt_objcls())();
arraycls.prototype.constructor = arraycls;
arraycls.prototype.toString = function() {
var str = "[";
for (var i = 0; i < this.data.length; ++i) {
if (i > 0) {
str += ", ";
}
str += this.data[i].toString();
}
str += "]";
return str;
}
arraycls.$meta = { item : cls, supertypes : [$rt_objcls()], primitive : false, superclass : $rt_objcls() };
cls.$array = arraycls;
}
@ -392,20 +403,64 @@ function $rt_s(index) {
}
function $dbg_repr(obj) {
return obj.toString();
return obj.toString ? obj.toString() : "";
}
function $dbg_class(obj) {
if (obj instanceof Long) {
return "long";
}
var meta = obj.constructor.$meta;
return meta ? meta.name : "";
var cls = obj.constructor;
var arrayDegree = 0;
while (cls.$meta && cls.$meta.item) {
++arrayDegree;
cls = cls.$meta.item;
}
var clsName = "";
if (cls === $rt_booleancls()) {
clsName = "boolean";
} else if (cls === $rt_bytecls()) {
clsName = "byte";
} else if (cls === $rt_shortcls()) {
clsName = "short";
} else if (cls === $rt_charcls()) {
clsName = "char";
} else if (cls === $rt_intcls()) {
clsName = "int";
} else if (cls === $rt_longcls()) {
clsName = "long";
} else if (cls === $rt_floatcls()) {
clsName = "float";
} else if (cls === $rt_doublecls()) {
clsName = "double";
} else {
clsName = cls.$meta ? cls.$meta.name : "@" + cls.name;
}
while (arrayDegree-- > 0) {
clsName += "[]";
}
return clsName;
}
Long = function(lo, hi) {
this.lo = lo | 0;
this.hi = hi | 0;
}
Long.prototype.toString = function() {
var result = [];
var n = this;
var positive = Long_isPositive(n);
if (!positive) {
n = Long_neg(n);
}
var radix = new Long(10, 0);
do {
var divRem = Long_divRem(n, radix);
result.push(String.fromCharCode(48 + divRem[1].lo));
n = divRem[0];
} while (n.lo != 0 || n.hi != 0);
result = result.reverse().join('');
return positive ? result : "-" + result;
}
Long_ZERO = new Long(0, 0);
Long_fromInt = function(val) {
return val >= 0 ? new Long(val, 0) : new Long(val, -1);