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

This commit is contained in:
Steve Hannah 2015-02-06 13:17:43 -08:00
commit d84889798c
31 changed files with 379 additions and 872 deletions

View File

@ -59,13 +59,13 @@ public class ThreadNativeGenerator implements Generator, DependencyPlugin {
private void generateSleep(GeneratorContext context, SourceWriter writer) throws IOException {
writer.append("setTimeout(function() {").indent().softNewLine();
writer.append(context.getCompleteContinuation()).append("();").softNewLine();
writer.append(context.getCompleteContinuation()).append("($rt_asyncResult(null));").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.append(context.getCompleteContinuation()).append("($rt_asyncResult(null));").softNewLine();
writer.outdent().append("},").ws().append("0);").softNewLine();
}

View File

@ -0,0 +1,76 @@
/*
* 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 static org.junit.Assert.*;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
/**
*
* @author Alexey Andreev
*/
public class ThreadTest {
@Test
public void sleeps() throws InterruptedException {
long start = System.currentTimeMillis();
Thread.sleep(100);
long duration = System.currentTimeMillis() - start;
assertTrue("Thread.sleed did not wait enogh", duration >= 100);
}
@Test
public void catchesAsyncException() {
try {
throwException();
fail("Exception should have been thrown");
} catch (IllegalStateException e) {
// all is ok
}
}
private void throwException() {
Thread.yield();
throw new IllegalStateException();
}
@Test
public void asyncVirtualCallsSupported() {
List<A> alist = new ArrayList<>();
alist.add(new A() {
@Override int foo() {
return 3;
}
});
alist.add(new A() {
@Override int foo() {
Thread.yield();
return 5;
}
});
int sum = 0;
for (A a : alist) {
sum += a.foo();
}
assertEquals(8, sum);
}
abstract class A {
abstract int foo();
}
}

View File

@ -306,6 +306,16 @@ public class AstIO {
}
}
@Override
public void visit(RestoreAsyncStatement statement) {
try {
output.writeByte(17);
output.writeShort(statement.getReceiver() != null ? statement.getReceiver() : -1);
} catch (IOException e) {
throw new IOExceptionWrapper(e);
}
}
@Override
public void visit(BinaryExpr expr) {
try {
@ -651,6 +661,12 @@ public class AstIO {
readSequence(input, stmt.getHandler());
return stmt;
}
case 17: {
short var = input.readShort();
RestoreAsyncStatement stmt = new RestoreAsyncStatement();
stmt.setReceiver(var >= 0 ? (int)var : null);
return stmt;
}
default:
throw new RuntimeException("Unexpected statement type: " + type);
}

View File

@ -257,6 +257,10 @@ public class DiskRegularMethodNodeCache implements RegularMethodNodeCache {
@Override
public void visit(StaticClassExpr expr) {
}
@Override
public void visit(RestoreAsyncStatement statement) {
}
}
static class Item {

View File

@ -112,4 +112,8 @@ class BreakToContinueReplacer implements StatementVisitor {
visitSequence(statement.getProtectedBody());
visitSequence(statement.getHandler());
}
@Override
public void visit(RestoreAsyncStatement statement) {
}
}

View File

@ -107,4 +107,8 @@ class CertainBlockCountVisitor implements StatementVisitor {
visit(statement.getProtectedBody());
visit(statement.getHandler());
}
@Override
public void visit(RestoreAsyncStatement statement) {
}
}

View File

@ -202,19 +202,24 @@ public class Decompiler {
AsyncMethodNode node = new AsyncMethodNode(method.getReference());
AsyncProgramSplitter splitter = new AsyncProgramSplitter(asyncMethods);
splitter.split(method.getProgram());
List<Program> partPrograms = new ArrayList<>();
for (int i = 0; i < splitter.size(); ++i) {
AsyncMethodPart part = getRegularMethodStatement(splitter.getProgram(i), splitter.getBlockSuccessors(i));
part.setInputVariable(splitter.getInput(i));
Integer input = null;
if (i > 0) {
input = splitter.getInput(i);
if (input == null) {
input = -1;
}
}
AsyncMethodPart part = getRegularMethodStatement(splitter.getProgram(i), splitter.getBlockSuccessors(i),
input);
node.getBody().add(part);
partPrograms.add(splitter.getProgram(i));
}
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, partPrograms);
optimizer.optimize(node, program, splitter);
node.getModifiers().addAll(mapModifiers(method.getModifiers()));
int paramCount = Math.min(method.getSignature().length, program.variableCount());
for (int i = 0; i < paramCount; ++i) {
@ -229,7 +234,7 @@ public class Decompiler {
Program program = method.getProgram();
int[] targetBlocks = new int[program.basicBlockCount()];
Arrays.fill(targetBlocks, -1);
methodNode.setBody(getRegularMethodStatement(program, targetBlocks).getStatement());
methodNode.setBody(getRegularMethodStatement(program, targetBlocks, null).getStatement());
for (int i = 0; i < program.variableCount(); ++i) {
methodNode.getVariables().add(program.variableAt(i).getRegister());
}
@ -244,7 +249,7 @@ public class Decompiler {
return methodNode;
}
private AsyncMethodPart getRegularMethodStatement(Program program, int[] targetBlocks) {
private AsyncMethodPart getRegularMethodStatement(Program program, int[] targetBlocks, Integer inputVar) {
AsyncMethodPart result = new AsyncMethodPart();
lastBlockId = 1;
graph = ProgramUtils.buildControlFlowGraph(program);
@ -297,11 +302,15 @@ public class Decompiler {
int tmp = indexer.nodeAt(next);
generator.nextBlock = tmp >= 0 && next < indexer.size() ? program.basicBlockAt(tmp) : null;
generator.statements.clear();
if (node == 0 && inputVar != null) {
RestoreAsyncStatement restoreStmt = new RestoreAsyncStatement();
restoreStmt.setReceiver(inputVar >= 0 ? inputVar : null);
generator.statements.add(restoreStmt);
}
generator.asyncTarget = null;
InstructionLocation lastLocation = null;
NodeLocation nodeLocation = null;
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()) {
@ -313,41 +322,20 @@ public class Decompiler {
}
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()) {
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);
TryCatchStatement tryCatchStmt = new TryCatchStatement();
tryCatchStmt.setExceptionType(tryCatch.getExceptionType());
tryCatchStmt.setExceptionVariable(tryCatch.getExceptionVariable().getIndex());
tryCatchStmt.getProtectedBody().addAll(generator.statements);
generator.statements.clear();
generator.statements.add(guardTryCatch);
generator.statements.add(tryCatchStmt);
Statement handlerStmt = generator.generateJumpStatement(tryCatch.getHandler());
if (handlerStmt != null) {
tryCatchStmt.getHandler().add(handlerStmt);
}
}
block.body.addAll(generator.statements);
}

View File

@ -15,12 +15,11 @@
*/
package org.teavm.javascript;
import java.util.List;
import org.teavm.javascript.ast.AsyncMethodNode;
import org.teavm.javascript.ast.AsyncMethodPart;
import org.teavm.javascript.ast.RegularMethodNode;
import org.teavm.model.Program;
import org.teavm.model.util.AsyncProgramSplitter;
/**
*
@ -44,13 +43,17 @@ public class Optimizer {
}
}
public void optimize(AsyncMethodNode method, List<Program> programs) {
public void optimize(AsyncMethodNode method, Program program, AsyncProgramSplitter splitter) {
ReadWriteStatsBuilder stats = new ReadWriteStatsBuilder(method.getVariables().size());
for (Program program : programs) {
stats.analyze(program);
stats.analyze(program);
for (int i = 0; i < splitter.size(); ++i) {
Integer var = splitter.getInput(i);
if (var != null) {
stats.reads[var]++;
}
}
OptimizingVisitor optimizer = new OptimizingVisitor(stats);
for (AsyncMethodPart part : method.getBody()) {
OptimizingVisitor optimizer = new OptimizingVisitor(stats.copy());
part.getStatement().acceptVisitor(optimizer);
part.setStatement(optimizer.resultStmt);
}

View File

@ -289,6 +289,7 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
List<Statement> backup = resultSequence;
resultSequence = new ArrayList<>();
processSequenceImpl(statements);
wieldTryCatch(resultSequence);
List<Statement> result = new ArrayList<>();
for (Statement part : resultSequence) {
if (part != null) {
@ -324,6 +325,41 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
return true;
}
private void wieldTryCatch(List<Statement> statements) {
for (int i = 0; i < statements.size() - 1; ++i) {
if (statements.get(i) instanceof TryCatchStatement && statements.get(i + 1) instanceof TryCatchStatement) {
TryCatchStatement first = (TryCatchStatement)statements.get(i);
TryCatchStatement second = (TryCatchStatement)statements.get(i + 1);
if (Objects.equals(first.getExceptionType(), second.getExceptionType()) &&
Objects.equals(first.getExceptionVariable(), second.getExceptionVariable()) &&
briefStatementComparison(first.getHandler(), second.getHandler())) {
first.getProtectedBody().addAll(second.getProtectedBody());
statements.remove(i + 1);
wieldTryCatch(first.getProtectedBody());
--i;
continue;
}
}
}
}
private boolean briefStatementComparison(List<Statement> firstSeq, List<Statement> secondSeq) {
if (firstSeq.isEmpty() && secondSeq.isEmpty()) {
return true;
}
if (firstSeq.size() != 1 || secondSeq.size() != 1) {
return false;
}
Statement first = firstSeq.get(0);
Statement second = secondSeq.get(0);
if (first instanceof BreakStatement && second instanceof BreakStatement) {
BreakStatement firstBreak = (BreakStatement)first;
BreakStatement secondBreak = (BreakStatement)second;
return firstBreak.getTarget() == secondBreak.getTarget();
}
return false;
}
private void eliminateRedundantBreaks(List<Statement> statements, IdentifiedStatement exit) {
if (statements.isEmpty()) {
return;
@ -577,4 +613,9 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
statement.getHandler().addAll(statements);
resultStmt = statement;
}
@Override
public void visit(RestoreAsyncStatement statement) {
resultStmt = statement;
}
}

View File

@ -15,6 +15,7 @@
*/
package org.teavm.javascript;
import java.util.Arrays;
import org.teavm.model.*;
import org.teavm.model.util.DefinitionExtractor;
import org.teavm.model.util.UsageExtractor;
@ -27,11 +28,21 @@ class ReadWriteStatsBuilder {
public int[] reads;
public int[] writes;
private ReadWriteStatsBuilder() {
}
public ReadWriteStatsBuilder(int variableCount) {
reads = new int[variableCount];
writes = new int[variableCount];
}
public ReadWriteStatsBuilder copy() {
ReadWriteStatsBuilder result = new ReadWriteStatsBuilder();
result.reads = Arrays.copyOf(reads, reads.length);
result.writes = Arrays.copyOf(writes, writes.length);
return result;
}
public void analyze(Program program) {
DefinitionExtractor defExtractor = new DefinitionExtractor();
UsageExtractor useExtractor = new UsageExtractor();

View File

@ -117,4 +117,8 @@ class RedundantLabelEliminator implements StatementVisitor {
visitSequence(statement.getProtectedBody());
visitSequence(statement.getHandler());
}
@Override
public void visit(RestoreAsyncStatement statement) {
}
}

View File

@ -111,4 +111,8 @@ class ReferenceCountingVisitor implements StatementVisitor {
part.acceptVisitor(this);
}
}
@Override
public void visit(RestoreAsyncStatement statement) {
}
}

View File

@ -529,11 +529,15 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
if (startParam < ref.parameterCount() + 1) {
writer.append(',').ws();
}
writer.append("$return,").ws().append("$throw");
writer.append("$return");
}
writer.append(")").ws().append("{").softNewLine().indent();
method.acceptVisitor(new MethodBodyRenderer());
writer.outdent().append("}").newLine();
writer.outdent().append("}");
if (inner) {
writer.append(';');
}
writer.newLine();
debugEmitter.emitMethod(null);
}
@ -609,6 +613,16 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
for (int i = ref.parameterCount() + 1; i < variableCount; ++i) {
variableNames.add(variableName(i));
}
TryCatchFinder tryCatchFinder = new TryCatchFinder();
for (AsyncMethodPart part : methodNode.getBody()) {
if (!tryCatchFinder.tryCatchFound) {
part.getStatement().acceptVisitor(tryCatchFinder);
}
}
boolean hasTryCatch = tryCatchFinder.tryCatchFound;
if (hasTryCatch) {
variableNames.add("$je");
}
if (!variableNames.isEmpty()) {
writer.append("var ");
for (int i = 0; i < variableNames.size(); ++i) {
@ -620,15 +634,14 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
writer.append(";").softNewLine();
}
for (int i = 0; i < methodNode.getBody().size(); ++i) {
writer.append("function $part_").append(i).append("($input)").ws().append('{')
.indent().softNewLine();
AsyncMethodPart part = methodNode.getBody().get(i);
if (part.getInputVariable() != null) {
writer.append(variableName(part.getInputVariable())).ws().append('=').ws().append("$input;")
.softNewLine();
writer.append("var $part_").append(i).ws().append("=").ws().append("$rt_guardAsync(function(");
if (i > 0) {
writer.append("$restore");
}
writer.append(")").ws().append("{").indent().softNewLine();
AsyncMethodPart part = methodNode.getBody().get(i);
part.getStatement().acceptVisitor(Renderer.this);
writer.outdent().append('}').softNewLine();
writer.outdent().append("},").ws().append("$return);").softNewLine();
}
writer.append("return $part_0();").softNewLine();
} catch (IOException e) {
@ -666,11 +679,6 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
return async;
}
@Override
public String getErrorContinuation() {
return "$throw";
}
@Override
public String getCompleteContinuation() {
return "$return";
@ -912,16 +920,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
}
writer.append("return");
if (async) {
writer.append(" $return(");
writer.append(" $return($rt_asyncResult(");
}
if (statement.getResult() != null) {
writer.append(' ');
if (!async) {
writer.append(' ');
}
prevCallSite = debugEmitter.emitCallSite();
statement.getResult().acceptVisitor(this);
debugEmitter.emitCallSite();
}
if (async) {
writer.append(')');
writer.append("))");
}
writer.append(";").softNewLine();
if (statement.getLocation() != null) {
@ -1754,6 +1764,18 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
}
}
@Override
public void visit(RestoreAsyncStatement statement) {
try {
if (statement.getReceiver() != null) {
writer.append(variableName(statement.getReceiver())).ws().append('=').ws();
}
writer.append("$restore();").softNewLine();
} catch (IOException e) {
throw new RenderingException("IO error occured", e);
}
}
private Injector getInjector(MethodReference ref) {
InjectorHolder holder = injectorMap.get(ref);
if (holder == null) {

View File

@ -110,4 +110,8 @@ class TryCatchFinder implements StatementVisitor {
public void visit(TryCatchStatement statement) {
tryCatchFound = true;
}
@Override
public void visit(RestoreAsyncStatement statement) {
}
}

View File

@ -224,4 +224,8 @@ class UnusedVariableEliminator implements ExprVisitor, StatementVisitor {
}
}
}
@Override
public void visit(RestoreAsyncStatement statement) {
}
}

View File

@ -15,17 +15,12 @@
*/
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;
@ -34,16 +29,4 @@ public class AsyncMethodPart {
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

@ -191,4 +191,11 @@ public class RenamingVisitor implements StatementVisitor, ExprVisitor {
}
statement.setExceptionVariable(varNames[statement.getExceptionVariable()]);
}
@Override
public void visit(RestoreAsyncStatement statement) {
if (statement.getReceiver() != null) {
statement.setReceiver(varNames[statement.getReceiver()]);
}
}
}

View File

@ -15,35 +15,23 @@
*/
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 class RestoreAsyncStatement extends Statement {
private Integer receiver;
public List<Statement> getHandler() {
return handler;
public Integer getReceiver() {
return receiver;
}
public String getExceptionType() {
return exceptionType;
public void setReceiver(Integer receiver) {
this.receiver = receiver;
}
public void setExceptionType(String exceptionType) {
this.exceptionType = exceptionType;
}
public Integer getExceptionVariable() {
return exceptionVariable;
}
public void setExceptionVariable(Integer exceptionVariable) {
this.exceptionVariable = exceptionVariable;
@Override
public void acceptVisitor(StatementVisitor visitor) {
visitor.visit(this);
}
}

View File

@ -43,4 +43,6 @@ public interface StatementVisitor {
void visit(InitClassStatement statement);
void visit(TryCatchStatement statement);
void visit(RestoreAsyncStatement statement);
}

View File

@ -34,7 +34,5 @@ public interface GeneratorContext extends ServiceRepository {
boolean isAsync();
String getErrorContinuation();
String getCompleteContinuation();
}

View File

@ -68,7 +68,8 @@ public class AsyncProgramSplitter {
// 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());
targetBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock,
targetBlock.getProgram()));
for (TryCatchBlock tryCatch : targetBlock.getTryCatchBlocks()) {
if (tryCatch.getHandler() != null) {
Step next = new Step();
@ -107,12 +108,15 @@ public class AsyncProgramSplitter {
JumpInstruction jumpToNextBlock = new JumpInstruction();
jumpToNextBlock.setTarget(targetBlock);
nextProgram.basicBlockAt(0).getInstructions().add(jumpToNextBlock);
nextProgram.basicBlockAt(0).getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock,
nextProgram));
}
step.targetPart = part;
}
}
targetBlock.getInstructions().addAll(ProgramUtils.copyInstructions(sourceBlock,
last, sourceBlock.getInstructions().size(), targetBlock.getProgram()));
targetBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(sourceBlock, targetBlock.getProgram()));
for (TryCatchBlock tryCatch : targetBlock.getTryCatchBlocks()) {
if (tryCatch.getHandler() != null) {
Step next = new Step();

View File

@ -21,6 +21,7 @@ import org.objectweb.asm.tree.*;
import org.teavm.model.*;
import org.teavm.model.instructions.*;
import org.teavm.model.util.InstructionTransitionExtractor;
import org.teavm.model.util.ProgramUtils;
/**
*
@ -112,6 +113,8 @@ public class ProgramParser implements VariableDebugInformation {
while (program.variableCount() <= signatureVars) {
program.createVariable();
}
program.basicBlockAt(0).getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(
program.basicBlockAt(1), program));
return program;
}

View File

@ -361,7 +361,7 @@ public class TeaVMTestTool {
MethodReference exceptionMsg = new MethodReference(ExceptionHelper.class, "showException",
Throwable.class, String.class);
vm.entryPoint("initInstance", cons);
vm.entryPoint("runTest", methodRef).withValue(0, cons.getClassName());
vm.entryPoint("runTest", methodRef).withValue(0, cons.getClassName()).async();
vm.entryPoint("extractException", exceptionMsg);
vm.exportType("TestClass", cons.getClassName());
vm.setDebugEmitter(debugInfoBuilder);

View File

@ -264,7 +264,7 @@ public class TeaVMTool {
if (mainClass != null) {
MethodDescriptor mainMethodDesc = new MethodDescriptor("main", String[].class, void.class);
vm.entryPoint("main", new MethodReference(mainClass, mainMethodDesc))
.withValue(1, "java.lang.String");
.withValue(1, "java.lang.String").async();
}
for (ClassAlias alias : classAliases) {
vm.exportType(alias.getAlias(), alias.getClassName());
@ -272,7 +272,7 @@ public class TeaVMTool {
for (MethodAlias methodAlias : methodAliases) {
MethodReference ref = new MethodReference(methodAlias.getClassName(), methodAlias.getMethodName(),
MethodDescriptor.parseSignature(methodAlias.getDescriptor()));
TeaVMEntryPoint entryPoint = vm.entryPoint(methodAlias.getAlias(), ref);
TeaVMEntryPoint entryPoint = vm.entryPoint(methodAlias.getAlias(), ref).async();
if (methodAlias.getTypes() != null) {
for (int i = 0; i < methodAlias.getTypes().length; ++i) {
String types = methodAlias.getTypes()[i];
@ -299,6 +299,9 @@ public class TeaVMTool {
cancelled = true;
return;
}
if (mainClass != null) {
writer.append("main = $rt_rootInvocationAdapter(main);\n");
}
ProblemProvider problemProvider = vm.getProblemProvider();
if (problemProvider.getProblems().isEmpty()) {
log.info("JavaScript file successfully built");

View File

@ -89,6 +89,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
private TeaVMProgressListener progressListener;
private boolean cancelled;
private ListableClassHolderSource writtenClasses;
private Set<MethodReference> asyncMethods = new HashSet<>();
TeaVM(ClassReaderSource classSource, ClassLoader classLoader) {
this.classSource = classSource;
@ -462,9 +463,16 @@ 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()
.append("$rt_rootInvocationAdapter(")
.appendMethodBody(entry.getValue().reference).append(");").softNewLine();
sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws();
boolean wrapAsync = !asyncMethods.contains(entry.getValue().reference) && entry.getValue().isAsync();
if (wrapAsync) {
sourceWriter.append("$rt_asyncAdapter(");
}
sourceWriter.appendMethodBody(entry.getValue().reference);
if (wrapAsync) {
sourceWriter.append(")");
}
sourceWriter.append(";").softNewLine();
}
for (Map.Entry<String, String> entry : exportedClasses.entrySet()) {
sourceWriter.append("var ").append(entry.getKey()).ws().append("=").ws()
@ -556,6 +564,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
private List<ClassNode> modelToAst(ListableClassHolderSource classes) {
AsyncMethodFinder asyncFinder = new AsyncMethodFinder(dependencyChecker.getCallGraph(), diagnostics);
asyncFinder.find(classes);
asyncMethods.addAll(asyncFinder.getAsyncMethods());
progressListener.phaseStarted(TeaVMPhase.DECOMPILATION, classes.getClassNames().size());
Decompiler decompiler = new Decompiler(classes, classLoader, asyncFinder.getAsyncMethods());

View File

@ -72,6 +72,7 @@ public class TeaVMEntryPoint {
private String publicName;
MethodReference reference;
private MethodDependency method;
private boolean async;
TeaVMEntryPoint(String publicName, MethodReference reference, MethodDependency method) {
this.publicName = publicName;
@ -84,6 +85,10 @@ public class TeaVMEntryPoint {
return publicName;
}
boolean isAsync() {
return async;
}
public TeaVMEntryPoint withValue(int argument, String type) {
if (argument > reference.parameterCount()) {
throw new IllegalArgumentException("Illegal argument #" + argument + " of " + reference.parameterCount());
@ -91,4 +96,9 @@ public class TeaVMEntryPoint {
method.getVariable(argument).propagate(method.getDependencyAgent().getType(type));
return this;
}
public TeaVMEntryPoint async() {
this.async = true;
return this;
}
}

View File

@ -392,25 +392,41 @@ function $rt_virtualMethods(cls) {
}
}
}
function $rt_asyncResult(value) {
return function() {
return value;
}
}
function $rt_asyncError(e) {
return function() {
throw e;
}
}
function $rt_asyncAdapter(f) {
return function() {
var e;
var result;
var args = Array.prototype.slice.apply(arguments);
var $throw = args.pop();
var $return = args.pop();
try {
var result = f.apply(this, args);
result = f.apply(this, args);
} catch (e) {
return $throw(e);
return $return($rt_asyncError(e));
}
return $return(result);
return $return($rt_asyncResult(result));
}
}
function $rt_rootInvocationAdapter(f) {
return function() {
var args = Array.prototype.slice.apply(arguments);
args.push(function() {});
args.push(function() {});
if (extraArgs) {
for (var i = 0; i < extraArts.length; ++i) {
args.push(extraArgs[i]);
}
}
args.push(function(result) {
result();
});
return f.apply(this, args);
}
}
@ -439,6 +455,15 @@ function $rt_continue(f) {
return f;
}
}
function $rt_guardAsync(f, continuation) {
return function() {
try {
return f.apply(this, arguments);
} catch (e) {
return continuation($rt_asyncError(e));
}
}
}
function $dbg_repr(obj) {
return obj.toString ? obj.toString() : "";

View File

@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
JUnitServer = function() {
"use strict";
function JUnitServer() {
this.tree = new Tree(document.getElementById("test-tree"));
this.totalTimeSpent = 0;
this.expectedExceptions = [];
@ -113,11 +114,10 @@ JUnitServer.prototype.runTest = function(node, callback) {
this.currentTestNode = node;
var self = this;
this.loadCode(node.testCase.script, node.testCase.additionalScripts, function() {
messageHandler = function(event) {
function messageHandler(event) {
window.removeEventListener("message", messageHandler);
var timeSpent = new Date().getTime() - startTime;
node.timeIndicator.appendChild(document.createTextNode(
"(" + (timeSpent / 1000).toFixed(3) + ")"));
node.timeIndicator.appendChild(document.createTextNode("(" + (timeSpent / 1000).toFixed(3) + ")"));
self.handleEvent(JSON.parse(event.data), callback);
};
window.addEventListener("message", messageHandler);
@ -134,8 +134,7 @@ JUnitServer.prototype.runTest = function(node, callback) {
break;
}
}
node.indicator.className = "complete-indicator " +
(node.success ? "successfull" : "failed");
node.indicator.className = "complete-indicator " + (node.success ? "successfull" : "failed");
if (!node.success) {
node.open();
}
@ -253,7 +252,7 @@ JUnitServer.prototype.cleanupNode = function(node) {
}
}
Tree = function(container) {
function Tree(container) {
this.container = container;
this.nodes = [];
this.selectedNode = null;
@ -288,7 +287,7 @@ Tree.prototype.getNodes = function() {
Tree.prototype.addSelectionListener = function(listener) {
this.selectionListeners.push(listener);
}
TreeNode = function(elem, content, button, children, tree) {
function TreeNode(elem, content, button, children, tree) {
this.elem = elem;
this.content = content;
this.button = button;
@ -367,31 +366,41 @@ TreeNode.prototype.select = function() {
}
}
JUnitClient = {};
var JUnitClient = {};
JUnitClient.run = function() {
var handler = window.addEventListener("message", function() {
window.removeEventListener("message", handler);
var message = {};
try {
var instance = new TestClass();
initInstance(instance);
runTest(instance);
message.status = "ok";
runTest(instance, function(restore) {
var message = {};
try {
var result = restore();
message.status = "ok";
} catch (e) {
JUnitClient.makeErrorMessage(message, e);
}
window.parent.postMessage(JSON.stringify(message), "*");
});
} catch (e) {
message.status = "exception";
if (e.$javaException && e.$javaException.constructor.$meta) {
message.exception = e.$javaException.constructor.$meta.name;
message.stack = e.$javaException.constructor.$meta.name + ": ";
var exceptionMessage = extractException(e.$javaException);
message.stack += exceptionMessage ? $rt_ustr(exceptionMessage) : "";
message.stack += "\n" + e.stack;
} else {
message.stack = e.stack;
}
JUnitClient.makeErrorMessage(message, e);
window.parent.postMessage(JSON.stringify(message), "*");
}
window.parent.postMessage(JSON.stringify(message), "*");
});
}
JUnitClient.makeErrorMessage = function(message, e) {
message.status = "exception";
if (e.$javaException && e.$javaException.constructor.$meta) {
message.exception = e.$javaException.constructor.$meta.name;
message.stack = e.$javaException.constructor.$meta.name + ": ";
var exceptionMessage = extractException(e.$javaException);
message.stack += exceptionMessage ? $rt_ustr(exceptionMessage) : "";
message.stack += "\n" + e.stack;
} else {
message.stack = e.stack;
}
}
JUnitClient.reportError = function(error) {
var handler = window.addEventListener("message", function() {
window.removeEventListener("message", handler);

View File

@ -1,732 +0,0 @@
/*
* Copyright 2013 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.
*/
$rt_lastObjectId = 0;
$rt_nextId = function() {
return $rt_lastObjectId++;
}
$rt_compare = function(a, b) {
return a > b ? 1 : a < b ? -1 : 0;
}
$rt_isInstance = function(obj, cls) {
return obj != null && obj.constructor.$meta && $rt_isAssignable(obj.constructor, cls);
}
$rt_isAssignable = function(from, to) {
if (from === to) {
return true;
}
var supertypes = from.$meta.supertypes;
for (var i = 0; i < supertypes.length; i = (i + 1) | 0) {
if ($rt_isAssignable(supertypes[i], to)) {
return true;
}
}
return false;
}
$rt_createArray = function(cls, sz) {
var data = new Array(sz);
var arr = new ($rt_arraycls(cls))(data);
for (var i = 0; i < sz; i = (i + 1) | 0) {
data[i] = null;
}
return arr;
}
$rt_wrapArray = function(cls, data) {
var arr = new ($rt_arraycls(cls))(data);
return arr;
}
$rt_createUnfilledArray = function(cls, sz) {
return new ($rt_arraycls(cls))(new Array(sz));
}
$rt_createLongArray = function(sz) {
var data = new Array(sz);
var arr = new ($rt_arraycls($rt_longcls()))(data);
for (var i = 0; i < sz; i = (i + 1) | 0) {
data[i] = Long.ZERO;
}
return arr;
}
if (ArrayBuffer) {
$rt_createNumericArray = function(cls, nativeArray) {
return new ($rt_arraycls(cls))(nativeArray);
}
$rt_createByteArray = function(sz) {
return $rt_createNumericArray($rt_bytecls(), new Int8Array(new ArrayBuffer(sz)), 0);
};
$rt_createShortArray = function(sz) {
return $rt_createNumericArray($rt_shortcls(), new Int16Array(new ArrayBuffer(sz << 1)), 0);
};
$rt_createIntArray = function(sz) {
return $rt_createNumericArray($rt_intcls(), new Int32Array(new ArrayBuffer(sz << 2)), 0);
};
$rt_createBooleanArray = function(sz) {
return $rt_createNumericArray($rt_booleancls(), new Int8Array(new ArrayBuffer(sz)), 0);
};
$rt_createFloatArray = function(sz) {
return $rt_createNumericArray($rt_floatcls(), new Float32Array(new ArrayBuffer(sz << 2)), 0);
};
$rt_createDoubleArray = function(sz) {
return $rt_createNumericArray($rt_doublecls(), new Float64Array(new ArrayBuffer(sz << 3)), 0);
};
$rt_createCharArray = function(sz) {
return $rt_createNumericArray($rt_charcls(), new Uint16Array(new ArrayBuffer(sz << 1)), 0);
};
} else {
$rt_createNumericArray = function(cls, sz) {
var data = new Array(sz);
var arr = new ($rt_arraycls(cls))(data);
for (var i = 0; i < sz; i = (i + 1) | 0) {
data[i] = 0;
}
return arr;
}
$rt_createByteArray = function(sz) { return $rt_createNumericArray($rt_bytecls(), sz); }
$rt_createShortArray = function(sz) { return $rt_createNumericArray($rt_shortcls(), sz); }
$rt_createIntArray = function(sz) { return $rt_createNumericArray($rt_intcls(), sz); }
$rt_createBooleanArray = function(sz) { return $rt_createNumericArray($rt_booleancls(), sz); }
$rt_createFloatArray = function(sz) { return $rt_createNumericArray($rt_floatcls(), sz); }
$rt_createDoubleArray = function(sz) { return $rt_createNumericArray($rt_doublecls(), sz); }
$rt_createCharArray = function(sz) { return $rt_createNumericArray($rt_charcls(), sz); }
}
$rt_arraycls = function(cls) {
if (cls.$array == undefined) {
var arraycls = function(data) {
this.data = data;
this.$id = $rt_nextId();
};
arraycls.prototype = new ($rt_objcls())();
arraycls.prototype.constructor = arraycls;
arraycls.$meta = { item : cls, supertypes : [$rt_objcls()], primitive : false, superclass : $rt_objcls() };
cls.$array = arraycls;
}
return cls.$array;
}
$rt_createcls = function() {
return {
$meta : {
supertypes : []
}
};
}
$rt_booleanclsCache = null;
$rt_booleancls = function() {
if ($rt_booleanclsCache == null) {
$rt_booleanclsCache = $rt_createcls();
$rt_booleanclsCache.primitive = true;
$rt_booleanclsCache.name = "boolean";
}
return $rt_booleanclsCache;
}
$rt_charclsCache = null;
$rt_charcls = function() {
if ($rt_charclsCache == null) {
$rt_charclsCache = $rt_createcls();
$rt_charclsCache.primitive = true;
$rt_charclsCache.name = "char";
}
return $rt_charclsCache;
}
$rt_byteclsCache = null;
$rt_bytecls = function() {
if ($rt_byteclsCache == null) {
$rt_byteclsCache = $rt_createcls();
$rt_byteclsCache.primitive = true;
$rt_byteclsCache.name = "byte";
}
return $rt_byteclsCache;
}
$rt_shortclsCache = null;
$rt_shortcls = function() {
if ($rt_shortclsCache == null) {
$rt_shortclsCache = $rt_createcls();
$rt_shortclsCache.primitive = true;
$rt_shortclsCache.name = "short";
}
return $rt_shortclsCache;
}
$rt_intclsCache = null;
$rt_intcls = function() {
if ($rt_intclsCache === null) {
$rt_intclsCache = $rt_createcls();
$rt_intclsCache.primitive = true;
$rt_intclsCache.name = "int";
}
return $rt_intclsCache;
}
$rt_longclsCache = null;
$rt_longcls = function() {
if ($rt_longclsCache === null) {
$rt_longclsCache = $rt_createcls();
$rt_longclsCache.primitive = true;
$rt_longclsCache.name = "long";
}
return $rt_longclsCache;
}
$rt_floatclsCache = null;
$rt_floatcls = function() {
if ($rt_floatclsCache === null) {
$rt_floatclsCache = $rt_createcls();
$rt_floatclsCache.primitive = true;
$rt_floatclsCache.name = "float";
}
return $rt_floatclsCache;
}
$rt_doubleclsCache = null;
$rt_doublecls = function() {
if ($rt_doubleclsCache === null) {
$rt_doubleclsCache = $rt_createcls();
$rt_doubleclsCache.primitive = true;
$rt_doubleclsCache.name = "double";
}
return $rt_doubleclsCache;
}
$rt_voidclsCache = null;
$rt_voidcls = function() {
if ($rt_voidclsCache === null) {
$rt_voidclsCache = $rt_createcls();
$rt_voidclsCache.primitive = true;
$rt_voidclsCache.name = "void";
}
return $rt_voidclsCache;
}
$rt_equals = function(a, b) {
if (a === b) {
return true;
}
if (a === null || b === null) {
return false;
}
if (typeof(a) == 'object') {
return a.equals(b);
} else {
return false;
}
}
$rt_clinit = function(cls) {
if (cls.$clinit) {
var f = cls.$clinit;
delete cls.$clinit;
f();
}
return cls;
}
$rt_init = function(cls, constructor, args) {
var obj = new cls();
cls.prototype[constructor].apply(obj, args);
return obj;
}
$rt_throw = function(ex) {
var err = ex.$jsException;
if (!err) {
var err = new Error("Java exception thrown");
err.$javaException = ex;
ex.$jsException = err;
}
throw err;
}
$rt_byteToInt = function(value) {
return value > 0xFF ? value | 0xFFFFFF00 : value;
}
$rt_shortToInt = function(value) {
return value > 0xFFFF ? value | 0xFFFF0000 : value;
}
$rt_createMultiArray = function(cls, dimensions) {
var arrays = new Array($rt_primitiveArrayCount(dimensions));
var firstDim = dimensions[0] | 0;
for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) {
arrays[i] = $rt_createArray(cls, firstDim);
}
return $rt_createMultiArrayImpl(cls, arrays, dimensions);
}
$rt_createByteMultiArray = function(dimensions) {
var arrays = new Array($rt_primitiveArrayCount(dimensions));
var firstDim = dimensions[0] | 0;
for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) {
arrays[i] = $rt_createByteArray(firstDim);
}
return $rt_createMultiArrayImpl($rt_bytecls(), arrays, dimensions);
}
$rt_createBooleanMultiArray = function(dimensions) {
var arrays = new Array($rt_primitiveArrayCount(dimensions));
var firstDim = dimensions[0] | 0;
for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) {
arrays[i] = $rt_createBooleanArray(firstDim);
}
return $rt_createMultiArrayImpl($rt_booleancls(), arrays, dimensions);
}
$rt_createShortMultiArray = function(dimensions) {
var arrays = new Array($rt_primitiveArrayCount(dimensions));
var firstDim = dimensions[0] | 0;
for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) {
arrays[i] = $rt_createShortArray(firstDim);
}
return $rt_createMultiArrayImpl($rt_shortcls(), arrays, dimensions);
}
$rt_createIntMultiArray = function(dimensions) {
var arrays = new Array($rt_primitiveArrayCount(dimensions));
var firstDim = dimensions[0] | 0;
for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) {
arrays[i] = $rt_createIntArray(firstDim);
}
return $rt_createMultiArrayImpl($rt_intcls(), arrays, dimensions);
}
$rt_createLongMultiArray = function(dimensions) {
var arrays = new Array($rt_primitiveArrayCount(dimensions));
var firstDim = dimensions[0] | 0;
for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) {
arrays[i] = $rt_createLongArray(firstDim);
}
return $rt_createMultiArrayImpl($rt_longcls(), arrays, dimensions);
}
$rt_createFloatMultiArray = function(dimensions) {
var arrays = new Array($rt_primitiveArrayCount(dimensions));
var firstDim = dimensions[0] | 0;
for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) {
arrays[i] = $rt_createFloatArray(firstDim);
}
return $rt_createMultiArrayImpl($rt_floatcls(), arrays, dimensions);
}
$rt_createDoubleMultiArray = function(dimensions) {
var arrays = new Array($rt_primitiveArrayCount(dimensions));
var firstDim = dimensions[0] | 0;
for (var i = 0 | 0; i < arrays.length; i = (i + 1) | 0) {
arrays[i] = $rt_createDoubleArray(firstDim);
}
return $rt_createMultiArrayImpl($rt_doublecls(), arrays, dimensions);
}
$rt_primitiveArrayCount = function(dimensions) {
var val = dimensions[1] | 0;
for (var i = 2 | 0; i < dimensions.length; i = (i + 1) | 0) {
val = (val * (dimensions[i] | 0)) | 0;
}
return val;
}
$rt_createMultiArrayImpl = function(cls, arrays, dimensions) {
var limit = arrays.length;
for (var i = 1 | 0; i < dimensions.length; i = (i + 1) | 0) {
cls = $rt_arraycls(cls);
var dim = dimensions[i];
var index = 0;
var packedIndex = 0;
while (index < limit) {
var arr = $rt_createUnfilledArray(cls, dim);
for (var j = 0; j < dim; j = (j + 1) | 0) {
arr.data[j] = arrays[index];
index = (index + 1) | 0;
}
arrays[packedIndex] = arr;
packedIndex = (packedIndex + 1) | 0;
}
limit = packedIndex;
}
return arrays[0];
}
$rt_assertNotNaN = function(value) {
if (typeof value == 'number' && isNaN(value)) {
throw "NaN";
}
return value;
}
$rt_methodStubs = function(clinit, names) {
for (var i = 0; i < names.length; i = (i + 1) | 0) {
window[names[i]] = (function(name) {
return function() {
clinit();
return window[name].apply(window, arguments);
}
})(names[i]);
}
}
$rt_stdoutBuffer = "";
$rt_putStdout = function(ch) {
if (ch === 0xA) {
if (console) {
console.info($rt_stdoutBuffer);
}
$rt_stdoutBuffer = "";
} else {
$rt_stdoutBuffer += String.fromCharCode(ch);
}
}
$rt_stderrBuffer = "";
$rt_putStderr = function(ch) {
if (ch === 0xA) {
if (console) {
console.info($rt_stderrBuffer);
}
$rt_stderrBuffer = "";
} else {
$rt_stderrBuffer += String.fromCharCode(ch);
}
}
function $rt_declClass(cls, data) {
cls.name = data.name;
cls.$meta = {};
cls.$meta.superclass = data.superclass;
cls.$meta.supertypes = data.interfaces ? data.interfaces.slice() : [];
if (data.superclass) {
cls.$meta.supertypes.push(data.superclass);
cls.prototype = new data.superclass();
} else {
cls.prototype = new Object();
}
cls.$meta.name = data.name;
cls.$meta.enum = data.enum;
cls.prototype.constructor = cls;
cls.$clinit = data.clinit;
}
function $rt_virtualMethods(cls) {
for (var i = 1; i < arguments.length; i += 2) {
var name = arguments[i];
var func = arguments[i + 1];
if (typeof name == 'string') {
cls.prototype[name] = func;
} else {
for (var j = 0; j < name.length; ++j) {
cls.prototype[name[j]] = func;
}
}
}
}
Long = function(lo, hi) {
this.lo = lo | 0;
this.hi = hi | 0;
}
Long_ZERO = new Long(0, 0);
Long_fromInt = function(val) {
return val >= 0 ? new Long(val, 0) : new Long(val, -1);
}
Long_fromNumber = function(val) {
return new Long(val | 0, (val / 0x100000000) | 0);
}
Long_toNumber = function(val) {
return val.hi >= 0 ? val.lo + 0x100000000 * val.hi : -0x100000000 * (val.hi ^ 0xFFFFFFFF) + val.lo;
}
Long_add = function(a, b) {
var a_lolo = a.lo & 0xFFFF;
var a_lohi = a.lo >>> 16;
var a_hilo = a.hi & 0xFFFF;
var a_hihi = a.hi >>> 16;
var b_lolo = b.lo & 0xFFFF;
var b_lohi = b.lo >>> 16;
var b_hilo = b.hi & 0xFFFF;
var b_hihi = b.hi >>> 16;
var lolo = (a_lolo + b_lolo) | 0;
var lohi = (a_lohi + b_lohi + (lolo >> 16)) | 0;
var hilo = (a_hilo + b_hilo + (lohi >> 16)) | 0;
var hihi = (a_hihi + b_hihi + (hilo >> 16)) | 0;
return new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16),
(hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16));
}
Long_inc = function(a) {
var lo = (a.lo + 1) | 0;
var hi = a.hi;
if (lo === 0) {
hi = (hi + 1) | 0;
}
return new Long(lo, hi);
}
Long_dec = function(a) {
var lo = (a.lo - 1) | 0;
var hi = a.hi;
if (lo === -1) {
hi = (hi - 1) | 0;
}
return new Long(lo, hi);
}
Long_neg = function(a) {
return Long_inc(new Long(a.lo ^ 0xFFFFFFFF, a.hi ^ 0xFFFFFFFF));
}
Long_sub = function(a, b) {
var a_lolo = a.lo & 0xFFFF;
var a_lohi = a.lo >>> 16;
var a_hilo = a.hi & 0xFFFF;
var a_hihi = a.hi >>> 16;
var b_lolo = b.lo & 0xFFFF;
var b_lohi = b.lo >>> 16;
var b_hilo = b.hi & 0xFFFF;
var b_hihi = b.hi >>> 16;
var lolo = (a_lolo - b_lolo) | 0;
var lohi = (a_lohi - b_lohi + (lolo >> 16)) | 0;
var hilo = (a_hilo - b_hilo + (lohi >> 16)) | 0;
var hihi = (a_hihi - b_hihi + (hilo >> 16)) | 0;
return new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16),
(hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16));
}
Long_compare = function(a, b) {
var r = a.hi - b.hi;
if (r !== 0) {
return r;
}
var r = (a.lo >>> 1) - (b.lo >>> 1);
if (r !== 0) {
return r;
}
return (a.lo & 1) - (b.lo & 1);
}
Long_isPositive = function(a) {
return (a.hi & 0x80000000) === 0;
}
Long_isNegative = function(a) {
return (a.hi & 0x80000000) !== 0;
}
Long_mul = function(a, b) {
var positive = Long_isNegative(a) === Long_isNegative(b);
if (Long_isNegative(a)) {
a = Long_neg(a);
}
if (Long_isNegative(b)) {
b = Long_neg(b);
}
var a_lolo = a.lo & 0xFFFF;
var a_lohi = a.lo >>> 16;
var a_hilo = a.hi & 0xFFFF;
var a_hihi = a.hi >>> 16;
var b_lolo = b.lo & 0xFFFF;
var b_lohi = b.lo >>> 16;
var b_hilo = b.hi & 0xFFFF;
var b_hihi = b.hi >>> 16;
var lolo = (a_lolo * b_lolo) | 0;
var lohi = (a_lohi * b_lolo + a_lolo * b_lohi + (lolo >> 16)) | 0;
var hilo = (a_hilo * b_lolo + a_lohi * b_lohi + a_lolo * b_hilo + (lohi >> 16)) | 0;
var hihi = (a_hihi * b_lolo + a_hilo * b_lohi + a_lohi * b_hilo + a_lolo * b_hihi + (hilo >> 16)) | 0;
var result = new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16), (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16));
return positive ? result : Long_neg(result);
}
Long_div = function(a, b) {
return Long_divRem(a, b)[0];
}
Long_rem = function(a, b) {
return Long_divRem(a, b)[1];
}
Long_divRem = function(a, b) {
var positive = Long_isNegative(a) === Long_isNegative(b);
if (Long_isNegative(a)) {
a = Long_neg(a);
}
if (Long_isNegative(b)) {
b = Long_neg(b);
}
a = new LongInt(a.lo, a.hi, 0);
b = new LongInt(b.lo, b.hi, 0);
var q = LongInt_div(a, b);
a = new Long(a.lo, a.hi);
q = new Long(q.lo, q.hi);
return positive ? [q, a] : [Long_neg(q), Long_neg(a)];
}
Long_shiftLeft16 = function(a) {
return new Long(a.lo << 16, (a.lo >>> 16) | (a.hi << 16));
}
Long_shiftRight16 = function(a) {
return new Long((a.lo >>> 16) | (a.hi << 16), a.hi >>> 16);
}
Long_and = function(a, b) {
return new Long(a.lo & b.lo, a.hi & b.hi);
}
Long_or = function(a, b) {
return new Long(a.lo | b.lo, a.hi | b.hi);
}
Long_xor = function(a, b) {
return new Long(a.lo ^ b.lo, a.hi ^ b.hi);
}
Long_shl = function(a, b) {
if (b < 32) {
return new Long(a.lo << b, (a.lo >>> (32 - b)) | (a.hi << b));
} else {
return new Long(0, a.lo << (b - 32));
}
}
Long_shr = function(a, b) {
if (b < 32) {
return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >> b);
} else {
return new Long((a.hi >> (b - 32)), -1);
}
}
Long_shru = function(a, b) {
if (b < 32) {
return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >>> b);
} else {
return new Long((a.hi >>> (b - 32)), 0);
}
}
// Represents a mutable 80-bit unsigned integer
LongInt = function(lo, hi, sup) {
this.lo = lo;
this.hi = hi;
this.sup = sup;
}
LongInt_mul = function(a, b) {
var a_lolo = ((a.lo & 0xFFFF) * b) | 0;
var a_lohi = ((a.lo >>> 16) * b) | 0;
var a_hilo = ((a.hi & 0xFFFF) * b) | 0;
var a_hihi = ((a.hi >>> 16) * b) | 0;
var sup = (a.sup * b) | 0;
a_lohi = (a_lohi + (a_lolo >> 16)) | 0;
a_hilo = (a_hilo + (a_lohi >> 16)) | 0;
a_hihi = (a_hihi + (a_hilo >> 16)) | 0;
sup = (sup + (a_hihi >> 16)) | 0;
a.lo = (a_lolo & 0xFFFF) | (a_lohi << 16);
a.hi = (a_hilo & 0xFFFF) | (a_hihi << 16);
a.sup = sup & 0xFFFF;
}
LongInt_sub = function(a, b) {
var a_lolo = a.lo & 0xFFFF;
var a_lohi = a.lo >>> 16;
var a_hilo = a.hi & 0xFFFF;
var a_hihi = a.hi >>> 16;
var b_lolo = b.lo & 0xFFFF;
var b_lohi = b.lo >>> 16;
var b_hilo = b.hi & 0xFFFF;
var b_hihi = b.hi >>> 16;
a_lolo = (a_lolo - b_lolo) | 0;
a_lohi = (a_lohi - b_lohi + (a_lolo >> 16)) | 0;
a_hilo = (a_hilo - b_hilo + (a_lohi >> 16)) | 0;
a_hihi = (a_hihi - b_hihi + (a_hilo >> 16)) | 0;
sup = (a.sup - b.sup + (a_hihi >> 16)) | 0;
a.lo = (a_lolo & 0xFFFF) | ((a_lohi & 0xFFFF) << 16);
a.hi = (a_hilo & 0xFFFF) | ((a_hihi & 0xFFFF) << 16);
a.sup = sup;
}
LongInt_add = function(a, b) {
var a_lolo = a.lo & 0xFFFF;
var a_lohi = a.lo >>> 16;
var a_hilo = a.hi & 0xFFFF;
var a_hihi = a.hi >>> 16;
var b_lolo = b.lo & 0xFFFF;
var b_lohi = b.lo >>> 16;
var b_hilo = b.hi & 0xFFFF;
var b_hihi = b.hi >>> 16;
a_lolo = (a_lolo + b_lolo) | 0;
a_lohi = (a_lohi + b_lohi + (a_lolo >> 16)) | 0;
a_hilo = (a_hilo + b_hilo + (a_lohi >> 16)) | 0;
a_hihi = (a_hihi + b_hihi + (a_hilo >> 16)) | 0;
sup = (a.sup + b.sup + (a_hihi >> 16)) | 0;
a.lo = (a_lolo & 0xFFFF) | (a_lohi << 16);
a.hi = (a_hilo & 0xFFFF) | (a_hihi << 16);
a.sup = sup;
}
LongInt_ucompare = function(a, b) {
var r = (a.sup - b.sup);
if (r != 0) {
return r;
}
var r = (a.hi >>> 1) - (b.hi >>> 1);
if (r != 0) {
return r;
}
var r = (a.hi & 1) - (b.hi & 1);
if (r != 0) {
return r;
}
var r = (a.lo >>> 1) - (b.lo >>> 1);
if (r != 0) {
return r;
}
return (a.lo & 1) - (b.lo & 1);
}
LongInt_numOfLeadingZeroBits = function(a) {
var n = 0;
var d = 16;
while (d > 0) {
if ((a >>> d) !== 0) {
a >>>= d;
n = (n + d) | 0;
}
d = (d / 2) | 0;
}
return 31 - n;
}
LongInt_shl = function(a, b) {
if (b < 32) {
a.sup = ((a.hi >>> (32 - b)) | (a.sup << b)) & 0xFFFF;
a.hi = (a.lo >>> (32 - b)) | (a.hi << b);
a.lo <<= b;
} else if (b < 64) {
a.sup = ((a.lo >>> (64 - b)) | (a.hi << (b - 32))) & 0xFFFF;
a.hi = a.lo << b;
a.lo = 0;
} else {
a.sup = (a.lo << (b - 64)) & 0xFFFF;
a.hi = 0;
a.lo = 0;
}
}
LongInt_shr = function(a, b) {
if (b < 32) {
a.lo = (a.lo >>> b) | (a.hi << (32 - b));
a.hi = (a.hi >>> b) | (a.sup << (32 - b));
a.sup >>>= b;
} else if (b < 64) {
a.lo = (a.hi >>> (b - 32)) | (a.sup << (64 - b));
a.hi = a.sup >>> (b - 32);
a.sup = 0;
} else {
a.lo = a.sup >>> (b - 64);
a.hi = 0;
a.sup = 0;
}
}
LongInt_copy = function(a) {
return new LongInt(a.lo, a.hi, a.sup);
}
LongInt_div = function(a, b) {
// Normalize divisor
var bits = b.hi !== 0 ? LongInt_numOfLeadingZeroBits(b.hi) : LongInt_numOfLeadingZeroBits(b.lo) + 32;
var sz = 1 + ((bits / 16) | 0);
var dividentBits = bits % 16;
LongInt_shl(b, bits);
LongInt_shl(a, dividentBits);
q = new LongInt(0, 0, 0);
while (sz-- > 0) {
LongInt_shl(q, 16);
// Calculate approximate q
var digitA = (a.hi >>> 16) + (0x10000 * a.sup);
var digitB = b.hi >>> 16;
var digit = (digitA / digitB) | 0;
var t = LongInt_copy(b);
LongInt_mul(t, digit);
// Adjust q either down or up
if (LongInt_ucompare(t, a) >= 0) {
while (LongInt_ucompare(t, a) > 0) {
LongInt_sub(t, b);
q = (q - 1) | 0;
}
} else {
while (true) {
var nextT = LongInt_copy(t);
LongInt_add(nextT, b);
if (LongInt_ucompare(nextT, a) > 0) {
break;
}
t = nextT;
q = (q + 1) | 0;
}
}
LongInt_sub(a, t);
q.lo |= digit;
LongInt_shl(a, 16);
}
LongInt_shr(a, bits + 16);
return q;
}

View File

@ -99,5 +99,18 @@ public final class AsyncProgram {
Thread.sleep(1000);
System.out.println("Complete async");
System.out.println("Throwing exception");
try {
throwException();
} catch (IllegalStateException e) {
System.out.println("Exception caught");
}
}
private static void throwException() {
Thread.yield();
System.out.println("Thread.yield called");
throw new IllegalStateException();
}
}

View File

@ -21,7 +21,7 @@
<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()">
<body onload="main(null)">
<p>Please, open developer's console to view program's output</p>
</body>
</html>