Reduce minimum runtime size

This commit is contained in:
Alexey Andreev 2018-12-01 23:07:03 +03:00
parent 45d0a13c9b
commit 1cf3cce076
34 changed files with 909 additions and 1448 deletions

View File

@ -90,6 +90,7 @@ public class JCLPlugin implements TeaVMPlugin {
}
host.add(new NumericClassTransformer());
host.add(new SystemClassTransformer());
if (!isBootstrap()) {
List<ReflectionSupplier> reflectionSuppliers = new ArrayList<>();

View File

@ -0,0 +1,70 @@
/*
* Copyright 2018 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.impl;
import org.teavm.diagnostics.Diagnostics;
import org.teavm.model.BasicBlock;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.FieldReference;
import org.teavm.model.Instruction;
import org.teavm.model.MethodHolder;
import org.teavm.model.MethodReference;
import org.teavm.model.Program;
import org.teavm.model.instructions.GetFieldInstruction;
import org.teavm.model.instructions.InvocationType;
import org.teavm.model.instructions.InvokeInstruction;
public class SystemClassTransformer implements ClassHolderTransformer {
@Override
public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) {
for (MethodHolder method : cls.getMethods()) {
if (method.getProgram() != null) {
transformProgram(method.getProgram());
}
}
}
private void transformProgram(Program program) {
for (BasicBlock block : program.getBasicBlocks()) {
for (Instruction insn : block) {
if (!(insn instanceof GetFieldInstruction)) {
continue;
}
GetFieldInstruction getField = (GetFieldInstruction) insn;
FieldReference field = getField.getField();
if (field.getClassName().equals("java.lang.System")) {
switch (field.getFieldName()) {
case "err":
case "out":
case "in": {
InvokeInstruction invoke = new InvokeInstruction();
invoke.setType(InvocationType.SPECIAL);
invoke.setMethod(new MethodReference("java.lang.System", field.getFieldName(),
getField.getFieldType()));
invoke.setReceiver(getField.getReceiver());
invoke.setLocation(insn.getLocation());
insn.replace(invoke);
break;
}
}
}
}
}
}
}

View File

@ -22,10 +22,8 @@ import org.teavm.backend.javascript.spi.GeneratorContext;
import org.teavm.dependency.DependencyAgent;
import org.teavm.dependency.DependencyNode;
import org.teavm.dependency.DependencyPlugin;
import org.teavm.dependency.FieldDependency;
import org.teavm.dependency.MethodDependency;
import org.teavm.model.CallLocation;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodReference;
public class SystemNativeGenerator implements Generator, DependencyPlugin {
@ -38,14 +36,6 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin {
case "currentTimeMillis":
generateCurrentTimeMillis(writer);
break;
case "setOut":
writer.appendStaticField(new FieldReference("java.lang.System", "out"))
.ws().append('=').ws().append(context.getParameterName(1)).append(";").softNewLine();
break;
case "setErr":
writer.appendStaticField(new FieldReference("java.lang.System", "err"))
.ws().append('=').ws().append(context.getParameterName(1)).append(";").softNewLine();
break;
}
}
@ -53,13 +43,7 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin {
public void methodReached(DependencyAgent agent, MethodDependency method, CallLocation location) {
switch (method.getReference().getName()) {
case "doArrayCopy":
achieveArrayCopy(method);
break;
case "setOut":
achieveSetOut(agent, method);
break;
case "setErr":
achieveSetErr(agent, method);
reachArrayCopy(method);
break;
}
}
@ -84,23 +68,12 @@ public class SystemNativeGenerator implements Generator, DependencyPlugin {
}
private void generateCurrentTimeMillis(SourceWriter writer) throws IOException {
writer.append(writer.getNaming().getNameForClassInit("java.lang.System")).append("();").softNewLine();
writer.append("return Long_fromNumber(new Date().getTime());").softNewLine();
}
private void achieveArrayCopy(MethodDependency method) {
private void reachArrayCopy(MethodDependency method) {
DependencyNode src = method.getVariable(1);
DependencyNode dest = method.getVariable(3);
src.getArrayItem().connect(dest.getArrayItem());
}
private void achieveSetErr(DependencyAgent agent, MethodDependency method) {
FieldDependency fieldDep = agent.linkField(new FieldReference("java.lang.System", "err"), null);
method.getVariable(1).connect(fieldDep.getValue());
}
private void achieveSetOut(DependencyAgent agent, MethodDependency method) {
FieldDependency fieldDep = agent.linkField(new FieldReference("java.lang.System", "out"), null);
method.getVariable(1).connect(fieldDep.getValue());
}
}

View File

@ -19,6 +19,7 @@ import org.teavm.classlib.java.io.TSerializable;
import org.teavm.classlib.java.util.TArrays;
class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequence {
static class Constants {
private static float[] powersOfTen = { 1E1f, 1E2f, 1E4f, 1E8f, 1E16f, 1E32f };
private static double[] doublePowersOfTen = { 1E1, 1E2, 1E4, 1E8, 1E16, 1E32, 1E64, 1E128, 1E256 };
private static float[] negPowersOfTen = { 1E-1f, 1E-2f, 1E-4f, 1E-8f, 1E-16f, 1E-32f };
@ -30,6 +31,7 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
1000000000, 10000000000L, 100000000000L, 1000000000000L, 10000000000000L, 100000000000000L,
1000000000000000L, 10000000000000000L, 100000000000000000L, 1000000000000000000L };
private static final long[] longLogPowersOfTen = { 1, 10, 100, 10000, 100000000, 10000000000000000L, };
private static final int FLOAT_DECIMAL_PRECISION = 7;
private static final int DOUBLE_DECIMAL_PRECISION = 16;
private static final float FLOAT_DECIMAL_FACTOR = 1E6f;
@ -38,6 +40,8 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
private static final int DOUBLE_MAX_EXPONENT = 308;
private static final int FLOAT_MAX_POS = 1000000;
private static final long DOUBLE_MAX_POS = 1000000000000000L;
}
char[] buffer;
private int length;
@ -243,27 +247,27 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
int bit = 32;
exp = 0;
float digit = 1;
for (int i = powersOfTen.length - 1; i >= 0; --i) {
if ((exp | bit) <= FLOAT_MAX_EXPONENT && powersOfTen[i] * digit <= value) {
digit *= powersOfTen[i];
for (int i = Constants.powersOfTen.length - 1; i >= 0; --i) {
if ((exp | bit) <= Constants.FLOAT_MAX_EXPONENT && Constants.powersOfTen[i] * digit <= value) {
digit *= Constants.powersOfTen[i];
exp |= bit;
}
bit >>= 1;
}
mantissa = (int) ((value / (digit / FLOAT_DECIMAL_FACTOR)) + 0.5f);
mantissa = (int) ((value / (digit / Constants.FLOAT_DECIMAL_FACTOR)) + 0.5f);
} else {
int bit = 32;
exp = 0;
float digit = 1;
for (int i = negPowersOfTen.length - 1; i >= 0; --i) {
if ((exp | bit) <= 38 && negPowersOfTen[i] * digit * 10 > value) {
digit *= negPowersOfTen[i];
for (int i = Constants.negPowersOfTen.length - 1; i >= 0; --i) {
if ((exp | bit) <= 38 && Constants.negPowersOfTen[i] * digit * 10 > value) {
digit *= Constants.negPowersOfTen[i];
exp |= bit;
}
bit >>= 1;
}
exp = -exp;
mantissa = (int) (((value * FLOAT_MAX_POS) / digit) + 0.5f);
mantissa = (int) (((value * Constants.FLOAT_MAX_POS) / digit) + 0.5f);
while (mantissa >= 10000000) {
mantissa /= 10;
@ -272,7 +276,7 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
}
// Remove trailing zeros
digits = FLOAT_DECIMAL_PRECISION;
digits = Constants.FLOAT_DECIMAL_PRECISION;
int zeros = trailingDecimalZeros(mantissa);
if (zeros > 0) {
digits -= zeros;
@ -285,7 +289,7 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
digits = Math.max(digits, intPart + 1);
exp = 0;
} else if (exp < 0) {
mantissa /= intPowersOfTen[-exp];
mantissa /= Constants.intPowersOfTen[-exp];
digits -= exp;
exp = 0;
}
@ -312,7 +316,7 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
if (negative) {
buffer[target++] = '-';
}
int pos = FLOAT_MAX_POS;
int pos = Constants.FLOAT_MAX_POS;
for (int i = 0; i < digits; ++i) {
int intDigit;
if (pos > 0) {
@ -402,31 +406,31 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
int bit = 256;
exp = 0;
double digit = 1;
for (int i = doublePowersOfTen.length - 1; i >= 0; --i) {
if ((exp | bit) <= DOUBLE_MAX_EXPONENT && doublePowersOfTen[i] * digit <= value) {
digit *= doublePowersOfTen[i];
for (int i = Constants.doublePowersOfTen.length - 1; i >= 0; --i) {
if ((exp | bit) <= Constants.DOUBLE_MAX_EXPONENT && Constants.doublePowersOfTen[i] * digit <= value) {
digit *= Constants.doublePowersOfTen[i];
exp |= bit;
}
bit >>= 1;
}
mantissa = (long) (((value / digit) * DOUBLE_DECIMAL_FACTOR) + 0.5);
mantissa = (long) (((value / digit) * Constants.DOUBLE_DECIMAL_FACTOR) + 0.5);
} else {
int bit = 256;
exp = 0;
double digit = 1;
for (int i = negDoublePowersOfTen.length - 1; i >= 0; --i) {
if ((exp | bit) <= 324 && negDoublePowersOfTen[i] * digit * 10 > value) {
for (int i = Constants.negDoublePowersOfTen.length - 1; i >= 0; --i) {
if ((exp | bit) <= 324 && Constants.negDoublePowersOfTen[i] * digit * 10 > value) {
exp |= bit;
if (exp == 324) {
value /= negDoublePowersOfTen[i];
value /= Constants.negDoublePowersOfTen[i];
} else {
digit *= negDoublePowersOfTen[i];
digit *= Constants.negDoublePowersOfTen[i];
}
}
bit >>= 1;
}
exp = -exp;
mantissa = (long) (((value * DOUBLE_MAX_POS) / digit) + 0.5);
mantissa = (long) (((value * Constants.DOUBLE_MAX_POS) / digit) + 0.5);
while (mantissa >= 10000000000000000L) {
mantissa /= 10;
@ -435,7 +439,7 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
}
// Remove trailing zeros
digits = DOUBLE_DECIMAL_PRECISION;
digits = Constants.DOUBLE_DECIMAL_PRECISION;
int zeros = trailingDecimalZeros(mantissa);
if (zeros > 0) {
digits -= zeros;
@ -448,7 +452,7 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
digits = Math.max(digits, intPart + 1);
exp = 0;
} else if (exp < 0) {
mantissa /= longPowersOfTen[-exp];
mantissa /= Constants.longPowersOfTen[-exp];
digits -= exp;
exp = 0;
}
@ -478,7 +482,7 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
if (negative) {
buffer[target++] = '-';
}
long pos = DOUBLE_MAX_POS;
long pos = Constants.DOUBLE_MAX_POS;
for (int i = 0; i < digits; ++i) {
int intDigit;
if (pos > 0) {
@ -541,10 +545,10 @@ class TAbstractStringBuilder extends TObject implements TSerializable, TCharSequ
long zeros = 1;
int result = 0;
int bit = 16;
for (int i = longLogPowersOfTen.length - 1; i >= 0; --i) {
if (n % (zeros * longLogPowersOfTen[i]) == 0) {
for (int i = Constants.longLogPowersOfTen.length - 1; i >= 0; --i) {
if (n % (zeros * Constants.longLogPowersOfTen[i]) == 0) {
result |= bit;
zeros *= longLogPowersOfTen[i];
zeros *= Constants.longLogPowersOfTen[i];
}
bit >>>= 1;
}

View File

@ -36,7 +36,6 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
public static final TComparator<TString> CASE_INSENSITIVE_ORDER = (o1, o2) -> o1.compareToIgnoreCase(o2);
private char[] characters;
private transient int hashCode;
private static TMap<TString, TString> pool = new THashMap<>();
public TString() {
this.characters = new char[0];
@ -633,10 +632,10 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
}
public TString intern() {
TString interned = pool.get(this);
TString interned = PoolHolder.pool.get(this);
if (interned == null) {
interned = this;
pool.put(interned, interned);
PoolHolder.pool.put(interned, interned);
}
return interned;
}
@ -712,4 +711,8 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
}
return sb.toString();
}
static class PoolHolder {
static TMap<TString, TString> pool = new THashMap<>();
}
}

View File

@ -23,7 +23,6 @@ import org.teavm.classlib.java.io.TConsole;
import org.teavm.classlib.java.io.TInputStream;
import org.teavm.classlib.java.io.TPrintStream;
import org.teavm.classlib.java.lang.reflect.TArray;
import org.teavm.dependency.PluggableDependency;
import org.teavm.interop.Address;
import org.teavm.interop.DelegateTo;
import org.teavm.interop.Import;
@ -34,14 +33,35 @@ import org.teavm.runtime.RuntimeArray;
import org.teavm.runtime.RuntimeClass;
public final class TSystem extends TObject {
public static final TPrintStream out = new TPrintStream(new TConsoleOutputStreamStdout(), false);
public static final TPrintStream err = new TPrintStream(new TConsoleOutputStreamStderr(), false);
public static final TInputStream in = new TConsoleInputStream();
private static TPrintStream outCache;
private static TPrintStream errCache;
private static TInputStream inCache;
private static Properties properties;
private TSystem() {
}
public static TPrintStream out() {
if (outCache == null) {
new TPrintStream(new TConsoleOutputStreamStdout(), false);
}
return outCache;
}
public static TPrintStream err() {
if (errCache == null) {
errCache = new TPrintStream(new TConsoleOutputStreamStderr(), false);
}
return errCache;
}
public static TInputStream in() {
if (inCache == null) {
inCache = new TConsoleInputStream();
}
return inCache;
}
public static TConsole console() {
return null;
}
@ -177,13 +197,13 @@ public final class TSystem extends TObject {
return (String) properties.remove(key);
}
@GeneratedBy(SystemNativeGenerator.class)
@PluggableDependency(SystemNativeGenerator.class)
public static native void setErr(TPrintStream err);
public static void setErr(TPrintStream err) {
errCache = err;
}
@GeneratedBy(SystemNativeGenerator.class)
@PluggableDependency(SystemNativeGenerator.class)
public static native void setOut(TPrintStream err);
public static void setOut(TPrintStream out) {
outCache = out;
}
@DelegateTo("gcLowLevel")
public static void gc() {

View File

@ -150,7 +150,7 @@ public class TThrowable extends RuntimeException {
@Override
public void printStackTrace() {
printStackTrace(TSystem.err);
printStackTrace(TSystem.err());
}
public void printStackTrace(TPrintStream stream) {

View File

@ -24,11 +24,6 @@ public abstract class TCharset implements Comparable<TCharset> {
private String canonicalName;
private String[] aliases;
private Set<String> aliasSet;
private static final Map<String, TCharset> charsets = new HashMap<>();
static {
charsets.put("UTF-8", new TUTF8Charset());
}
protected TCharset(String canonicalName, String[] aliases) {
checkCanonicalName(canonicalName);
@ -73,7 +68,7 @@ public abstract class TCharset implements Comparable<TCharset> {
throw new IllegalArgumentException("charsetName is null");
}
checkCanonicalName(charsetName);
TCharset charset = charsets.get(charsetName.toUpperCase());
TCharset charset = Charsets.value.get(charsetName.toUpperCase());
if (charset == null) {
throw new TUnsupportedCharsetException(charsetName);
}
@ -81,10 +76,9 @@ public abstract class TCharset implements Comparable<TCharset> {
}
public static TCharset defaultCharset() {
return charsets.get("UTF-8");
return Charsets.value.get("UTF-8");
}
public final String name() {
return canonicalName;
}
@ -144,4 +138,12 @@ public abstract class TCharset implements Comparable<TCharset> {
public final int compareTo(TCharset that) {
return canonicalName.compareToIgnoreCase(that.canonicalName);
}
static class Charsets {
private static final Map<String, TCharset> value = new HashMap<>();
static {
value.put("UTF-8", new TUTF8Charset());
}
}
}

View File

@ -21,6 +21,7 @@ import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.nio.charset.StandardCharsets;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
@ -67,6 +68,7 @@ import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldReference;
import org.teavm.model.ListableClassHolderSource;
import org.teavm.model.ListableClassReaderSource;
import org.teavm.model.MethodHolder;
@ -215,26 +217,9 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
dep.getVariable(1).propagate(dependencyAnalyzer.getType("[C"));
dep.use();
dep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "getChars", int.class, int.class,
char[].class, int.class, void.class), null);
dep.getVariable(0).propagate(stringType);
dep.getVariable(3).propagate(dependencyAnalyzer.getType("[C"));
dep.use();
MethodDependency internDep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "intern",
String.class), null);
internDep.getVariable(0).propagate(stringType);
internDep.use();
dep = dependencyAnalyzer.linkMethod(new MethodReference(String.class, "length", int.class), null);
dep.getVariable(0).propagate(stringType);
dep.use();
dependencyAnalyzer.linkField(new FieldReference(String.class.getName(), "characters"), null);
dependencyAnalyzer.linkMethod(new MethodReference(Object.class, "clone", Object.class), null).use();
dependencyAnalyzer.linkMethod(new MethodReference(Thread.class, "currentThread", Thread.class), null).use();
dependencyAnalyzer.linkMethod(new MethodReference(Thread.class, "getMainThread", Thread.class), null).use();
dependencyAnalyzer.linkMethod(
new MethodReference(Thread.class, "setCurrentThread", Thread.class, void.class), null).use();
MethodDependency exceptionCons = dependencyAnalyzer.linkMethod(new MethodReference(
NoClassDefFoundError.class, "<init>", String.class, void.class), null);
@ -281,7 +266,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
@Override
public void emit(ListableClassHolderSource classes, BuildTarget target, String outputName) {
try (OutputStream output = target.createResource(outputName);
Writer writer = new OutputStreamWriter(output, "UTF-8")) {
Writer writer = new OutputStreamWriter(output, StandardCharsets.UTF_8)) {
emit(classes, writer, target);
} catch (IOException e) {
throw new RenderingException(e);
@ -322,7 +307,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
renderingContext.setMinifying(minifying);
Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods,
controller.getDiagnostics(), renderingContext);
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(naming, sourceWriter);
RuntimeRenderer runtimeRenderer = new RuntimeRenderer(classes, naming, sourceWriter);
renderer.setProperties(controller.getProperties());
renderer.setMinifying(minifying);
if (debugEmitter != null) {
@ -358,6 +343,15 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
renderer.renderStringConstants();
renderer.renderCompatibilityStubs();
if (renderer.isLongLibraryUsed()) {
runtimeRenderer.renderHandWrittenRuntime("long.js");
}
if (renderer.isThreadLibraryUsed()) {
runtimeRenderer.renderHandWrittenRuntime("thread.js");
} else {
runtimeRenderer.renderHandWrittenRuntime("simpleThread.js");
}
for (Map.Entry<? extends String, ? extends TeaVMEntryPoint> entry
: controller.getEntryPoints().entrySet()) {
sourceWriter.append("").append(entry.getKey()).ws().append("=").ws();

View File

@ -77,6 +77,9 @@ public class Renderer implements RenderingManager {
private int stringPoolSize;
private int metadataSize;
private boolean longLibraryUsed;
private boolean threadLibraryUsed;
public Renderer(SourceWriter writer, Set<MethodReference> asyncMethods, Set<MethodReference> asyncFamilyMethods,
Diagnostics diagnostics, RenderingContext context) {
this.naming = context.getNaming();
@ -90,6 +93,14 @@ public class Renderer implements RenderingManager {
this.context = context;
}
public boolean isLongLibraryUsed() {
return longLibraryUsed;
}
public boolean isThreadLibraryUsed() {
return threadLibraryUsed;
}
public int getStringPoolSize() {
return stringPoolSize;
}
@ -752,6 +763,8 @@ public class Renderer implements RenderingManager {
writer.newLine();
debugEmitter.emitMethod(null);
longLibraryUsed |= statementRenderer.isLongLibraryUsed();
}
private void renderAsyncPrologue() throws IOException {
@ -831,6 +844,7 @@ public class Renderer implements RenderingManager {
@Override
public void visit(AsyncMethodNode methodNode) {
threadLibraryUsed = true;
try {
statementRenderer.setAsync(true);
this.async = true;

View File

@ -26,22 +26,38 @@ import org.mozilla.javascript.ast.AstRoot;
import org.teavm.backend.javascript.codegen.NamingException;
import org.teavm.backend.javascript.codegen.NamingStrategy;
import org.teavm.backend.javascript.codegen.SourceWriter;
import org.teavm.model.ClassReader;
import org.teavm.model.ClassReaderSource;
import org.teavm.model.FieldReference;
import org.teavm.model.MethodDescriptor;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.vm.RenderingException;
public class RuntimeRenderer {
private static final String STRING_CLASS = String.class.getName();
private static final String THREAD_CLASS = Thread.class.getName();
private static final MethodReference NPE_INIT_METHOD = new MethodReference(NullPointerException.class,
"<init>", void.class);
private static final MethodDescriptor STRING_INTERN_METHOD = new MethodDescriptor("intern",
String.class, String.class);
private static final MethodDescriptor CURRENT_THREAD_METHOD = new MethodDescriptor("currentThread",
Thread.class);
private final ClassReaderSource classSource;
private final NamingStrategy naming;
private final SourceWriter writer;
public RuntimeRenderer(NamingStrategy naming, SourceWriter writer) {
public RuntimeRenderer(ClassReaderSource classSource, NamingStrategy naming, SourceWriter writer) {
this.classSource = classSource;
this.naming = naming;
this.writer = writer;
}
public void renderRuntime() throws RenderingException {
try {
renderHandWrittenRuntime();
renderHandWrittenRuntime("runtime.js");
renderSetCloneMethod();
renderRuntimeCls();
renderRuntimeString();
@ -60,22 +76,22 @@ public class RuntimeRenderer {
}
}
private void renderHandWrittenRuntime() throws IOException {
AstRoot ast = parseRuntime();
public void renderHandWrittenRuntime(String name) throws IOException {
AstRoot ast = parseRuntime(name);
ast.visit(new StringConstantElimination());
AstWriter astWriter = new AstWriter(writer);
astWriter.hoist(ast);
astWriter.print(ast);
}
private AstRoot parseRuntime() throws IOException {
private AstRoot parseRuntime(String name) throws IOException {
CompilerEnvirons env = new CompilerEnvirons();
env.setRecoverFromErrors(true);
env.setLanguageVersion(Context.VERSION_1_8);
JSParser factory = new JSParser(env);
ClassLoader loader = RuntimeRenderer.class.getClassLoader();
try (InputStream input = loader.getResourceAsStream("org/teavm/backend/javascript/runtime.js");
try (InputStream input = loader.getResourceAsStream("org/teavm/backend/javascript/" + name);
Reader reader = new InputStreamReader(input, StandardCharsets.UTF_8)) {
return factory.parse(reader, null, 0);
}
@ -113,29 +129,21 @@ public class RuntimeRenderer {
}
private void renderRuntimeUnwrapString() throws IOException {
MethodReference stringLen = new MethodReference(String.class, "length", int.class);
MethodReference getChars = new MethodReference(String.class, "getChars", int.class, int.class,
char[].class, int.class, void.class);
FieldReference stringChars = new FieldReference(STRING_CLASS, "characters");
writer.append("function $rt_ustr(str) {").indent().softNewLine();
writer.append("if (str === null) {").indent().softNewLine();
writer.append("return null;").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("var result = \"\";").softNewLine();
writer.append("var sz = ").appendMethodBody(stringLen).append("(str);").softNewLine();
writer.append("var array = $rt_createCharArray(sz);").softNewLine();
writer.appendMethodBody(getChars).append("(str, 0, sz, array, 0);").softNewLine();
writer.append("for (var i = 0; i < sz; i = (i + 1) | 0) {").indent().softNewLine();
writer.append("result += String.fromCharCode(array.data[i]);").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return result;").softNewLine();
writer.append("return String.fromCharCode.apply(null, str.").appendField(stringChars)
.append(".data);").softNewLine();
writer.outdent().append("}").newLine();
}
private void renderRuntimeNullCheck() throws IOException {
writer.append("function $rt_nullCheck(val) {").indent().softNewLine();
writer.append("if (val === null) {").indent().softNewLine();
writer.append("$rt_throw(").append(naming.getNameForInit(new MethodReference(NullPointerException.class,
"<init>", void.class))).append("());").softNewLine();
writer.append("$rt_throw(").append(naming.getNameForInit(NPE_INIT_METHOD)).append("());").softNewLine();
writer.outdent().append("}").softNewLine();
writer.append("return val;").softNewLine();
writer.outdent().append("}").newLine();
@ -143,8 +151,14 @@ public class RuntimeRenderer {
private void renderRuntimeIntern() throws IOException {
writer.append("function $rt_intern(str) {").indent().softNewLine();
ClassReader stringCls = classSource.get(STRING_CLASS);
if (stringCls != null && stringCls.getMethod(STRING_INTERN_METHOD) != null) {
writer.append("return ").appendMethodBody(new MethodReference(String.class, "intern", String.class))
.append("(str);").softNewLine();
} else {
writer.append("return str;").softNewLine();
}
writer.outdent().append("}").newLine();
}
@ -153,14 +167,23 @@ public class RuntimeRenderer {
}
private void renderRuntimeThreads() throws IOException {
ClassReader threadCls = classSource.get(THREAD_CLASS);
boolean threadUsed = threadCls != null && threadCls.getMethod(CURRENT_THREAD_METHOD) != null;
writer.append("function $rt_getThread()").ws().append("{").indent().softNewLine();
if (threadUsed) {
writer.append("return ").appendMethodBody(Thread.class, "currentThread", Thread.class).append("();")
.softNewLine();
} else {
writer.append("return null;").softNewLine();
}
writer.outdent().append("}").newLine();
writer.append("function $rt_setThread(t)").ws().append("{").indent().softNewLine();
if (threadUsed) {
writer.append("return ").appendMethodBody(Thread.class, "setCurrentThread", Thread.class, void.class)
.append("(t);").softNewLine();
}
writer.outdent().append("}").newLine();
}

View File

@ -102,6 +102,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
private int currentPart;
private List<String> blockIds = new ArrayList<>();
private IntIndexedContainer blockIndexMap = new IntArrayList();
private boolean longLibraryUsed;
public StatementRenderer(RenderingContext context, SourceWriter writer) {
this.context = context;
@ -112,6 +113,10 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
this.debugEmitter = context.getDebugEmitter();
}
public boolean isLongLibraryUsed() {
return longLibraryUsed;
}
public boolean isAsync() {
return async;
}
@ -621,6 +626,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
@Override
public void visit(BinaryExpr expr) {
if (expr.getType() == OperationType.LONG) {
longLibraryUsed = true;
switch (expr.getOperation()) {
case ADD:
visitBinaryFunction(expr, "Long_add");
@ -763,6 +769,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
switch (expr.getOperation()) {
case NOT: {
if (expr.getType() == OperationType.LONG) {
longLibraryUsed = true;
writer.append("Long_not(");
precedence = Precedence.min();
expr.getOperand().acceptVisitor(this);
@ -782,6 +789,7 @@ public class StatementRenderer implements ExprVisitor, StatementVisitor {
}
case NEGATE:
if (expr.getType() == OperationType.LONG) {
longLibraryUsed = true;
writer.append("Long_neg(");
precedence = Precedence.min();
expr.getOperand().acceptVisitor(this);

View File

@ -0,0 +1,478 @@
/*
* Copyright 2018 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.
*/
"use strict";
function Long_eq(a, b) {
return a.hi === b.hi && a.lo === b.lo;
}
function Long_ne(a, b) {
return a.hi !== b.hi || a.lo !== b.lo;
}
function Long_gt(a, b) {
if (a.hi < b.hi) {
return false;
}
if (a.hi > b.hi) {
return true;
}
var x = a.lo >>> 1;
var y = b.lo >>> 1;
if (x !== y) {
return x > y;
}
return (a.lo & 1) > (b.lo & 1);
}
function Long_ge(a, b) {
if (a.hi < b.hi) {
return false;
}
if (a.hi > b.hi) {
return true;
}
var x = a.lo >>> 1;
var y = b.lo >>> 1;
if (x !== y) {
return x >= y;
}
return (a.lo & 1) >= (b.lo & 1);
}
function Long_lt(a, b) {
if (a.hi > b.hi) {
return false;
}
if (a.hi < b.hi) {
return true;
}
var x = a.lo >>> 1;
var y = b.lo >>> 1;
if (x !== y) {
return x < y;
}
return (a.lo & 1) < (b.lo & 1);
}
function Long_le(a, b) {
if (a.hi > b.hi) {
return false;
}
if (a.hi < b.hi) {
return true;
}
var x = a.lo >>> 1;
var y = b.lo >>> 1;
if (x !== y) {
return x <= y;
}
return (a.lo & 1) <= (b.lo & 1);
}
function Long_add(a, b) {
if (a.hi === (a.lo >> 31) && b.hi === (b.lo >> 31)) {
return Long_fromNumber(a.lo + b.lo);
} else if (Math.abs(a.hi) < Long_MAX_NORMAL && Math.abs(b.hi) < Long_MAX_NORMAL) {
return Long_fromNumber(Long_toNumber(a) + Long_toNumber(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));
}
function Long_inc(a) {
var lo = (a.lo + 1) | 0;
var hi = a.hi;
if (lo === 0) {
hi = (hi + 1) | 0;
}
return new Long(lo, hi);
}
function Long_dec(a) {
var lo = (a.lo - 1) | 0;
var hi = a.hi;
if (lo === -1) {
hi = (hi - 1) | 0;
}
return new Long(lo, hi);
}
function Long_neg(a) {
return Long_inc(new Long(a.lo ^ 0xFFFFFFFF, a.hi ^ 0xFFFFFFFF));
}
function Long_sub(a, b) {
if (a.hi === (a.lo >> 31) && b.hi === (b.lo >> 31)) {
return Long_fromNumber(a.lo - b.lo);
}
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));
}
function Long_compare(a, b) {
var r = a.hi - b.hi;
if (r !== 0) {
return r;
}
r = (a.lo >>> 1) - (b.lo >>> 1);
if (r !== 0) {
return r;
}
return (a.lo & 1) - (b.lo & 1);
}
function Long_isPositive(a) {
return (a.hi & 0x80000000) === 0;
}
function Long_isNegative(a) {
return (a.hi & 0x80000000) !== 0;
}
function Long_mul(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 = 0;
var lohi = 0;
var hilo = 0;
var hihi = 0;
lolo = (a_lolo * b_lolo) | 0;
lohi = lolo >>> 16;
lohi = ((lohi & 0xFFFF) + a_lohi * b_lolo) | 0;
hilo = (hilo + (lohi >>> 16)) | 0;
lohi = ((lohi & 0xFFFF) + a_lolo * b_lohi) | 0;
hilo = (hilo + (lohi >>> 16)) | 0;
hihi = hilo >>> 16;
hilo = ((hilo & 0xFFFF) + a_hilo * b_lolo) | 0;
hihi = (hihi + (hilo >>> 16)) | 0;
hilo = ((hilo & 0xFFFF) + a_lohi * b_lohi) | 0;
hihi = (hihi + (hilo >>> 16)) | 0;
hilo = ((hilo & 0xFFFF) + a_lolo * b_hilo) | 0;
hihi = (hihi + (hilo >>> 16)) | 0;
hihi = (hihi + a_hihi * b_lolo + a_hilo * b_lohi + a_lohi * b_hilo + a_lolo * b_hihi) | 0;
var result = new Long((lolo & 0xFFFF) | (lohi << 16), (hilo & 0xFFFF) | (hihi << 16));
return positive ? result : Long_neg(result);
}
function Long_div(a, b) {
if (Math.abs(a.hi) < Long_MAX_NORMAL && Math.abs(b.hi) < Long_MAX_NORMAL) {
return Long_fromNumber(Long_toNumber(a) / Long_toNumber(b));
}
return Long_divRem(a, b)[0];
}
function Long_rem(a, b) {
if (Math.abs(a.hi) < Long_MAX_NORMAL && Math.abs(b.hi) < Long_MAX_NORMAL) {
return Long_fromNumber(Long_toNumber(a) % Long_toNumber(b));
}
return Long_divRem(a, b)[1];
}
function Long_divRem(a, b) {
if (b.lo === 0 && b.hi === 0) {
throw new Error("Division by zero");
}
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)];
}
function Long_shiftLeft16(a) {
return new Long(a.lo << 16, (a.lo >>> 16) | (a.hi << 16));
}
function Long_shiftRight16(a) {
return new Long((a.lo >>> 16) | (a.hi << 16), a.hi >>> 16);
}
function Long_and(a, b) {
return new Long(a.lo & b.lo, a.hi & b.hi);
}
function Long_or(a, b) {
return new Long(a.lo | b.lo, a.hi | b.hi);
}
function Long_xor(a, b) {
return new Long(a.lo ^ b.lo, a.hi ^ b.hi);
}
function Long_shl(a, b) {
b &= 63;
if (b === 0) {
return a;
} else if (b < 32) {
return new Long(a.lo << b, (a.lo >>> (32 - b)) | (a.hi << b));
} else if (b === 32) {
return new Long(0, a.lo);
} else {
return new Long(0, a.lo << (b - 32));
}
}
function Long_shr(a, b) {
b &= 63;
if (b === 0) {
return a;
} else if (b < 32) {
return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >> b);
} else if (b === 32) {
return new Long(a.hi, a.hi >> 31);
} else {
return new Long((a.hi >> (b - 32)), a.hi >> 31);
}
}
function Long_shru(a, b) {
b &= 63;
if (b === 0) {
return a;
} else if (b < 32) {
return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >>> b);
} else if (b === 32) {
return new Long(a.hi, 0);
} else {
return new Long((a.hi >>> (b - 32)), 0);
}
}
// Represents a mutable 80-bit unsigned integer
function LongInt(lo, hi, sup) {
this.lo = lo;
this.hi = hi;
this.sup = sup;
}
function LongInt_mul(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;
}
function LongInt_sub(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;
var 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;
}
function LongInt_add(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;
var 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;
}
function LongInt_inc(a) {
a.lo = (a.lo + 1) | 0;
if (a.lo === 0) {
a.hi = (a.hi + 1) | 0;
if (a.hi === 0) {
a.sup = (a.sup + 1) & 0xFFFF;
}
}
}
function LongInt_dec(a) {
a.lo = (a.lo - 1) | 0;
if (a.lo === -1) {
a.hi = (a.hi - 1) | 0;
if (a.hi === -1) {
a.sup = (a.sup - 1) & 0xFFFF;
}
}
}
function LongInt_ucompare(a, b) {
var r = (a.sup - b.sup);
if (r !== 0) {
return r;
}
r = (a.hi >>> 1) - (b.hi >>> 1);
if (r !== 0) {
return r;
}
r = (a.hi & 1) - (b.hi & 1);
if (r !== 0) {
return r;
}
r = (a.lo >>> 1) - (b.lo >>> 1);
if (r !== 0) {
return r;
}
return (a.lo & 1) - (b.lo & 1);
}
function LongInt_numOfLeadingZeroBits(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;
}
function LongInt_shl(a, b) {
if (b === 0) {
return;
}
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 === 32) {
a.sup = a.hi & 0xFFFF;
a.hi = a.lo;
a.lo = 0;
} else if (b < 64) {
a.sup = ((a.lo >>> (64 - b)) | (a.hi << (b - 32))) & 0xFFFF;
a.hi = a.lo << b;
a.lo = 0;
} else if (b === 64) {
a.sup = a.lo & 0xFFFF;
a.hi = 0;
a.lo = 0;
} else {
a.sup = (a.lo << (b - 64)) & 0xFFFF;
a.hi = 0;
a.lo = 0;
}
}
function LongInt_shr(a, b) {
if (b === 0) {
return;
}
if (b === 32) {
a.lo = a.hi;
a.hi = a.sup;
a.sup = 0;
} else 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.sup;
a.hi = 0;
a.sup = 0;
} 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;
}
}
function LongInt_copy(a) {
return new LongInt(a.lo, a.hi, a.sup);
}
function LongInt_div(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);
var 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);
--digit;
}
} else {
while (true) {
var nextT = LongInt_copy(t);
LongInt_add(nextT, b);
if (LongInt_ucompare(nextT, a) > 0) {
break;
}
t = nextT;
++digit;
}
}
LongInt_sub(a, t);
q.lo |= digit;
LongInt_shl(a, 16);
}
LongInt_shr(a, bits + 16);
return q;
}

View File

@ -507,99 +507,6 @@ function $rt_s(index) {
function $rt_eraseClinit(target) {
return target.$clinit = function() {};
}
function TeaVMThread(runner) {
this.status = 3;
this.stack = [];
this.suspendCallback = null;
this.runner = runner;
this.attribute = null;
this.completeCallback = null;
}
TeaVMThread.prototype.push = function() {
for (var i = 0; i < arguments.length; ++i) {
this.stack.push(arguments[i]);
}
return this;
};
TeaVMThread.prototype.s = TeaVMThread.prototype.push;
TeaVMThread.prototype.pop = function() {
return this.stack.pop();
};
TeaVMThread.prototype.l = TeaVMThread.prototype.pop;
TeaVMThread.prototype.isResuming = function() {
return this.status === 2;
};
TeaVMThread.prototype.isSuspending = function() {
return this.status === 1;
};
TeaVMThread.prototype.suspend = function(callback) {
this.suspendCallback = callback;
this.status = 1;
};
TeaVMThread.prototype.start = function(callback) {
if (this.status !== 3) {
throw new Error("Thread already started");
}
if ($rt_currentNativeThread !== null) {
throw new Error("Another thread is running");
}
this.status = 0;
this.completeCallback = callback ? callback : function(result) {
if (result instanceof Error) {
throw result;
}
};
this.run();
};
TeaVMThread.prototype.resume = function() {
if ($rt_currentNativeThread !== null) {
throw new Error("Another thread is running");
}
this.status = 2;
this.run();
};
TeaVMThread.prototype.run = function() {
$rt_currentNativeThread = this;
var result;
try {
result = this.runner();
} catch (e) {
result = e;
} finally {
$rt_currentNativeThread = null;
}
if (this.suspendCallback !== null) {
var self = this;
var callback = this.suspendCallback;
this.suspendCallback = null;
callback(function() {
self.resume();
});
} else if (this.status === 0) {
this.completeCallback(result);
}
};
function $rt_suspending() {
var thread = $rt_nativeThread();
return thread != null && thread.isSuspending();
}
function $rt_resuming() {
var thread = $rt_nativeThread();
return thread != null && thread.isResuming();
}
function $rt_suspend(callback) {
return $rt_nativeThread().suspend(callback);
}
function $rt_startThread(runner, callback) {
new TeaVMThread(runner).start(callback);
}
var $rt_currentNativeThread = null;
function $rt_nativeThread() {
return $rt_currentNativeThread;
}
function $rt_invalidPointer() {
throw new Error("Invalid recorded state");
}
var $rt_numberConversionView = new DataView(new ArrayBuffer(8));
@ -716,465 +623,3 @@ function Long_toNumber(val) {
}
return 0x100000000 * hi + lo;
}
function Long_eq(a, b) {
return a.hi === b.hi && a.lo === b.lo;
}
function Long_ne(a, b) {
return a.hi !== b.hi || a.lo !== b.lo;
}
function Long_gt(a, b) {
if (a.hi < b.hi) {
return false;
}
if (a.hi > b.hi) {
return true;
}
var x = a.lo >>> 1;
var y = b.lo >>> 1;
if (x !== y) {
return x > y;
}
return (a.lo & 1) > (b.lo & 1);
}
function Long_ge(a, b) {
if (a.hi < b.hi) {
return false;
}
if (a.hi > b.hi) {
return true;
}
var x = a.lo >>> 1;
var y = b.lo >>> 1;
if (x !== y) {
return x >= y;
}
return (a.lo & 1) >= (b.lo & 1);
}
function Long_lt(a, b) {
if (a.hi > b.hi) {
return false;
}
if (a.hi < b.hi) {
return true;
}
var x = a.lo >>> 1;
var y = b.lo >>> 1;
if (x !== y) {
return x < y;
}
return (a.lo & 1) < (b.lo & 1);
}
function Long_le(a, b) {
if (a.hi > b.hi) {
return false;
}
if (a.hi < b.hi) {
return true;
}
var x = a.lo >>> 1;
var y = b.lo >>> 1;
if (x !== y) {
return x <= y;
}
return (a.lo & 1) <= (b.lo & 1);
}
function Long_add(a, b) {
if (a.hi === (a.lo >> 31) && b.hi === (b.lo >> 31)) {
return Long_fromNumber(a.lo + b.lo);
} else if (Math.abs(a.hi) < Long_MAX_NORMAL && Math.abs(b.hi) < Long_MAX_NORMAL) {
return Long_fromNumber(Long_toNumber(a) + Long_toNumber(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));
}
function Long_inc(a) {
var lo = (a.lo + 1) | 0;
var hi = a.hi;
if (lo === 0) {
hi = (hi + 1) | 0;
}
return new Long(lo, hi);
}
function Long_dec(a) {
var lo = (a.lo - 1) | 0;
var hi = a.hi;
if (lo === -1) {
hi = (hi - 1) | 0;
}
return new Long(lo, hi);
}
function Long_neg(a) {
return Long_inc(new Long(a.lo ^ 0xFFFFFFFF, a.hi ^ 0xFFFFFFFF));
}
function Long_sub(a, b) {
if (a.hi === (a.lo >> 31) && b.hi === (b.lo >> 31)) {
return Long_fromNumber(a.lo - b.lo);
}
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));
}
function Long_compare(a, b) {
var r = a.hi - b.hi;
if (r !== 0) {
return r;
}
r = (a.lo >>> 1) - (b.lo >>> 1);
if (r !== 0) {
return r;
}
return (a.lo & 1) - (b.lo & 1);
}
function Long_isPositive(a) {
return (a.hi & 0x80000000) === 0;
}
function Long_isNegative(a) {
return (a.hi & 0x80000000) !== 0;
}
function Long_mul(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 = 0;
var lohi = 0;
var hilo = 0;
var hihi = 0;
lolo = (a_lolo * b_lolo) | 0;
lohi = lolo >>> 16;
lohi = ((lohi & 0xFFFF) + a_lohi * b_lolo) | 0;
hilo = (hilo + (lohi >>> 16)) | 0;
lohi = ((lohi & 0xFFFF) + a_lolo * b_lohi) | 0;
hilo = (hilo + (lohi >>> 16)) | 0;
hihi = hilo >>> 16;
hilo = ((hilo & 0xFFFF) + a_hilo * b_lolo) | 0;
hihi = (hihi + (hilo >>> 16)) | 0;
hilo = ((hilo & 0xFFFF) + a_lohi * b_lohi) | 0;
hihi = (hihi + (hilo >>> 16)) | 0;
hilo = ((hilo & 0xFFFF) + a_lolo * b_hilo) | 0;
hihi = (hihi + (hilo >>> 16)) | 0;
hihi = (hihi + a_hihi * b_lolo + a_hilo * b_lohi + a_lohi * b_hilo + a_lolo * b_hihi) | 0;
var result = new Long((lolo & 0xFFFF) | (lohi << 16), (hilo & 0xFFFF) | (hihi << 16));
return positive ? result : Long_neg(result);
}
function Long_div(a, b) {
if (Math.abs(a.hi) < Long_MAX_NORMAL && Math.abs(b.hi) < Long_MAX_NORMAL) {
return Long_fromNumber(Long_toNumber(a) / Long_toNumber(b));
}
return Long_divRem(a, b)[0];
}
function Long_rem(a, b) {
if (Math.abs(a.hi) < Long_MAX_NORMAL && Math.abs(b.hi) < Long_MAX_NORMAL) {
return Long_fromNumber(Long_toNumber(a) % Long_toNumber(b));
}
return Long_divRem(a, b)[1];
}
function Long_divRem(a, b) {
if (b.lo === 0 && b.hi === 0) {
throw new Error("Division by zero");
}
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)];
}
function Long_shiftLeft16(a) {
return new Long(a.lo << 16, (a.lo >>> 16) | (a.hi << 16));
}
function Long_shiftRight16(a) {
return new Long((a.lo >>> 16) | (a.hi << 16), a.hi >>> 16);
}
function Long_and(a, b) {
return new Long(a.lo & b.lo, a.hi & b.hi);
}
function Long_or(a, b) {
return new Long(a.lo | b.lo, a.hi | b.hi);
}
function Long_xor(a, b) {
return new Long(a.lo ^ b.lo, a.hi ^ b.hi);
}
function Long_shl(a, b) {
b &= 63;
if (b === 0) {
return a;
} else if (b < 32) {
return new Long(a.lo << b, (a.lo >>> (32 - b)) | (a.hi << b));
} else if (b === 32) {
return new Long(0, a.lo);
} else {
return new Long(0, a.lo << (b - 32));
}
}
function Long_shr(a, b) {
b &= 63;
if (b === 0) {
return a;
} else if (b < 32) {
return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >> b);
} else if (b === 32) {
return new Long(a.hi, a.hi >> 31);
} else {
return new Long((a.hi >> (b - 32)), a.hi >> 31);
}
}
function Long_shru(a, b) {
b &= 63;
if (b === 0) {
return a;
} else if (b < 32) {
return new Long((a.lo >>> b) | (a.hi << (32 - b)), a.hi >>> b);
} else if (b === 32) {
return new Long(a.hi, 0);
} else {
return new Long((a.hi >>> (b - 32)), 0);
}
}
// Represents a mutable 80-bit unsigned integer
function LongInt(lo, hi, sup) {
this.lo = lo;
this.hi = hi;
this.sup = sup;
}
function LongInt_mul(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;
}
function LongInt_sub(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;
var 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;
}
function LongInt_add(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;
var 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;
}
function LongInt_inc(a) {
a.lo = (a.lo + 1) | 0;
if (a.lo === 0) {
a.hi = (a.hi + 1) | 0;
if (a.hi === 0) {
a.sup = (a.sup + 1) & 0xFFFF;
}
}
}
function LongInt_dec(a) {
a.lo = (a.lo - 1) | 0;
if (a.lo === -1) {
a.hi = (a.hi - 1) | 0;
if (a.hi === -1) {
a.sup = (a.sup - 1) & 0xFFFF;
}
}
}
function LongInt_ucompare(a, b) {
var r = (a.sup - b.sup);
if (r !== 0) {
return r;
}
r = (a.hi >>> 1) - (b.hi >>> 1);
if (r !== 0) {
return r;
}
r = (a.hi & 1) - (b.hi & 1);
if (r !== 0) {
return r;
}
r = (a.lo >>> 1) - (b.lo >>> 1);
if (r !== 0) {
return r;
}
return (a.lo & 1) - (b.lo & 1);
}
function LongInt_numOfLeadingZeroBits(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;
}
function LongInt_shl(a, b) {
if (b === 0) {
return;
}
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 === 32) {
a.sup = a.hi & 0xFFFF;
a.hi = a.lo;
a.lo = 0;
} else if (b < 64) {
a.sup = ((a.lo >>> (64 - b)) | (a.hi << (b - 32))) & 0xFFFF;
a.hi = a.lo << b;
a.lo = 0;
} else if (b === 64) {
a.sup = a.lo & 0xFFFF;
a.hi = 0;
a.lo = 0;
} else {
a.sup = (a.lo << (b - 64)) & 0xFFFF;
a.hi = 0;
a.lo = 0;
}
}
function LongInt_shr(a, b) {
if (b === 0) {
return;
}
if (b === 32) {
a.lo = a.hi;
a.hi = a.sup;
a.sup = 0;
} else 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.sup;
a.hi = 0;
a.sup = 0;
} 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;
}
}
function LongInt_copy(a) {
return new LongInt(a.lo, a.hi, a.sup);
}
function LongInt_div(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);
var 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);
--digit;
}
} else {
while (true) {
var nextT = LongInt_copy(t);
LongInt_add(nextT, b);
if (LongInt_ucompare(nextT, a) > 0) {
break;
}
t = nextT;
++digit;
}
}
LongInt_sub(a, t);
q.lo |= digit;
LongInt_shl(a, 16);
}
LongInt_shr(a, bits + 16);
return q;
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2018 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.
*/
"use strict";
function $rt_startThread(runner, callback) {
var result;
try {
result = runner();
} catch (e) {
result = e;
}
if (typeof callback !== 'undefined') {
callback(result);
} else if (e instanceof Error) {
throw e;
}
}
function $rt_suspending() {
return false;
}
function $rt_resuming() {
return false;
}
function $rt_nativeThread() {
return null;
}
function $rt_invalidPointer() {
}

View File

@ -0,0 +1,111 @@
/*
* Copyright 2018 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.
*/
"use strict";
function TeaVMThread(runner) {
this.status = 3;
this.stack = [];
this.suspendCallback = null;
this.runner = runner;
this.attribute = null;
this.completeCallback = null;
}
TeaVMThread.prototype.push = function() {
for (var i = 0; i < arguments.length; ++i) {
this.stack.push(arguments[i]);
}
return this;
};
TeaVMThread.prototype.s = TeaVMThread.prototype.push;
TeaVMThread.prototype.pop = function() {
return this.stack.pop();
};
TeaVMThread.prototype.l = TeaVMThread.prototype.pop;
TeaVMThread.prototype.isResuming = function() {
return this.status === 2;
};
TeaVMThread.prototype.isSuspending = function() {
return this.status === 1;
};
TeaVMThread.prototype.suspend = function(callback) {
this.suspendCallback = callback;
this.status = 1;
};
TeaVMThread.prototype.start = function(callback) {
if (this.status !== 3) {
throw new Error("Thread already started");
}
if ($rt_currentNativeThread !== null) {
throw new Error("Another thread is running");
}
this.status = 0;
this.completeCallback = callback ? callback : function(result) {
if (result instanceof Error) {
throw result;
}
};
this.run();
};
TeaVMThread.prototype.resume = function() {
if ($rt_currentNativeThread !== null) {
throw new Error("Another thread is running");
}
this.status = 2;
this.run();
};
TeaVMThread.prototype.run = function() {
$rt_currentNativeThread = this;
var result;
try {
result = this.runner();
} catch (e) {
result = e;
} finally {
$rt_currentNativeThread = null;
}
if (this.suspendCallback !== null) {
var self = this;
var callback = this.suspendCallback;
this.suspendCallback = null;
callback(function() {
self.resume();
});
} else if (this.status === 0) {
this.completeCallback(result);
}
};
function $rt_suspending() {
var thread = $rt_nativeThread();
return thread != null && thread.isSuspending();
}
function $rt_resuming() {
var thread = $rt_nativeThread();
return thread != null && thread.isResuming();
}
function $rt_suspend(callback) {
return $rt_nativeThread().suspend(callback);
}
function $rt_startThread(runner, callback) {
new TeaVMThread(runner).start(callback);
}
var $rt_currentNativeThread = null;
function $rt_nativeThread() {
return $rt_currentNativeThread;
}
function $rt_invalidPointer() {
throw new Error("Invalid recorded state");
}

View File

@ -103,10 +103,14 @@ class UsageGenerator {
MethodDependency hashCodeDep = agent.linkMethod(new MethodReference(String.class, "hashCode", int.class),
location);
hashCodeDep.getVariable(0).propagate(agent.getType("java.lang.String"));
nameDep.getResult().connect(hashCodeDep.getVariable(0));
hashCodeDep.getThrown().connect(methodDep.getThrown());
hashCodeDep.use();
agent.linkMethod(new MethodReference(Object.class, "hashCode", int.class), null);
agent.linkMethod(new MethodReference(Object.class, "equals", Object.class, boolean.class), null);
return nameDep;
}

12
pom.xml
View File

@ -68,7 +68,6 @@
<html4j.version>1.5</html4j.version>
<jetty.version>9.2.1.v20140609</jetty.version>
<slf4j.version>1.7.7</slf4j.version>
<selenium.version>2.47.2</selenium.version>
<jackson.version>2.6.2</jackson.version>
<idea.version>2017.3.5</idea.version>
<asm.version>7.0</asm.version>
@ -77,7 +76,6 @@
<teavm.test.incremental>false</teavm.test.incremental>
<teavm.test.threads>1</teavm.test.threads>
<teavm.test.selenium/>
<teavm.test.skip>true</teavm.test.skip>
</properties>
@ -183,16 +181,6 @@
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
<version>${selenium.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>

View File

@ -276,34 +276,27 @@ class TestRunner {
function runTeaVM() {
return new Promise(resolve => {
$rt_startThread(() => {
const thread = $rt_nativeThread();
let instance;
if (thread.isResuming()) {
instance = thread.pop();
main([], result => {
const message = {};
if (result instanceof Error) {
makeErrorMessage(message, result);
} else {
message.status = "OK";
}
try {
runTest();
} catch (e) {
resolve({ status: "failed", errorMessage: buildErrorMessage(e) });
return;
}
if (thread.isSuspending()) {
thread.push(instance);
return;
}
resolve({ status: "OK" });
resolve(message);
});
function buildErrorMessage(e) {
let stack = e.stack;
if (e.$javaException && e.$javaException.constructor.$meta) {
stack = e.$javaException.constructor.$meta.name + ": ";
const exceptionMessage = extractException(e.$javaException);
stack += exceptionMessage ? $rt_ustr(exceptionMessage) : "";
function makeErrorMessage(message, e) {
message.status = "failed";
if (e.$javaException) {
message.className = e.$javaException.constructor.name;
message.message = e.$javaException.getMessage();
} else {
message.className = Object.getPrototypeOf(e).name;
message.message = e.message;
}
stack += "\n" + stack;
return stack;
message.exception = e;
message.stack = e.stack;
}
})
}

View File

@ -1,25 +0,0 @@
<!--
Copyright 2015 Alexey Andreev.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
</head>
<body>
<script type="text/javascript" src="res/junit-client.js"></script>
<script type="text/javascript" src="res/runtime.js"></script>
</body>
</html>

View File

@ -1,52 +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.
-->
<!DOCTYPE html>
<html>
<head>
<title>TeaVM JUnit tests</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
<link rel="stylesheet" href="res/junit.css" type="text/css"/>
<script type="text/javascript" src="res/junit-support.js"></script>
<script type="text/javascript" src="tests/all.js"></script>
</head>
<body>
<script type="text/javascript">
function runTests() {
document.getElementById("start-button").disabled = true;
server.runAllTests(function() {
document.getElementById("start-button").disabled = false;
});
}
</script>
<div class="test-header">
<div class="test-count">Total tests:
<span id="test-count"></span><span id="total-time" class="time-indicator"></span>
</div>
<div class="failed-test-count">Failed: <span id="failed-test-count"></span></div>
<div class="progress-bar">
<div id="progress-bar-content"></div>
</div>
<button id="start-button" onclick="runTests()">Run</button>
</div>
<div id="test-tree">
</div>
<div id="test-trace">
</div>
<script>
server = prepare();
</script>
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 774 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 297 B

View File

@ -1,78 +0,0 @@
"use strict";
var JUnitClient = {};
JUnitClient.run = function() {
var handler = function(event) {
switch (event.data.command) {
case "runTest":
window.removeEventListener("message", handler);
$rt_startThread(function() {
JUnitClient.runTest();
});
break;
}
};
window.addEventListener("message", handler);
window.parent.postMessage("ready", "*");
};
JUnitClient.runTest = function() {
var thread = $rt_nativeThread();
var instance;
var ptr = 0;
var message;
if (thread.isResuming()) {
ptr = thread.pop();
instance = thread.pop();
}
loop: while (true) { switch (ptr) {
case 0:
try {
runTest();
} catch (e) {
message = {};
JUnitClient.makeErrorMessage(message, e);
break loop;
}
if (thread.isSuspending()) {
thread.push(instance);
thread.push(ptr);
return;
}
message = {};
message.status = "ok";
break loop;
}}
window.parent.postMessage(message, "*");
};
JUnitClient.makeErrorMessage = function(message, e) {
message.status = "exception";
var stack = e.stack;
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" + stack;
};
JUnitClient.reportError = function(error) {
var handler = function() {
window.removeEventListener("message", handler);
var message = { status : "exception", stack : error };
window.parent.postMessage(message, "*");
};
window.addEventListener("message", handler);
};
JUnitClient.loadScript = function(scriptPath) {
var script = document.createElement("script");
script.src = scriptPath;
document.body.appendChild(script);
};
window.addEventListener("message", function(event) {
var data = event.data;
switch (data.command) {
case "loadScript":
JUnitClient.loadScript(data.script);
break;
}
});
window.parent.postMessage("loaded", "*");

View File

@ -1,362 +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.
*/
"use strict";
function JUnitServer() {
this.tree = new Tree(document.getElementById("test-tree"));
this.totalTimeSpent = 0;
this.expectedExceptions = [];
this.frame = null;
this.tests = [];
this.currentTestNode = null;
this.testCaseCount = 0;
this.progressElem = document.getElementById("progress-bar-content");
this.runCount = 0;
this.failCount = 0;
this.traceElem = document.getElementById("test-trace");
this.failedElem = document.getElementById("failed-test-count");
this.totalTimeElem = document.getElementById("total-time");
var self = this;
this.tree.addSelectionListener(function(node) {
while (self.traceElem.firstChild) {
self.traceElem.removeChild(self.traceElem.firstChild);
}
if (node.error) {
var pre = document.createElement("pre");
pre.appendChild(document.createTextNode(node.error));
self.traceElem.appendChild(pre);
}
});
}
JUnitServer.prototype = {};
JUnitServer.prototype.handleEvent = function(message, callback) {
if (message.status === "ok") {
if (this.expectedExceptions.length > 0) {
this.currentTestNode.success = false;
this.currentTestNode.error = "Expected exception not thrown";
this.failCount++;
} else {
this.currentTestNode.success = true;
}
} else if (message.status === "exception") {
if (message.exception && this.isExpectedException(message.exception)) {
this.currentTestNode.success = true;
} else {
this.currentTestNode.success = false;
this.currentTestNode.error = message.stack;
this.failCount++;
}
}
this.currentTestNode.indicator.className = "complete-indicator " +
(this.currentTestNode.success ? "successfull" : "failed");
this.runCount++;
this.progressElem.style.width = (100 * this.runCount / this.testCaseCount).toFixed(2) + "%";
document.body.removeChild(this.frame);
self.frame = null;
callback();
};
JUnitServer.prototype.isExpectedException = function(ex) {
for (var i = 0; i < this.expectedExceptions.length; ++i) {
if (this.expectedExceptions[i] === ex) {
return true;
}
}
return false;
};
JUnitServer.prototype.loadCode = function(path, additionalScripts, callback) {
this.frame = document.createElement("iframe");
this.frame.src = "junit-client.html";
document.body.appendChild(this.frame);
var sequence = [];
sequence.push(path);
for (var i = 0; i < additionalScripts.length; ++i) {
sequence.push(additionalScripts[i]);
}
var self = this;
var handler = function() {
window.removeEventListener("message", handler);
self.loadScripts(sequence, callback);
};
window.addEventListener("message", handler);
};
JUnitServer.prototype.loadScripts = function(scripts, callback) {
for (var i = 0; i < scripts.length; ++i) {
this.frame.contentWindow.postMessage({ command : "loadScript", "script" : scripts[i] }, "*");
}
var handler = function() {
window.removeEventListener("message", handler);
callback();
};
window.addEventListener("message", handler);
};
JUnitServer.prototype.runTest = function(node, callback) {
node.indicator.className = "complete-indicator in-progress";
var startTime = new Date().getTime();
if (node.testCase) {
this.expectedExceptions = node.testCase.expected;
this.currentTestNode = node;
var self = this;
this.loadCode(node.testCase.script, node.testCase.additionalScripts, function() {
function messageHandler(event) {
window.removeEventListener("message", messageHandler);
var timeSpent = new Date().getTime() - startTime;
node.timeIndicator.appendChild(document.createTextNode("(" + (timeSpent / 1000).toFixed(3) + ")"));
self.handleEvent(event.data, callback);
}
window.addEventListener("message", messageHandler);
self.frame.contentWindow.postMessage({ command : "runTest" }, "*");
});
} else {
var self = this;
var nodes = node.getNodes();
this.runTestFromList(nodes, 0, function() {
node.success = true;
for (var i = 0; i < nodes.length; ++i) {
if (!nodes[i].success) {
node.success = false;
break;
}
}
node.indicator.className = "complete-indicator " + (node.success ? "successfull" : "failed");
if (!node.success) {
node.open();
}
var timeSpent = new Date().getTime() - startTime;
node.timeIndicator.appendChild(document.createTextNode(
"(" + (timeSpent / 1000).toFixed(3) + ")"));
callback();
});
}
};
JUnitServer.prototype.readTests = function(tests) {
var groups = this.groupTests(tests);
var groupNames = [];
for (var groupName in groups) {
groupNames.push(groupName);
}
groupNames.sort();
this.tests = [];
for (var i = 0; i < groupNames.length; ++i) {
var groupName = groupNames[i];
var group = groups[groupName];
var pkgNode = this.createNode(this.tree, "package", groupName);
group.sort();
for (var j = 0; j < group.length; ++j) {
var test = group[j];
var simpleName = test.name.substring(groupName.length + 1);
var testNode = this.createNode(pkgNode, "test", simpleName);
var methods = test.methods.slice();
methods.sort();
for (var k = 0; k < methods.length; ++k) {
var method = methods[k];
var caseNode = this.createNode(testNode, "case", method.name);
caseNode.testCase = method;
++this.testCaseCount;
}
}
}
document.getElementById("test-count").appendChild(document.createTextNode(this.testCaseCount));
return this;
};
JUnitServer.prototype.createNode = function(parent, className, name) {
var elem = document.createElement("div");
elem.className = className;
elem.appendChild(document.createTextNode(name));
var node = parent.add(elem);
node.indicator = document.createElement("div");
node.indicator.className = "complete-indicator";
elem.appendChild(node.indicator);
node.timeIndicator = document.createElement("span");
node.timeIndicator.className = "time-indicator";
elem.appendChild(node.timeIndicator);
return node;
};
JUnitServer.prototype.groupTests = function(tests) {
var groups = {};
for (var i = 0; i < tests.length; ++i) {
var test = tests[i];
var pkg = test.name.substring(0, test.name.lastIndexOf('.'));
var group = groups[pkg];
if (!group) {
group = [];
groups[pkg] = group;
}
group.push(test);
}
return groups;
};
JUnitServer.prototype.runAllTests = function(callback) {
this.cleanupTests();
var self = this;
var startTime = new Date().getTime();
this.runTestFromList(this.tree.getNodes(), 0, function() {
self.failedElem.appendChild(document.createTextNode(self.failCount));
var totalTime = new Date().getTime() - startTime;
self.totalTimeElem.appendChild(document.createTextNode("(" +
(totalTime / 1000).toFixed(3) + ")"));
callback();
});
};
JUnitServer.prototype.runTestFromList = function(nodes, index, callback) {
if (index < nodes.length) {
var node = nodes[index];
var self = this;
this.runTest(node, function() {
self.runTestFromList(nodes, index + 1, callback);
});
} else {
callback();
}
};
JUnitServer.prototype.cleanupTests = function() {
if (this.failedElem.firstChild) {
this.failedElem.removeChild(this.failedElem.firstChild);
}
if (this.totalTimeElem.firstChild) {
this.totalTimeElem.removeChild(this.totalTimeElem.firstChild);
}
this.runCount = 0;
this.failCount = 0;
this.progressElem.style.width = "0%";
var nodes = this.tree.getNodes();
for (var i = 0; i < nodes.length; ++i) {
this.cleanupNode(nodes[i]);
}
};
JUnitServer.prototype.cleanupNode = function(node) {
delete node.error;
node.indicator.className = "complete-indicator";
if (node.timeIndicator.firstChild) {
node.timeIndicator.removeChild(node.timeIndicator.firstChild);
}
var nodes = node.getNodes();
for (var i = 0; i < nodes.length; ++i) {
this.cleanupNode(nodes[i]);
}
};
function Tree(container) {
this.container = container;
this.nodes = [];
this.selectedNode = null;
this.selectionListeners = [];
}
Tree.prototype.createNode = function(content) {
var elem = document.createElement("div");
elem.className = "tree-node";
var contentElem = document.createElement("div");
contentElem.className = "tree-node-content";
elem.appendChild(contentElem);
contentElem.appendChild(content);
var buttonElem = document.createElement("div");
buttonElem.className = "tree-node-button closed";
buttonElem.style.display = "none";
elem.appendChild(buttonElem);
var childrenElem = document.createElement("div");
childrenElem.className = "tree-node-children closed";
childrenElem.style.display = "none";
elem.appendChild(childrenElem);
return new TreeNode(elem, contentElem, buttonElem, childrenElem, this);
};
Tree.prototype.add = function(content) {
var node = this.createNode(content);
this.container.appendChild(node.elem);
this.nodes.push(node);
return node;
};
Tree.prototype.getNodes = function() {
return this.nodes;
};
Tree.prototype.addSelectionListener = function(listener) {
this.selectionListeners.push(listener);
};
function TreeNode(elem, content, button, children, tree) {
this.elem = elem;
this.content = content;
this.button = button;
this.children = children;
this.opened = false;
this.parent = null;
this.nodes = [];
this.tree = tree;
var self = this;
this.button.onclick = function() {
self.toggle();
};
this.content.onclick = function() {
self.select();
}
}
TreeNode.prototype.add = function(content) {
var node = this.tree.createNode(content);
this.children.appendChild(node.elem);
this.button.style.display = "";
this.children.style.display = "";
this.content.className = "tree-node-content";
node.parent = this;
this.nodes.push(node);
return node;
};
TreeNode.prototype.isOpened = function() {
return this.opened;
};
TreeNode.prototype.open = function() {
if (this.isOpened()) {
return;
}
this.opened = true;
this.children.className = "tree-node-children opened";
this.button.className = "tree-node-button opened";
};
TreeNode.prototype.close = function() {
if (!this.isOpened()) {
return;
}
this.opened = false;
this.children.className = "tree-node-children closed";
this.button.className = "tree-node-button closed";
};
TreeNode.prototype.toggle = function() {
if (this.isOpened()) {
this.close();
} else {
this.open();
}
};
TreeNode.prototype.getNodes = function() {
return this.nodes;
};
TreeNode.prototype.getParent = function() {
return this.parent;
};
TreeNode.prototype.getTree = function() {
return this.tree;
};
TreeNode.prototype.isSelected = function() {
return this.tree.selectedNode === this;
};
TreeNode.prototype.select = function() {
if (this.isSelected()) {
return;
}
if (this.tree.selectedNode != null) {
this.tree.selectedNode.content.className = "tree-node-content";
}
this.content.className = "tree-node-content selected";
this.tree.selectedNode = this;
for (var i = 0; i < this.tree.selectionListeners.length; ++i) {
this.tree.selectionListeners[i](this);
}
};

View File

@ -1,185 +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.
*/
iframe {
width: 1px;
height: 1px;
padding: 0;
margin: 0;
border-style: none;
}
* {
font-family: sans-serif;
font-size: 10pt;
}
html, body {
position: absolute;
left: 0;
right: 0;
top: 0;
bottom: 0;
padding: 0;
margin: 0;
}
.test-header {
position: absolute;
top: 0;
right: 0;
left: 0;
height: 40px;
border-bottom-color: gray;
border-bottom-width: 1px;
border-bottom-style: solid;
}
.test-count, .failed-test-count {
position: absolute;
top: 0;
bottom: 0;
line-height: 40px;
padding: 0;
margin: 0;
vertical-align: middle;
}
.test-count {
left: 5px;
width: 200px;
}
.failed-test-count {
left: 205px;
width: 130px;
}
#start-button {
display: block;
position: absolute;
left: 340px;
top: 6px;
bottom: 6px;
width: 70px;
}
.progress-bar {
position: absolute;
left: 420px;
right: 20px;
top: 5px;
height: 30px;
border-color: grey;
border-radius: 2px;
border-width: 1px;
border-style: solid;
}
#progress-bar-content {
position: absolute;
background-color: green;
left: 0;
top: 0;
bottom: 0;
width: 0;
}
#test-tree {
position: absolute;
top: 41px;
bottom: 0;
left: 0;
width: 50%;
border-right-color: grey;
border-right-width: 1px;
border-right-style: solid;
overflow: auto;
}
#test-trace {
position: absolute;
top: 41px;
bottom: 0;
left: 50%;
right: 0;
overflow: auto;
}
.tree-node {
position: relative;
}
.tree-node-content {
margin-left: 18px;
cursor: default;
white-space: nowrap;
}
.tree-node-content.selected {
background-color: rgb(160,160,255);
}
.tree-node-children {
padding-left: 18px;
}
.tree-node-children.closed {
display: none;
}
.tree-node-button {
position: absolute;
cursor: pointer;
left: 0;
top: 0;
width: 16px;
height: 16px;
background-repeat: no-repeat;
background-position: 0 0;
}
.tree-node-button.opened {
background-image: url(toggle-small.png);
}
.tree-node-button.closed {
background-image: url(toggle-small-expand.png);
}
.package, .test, .case {
min-height: 18px;
padding-left: 20px;
background-position: 2px 1px;
background-repeat: no-repeat;
position: relative;
}
.package {
background-image: url(package_obj.png);
}
.test {
background-image: url(class_obj.png);
}
.case {
background-image: url(methpub_obj.png);
}
.complete-indicator {
position: absolute;
left: -4px;
top: 5px;
width: 16px;
height: 16px;
background-position: 0 0;
background-repeat: no-repeat;
display: none;
}
.complete-indicator.successfull {
display: block;
background-image: url(tick-small.png);
}
.complete-indicator.failed {
display: block;
background-image: url(tick-small-red.png);
}
.complete-indicator.in-progress {
display: block;
background-image: url(control-000-small.png);
}
.time-indicator {
font-size: 9pt;
font-style: italic;
color: rgb(0,80,160);
padding-left: 0.5em;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 345 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 321 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 418 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 394 B

View File

@ -36,15 +36,16 @@ import org.teavm.model.MethodReference;
final class RhinoResultParser {
private static Pattern pattern = Pattern.compile("(([A-Za-z_$]+)\\(\\))?@.+:([0-9]+)");
private static Pattern lineSeparator = Pattern.compile("\\r\\n|\r|\n");
private File debugFile;
private DebugInformation debugInformation;
private String[] script;
RhinoResultParser(File debugFile) {
debugInformation = debugFile != null ? getDebugInformation(debugFile) : null;
if (debugFile != null) {
debugInformation = getDebugInformation(debugFile);
script = getScript(new File(debugFile.getParentFile(),
debugFile.getName().substring(0, debugFile.getName().length() - 9)));
}
}
void parseResult(Scriptable result, TestRunCallback callback) {
if (result == null) {
@ -57,8 +58,6 @@ final class RhinoResultParser {
callback.complete();
break;
case "exception": {
DebugInformation debugInformation = debugFile != null ? getDebugInformation(debugFile) : null;
String className = String.valueOf(result.get("className", result));
if (debugInformation != null) {
String decodedName = debugInformation.getClassNameByJsName(className);

View File

@ -57,14 +57,6 @@
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId>
</dependency>
<dependency>
<groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-remote-driver</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>