Add exception support to async methods

This commit is contained in:
Alexey Andreev 2015-02-05 17:50:25 +04:00
parent 8a11239436
commit 73721e5b31
23 changed files with 174 additions and 833 deletions

View File

@ -37,13 +37,13 @@ public class ThreadNativeGenerator implements Generator {
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

@ -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

@ -204,8 +204,15 @@ public class Decompiler {
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));
}
@ -229,7 +236,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 +251,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 +304,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 +324,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

@ -577,4 +577,9 @@ class OptimizingVisitor implements StatementVisitor, ExprVisitor {
statement.getHandler().addAll(statements);
resultStmt = statement;
}
@Override
public void visit(RestoreAsyncStatement statement) {
resultStmt = statement;
}
}

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,7 +529,7 @@ 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());
@ -609,6 +609,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 +630,18 @@ 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("function $part_").append(i).append("(");
if (i > 0) {
writer.append("$restore");
}
writer.append(")").ws().append('{').indent().softNewLine();
writer.append("try {").indent().softNewLine();
AsyncMethodPart part = methodNode.getBody().get(i);
part.getStatement().acceptVisitor(Renderer.this);
writer.outdent().append('}').softNewLine();
writer.outdent().append("} catch ($guard) {").indent().softNewLine();
writer.append("return $return($rt_asyncError($guard));").softNewLine();
writer.outdent().append("}").softNewLine();
writer.outdent().append("}").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,7 +920,7 @@ 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(' ');
@ -921,7 +929,7 @@ public class Renderer implements ExprVisitor, StatementVisitor, RenderingContext
debugEmitter.emitCallSite();
}
if (async) {
writer.append(')');
writer.append("))");
}
writer.append(";").softNewLine();
if (statement.getLocation() != null) {
@ -1754,6 +1762,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

@ -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 $rt_asyncError(e);
}
return $return(result);
return $rt_asyncResult(result);
}
}
function $rt_rootInvocationAdapter(f) {
function $rt_rootInvocationAdapter(f, extraArgs) {
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);
}
}

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

@ -55,5 +55,18 @@ public final class AsyncProgram {
}
}
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>