Further work on CPS generator

This commit is contained in:
konsoletyper 2015-02-01 17:43:21 +04:00
parent 672de2f111
commit 3c9acd8fab
20 changed files with 273 additions and 47 deletions

View File

@ -15,6 +15,9 @@
*/
package org.teavm.classlib.java.lang;
import org.teavm.javascript.ni.GeneratedBy;
import org.teavm.runtime.Async;
/**
*
* @author Alexey Andreev
@ -56,8 +59,9 @@ public class TThread extends TObject implements TRunnable {
return name;
}
public static void yield() {
}
@Async
@GeneratedBy(ThreadNativeGenerator.class)
public static native void yield();
public void interrupt() {
}
@ -81,4 +85,12 @@ public class TThread extends TObject implements TRunnable {
public static boolean holdsLock(@SuppressWarnings("unused") TObject obj) {
return true;
}
public static void sleep(long millis) throws TInterruptedException {
sleep((double)millis);
}
@Async
@GeneratedBy(ThreadNativeGenerator.class)
private static native void sleep(double millis) throws TInterruptedException;
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2015 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.java.lang;
import java.io.IOException;
import org.teavm.codegen.SourceWriter;
import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.GeneratorContext;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class ThreadNativeGenerator implements Generator {
@Override
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
if (methodRef.getName().equals("sleep")) {
generateSleep(context, writer);
} else if (methodRef.getName().equals("yield")) {
generateYield(context, writer);
}
}
private void generateSleep(GeneratorContext context, SourceWriter writer) throws IOException {
writer.append("setTimer(function() {").indent().softNewLine();
writer.append(context.getCompleteContinuation()).append("();").softNewLine();
writer.outdent().append(',').ws().append(context.getParameterName(1)).append(");").softNewLine();
}
private void generateYield(GeneratorContext context, SourceWriter writer) throws IOException {
writer.append("setTimer(function() {").indent().softNewLine();
writer.append(context.getCompleteContinuation()).append("();").softNewLine();
writer.outdent().append(',').ws().append("0);").softNewLine();
}
}

View File

@ -57,6 +57,15 @@ public class DefaultNamingStrategy implements NamingStrategy {
@Override
public String getNameFor(MethodReference method) {
return getNameFor(method, false);
}
@Override
public String getNameForAsync(MethodReference method) throws NamingException {
return getNameFor(method, true);
}
private String getNameFor(MethodReference method, boolean async) {
MethodReference origMethod = method;
method = getRealMethod(method);
if (method == null) {
@ -67,7 +76,7 @@ public class DefaultNamingStrategy implements NamingStrategy {
if (methodHolder.hasModifier(ElementModifier.STATIC) ||
method.getDescriptor().getName().equals("<init>") ||
methodHolder.getLevel() == AccessLevel.PRIVATE) {
String key = method.toString();
String key = (async ? "A" : "S") + method.toString();
String alias = privateAliases.get(key);
if (alias == null) {
alias = aliasProvider.getAlias(method);
@ -75,7 +84,7 @@ public class DefaultNamingStrategy implements NamingStrategy {
}
return alias;
} else {
String key = method.getDescriptor().toString();
String key = (async ? "A" : "S") + method.getDescriptor().toString();
String alias = aliases.get(key);
if (alias == null) {
alias = aliasProvider.getAlias(method);

View File

@ -27,6 +27,8 @@ public interface NamingStrategy {
String getNameFor(MethodReference method) throws NamingException;
String getNameForAsync(MethodReference method) throws NamingException;
String getFullNameFor(MethodReference method) throws NamingException;
String getNameFor(FieldReference field) throws NamingException;

View File

@ -149,7 +149,6 @@ public class Decompiler {
if (method.getAnnotations().get(PreserveOriginalName.class.getName()) != null) {
methodNode.setOriginalNamePreserved(true);
}
clsNode.getAsyncMethods().add(decompileAsync(method));
}
clsNode.getInterfaces().addAll(cls.getInterfaces());
clsNode.getModifiers().addAll(mapModifiers(cls.getModifiers()));
@ -158,12 +157,7 @@ public class Decompiler {
public MethodNode decompile(MethodHolder method) {
return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method, false) :
decompileRegular(method);
}
public MethodNode decompileAsync(MethodHolder method) {
return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method, true) :
decompileRegularAsync(method);
!asyncMethods.contains(method.getReference()) ? decompileRegular(method) : decompileAsync(method);
}
public NativeMethodNode decompileNative(MethodHolder method, boolean async) {
@ -204,7 +198,7 @@ public class Decompiler {
return node;
}
public AsyncMethodNode decompileRegularAsync(MethodHolder method) {
public AsyncMethodNode decompileAsync(MethodHolder method) {
AsyncMethodNode node = new AsyncMethodNode(method.getReference());
AsyncProgramSplitter splitter = new AsyncProgramSplitter(asyncMethods);
splitter.split(method.getProgram());

View File

@ -180,7 +180,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
}
private boolean tryApplyConstructor(InvocationExpr expr) {
if (!expr.getMethod().getName().equals("<init>")) {
if (expr.getAsyncTarget() != null || !expr.getMethod().getName().equals("<init>")) {
return false;
}
if (resultSequence == null || resultSequence.isEmpty()) {
@ -211,7 +211,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
}
Expr[] args = expr.getArguments().toArray(new Expr[0]);
args = Arrays.copyOfRange(args, 1, args.length);
Expr constructrExpr = Expr.constructObject(expr.getMethod(), args);
InvocationExpr constructrExpr = Expr.constructObject(expr.getMethod(), args);
constructrExpr.setLocation(expr.getLocation());
assignment.setRightValue(constructrExpr);
stats.reads[var.getIndex()]--;

View File

@ -370,11 +370,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
for (MethodNode method : nonInitMethods) {
renderBody(method, false);
}
for (MethodNode method : cls.getAsyncMethods()) {
writer.append("/*").softNewLine();
renderBody(method, false);
writer.append("*/").softNewLine();
}
renderVirtualDeclarations(cls.getName(), virtualMethods);
} catch (NamingException e) {
throw new RenderingException("Error rendering class " + cls.getName() + ". See a cause for details", e);
@ -475,6 +470,12 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
}
writer.append(");").ws().append("}");
debugEmitter.emitMethod(null);
if (!method.isAsync()) {
writer.append(",").newLine();
writer.append("\"").append(naming.getNameForAsync(ref)).append("\",").ws();
writer.append("$rt_asyncAdapter(").appendMethodBody(ref).append(')');
}
}
writer.append(");").newLine().outdent();
}
@ -524,6 +525,9 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
}
writer.append(variableName(i));
}
if (method.isAsync()) {
writer.append(',').ws().append("$return,").ws().append("$throw");
}
writer.append(")").ws().append("{").softNewLine().indent();
method.acceptVisitor(new MethodBodyRenderer());
writer.outdent().append("}").newLine();
@ -531,9 +535,13 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
}
private class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
private boolean async;
@Override
public void visit(NativeMethodNode methodNode) {
try {
this.async = methodNode.isAsync();
Renderer.this.async = methodNode.isAsync();
methodNode.getGenerator().generate(this, writer, methodNode.getReference());
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
@ -544,6 +552,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
public void visit(RegularMethodNode method) {
try {
Renderer.this.async = false;
this.async = false;
MethodReference ref = method.getReference();
for (int i = 0; i < method.getParameterDebugNames().size(); ++i) {
debugEmitter.emitVariable(method.getParameterDebugNames().get(i).toArray(new String[0]),
@ -583,6 +592,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
public void visit(AsyncMethodNode methodNode) {
try {
Renderer.this.async = true;
this.async = true;
MethodReference ref = methodNode.getReference();
for (int i = 0; i < methodNode.getParameterDebugNames().size(); ++i) {
debugEmitter.emitVariable(methodNode.getParameterDebugNames().get(i).toArray(new String[0]),
@ -607,8 +617,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append(";").softNewLine();
}
for (int i = 0; i < methodNode.getBody().size(); ++i) {
writer.append("function $part_").append(i).append("($input,").ws().append("$return,").ws()
.append("$throw)").ws().append('{').indent().softNewLine();
writer.append("function $part_").append(i).append("($input)").ws().append('{')
.indent().softNewLine();
AsyncMethodPart part = methodNode.getBody().get(i);
if (part.getInputVariable() != null) {
writer.append(variableName(part.getInputVariable())).ws().append('=').ws().append("$input;")
@ -617,7 +627,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
part.getStatement().acceptVisitor(Renderer.this);
writer.outdent().append('}').softNewLine();
}
writer.append("return $part_0;").softNewLine();
writer.append("return $part_0();").softNewLine();
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
@ -647,6 +657,21 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
public <T> T getService(Class<T> type) {
return services.getService(type);
}
@Override
public boolean isAsync() {
return async;
}
@Override
public String getErrorContinuation() {
return "$throw";
}
@Override
public String getCompleteContinuation() {
return "$return";
}
}
private void pushLocation(NodeLocation location) {
@ -1404,7 +1429,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
expr.getArguments().get(0).acceptVisitor(this);
}
String className = naming.getNameFor(expr.getMethod().getClassName());
String name = naming.getNameFor(expr.getMethod());
String name = expr.getAsyncTarget() == null ? naming.getNameFor(expr.getMethod()) :
naming.getNameForAsync(expr.getMethod());
String fullName = naming.getFullNameFor(expr.getMethod());
DeferredCallSite callSite = prevCallSite;
boolean shouldEraseCallSite = lastCallSite == null;

View File

@ -49,4 +49,9 @@ public class AsyncMethodNode extends MethodNode {
public void acceptVisitor(MethodNodeVisitor visitor) {
visitor.visit(this);
}
@Override
public boolean isAsync() {
return true;
}
}

View File

@ -30,7 +30,6 @@ public class ClassNode {
private Set<NodeModifier> modifiers = EnumSet.noneOf(NodeModifier.class);
private List<FieldNode> fields = new ArrayList<>();
private List<MethodNode> methods = new ArrayList<>();
private List<MethodNode> asyncMethods = new ArrayList<>();
private List<String> interfaces = new ArrayList<>();
public ClassNode(String name, String parentName) {
@ -54,10 +53,6 @@ public class ClassNode {
return methods;
}
public List<MethodNode> getAsyncMethods() {
return asyncMethods;
}
public List<String> getInterfaces() {
return interfaces;
}

View File

@ -104,7 +104,7 @@ public abstract class Expr implements Cloneable {
return expr;
}
public static Expr constructObject(MethodReference method, Expr[] arguments) {
public static InvocationExpr constructObject(MethodReference method, Expr[] arguments) {
InvocationExpr expr = new InvocationExpr();
expr.setMethod(method);
expr.setType(InvocationType.CONSTRUCTOR);

View File

@ -50,4 +50,6 @@ public abstract class MethodNode {
}
public abstract void acceptVisitor(MethodNodeVisitor visitor);
public abstract boolean isAsync();
}

View File

@ -38,6 +38,7 @@ public class NativeMethodNode extends MethodNode {
this.generator = generator;
}
@Override
public boolean isAsync() {
return async;
}

View File

@ -53,4 +53,9 @@ public class RegularMethodNode extends MethodNode {
public void acceptVisitor(MethodNodeVisitor visitor) {
visitor.visit(this);
}
@Override
public boolean isAsync() {
return false;
}
}

View File

@ -31,4 +31,10 @@ public interface GeneratorContext extends ServiceRepository {
ClassLoader getClassLoader();
Properties getProperties();
boolean isAsync();
String getErrorContinuation();
String getCompleteContinuation();
}

View File

@ -16,9 +16,7 @@
package org.teavm.model.instructions;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.teavm.model.Instruction;
import org.teavm.model.MethodReference;
import org.teavm.model.Variable;
@ -32,8 +30,6 @@ public class InvokeInstruction extends Instruction {
private MethodReference method;
private Variable instance;
private List<Variable> arguments = new ArrayList<>();
private Set<MethodReference> implemenetations = new HashSet<MethodReference>();
private boolean resolved;
private Variable receiver;
public InvocationType getType() {
@ -72,18 +68,6 @@ public class InvokeInstruction extends Instruction {
this.receiver = receiver;
}
public Set<MethodReference> getImplemenetations() {
return implemenetations;
}
public boolean isResolved() {
return resolved;
}
public void setResolved(boolean resolved) {
this.resolved = resolved;
}
@Override
public void acceptVisitor(InstructionVisitor visitor) {
visitor.visit(this);

View File

@ -0,0 +1,89 @@
/*
* Copyright 2015 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.model.util;
import java.util.HashSet;
import java.util.Set;
import org.teavm.callgraph.CallGraph;
import org.teavm.callgraph.CallGraphNode;
import org.teavm.callgraph.CallSite;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.javascript.ni.InjectedBy;
import org.teavm.model.*;
import org.teavm.runtime.Async;
import org.teavm.runtime.Sync;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public class AsyncMethodFinder {
private Set<MethodReference> asyncMethods = new HashSet<>();
private CallGraph callGraph;
private Diagnostics diagnostics;
private ListableClassReaderSource classSource;
public AsyncMethodFinder(CallGraph callGraph, Diagnostics diagnostics) {
this.callGraph = callGraph;
this.diagnostics = diagnostics;
}
public Set<MethodReference> getAsyncMethods() {
return asyncMethods;
}
public void find(ListableClassReaderSource classSource) {
this.classSource = classSource;
for (String clsName : classSource.getClassNames()) {
ClassReader cls = classSource.get(clsName);
for (MethodReader method : cls.getMethods()) {
if (asyncMethods.contains(method.getReference())) {
continue;
}
if (method.getAnnotations().get(Async.class.getName()) != null) {
add(method.getReference());
}
}
}
}
private void add(MethodReference methodRef) {
if (!asyncMethods.add(methodRef)) {
return;
}
CallGraphNode node = callGraph.getNode(methodRef);
if (node == null) {
return;
}
ClassReader cls = classSource.get(methodRef.getClassName());
if (cls == null) {
return;
}
MethodReader method = cls.getMethod(methodRef.getDescriptor());
if (method == null) {
return;
}
if (method.getAnnotations().get(Sync.class.getName()) != null ||
method.getAnnotations().get(InjectedBy.class.getName()) != null) {
diagnostics.error(new CallLocation(methodRef), "Method {{m0}} is claimed to be synchronous, " +
"but it is has invocations of asynchronous methods", methodRef);
return;
}
for (CallSite callSite : node.getCallerCallSites()) {
add(callSite.getCaller().getMethod());
}
}
}

View File

@ -61,7 +61,7 @@ public class AsyncProgramSplitter {
Instruction insn = sourceBlock.getInstructions().get(i);
if (insn instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction)insn;
if (true) { //asyncMethods.contains(invoke)) {
if (asyncMethods.contains(invoke.getMethod())) {
asyncOccured = true;
long key = ((long)step.source << 32) | i;
if (partMap.containsKey(key)) {

View File

@ -0,0 +1,30 @@
/*
* Copyright 2015 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.runtime;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Sync {
}

View File

@ -526,8 +526,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
}
private List<ClassNode> modelToAst(ListableClassHolderSource classes) {
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(dependencyChecker.getCallGraph(), diagnostics);
asyncFinder.find(classes);
progressListener.phaseStarted(TeaVMPhase.DECOMPILATION, classes.getClassNames().size());
Decompiler decompiler = new Decompiler(classes, classLoader, new HashSet<MethodReference>());
Decompiler decompiler = new Decompiler(classes, classLoader, asyncFinder.getAsyncMethods());
decompiler.setRegularMethodCache(incremental ? astCache : null);
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {

View File

@ -392,6 +392,20 @@ function $rt_virtualMethods(cls) {
}
}
}
function $rt_asyncAdapter(f) {
return function() {
var e;
var args = Array.prototype.slice.apply(arguments);
var $throw = args.pop();
var $return args.pop();
try {
var result = f.apply(this, args);
} catch (e) {
return $throw(e);
}
return $return(result);
}
}
var $rt_stringPool_instance;
function $rt_stringPool(strings) {
$rt_stringPool_instance = new Array(strings.length);