mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Merge branch 'async' of https://github.com/konsoletyper/teavm into threads
This commit is contained in:
commit
d84889798c
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -257,6 +257,10 @@ public class DiskRegularMethodNodeCache implements RegularMethodNodeCache {
|
|||
@Override
|
||||
public void visit(StaticClassExpr expr) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RestoreAsyncStatement statement) {
|
||||
}
|
||||
}
|
||||
|
||||
static class Item {
|
||||
|
|
|
@ -112,4 +112,8 @@ class BreakToContinueReplacer implements StatementVisitor {
|
|||
visitSequence(statement.getProtectedBody());
|
||||
visitSequence(statement.getHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RestoreAsyncStatement statement) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -107,4 +107,8 @@ class CertainBlockCountVisitor implements StatementVisitor {
|
|||
visit(statement.getProtectedBody());
|
||||
visit(statement.getHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RestoreAsyncStatement statement) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -117,4 +117,8 @@ class RedundantLabelEliminator implements StatementVisitor {
|
|||
visitSequence(statement.getProtectedBody());
|
||||
visitSequence(statement.getHandler());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RestoreAsyncStatement statement) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -111,4 +111,8 @@ class ReferenceCountingVisitor implements StatementVisitor {
|
|||
part.acceptVisitor(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RestoreAsyncStatement statement) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -110,4 +110,8 @@ class TryCatchFinder implements StatementVisitor {
|
|||
public void visit(TryCatchStatement statement) {
|
||||
tryCatchFound = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RestoreAsyncStatement statement) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -224,4 +224,8 @@ class UnusedVariableEliminator implements ExprVisitor, StatementVisitor {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RestoreAsyncStatement statement) {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -43,4 +43,6 @@ public interface StatementVisitor {
|
|||
void visit(InitClassStatement statement);
|
||||
|
||||
void visit(TryCatchStatement statement);
|
||||
|
||||
void visit(RestoreAsyncStatement statement);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,5 @@ public interface GeneratorContext extends ServiceRepository {
|
|||
|
||||
boolean isAsync();
|
||||
|
||||
String getErrorContinuation();
|
||||
|
||||
String getCompleteContinuation();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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() : "";
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
Loading…
Reference in New Issue
Block a user