Merge branch 'async' of https://github.com/konsoletyper/teavm into threads

This commit is contained in:
Steve Hannah 2015-02-02 08:26:12 -08:00
commit 7c084effb0
37 changed files with 1220 additions and 68 deletions

View File

@ -15,6 +15,9 @@
*/ */
package org.teavm.classlib.java.lang; package org.teavm.classlib.java.lang;
import org.teavm.javascript.ni.GeneratedBy;
import org.teavm.runtime.Async;
/** /**
* *
* @author Alexey Andreev * @author Alexey Andreev
@ -56,8 +59,9 @@ public class TThread extends TObject implements TRunnable {
return name; return name;
} }
public static void yield() { @Async
} @GeneratedBy(ThreadNativeGenerator.class)
public static native void yield();
public void interrupt() { public void interrupt() {
} }
@ -81,4 +85,12 @@ public class TThread extends TObject implements TRunnable {
public static boolean holdsLock(@SuppressWarnings("unused") TObject obj) { public static boolean holdsLock(@SuppressWarnings("unused") TObject obj) {
return true; 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("setTimeout(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("setTimeout(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 @Override
public String getNameFor(MethodReference method) { 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; MethodReference origMethod = method;
method = getRealMethod(method); method = getRealMethod(method);
if (method == null) { if (method == null) {
@ -67,7 +76,7 @@ public class DefaultNamingStrategy implements NamingStrategy {
if (methodHolder.hasModifier(ElementModifier.STATIC) || if (methodHolder.hasModifier(ElementModifier.STATIC) ||
method.getDescriptor().getName().equals("<init>") || method.getDescriptor().getName().equals("<init>") ||
methodHolder.getLevel() == AccessLevel.PRIVATE) { methodHolder.getLevel() == AccessLevel.PRIVATE) {
String key = method.toString(); String key = (async ? "A" : "S") + method.toString();
String alias = privateAliases.get(key); String alias = privateAliases.get(key);
if (alias == null) { if (alias == null) {
alias = aliasProvider.getAlias(method); alias = aliasProvider.getAlias(method);
@ -75,7 +84,7 @@ public class DefaultNamingStrategy implements NamingStrategy {
} }
return alias; return alias;
} else { } else {
String key = method.getDescriptor().toString(); String key = (async ? "A" : "S") + method.getDescriptor().toString();
String alias = aliases.get(key); String alias = aliases.get(key);
if (alias == null) { if (alias == null) {
alias = aliasProvider.getAlias(method); alias = aliasProvider.getAlias(method);

View File

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

View File

@ -0,0 +1,25 @@
/*
* 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.javascript;
/**
*
* @author Alexey Andreev
*/
public enum AsyncInvocationType {
COMPLETE,
ERROR
}

View File

@ -23,6 +23,7 @@ import org.teavm.javascript.ni.Generator;
import org.teavm.javascript.ni.InjectedBy; import org.teavm.javascript.ni.InjectedBy;
import org.teavm.javascript.ni.PreserveOriginalName; import org.teavm.javascript.ni.PreserveOriginalName;
import org.teavm.model.*; import org.teavm.model.*;
import org.teavm.model.util.AsyncProgramSplitter;
import org.teavm.model.util.ProgramUtils; import org.teavm.model.util.ProgramUtils;
/** /**
@ -45,10 +46,12 @@ public class Decompiler {
private Map<MethodReference, Generator> generators = new HashMap<>(); private Map<MethodReference, Generator> generators = new HashMap<>();
private Set<MethodReference> methodsToPass = new HashSet<>(); private Set<MethodReference> methodsToPass = new HashSet<>();
private RegularMethodNodeCache regularMethodCache; private RegularMethodNodeCache regularMethodCache;
private Set<MethodReference> asyncMethods;
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader) { public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, Set<MethodReference> asyncMethods) {
this.classSource = classSource; this.classSource = classSource;
this.classLoader = classLoader; this.classLoader = classLoader;
this.asyncMethods = asyncMethods;
} }
public RegularMethodNodeCache getRegularMethodCache() { public RegularMethodNodeCache getRegularMethodCache() {
@ -154,7 +157,7 @@ public class Decompiler {
public MethodNode decompile(MethodHolder method) { public MethodNode decompile(MethodHolder method) {
return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method) : return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method) :
decompileRegular(method); !asyncMethods.contains(method.getReference()) ? decompileRegular(method) : decompileAsync(method);
} }
public NativeMethodNode decompileNative(MethodHolder method) { public NativeMethodNode decompileNative(MethodHolder method) {
@ -179,6 +182,7 @@ public class Decompiler {
method.getDescriptor())); method.getDescriptor()));
methodNode.getModifiers().addAll(mapModifiers(method.getModifiers())); methodNode.getModifiers().addAll(mapModifiers(method.getModifiers()));
methodNode.setGenerator(generator); methodNode.setGenerator(generator);
methodNode.setAsync(asyncMethods.contains(method.getReference()));
return methodNode; return methodNode;
} }
@ -194,14 +198,58 @@ public class Decompiler {
return node; return node;
} }
public AsyncMethodNode decompileAsync(MethodHolder method) {
AsyncMethodNode node = new AsyncMethodNode(method.getReference());
AsyncProgramSplitter splitter = new AsyncProgramSplitter(asyncMethods);
splitter.split(method.getProgram());
for (int i = 0; i < splitter.size(); ++i) {
AsyncMethodPart part = getRegularMethodStatement(splitter.getProgram(i), splitter.getBlockSuccessors(i));
part.setInputVariable(splitter.getInput(i));
node.getBody().add(part);
}
Program program = method.getProgram();
for (int i = 0; i < program.variableCount(); ++i) {
node.getVariables().add(program.variableAt(i).getRegister());
}
Optimizer optimizer = new Optimizer();
optimizer.optimize(node, method.getProgram());
node.getModifiers().addAll(mapModifiers(method.getModifiers()));
int paramCount = Math.min(method.getSignature().length, program.variableCount());
for (int i = 0; i < paramCount; ++i) {
Variable var = program.variableAt(i);
node.getParameterDebugNames().add(new HashSet<>(var.getDebugNames()));
}
return node;
}
public RegularMethodNode decompileRegularCacheMiss(MethodHolder method) { public RegularMethodNode decompileRegularCacheMiss(MethodHolder method) {
RegularMethodNode methodNode = new RegularMethodNode(method.getReference());
Program program = method.getProgram();
int[] targetBlocks = new int[program.basicBlockCount()];
Arrays.fill(targetBlocks, -1);
methodNode.setBody(getRegularMethodStatement(program, targetBlocks).getStatement());
for (int i = 0; i < program.variableCount(); ++i) {
methodNode.getVariables().add(program.variableAt(i).getRegister());
}
Optimizer optimizer = new Optimizer();
optimizer.optimize(methodNode, method.getProgram());
methodNode.getModifiers().addAll(mapModifiers(method.getModifiers()));
int paramCount = Math.min(method.getSignature().length, program.variableCount());
for (int i = 0; i < paramCount; ++i) {
Variable var = program.variableAt(i);
methodNode.getParameterDebugNames().add(new HashSet<>(var.getDebugNames()));
}
return methodNode;
}
private AsyncMethodPart getRegularMethodStatement(Program program, int[] targetBlocks) {
AsyncMethodPart result = new AsyncMethodPart();
lastBlockId = 1; lastBlockId = 1;
graph = ProgramUtils.buildControlFlowGraph(method.getProgram()); graph = ProgramUtils.buildControlFlowGraph(program);
indexer = new GraphIndexer(graph); indexer = new GraphIndexer(graph);
graph = indexer.getGraph(); graph = indexer.getGraph();
loopGraph = new LoopGraph(this.graph); loopGraph = new LoopGraph(this.graph);
unflatCode(); unflatCode();
Program program = method.getProgram();
blockMap = new Block[program.basicBlockCount() * 2 + 1]; blockMap = new Block[program.basicBlockCount() * 2 + 1];
Deque<Block> stack = new ArrayDeque<>(); Deque<Block> stack = new ArrayDeque<>();
BlockStatement rootStmt = new BlockStatement(); BlockStatement rootStmt = new BlockStatement();
@ -247,9 +295,13 @@ public class Decompiler {
int tmp = indexer.nodeAt(next); int tmp = indexer.nodeAt(next);
generator.nextBlock = tmp >= 0 && next < indexer.size() ? program.basicBlockAt(tmp) : null; generator.nextBlock = tmp >= 0 && next < indexer.size() ? program.basicBlockAt(tmp) : null;
generator.statements.clear(); generator.statements.clear();
generator.asyncTarget = null;
InstructionLocation lastLocation = null; InstructionLocation lastLocation = null;
NodeLocation nodeLocation = null; NodeLocation nodeLocation = null;
for (Instruction insn : generator.currentBlock.getInstructions()) { List<Instruction> instructions = generator.currentBlock.getInstructions();
boolean asyncInvocation = false;
for (int j = 0; j < instructions.size(); ++j) {
Instruction insn = generator.currentBlock.getInstructions().get(j);
if (insn.getLocation() != null && lastLocation != insn.getLocation()) { if (insn.getLocation() != null && lastLocation != insn.getLocation()) {
lastLocation = insn.getLocation(); lastLocation = insn.getLocation();
nodeLocation = new NodeLocation(lastLocation.getFileName(), lastLocation.getLine()); nodeLocation = new NodeLocation(lastLocation.getFileName(), lastLocation.getLine());
@ -257,9 +309,15 @@ public class Decompiler {
if (insn.getLocation() != null) { if (insn.getLocation() != null) {
generator.setCurrentLocation(nodeLocation); generator.setCurrentLocation(nodeLocation);
} }
if (targetBlocks[node] >= 0 && j == instructions.size() - 1) {
generator.asyncTarget = targetBlocks[node];
asyncInvocation = true;
}
insn.acceptVisitor(generator); insn.acceptVisitor(generator);
} }
boolean hasAsyncCatch = false;
for (TryCatchBlock tryCatch : generator.currentBlock.getTryCatchBlocks()) { for (TryCatchBlock tryCatch : generator.currentBlock.getTryCatchBlocks()) {
if (asyncInvocation) {
TryCatchStatement tryCatchStmt = new TryCatchStatement(); TryCatchStatement tryCatchStmt = new TryCatchStatement();
tryCatchStmt.setExceptionType(tryCatch.getExceptionType()); tryCatchStmt.setExceptionType(tryCatch.getExceptionType());
tryCatchStmt.setExceptionVariable(tryCatch.getExceptionVariable().getIndex()); tryCatchStmt.setExceptionVariable(tryCatch.getExceptionVariable().getIndex());
@ -270,28 +328,32 @@ public class Decompiler {
if (handlerStmt != null) { if (handlerStmt != null) {
tryCatchStmt.getHandler().add(handlerStmt); tryCatchStmt.getHandler().add(handlerStmt);
} }
} else {
AsyncMethodCatch asyncCatch = new AsyncMethodCatch();
asyncCatch.setExceptionType(tryCatch.getExceptionType());
asyncCatch.setExceptionVariable(tryCatch.getExceptionVariable().getIndex());
Statement handlerStmt = generator.generateJumpStatement(tryCatch.getHandler());
if (handlerStmt != null) {
asyncCatch.getHandler().add(handlerStmt);
}
result.getCatches().add(asyncCatch);
hasAsyncCatch = true;
}
}
if (hasAsyncCatch) {
TryCatchStatement guardTryCatch = new TryCatchStatement();
guardTryCatch.setAsync(true);
guardTryCatch.getProtectedBody().addAll(generator.statements);
generator.statements.clear();
generator.statements.add(guardTryCatch);
} }
block.body.addAll(generator.statements); block.body.addAll(generator.statements);
} }
} }
SequentialStatement result = new SequentialStatement(); SequentialStatement resultBody = new SequentialStatement();
result.getSequence().addAll(rootStmt.getBody()); resultBody.getSequence().addAll(rootStmt.getBody());
MethodReference reference = new MethodReference(method.getOwnerName(), method.getDescriptor()); result.setStatement(resultBody);
RegularMethodNode methodNode = new RegularMethodNode(reference); return result;
methodNode.getModifiers().addAll(mapModifiers(method.getModifiers()));
methodNode.setBody(result);
for (int i = 0; i < program.variableCount(); ++i) {
methodNode.getVariables().add(program.variableAt(i).getRegister());
}
Optimizer optimizer = new Optimizer();
optimizer.optimize(methodNode, method.getProgram());
methodNode.getModifiers().addAll(mapModifiers(method.getModifiers()));
int paramCount = Math.min(method.getSignature().length, program.variableCount());
for (int i = 0; i < paramCount; ++i) {
Variable var = program.variableAt(i);
methodNode.getParameterDebugNames().add(new HashSet<>(var.getDebugNames()));
}
return methodNode;
} }
private Set<NodeModifier> mapModifiers(Set<ElementModifier> modifiers) { private Set<NodeModifier> mapModifiers(Set<ElementModifier> modifiers) {

View File

@ -15,6 +15,8 @@
*/ */
package org.teavm.javascript; package org.teavm.javascript;
import org.teavm.javascript.ast.AsyncMethodNode;
import org.teavm.javascript.ast.AsyncMethodPart;
import org.teavm.javascript.ast.RegularMethodNode; import org.teavm.javascript.ast.RegularMethodNode;
import org.teavm.model.Program; import org.teavm.model.Program;
@ -40,4 +42,27 @@ public class Optimizer {
method.getVariables().set(i, i); method.getVariables().set(i, i);
} }
} }
public void optimize(AsyncMethodNode method, Program program) {
ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariables().size());
stats.analyze(program);
OptimizingVisitor optimizer = new OptimizingVisitor(stats);
for (AsyncMethodPart part : method.getBody()) {
part.getStatement().acceptVisitor(optimizer);
part.setStatement(optimizer.resultStmt);
}
int paramCount = method.getReference().parameterCount();
UnusedVariableEliminator unusedEliminator = new UnusedVariableEliminator(paramCount, method.getVariables());
for (AsyncMethodPart part : method.getBody()) {
part.getStatement().acceptVisitor(unusedEliminator);
}
method.getVariables().subList(unusedEliminator.lastIndex, method.getVariables().size()).clear();
RedundantLabelEliminator labelEliminator = new RedundantLabelEliminator();
for (AsyncMethodPart part : method.getBody()) {
part.getStatement().acceptVisitor(labelEliminator);
}
for (int i = 0; i < method.getVariables().size(); ++i) {
method.getVariables().set(i, i);
}
}
} }

View File

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

View File

@ -53,6 +53,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
private Deque<LocationStackEntry> locationStack = new ArrayDeque<>(); private Deque<LocationStackEntry> locationStack = new ArrayDeque<>();
private DeferredCallSite lastCallSite; private DeferredCallSite lastCallSite;
private DeferredCallSite prevCallSite; private DeferredCallSite prevCallSite;
private boolean async;
private static class InjectorHolder { private static class InjectorHolder {
public final Injector injector; public final Injector injector;
@ -469,6 +470,12 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} }
writer.append(");").ws().append("}"); writer.append(");").ws().append("}");
debugEmitter.emitMethod(null); 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(); writer.append(");").newLine().outdent();
} }
@ -518,6 +525,12 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} }
writer.append(variableName(i)); writer.append(variableName(i));
} }
if (method.isAsync()) {
if (startParam < ref.parameterCount() + 1) {
writer.append(',').ws();
}
writer.append("$return,").ws().append("$throw");
}
writer.append(")").ws().append("{").softNewLine().indent(); writer.append(")").ws().append("{").softNewLine().indent();
method.acceptVisitor(new MethodBodyRenderer()); method.acceptVisitor(new MethodBodyRenderer());
writer.outdent().append("}").newLine(); writer.outdent().append("}").newLine();
@ -525,9 +538,13 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} }
private class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext { private class MethodBodyRenderer implements MethodNodeVisitor, GeneratorContext {
private boolean async;
@Override @Override
public void visit(NativeMethodNode methodNode) { public void visit(NativeMethodNode methodNode) {
try { try {
this.async = methodNode.isAsync();
Renderer.this.async = methodNode.isAsync();
methodNode.getGenerator().generate(this, writer, methodNode.getReference()); methodNode.getGenerator().generate(this, writer, methodNode.getReference());
} catch (IOException e) { } catch (IOException e) {
throw new RenderingException("IO error occured", e); throw new RenderingException("IO error occured", e);
@ -537,6 +554,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override @Override
public void visit(RegularMethodNode method) { public void visit(RegularMethodNode method) {
try { try {
Renderer.this.async = false;
this.async = false;
MethodReference ref = method.getReference(); MethodReference ref = method.getReference();
for (int i = 0; i < method.getParameterDebugNames().size(); ++i) { for (int i = 0; i < method.getParameterDebugNames().size(); ++i) {
debugEmitter.emitVariable(method.getParameterDebugNames().get(i).toArray(new String[0]), debugEmitter.emitVariable(method.getParameterDebugNames().get(i).toArray(new String[0]),
@ -572,6 +591,51 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
} }
} }
@Override
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]),
variableName(i));
}
int variableCount = 0;
for (int var : methodNode.getVariables()) {
variableCount = Math.max(variableCount, var + 1);
}
List<String> variableNames = new ArrayList<>();
for (int i = ref.parameterCount() + 1; i < variableCount; ++i) {
variableNames.add(variableName(i));
}
if (!variableNames.isEmpty()) {
writer.append("var ");
for (int i = 0; i < variableNames.size(); ++i) {
if (i > 0) {
writer.append(",").ws();
}
writer.append(variableNames.get(i));
}
writer.append(";").softNewLine();
}
for (int i = 0; i < methodNode.getBody().size(); ++i) {
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;")
.softNewLine();
}
part.getStatement().acceptVisitor(Renderer.this);
writer.outdent().append('}').softNewLine();
}
writer.append("return $part_0();").softNewLine();
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
}
@Override @Override
public String getParameterName(int index) { public String getParameterName(int index) {
return variableName(index); return variableName(index);
@ -596,6 +660,21 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
public <T> T getService(Class<T> type) { public <T> T getService(Class<T> type) {
return services.getService(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) { private void pushLocation(NodeLocation location) {
@ -832,12 +911,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
pushLocation(statement.getLocation()); pushLocation(statement.getLocation());
} }
writer.append("return"); writer.append("return");
if (async) {
writer.append(" $return(");
}
if (statement.getResult() != null) { if (statement.getResult() != null) {
writer.append(' '); writer.append(' ');
prevCallSite = debugEmitter.emitCallSite(); prevCallSite = debugEmitter.emitCallSite();
statement.getResult().acceptVisitor(this); statement.getResult().acceptVisitor(this);
debugEmitter.emitCallSite(); debugEmitter.emitCallSite();
} }
if (async) {
writer.append(')');
}
writer.append(";").softNewLine(); writer.append(";").softNewLine();
if (statement.getLocation() != null) { if (statement.getLocation() != null) {
popLocation(); popLocation();
@ -1336,11 +1421,15 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
if (injector != null) { if (injector != null) {
injector.generate(new InjectorContextImpl(expr.getArguments()), expr.getMethod()); injector.generate(new InjectorContextImpl(expr.getArguments()), expr.getMethod());
} else { } else {
if (expr.getAsyncTarget() != null) {
writer.append("return ");
}
if (expr.getType() == InvocationType.DYNAMIC) { if (expr.getType() == InvocationType.DYNAMIC) {
expr.getArguments().get(0).acceptVisitor(this); expr.getArguments().get(0).acceptVisitor(this);
} }
String className = naming.getNameFor(expr.getMethod().getClassName()); 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()); String fullName = naming.getFullNameFor(expr.getMethod());
DeferredCallSite callSite = prevCallSite; DeferredCallSite callSite = prevCallSite;
boolean shouldEraseCallSite = lastCallSite == null; boolean shouldEraseCallSite = lastCallSite == null;
@ -1348,6 +1437,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
lastCallSite = callSite; lastCallSite = callSite;
} }
boolean virtual = false; boolean virtual = false;
boolean hasParams = false;
switch (expr.getType()) { switch (expr.getType()) {
case STATIC: case STATIC:
writer.append(fullName).append("("); writer.append(fullName).append("(");
@ -1357,18 +1447,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append(",").ws(); writer.append(",").ws();
} }
expr.getArguments().get(i).acceptVisitor(this); expr.getArguments().get(i).acceptVisitor(this);
hasParams = true;
} }
writer.append(')');
break; break;
case SPECIAL: case SPECIAL:
writer.append(fullName).append("("); writer.append(fullName).append("(");
prevCallSite = debugEmitter.emitCallSite(); prevCallSite = debugEmitter.emitCallSite();
expr.getArguments().get(0).acceptVisitor(this); expr.getArguments().get(0).acceptVisitor(this);
hasParams = true;
for (int i = 1; i < expr.getArguments().size(); ++i) { for (int i = 1; i < expr.getArguments().size(); ++i) {
writer.append(",").ws(); writer.append(",").ws();
expr.getArguments().get(i).acceptVisitor(this); expr.getArguments().get(i).acceptVisitor(this);
} }
writer.append(")");
break; break;
case DYNAMIC: case DYNAMIC:
writer.append(".").append(name).append("("); writer.append(".").append(name).append("(");
@ -1377,9 +1467,9 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
if (i > 1) { if (i > 1) {
writer.append(",").ws(); writer.append(",").ws();
} }
hasParams = true;
expr.getArguments().get(i).acceptVisitor(this); expr.getArguments().get(i).acceptVisitor(this);
} }
writer.append(')');
virtual = true; virtual = true;
break; break;
case CONSTRUCTOR: case CONSTRUCTOR:
@ -1389,11 +1479,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
if (i > 0) { if (i > 0) {
writer.append(",").ws(); writer.append(",").ws();
} }
hasParams = true;
expr.getArguments().get(i).acceptVisitor(this); expr.getArguments().get(i).acceptVisitor(this);
} }
writer.append(')');
break; break;
} }
if (expr.getAsyncTarget() != null) {
if (hasParams) {
writer.append(',').ws();
}
writer.append("$rt_continue($part_").append(expr.getAsyncTarget()).append(')');
}
writer.append(')');
if (lastCallSite != null) { if (lastCallSite != null) {
if (virtual) { if (virtual) {
lastCallSite.setVirtualMethod(expr.getMethod()); lastCallSite.setVirtualMethod(expr.getMethod());

View File

@ -37,6 +37,7 @@ class StatementGenerator implements InstructionVisitor {
Program program; Program program;
ClassHolderSource classSource; ClassHolderSource classSource;
private NodeLocation currentLocation; private NodeLocation currentLocation;
Integer asyncTarget;
public void setCurrentLocation(NodeLocation currentLocation) { public void setCurrentLocation(NodeLocation currentLocation) {
this.currentLocation = currentLocation; this.currentLocation = currentLocation;
@ -546,7 +547,7 @@ class StatementGenerator implements InstructionVisitor {
for (int i = 0; i < insn.getArguments().size(); ++i) { for (int i = 0; i < insn.getArguments().size(); ++i) {
exprArgs[i] = Expr.var(insn.getArguments().get(i).getIndex()); exprArgs[i] = Expr.var(insn.getArguments().get(i).getIndex());
} }
Expr invocationExpr; InvocationExpr invocationExpr;
if (insn.getInstance() != null) { if (insn.getInstance() != null) {
if (insn.getType() == InvocationType.VIRTUAL) { if (insn.getType() == InvocationType.VIRTUAL) {
invocationExpr = Expr.invoke(insn.getMethod(), Expr.var(insn.getInstance().getIndex()), exprArgs); invocationExpr = Expr.invoke(insn.getMethod(), Expr.var(insn.getInstance().getIndex()), exprArgs);
@ -557,6 +558,8 @@ class StatementGenerator implements InstructionVisitor {
} else { } else {
invocationExpr = Expr.invokeStatic(insn.getMethod(), exprArgs); invocationExpr = Expr.invokeStatic(insn.getMethod(), exprArgs);
} }
invocationExpr.setAsyncTarget(asyncTarget);
if (asyncTarget == null) {
if (insn.getReceiver() != null) { if (insn.getReceiver() != null) {
assign(invocationExpr, insn.getReceiver()); assign(invocationExpr, insn.getReceiver());
} else { } else {
@ -564,6 +567,11 @@ class StatementGenerator implements InstructionVisitor {
stmt.setLocation(currentLocation); stmt.setLocation(currentLocation);
statements.add(stmt); statements.add(stmt);
} }
} else {
AssignmentStatement stmt = Statement.assign(null, invocationExpr);
stmt.setLocation(currentLocation);
statements.add(stmt);
}
} }
@Override @Override

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.javascript.ast;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Alexey Andreev
*/
public class AsyncMethodCatch {
private List<Statement> handler = new ArrayList<>();
private String exceptionType;
private Integer exceptionVariable;
public List<Statement> getHandler() {
return handler;
}
public String getExceptionType() {
return exceptionType;
}
public void setExceptionType(String exceptionType) {
this.exceptionType = exceptionType;
}
public Integer getExceptionVariable() {
return exceptionVariable;
}
public void setExceptionVariable(Integer exceptionVariable) {
this.exceptionVariable = exceptionVariable;
}
}

View File

@ -0,0 +1,57 @@
/*
* 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.javascript.ast;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.teavm.model.MethodReference;
/**
*
* @author Alexey Andreev
*/
public class AsyncMethodNode extends MethodNode {
private List<AsyncMethodPart> body = new ArrayList<>();
private List<Integer> variables = new ArrayList<>();
private List<Set<String>> parameterDebugNames = new ArrayList<>();
public AsyncMethodNode(MethodReference reference) {
super(reference);
}
public List<AsyncMethodPart> getBody() {
return body;
}
public List<Integer> getVariables() {
return variables;
}
public List<Set<String>> getParameterDebugNames() {
return parameterDebugNames;
}
@Override
public void acceptVisitor(MethodNodeVisitor visitor) {
visitor.visit(this);
}
@Override
public boolean isAsync() {
return true;
}
}

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.javascript.ast;
import java.util.ArrayList;
import java.util.List;
/**
*
* @author Alexey Andreev
*/
public class AsyncMethodPart {
private Statement statement;
private Integer inputVariable;
private List<AsyncMethodCatch> catches = new ArrayList<>();
public Statement getStatement() {
return statement;
}
public void setStatement(Statement statement) {
this.statement = statement;
}
public Integer getInputVariable() {
return inputVariable;
}
public void setInputVariable(Integer inputVariable) {
this.inputVariable = inputVariable;
}
public List<AsyncMethodCatch> getCatches() {
return catches;
}
}

View File

@ -104,7 +104,7 @@ public abstract class Expr implements Cloneable {
return expr; return expr;
} }
public static Expr constructObject(MethodReference method, Expr[] arguments) { public static InvocationExpr constructObject(MethodReference method, Expr[] arguments) {
InvocationExpr expr = new InvocationExpr(); InvocationExpr expr = new InvocationExpr();
expr.setMethod(method); expr.setMethod(method);
expr.setType(InvocationType.CONSTRUCTOR); expr.setType(InvocationType.CONSTRUCTOR);
@ -119,7 +119,7 @@ public abstract class Expr implements Cloneable {
return expr; return expr;
} }
public static Expr invoke(MethodReference method, Expr target, Expr[] arguments) { public static InvocationExpr invoke(MethodReference method, Expr target, Expr[] arguments) {
InvocationExpr expr = new InvocationExpr(); InvocationExpr expr = new InvocationExpr();
expr.setMethod(method); expr.setMethod(method);
expr.setType(InvocationType.DYNAMIC); expr.setType(InvocationType.DYNAMIC);
@ -128,7 +128,7 @@ public abstract class Expr implements Cloneable {
return expr; return expr;
} }
public static Expr invokeSpecial(MethodReference method, Expr target, Expr[] arguments) { public static InvocationExpr invokeSpecial(MethodReference method, Expr target, Expr[] arguments) {
InvocationExpr expr = new InvocationExpr(); InvocationExpr expr = new InvocationExpr();
expr.setMethod(method); expr.setMethod(method);
expr.setType(InvocationType.SPECIAL); expr.setType(InvocationType.SPECIAL);
@ -137,7 +137,7 @@ public abstract class Expr implements Cloneable {
return expr; return expr;
} }
public static Expr invokeStatic(MethodReference method, Expr[] arguments) { public static InvocationExpr invokeStatic(MethodReference method, Expr[] arguments) {
InvocationExpr expr = new InvocationExpr(); InvocationExpr expr = new InvocationExpr();
expr.setMethod(method); expr.setMethod(method);
expr.setType(InvocationType.STATIC); expr.setType(InvocationType.STATIC);

View File

@ -28,6 +28,7 @@ public class InvocationExpr extends Expr {
private MethodReference method; private MethodReference method;
private InvocationType type; private InvocationType type;
private List<Expr> arguments = new ArrayList<>(); private List<Expr> arguments = new ArrayList<>();
private Integer asyncTarget;
public MethodReference getMethod() { public MethodReference getMethod() {
return method; return method;
@ -49,6 +50,14 @@ public class InvocationExpr extends Expr {
return arguments; return arguments;
} }
public Integer getAsyncTarget() {
return asyncTarget;
}
public void setAsyncTarget(Integer asyncTarget) {
this.asyncTarget = asyncTarget;
}
@Override @Override
public void acceptVisitor(ExprVisitor visitor) { public void acceptVisitor(ExprVisitor visitor) {
visitor.visit(this); visitor.visit(this);
@ -63,6 +72,7 @@ public class InvocationExpr extends Expr {
InvocationExpr copy = new InvocationExpr(); InvocationExpr copy = new InvocationExpr();
cache.put(this, copy); cache.put(this, copy);
copy.setMethod(method); copy.setMethod(method);
copy.setAsyncTarget(asyncTarget);
for (Expr arg : arguments) { for (Expr arg : arguments) {
copy.getArguments().add(arg.clone(cache)); copy.getArguments().add(arg.clone(cache));
} }

View File

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

View File

@ -22,5 +22,7 @@ package org.teavm.javascript.ast;
public interface MethodNodeVisitor { public interface MethodNodeVisitor {
void visit(RegularMethodNode methodNode); void visit(RegularMethodNode methodNode);
void visit(AsyncMethodNode methodNode);
void visit(NativeMethodNode methodNode); void visit(NativeMethodNode methodNode);
} }

View File

@ -24,6 +24,7 @@ import org.teavm.model.MethodReference;
*/ */
public class NativeMethodNode extends MethodNode { public class NativeMethodNode extends MethodNode {
private Generator generator; private Generator generator;
private boolean async;
public NativeMethodNode(MethodReference reference) { public NativeMethodNode(MethodReference reference) {
super(reference); super(reference);
@ -37,6 +38,15 @@ public class NativeMethodNode extends MethodNode {
this.generator = generator; this.generator = generator;
} }
@Override
public boolean isAsync() {
return async;
}
public void setAsync(boolean async) {
this.async = async;
}
@Override @Override
public void acceptVisitor(MethodNodeVisitor visitor) { public void acceptVisitor(MethodNodeVisitor visitor) {
visitor.visit(this); visitor.visit(this);

View File

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

View File

@ -27,6 +27,7 @@ public class TryCatchStatement extends Statement {
private List<Statement> handler = new ArrayList<>(); private List<Statement> handler = new ArrayList<>();
private String exceptionType; private String exceptionType;
private Integer exceptionVariable; private Integer exceptionVariable;
private boolean async;
public List<Statement> getProtectedBody() { public List<Statement> getProtectedBody() {
return protectedBody; return protectedBody;
@ -52,6 +53,14 @@ public class TryCatchStatement extends Statement {
this.exceptionVariable = exceptionVariable; this.exceptionVariable = exceptionVariable;
} }
public boolean isAsync() {
return async;
}
public void setAsync(boolean async) {
this.async = async;
}
@Override @Override
public void acceptVisitor(StatementVisitor visitor) { public void acceptVisitor(StatementVisitor visitor) {
visitor.visit(this); visitor.visit(this);

View File

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

View File

@ -0,0 +1,36 @@
/*
* 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;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author Alexey Andreev
*/
public class AsyncInformation {
private Set<MethodReference> syncMethods = new HashSet<>();
private Set<MethodReference> asyncMethods = new HashSet<>();
public Set<MethodReference> getSyncMethods() {
return syncMethods;
}
public Set<MethodReference> getAsyncMethods() {
return asyncMethods;
}
}

View File

@ -17,7 +17,9 @@ package org.teavm.model.instructions;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import org.teavm.model.*; import org.teavm.model.Instruction;
import org.teavm.model.MethodReference;
import org.teavm.model.Variable;
/** /**
* *

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

@ -0,0 +1,178 @@
/*
* 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.*;
import org.teavm.model.*;
import org.teavm.model.instructions.InvokeInstruction;
import org.teavm.model.instructions.JumpInstruction;
/**
*
* @author Alexey Andreev
*/
public class AsyncProgramSplitter {
private List<Part> parts = new ArrayList<>();
private Map<Long, Integer> partMap = new HashMap<>();
private Set<MethodReference> asyncMethods = new HashSet<>();
public AsyncProgramSplitter(Set<MethodReference> asyncMethods) {
this.asyncMethods = asyncMethods;
}
public void split(Program program) {
parts.clear();
Program initialProgram = createStubCopy(program);
Part initialPart = new Part();
initialPart.program = initialProgram;
initialPart.blockSuccessors = new int[program.basicBlockCount()];
Arrays.fill(initialPart.blockSuccessors, -1);
parts.add(initialPart);
partMap.put(0L, 0);
Step initialStep = new Step();
initialStep.source = 0;
initialStep.targetPart = initialPart;
Queue<Step> queue = new ArrayDeque<>();
queue.add(initialStep);
taskLoop: while (!queue.isEmpty()) {
Step step = queue.remove();
BasicBlock targetBlock = step.targetPart.program.basicBlockAt(step.source);
if (targetBlock.instructionCount() > 0) {
continue;
}
BasicBlock sourceBlock = program.basicBlockAt(step.source);
int last = 0;
for (int i = 0; i < sourceBlock.getInstructions().size(); ++i) {
Instruction insn = sourceBlock.getInstructions().get(i);
if (insn instanceof InvokeInstruction) {
InvokeInstruction invoke = (InvokeInstruction)insn;
if (!asyncMethods.contains(invoke.getMethod())) {
continue;
}
// If we met asynchronous invocation...
// Copy portion of current block from last occurence (or from start) to i'th instruction.
targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock,
last, i + 1, targetBlock.getProgram()));
ProgramUtils.copyTryCatches(sourceBlock, targetBlock.getProgram());
for (TryCatchBlock tryCatch : targetBlock.getTryCatchBlocks()) {
if (tryCatch.getHandler() != null) {
Step next = new Step();
next.source = tryCatch.getHandler().getIndex();
next.targetPart = step.targetPart;
queue.add(next);
}
}
last = i + 1;
// If this instruction already separates program, end with current block and refer to the
// existing part
long key = ((long)step.source << 32) | i;
if (partMap.containsKey(key)) {
step.targetPart.blockSuccessors[targetBlock.getIndex()] = partMap.get(key);
continue taskLoop;
}
// Create a new part
Program nextProgram = createStubCopy(program);
Part part = new Part();
part.input = invoke.getReceiver() != null ? invoke.getReceiver().getIndex() : null;
part.program = nextProgram;
int partId = parts.size();
parts.add(part);
part.blockSuccessors = new int[program.basicBlockCount() + 1];
Arrays.fill(part.blockSuccessors, -1);
// Mark current instruction as a separator and remember which part is in charge.
partMap.put(key, partId);
step.targetPart.blockSuccessors[targetBlock.getIndex()] = partId;
// Continue with a new block in the new part
targetBlock = nextProgram.createBasicBlock();
if (step.source > 0) {
JumpInstruction jumpToNextBlock = new JumpInstruction();
jumpToNextBlock.setTarget(targetBlock);
nextProgram.basicBlockAt(0).getInstructions().add(jumpToNextBlock);
}
step.targetPart = part;
}
}
targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock,
last, sourceBlock.getInstructions().size(), targetBlock.getProgram()));
for (TryCatchBlock tryCatch : targetBlock.getTryCatchBlocks()) {
if (tryCatch.getHandler() != null) {
Step next = new Step();
next.source = tryCatch.getHandler().getIndex();
next.targetPart = step.targetPart;
queue.add(next);
}
}
InstructionTransitionExtractor successorExtractor = new InstructionTransitionExtractor();
sourceBlock.getLastInstruction().acceptVisitor(successorExtractor);
for (BasicBlock successor : successorExtractor.getTargets()) {
BasicBlock targetSuccessor = targetBlock.getProgram().basicBlockAt(successor.getIndex());
if (targetSuccessor.instructionCount() == 0) {
Step next = new Step();
next.source = successor.getIndex();
next.targetPart = step.targetPart;
queue.add(next);
}
}
}
partMap.clear();
}
private Program createStubCopy(Program program) {
Program copy = new Program();
for (int i = 0; i < program.basicBlockCount(); ++i) {
copy.createBasicBlock();
}
for (int i = 0; i < program.variableCount(); ++i) {
copy.createVariable();
}
return copy;
}
public int size() {
return parts.size();
}
public Program getProgram(int index) {
return parts.get(index).program;
}
public Integer getInput(int index) {
return parts.get(index).input;
}
public int[] getBlockSuccessors(int index) {
int[] result = parts.get(index).blockSuccessors;
return Arrays.copyOf(result, result.length);
}
private static class Part {
Program program;
Integer input;
int[] blockSuccessors;
}
private static class Step {
Part targetPart;
int source;
}
}

View File

@ -92,21 +92,8 @@ public final class ProgramUtils {
for (int i = 0; i < program.basicBlockCount(); ++i) { for (int i = 0; i < program.basicBlockCount(); ++i) {
BasicBlockReader block = program.basicBlockAt(i); BasicBlockReader block = program.basicBlockAt(i);
BasicBlock blockCopy = copy.basicBlockAt(i); BasicBlock blockCopy = copy.basicBlockAt(i);
for (int j = 0; j < block.instructionCount(); ++j) { blockCopy.getInstructions().addAll(copyInstructions(block, 0, block.instructionCount(), copy));
block.readInstruction(j, insnCopier); blockCopy.getPhis().addAll(copyPhis(block, copy));
blockCopy.getInstructions().add(insnCopier.copy);
}
for (PhiReader phi : block.readPhis()) {
Phi phiCopy = new Phi();
phiCopy.setReceiver(copy.variableAt(phi.getReceiver().getIndex()));
for (IncomingReader incoming : phi.readIncomings()) {
Incoming incomingCopy = new Incoming();
incomingCopy.setSource(copy.basicBlockAt(incoming.getSource().getIndex()));
incomingCopy.setValue(copy.variableAt(incoming.getValue().getIndex()));
phiCopy.getIncomings().add(incomingCopy);
}
blockCopy.getPhis().add(phiCopy);
}
for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) { for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) {
TryCatchBlock tryCatchCopy = new TryCatchBlock(); TryCatchBlock tryCatchCopy = new TryCatchBlock();
tryCatchCopy.setExceptionType(tryCatch.getExceptionType()); tryCatchCopy.setExceptionType(tryCatch.getExceptionType());
@ -118,6 +105,46 @@ public final class ProgramUtils {
return copy; return copy;
} }
public static List<Instruction> copyInstructions(BasicBlockReader block, int from, int to, Program target) {
List<Instruction> result = new ArrayList<>();
InstructionCopyReader copyReader = new InstructionCopyReader();
copyReader.programCopy = target;
for (int i = from; i < to; ++i) {
block.readInstruction(i, copyReader);
copyReader.copy.setLocation(copyReader.location);
result.add(copyReader.copy);
}
return result;
}
public static List<Phi> copyPhis(BasicBlockReader block, Program target) {
List<Phi> result = new ArrayList<>();
for (PhiReader phi : block.readPhis()) {
Phi phiCopy = new Phi();
phiCopy.setReceiver(target.variableAt(phi.getReceiver().getIndex()));
for (IncomingReader incoming : phi.readIncomings()) {
Incoming incomingCopy = new Incoming();
incomingCopy.setSource(target.basicBlockAt(incoming.getSource().getIndex()));
incomingCopy.setValue(target.variableAt(incoming.getValue().getIndex()));
phiCopy.getIncomings().add(incomingCopy);
}
result.add(phiCopy);
}
return result;
}
public static List<TryCatchBlock> copyTryCatches(BasicBlockReader block, Program target) {
List<TryCatchBlock> result = new ArrayList<>();
for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) {
TryCatchBlock tryCatchCopy = new TryCatchBlock();
tryCatchCopy.setExceptionType(tryCatch.getExceptionType());
tryCatchCopy.setExceptionVariable(target.variableAt(tryCatch.getExceptionVariable().getIndex()));
tryCatchCopy.setHandler(target.basicBlockAt(tryCatch.getHandler().getIndex()));
result.add(tryCatchCopy);
}
return result;
}
private static class InstructionCopyReader implements InstructionReader { private static class InstructionCopyReader implements InstructionReader {
Instruction copy; Instruction copy;
Program programCopy; Program programCopy;

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
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.CONSTRUCTOR, ElementType.METHOD })
public @interface Async {
}

View File

@ -0,0 +1,26 @@
/*
* 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;
/**
*
* @author Alexey Andreev
*/
public interface AsyncCallback<T> {
void complete(T value);
void error(Exception e);
}

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

@ -436,7 +436,8 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
renderer.renderStringPool(); renderer.renderStringPool();
for (Map.Entry<String, TeaVMEntryPoint> entry : entryPoints.entrySet()) { for (Map.Entry<String, TeaVMEntryPoint> entry : entryPoints.entrySet()) {
sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws() sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws()
.appendMethodBody(entry.getValue().reference).append(";").softNewLine(); .append("$rt_rootInvocationAdapter(")
.appendMethodBody(entry.getValue().reference).append(");").softNewLine();
} }
for (Map.Entry<String, String> entry : exportedClasses.entrySet()) { for (Map.Entry<String, String> entry : exportedClasses.entrySet()) {
sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws() sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws()
@ -526,8 +527,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
} }
private List<ClassNode> modelToAst(ListableClassHolderSource classes) { private List<ClassNode> modelToAst(ListableClassHolderSource classes) {
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(dependencyChecker.getCallGraph(), diagnostics);
asyncFinder.find(classes);
progressListener.phaseStarted(TeaVMPhase.DECOMPILATION, classes.getClassNames().size()); progressListener.phaseStarted(TeaVMPhase.DECOMPILATION, classes.getClassNames().size());
Decompiler decompiler = new Decompiler(classes, classLoader); Decompiler decompiler = new Decompiler(classes, classLoader, asyncFinder.getAsyncMethods());
decompiler.setRegularMethodCache(incremental ? astCache : null); decompiler.setRegularMethodCache(incremental ? astCache : null);
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) { for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {

View File

@ -392,6 +392,28 @@ 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);
}
}
function $rt_rootInvocationAdapter(f) {
return function() {
var args = Array.prototype.slice.apply(arguments);
args.push(function() {});
args.push(function() {});
return f.apply(this, args);
}
}
var $rt_stringPool_instance; var $rt_stringPool_instance;
function $rt_stringPool(strings) { function $rt_stringPool(strings) {
$rt_stringPool_instance = new Array(strings.length); $rt_stringPool_instance = new Array(strings.length);
@ -402,6 +424,21 @@ function $rt_stringPool(strings) {
function $rt_s(index) { function $rt_s(index) {
return $rt_stringPool_instance[index]; return $rt_stringPool_instance[index];
} }
var $rt_continueCounter = 0;
function $rt_continue(f) {
if ($rt_continueCounter++ == 10) {
$rt_continueCounter = 0;
return function() {
var self = this;
var args = arguments;
setTimeout(function() {
f.apply(self, args);
}, 0);
};
} else {
return f;
}
}
function $dbg_repr(obj) { function $dbg_repr(obj) {
return obj.toString ? obj.toString() : ""; return obj.toString ? obj.toString() : "";

View File

@ -34,5 +34,6 @@
<module>teavm-samples-benchmark</module> <module>teavm-samples-benchmark</module>
<module>teavm-samples-storage</module> <module>teavm-samples-storage</module>
<module>teavm-samples-video</module> <module>teavm-samples-video</module>
<module>teavm-samples-async</module>
</modules> </modules>
</project> </project>

View File

@ -0,0 +1,4 @@
/target
/.settings
/.classpath
/.project

View File

@ -0,0 +1,93 @@
<!--
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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.teavm</groupId>
<artifactId>teavm-samples</artifactId>
<version>0.3.0-SNAPSHOT</version>
</parent>
<artifactId>teavm-samples-async</artifactId>
<packaging>war</packaging>
<name>TeaVM CPS demo</name>
<description>TeaVM application that demonstrates continuation-passing style generator</description>
<dependencies>
<dependency>
<groupId>org.teavm</groupId>
<artifactId>teavm-classlib</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<webResources>
<resource>
<directory>${project.build.directory}/generated/js</directory>
</resource>
</webResources>
</configuration>
</plugin>
<plugin>
<groupId>org.teavm</groupId>
<artifactId>teavm-maven-plugin</artifactId>
<version>${project.version}</version>
<executions>
<execution>
<id>web-client</id>
<phase>prepare-package</phase>
<goals>
<goal>build-javascript</goal>
</goals>
<configuration>
<targetDirectory>${project.build.directory}/generated/js/teavm</targetDirectory>
<mainClass>org.teavm.samples.async.AsyncProgram</mainClass>
<runtime>SEPARATE</runtime>
<minifying>false</minifying>
<debugInformationGenerated>true</debugInformationGenerated>
<sourceMapsGenerated>true</sourceMapsGenerated>
<sourceFilesCopied>true</sourceFilesCopied>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<configuration>
<configLocation>../../checkstyle.xml</configLocation>
<propertyExpansion>config_loc=${basedir}/../..</propertyExpansion>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,59 @@
/*
* 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.samples.async;
/**
*
* @author Alexey Andreev <konsoletyper@gmail.com>
*/
public final class AsyncProgram {
private AsyncProgram() {
}
public static void main(String[] args) throws InterruptedException {
withoutAsync();
System.out.println();
withAsync();
}
private static void withoutAsync() {
System.out.println("Start sync");
for (int i = 0; i < 20; ++i) {
for (int j = 0; j <= i; ++j) {
System.out.print(j);
System.out.print(' ');
}
System.out.println();
}
System.out.println("Complete sync");
}
private static void withAsync() throws InterruptedException {
System.out.println("Start async");
for (int i = 0; i < 20; ++i) {
for (int j = 0; j <= i; ++j) {
System.out.print(j);
System.out.print(' ');
}
System.out.println();
if (i % 3 == 0) {
System.out.println("Suspend for a second");
Thread.sleep(1000);
}
}
System.out.println("Complete async");
}
}

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
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.
-->
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
</web-app>

View File

@ -0,0 +1,27 @@
<!--
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.
-->
<!DOCTYPE html>
<html>
<head>
<title>Continuation-passing style demo</title>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8">
<script type="text/javascript" charset="utf-8" src="teavm/runtime.js"></script>
<script type="text/javascript" charset="utf-8" src="teavm/classes.js"></script>
</head>
<body onload="main()">
<p>Please, open developer's console to view program's output</p>
</body>
</html>