mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Call <clinit> methods before main method when possible, eliminate
lazy class initialization for these <clinit> methods.
This commit is contained in:
parent
955ac92035
commit
de84105241
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.impl;
|
package org.teavm.classlib.impl;
|
||||||
|
|
||||||
import org.teavm.classlib.java.lang.TCharacter;
|
|
||||||
|
|
||||||
public final class IntegerUtil {
|
public final class IntegerUtil {
|
||||||
private IntegerUtil() {
|
private IntegerUtil() {
|
||||||
}
|
}
|
||||||
|
@ -34,7 +32,7 @@ public final class IntegerUtil {
|
||||||
int pos = (sz - 1) * radixLog2;
|
int pos = (sz - 1) * radixLog2;
|
||||||
int target = 0;
|
int target = 0;
|
||||||
while (pos >= 0) {
|
while (pos >= 0) {
|
||||||
chars[target++] = TCharacter.forDigit((value >>> pos) & mask, radix);
|
chars[target++] = Character.forDigit((value >>> pos) & mask, radix);
|
||||||
pos -= radixLog2;
|
pos -= radixLog2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +52,7 @@ public final class IntegerUtil {
|
||||||
long pos = (sz - 1) * radixLog2;
|
long pos = (sz - 1) * radixLog2;
|
||||||
int target = 0;
|
int target = 0;
|
||||||
while (pos >= 0) {
|
while (pos >= 0) {
|
||||||
chars[target++] = TCharacter.forDigit((int) (value >>> pos) & mask, radix);
|
chars[target++] = Character.forDigit((int) (value >>> pos) & mask, radix);
|
||||||
pos -= radixLog2;
|
pos -= radixLog2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -151,7 +151,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
||||||
|
|
||||||
appendProperty(writer, "getter", false, () -> {
|
appendProperty(writer, "getter", false, () -> {
|
||||||
if (accessibleFields != null && accessibleFields.contains(field.getName())) {
|
if (accessibleFields != null && accessibleFields.contains(field.getName())) {
|
||||||
renderGetter(writer, field);
|
renderGetter(context, writer, field);
|
||||||
} else {
|
} else {
|
||||||
writer.append("null");
|
writer.append("null");
|
||||||
}
|
}
|
||||||
|
@ -159,7 +159,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
||||||
|
|
||||||
appendProperty(writer, "setter", false, () -> {
|
appendProperty(writer, "setter", false, () -> {
|
||||||
if (accessibleFields != null && accessibleFields.contains(field.getName())) {
|
if (accessibleFields != null && accessibleFields.contains(field.getName())) {
|
||||||
renderSetter(writer, field);
|
renderSetter(context, writer, field);
|
||||||
} else {
|
} else {
|
||||||
writer.append("null");
|
writer.append("null");
|
||||||
}
|
}
|
||||||
|
@ -199,7 +199,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
||||||
|
|
||||||
appendProperty(writer, "callable", false, () -> {
|
appendProperty(writer, "callable", false, () -> {
|
||||||
if (accessibleMethods != null && accessibleMethods.contains(method.getDescriptor())) {
|
if (accessibleMethods != null && accessibleMethods.contains(method.getDescriptor())) {
|
||||||
renderCallable(writer, method);
|
renderCallable(context, writer, method);
|
||||||
} else {
|
} else {
|
||||||
writer.append("null");
|
writer.append("null");
|
||||||
}
|
}
|
||||||
|
@ -239,18 +239,18 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
||||||
value.render();
|
value.render();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderGetter(SourceWriter writer, FieldReader field) throws IOException {
|
private void renderGetter(GeneratorContext context, SourceWriter writer, FieldReader field) throws IOException {
|
||||||
writer.append("function(obj)").ws().append("{").indent().softNewLine();
|
writer.append("function(obj)").ws().append("{").indent().softNewLine();
|
||||||
initClass(writer, field);
|
initClass(context, writer, field);
|
||||||
writer.append("return ");
|
writer.append("return ");
|
||||||
boxIfNecessary(writer, field.getType(), () -> fieldAccess(writer, field));
|
boxIfNecessary(writer, field.getType(), () -> fieldAccess(writer, field));
|
||||||
writer.append(";").softNewLine();
|
writer.append(";").softNewLine();
|
||||||
writer.outdent().append("}");
|
writer.outdent().append("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderSetter(SourceWriter writer, FieldReader field) throws IOException {
|
private void renderSetter(GeneratorContext context, SourceWriter writer, FieldReader field) throws IOException {
|
||||||
writer.append("function(obj,").ws().append("val)").ws().append("{").indent().softNewLine();
|
writer.append("function(obj,").ws().append("val)").ws().append("{").indent().softNewLine();
|
||||||
initClass(writer, field);
|
initClass(context, writer, field);
|
||||||
fieldAccess(writer, field);
|
fieldAccess(writer, field);
|
||||||
writer.ws().append('=').ws();
|
writer.ws().append('=').ws();
|
||||||
unboxIfNecessary(writer, field.getType(), () -> writer.append("val"));
|
unboxIfNecessary(writer, field.getType(), () -> writer.append("val"));
|
||||||
|
@ -258,10 +258,10 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
||||||
writer.outdent().append("}");
|
writer.outdent().append("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderCallable(SourceWriter writer, MethodReader method) throws IOException {
|
private void renderCallable(GeneratorContext context, SourceWriter writer, MethodReader method) throws IOException {
|
||||||
writer.append("function(obj,").ws().append("args)").ws().append("{").indent().softNewLine();
|
writer.append("function(obj,").ws().append("args)").ws().append("{").indent().softNewLine();
|
||||||
|
|
||||||
initClass(writer, method);
|
initClass(context, writer, method);
|
||||||
|
|
||||||
if (method.getResultType() != ValueType.VOID) {
|
if (method.getResultType() != ValueType.VOID) {
|
||||||
writer.append("return ");
|
writer.append("return ");
|
||||||
|
@ -290,8 +290,8 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
||||||
writer.outdent().append("}");
|
writer.outdent().append("}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initClass(SourceWriter writer, MemberReader member) throws IOException {
|
private void initClass(GeneratorContext context, SourceWriter writer, MemberReader member) throws IOException {
|
||||||
if (member.hasModifier(ElementModifier.STATIC)) {
|
if (member.hasModifier(ElementModifier.STATIC) && context.isDynamicInitializer(member.getOwnerName())) {
|
||||||
writer.appendClassInit(member.getOwnerName()).append("();").softNewLine();
|
writer.appendClassInit(member.getOwnerName()).append("();").softNewLine();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 Alexey Andreev.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
|
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||||
|
import org.teavm.dependency.DependencyAgent;
|
||||||
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
|
import org.teavm.dependency.MethodDependency;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public class StringNativeGenerator implements Generator, DependencyPlugin {
|
||||||
|
@Override
|
||||||
|
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||||
|
if (methodRef.getName().equals("intern")) {
|
||||||
|
writer.append("return $rt_intern(").append(context.getParameterName(0)).append(");").softNewLine();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
|
if (method.getReference().getName().equals("intern")) {
|
||||||
|
agent.linkMethod(new MethodReference(String.class, "hashCode", int.class))
|
||||||
|
.propagate(0, agent.getType("java.lang.String"))
|
||||||
|
.use();
|
||||||
|
agent.linkMethod(new MethodReference(String.class, "equals", Object.class, boolean.class))
|
||||||
|
.propagate(0, agent.getType("java.lang.String"))
|
||||||
|
.propagate(1, agent.getType("java.lang.String"))
|
||||||
|
.use();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -41,6 +41,7 @@ import org.teavm.classlib.java.lang.reflect.TModifier;
|
||||||
import org.teavm.dependency.PluggableDependency;
|
import org.teavm.dependency.PluggableDependency;
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.DelegateTo;
|
import org.teavm.interop.DelegateTo;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
import org.teavm.interop.Unmanaged;
|
import org.teavm.interop.Unmanaged;
|
||||||
import org.teavm.jso.core.JSArray;
|
import org.teavm.jso.core.JSArray;
|
||||||
import org.teavm.platform.Platform;
|
import org.teavm.platform.Platform;
|
||||||
|
@ -209,6 +210,7 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(ClassGenerator.class)
|
@GeneratedBy(ClassGenerator.class)
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
private static native void createMetadata();
|
private static native void createMetadata();
|
||||||
|
|
||||||
public TField[] getFields() throws TSecurityException {
|
public TField[] getFields() throws TSecurityException {
|
||||||
|
|
|
@ -19,6 +19,7 @@ import java.io.ByteArrayInputStream;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
import org.teavm.classlib.impl.Base64Impl;
|
import org.teavm.classlib.impl.Base64Impl;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
import org.teavm.jso.JSIndexer;
|
import org.teavm.jso.JSIndexer;
|
||||||
import org.teavm.jso.JSObject;
|
import org.teavm.jso.JSObject;
|
||||||
|
@ -68,6 +69,7 @@ public abstract class TClassLoader extends TObject {
|
||||||
private static native String resourceToString(JSObject resource);
|
private static native String resourceToString(JSObject resource);
|
||||||
|
|
||||||
@InjectedBy(ClassLoaderNativeGenerator.class)
|
@InjectedBy(ClassLoaderNativeGenerator.class)
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
private static native ResourceContainer supplyResources();
|
private static native ResourceContainer supplyResources();
|
||||||
|
|
||||||
interface ResourceContainer extends JSObject {
|
interface ResourceContainer extends JSObject {
|
||||||
|
|
|
@ -16,9 +16,11 @@
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
|
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
public class TDouble extends TNumber implements TComparable<TDouble> {
|
public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||||
public static final double POSITIVE_INFINITY = 1 / 0.0;
|
public static final double POSITIVE_INFINITY = 1 / 0.0;
|
||||||
public static final double NEGATIVE_INFINITY = -POSITIVE_INFINITY;
|
public static final double NEGATIVE_INFINITY = -POSITIVE_INFINITY;
|
||||||
|
@ -208,6 +210,7 @@ public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||||
return (int) (h >>> 32) ^ (int) h;
|
return (int) (h >>> 32) ^ (int) h;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
public static native int compare(double a, double b);
|
public static native int compare(double a, double b);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
|
|
||||||
|
@ -237,6 +238,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
||||||
return isInfinite(value);
|
return isInfinite(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
public static native int compare(float f1, float f2);
|
public static native int compare(float f1, float f2);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
import static org.teavm.classlib.impl.IntegerUtil.toUnsignedLogRadixString;
|
import static org.teavm.classlib.impl.IntegerUtil.toUnsignedLogRadixString;
|
||||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
|
|
||||||
public class TInteger extends TNumber implements TComparable<TInteger> {
|
public class TInteger extends TNumber implements TComparable<TInteger> {
|
||||||
public static final int SIZE = 32;
|
public static final int SIZE = 32;
|
||||||
|
@ -251,6 +252,7 @@ public class TInteger extends TNumber implements TComparable<TInteger> {
|
||||||
return compare(value, other.value);
|
return compare(value, other.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
public static native int compare(int x, int y);
|
public static native int compare(int x, int y);
|
||||||
|
|
||||||
public static int numberOfLeadingZeros(int i) {
|
public static int numberOfLeadingZeros(int i) {
|
||||||
|
@ -356,8 +358,10 @@ public class TInteger extends TNumber implements TComparable<TInteger> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@InjectedBy(IntegerNativeGenerator.class)
|
@InjectedBy(IntegerNativeGenerator.class)
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
public static native int divideUnsigned(int dividend, int divisor);
|
public static native int divideUnsigned(int dividend, int divisor);
|
||||||
|
|
||||||
@InjectedBy(IntegerNativeGenerator.class)
|
@InjectedBy(IntegerNativeGenerator.class)
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
public static native int remainderUnsigned(int dividend, int divisor);
|
public static native int remainderUnsigned(int dividend, int divisor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
import static org.teavm.classlib.impl.IntegerUtil.toUnsignedLogRadixString;
|
import static org.teavm.classlib.impl.IntegerUtil.toUnsignedLogRadixString;
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
|
|
||||||
public class TLong extends TNumber implements TComparable<TLong> {
|
public class TLong extends TNumber implements TComparable<TLong> {
|
||||||
public static final long MIN_VALUE = -0x8000000000000000L;
|
public static final long MIN_VALUE = -0x8000000000000000L;
|
||||||
|
@ -212,6 +213,7 @@ public class TLong extends TNumber implements TComparable<TLong> {
|
||||||
return other instanceof TLong && ((TLong) other).value == value;
|
return other instanceof TLong && ((TLong) other).value == value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
public static native int compare(long a, long b);
|
public static native int compare(long a, long b);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -351,8 +353,10 @@ public class TLong extends TNumber implements TComparable<TLong> {
|
||||||
}
|
}
|
||||||
|
|
||||||
@GeneratedBy(LongNativeGenerator.class)
|
@GeneratedBy(LongNativeGenerator.class)
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
public static native long divideUnsigned(long dividend, long divisor);
|
public static native long divideUnsigned(long dividend, long divisor);
|
||||||
|
|
||||||
@GeneratedBy(LongNativeGenerator.class)
|
@GeneratedBy(LongNativeGenerator.class)
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
public static native long remainderUnsigned(long dividend, long divisor);
|
public static native long remainderUnsigned(long dividend, long divisor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,8 +16,10 @@
|
||||||
package org.teavm.classlib.java.lang;
|
package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
|
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
public final class TMath extends TObject {
|
public final class TMath extends TObject {
|
||||||
public static final double E = 2.71828182845904523536;
|
public static final double E = 2.71828182845904523536;
|
||||||
public static final double PI = 3.14159265358979323846;
|
public static final double PI = 3.14159265358979323846;
|
||||||
|
|
|
@ -21,6 +21,7 @@ import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.Async;
|
import org.teavm.interop.Async;
|
||||||
import org.teavm.interop.AsyncCallback;
|
import org.teavm.interop.AsyncCallback;
|
||||||
import org.teavm.interop.DelegateTo;
|
import org.teavm.interop.DelegateTo;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
import org.teavm.interop.Rename;
|
import org.teavm.interop.Rename;
|
||||||
import org.teavm.interop.Structure;
|
import org.teavm.interop.Structure;
|
||||||
import org.teavm.interop.Superclass;
|
import org.teavm.interop.Superclass;
|
||||||
|
@ -272,6 +273,7 @@ public class TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@DelegateTo("hashCodeLowLevelImpl")
|
@DelegateTo("hashCodeLowLevelImpl")
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
private static native int hashCodeLowLevel(TObject obj);
|
private static native int hashCodeLowLevel(TObject obj);
|
||||||
|
|
||||||
@Unmanaged
|
@Unmanaged
|
||||||
|
@ -280,6 +282,7 @@ public class TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@DelegateTo("setHashCodeLowLevelImpl")
|
@DelegateTo("setHashCodeLowLevelImpl")
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
private static native void setHashCodeLowLevel(TObject obj, int value);
|
private static native void setHashCodeLowLevel(TObject obj, int value);
|
||||||
|
|
||||||
@Unmanaged
|
@Unmanaged
|
||||||
|
@ -300,6 +303,7 @@ public class TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@DelegateTo("identityOrMonitorLowLevel")
|
@DelegateTo("identityOrMonitorLowLevel")
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
private native int identityOrMonitor();
|
private native int identityOrMonitor();
|
||||||
|
|
||||||
private static int identityOrMonitorLowLevel(RuntimeObject object) {
|
private static int identityOrMonitorLowLevel(RuntimeObject object) {
|
||||||
|
@ -307,6 +311,7 @@ public class TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@DelegateTo("setIdentityLowLevel")
|
@DelegateTo("setIdentityLowLevel")
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
native void setIdentity(int id);
|
native void setIdentity(int id);
|
||||||
|
|
||||||
private static void setIdentityLowLevel(RuntimeObject object, int id) {
|
private static void setIdentityLowLevel(RuntimeObject object, int id) {
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.classlib.java.lang;
|
||||||
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.classlib.java.io.TSerializable;
|
import org.teavm.classlib.java.io.TSerializable;
|
||||||
import org.teavm.classlib.java.io.TUnsupportedEncodingException;
|
import org.teavm.classlib.java.io.TUnsupportedEncodingException;
|
||||||
import org.teavm.classlib.java.nio.TByteBuffer;
|
import org.teavm.classlib.java.nio.TByteBuffer;
|
||||||
|
@ -26,10 +27,10 @@ import org.teavm.classlib.java.nio.charset.impl.TUTF8Charset;
|
||||||
import org.teavm.classlib.java.util.TArrays;
|
import org.teavm.classlib.java.util.TArrays;
|
||||||
import org.teavm.classlib.java.util.TComparator;
|
import org.teavm.classlib.java.util.TComparator;
|
||||||
import org.teavm.classlib.java.util.TFormatter;
|
import org.teavm.classlib.java.util.TFormatter;
|
||||||
import org.teavm.classlib.java.util.THashMap;
|
|
||||||
import org.teavm.classlib.java.util.TLocale;
|
import org.teavm.classlib.java.util.TLocale;
|
||||||
import org.teavm.classlib.java.util.TMap;
|
|
||||||
import org.teavm.classlib.java.util.regex.TPattern;
|
import org.teavm.classlib.java.util.regex.TPattern;
|
||||||
|
import org.teavm.dependency.PluggableDependency;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
|
|
||||||
public class TString extends TObject implements TSerializable, TComparable<TString>, TCharSequence {
|
public class TString extends TObject implements TSerializable, TComparable<TString>, TCharSequence {
|
||||||
public static final TComparator<TString> CASE_INSENSITIVE_ORDER = (o1, o2) -> o1.compareToIgnoreCase(o2);
|
public static final TComparator<TString> CASE_INSENSITIVE_ORDER = (o1, o2) -> o1.compareToIgnoreCase(o2);
|
||||||
|
@ -625,14 +626,10 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
|
||||||
return toUpperCase();
|
return toUpperCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
public TString intern() {
|
@GeneratedBy(StringNativeGenerator.class)
|
||||||
TString interned = PoolHolder.pool.get(this);
|
@PluggableDependency(StringNativeGenerator.class)
|
||||||
if (interned == null) {
|
@DoesNotModifyStaticFields
|
||||||
interned = this;
|
public native TString intern();
|
||||||
PoolHolder.pool.put(interned, interned);
|
|
||||||
}
|
|
||||||
return interned;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean matches(String regex) {
|
public boolean matches(String regex) {
|
||||||
return TPattern.matches(regex, this.toString());
|
return TPattern.matches(regex, this.toString());
|
||||||
|
@ -705,8 +702,4 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
|
||||||
}
|
}
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
static class PoolHolder {
|
|
||||||
static TMap<TString, TString> pool = new THashMap<>();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.teavm.classlib.java.io.TPrintStream;
|
||||||
import org.teavm.classlib.java.lang.reflect.TArray;
|
import org.teavm.classlib.java.lang.reflect.TArray;
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.DelegateTo;
|
import org.teavm.interop.DelegateTo;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
import org.teavm.interop.Unmanaged;
|
import org.teavm.interop.Unmanaged;
|
||||||
import org.teavm.runtime.Allocator;
|
import org.teavm.runtime.Allocator;
|
||||||
|
@ -103,6 +104,7 @@ public final class TSystem extends TObject {
|
||||||
|
|
||||||
@GeneratedBy(SystemNativeGenerator.class)
|
@GeneratedBy(SystemNativeGenerator.class)
|
||||||
@DelegateTo("doArrayCopyLowLevel")
|
@DelegateTo("doArrayCopyLowLevel")
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
private static native void doArrayCopy(Object src, int srcPos, Object dest, int destPos, int length);
|
private static native void doArrayCopy(Object src, int srcPos, Object dest, int destPos, int length);
|
||||||
|
|
||||||
@Unmanaged
|
@Unmanaged
|
||||||
|
@ -124,6 +126,7 @@ public final class TSystem extends TObject {
|
||||||
|
|
||||||
@DelegateTo("currentTimeMillisLowLevel")
|
@DelegateTo("currentTimeMillisLowLevel")
|
||||||
@GeneratedBy(SystemNativeGenerator.class)
|
@GeneratedBy(SystemNativeGenerator.class)
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
public static native long currentTimeMillis();
|
public static native long currentTimeMillis();
|
||||||
|
|
||||||
private static long currentTimeMillisLowLevel() {
|
private static long currentTimeMillisLowLevel() {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.teavm.classlib.java.lang.TNullPointerException;
|
||||||
import org.teavm.classlib.java.lang.TObject;
|
import org.teavm.classlib.java.lang.TObject;
|
||||||
import org.teavm.dependency.PluggableDependency;
|
import org.teavm.dependency.PluggableDependency;
|
||||||
import org.teavm.interop.DelegateTo;
|
import org.teavm.interop.DelegateTo;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
import org.teavm.interop.Unmanaged;
|
import org.teavm.interop.Unmanaged;
|
||||||
import org.teavm.platform.PlatformClass;
|
import org.teavm.platform.PlatformClass;
|
||||||
import org.teavm.runtime.Allocator;
|
import org.teavm.runtime.Allocator;
|
||||||
|
@ -35,6 +36,7 @@ public final class TArray extends TObject {
|
||||||
@GeneratedBy(ArrayNativeGenerator.class)
|
@GeneratedBy(ArrayNativeGenerator.class)
|
||||||
@PluggableDependency(ArrayNativeGenerator.class)
|
@PluggableDependency(ArrayNativeGenerator.class)
|
||||||
@DelegateTo("getLengthLowLevel")
|
@DelegateTo("getLengthLowLevel")
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
public static native int getLength(TObject array) throws TIllegalArgumentException;
|
public static native int getLength(TObject array) throws TIllegalArgumentException;
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
@ -63,6 +65,7 @@ public final class TArray extends TObject {
|
||||||
|
|
||||||
@GeneratedBy(ArrayNativeGenerator.class)
|
@GeneratedBy(ArrayNativeGenerator.class)
|
||||||
@DelegateTo("newInstanceLowLevel")
|
@DelegateTo("newInstanceLowLevel")
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
private static native TObject newInstanceImpl(PlatformClass componentType, int length);
|
private static native TObject newInstanceImpl(PlatformClass componentType, int length);
|
||||||
|
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
|
@ -89,9 +92,11 @@ public final class TArray extends TObject {
|
||||||
|
|
||||||
@GeneratedBy(ArrayNativeGenerator.class)
|
@GeneratedBy(ArrayNativeGenerator.class)
|
||||||
@PluggableDependency(ArrayNativeGenerator.class)
|
@PluggableDependency(ArrayNativeGenerator.class)
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
private static native TObject getImpl(TObject array, int index);
|
private static native TObject getImpl(TObject array, int index);
|
||||||
|
|
||||||
@GeneratedBy(ArrayNativeGenerator.class)
|
@GeneratedBy(ArrayNativeGenerator.class)
|
||||||
@PluggableDependency(ArrayNativeGenerator.class)
|
@PluggableDependency(ArrayNativeGenerator.class)
|
||||||
|
@DoesNotModifyStaticFields
|
||||||
private static native void setImpl(TObject array, int index, TObject value);
|
private static native void setImpl(TObject array, int index, TObject value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -153,7 +153,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||||
classInitializerTransformer = new ClassInitializerTransformer();
|
classInitializerTransformer = new ClassInitializerTransformer();
|
||||||
shadowStackTransformer = new ShadowStackTransformer(characteristics);
|
shadowStackTransformer = new ShadowStackTransformer(characteristics);
|
||||||
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
|
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource(),
|
||||||
|
controller.getClassInitializerInfo());
|
||||||
nullCheckInsertion = new NullCheckInsertion(characteristics);
|
nullCheckInsertion = new NullCheckInsertion(characteristics);
|
||||||
nullCheckTransformation = new NullCheckTransformation();
|
nullCheckTransformation = new NullCheckTransformation();
|
||||||
|
|
||||||
|
|
|
@ -142,7 +142,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
@Override
|
@Override
|
||||||
public void setController(TeaVMTargetController controller) {
|
public void setController(TeaVMTargetController controller) {
|
||||||
this.controller = controller;
|
this.controller = controller;
|
||||||
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
|
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource(),
|
||||||
|
controller.getClassInitializerInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -350,7 +351,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
RenderingContext renderingContext = new RenderingContext(debugEmitterToUse,
|
RenderingContext renderingContext = new RenderingContext(debugEmitterToUse,
|
||||||
controller.getUnprocessedClassSource(), classes,
|
controller.getUnprocessedClassSource(), classes,
|
||||||
controller.getClassLoader(), controller.getServices(), controller.getProperties(), naming,
|
controller.getClassLoader(), controller.getServices(), controller.getProperties(), naming,
|
||||||
controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m));
|
controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m),
|
||||||
|
controller.getClassInitializerInfo());
|
||||||
renderingContext.setMinifying(minifying);
|
renderingContext.setMinifying(minifying);
|
||||||
Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods,
|
Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods,
|
||||||
controller.getDiagnostics(), renderingContext);
|
controller.getDiagnostics(), renderingContext);
|
||||||
|
|
|
@ -76,7 +76,7 @@ public class Renderer implements RenderingManager {
|
||||||
private RenderingContext context;
|
private RenderingContext context;
|
||||||
private List<PostponedFieldInitializer> postponedFieldInitializers = new ArrayList<>();
|
private List<PostponedFieldInitializer> postponedFieldInitializers = new ArrayList<>();
|
||||||
private IntFunction<TeaVMProgressFeedback> progressConsumer = p -> TeaVMProgressFeedback.CONTINUE;
|
private IntFunction<TeaVMProgressFeedback> progressConsumer = p -> TeaVMProgressFeedback.CONTINUE;
|
||||||
private static final MethodDescriptor CLINIT_METHOD = new MethodDescriptor("<clinit>", ValueType.VOID);
|
public static final MethodDescriptor CLINIT_METHOD = new MethodDescriptor("<clinit>", ValueType.VOID);
|
||||||
|
|
||||||
private ObjectIntMap<String> sizeByClass = new ObjectIntHashMap<>();
|
private ObjectIntMap<String> sizeByClass = new ObjectIntHashMap<>();
|
||||||
private int stringPoolSize;
|
private int stringPoolSize;
|
||||||
|
@ -379,7 +379,7 @@ public class Renderer implements RenderingManager {
|
||||||
try {
|
try {
|
||||||
MethodReader clinit = classSource.get(cls.getName()).getMethod(CLINIT_METHOD);
|
MethodReader clinit = classSource.get(cls.getName()).getMethod(CLINIT_METHOD);
|
||||||
|
|
||||||
if (clinit != null) {
|
if (clinit != null && context.isDynamicInitializer(cls.getName())) {
|
||||||
renderCallClinit(clinit, cls);
|
renderCallClinit(clinit, cls);
|
||||||
}
|
}
|
||||||
if (!cls.getClassHolder().getModifiers().contains(ElementModifier.INTERFACE)) {
|
if (!cls.getClassHolder().getModifiers().contains(ElementModifier.INTERFACE)) {
|
||||||
|
@ -541,7 +541,7 @@ public class Renderer implements RenderingManager {
|
||||||
writer.append(cls.getClassHolder().getLevel().ordinal()).append(',').ws();
|
writer.append(cls.getClassHolder().getLevel().ordinal()).append(',').ws();
|
||||||
|
|
||||||
MethodReader clinit = classSource.get(cls.getName()).getMethod(CLINIT_METHOD);
|
MethodReader clinit = classSource.get(cls.getName()).getMethod(CLINIT_METHOD);
|
||||||
if (clinit != null) {
|
if (clinit != null && context.isDynamicInitializer(cls.getName())) {
|
||||||
writer.appendClassInit(cls.getName());
|
writer.appendClassInit(cls.getName());
|
||||||
} else {
|
} else {
|
||||||
writer.append('0');
|
writer.append('0');
|
||||||
|
@ -1112,6 +1112,11 @@ public class Renderer implements RenderingManager {
|
||||||
public void useLongLibrary() {
|
public void useLongLibrary() {
|
||||||
longLibraryUsed = true;
|
longLibraryUsed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDynamicInitializer(String className) {
|
||||||
|
return context.isDynamicInitializer(className);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void appendMonitor(StatementRenderer statementRenderer, MethodNode methodNode) throws IOException {
|
private void appendMonitor(StatementRenderer statementRenderer, MethodNode methodNode) throws IOException {
|
||||||
|
@ -1132,7 +1137,7 @@ public class Renderer implements RenderingManager {
|
||||||
FieldReference field;
|
FieldReference field;
|
||||||
String value;
|
String value;
|
||||||
|
|
||||||
public PostponedFieldInitializer(FieldReference field, String value) {
|
PostponedFieldInitializer(FieldReference field, String value) {
|
||||||
this.field = field;
|
this.field = field;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,7 @@ import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.TextLocation;
|
import org.teavm.model.TextLocation;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||||
|
|
||||||
public class RenderingContext {
|
public class RenderingContext {
|
||||||
private final DebugInformationEmitter debugEmitter;
|
private final DebugInformationEmitter debugEmitter;
|
||||||
|
@ -60,12 +61,13 @@ public class RenderingContext {
|
||||||
private final List<String> readonlyStringPool = Collections.unmodifiableList(stringPool);
|
private final List<String> readonlyStringPool = Collections.unmodifiableList(stringPool);
|
||||||
private final Map<MethodReference, InjectorHolder> injectorMap = new HashMap<>();
|
private final Map<MethodReference, InjectorHolder> injectorMap = new HashMap<>();
|
||||||
private boolean minifying;
|
private boolean minifying;
|
||||||
|
private ClassInitializerInfo classInitializerInfo;
|
||||||
|
|
||||||
public RenderingContext(DebugInformationEmitter debugEmitter,
|
public RenderingContext(DebugInformationEmitter debugEmitter,
|
||||||
ClassReaderSource initialClassSource, ListableClassReaderSource classSource,
|
ClassReaderSource initialClassSource, ListableClassReaderSource classSource,
|
||||||
ClassLoader classLoader, ServiceRepository services, Properties properties,
|
ClassLoader classLoader, ServiceRepository services, Properties properties,
|
||||||
NamingStrategy naming, DependencyInfo dependencyInfo,
|
NamingStrategy naming, DependencyInfo dependencyInfo,
|
||||||
Predicate<MethodReference> virtualPredicate) {
|
Predicate<MethodReference> virtualPredicate, ClassInitializerInfo classInitializerInfo) {
|
||||||
this.debugEmitter = debugEmitter;
|
this.debugEmitter = debugEmitter;
|
||||||
this.initialClassSource = initialClassSource;
|
this.initialClassSource = initialClassSource;
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
|
@ -75,6 +77,7 @@ public class RenderingContext {
|
||||||
this.naming = naming;
|
this.naming = naming;
|
||||||
this.dependencyInfo = dependencyInfo;
|
this.dependencyInfo = dependencyInfo;
|
||||||
this.virtualPredicate = virtualPredicate;
|
this.virtualPredicate = virtualPredicate;
|
||||||
|
this.classInitializerInfo = classInitializerInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ClassReaderSource getInitialClassSource() {
|
public ClassReaderSource getInitialClassSource() {
|
||||||
|
@ -117,6 +120,10 @@ public class RenderingContext {
|
||||||
return virtualPredicate.test(method);
|
return virtualPredicate.test(method);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isDynamicInitializer(String className) {
|
||||||
|
return classInitializerInfo.isDynamicInitializer(className);
|
||||||
|
}
|
||||||
|
|
||||||
public void pushLocation(TextLocation location) {
|
public void pushLocation(TextLocation location) {
|
||||||
LocationStackEntry prevEntry = locationStack.peek();
|
LocationStackEntry prevEntry = locationStack.peek();
|
||||||
if (location != null) {
|
if (location != null) {
|
||||||
|
|
|
@ -151,16 +151,29 @@ public class RuntimeRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderRuntimeIntern() throws IOException {
|
private void renderRuntimeIntern() throws IOException {
|
||||||
|
if (!needInternMethod()) {
|
||||||
writer.append("function $rt_intern(str) {").indent().softNewLine();
|
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, STRING_INTERN_METHOD))
|
|
||||||
.append("(str);").softNewLine();
|
|
||||||
} else {
|
|
||||||
writer.append("return str;").softNewLine();
|
writer.append("return str;").softNewLine();
|
||||||
|
writer.outdent().append("}").softNewLine();
|
||||||
|
} else {
|
||||||
|
renderHandWrittenRuntime("intern.js");
|
||||||
|
writer.append("function $rt_stringHash(s)").ws().append("{").indent().softNewLine();
|
||||||
|
writer.append("return ").appendMethodBody(String.class, "hashCode", int.class)
|
||||||
|
.append("(s);").softNewLine();
|
||||||
|
writer.outdent().append("}").softNewLine();
|
||||||
|
writer.append("function $rt_stringEquals(a,").ws().append("b)").ws().append("{").indent().softNewLine();
|
||||||
|
writer.append("return ").appendMethodBody(String.class, "equals", Object.class, boolean.class)
|
||||||
|
.append("(a").ws().append(",b);").softNewLine();
|
||||||
|
writer.outdent().append("}").softNewLine();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.outdent().append("}").newLine();
|
private boolean needInternMethod() {
|
||||||
|
ClassReader cls = classSource.get(STRING_CLASS);
|
||||||
|
if (cls == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return cls.getMethod(STRING_INTERN_METHOD) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void renderRuntimeObjcls() throws IOException {
|
private void renderRuntimeObjcls() throws IOException {
|
||||||
|
|
|
@ -49,4 +49,6 @@ public interface GeneratorContext extends ServiceRepository {
|
||||||
void typeToClassString(SourceWriter writer, ValueType type);
|
void typeToClassString(SourceWriter writer, ValueType type);
|
||||||
|
|
||||||
void useLongLibrary();
|
void useLongLibrary();
|
||||||
|
|
||||||
|
boolean isDynamicInitializer(String className);
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,7 +164,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||||
classInitializerTransformer = new ClassInitializerTransformer();
|
classInitializerTransformer = new ClassInitializerTransformer();
|
||||||
shadowStackTransformer = new ShadowStackTransformer(managedMethodRepository);
|
shadowStackTransformer = new ShadowStackTransformer(managedMethodRepository);
|
||||||
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
|
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource(),
|
||||||
|
controller.getClassInitializerInfo());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,306 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 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.analysis;
|
||||||
|
|
||||||
|
import com.carrotsearch.hppc.ObjectByteHashMap;
|
||||||
|
import com.carrotsearch.hppc.ObjectByteMap;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.teavm.dependency.DependencyInfo;
|
||||||
|
import org.teavm.dependency.MethodDependencyInfo;
|
||||||
|
import org.teavm.dependency.ValueDependencyInfo;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
|
import org.teavm.model.BasicBlockReader;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
|
import org.teavm.model.ClassReader;
|
||||||
|
import org.teavm.model.ElementModifier;
|
||||||
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ProgramReader;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.VariableReader;
|
||||||
|
import org.teavm.model.instructions.AbstractInstructionReader;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.optimization.Devirtualization;
|
||||||
|
|
||||||
|
public class ClassInitializerAnalysis implements ClassInitializerInfo {
|
||||||
|
private static final MethodDescriptor CLINIT = new MethodDescriptor("<clinit>", void.class);
|
||||||
|
private static final byte BEING_ANALYZED = 1;
|
||||||
|
private static final byte DYNAMIC = 2;
|
||||||
|
private static final byte STATIC = 3;
|
||||||
|
private ObjectByteMap<String> classStatuses = new ObjectByteHashMap<>();
|
||||||
|
private Map<MethodReference, MethodInfo> methodInfoMap = new HashMap<>();
|
||||||
|
private ListableClassReaderSource classes;
|
||||||
|
private ClassHierarchy hierarchy;
|
||||||
|
private List<String> order = new ArrayList<>();
|
||||||
|
private List<? extends String> readonlyOrder = Collections.unmodifiableList(order);
|
||||||
|
private String currentAnalyzedClass;
|
||||||
|
private DependencyInfo dependencyInfo;
|
||||||
|
|
||||||
|
public ClassInitializerAnalysis(ListableClassReaderSource classes, ClassHierarchy hierarchy) {
|
||||||
|
this.classes = classes;
|
||||||
|
this.hierarchy = hierarchy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void analyze(DependencyInfo dependencyInfo) {
|
||||||
|
if (methodInfoMap == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.dependencyInfo = dependencyInfo;
|
||||||
|
|
||||||
|
for (String className : classes.getClassNames()) {
|
||||||
|
analyze(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
methodInfoMap = null;
|
||||||
|
classes = null;
|
||||||
|
hierarchy = null;
|
||||||
|
this.dependencyInfo = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDynamicInitializer(String className) {
|
||||||
|
return classStatuses.get(className) != STATIC;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends String> getInitializationOrder() {
|
||||||
|
return readonlyOrder;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void analyze(String className) {
|
||||||
|
byte classStatus = classStatuses.get(className);
|
||||||
|
switch (classStatus) {
|
||||||
|
case BEING_ANALYZED:
|
||||||
|
if (!className.equals(currentAnalyzedClass)) {
|
||||||
|
classStatuses.put(className, DYNAMIC);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
case DYNAMIC:
|
||||||
|
case STATIC:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClassReader cls = classes.get(className);
|
||||||
|
|
||||||
|
if (cls == null) {
|
||||||
|
classStatuses.put(className, STATIC);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
classStatuses.put(className, BEING_ANALYZED);
|
||||||
|
String previousClass = currentAnalyzedClass;
|
||||||
|
currentAnalyzedClass = className;
|
||||||
|
|
||||||
|
MethodReader initializer = cls.getMethod(CLINIT);
|
||||||
|
boolean isStatic = true;
|
||||||
|
if (initializer != null) {
|
||||||
|
MethodInfo initializerInfo = analyzeMethod(initializer);
|
||||||
|
if (isDynamicInitializer(initializerInfo, className)) {
|
||||||
|
isStatic = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAnalyzedClass = previousClass;
|
||||||
|
if (classStatuses.get(className) == BEING_ANALYZED) {
|
||||||
|
classStatuses.put(className, isStatic ? STATIC : DYNAMIC);
|
||||||
|
if (isStatic && initializer != null) {
|
||||||
|
order.add(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isDynamicInitializer(MethodInfo methodInfo, String className) {
|
||||||
|
if (methodInfo.anyFieldModified) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (methodInfo.classesWithModifiedFields != null) {
|
||||||
|
for (String affectedClass : methodInfo.classesWithModifiedFields) {
|
||||||
|
if (!affectedClass.equals(className)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private MethodInfo analyzeMethod(MethodReader method) {
|
||||||
|
MethodInfo methodInfo = methodInfoMap.get(method.getReference());
|
||||||
|
if (methodInfo == null) {
|
||||||
|
methodInfo = new MethodInfo(method.getReference());
|
||||||
|
methodInfoMap.put(method.getReference(), methodInfo);
|
||||||
|
|
||||||
|
String currentClass = method.getDescriptor().equals(CLINIT) ? method.getOwnerName() : null;
|
||||||
|
InstructionAnalyzer reader = new InstructionAnalyzer(currentClass, methodInfo);
|
||||||
|
ProgramReader program = method.getProgram();
|
||||||
|
if (program == null) {
|
||||||
|
methodInfo.anyFieldModified = true;
|
||||||
|
if (method.getAnnotations().get(DoesNotModifyStaticFields.class.getName()) != null) {
|
||||||
|
methodInfo.anyFieldModified = false;
|
||||||
|
} else {
|
||||||
|
ClassReader containingClass = classes.get(method.getOwnerName());
|
||||||
|
if (containingClass.getAnnotations().get(DoesNotModifyStaticFields.class.getName()) != null) {
|
||||||
|
methodInfo.anyFieldModified = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (BasicBlockReader block : program.getBasicBlocks()) {
|
||||||
|
block.readAllInstructions(reader);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method.hasModifier(ElementModifier.SYNCHRONIZED)) {
|
||||||
|
reader.initClass("java.lang.Thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
methodInfo.complete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return methodInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
class InstructionAnalyzer extends AbstractInstructionReader {
|
||||||
|
String currentClass;
|
||||||
|
MethodInfo methodInfo;
|
||||||
|
MethodDependencyInfo methodDep;
|
||||||
|
|
||||||
|
InstructionAnalyzer(String currentClass, MethodInfo methodInfo) {
|
||||||
|
this.currentClass = currentClass;
|
||||||
|
this.methodInfo = methodInfo;
|
||||||
|
methodDep = dependencyInfo.getMethod(methodInfo.method);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void stringConstant(VariableReader receiver, String cst) {
|
||||||
|
analyzeInitializer("java.lang.String");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void create(VariableReader receiver, String type) {
|
||||||
|
analyzeInitializer(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void getField(VariableReader receiver, VariableReader instance, FieldReference field,
|
||||||
|
ValueType fieldType) {
|
||||||
|
if (instance == null) {
|
||||||
|
analyzeInitializer(field.getClassName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void putField(VariableReader instance, FieldReference field, VariableReader value, ValueType fieldType) {
|
||||||
|
if (instance == null) {
|
||||||
|
analyzeInitializer(field.getClassName());
|
||||||
|
if (!methodInfo.anyFieldModified && !field.getClassName().equals(currentClass)) {
|
||||||
|
if (methodInfo.classesWithModifiedFields == null) {
|
||||||
|
methodInfo.classesWithModifiedFields = new HashSet<>();
|
||||||
|
}
|
||||||
|
methodInfo.classesWithModifiedFields.add(field.getClassName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invoke(VariableReader receiver, VariableReader instance, MethodReference method,
|
||||||
|
List<? extends VariableReader> arguments, InvocationType type) {
|
||||||
|
if (type == InvocationType.VIRTUAL) {
|
||||||
|
ValueDependencyInfo instanceDep = methodDep.getVariable(instance.getIndex());
|
||||||
|
Set<MethodReference> implementations = Devirtualization.implementations(hierarchy, dependencyInfo,
|
||||||
|
instanceDep.getTypes(), method);
|
||||||
|
for (MethodReference implementation : implementations) {
|
||||||
|
invokeMethod(implementation);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
analyzeInitializer(method.getClassName());
|
||||||
|
invokeMethod(method);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void invokeMethod(MethodReference method) {
|
||||||
|
ClassReader cls = classes.get(method.getClassName());
|
||||||
|
if (cls != null) {
|
||||||
|
MethodReader methodReader = cls.getMethod(method.getDescriptor());
|
||||||
|
if (methodReader != null) {
|
||||||
|
analyzeCalledMethod(analyzeMethod(methodReader));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initClass(String className) {
|
||||||
|
analyzeInitializer(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void monitorEnter(VariableReader objectRef) {
|
||||||
|
initClass("java.lang.Thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void monitorExit(VariableReader objectRef) {
|
||||||
|
initClass("java.lang.Thread");
|
||||||
|
}
|
||||||
|
|
||||||
|
void analyzeInitializer(String className) {
|
||||||
|
if (className.equals(currentClass)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
analyze(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void analyzeCalledMethod(MethodInfo calledMethod) {
|
||||||
|
if (methodInfo.anyFieldModified) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (calledMethod.anyFieldModified) {
|
||||||
|
methodInfo.anyFieldModified = true;
|
||||||
|
methodInfo.classesWithModifiedFields = null;
|
||||||
|
} else if (calledMethod.classesWithModifiedFields != null) {
|
||||||
|
for (String className : calledMethod.classesWithModifiedFields) {
|
||||||
|
if (className.equals(currentClass)) {
|
||||||
|
if (methodInfo.classesWithModifiedFields == null) {
|
||||||
|
methodInfo.classesWithModifiedFields = new HashSet<>();
|
||||||
|
}
|
||||||
|
methodInfo.classesWithModifiedFields.add(className);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static class MethodInfo {
|
||||||
|
MethodReference method;
|
||||||
|
boolean complete;
|
||||||
|
Set<MethodInfo> recursiveCallers;
|
||||||
|
Set<String> classesWithModifiedFields;
|
||||||
|
boolean anyFieldModified;
|
||||||
|
|
||||||
|
MethodInfo(MethodReference method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 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.analysis;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public interface ClassInitializerInfo {
|
||||||
|
boolean isDynamicInitializer(String className);
|
||||||
|
|
||||||
|
List<? extends String> getInitializationOrder();
|
||||||
|
|
||||||
|
ClassInitializerInfo EMPTY = new ClassInitializerInfo() {
|
||||||
|
@Override
|
||||||
|
public boolean isDynamicInitializer(String className) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<? extends String> getInitializationOrder() {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -17,6 +17,7 @@ package org.teavm.model.optimization;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.common.OptionalPredicate;
|
import org.teavm.common.OptionalPredicate;
|
||||||
import org.teavm.dependency.DependencyInfo;
|
import org.teavm.dependency.DependencyInfo;
|
||||||
|
@ -73,8 +74,13 @@ public class Devirtualization {
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<MethodReference> getImplementations(String[] classNames, MethodReference ref) {
|
private Set<MethodReference> getImplementations(String[] classNames, MethodReference ref) {
|
||||||
|
return implementations(hierarchy, dependency, classNames, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Set<MethodReference> implementations(ClassHierarchy hierarchy, DependencyInfo dependency,
|
||||||
|
String[] classNames, MethodReference ref) {
|
||||||
OptionalPredicate<String> isSuperclass = hierarchy.getSuperclassPredicate(ref.getClassName());
|
OptionalPredicate<String> isSuperclass = hierarchy.getSuperclassPredicate(ref.getClassName());
|
||||||
Set<MethodReference> methods = new HashSet<>();
|
Set<MethodReference> methods = new LinkedHashSet<>();
|
||||||
for (String className : classNames) {
|
for (String className : classNames) {
|
||||||
if (className.startsWith("[")) {
|
if (className.startsWith("[")) {
|
||||||
className = "java.lang.Object";
|
className = "java.lang.Object";
|
||||||
|
|
|
@ -47,6 +47,7 @@ import org.teavm.model.ProgramReader;
|
||||||
import org.teavm.model.TryCatchBlock;
|
import org.teavm.model.TryCatchBlock;
|
||||||
import org.teavm.model.VariableReader;
|
import org.teavm.model.VariableReader;
|
||||||
import org.teavm.model.analysis.ClassInference;
|
import org.teavm.model.analysis.ClassInference;
|
||||||
|
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||||
import org.teavm.model.instructions.AbstractInstructionReader;
|
import org.teavm.model.instructions.AbstractInstructionReader;
|
||||||
import org.teavm.model.instructions.AssignInstruction;
|
import org.teavm.model.instructions.AssignInstruction;
|
||||||
import org.teavm.model.instructions.ExitInstruction;
|
import org.teavm.model.instructions.ExitInstruction;
|
||||||
|
@ -70,15 +71,17 @@ public class Inlining {
|
||||||
private MethodUsageCounter usageCounter;
|
private MethodUsageCounter usageCounter;
|
||||||
private Set<MethodReference> methodsUsedOnce = new HashSet<>();
|
private Set<MethodReference> methodsUsedOnce = new HashSet<>();
|
||||||
private boolean devirtualization;
|
private boolean devirtualization;
|
||||||
|
private ClassInitializerInfo classInitializerInfo;
|
||||||
|
|
||||||
public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo, InliningStrategy strategy,
|
public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo, InliningStrategy strategy,
|
||||||
ListableClassReaderSource classes, Predicate<MethodReference> externalMethods,
|
ListableClassReaderSource classes, Predicate<MethodReference> externalMethods,
|
||||||
boolean devirtualization) {
|
boolean devirtualization, ClassInitializerInfo classInitializerInfo) {
|
||||||
this.hierarchy = hierarchy;
|
this.hierarchy = hierarchy;
|
||||||
this.classes = classes;
|
this.classes = classes;
|
||||||
this.dependencyInfo = dependencyInfo;
|
this.dependencyInfo = dependencyInfo;
|
||||||
this.strategy = strategy;
|
this.strategy = strategy;
|
||||||
this.devirtualization = devirtualization;
|
this.devirtualization = devirtualization;
|
||||||
|
this.classInitializerInfo = classInitializerInfo;
|
||||||
usageCounter = new MethodUsageCounter(externalMethods);
|
usageCounter = new MethodUsageCounter(externalMethods);
|
||||||
|
|
||||||
for (String className : classes.getClassNames()) {
|
for (String className : classes.getClassNames()) {
|
||||||
|
@ -223,7 +226,8 @@ public class Inlining {
|
||||||
splitBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
|
splitBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
|
||||||
|
|
||||||
invoke.delete();
|
invoke.delete();
|
||||||
if (invoke.getMethod().getName().equals("<init>") || invoke.getInstance() == null) {
|
if ((invoke.getMethod().getName().equals("<init>") || invoke.getInstance() == null)
|
||||||
|
&& classInitializerInfo.isDynamicInitializer(invoke.getMethod().getClassName())) {
|
||||||
InitClassInstruction clinit = new InitClassInstruction();
|
InitClassInstruction clinit = new InitClassInstruction();
|
||||||
clinit.setClassName(invoke.getMethod().getClassName());
|
clinit.setClassName(invoke.getMethod().getClassName());
|
||||||
block.add(clinit);
|
block.add(clinit);
|
||||||
|
|
|
@ -22,20 +22,24 @@ import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||||
import org.teavm.model.instructions.InitClassInstruction;
|
import org.teavm.model.instructions.InitClassInstruction;
|
||||||
|
|
||||||
public class ClassInitializerInsertionTransformer {
|
public class ClassInitializerInsertionTransformer {
|
||||||
private static final MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class);
|
private static final MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class);
|
||||||
private ClassReaderSource classes;
|
private ClassReaderSource classes;
|
||||||
|
private ClassInitializerInfo classInitializerInfo;
|
||||||
|
|
||||||
public ClassInitializerInsertionTransformer(ClassReaderSource classes) {
|
public ClassInitializerInsertionTransformer(ClassReaderSource classes, ClassInitializerInfo classInitializerInfo) {
|
||||||
this.classes = classes;
|
this.classes = classes;
|
||||||
|
this.classInitializerInfo = classInitializerInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void apply(MethodReader method, Program program) {
|
public void apply(MethodReader method, Program program) {
|
||||||
ClassReader cls = classes.get(method.getOwnerName());
|
ClassReader cls = classes.get(method.getOwnerName());
|
||||||
boolean hasClinit = cls.getMethod(clinitDescriptor) != null;
|
boolean hasClinit = cls.getMethod(clinitDescriptor) != null
|
||||||
if (needsClinitCall(method) && hasClinit) {
|
&& classInitializerInfo.isDynamicInitializer(cls.getName());
|
||||||
|
if (hasClinit && needsClinitCall(method)) {
|
||||||
BasicBlock entryBlock = program.basicBlockAt(0);
|
BasicBlock entryBlock = program.basicBlockAt(0);
|
||||||
InitClassInstruction initInsn = new InitClassInstruction();
|
InitClassInstruction initInsn = new InitClassInstruction();
|
||||||
initInsn.setClassName(method.getOwnerName());
|
initInsn.setClassName(method.getOwnerName());
|
||||||
|
|
|
@ -51,14 +51,17 @@ import org.teavm.dependency.MethodDependencyInfo;
|
||||||
import org.teavm.diagnostics.AccumulationDiagnostics;
|
import org.teavm.diagnostics.AccumulationDiagnostics;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.diagnostics.ProblemProvider;
|
import org.teavm.diagnostics.ProblemProvider;
|
||||||
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.ClassHierarchy;
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
|
import org.teavm.model.ClassHolderSource;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldHolder;
|
import org.teavm.model.FieldHolder;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.Instruction;
|
||||||
import org.teavm.model.ListableClassHolderSource;
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
import org.teavm.model.ListableClassReaderSource;
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
@ -69,6 +72,10 @@ import org.teavm.model.MutableClassHolderSource;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
import org.teavm.model.ProgramCache;
|
import org.teavm.model.ProgramCache;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.analysis.ClassInitializerAnalysis;
|
||||||
|
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||||
|
import org.teavm.model.instructions.InitClassInstruction;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.optimization.ArrayUnwrapMotion;
|
import org.teavm.model.optimization.ArrayUnwrapMotion;
|
||||||
import org.teavm.model.optimization.ClassInitElimination;
|
import org.teavm.model.optimization.ClassInitElimination;
|
||||||
import org.teavm.model.optimization.ConstantConditionElimination;
|
import org.teavm.model.optimization.ConstantConditionElimination;
|
||||||
|
@ -154,6 +161,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
private int compileProgressLimit;
|
private int compileProgressLimit;
|
||||||
private int compileProgressValue;
|
private int compileProgressValue;
|
||||||
private ClassSourcePacker classSourcePacker;
|
private ClassSourcePacker classSourcePacker;
|
||||||
|
private ClassInitializerInfo classInitializerInfo;
|
||||||
|
|
||||||
TeaVM(TeaVMBuilder builder) {
|
TeaVM(TeaVMBuilder builder) {
|
||||||
target = builder.target;
|
target = builder.target;
|
||||||
|
@ -374,19 +382,19 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
cacheStatus = new AnnotationAwareCacheStatus(rawCacheStatus, dependencyAnalyzer.getIncrementalDependencies(),
|
cacheStatus = new AnnotationAwareCacheStatus(rawCacheStatus, dependencyAnalyzer.getIncrementalDependencies(),
|
||||||
dependencyAnalyzer.getClassSource());
|
dependencyAnalyzer.getClassSource());
|
||||||
cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass);
|
cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass);
|
||||||
target.setController(targetController);
|
|
||||||
|
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
target.analyzeBeforeOptimizations(new ListableClassReaderSourceAdapter(
|
|
||||||
dependencyAnalyzer.getClassSource(),
|
|
||||||
new LinkedHashSet<>(dependencyAnalyzer.getReachableClasses())));
|
|
||||||
|
|
||||||
boolean isLazy = optimizationLevel == TeaVMOptimizationLevel.SIMPLE;
|
boolean isLazy = optimizationLevel == TeaVMOptimizationLevel.SIMPLE;
|
||||||
ListableClassHolderSource classSet;
|
ListableClassHolderSource classSet;
|
||||||
if (isLazy) {
|
if (isLazy) {
|
||||||
|
classInitializerInfo = ClassInitializerInfo.EMPTY;
|
||||||
|
target.setController(targetController);
|
||||||
|
target.analyzeBeforeOptimizations(new ListableClassReaderSourceAdapter(
|
||||||
|
dependencyAnalyzer.getClassSource(),
|
||||||
|
new LinkedHashSet<>(dependencyAnalyzer.getReachableClasses())));
|
||||||
initCompileProgress(1000);
|
initCompileProgress(1000);
|
||||||
classSet = lazyPipeline();
|
classSet = lazyPipeline();
|
||||||
} else {
|
} else {
|
||||||
|
@ -439,6 +447,14 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ClassInitializerAnalysis classInitializerAnalysis = new ClassInitializerAnalysis(classSet,
|
||||||
|
dependencyAnalyzer.getClassHierarchy());
|
||||||
|
classInitializerAnalysis.analyze(dependencyAnalyzer);
|
||||||
|
classInitializerInfo = classInitializerAnalysis;
|
||||||
|
eliminateClassInit(classSet);
|
||||||
|
} else {
|
||||||
|
classInitializerInfo = ClassInitializerInfo.EMPTY;
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyAnalyzer.cleanupTypes();
|
dependencyAnalyzer.cleanupTypes();
|
||||||
|
@ -448,6 +464,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
target.setController(targetController);
|
||||||
|
target.analyzeBeforeOptimizations(new ListableClassReaderSourceAdapter(
|
||||||
|
dependencyAnalyzer.getClassSource(),
|
||||||
|
new LinkedHashSet<>(dependencyAnalyzer.getReachableClasses())));
|
||||||
|
|
||||||
// Optimize and allocate registers
|
// Optimize and allocate registers
|
||||||
optimize(classSet);
|
optimize(classSet);
|
||||||
if (wasCancelled()) {
|
if (wasCancelled()) {
|
||||||
|
@ -461,6 +482,53 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
return new PostProcessingClassHolderSource();
|
return new PostProcessingClassHolderSource();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void eliminateClassInit(ListableClassHolderSource classes) {
|
||||||
|
for (String className : classes.getClassNames()) {
|
||||||
|
ClassHolder cls = classes.get(className);
|
||||||
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
|
Program program = method.getProgram();
|
||||||
|
if (program == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (BasicBlock block : program.getBasicBlocks()) {
|
||||||
|
for (Instruction instruction : block) {
|
||||||
|
if (instruction instanceof InitClassInstruction) {
|
||||||
|
InitClassInstruction clinit = (InitClassInstruction) instruction;
|
||||||
|
if (!classInitializerInfo.isDynamicInitializer(clinit.getClassName())) {
|
||||||
|
clinit.delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (TeaVMEntryPoint entryPoint : entryPoints.values()) {
|
||||||
|
addInitializersToEntryPoint(classes, entryPoint.getMethod());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addInitializersToEntryPoint(ClassHolderSource classes, MethodReference methodRef) {
|
||||||
|
ClassHolder cls = classes.get(methodRef.getClassName());
|
||||||
|
if (cls == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodHolder method = cls.getMethod(methodRef.getDescriptor());
|
||||||
|
if (method == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Program program = method.getProgram();
|
||||||
|
BasicBlock block = program.basicBlockAt(0);
|
||||||
|
Instruction first = block.getFirstInstruction();
|
||||||
|
for (String className : classInitializerInfo.getInitializationOrder()) {
|
||||||
|
InvokeInstruction invoke = new InvokeInstruction();
|
||||||
|
invoke.setMethod(new MethodReference(className, "<clinit>", ValueType.VOID));
|
||||||
|
first.insertPrevious(invoke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public ListableClassHolderSource link(DependencyAnalyzer dependency) {
|
public ListableClassHolderSource link(DependencyAnalyzer dependency) {
|
||||||
Linker linker = new Linker(dependency);
|
Linker linker = new Linker(dependency);
|
||||||
MutableClassHolderSource cutClasses = new MutableClassHolderSource();
|
MutableClassHolderSource cutClasses = new MutableClassHolderSource();
|
||||||
|
@ -529,7 +597,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void inline(ListableClassHolderSource classes) {
|
private void inline(ListableClassHolderSource classes) {
|
||||||
if (optimizationLevel != TeaVMOptimizationLevel.ADVANCED && optimizationLevel != TeaVMOptimizationLevel.FULL) {
|
if (optimizationLevel == TeaVMOptimizationLevel.SIMPLE) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -541,7 +609,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyAnalyzer, inliningStrategy,
|
Inlining inlining = new Inlining(new ClassHierarchy(classes), dependencyAnalyzer, inliningStrategy,
|
||||||
classes, this::isExternal, optimizationLevel == TeaVMOptimizationLevel.FULL);
|
classes, this::isExternal, optimizationLevel == TeaVMOptimizationLevel.FULL, classInitializerInfo);
|
||||||
List<MethodReference> methodReferences = inlining.getOrder();
|
List<MethodReference> methodReferences = inlining.getOrder();
|
||||||
int classCount = classes.getClassNames().size();
|
int classCount = classes.getClassNames().size();
|
||||||
int initialValue = compileProgressValue;
|
int initialValue = compileProgressValue;
|
||||||
|
@ -811,6 +879,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
public void addVirtualMethods(Predicate<MethodReference> methods) {
|
public void addVirtualMethods(Predicate<MethodReference> methods) {
|
||||||
TeaVM.this.addVirtualMethods(methods);
|
TeaVM.this.addVirtualMethods(methods);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassInitializerInfo getClassInitializerInfo() {
|
||||||
|
return classInitializerInfo;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PostProcessingClassHolderSource implements ListableClassHolderSource {
|
class PostProcessingClassHolderSource implements ListableClassHolderSource {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.teavm.dependency.DependencyInfo;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||||
|
|
||||||
public interface TeaVMTargetController {
|
public interface TeaVMTargetController {
|
||||||
boolean wasCancelled();
|
boolean wasCancelled();
|
||||||
|
@ -54,4 +55,6 @@ public interface TeaVMTargetController {
|
||||||
TeaVMProgressFeedback reportProgress(int progress);
|
TeaVMProgressFeedback reportProgress(int progress);
|
||||||
|
|
||||||
void addVirtualMethods(Predicate<MethodReference> methods);
|
void addVirtualMethods(Predicate<MethodReference> methods);
|
||||||
|
|
||||||
|
ClassInitializerInfo getClassInitializerInfo();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 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";
|
||||||
|
|
||||||
|
var $rt_intern = function() {
|
||||||
|
var table = new Array(100);
|
||||||
|
var size = 0;
|
||||||
|
|
||||||
|
function get(str) {
|
||||||
|
var hash = $rt_stringHash(str);
|
||||||
|
var bucket = getBucket(hash);
|
||||||
|
for (var i = 0; i < bucket.length; ++i) {
|
||||||
|
if ($rt_stringEquals(bucket[i], str)) {
|
||||||
|
return bucket[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bucket.push(str);
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBucket(hash) {
|
||||||
|
while (true) {
|
||||||
|
var position = hash % table.length;
|
||||||
|
var bucket = table[position];
|
||||||
|
if (typeof bucket !== "undefined") {
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
if (++size / table.length > 0.5) {
|
||||||
|
rehash();
|
||||||
|
} else {
|
||||||
|
bucket = [];
|
||||||
|
table[position] = bucket;
|
||||||
|
return bucket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function rehash() {
|
||||||
|
var old = table;
|
||||||
|
table = new Array(table.length * 2);
|
||||||
|
size = 0;
|
||||||
|
for (var i = 0; i < old.length; ++i) {
|
||||||
|
var bucket = old[i];
|
||||||
|
if (typeof bucket !== "undefined") {
|
||||||
|
for (var j = 0; j < bucket.length; ++j) {
|
||||||
|
get(bucket[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return get;
|
||||||
|
}();
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2019 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.interop;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||||
|
public @interface DoesNotModifyStaticFields {
|
||||||
|
}
|
|
@ -32,6 +32,7 @@ import org.mozilla.javascript.ast.FunctionNode;
|
||||||
import org.teavm.backend.javascript.rendering.JSParser;
|
import org.teavm.backend.javascript.rendering.JSParser;
|
||||||
import org.teavm.cache.IncrementalDependencyRegistration;
|
import org.teavm.cache.IncrementalDependencyRegistration;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
import org.teavm.interop.Sync;
|
import org.teavm.interop.Sync;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
import org.teavm.jso.JSByRef;
|
import org.teavm.jso.JSByRef;
|
||||||
|
@ -653,6 +654,7 @@ class JSClassProcessor {
|
||||||
MethodHolder proxyMethod = new MethodHolder(proxyRef.getDescriptor());
|
MethodHolder proxyMethod = new MethodHolder(proxyRef.getDescriptor());
|
||||||
proxyMethod.getModifiers().add(ElementModifier.NATIVE);
|
proxyMethod.getModifiers().add(ElementModifier.NATIVE);
|
||||||
proxyMethod.getModifiers().add(ElementModifier.STATIC);
|
proxyMethod.getModifiers().add(ElementModifier.STATIC);
|
||||||
|
proxyMethod.getAnnotations().add(new AnnotationHolder(DoesNotModifyStaticFields.class.getName()));
|
||||||
boolean inline = repository.inlineMethods.contains(methodRef);
|
boolean inline = repository.inlineMethods.contains(methodRef);
|
||||||
AnnotationHolder generatorAnnot = new AnnotationHolder(inline
|
AnnotationHolder generatorAnnot = new AnnotationHolder(inline
|
||||||
? DynamicInjector.class.getName() : DynamicGenerator.class.getName());
|
? DynamicInjector.class.getName() : DynamicGenerator.class.getName());
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.platform.plugin;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
|
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||||
import org.teavm.model.AccessLevel;
|
import org.teavm.model.AccessLevel;
|
||||||
import org.teavm.model.AnnotationHolder;
|
import org.teavm.model.AnnotationHolder;
|
||||||
import org.teavm.model.AnnotationValue;
|
import org.teavm.model.AnnotationValue;
|
||||||
|
@ -67,6 +68,9 @@ class MetadataProviderTransformer implements ClassHolderTransformer {
|
||||||
MetadataProviderNativeGenerator.class.getName())));
|
MetadataProviderNativeGenerator.class.getName())));
|
||||||
createMethod.getAnnotations().add(genAnnot);
|
createMethod.getAnnotations().add(genAnnot);
|
||||||
ModelUtils.copyAnnotations(method.getAnnotations(), createMethod.getAnnotations());
|
ModelUtils.copyAnnotations(method.getAnnotations(), createMethod.getAnnotations());
|
||||||
|
if (createMethod.getAnnotations().get(DoesNotModifyStaticFields.class.getName()) == null) {
|
||||||
|
createMethod.getAnnotations().add(new AnnotationHolder(DoesNotModifyStaticFields.class.getName()));
|
||||||
|
}
|
||||||
|
|
||||||
AnnotationHolder refAnnot = new AnnotationHolder(MetadataProviderRef.class.getName());
|
AnnotationHolder refAnnot = new AnnotationHolder(MetadataProviderRef.class.getName());
|
||||||
refAnnot.getValues().put("value", new AnnotationValue(method.getReference().toString()));
|
refAnnot.getValues().put("value", new AnnotationValue(method.getReference().toString()));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user