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;
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("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
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

@ -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.PreserveOriginalName;
import org.teavm.model.*;
import org.teavm.model.util.AsyncProgramSplitter;
import org.teavm.model.util.ProgramUtils;
/**
@ -45,10 +46,12 @@ public class Decompiler {
private Map<MethodReference, Generator> generators = new HashMap<>();
private Set<MethodReference> methodsToPass = new HashSet<>();
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.classLoader = classLoader;
this.asyncMethods = asyncMethods;
}
public RegularMethodNodeCache getRegularMethodCache() {
@ -154,7 +157,7 @@ public class Decompiler {
public MethodNode decompile(MethodHolder method) {
return method.getModifiers().contains(ElementModifier.NATIVE) ? decompileNative(method) :
decompileRegular(method);
!asyncMethods.contains(method.getReference()) ? decompileRegular(method) : decompileAsync(method);
}
public NativeMethodNode decompileNative(MethodHolder method) {
@ -179,6 +182,7 @@ public class Decompiler {
method.getDescriptor()));
methodNode.getModifiers().addAll(mapModifiers(method.getModifiers()));
methodNode.setGenerator(generator);
methodNode.setAsync(asyncMethods.contains(method.getReference()));
return methodNode;
}
@ -194,14 +198,58 @@ public class Decompiler {
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) {
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;
graph = ProgramUtils.buildControlFlowGraph(method.getProgram());
graph = ProgramUtils.buildControlFlowGraph(program);
indexer = new GraphIndexer(graph);
graph = indexer.getGraph();
loopGraph = new LoopGraph(this.graph);
unflatCode();
Program program = method.getProgram();
blockMap = new Block[program.basicBlockCount() * 2 + 1];
Deque<Block> stack = new ArrayDeque<>();
BlockStatement rootStmt = new BlockStatement();
@ -247,9 +295,13 @@ public class Decompiler {
int tmp = indexer.nodeAt(next);
generator.nextBlock = tmp >= 0 && next < indexer.size() ? program.basicBlockAt(tmp) : null;
generator.statements.clear();
generator.asyncTarget = null;
InstructionLocation lastLocation = 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()) {
lastLocation = insn.getLocation();
nodeLocation = new NodeLocation(lastLocation.getFileName(), lastLocation.getLine());
@ -257,41 +309,51 @@ public class Decompiler {
if (insn.getLocation() != null) {
generator.setCurrentLocation(nodeLocation);
}
if (targetBlocks[node] >= 0 && j == instructions.size() - 1) {
generator.asyncTarget = targetBlocks[node];
asyncInvocation = true;
}
insn.acceptVisitor(generator);
}
boolean hasAsyncCatch = false;
for (TryCatchBlock tryCatch : generator.currentBlock.getTryCatchBlocks()) {
TryCatchStatement tryCatchStmt = new TryCatchStatement();
tryCatchStmt.setExceptionType(tryCatch.getExceptionType());
tryCatchStmt.setExceptionVariable(tryCatch.getExceptionVariable().getIndex());
tryCatchStmt.getProtectedBody().addAll(generator.statements);
generator.statements.clear();
generator.statements.add(tryCatchStmt);
Statement handlerStmt = generator.generateJumpStatement(tryCatch.getHandler());
if (handlerStmt != null) {
tryCatchStmt.getHandler().add(handlerStmt);
if (asyncInvocation) {
TryCatchStatement tryCatchStmt = new TryCatchStatement();
tryCatchStmt.setExceptionType(tryCatch.getExceptionType());
tryCatchStmt.setExceptionVariable(tryCatch.getExceptionVariable().getIndex());
tryCatchStmt.getProtectedBody().addAll(generator.statements);
generator.statements.clear();
generator.statements.add(tryCatchStmt);
Statement handlerStmt = generator.generateJumpStatement(tryCatch.getHandler());
if (handlerStmt != null) {
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);
}
}
SequentialStatement result = new SequentialStatement();
result.getSequence().addAll(rootStmt.getBody());
MethodReference reference = new MethodReference(method.getOwnerName(), method.getDescriptor());
RegularMethodNode methodNode = new RegularMethodNode(reference);
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;
SequentialStatement resultBody = new SequentialStatement();
resultBody.getSequence().addAll(rootStmt.getBody());
result.setStatement(resultBody);
return result;
}
private Set<NodeModifier> mapModifiers(Set<ElementModifier> modifiers) {

View File

@ -15,6 +15,8 @@
*/
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.model.Program;
@ -40,4 +42,27 @@ public class Optimizer {
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) {
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

@ -53,6 +53,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
private Deque<LocationStackEntry> locationStack = new ArrayDeque<>();
private DeferredCallSite lastCallSite;
private DeferredCallSite prevCallSite;
private boolean async;
private static class InjectorHolder {
public final Injector injector;
@ -469,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();
}
@ -518,6 +525,12 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
}
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();
method.acceptVisitor(new MethodBodyRenderer());
writer.outdent().append("}").newLine();
@ -525,9 +538,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);
@ -537,6 +554,8 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
@Override
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]),
@ -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
public String getParameterName(int index) {
return variableName(index);
@ -596,6 +660,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) {
@ -832,12 +911,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
pushLocation(statement.getLocation());
}
writer.append("return");
if (async) {
writer.append(" $return(");
}
if (statement.getResult() != null) {
writer.append(' ');
prevCallSite = debugEmitter.emitCallSite();
statement.getResult().acceptVisitor(this);
debugEmitter.emitCallSite();
}
if (async) {
writer.append(')');
}
writer.append(";").softNewLine();
if (statement.getLocation() != null) {
popLocation();
@ -1336,11 +1421,15 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
if (injector != null) {
injector.generate(new InjectorContextImpl(expr.getArguments()), expr.getMethod());
} else {
if (expr.getAsyncTarget() != null) {
writer.append("return ");
}
if (expr.getType() == InvocationType.DYNAMIC) {
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;
@ -1348,6 +1437,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
lastCallSite = callSite;
}
boolean virtual = false;
boolean hasParams = false;
switch (expr.getType()) {
case STATIC:
writer.append(fullName).append("(");
@ -1357,18 +1447,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append(",").ws();
}
expr.getArguments().get(i).acceptVisitor(this);
hasParams = true;
}
writer.append(')');
break;
case SPECIAL:
writer.append(fullName).append("(");
prevCallSite = debugEmitter.emitCallSite();
expr.getArguments().get(0).acceptVisitor(this);
hasParams = true;
for (int i = 1; i < expr.getArguments().size(); ++i) {
writer.append(",").ws();
expr.getArguments().get(i).acceptVisitor(this);
}
writer.append(")");
break;
case DYNAMIC:
writer.append(".").append(name).append("(");
@ -1377,9 +1467,9 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
if (i > 1) {
writer.append(",").ws();
}
hasParams = true;
expr.getArguments().get(i).acceptVisitor(this);
}
writer.append(')');
virtual = true;
break;
case CONSTRUCTOR:
@ -1389,11 +1479,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
if (i > 0) {
writer.append(",").ws();
}
hasParams = true;
expr.getArguments().get(i).acceptVisitor(this);
}
writer.append(')');
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 (virtual) {
lastCallSite.setVirtualMethod(expr.getMethod());

View File

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

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;
}
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);
@ -119,7 +119,7 @@ public abstract class Expr implements Cloneable {
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();
expr.setMethod(method);
expr.setType(InvocationType.DYNAMIC);
@ -128,7 +128,7 @@ public abstract class Expr implements Cloneable {
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();
expr.setMethod(method);
expr.setType(InvocationType.SPECIAL);
@ -137,7 +137,7 @@ public abstract class Expr implements Cloneable {
return expr;
}
public static Expr invokeStatic(MethodReference method, Expr[] arguments) {
public static InvocationExpr invokeStatic(MethodReference method, Expr[] arguments) {
InvocationExpr expr = new InvocationExpr();
expr.setMethod(method);
expr.setType(InvocationType.STATIC);

View File

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

View File

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

View File

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

View File

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

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

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

View File

@ -31,4 +31,10 @@ public interface GeneratorContext extends ServiceRepository {
ClassLoader getClassLoader();
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.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) {
BasicBlockReader block = program.basicBlockAt(i);
BasicBlock blockCopy = copy.basicBlockAt(i);
for (int j = 0; j < block.instructionCount(); ++j) {
block.readInstruction(j, insnCopier);
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);
}
blockCopy.getInstructions().addAll(copyInstructions(block, 0, block.instructionCount(), copy));
blockCopy.getPhis().addAll(copyPhis(block, copy));
for (TryCatchBlockReader tryCatch : block.readTryCatchBlocks()) {
TryCatchBlock tryCatchCopy = new TryCatchBlock();
tryCatchCopy.setExceptionType(tryCatch.getExceptionType());
@ -118,6 +105,46 @@ public final class ProgramUtils {
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 {
Instruction copy;
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();
for (Map.Entry<String, TeaVMEntryPoint> entry : entryPoints.entrySet()) {
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()) {
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) {
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(dependencyChecker.getCallGraph(), diagnostics);
asyncFinder.find(classes);
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);
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;
function $rt_stringPool(strings) {
$rt_stringPool_instance = new Array(strings.length);
@ -402,6 +424,21 @@ function $rt_stringPool(strings) {
function $rt_s(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) {
return obj.toString ? obj.toString() : "";

View File

@ -34,5 +34,6 @@
<module>teavm-samples-benchmark</module>
<module>teavm-samples-storage</module>
<module>teavm-samples-video</module>
<module>teavm-samples-async</module>
</modules>
</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>