From df05104e3c5dbc16afb36312f2d36f61a4f0d923 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Sun, 27 Oct 2013 12:16:07 +0400 Subject: [PATCH] Doing a lot of things --- pom.xml | 1 + teavm-classlib/.gitignore | 4 + teavm-classlib/pom.xml | 20 ++ .../org/teavm/classlib/java/lang/TClass.java | 12 + .../java/lang/TClassNativeGenerator.java | 34 ++ .../java/lang/TInterruptedException.java | 9 + .../org/teavm/classlib/java/lang/TObject.java | 59 ++++ .../java/lang/TObjectNativeGenerator.java | 69 ++++ .../org/teavm/classlib/java/lang/TString.java | 9 + .../teavm/classlib/java/lang/TThrowable.java | 9 + .../org/teavm/classlib/org/junit/Assert.java | 60 ++++ .../org/junit/AssertNativeGenerator.java | 25 ++ .../org/teavm/classlib/org/junit/Test.java | 15 + .../classlibgen/ClasslibTestGenerator.java | 35 ++ .../main/resources/META-INF/teavm.properties | 3 + teavm-core/pom.xml | 3 +- .../teavm/codegen/ConcurrentCachedMapper.java | 55 +++ .../main/java/org/teavm/codegen/Mapper.java | 9 + .../teavm/dependency/DependencyChecker.java | 200 +++++++++++ .../teavm/dependency/DependencyConsumer.java | 11 + .../dependency/DependencyGraphBuilder.java | 294 ++++++++++++++++ .../org/teavm/dependency/DependencyNode.java | 76 +++++ .../org/teavm/dependency/MethodGraph.java | 61 ++++ .../org/teavm/dependency/TypeAnalyzer.java | 188 +++++++++++ .../dependency/TypePropagationListener.java | 24 ++ .../teavm/javascript/MethodDecompiler.java | 2 +- .../javascript/ast/RenderableMethod.java | 1 - .../org/teavm/javascript/ni/GeneratedBy.java | 16 + .../org/teavm/javascript/ni/Generator.java | 12 + .../teavm/javascript/ni/GeneratorContext.java | 9 + .../java/org/teavm/javascript/ni/Rename.java | 16 + .../org/teavm/model/ClassHolderSource.java | 34 +- .../java/org/teavm/model/ElementHolder.java | 4 - .../java/org/teavm/model/FieldReference.java | 40 +++ .../java/org/teavm/model/MethodReference.java | 45 +++ .../teavm/model/MutableClassHolderSource.java | 36 ++ .../java/org/teavm/model/package-info.java | 2 +- .../resource/ClasspathClassHolderSource.java | 28 ++ .../resource/ClasspathResourceMapper.java | 83 +++++ .../resource/ClasspathResourceReader.java | 30 ++ .../resource/MapperClassHolderSource.java | 23 ++ .../resource/ResourceClassHolderMapper.java | 37 ++ .../teavm/model/resource/ResourceParser.java | 33 ++ .../teavm/model/resource/ResourceReader.java | 14 + .../resources/org/teavm/javascript/runtime.js | 316 ++++++++++++++++++ 45 files changed, 2028 insertions(+), 38 deletions(-) create mode 100644 teavm-classlib/.gitignore create mode 100644 teavm-classlib/pom.xml create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClassNativeGenerator.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TInterruptedException.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObjectNativeGenerator.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TString.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Assert.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/org/junit/AssertNativeGenerator.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Test.java create mode 100644 teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java create mode 100644 teavm-classlib/src/main/resources/META-INF/teavm.properties create mode 100644 teavm-core/src/main/java/org/teavm/codegen/ConcurrentCachedMapper.java create mode 100644 teavm-core/src/main/java/org/teavm/codegen/Mapper.java create mode 100644 teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java create mode 100644 teavm-core/src/main/java/org/teavm/dependency/DependencyConsumer.java create mode 100644 teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java create mode 100644 teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java create mode 100644 teavm-core/src/main/java/org/teavm/dependency/MethodGraph.java create mode 100644 teavm-core/src/main/java/org/teavm/dependency/TypeAnalyzer.java create mode 100644 teavm-core/src/main/java/org/teavm/dependency/TypePropagationListener.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/ni/GeneratedBy.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/ni/Generator.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java create mode 100644 teavm-core/src/main/java/org/teavm/javascript/ni/Rename.java create mode 100644 teavm-core/src/main/java/org/teavm/model/FieldReference.java create mode 100644 teavm-core/src/main/java/org/teavm/model/MethodReference.java create mode 100644 teavm-core/src/main/java/org/teavm/model/MutableClassHolderSource.java create mode 100644 teavm-core/src/main/java/org/teavm/model/resource/ClasspathClassHolderSource.java create mode 100644 teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceMapper.java create mode 100644 teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceReader.java create mode 100644 teavm-core/src/main/java/org/teavm/model/resource/MapperClassHolderSource.java create mode 100644 teavm-core/src/main/java/org/teavm/model/resource/ResourceClassHolderMapper.java create mode 100644 teavm-core/src/main/java/org/teavm/model/resource/ResourceParser.java create mode 100644 teavm-core/src/main/java/org/teavm/model/resource/ResourceReader.java create mode 100644 teavm-core/src/main/resources/org/teavm/javascript/runtime.js diff --git a/pom.xml b/pom.xml index 704fe8bbc..f683f4bee 100644 --- a/pom.xml +++ b/pom.xml @@ -27,5 +27,6 @@ teavm-core + teavm-classlib \ No newline at end of file diff --git a/teavm-classlib/.gitignore b/teavm-classlib/.gitignore new file mode 100644 index 000000000..324450954 --- /dev/null +++ b/teavm-classlib/.gitignore @@ -0,0 +1,4 @@ +target +.settings +.project +.classpath \ No newline at end of file diff --git a/teavm-classlib/pom.xml b/teavm-classlib/pom.xml new file mode 100644 index 000000000..3d9a13a75 --- /dev/null +++ b/teavm-classlib/pom.xml @@ -0,0 +1,20 @@ + + 4.0.0 + + + org.teavm + teavm + 0.0.1-SNAPSHOT + + teavm-classlib + + + + org.teavm + teavm-core + 0.0.1-SNAPSHOT + true + + + \ No newline at end of file diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java new file mode 100644 index 000000000..00c01edfa --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClass.java @@ -0,0 +1,12 @@ +package org.teavm.classlib.java.lang; + +import org.teavm.javascript.ni.GeneratedBy; + +/** + * + * @author Alexey Andreev + */ +public class TClass extends TObject { + @GeneratedBy(TClassNativeGenerator.class) + public native boolean isInstance(TObject obj); +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClassNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClassNativeGenerator.java new file mode 100644 index 000000000..5f9b27d0f --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TClassNativeGenerator.java @@ -0,0 +1,34 @@ +package org.teavm.classlib.java.lang; + +import org.teavm.codegen.SourceWriter; +import org.teavm.javascript.ni.Generator; +import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class TClassNativeGenerator implements Generator { + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { + switch (methodRef.getClassName()) { + case "isInstance": + generateIsInstance(context, writer); + break; + case "isAssignable": + generateIsAssignableFrom(context, writer); + break; + } + } + + private void generateIsInstance(GeneratorContext context, SourceWriter writer) { + writer.append("return $rt_isInstance(").append(context.getParameterName(1)).append(", ") + .append(context.getParameterName(0)).append(".$data);").newLine(); + } + + private void generateIsAssignableFrom(GeneratorContext context, SourceWriter writer) { + writer.append("return $rt_isAssignable(").append(context.getParameterName(1)).append(".$data, ") + .append(context.getParameterName(0)).append(".$data;").newLine(); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TInterruptedException.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TInterruptedException.java new file mode 100644 index 000000000..ba7deef61 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TInterruptedException.java @@ -0,0 +1,9 @@ +package org.teavm.classlib.java.lang; + +/** + * + * @author Alexey Andreev + */ +public class TInterruptedException extends Exception { + private static final long serialVersionUID = -7832805114281254695L; +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java new file mode 100644 index 000000000..3e93fc065 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObject.java @@ -0,0 +1,59 @@ +package org.teavm.classlib.java.lang; + +import org.teavm.javascript.ni.GeneratedBy; +import org.teavm.javascript.ni.Rename; + +/** + * + * @author Alexey Andreev + */ +public class TObject { + public TObject() { + init(); + } + + @GeneratedBy(TObjectNativeGenerator.class) + private native void init(); + + @GeneratedBy(TObjectNativeGenerator.class) + @Rename("getClass") + public native final TClass getClass0(); + + @Override + @GeneratedBy(TObjectNativeGenerator.class) + public native int hashCode(); + + @GeneratedBy(TObjectNativeGenerator.class) + public native boolean equals(TObject other); + + @Override + @GeneratedBy(TObjectNativeGenerator.class) + protected native TObject clone(); + + @Rename("notify") + public final void notify0() { + } + + @Rename("notifyAll") + public final void notifyAll0() { + } + + @SuppressWarnings("unused") + @Rename("wait") + public final void wait0(long timeout) throws TInterruptedException { + } + + @SuppressWarnings("unused") + @Rename("wait") + public final void wait0(long timeout, int nanos) throws TInterruptedException { + } + + @SuppressWarnings("unused") + @Rename("wait") + public final void wait0() throws TInterruptedException { + } + + @Override + protected void finalize() throws TThrowable { + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObjectNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObjectNativeGenerator.java new file mode 100644 index 000000000..b1c327120 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TObjectNativeGenerator.java @@ -0,0 +1,69 @@ +package org.teavm.classlib.java.lang; + +import org.teavm.codegen.SourceWriter; +import org.teavm.javascript.ni.Generator; +import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; + +/** + * + * @author Alexey Andreev + */ +public class TObjectNativeGenerator implements Generator { + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { + switch (methodRef.getDescriptor().getName()) { + case "init": + generateInit(context, writer); + break; + case "getClass": + generateGetClass(context, writer); + break; + case "hashCode": + generateHashCode(context, writer); + break; + case "equals": + generateEquals(context, writer); + break; + case "clone": + generateClone(context, writer); + break; + } + } + + private void generateInit(GeneratorContext context, SourceWriter writer) { + writer.append(context.getParameterName(0)).append(".$id = $rt_nextId();").newLine(); + } + + private void generateGetClass(GeneratorContext context, SourceWriter writer) { + String thisArg = context.getParameterName(0); + String classClass = "java.lang.Class"; + writer.append("var cls = ").append(thisArg).append(".$class.classObject;").newLine(); + writer.append("if (cls === undefined) {").newLine().indent(); + writer.append("cls = ").appendClass(classClass) + .appendMethod(classClass, new MethodDescriptor("createNew", ValueType.object(classClass))) + .append("();").newLine(); + writer.append("cls.$data = ").append(thisArg).append(".$class;").newLine().outdent().append("}").newLine(); + writer.append("return cls;").newLine(); + } + + private void generateHashCode(GeneratorContext context, SourceWriter writer) { + writer.append("return ").append(context.getParameterName(0)).append(".$id;").newLine(); + } + + private void generateEquals(GeneratorContext context, SourceWriter writer) { + writer.append("return ").append(context.getParameterName(0)).append(" == ") + .append(context.getParameterName(1)).append(";").newLine(); + } + + private void generateClone(GeneratorContext context, SourceWriter writer) { + writer.append("var copy = new ").append(context.getParameterName(0)).append("obj.$class();").newLine(); + writer.append("for (var field in obj) {").newLine().indent(); + writer.append("if (!obj.hasOwnProperty(field)) {").newLine().indent(); + writer.append("continue;").newLine().outdent().append("}").newLine(); + writer.append("copy[field] = obj[field];").newLine().outdent().append("}").newLine(); + writer.append("return copy;").newLine(); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TString.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TString.java new file mode 100644 index 000000000..b421e4f2b --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TString.java @@ -0,0 +1,9 @@ +package org.teavm.classlib.java.lang; + +/** + * + * @author Alexey Andreev + */ +public class TString { + +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java new file mode 100644 index 000000000..74ecc98c4 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TThrowable.java @@ -0,0 +1,9 @@ +package org.teavm.classlib.java.lang; + +/** + * + * @author Alexey Andreev + */ +public class TThrowable extends Throwable { + private static final long serialVersionUID = 2026791432677149320L; +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Assert.java b/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Assert.java new file mode 100644 index 000000000..76784965d --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Assert.java @@ -0,0 +1,60 @@ +package org.teavm.classlib.org.junit; + +import org.teavm.javascript.ni.GeneratedBy; + +/** + * + * @author Alexey Andreev + */ +public class Assert { + public static void assertTrue(boolean condition) { + if (!condition) { + fail(); + } + } + + public static void assertFalse(boolean condition) { + if (condition) { + fail(); + } + } + + @GeneratedBy(AssertNativeGenerator.class) + public static native void fail(); + + public static void assertEquals(Object expected, Object actual) { + if (expected != null ? !expected.equals(actual) : actual != null) { + fail(); + } + } + + public static void assertNotEquals(Object expected, Object actual) { + if (expected != null ? expected.equals(actual) : actual == null) { + fail(); + } + } + + public static void assertNotNull(Object object) { + if (object == null) { + fail(); + } + } + + public static void assertNull(Object object) { + if (object != null) { + fail(); + } + } + + public static void assertSame(Object expected, Object actual) { + if (expected != actual) { + fail(); + } + } + + public static void assertNotSame(Object expected, Object actual) { + if (expected == actual) { + fail(); + } + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/AssertNativeGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/AssertNativeGenerator.java new file mode 100644 index 000000000..740177ec8 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/AssertNativeGenerator.java @@ -0,0 +1,25 @@ +package org.teavm.classlib.org.junit; + +import org.teavm.codegen.SourceWriter; +import org.teavm.javascript.ni.Generator; +import org.teavm.javascript.ni.GeneratorContext; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public class AssertNativeGenerator implements Generator { + @Override + public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) { + switch (methodRef.getDescriptor().getName()) { + case "fail": + generateFail(writer); + break; + } + } + + private void generateFail(SourceWriter writer) { + writer.append("throw new JUnitAssertionFailure();").newLine(); + } +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Test.java b/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Test.java new file mode 100644 index 000000000..4144b1262 --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlib/org/junit/Test.java @@ -0,0 +1,15 @@ +package org.teavm.classlib.org.junit; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Alexey Andreev + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Test { +} diff --git a/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java b/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java new file mode 100644 index 000000000..bf68024ae --- /dev/null +++ b/teavm-classlib/src/main/java/org/teavm/classlibgen/ClasslibTestGenerator.java @@ -0,0 +1,35 @@ +package org.teavm.classlibgen; + +import org.teavm.codegen.DefaultAliasProvider; +import org.teavm.codegen.DefaultNamingStrategy; +import org.teavm.codegen.SourceWriter; +import org.teavm.javascript.MethodDecompiler; +import org.teavm.javascript.Optimizer; +import org.teavm.javascript.Renderer; +import org.teavm.javascript.ast.RenderableMethod; +import org.teavm.model.ClassHolder; +import org.teavm.model.MethodHolder; +import org.teavm.model.resource.ClasspathClassHolderSource; + +/** + * + * @author Alexey Andreev + */ +public class ClasslibTestGenerator { + public static void main(String[] args) { + ClasspathClassHolderSource source = new ClasspathClassHolderSource(); + MethodDecompiler decompiler = new MethodDecompiler(source); + DefaultAliasProvider aliasProvider = new DefaultAliasProvider(); + DefaultNamingStrategy naming = new DefaultNamingStrategy(aliasProvider, source); + SourceWriter writer = new SourceWriter(naming); + Renderer renderer = new Renderer(writer, source); + Optimizer optimizer = new Optimizer(); + ClassHolder cls = source.getClassHolder("java.lang.Object"); + for (MethodHolder method : cls.getMethods()) { + RenderableMethod renderableMethod = decompiler.decompile(method); + optimizer.optimize(renderableMethod); + renderer.render(renderableMethod); + } + System.out.println(writer); + } +} diff --git a/teavm-classlib/src/main/resources/META-INF/teavm.properties b/teavm-classlib/src/main/resources/META-INF/teavm.properties new file mode 100644 index 000000000..4f7d49c24 --- /dev/null +++ b/teavm-classlib/src/main/resources/META-INF/teavm.properties @@ -0,0 +1,3 @@ +packagePrefix.java=org.teavm.classlib +classPrefix.java=T +packagePrefix.org.junit=org.teavm.classlib \ No newline at end of file diff --git a/teavm-core/pom.xml b/teavm-core/pom.xml index ad22d8690..c3339ebd5 100644 --- a/teavm-core/pom.xml +++ b/teavm-core/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 diff --git a/teavm-core/src/main/java/org/teavm/codegen/ConcurrentCachedMapper.java b/teavm-core/src/main/java/org/teavm/codegen/ConcurrentCachedMapper.java new file mode 100644 index 000000000..e172598fb --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/codegen/ConcurrentCachedMapper.java @@ -0,0 +1,55 @@ +package org.teavm.codegen; + +import java.util.Collection; +import java.util.HashSet; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.CountDownLatch; + +/** + * + * @author Alexey Andreev + */ +public class ConcurrentCachedMapper implements Mapper { + private Mapper innerMapper; + private ConcurrentMap> cache = new ConcurrentHashMap<>(); + + private static class Wrapper { + volatile S value; + volatile CountDownLatch latch = new CountDownLatch(1); + } + + public ConcurrentCachedMapper(Mapper innerMapper) { + this.innerMapper = innerMapper; + } + + @Override + public R map(T preimage) { + Wrapper wrapper = cache.get(preimage); + if (wrapper == null) { + wrapper = new Wrapper<>(); + Wrapper oldWrapper = cache.putIfAbsent(preimage, wrapper); + if (oldWrapper == null) { + wrapper.value = innerMapper.map(preimage); + wrapper.latch.countDown(); + wrapper.latch = null; + } else { + CountDownLatch latch = oldWrapper.latch; + try { + latch.await(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + } + return wrapper.value; + } + + public boolean caches(T preimage) { + return cache.get(preimage) != null; + } + + public Collection getCachedPreimages() { + return new HashSet<>(cache.keySet()); + } +} diff --git a/teavm-core/src/main/java/org/teavm/codegen/Mapper.java b/teavm-core/src/main/java/org/teavm/codegen/Mapper.java new file mode 100644 index 000000000..2fb54f5ea --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/codegen/Mapper.java @@ -0,0 +1,9 @@ +package org.teavm.codegen; + +/** + * + * @author Alexey Andreev + */ +public interface Mapper { + R map(T preimage); +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java new file mode 100644 index 000000000..1da3296ab --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyChecker.java @@ -0,0 +1,200 @@ +/* + * Copyright 2012 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.dependency; + +import java.util.Collection; +import java.util.HashSet; +import java.util.concurrent.*; +import org.teavm.codegen.ConcurrentCachedMapper; +import org.teavm.codegen.Mapper; +import org.teavm.model.*; + +/** + * + * @author Alexey Andreev + */ +public class DependencyChecker { + private static Object dummyValue = new Object(); + static final boolean shouldLog = System.getProperty("org.teavm.logDependencies", "false").equals("true"); + private ClassHolderSource classSource; + private ScheduledThreadPoolExecutor executor; + private ConcurrentCachedMapper methodCache; + private ConcurrentCachedMapper fieldCache; + private ConcurrentMap achievableClasses = new ConcurrentHashMap<>(); + private ConcurrentMap initializedClasses = new ConcurrentHashMap<>(); + private volatile RuntimeException exceptionOccured; + + public DependencyChecker(ClassHolderSource classSource) { + this(classSource, 1); + } + + public DependencyChecker(ClassHolderSource classSource, int numThreads) { + this.classSource = classSource; + executor = new ScheduledThreadPoolExecutor(numThreads); + methodCache = new ConcurrentCachedMapper<>(new Mapper() { + @Override public MethodGraph map(MethodReference preimage) { + return createMethodGraph(preimage); + } + }); + fieldCache = new ConcurrentCachedMapper<>(new Mapper() { + @Override public DependencyNode map(FieldReference preimage) { + return createFieldNode(preimage); + } + }); + } + + public DependencyNode createNode() { + return new DependencyNode(this); + } + + public ClassHolderSource getClassSource() { + return classSource; + } + + public void addEntryPoint(MethodReference methodRef, String... argumentTypes) { + ValueType[] parameters = methodRef.getDescriptor().getParameterTypes(); + if (parameters.length != argumentTypes.length) { + throw new IllegalArgumentException("argumentTypes length does not match the number of method's arguments"); + } + MethodGraph graph = attachMethodGraph(methodRef); + DependencyNode[] varNodes = graph.getVariableNodes(); + schedulePropagation(varNodes[0], methodRef.getClassName()); + for (int i = 0; i < argumentTypes.length; ++i) { + schedulePropagation(varNodes[i + 1], argumentTypes[i]); + } + } + + public void schedulePropagation(final DependencyConsumer targetNode, final String type) { + schedule(new Runnable() { + @Override public void run() { + targetNode.propagate(type); + } + }); + } + + void schedule(final Runnable runnable) { + executor.execute(new Runnable() { + @Override + public void run() { + try { + runnable.run(); + } catch (RuntimeException e) { + exceptionOccured = e; + executor.shutdownNow(); + } + } + }); + } + + public void checkDependencies() { + exceptionOccured = null; + while (true) { + try { + if (executor.awaitTermination(1, TimeUnit.SECONDS)) { + break; + } + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + break; + } + } + if (exceptionOccured != null) { + throw exceptionOccured; + } + } + + void achieveClass(String className) { + achievableClasses.put(className, dummyValue); + } + + public MethodGraph attachMethodGraph(MethodReference methodRef) { + return methodCache.map(methodRef); + } + + private void initClass(String className) { + MethodDescriptor clinitDesc = new MethodDescriptor(""); + while (className != null) { + if (initializedClasses.putIfAbsent(className, clinitDesc) != null) { + break; + } + ClassHolder cls = classSource.getClassHolder(className); + if (cls.getMethod(clinitDesc) != null) { + attachMethodGraph(new MethodReference(className, clinitDesc)); + } + className = cls.getParent(); + } + } + + private MethodGraph createMethodGraph(MethodReference methodRef) { + initClass(methodRef.getClassName()); + ClassHolder cls = classSource.getClassHolder(methodRef.getClassName()); + MethodHolder method = cls.getMethod(methodRef.getDescriptor()); + ValueType[] arguments = method.getParameterTypes(); + int paramCount = arguments.length + 1; + int varCount = Math.max(paramCount, method.getProgram().variableCount()); + DependencyNode[] parameterNodes = new DependencyNode[varCount]; + for (int i = 0; i < varCount; ++i) { + parameterNodes[i] = new DependencyNode(this); + if (shouldLog) { + parameterNodes[i].setTag(method.getOwner().getName() + "#" + + method.getName() + method.getDescriptor() + ":" + i); + } + } + DependencyNode resultNode; + if (method.getResultType() == ValueType.VOID) { + resultNode = null; + } else { + resultNode = new DependencyNode(this); + if (shouldLog) { + resultNode.setTag(method.getOwner().getName() + "#" + + method.getName() + MethodDescriptor.get(method) + ":RESULT"); + } + } + MethodGraph graph = new MethodGraph(parameterNodes, paramCount, resultNode, this); + DependencyGraphBuilder graphBuilder = new DependencyGraphBuilder(this); + graphBuilder.buildGraph(method, graph); + return graph; + } + + public boolean isMethodAchievable(MethodReference methodRef) { + return methodCache.caches(methodRef); + } + + public Collection getAchievableMethods() { + return methodCache.getCachedPreimages(); + } + + public Collection getAchievableFields() { + return fieldCache.getCachedPreimages(); + } + + public Collection getAchievableClasses() { + return new HashSet<>(achievableClasses.keySet()); + } + + public DependencyNode getFieldNode(FieldReference fieldRef) { + return fieldCache.map(fieldRef); + } + + private DependencyNode createFieldNode(FieldReference fieldRef) { + initClass(fieldRef.getClassName()); + DependencyNode node = new DependencyNode(this); + if (shouldLog) { + node.setTag(fieldRef.getClassName() + "#" + fieldRef.getFieldName()); + } + return node; + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyConsumer.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyConsumer.java new file mode 100644 index 000000000..1bfb152e7 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyConsumer.java @@ -0,0 +1,11 @@ +package org.teavm.dependency; + +/** + * + * @author Alexey Andreev + */ +public interface DependencyConsumer { + void propagate(String type); + + boolean hasType(String type); +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java new file mode 100644 index 000000000..e36d876dc --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyGraphBuilder.java @@ -0,0 +1,294 @@ +/* + * Copyright 2012 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.dependency; + +import java.util.Set; +import org.teavm.model.*; +import org.teavm.model.instructions.*; + +/** + * + * @author Alexey Andreev + */ +class DependencyGraphBuilder { + private DependencyChecker dependencyChecker; + private ClassHolderSource classSource; + private DependencyNode[] nodes; + private DependencyNode resultNode; + private Program program; + private ValueType resultType; + + public DependencyGraphBuilder(DependencyChecker dependencyChecker) { + this.dependencyChecker = dependencyChecker; + this.classSource = dependencyChecker.getClassSource(); + } + + public void buildGraph(MethodHolder method, MethodGraph graph) { + if (method.getProgram().basicBlockCount() == 0) { + return; + } + program = method.getProgram(); + resultType = method.getResultType(); + resultNode = graph.getResultNode(); + nodes = graph.getVariableNodes(); + for (int i = 0; i < program.basicBlockCount(); ++i) { + BasicBlock block = program.basicBlockAt(i); + for (Instruction insn : block.getInstructions()) { + insn.acceptVisitor(visitor); + } + for (Phi phi : block.getPhis()) { + for (Incoming incoming : phi.getIncomings()) { + nodes[incoming.getValue().getIndex()].connect(nodes[phi.getReceiver().getIndex()]); + } + } + } + } + + private static boolean hasBody(MethodHolder method) { + Set modifiers = method.getModifiers(); + return !modifiers.contains(ElementModifier.ABSTRACT) && + !modifiers.contains(ElementModifier.NATIVE); + } + + private static class VirtualCallPropagationListener implements DependencyConsumer { + private final DependencyNode node; + private final MethodDescriptor methodDesc; + private final DependencyChecker checker; + private final ValueType[] paramTypes; + private final DependencyNode[] parameters; + private final ValueType resultType; + private final DependencyNode result; + + public VirtualCallPropagationListener(DependencyNode node, MethodDescriptor methodDesc, + DependencyChecker checker, ValueType[] paramTypes, DependencyNode[] parameters, + ValueType resultType, DependencyNode result) { + this.node = node; + this.methodDesc = methodDesc; + this.checker = checker; + this.paramTypes = paramTypes; + this.parameters = parameters; + this.resultType = resultType; + this.result = result; + } + + @Override + public void propagate(String className) { + if (DependencyChecker.shouldLog) { + System.out.println("Virtual call of " + methodDesc + " detected on " + + node.getTag() + ". Target class is " + className); + } + MethodReference methodRef = new MethodReference(className, methodDesc); + MethodHolder method = findMethod(methodRef, checker.getClassSource()); + if (method == null) { + return; + } + MethodGraph targetGraph = checker.attachMethodGraph(methodRef); + DependencyNode[] targetParams = targetGraph.getVariableNodes(); + for (int i = 0; i < parameters.length; ++i) { + parameters[i].connect(targetParams[i]); + if (hasBody(method)) {// && isPossibleArrayPair(paramTypes[i], method.getProgram().variableAt(i))) { + targetParams[i].connect(parameters[i]); + } + } + if (targetGraph.getResultNode() != null) { + targetGraph.getResultNode().connect(result); + if (isPossibleArrayPair(method.getResultType(), resultType)) { + result.connect(targetGraph.getResultNode()); + } + } + } + + @Override + public boolean hasType(String type) { + return false; + } + } + + private static MethodHolder findMethod(MethodReference methodRef, ClassHolderSource classSource) { + String className = methodRef.getClassName(); + while (className != null) { + ClassHolder cls = classSource.getClassHolder(className); + if (cls == null) { + break; + } + MethodHolder method = cls.getMethod(methodRef.getDescriptor()); + if (method != null) { + return method; + } + className = cls.getParent(); + } + return null; + } + + private static MethodHolder requireMethod(MethodReference methodRef, ClassHolderSource classSource) { + MethodHolder method = findMethod(methodRef, classSource); + if (method == null) { + throw new IllegalStateException("Method not found: " + methodRef); + } + return method; + } + + private static FieldHolder findField(FieldReference fieldRef, ClassHolderSource classSource) { + String className = fieldRef.getClassName(); + while (className != null) { + ClassHolder cls = classSource.getClassHolder(className); + if (cls == null) { + break; + } + FieldHolder field = cls.getField(fieldRef.getFieldName()); + if (field != null) { + return field; + } + className = cls.getParent(); + } + return null; + } + + private static boolean isPossibleArrayPair(ValueType a, ValueType b) { + if (a instanceof ValueType.Array || b instanceof ValueType.Array) { + return true; + } + if (a == null || b == null) { + return false; + } + if (a.toString().equals("Ljava/lang/Object;") && b.toString().equals("Ljava/lang/Object;")) { + return true; + } + return false; + } + + private InstructionVisitor visitor = new InstructionVisitor() { + @Override + public void visit(IsInstanceInstruction insn) { + } + + @Override + public void visit(InvokeInstruction insn) { + } + + @Override + public void visit(PutElementInstruction insn) { + } + + @Override + public void visit(GetElementInstruction insn) { + } + + @Override + public void visit(CloneArrayInstruction insn) { + } + + @Override + public void visit(ArrayLengthInstruction insn) { + } + + @Override + public void visit(PutFieldInstruction insn) { + } + + @Override + public void visit(GetFieldInstruction insn) { + } + + @Override + public void visit(ConstructMultiArrayInstruction insn) { + } + + @Override + public void visit(ConstructInstruction insn) { + } + + @Override + public void visit(ConstructArrayInstruction insn) { + } + + @Override + public void visit(RaiseInstruction insn) { + } + + @Override + public void visit(ExitInstruction insn) { + } + + @Override + public void visit(SwitchInstruction insn) { + } + + @Override + public void visit(JumpInstruction insn) { + } + + @Override + public void visit(BinaryBranchingInstruction insn) { + } + + @Override + public void visit(BranchingInstruction insn) { + } + + @Override + public void visit(CastNumberInstruction insn) { + } + + @Override + public void visit(CastInstruction insn) { + } + + @Override + public void visit(AssignInstruction insn) { + } + + @Override + public void visit(NegateInstruction insn) { + } + + @Override + public void visit(BinaryInstruction insn) { + } + + @Override + public void visit(StringConstantInstruction insn) { + } + + @Override + public void visit(DoubleConstantInstruction insn) { + } + + @Override + public void visit(FloatConstantInstruction insn) { + } + + @Override + public void visit(LongConstantInstruction insn) { + } + + @Override + public void visit(IntegerConstantInstruction insn) { + } + + @Override + public void visit(NullConstantInstruction insn) { + } + + @Override + public void visit(ClassConstantInstruction insn) { + } + + @Override + public void visit(EmptyInstruction insn) { + } + }; +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java new file mode 100644 index 000000000..b33976bfe --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/DependencyNode.java @@ -0,0 +1,76 @@ +/* + * Copyright 2012 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.dependency; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * + * @author Alexey Andreev + */ +public class DependencyNode implements DependencyConsumer { + private DependencyChecker dependencyChecker; + private static final Object mapValue = new Object(); + private ConcurrentMap followers = new ConcurrentHashMap<>(); + private ConcurrentMap types = new ConcurrentHashMap<>(); + private volatile String tag; + + DependencyNode(DependencyChecker dependencyChecker) { + this.dependencyChecker = dependencyChecker; + } + + @Override + public void propagate(String type) { + if (types.putIfAbsent(type, mapValue) == null) { + if (DependencyChecker.shouldLog) { + System.out.println(tag + " -> " + type); + } + for (DependencyConsumer follower : followers.keySet().toArray(new DependencyConsumer[0])) { + if (follower.hasType(type)) { + dependencyChecker.schedulePropagation(follower, type); + } + } + } + } + + public void connect(DependencyConsumer follower) { + if (followers.putIfAbsent(follower, mapValue) == null) { + for (String type : types.keySet().toArray(new String[0])) { + if (follower.hasType(type)) { + dependencyChecker.schedulePropagation(follower, type); + } + } + } + } + + @Override + public boolean hasType(String type) { + return types.containsKey(type); + } + + public String[] getTypes() { + return types != null ? types.keySet().toArray(new String[types.size()]) : new String[0]; + } + + public String getTag() { + return tag; + } + + public void setTag(String tag) { + this.tag = tag; + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/MethodGraph.java b/teavm-core/src/main/java/org/teavm/dependency/MethodGraph.java new file mode 100644 index 000000000..020a4432f --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/MethodGraph.java @@ -0,0 +1,61 @@ +/* + * Copyright 2012 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.dependency; + +import java.util.Arrays; + +/** + * + * @author Alexey Andreev + */ +public class MethodGraph { + private DependencyNode[] variableNodes; + private int parameterCount; + private DependencyNode resultNode; + private DependencyNode sideEffectNode; + + MethodGraph(DependencyNode[] variableNodes, int parameterCount, DependencyNode resultNode, + DependencyChecker checker) { + this.variableNodes = Arrays.copyOf(variableNodes, variableNodes.length); + this.parameterCount = parameterCount; + this.resultNode = resultNode; + this.sideEffectNode = checker.createNode(); + } + + public DependencyNode[] getVariableNodes() { + return Arrays.copyOf(variableNodes, variableNodes.length); + } + + public int getVariableNodesCount() { + return variableNodes.length; + } + + public DependencyNode getVariableNode(int index) { + return variableNodes[index]; + } + + public int getParameterCount() { + return parameterCount; + } + + public DependencyNode getResultNode() { + return resultNode; + } + + public DependencyNode getSideEffectNode() { + return sideEffectNode; + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/TypeAnalyzer.java b/teavm-core/src/main/java/org/teavm/dependency/TypeAnalyzer.java new file mode 100644 index 000000000..14388d575 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/TypeAnalyzer.java @@ -0,0 +1,188 @@ +package org.teavm.dependency; + +import org.teavm.model.ClassHolderSource; +import org.teavm.model.MethodHolder; +import org.teavm.model.ValueType; +import org.teavm.model.Variable; +import org.teavm.model.instructions.*; + +/** + * + * @author Alexey Andreev + */ +class TypeAnalyzer implements InstructionVisitor { + private ClassHolderSource classSource; + private ValueType[] types; + private int[] definedVars; + + public TypeAnalyzer(ClassHolderSource classSource, int variableCount) { + types = new ValueType[variableCount]; + } + + @Override + public void visit(EmptyInstruction insn) { + } + + private void define(Variable var, ValueType type) { + types[var.getIndex()] = type; + definedVars = new int[] { var.getIndex() }; + } + + @Override + public void visit(ClassConstantInstruction insn) { + define(insn.getReceiver(), ValueType.object("java.lang.Class")); + } + + @Override + public void visit(NullConstantInstruction insn) { + define(insn.getReceiver(), ValueType.NULL); + } + + @Override + public void visit(IntegerConstantInstruction insn) { + define(insn.getReceiver(), ValueType.INTEGER); + } + + @Override + public void visit(LongConstantInstruction insn) { + define(insn.getReceiver(), ValueType.LONG); + } + + @Override + public void visit(FloatConstantInstruction insn) { + define(insn.getReceiver(), ValueType.FLOAT); + } + + @Override + public void visit(DoubleConstantInstruction insn) { + define(insn.getReceiver(), ValueType.DOUBLE); + } + + @Override + public void visit(StringConstantInstruction insn) { + define(insn.getReceiver(), ValueType.object("java.lang.String")); + } + + @Override + public void visit(BinaryInstruction insn) { + switch (insn.getOperation()) { + case ADD: + case SUBTRACT: + case MULTIPLY: + case DIVIDE: + case MODULO: + case SHIFT_LEFT: + case SHIFT_RIGHT: + case SHIFT_RIGHT_UNSIGNED: + case AND: + case OR: + case XOR: + define(insn.getReceiver(), map(insn.getOperandType())); + break; + case COMPARE: + define(insn.getReceiver(), ValueType.INTEGER); + break; + } + } + + private ValueType map(NumericOperandType type) { + switch (type) { + case INT: + return ValueType.INTEGER; + case LONG: + return ValueType.LONG; + case FLOAT: + return ValueType.FLOAT; + case DOUBLE: + return ValueType.DOUBLE; + } + throw new AssertionError("Unknown type: " + type); + } + + @Override + public void visit(NegateInstruction insn) { + define(insn.getReceiver(), map(insn.getOperandType())); + } + + @Override + public void visit(AssignInstruction insn) { + define(insn.getReceiver(), types[insn.getAssignee().getIndex()]); + } + + @Override + public void visit(CastInstruction insn) { + define(insn.getReceiver(), insn.getTargetType()); + } + + @Override + public void visit(CastNumberInstruction insn) { + } + + @Override + public void visit(BranchingInstruction insn) { + } + + @Override + public void visit(BinaryBranchingInstruction insn) { + } + + @Override + public void visit(JumpInstruction insn) { + } + + @Override + public void visit(SwitchInstruction insn) { + } + + @Override + public void visit(ExitInstruction insn) { + } + + @Override + public void visit(RaiseInstruction insn) { + } + + @Override + public void visit(ConstructArrayInstruction insn) { + } + + @Override + public void visit(ConstructInstruction insn) { + } + + @Override + public void visit(ConstructMultiArrayInstruction insn) { + } + + @Override + public void visit(GetFieldInstruction insn) { + } + + @Override + public void visit(PutFieldInstruction insn) { + } + + @Override + public void visit(ArrayLengthInstruction insn) { + } + + @Override + public void visit(CloneArrayInstruction insn) { + } + + @Override + public void visit(GetElementInstruction insn) { + } + + @Override + public void visit(PutElementInstruction insn) { + } + + @Override + public void visit(InvokeInstruction insn) { + } + + @Override + public void visit(IsInstanceInstruction insn) { + } +} diff --git a/teavm-core/src/main/java/org/teavm/dependency/TypePropagationListener.java b/teavm-core/src/main/java/org/teavm/dependency/TypePropagationListener.java new file mode 100644 index 000000000..e26dcb844 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/dependency/TypePropagationListener.java @@ -0,0 +1,24 @@ +/* + * Copyright 2012 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.dependency; + +/** + * + * @author Alexey Andreev + */ +public interface TypePropagationListener { + void typeAdded(DependencyNode node, String type); +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/MethodDecompiler.java b/teavm-core/src/main/java/org/teavm/javascript/MethodDecompiler.java index c36e60030..c40b98489 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/MethodDecompiler.java +++ b/teavm-core/src/main/java/org/teavm/javascript/MethodDecompiler.java @@ -232,7 +232,7 @@ public class MethodDecompiler { } public static void main(String... args) throws IOException { - ClassHolderSource source = new ClassHolderSource(); + MutableClassHolderSource source = new MutableClassHolderSource(); ClassHolder arrayListCls = Parser.parseClass(readClass(ArrayList.class.getName())); source.putClassHolder(arrayListCls); source.putClassHolder(Parser.parseClass(readClass(AbstractList.class.getName()))); diff --git a/teavm-core/src/main/java/org/teavm/javascript/ast/RenderableMethod.java b/teavm-core/src/main/java/org/teavm/javascript/ast/RenderableMethod.java index bf5584257..6ac74590d 100644 --- a/teavm-core/src/main/java/org/teavm/javascript/ast/RenderableMethod.java +++ b/teavm-core/src/main/java/org/teavm/javascript/ast/RenderableMethod.java @@ -27,7 +27,6 @@ public class RenderableMethod { private int variableCount; public RenderableMethod(MethodHolder metadata) { - super(); this.metadata = metadata; } diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratedBy.java b/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratedBy.java new file mode 100644 index 000000000..aac6fe9e8 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratedBy.java @@ -0,0 +1,16 @@ +package org.teavm.javascript.ni; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Alexey Andreev + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface GeneratedBy { + Class value(); +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/Generator.java b/teavm-core/src/main/java/org/teavm/javascript/ni/Generator.java new file mode 100644 index 000000000..329a397ca --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/Generator.java @@ -0,0 +1,12 @@ +package org.teavm.javascript.ni; + +import org.teavm.codegen.SourceWriter; +import org.teavm.model.MethodReference; + +/** + * + * @author Alexey Andreev + */ +public interface Generator { + void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef); +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java b/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java new file mode 100644 index 000000000..ab215c949 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/GeneratorContext.java @@ -0,0 +1,9 @@ +package org.teavm.javascript.ni; + +/** + * + * @author Alexey Andreev + */ +public interface GeneratorContext { + String getParameterName(int index); +} diff --git a/teavm-core/src/main/java/org/teavm/javascript/ni/Rename.java b/teavm-core/src/main/java/org/teavm/javascript/ni/Rename.java new file mode 100644 index 000000000..dea50f7ab --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/javascript/ni/Rename.java @@ -0,0 +1,16 @@ +package org.teavm.javascript.ni; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * + * @author Alexey Andreev + */ +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.METHOD) +public @interface Rename { + String value(); +} diff --git a/teavm-core/src/main/java/org/teavm/model/ClassHolderSource.java b/teavm-core/src/main/java/org/teavm/model/ClassHolderSource.java index 8153d418b..4a545f17a 100644 --- a/teavm-core/src/main/java/org/teavm/model/ClassHolderSource.java +++ b/teavm-core/src/main/java/org/teavm/model/ClassHolderSource.java @@ -1,35 +1,9 @@ -/* - * Copyright 2012 Alexey Andreev. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ package org.teavm.model; -import java.util.HashMap; -import java.util.Map; - /** * - * @author Alexey Andreev + * @author konsoletyper */ -public class ClassHolderSource { - private Map classes = new HashMap<>(); - - public ClassHolder getClassHolder(String name) { - return classes.get(name); - } - - public void putClassHolder(ClassHolder classHolder) { - classes.put(classHolder.getName(), classHolder); - } -} +public interface ClassHolderSource { + ClassHolder getClassHolder(String name); +} \ No newline at end of file diff --git a/teavm-core/src/main/java/org/teavm/model/ElementHolder.java b/teavm-core/src/main/java/org/teavm/model/ElementHolder.java index ee8c258c7..be771fb19 100644 --- a/teavm-core/src/main/java/org/teavm/model/ElementHolder.java +++ b/teavm-core/src/main/java/org/teavm/model/ElementHolder.java @@ -49,10 +49,6 @@ public abstract class ElementHolder { return name; } - public void setName(String name) { - this.name = name; - } - public Map getAnnotations() { return annotations; } diff --git a/teavm-core/src/main/java/org/teavm/model/FieldReference.java b/teavm-core/src/main/java/org/teavm/model/FieldReference.java new file mode 100644 index 000000000..1c7394441 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/FieldReference.java @@ -0,0 +1,40 @@ +package org.teavm.model; + +/** + * + * @author konsoletyper + */ +public class FieldReference { + private String className; + private String fieldName; + + public FieldReference(String className, String fieldName) { + this.className = className; + this.fieldName = fieldName; + } + + public String getClassName() { + return className; + } + + public String getFieldName() { + return fieldName; + } + + @Override + public int hashCode() { + return className.hashCode() ^ fieldName.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (!(obj instanceof FieldReference)) { + return false; + } + FieldReference other = (FieldReference)obj; + return className.equals(other.className) && fieldName.equals(other.fieldName); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/MethodReference.java b/teavm-core/src/main/java/org/teavm/model/MethodReference.java new file mode 100644 index 000000000..6c22af75f --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/MethodReference.java @@ -0,0 +1,45 @@ +package org.teavm.model; + +/** + * + * @author konsoletyper + */ +public class MethodReference { + private String className; + private MethodDescriptor descriptor; + + public MethodReference(String className, MethodDescriptor descriptor) { + this.className = className; + this.descriptor = descriptor; + } + + public String getClassName() { + return className; + } + + public MethodDescriptor getDescriptor() { + return descriptor; + } + + @Override + public int hashCode() { + return className.hashCode() ^ descriptor.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return false; + } + if (!(obj instanceof MethodReference)) { + return false; + } + MethodReference other = (MethodReference)obj; + return className.equals(other.className) && descriptor.equals(other.descriptor); + } + + @Override + public String toString() { + return className + "." + descriptor; + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/MutableClassHolderSource.java b/teavm-core/src/main/java/org/teavm/model/MutableClassHolderSource.java new file mode 100644 index 000000000..6248e84c1 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/MutableClassHolderSource.java @@ -0,0 +1,36 @@ +/* + * Copyright 2012 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.model; + +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +/** + * + * @author Alexey Andreev + */ +public class MutableClassHolderSource implements ClassHolderSource { + private ConcurrentMap classes = new ConcurrentHashMap<>(); + + @Override + public ClassHolder getClassHolder(String name) { + return classes.get(name); + } + + public void putClassHolder(ClassHolder classHolder) { + classes.put(classHolder.getName(), classHolder); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/package-info.java b/teavm-core/src/main/java/org/teavm/model/package-info.java index 07eb567c9..b002ea412 100644 --- a/teavm-core/src/main/java/org/teavm/model/package-info.java +++ b/teavm-core/src/main/java/org/teavm/model/package-info.java @@ -4,7 +4,7 @@ * it allows to disassemble method bodies into three-address code that is very * close to JVM bytecode (see {@link org.teavm.instructions}. * - *

The entry point is some implementation of {@link ClassHolderSource} class. + *

The entry point is some implementation of {@link MutableClassHolderSource} class. * */ package org.teavm.model; \ No newline at end of file diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ClasspathClassHolderSource.java b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathClassHolderSource.java new file mode 100644 index 000000000..b5d892689 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathClassHolderSource.java @@ -0,0 +1,28 @@ +package org.teavm.model.resource; + +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderSource; + +/** + * + * @author Alexey Andreev + */ +public class ClasspathClassHolderSource implements ClassHolderSource { + private MapperClassHolderSource innerClassSource; + + public ClasspathClassHolderSource(ClassLoader classLoader) { + ClasspathResourceReader reader = new ClasspathResourceReader(); + ResourceClassHolderMapper rawMapper = new ResourceClassHolderMapper(reader); + ClasspathResourceMapper classPathMapper = new ClasspathResourceMapper(classLoader, rawMapper); + innerClassSource = new MapperClassHolderSource(classPathMapper); + } + + public ClasspathClassHolderSource() { + this(ClasspathClassHolderSource.class.getClassLoader()); + } + + @Override + public ClassHolder getClassHolder(String name) { + return innerClassSource.getClassHolder(name); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceMapper.java b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceMapper.java new file mode 100644 index 000000000..6efe96e84 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceMapper.java @@ -0,0 +1,83 @@ +package org.teavm.model.resource; + +import java.io.IOException; +import java.io.InputStream; +import java.net.URL; +import java.util.*; +import org.teavm.codegen.Mapper; +import org.teavm.model.ClassHolder; + +/** + * + * @author Alexey Andreev + */ +public class ClasspathResourceMapper implements Mapper { + private static String PACKAGE_PREFIX = "packagePrefix."; + private static String CLASS_PREFIX = "classPrefix."; + private Mapper innerMapper; + private List transformations = new ArrayList<>(); + + private static class Transformation { + String packageName; + String packagePrefix = ""; + String classPrefix = ""; + } + + public ClasspathResourceMapper(ClassLoader classLoader, Mapper innerMapper) { + this.innerMapper = innerMapper; + try { + Enumeration resources = classLoader.getResources("META-INF/teavm.properties"); + Map transformationMap = new HashMap<>(); + while (resources.hasMoreElements()) { + URL resource = resources.nextElement(); + Properties properties = new Properties(); + try (InputStream input = resource.openStream()) { + properties.load(input); + } + loadProperties(properties, transformationMap); + } + transformations.addAll(transformationMap.values()); + } catch (IOException e) { + throw new RuntimeException("Error reading resources", e); + } + } + + private void loadProperties(Properties properties, Map cache) { + for (String propertyName : properties.stringPropertyNames()) { + if (propertyName.startsWith(PACKAGE_PREFIX)) { + String packageName = propertyName.substring(PACKAGE_PREFIX.length()); + Transformation transformation = getTransformation(cache, packageName); + transformation.packagePrefix = properties.getProperty(propertyName) + "."; + } else if (propertyName.startsWith(CLASS_PREFIX)) { + String packageName = propertyName.substring(CLASS_PREFIX.length()); + Transformation transformation = getTransformation(cache, packageName); + transformation.classPrefix = properties.getProperty(propertyName); + } + } + } + + private Transformation getTransformation(Map cache, String packageName) { + Transformation transformation = cache.get(packageName); + if (transformation == null) { + transformation = new Transformation(); + transformation.packageName = packageName; + cache.put(packageName, transformation); + } + return transformation; + } + + @Override + public ClassHolder map(String name) { + for (Transformation transformation : transformations) { + if (name.startsWith(transformation.packageName)) { + int index = name.lastIndexOf('.'); + String className = name.substring(index + 1); + String packageName = name.substring(0, index); + ClassHolder classHolder = innerMapper.map(transformation.packagePrefix + "." + packageName + + "." + transformation.classPrefix + className); + return classHolder; + } + } + return innerMapper.map(name); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceReader.java b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceReader.java new file mode 100644 index 000000000..7b2a84bb1 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/resource/ClasspathResourceReader.java @@ -0,0 +1,30 @@ +package org.teavm.model.resource; + +import java.io.IOException; +import java.io.InputStream; + +/** + * + * @author Alexey Andreev + */ +public class ClasspathResourceReader implements ResourceReader { + private ClassLoader classLoader; + + public ClasspathResourceReader(ClassLoader classLoader) { + this.classLoader = classLoader; + } + + public ClasspathResourceReader() { + this(ClasspathResourceReader.class.getClassLoader()); + } + + @Override + public boolean hasResource(String name) { + return classLoader.getResource(name) != null; + } + + @Override + public InputStream openResource(String name) throws IOException { + return classLoader.getResourceAsStream(name); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/resource/MapperClassHolderSource.java b/teavm-core/src/main/java/org/teavm/model/resource/MapperClassHolderSource.java new file mode 100644 index 000000000..781df8bbb --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/resource/MapperClassHolderSource.java @@ -0,0 +1,23 @@ +package org.teavm.model.resource; + +import org.teavm.codegen.ConcurrentCachedMapper; +import org.teavm.codegen.Mapper; +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderSource; + +/** + * + * @author Alexey Andreev + */ +public class MapperClassHolderSource implements ClassHolderSource { + private Mapper mapper; + + public MapperClassHolderSource(Mapper mapper) { + this.mapper = new ConcurrentCachedMapper<>(mapper); + } + + @Override + public ClassHolder getClassHolder(String name) { + return mapper.map(name); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ResourceClassHolderMapper.java b/teavm-core/src/main/java/org/teavm/model/resource/ResourceClassHolderMapper.java new file mode 100644 index 000000000..e2bc77066 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/resource/ResourceClassHolderMapper.java @@ -0,0 +1,37 @@ +package org.teavm.model.resource; + +import java.io.IOException; +import java.io.InputStream; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; +import org.teavm.codegen.Mapper; +import org.teavm.model.ClassHolder; +import org.teavm.parsing.Parser; + +/** + * + * @author konsoletyper + */ +public class ResourceClassHolderMapper implements Mapper { + private ResourceReader resourceReader; + + public ResourceClassHolderMapper(ResourceReader resourceReader) { + this.resourceReader = resourceReader; + } + + @Override + public ClassHolder map(String name) { + ClassNode clsNode = new ClassNode(); + String resourceName = name.replace('.', '/') + ".class"; + if (!resourceReader.hasResource(resourceName)) { + return null; + } + try (InputStream input = resourceReader.openResource(resourceName)) { + ClassReader reader = new ClassReader(input); + reader.accept(clsNode, 0); + } catch (IOException e) { + throw new RuntimeException(e); + } + return Parser.parseClass(clsNode); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ResourceParser.java b/teavm-core/src/main/java/org/teavm/model/resource/ResourceParser.java new file mode 100644 index 000000000..ad04a0720 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/resource/ResourceParser.java @@ -0,0 +1,33 @@ +package org.teavm.model.resource; + +import java.io.IOException; +import java.io.InputStream; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.tree.ClassNode; +import org.teavm.codegen.Mapper; +import org.teavm.model.ClassHolder; +import org.teavm.parsing.Parser; + +/** + * + * @author Alexey Andreev + */ +public class ResourceParser implements Mapper { + private ResourceReader resourceReader; + + public ResourceParser(ResourceReader resourceReader) { + this.resourceReader = resourceReader; + } + + @Override + public ClassHolder map(String name) { + ClassNode clsNode = new ClassNode(); + try (InputStream input = resourceReader.openResource(name.replace('.', '/') + ".class")) { + ClassReader reader = new ClassReader(input); + reader.accept(clsNode, 0); + } catch (IOException e) { + throw new RuntimeException(e); + } + return Parser.parseClass(clsNode); + } +} diff --git a/teavm-core/src/main/java/org/teavm/model/resource/ResourceReader.java b/teavm-core/src/main/java/org/teavm/model/resource/ResourceReader.java new file mode 100644 index 000000000..d342dae29 --- /dev/null +++ b/teavm-core/src/main/java/org/teavm/model/resource/ResourceReader.java @@ -0,0 +1,14 @@ +package org.teavm.model.resource; + +import java.io.IOException; +import java.io.InputStream; + +/** + * + * @author konsoletyper + */ +public interface ResourceReader { + boolean hasResource(String name); + + InputStream openResource(String name) throws IOException; +} diff --git a/teavm-core/src/main/resources/org/teavm/javascript/runtime.js b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js new file mode 100644 index 000000000..ad13d3c9a --- /dev/null +++ b/teavm-core/src/main/resources/org/teavm/javascript/runtime.js @@ -0,0 +1,316 @@ +$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 $rt_isAssignable(obj.$class, 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 = { + createBooleanArray : function(cls, sz) { + var arr = $rt.createArray(cls, sz); + for (var i = 0; i < sz; i = (i + 1) | 0) { + arr[i] = false; + } + return arr; + }, + createNumericArray : function(cls, sz) { + var arr = $rt.createArray(cls, sz); + for (var i = 0; i < sz; i = (i + 1) | 0) { + arr[i] = 0; + } + return arr; + }, + createLongArray : function(sz) { + var arr = $rt.createArray($rt.longcls(), sz); + for (var i = 0; i < sz; i = (i + 1) | 0) { + arr[i] = Long.ZERO; + } + return arr; + }, + createArray : function(cls, sz) { + var arr = new Array(sz); + arr.$class = $rt.arraycls(cls); + arr.$id = $rt.lastObjectId++; + for (var i = 0; i < sz; i = (i + 1) | 0) { + arr[i] = null; + } + return arr; + }, + createMultiArray : function(cls, dimensions) { + for (var i = 1; i < dimensions.length; i = (i + 1) | 0) { + cls = $rt.arraycls(cls); + } + return $rt.createMultiArrayImpl(cls, dimensions, 0); + }, + createMultiArrayImpl : function(cls, dimensions, offset) { + var result = $rt.createArray(cls, dimensions[offset]); + offset = (offset + 1) | 0; + if (offset < dimensions.length) { + cls = cls.$meta.item; + for (var i = 0; i < result.length; i = (i + 1) | 0) { + result[i] = $rt.createMultiArrayImpl(cls, dimensions, offset); + } + } + return result; + }, + initializeArray : function(cls, initial) { + var arr = initial.slice(); + arr.$class = $rt.arraycls(cls); + $rt.setId(arr, $rt.lastObjectId++); + return arr; + }, + arraycls : function(cls) { + if (cls.$array == undefined) { + cls.$array = { + $meta : { item : cls }, + }; + if ($rt.objcls) { + cls.$array.$meta.supertypes = [$rt.objcls()]; + } + } + return cls.$array; + }, + createcls : function() { + return { + $meta : { + supertypes : [] + } + }; + }, + booleancls : function() { + if ($rt.booleanclsCache == null) { + $rt.booleanclsCache = $rt.createcls(); + } + return $rt.booleanclsCache; + }, + charcls : function() { + if ($rt.charclsCache == null) { + $rt.charclsCache = $rt.createcls(); + } + return $rt.charclsCache; + }, + bytecls : function() { + if ($rt.byteclsCache == null) { + $rt.byteclsCache = $rt.createcls(); + } + return $rt.byteclsCache; + }, + shortcls : function() { + if ($rt.shortclsCache == null) { + $rt.shortclsCache = $rt.createcls(); + } + return $rt.shortclsCache; + }, + intcls : function() { + if ($rt.intclsCache == null) { + $rt.intclsCache = $rt.createcls(); + } + return $rt.intclsCache; + }, + longcls : function() { + if ($rt.longclsCache == null) { + $rt.longclsCache = $rt.createcls(); + } + return $rt.longclsCache; + }, + floatcls : function() { + if ($rt.floatclsCache == null) { + $rt.floatclsCache = $rt.createcls(); + } + return $rt.floatclsCache; + }, + doublecls : function() { + if ($rt.doubleclsCache == null) { + $rt.doubleclsCache = $rt.createcls(); + } + return $rt.doubleclsCache; + }, + voidcls : function() { + if ($rt.voidclsCache == null) { + $rt.voidclsCache = $rt.createcls(); + } + return $rt.voidclsCache; + }, + 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; + } + }, + clinit : function(cls) { + if (cls.$clinit) { + var f = cls.$clinit; + delete cls.$clinit; + f(); + } + return cls; + }, + init : function(cls, constructor, args) { + var obj = new cls(); + cls.prototype[constructor].apply(obj, args); + return obj; + }, + assertNotNaN : function(value) { + if (typeof value == 'number' && isNaN(value)) { + throw "NaN"; + } + return value; + } +}; + +Long = function(lo, hi) { + this.lo = lo | 0; + this.hi = hi | 0; +} +Long.ZERO = new Long(0, 0); +Long.fromInt = function(val) { + return new Long(val, 0); +} +Long.fromNumber = function(val) { + return new Long(val | 0, (val / 0x100000000) | 0); +} +Long.toNumber = function(val) { + return val.lo + 0x100000000 * val.hi; +} +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 - a.hi; + if (r != 0) { + return r; + } + return a.lo - b.lo; +} +Long.isNegative = function(a) { + return a.hi < 0; +} +Long.mul = 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_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; + return new Long((lolo & 0xFFFF) | ((lohi & 0xFFFF) << 16), + (hilo & 0xFFFF) | ((hihi & 0xFFFF) << 16)); +} +Long.div = function(a, b) { + var result = (a.hi * 0x100000000 + a.lo) / (b.hi * 0x100000000 + b.lo); + return new Long(result | 0, (result / 0x100000000) | 0); +} +Long.rem = function(a, b) { + var result = (a.hi * 0x100000000 + a.lo) % (b.hi * 0x100000000 + b.lo); + return new Long(result | 0, (result / 0x100000000) | 0); +} +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); + } +} \ No newline at end of file