Minor improvements and bugfixes

This commit is contained in:
Alexey Andreev 2015-01-20 18:10:13 +04:00
parent 3935d27f5b
commit 525fd89374
6 changed files with 140 additions and 117 deletions

View File

@ -16,7 +16,6 @@
package org.teavm.callgraph; package org.teavm.callgraph;
import java.util.Objects; import java.util.Objects;
import org.teavm.model.CallLocation;
import org.teavm.model.InstructionLocation; import org.teavm.model.InstructionLocation;
/** /**
@ -27,15 +26,11 @@ public class DefaultCallSite implements CallSite {
private InstructionLocation location; private InstructionLocation location;
private DefaultCallGraphNode callee; private DefaultCallGraphNode callee;
private DefaultCallGraphNode caller; private DefaultCallGraphNode caller;
private CallLocation exactLocation;
DefaultCallSite(InstructionLocation location, DefaultCallGraphNode callee, DefaultCallGraphNode caller) { DefaultCallSite(InstructionLocation location, DefaultCallGraphNode callee, DefaultCallGraphNode caller) {
this.location = location; this.location = location;
this.callee = callee; this.callee = callee;
this.caller = caller; this.caller = caller;
if (caller != null) {
exactLocation = new CallLocation(caller.getMethod(), location);
}
} }
@Override @Override
@ -43,10 +38,6 @@ public class DefaultCallSite implements CallSite {
return location; return location;
} }
public CallLocation getExactLocation() {
return exactLocation;
}
@Override @Override
public DefaultCallGraphNode getCallee() { public DefaultCallGraphNode getCallee() {
return callee; return callee;

View File

@ -18,6 +18,7 @@ package org.teavm.diagnostics;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.InstructionLocation; import org.teavm.model.InstructionLocation;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
/** /**
* *
@ -58,4 +59,9 @@ public class DefaultProblemTextConsumer implements ProblemTextConsumer {
public void appendLocation(InstructionLocation location) { public void appendLocation(InstructionLocation location) {
sb.append(location); sb.append(location);
} }
@Override
public void appendType(ValueType type) {
sb.append(type);
}
} }

View File

@ -16,10 +16,7 @@
package org.teavm.diagnostics; package org.teavm.diagnostics;
import java.util.Arrays; import java.util.Arrays;
import org.teavm.model.CallLocation; import org.teavm.model.*;
import org.teavm.model.FieldReference;
import org.teavm.model.InstructionLocation;
import org.teavm.model.MethodReference;
/** /**
* *
@ -81,6 +78,9 @@ public class Problem {
case 'c': case 'c':
type = ParamType.CLASS; type = ParamType.CLASS;
break; break;
case 't':
type = ParamType.TYPE;
break;
case 'm': case 'm':
type = ParamType.METHOD; type = ParamType.METHOD;
break; break;
@ -113,6 +113,12 @@ public class Problem {
} }
consumer.appendClass((String)param); consumer.appendClass((String)param);
break; break;
case TYPE:
if (!(param instanceof ValueType)) {
return index;
}
consumer.appendType((ValueType)param);
break;
case METHOD: case METHOD:
if (!(param instanceof MethodReference)) { if (!(param instanceof MethodReference)) {
return index; return index;
@ -145,6 +151,7 @@ public class Problem {
enum ParamType { enum ParamType {
CLASS, CLASS,
TYPE,
METHOD, METHOD,
FIELD, FIELD,
LOCATION LOCATION

View File

@ -18,6 +18,7 @@ package org.teavm.diagnostics;
import org.teavm.model.FieldReference; import org.teavm.model.FieldReference;
import org.teavm.model.InstructionLocation; import org.teavm.model.InstructionLocation;
import org.teavm.model.MethodReference; import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
/** /**
* *
@ -28,6 +29,8 @@ public interface ProblemTextConsumer {
void appendClass(String className); void appendClass(String className);
void appendType(ValueType type);
void appendMethod(MethodReference method); void appendMethod(MethodReference method);
void appendField(FieldReference field); void appendField(FieldReference field);

View File

@ -65,10 +65,6 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder {
SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, TICKS_PER_PROFILE); SubProgressMonitor subMonitor = new SubProgressMonitor(monitor, TICKS_PER_PROFILE);
buildProfile(kind, subMonitor, profile, classLoader); buildProfile(kind, subMonitor, profile, classLoader);
} }
IMarker[] markers = getProject().findMarkers(TeaVMEclipsePlugin.PROBLEM_MARKER_ID, true, IResource.DEPTH_INFINITE);
for (IMarker marker : markers) {
System.out.println("MARKER INSTALLED: " + marker.getId());
}
} finally { } finally {
monitor.done(); monitor.done();
sourceContainers = null; sourceContainers = null;
@ -131,7 +127,8 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder {
monitor.beginTask("Running TeaVM", 10000); monitor.beginTask("Running TeaVM", 10000);
tool.generate(); tool.generate();
if (!tool.wasCancelled()) { if (!tool.wasCancelled()) {
putMarkers(tool.getDependencyInfo().getCallGraph(), tool.getProblemProvider().getProblems()); putMarkers(tool.getDependencyInfo().getCallGraph(), tool.getProblemProvider().getProblems(),
profile);
setClasses(profile, classesToResources(tool.getClasses())); setClasses(profile, classesToResources(tool.getClasses()));
refreshTarget(tool.getTargetDirectory()); refreshTarget(tool.getTargetDirectory());
} }
@ -235,80 +232,88 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder {
for (IMarker marker : markers) { for (IMarker marker : markers) {
String projectName = (String)marker.getAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROJECT_ATTRIBUTE); String projectName = (String)marker.getAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROJECT_ATTRIBUTE);
String profileName = (String)marker.getAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROFILE_ATTRIBUTE); String profileName = (String)marker.getAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROFILE_ATTRIBUTE);
if (projectName.equals(getProject().getName()) && profileName.equals(profile.getName())) { if (projectName.equals(getProject().getName()) && (profileName == null ||
profileName.equals(profile.getName()))) {
marker.delete(); marker.delete();
System.out.println("MARKER REMOVED: " + marker.getId() + " while building project " +
getProject().getName());
} }
} }
} }
getProject().deleteMarkers(TeaVMEclipsePlugin.CONFIG_MARKER_ID, true, IResource.DEPTH_INFINITE); getProject().deleteMarkers(TeaVMEclipsePlugin.CONFIG_MARKER_ID, true, IResource.DEPTH_INFINITE);
} }
private void putMarkers(CallGraph cg, List<Problem> problems) throws CoreException { private void putMarkers(CallGraph cg, List<Problem> problems, TeaVMProfile profile) throws CoreException {
for (Problem problem : problems) { for (Problem problem : problems) {
putMarker(cg, problem); putMarker(cg, problem, profile);
} }
} }
private void putMarker(CallGraph cg, Problem problem) throws CoreException { private void putMarker(CallGraph cg, Problem problem, TeaVMProfile profile) throws CoreException {
if (problem.getLocation() == null || problem.getLocation().getMethod() == null) { if (problem.getLocation() == null || problem.getLocation().getMethod() == null) {
putMarkerAtDefaultLocation(problem); putMarkerAtDefaultLocation(problem, profile);
return; return;
} }
CallGraphNode problemNode = cg.getNode(problem.getLocation().getMethod()); CallGraphNode problemNode = cg.getNode(problem.getLocation().getMethod());
if (problemNode == null) { if (problemNode == null) {
putMarkerAtDefaultLocation(problem); putMarkerAtDefaultLocation(problem, profile);
return; return;
} }
Set<CallSite> callSites = new HashSet<>();
collectCallSites(callSites, problemNode);
String messagePrefix = problemAsString(problem); String messagePrefix = problemAsString(problem);
boolean wasPut = false;
for (CallSite callSite : callSites) {
IResource resource = findResource(new CallLocation(callSite.getCaller().getMethod(),
callSite.getLocation()));
if (resource == null) {
continue;
}
wasPut = true;
CallGraphNode node = problemNode;
StringBuilder sb = new StringBuilder(messagePrefix);
while (node != callSite.getCaller()) {
sb.append(", used by ").append(getFullMethodName(node.getMethod()));
Iterator<? extends CallSite> callerCallSites = node.getCallerCallSites().iterator();
if (!callerCallSites.hasNext()) {
break;
}
node = callerCallSites.next().getCaller();
}
putMarker(resource, callSite.getLocation(), callSite.getCaller().getMethod(), sb.toString());
}
IResource resource = findResource(problem.getLocation()); IResource resource = findResource(problem.getLocation());
boolean wasPut = false;
if (resource != null) { if (resource != null) {
putMarker(resource, problem.getLocation().getSourceLocation(), problem.getLocation().getMethod(), wasPut |= putMarker(resource, problem.getLocation().getSourceLocation(), problem.getLocation().getMethod(),
messagePrefix); messagePrefix, profile, false);
wasPut = true;
} }
if (!wasPut) { if (!wasPut) {
putMarkerAtDefaultLocation(problem); wasPut |= putMarkersAtCallSites(problemNode, new HashSet<CallGraphNode>(), messagePrefix, profile,
false);
}
if (!wasPut) {
wasPut |= putMarkersAtCallSites(problemNode, new HashSet<CallGraphNode>(), messagePrefix, profile, true);
}
if (!wasPut) {
putMarkerAtDefaultLocation(problem, profile);
} }
} }
private void putMarker(IResource resource, InstructionLocation location, MethodReference method, private boolean putMarkersAtCallSites(CallGraphNode node, Set<CallGraphNode> visited, String problem,
String text) throws CoreException { TeaVMProfile profile, boolean force) throws CoreException {
IMarker marker = resource.createMarker(TeaVMEclipsePlugin.PROBLEM_MARKER_ID); if (!visited.add(node)) {
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); return false;
marker.setAttribute(IMarker.MESSAGE, text); }
marker.setAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROJECT_ATTRIBUTE, getProject().getName()); boolean wasPut = true;
for (CallSite callSite : node.getCallerCallSites()) {
IResource resource = findResource(new CallLocation(callSite.getCaller().getMethod(),
callSite.getLocation()));
if (resource != null) {
wasPut = putMarker(resource, callSite.getLocation(), callSite.getCaller().getMethod(), problem,
profile, force);
}
wasPut |= putMarkersAtCallSites(callSite.getCaller(), visited, problem + ", used by " +
getFullMethodName(callSite.getCaller().getMethod()), profile, force);
}
return wasPut;
}
private boolean putMarker(IResource resource, InstructionLocation location, MethodReference method,
String text, TeaVMProfile profile, boolean force) throws CoreException {
Integer lineNumber = location != null ? location.getLine() : null; Integer lineNumber = location != null ? location.getLine() : null;
if (lineNumber == null) { if (lineNumber == null) {
lineNumber = findMethodLocation(method, resource); lineNumber = findMethodLocation(method, resource);
} }
if (lineNumber == null) { if (lineNumber == null) {
if (!force) {
return false;
}
lineNumber = 1; lineNumber = 1;
} }
IMarker marker = resource.createMarker(TeaVMEclipsePlugin.PROBLEM_MARKER_ID);
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
marker.setAttribute(IMarker.MESSAGE, text);
marker.setAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROJECT_ATTRIBUTE, getProject().getName());
marker.setAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROFILE_ATTRIBUTE, profile.getName());
marker.setAttribute(IMarker.LINE_NUMBER, lineNumber); marker.setAttribute(IMarker.LINE_NUMBER, lineNumber);
return true;
} }
private IResource findResource(CallLocation location) { private IResource findResource(CallLocation location) {
@ -334,19 +339,12 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder {
return resource; return resource;
} }
private void collectCallSites(Set<CallSite> callSites, CallGraphNode node) { private void putMarkerAtDefaultLocation(Problem problem, TeaVMProfile profile) throws CoreException {
for (CallSite callSite : node.getCallerCallSites()) {
if (callSites.add(callSite)) {
collectCallSites(callSites, callSite.getCaller());
}
}
}
private void putMarkerAtDefaultLocation(Problem problem) throws CoreException {
IMarker marker = getProject().createMarker(TeaVMEclipsePlugin.PROBLEM_MARKER_ID); IMarker marker = getProject().createMarker(TeaVMEclipsePlugin.PROBLEM_MARKER_ID);
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR); marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
marker.setAttribute(IMarker.MESSAGE, problemAsString(problem)); marker.setAttribute(IMarker.MESSAGE, problemAsString(problem));
marker.setAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROJECT_ATTRIBUTE, getProject().getName()); marker.setAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROJECT_ATTRIBUTE, getProject().getName());
marker.setAttribute(TeaVMEclipsePlugin.PROBLEM_MARKER_PROFILE_ATTRIBUTE, profile.getName());
} }
private Integer findMethodLocation(MethodReference methodRef, IResource resource) throws CoreException { private Integer findMethodLocation(MethodReference methodRef, IResource resource) throws CoreException {
@ -364,12 +362,12 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder {
} }
for (IMethod method : type.getMethods()) { for (IMethod method : type.getMethods()) {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append('('); sb.append(method.getElementName()).append('(');
for (String paramType : method.getParameterTypes()) { for (String paramType : method.getParameterTypes()) {
sb.append(Signature.getTypeErasure(paramType)); sb.append(Signature.getTypeErasure(paramType));
} }
sb.append(')').append(Signature.getTypeErasure(method.getReturnType())); sb.append(')').append(Signature.getTypeErasure(method.getReturnType()));
if (sb.toString().equals(methodRef.toString())) { if (sb.toString().equals(methodRef.getDescriptor().toString())) {
return getLineNumber(method); return getLineNumber(method);
} }
} }
@ -401,6 +399,9 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder {
@Override public void appendClass(String cls) { @Override public void appendClass(String cls) {
sb.append(getSimpleClassName(cls)); sb.append(getSimpleClassName(cls));
} }
@Override public void appendType(ValueType type) {
sb.append(getTypeName(type));
}
@Override public void append(String text) { @Override public void append(String text) {
sb.append(text); sb.append(text);
} }
@ -472,7 +473,7 @@ public class TeaVMProjectBuilder extends IncrementalProjectBuilder {
private String getSimpleClassName(String className) { private String getSimpleClassName(String className) {
int index = className.lastIndexOf('.'); int index = className.lastIndexOf('.');
return className.substring(index + 1); return className.substring(index + 1).replace('$', '.');
} }
private TeaVMProjectSettings getProjectSettings() { private TeaVMProjectSettings getProjectSettings() {

View File

@ -95,10 +95,10 @@ class JavascriptNativeProcessor {
cutPrefix(method.getName(), 3); cutPrefix(method.getName(), 3);
} }
Variable result = invoke.getReceiver() != null ? program.createVariable() : null; Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
addPropertyGet(propertyName, invoke.getInstance(), result); addPropertyGet(propertyName, invoke.getInstance(), result, invoke.getLocation());
if (result != null) { if (result != null) {
result = unwrap(callLocation, result, method.getResultType()); result = unwrap(callLocation, result, method.getResultType());
copyVar(result, invoke.getReceiver()); copyVar(result, invoke.getReceiver(), invoke.getLocation());
} }
} else if (isProperSetter(method.getDescriptor())) { } else if (isProperSetter(method.getDescriptor())) {
String propertyName; String propertyName;
@ -110,28 +110,30 @@ class JavascriptNativeProcessor {
} }
Variable wrapped = wrapArgument(callLocation, invoke.getArguments().get(0), Variable wrapped = wrapArgument(callLocation, invoke.getArguments().get(0),
method.parameterType(0)); method.parameterType(0));
addPropertySet(propertyName, invoke.getInstance(), wrapped); addPropertySet(propertyName, invoke.getInstance(), wrapped, invoke.getLocation());
} else { } else {
diagnostics.error(callLocation, "Method " + invoke.getMethod() + " is not " + diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript property " +
"a proper native JavaScript property declaration"); "declaration", invoke.getMethod());
continue; continue;
} }
} else if (method.getAnnotations().get(JSIndexer.class.getName()) != null) { } else if (method.getAnnotations().get(JSIndexer.class.getName()) != null) {
if (isProperGetIndexer(method.getDescriptor())) { if (isProperGetIndexer(method.getDescriptor())) {
Variable result = invoke.getReceiver() != null ? program.createVariable() : null; Variable result = invoke.getReceiver() != null ? program.createVariable() : null;
addIndexerGet(invoke.getInstance(), wrap(invoke.getArguments().get(0), addIndexerGet(invoke.getInstance(), wrap(invoke.getArguments().get(0),
method.parameterType(0)), result); method.parameterType(0), invoke.getLocation()), result, invoke.getLocation());
if (result != null) { if (result != null) {
result = unwrap(callLocation, result, method.getResultType()); result = unwrap(callLocation, result, method.getResultType());
copyVar(result, invoke.getReceiver()); copyVar(result, invoke.getReceiver(), invoke.getLocation());
} }
} else if (isProperSetIndexer(method.getDescriptor())) { } else if (isProperSetIndexer(method.getDescriptor())) {
Variable index = wrap(invoke.getArguments().get(0), method.parameterType(0)); Variable index = wrap(invoke.getArguments().get(0), method.parameterType(0),
Variable value = wrap(invoke.getArguments().get(1), method.parameterType(1)); invoke.getLocation());
addIndexerSet(invoke.getInstance(), index, value); Variable value = wrap(invoke.getArguments().get(1), method.parameterType(1),
invoke.getLocation());
addIndexerSet(invoke.getInstance(), index, value, invoke.getLocation());
} else { } else {
diagnostics.error(callLocation, "Method " + invoke.getMethod() + " is not " + diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript indexer " +
"a proper native JavaScript indexer declaration"); "declaration", invoke.getMethod());
continue; continue;
} }
} else { } else {
@ -140,17 +142,17 @@ class JavascriptNativeProcessor {
boolean isConstructor = false; boolean isConstructor = false;
if (constructorAnnot != null) { if (constructorAnnot != null) {
if (!isSupportedType(method.getResultType())) { if (!isSupportedType(method.getResultType())) {
diagnostics.error(callLocation, "Method " + invoke.getMethod() + " is not " + diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript " +
"a proper native JavaScript constructor declaration"); "constructor declaration", invoke.getMethod());
continue; continue;
} }
AnnotationValue nameVal = constructorAnnot.getValue("value"); AnnotationValue nameVal = constructorAnnot.getValue("value");
name = nameVal != null ? constructorAnnot.getValue("value").getString() : ""; name = nameVal != null ? constructorAnnot.getValue("value").getString() : "";
if (name.isEmpty()) { if (name.isEmpty()) {
if (!method.getName().startsWith("new") || method.getName().length() == 3) { if (!method.getName().startsWith("new") || method.getName().length() == 3) {
diagnostics.error(callLocation, "Method " + invoke.getMethod() + " is not " + diagnostics.error(callLocation, "Method {{m0}} is not declared as a native " +
"declared as a native JavaScript constructor, but its name does " + "JavaScript constructor, but its name does not satisfy conventions",
"not satisfy conventions"); invoke.getMethod());
continue; continue;
} }
name = method.getName().substring(3); name = method.getName().substring(3);
@ -165,15 +167,15 @@ class JavascriptNativeProcessor {
} }
} }
if (method.getResultType() != ValueType.VOID && !isSupportedType(method.getResultType())) { if (method.getResultType() != ValueType.VOID && !isSupportedType(method.getResultType())) {
diagnostics.error(callLocation, "Method " + invoke.getMethod() + " is not " + diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript method " +
"a proper native JavaScript method declaration"); "declaration", invoke.getMethod());
continue; continue;
} }
} }
for (ValueType arg : method.getParameterTypes()) { for (ValueType arg : method.getParameterTypes()) {
if (!isSupportedType(arg)) { if (!isSupportedType(arg)) {
diagnostics.error(callLocation, "Method " + invoke.getMethod() + " is not " + diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript method " +
"a proper native JavaScript method or constructor declaration"); "or constructor declaration", invoke.getMethod());
continue; continue;
} }
} }
@ -186,7 +188,9 @@ class JavascriptNativeProcessor {
newInvoke.setType(InvocationType.SPECIAL); newInvoke.setType(InvocationType.SPECIAL);
newInvoke.setReceiver(result); newInvoke.setReceiver(result);
newInvoke.getArguments().add(invoke.getInstance()); newInvoke.getArguments().add(invoke.getInstance());
newInvoke.getArguments().add(addStringWrap(addString(name))); newInvoke.getArguments().add(addStringWrap(addString(name, invoke.getLocation()),
invoke.getLocation()));
newInvoke.setLocation(invoke.getLocation());
for (int k = 0; k < invoke.getArguments().size(); ++k) { for (int k = 0; k < invoke.getArguments().size(); ++k) {
Variable arg = wrapArgument(callLocation, invoke.getArguments().get(k), Variable arg = wrapArgument(callLocation, invoke.getArguments().get(k),
method.parameterType(k)); method.parameterType(k));
@ -195,7 +199,7 @@ class JavascriptNativeProcessor {
replacement.add(newInvoke); replacement.add(newInvoke);
if (result != null) { if (result != null) {
result = unwrap(callLocation, result, method.getResultType()); result = unwrap(callLocation, result, method.getResultType());
copyVar(result, invoke.getReceiver()); copyVar(result, invoke.getReceiver(), invoke.getLocation());
} }
} }
block.getInstructions().set(j, replacement.get(0)); block.getInstructions().set(j, replacement.get(0));
@ -205,19 +209,21 @@ class JavascriptNativeProcessor {
} }
} }
private void addPropertyGet(String propertyName, Variable instance, Variable receiver) { private void addPropertyGet(String propertyName, Variable instance, Variable receiver,
Variable nameVar = addStringWrap(addString(propertyName)); InstructionLocation location) {
Variable nameVar = addStringWrap(addString(propertyName, location), location);
InvokeInstruction insn = new InvokeInstruction(); InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL); insn.setType(InvocationType.SPECIAL);
insn.setMethod(new MethodReference(JS.class, "get", JSObject.class, JSObject.class, JSObject.class)); insn.setMethod(new MethodReference(JS.class, "get", JSObject.class, JSObject.class, JSObject.class));
insn.setReceiver(receiver); insn.setReceiver(receiver);
insn.getArguments().add(instance); insn.getArguments().add(instance);
insn.getArguments().add(nameVar); insn.getArguments().add(nameVar);
insn.setLocation(location);
replacement.add(insn); replacement.add(insn);
} }
private void addPropertySet(String propertyName, Variable instance, Variable value) { private void addPropertySet(String propertyName, Variable instance, Variable value, InstructionLocation location) {
Variable nameVar = addStringWrap(addString(propertyName)); Variable nameVar = addStringWrap(addString(propertyName, location), location);
InvokeInstruction insn = new InvokeInstruction(); InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL); insn.setType(InvocationType.SPECIAL);
insn.setMethod(new MethodReference(JS.class, "set", JSObject.class, JSObject.class, insn.setMethod(new MethodReference(JS.class, "set", JSObject.class, JSObject.class,
@ -225,20 +231,22 @@ class JavascriptNativeProcessor {
insn.getArguments().add(instance); insn.getArguments().add(instance);
insn.getArguments().add(nameVar); insn.getArguments().add(nameVar);
insn.getArguments().add(value); insn.getArguments().add(value);
insn.setLocation(location);
replacement.add(insn); replacement.add(insn);
} }
private void addIndexerGet(Variable array, Variable index, Variable receiver) { private void addIndexerGet(Variable array, Variable index, Variable receiver, InstructionLocation location) {
InvokeInstruction insn = new InvokeInstruction(); InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL); insn.setType(InvocationType.SPECIAL);
insn.setMethod(new MethodReference(JS.class, "get", JSObject.class, JSObject.class, JSObject.class)); insn.setMethod(new MethodReference(JS.class, "get", JSObject.class, JSObject.class, JSObject.class));
insn.setReceiver(receiver); insn.setReceiver(receiver);
insn.getArguments().add(array); insn.getArguments().add(array);
insn.getArguments().add(index); insn.getArguments().add(index);
insn.setLocation(location);
replacement.add(insn); replacement.add(insn);
} }
private void addIndexerSet(Variable array, Variable index, Variable value) { private void addIndexerSet(Variable array, Variable index, Variable value, InstructionLocation location) {
InvokeInstruction insn = new InvokeInstruction(); InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL); insn.setType(InvocationType.SPECIAL);
insn.setMethod(new MethodReference(JS.class, "set", JSObject.class, JSObject.class, insn.setMethod(new MethodReference(JS.class, "set", JSObject.class, JSObject.class,
@ -246,25 +254,28 @@ class JavascriptNativeProcessor {
insn.getArguments().add(array); insn.getArguments().add(array);
insn.getArguments().add(index); insn.getArguments().add(index);
insn.getArguments().add(value); insn.getArguments().add(value);
insn.setLocation(location);
replacement.add(insn); replacement.add(insn);
} }
private void copyVar(Variable a, Variable b) { private void copyVar(Variable a, Variable b, InstructionLocation location) {
AssignInstruction insn = new AssignInstruction(); AssignInstruction insn = new AssignInstruction();
insn.setAssignee(a); insn.setAssignee(a);
insn.setReceiver(b); insn.setReceiver(b);
insn.setLocation(location);
replacement.add(insn); replacement.add(insn);
} }
private Variable addStringWrap(Variable var) { private Variable addStringWrap(Variable var, InstructionLocation location) {
return wrap(var, ValueType.object("java.lang.String")); return wrap(var, ValueType.object("java.lang.String"), location);
} }
private Variable addString(String str) { private Variable addString(String str, InstructionLocation location) {
Variable var = program.createVariable(); Variable var = program.createVariable();
StringConstantInstruction nameInsn = new StringConstantInstruction(); StringConstantInstruction nameInsn = new StringConstantInstruction();
nameInsn.setReceiver(var); nameInsn.setReceiver(var);
nameInsn.setConstant(str); nameInsn.setConstant(str);
nameInsn.setLocation(location);
replacement.add(nameInsn); replacement.add(nameInsn);
return var; return var;
} }
@ -273,19 +284,19 @@ class JavascriptNativeProcessor {
if (type instanceof ValueType.Primitive) { if (type instanceof ValueType.Primitive) {
switch (((ValueType.Primitive)type).getKind()) { switch (((ValueType.Primitive)type).getKind()) {
case BOOLEAN: case BOOLEAN:
return unwrap(var, "unwrapBoolean", ValueType.BOOLEAN); return unwrap(var, "unwrapBoolean", ValueType.BOOLEAN, location.getSourceLocation());
case BYTE: case BYTE:
return unwrap(var, "unwrapByte", ValueType.BYTE); return unwrap(var, "unwrapByte", ValueType.BYTE, location.getSourceLocation());
case SHORT: case SHORT:
return unwrap(var, "unwrapShort", ValueType.SHORT); return unwrap(var, "unwrapShort", ValueType.SHORT, location.getSourceLocation());
case INTEGER: case INTEGER:
return unwrap(var, "unwrapInt", ValueType.INTEGER); return unwrap(var, "unwrapInt", ValueType.INTEGER, location.getSourceLocation());
case CHARACTER: case CHARACTER:
return unwrap(var, "unwrapCharacter", ValueType.CHARACTER); return unwrap(var, "unwrapCharacter", ValueType.CHARACTER, location.getSourceLocation());
case DOUBLE: case DOUBLE:
return unwrap(var, "unwrapDouble", ValueType.DOUBLE); return unwrap(var, "unwrapDouble", ValueType.DOUBLE, location.getSourceLocation());
case FLOAT: case FLOAT:
return unwrap(var, "unwrapFloat", ValueType.FLOAT); return unwrap(var, "unwrapFloat", ValueType.FLOAT, location.getSourceLocation());
case LONG: case LONG:
break; break;
} }
@ -294,22 +305,23 @@ class JavascriptNativeProcessor {
if (className.equals(JSObject.class.getName())) { if (className.equals(JSObject.class.getName())) {
return var; return var;
} else if (className.equals("java.lang.String")) { } else if (className.equals("java.lang.String")) {
return unwrap(var, "unwrapString", ValueType.object("java.lang.String")); return unwrap(var, "unwrapString", ValueType.object("java.lang.String"), location.getSourceLocation());
} else { } else {
Variable result = program.createVariable(); Variable result = program.createVariable();
CastInstruction castInsn = new CastInstruction(); CastInstruction castInsn = new CastInstruction();
castInsn.setReceiver(result); castInsn.setReceiver(result);
castInsn.setValue(var); castInsn.setValue(var);
castInsn.setTargetType(type); castInsn.setTargetType(type);
castInsn.setLocation(location.getSourceLocation());
replacement.add(castInsn); replacement.add(castInsn);
return result; return result;
} }
} }
diagnostics.error(location, "Unsupported type: " + type); diagnostics.error(location, "Unsupported type: {{t0}}", type);
return var; return var;
} }
private Variable unwrap(Variable var, String methodName, ValueType resultType) { private Variable unwrap(Variable var, String methodName, ValueType resultType, InstructionLocation location) {
Variable result = program.createVariable(); Variable result = program.createVariable();
InvokeInstruction insn = new InvokeInstruction(); InvokeInstruction insn = new InvokeInstruction();
insn.setMethod(new MethodReference(JS.class.getName(), methodName, ValueType.object(JSObject.class.getName()), insn.setMethod(new MethodReference(JS.class.getName(), methodName, ValueType.object(JSObject.class.getName()),
@ -317,6 +329,7 @@ class JavascriptNativeProcessor {
insn.getArguments().add(var); insn.getArguments().add(var);
insn.setReceiver(result); insn.setReceiver(result);
insn.setType(InvocationType.SPECIAL); insn.setType(InvocationType.SPECIAL);
insn.setLocation(location);
replacement.add(insn); replacement.add(insn);
return result; return result;
} }
@ -329,28 +342,29 @@ class JavascriptNativeProcessor {
return wrapFunctor(location, var, cls); return wrapFunctor(location, var, cls);
} }
} }
return wrap(var, type); return wrap(var, type, location.getSourceLocation());
} }
private Variable wrapFunctor(CallLocation location, Variable var, ClassReader type) { private Variable wrapFunctor(CallLocation location, Variable var, ClassReader type) {
if (!type.hasModifier(ElementModifier.INTERFACE) || type.getMethods().size() != 1) { if (!type.hasModifier(ElementModifier.INTERFACE) || type.getMethods().size() != 1) {
diagnostics.error(location, "Wrong functor: " + type.getName()); diagnostics.error(location, "Wrong functor: {{c0}}", type.getName());
return var; return var;
} }
String name = type.getMethods().iterator().next().getName(); String name = type.getMethods().iterator().next().getName();
Variable functor = program.createVariable(); Variable functor = program.createVariable();
Variable nameVar = addStringWrap(addString(name)); Variable nameVar = addStringWrap(addString(name, location.getSourceLocation()), location.getSourceLocation());
InvokeInstruction insn = new InvokeInstruction(); InvokeInstruction insn = new InvokeInstruction();
insn.setType(InvocationType.SPECIAL); insn.setType(InvocationType.SPECIAL);
insn.setMethod(new MethodReference(JS.class, "function", JSObject.class, JSObject.class, JSObject.class)); insn.setMethod(new MethodReference(JS.class, "function", JSObject.class, JSObject.class, JSObject.class));
insn.setReceiver(functor); insn.setReceiver(functor);
insn.getArguments().add(var); insn.getArguments().add(var);
insn.getArguments().add(nameVar); insn.getArguments().add(nameVar);
insn.setLocation(location.getSourceLocation());
replacement.add(insn); replacement.add(insn);
return functor; return functor;
} }
private Variable wrap(Variable var, ValueType type) { private Variable wrap(Variable var, ValueType type, InstructionLocation location) {
if (type instanceof ValueType.Object) { if (type instanceof ValueType.Object) {
String className = ((ValueType.Object)type).getClassName(); String className = ((ValueType.Object)type).getClassName();
if (!className.equals("java.lang.String")) { if (!className.equals("java.lang.String")) {
@ -364,6 +378,7 @@ class JavascriptNativeProcessor {
insn.getArguments().add(var); insn.getArguments().add(var);
insn.setReceiver(result); insn.setReceiver(result);
insn.setType(InvocationType.SPECIAL); insn.setType(InvocationType.SPECIAL);
insn.setLocation(location);
replacement.add(insn); replacement.add(insn);
return result; return result;
} }