Call <clinit> methods before main method when possible, eliminate

lazy class initialization for these <clinit> methods.
This commit is contained in:
Alexey Andreev 2019-03-22 19:40:56 +03:00
parent 955ac92035
commit de84105241
32 changed files with 690 additions and 58 deletions

View File

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

View File

@ -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();
} }
} }

View File

@ -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();
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
} }

View File

@ -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);
} }

View File

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

View File

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

View File

@ -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<>();
}
} }

View File

@ -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() {

View File

@ -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);
} }

View File

@ -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();

View File

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

View File

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

View File

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

View File

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

View File

@ -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);
} }

View File

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

View File

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

View File

@ -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();
}
};
}

View File

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

View File

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

View File

@ -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());

View File

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

View File

@ -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();
} }

View File

@ -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;
}();

View File

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

View File

@ -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());

View File

@ -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()));