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;
|
||||
|
||||
import org.teavm.classlib.java.lang.TCharacter;
|
||||
|
||||
public final class IntegerUtil {
|
||||
private IntegerUtil() {
|
||||
}
|
||||
|
@ -34,7 +32,7 @@ public final class IntegerUtil {
|
|||
int pos = (sz - 1) * radixLog2;
|
||||
int target = 0;
|
||||
while (pos >= 0) {
|
||||
chars[target++] = TCharacter.forDigit((value >>> pos) & mask, radix);
|
||||
chars[target++] = Character.forDigit((value >>> pos) & mask, radix);
|
||||
pos -= radixLog2;
|
||||
}
|
||||
|
||||
|
@ -54,7 +52,7 @@ public final class IntegerUtil {
|
|||
long pos = (sz - 1) * radixLog2;
|
||||
int target = 0;
|
||||
while (pos >= 0) {
|
||||
chars[target++] = TCharacter.forDigit((int) (value >>> pos) & mask, radix);
|
||||
chars[target++] = Character.forDigit((int) (value >>> pos) & mask, radix);
|
||||
pos -= radixLog2;
|
||||
}
|
||||
|
||||
|
|
|
@ -151,7 +151,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
|||
|
||||
appendProperty(writer, "getter", false, () -> {
|
||||
if (accessibleFields != null && accessibleFields.contains(field.getName())) {
|
||||
renderGetter(writer, field);
|
||||
renderGetter(context, writer, field);
|
||||
} else {
|
||||
writer.append("null");
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
|||
|
||||
appendProperty(writer, "setter", false, () -> {
|
||||
if (accessibleFields != null && accessibleFields.contains(field.getName())) {
|
||||
renderSetter(writer, field);
|
||||
renderSetter(context, writer, field);
|
||||
} else {
|
||||
writer.append("null");
|
||||
}
|
||||
|
@ -199,7 +199,7 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
|||
|
||||
appendProperty(writer, "callable", false, () -> {
|
||||
if (accessibleMethods != null && accessibleMethods.contains(method.getDescriptor())) {
|
||||
renderCallable(writer, method);
|
||||
renderCallable(context, writer, method);
|
||||
} else {
|
||||
writer.append("null");
|
||||
}
|
||||
|
@ -239,18 +239,18 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
|||
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();
|
||||
initClass(writer, field);
|
||||
initClass(context, writer, field);
|
||||
writer.append("return ");
|
||||
boxIfNecessary(writer, field.getType(), () -> fieldAccess(writer, field));
|
||||
writer.append(";").softNewLine();
|
||||
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();
|
||||
initClass(writer, field);
|
||||
initClass(context, writer, field);
|
||||
fieldAccess(writer, field);
|
||||
writer.ws().append('=').ws();
|
||||
unboxIfNecessary(writer, field.getType(), () -> writer.append("val"));
|
||||
|
@ -258,10 +258,10 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
|||
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();
|
||||
|
||||
initClass(writer, method);
|
||||
initClass(context, writer, method);
|
||||
|
||||
if (method.getResultType() != ValueType.VOID) {
|
||||
writer.append("return ");
|
||||
|
@ -290,8 +290,8 @@ public class ClassGenerator implements Generator, Injector, DependencyPlugin {
|
|||
writer.outdent().append("}");
|
||||
}
|
||||
|
||||
private void initClass(SourceWriter writer, MemberReader member) throws IOException {
|
||||
if (member.hasModifier(ElementModifier.STATIC)) {
|
||||
private void initClass(GeneratorContext context, SourceWriter writer, MemberReader member) throws IOException {
|
||||
if (member.hasModifier(ElementModifier.STATIC) && context.isDynamicInitializer(member.getOwnerName())) {
|
||||
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.interop.Address;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.jso.core.JSArray;
|
||||
import org.teavm.platform.Platform;
|
||||
|
@ -209,6 +210,7 @@ public class TClass<T> extends TObject implements TAnnotatedElement {
|
|||
}
|
||||
|
||||
@GeneratedBy(ClassGenerator.class)
|
||||
@DoesNotModifyStaticFields
|
||||
private static native void createMetadata();
|
||||
|
||||
public TField[] getFields() throws TSecurityException {
|
||||
|
|
|
@ -19,6 +19,7 @@ import java.io.ByteArrayInputStream;
|
|||
import java.io.InputStream;
|
||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||
import org.teavm.classlib.impl.Base64Impl;
|
||||
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSIndexer;
|
||||
import org.teavm.jso.JSObject;
|
||||
|
@ -68,6 +69,7 @@ public abstract class TClassLoader extends TObject {
|
|||
private static native String resourceToString(JSObject resource);
|
||||
|
||||
@InjectedBy(ClassLoaderNativeGenerator.class)
|
||||
@DoesNotModifyStaticFields
|
||||
private static native ResourceContainer supplyResources();
|
||||
|
||||
interface ResourceContainer extends JSObject {
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
package org.teavm.classlib.java.lang;
|
||||
|
||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.jso.JSBody;
|
||||
|
||||
@DoesNotModifyStaticFields
|
||||
public class TDouble extends TNumber implements TComparable<TDouble> {
|
||||
public static final double POSITIVE_INFINITY = 1 / 0.0;
|
||||
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;
|
||||
}
|
||||
|
||||
@DoesNotModifyStaticFields
|
||||
public static native int compare(double a, double b);
|
||||
|
||||
@Override
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
*/
|
||||
package org.teavm.classlib.java.lang;
|
||||
|
||||
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.jso.JSBody;
|
||||
|
||||
|
@ -237,6 +238,7 @@ public class TFloat extends TNumber implements TComparable<TFloat> {
|
|||
return isInfinite(value);
|
||||
}
|
||||
|
||||
@DoesNotModifyStaticFields
|
||||
public static native int compare(float f1, float f2);
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.classlib.java.lang;
|
|||
|
||||
import static org.teavm.classlib.impl.IntegerUtil.toUnsignedLogRadixString;
|
||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||
|
||||
public class TInteger extends TNumber implements TComparable<TInteger> {
|
||||
public static final int SIZE = 32;
|
||||
|
@ -251,6 +252,7 @@ public class TInteger extends TNumber implements TComparable<TInteger> {
|
|||
return compare(value, other.value);
|
||||
}
|
||||
|
||||
@DoesNotModifyStaticFields
|
||||
public static native int compare(int x, int y);
|
||||
|
||||
public static int numberOfLeadingZeros(int i) {
|
||||
|
@ -356,8 +358,10 @@ public class TInteger extends TNumber implements TComparable<TInteger> {
|
|||
}
|
||||
|
||||
@InjectedBy(IntegerNativeGenerator.class)
|
||||
@DoesNotModifyStaticFields
|
||||
public static native int divideUnsigned(int dividend, int divisor);
|
||||
|
||||
@InjectedBy(IntegerNativeGenerator.class)
|
||||
@DoesNotModifyStaticFields
|
||||
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 org.teavm.backend.javascript.spi.GeneratedBy;
|
||||
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||
|
||||
public class TLong extends TNumber implements TComparable<TLong> {
|
||||
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;
|
||||
}
|
||||
|
||||
@DoesNotModifyStaticFields
|
||||
public static native int compare(long a, long b);
|
||||
|
||||
@Override
|
||||
|
@ -351,8 +353,10 @@ public class TLong extends TNumber implements TComparable<TLong> {
|
|||
}
|
||||
|
||||
@GeneratedBy(LongNativeGenerator.class)
|
||||
@DoesNotModifyStaticFields
|
||||
public static native long divideUnsigned(long dividend, long divisor);
|
||||
|
||||
@GeneratedBy(LongNativeGenerator.class)
|
||||
@DoesNotModifyStaticFields
|
||||
public static native long remainderUnsigned(long dividend, long divisor);
|
||||
}
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
package org.teavm.classlib.java.lang;
|
||||
|
||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||
import org.teavm.interop.Import;
|
||||
|
||||
@DoesNotModifyStaticFields
|
||||
public final class TMath extends TObject {
|
||||
public static final double E = 2.71828182845904523536;
|
||||
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.AsyncCallback;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||
import org.teavm.interop.Rename;
|
||||
import org.teavm.interop.Structure;
|
||||
import org.teavm.interop.Superclass;
|
||||
|
@ -272,6 +273,7 @@ public class TObject {
|
|||
}
|
||||
|
||||
@DelegateTo("hashCodeLowLevelImpl")
|
||||
@DoesNotModifyStaticFields
|
||||
private static native int hashCodeLowLevel(TObject obj);
|
||||
|
||||
@Unmanaged
|
||||
|
@ -280,6 +282,7 @@ public class TObject {
|
|||
}
|
||||
|
||||
@DelegateTo("setHashCodeLowLevelImpl")
|
||||
@DoesNotModifyStaticFields
|
||||
private static native void setHashCodeLowLevel(TObject obj, int value);
|
||||
|
||||
@Unmanaged
|
||||
|
@ -300,6 +303,7 @@ public class TObject {
|
|||
}
|
||||
|
||||
@DelegateTo("identityOrMonitorLowLevel")
|
||||
@DoesNotModifyStaticFields
|
||||
private native int identityOrMonitor();
|
||||
|
||||
private static int identityOrMonitorLowLevel(RuntimeObject object) {
|
||||
|
@ -307,6 +311,7 @@ public class TObject {
|
|||
}
|
||||
|
||||
@DelegateTo("setIdentityLowLevel")
|
||||
@DoesNotModifyStaticFields
|
||||
native void setIdentity(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.Locale;
|
||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||
import org.teavm.classlib.java.io.TSerializable;
|
||||
import org.teavm.classlib.java.io.TUnsupportedEncodingException;
|
||||
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.TComparator;
|
||||
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.TMap;
|
||||
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 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();
|
||||
}
|
||||
|
||||
public TString intern() {
|
||||
TString interned = PoolHolder.pool.get(this);
|
||||
if (interned == null) {
|
||||
interned = this;
|
||||
PoolHolder.pool.put(interned, interned);
|
||||
}
|
||||
return interned;
|
||||
}
|
||||
@GeneratedBy(StringNativeGenerator.class)
|
||||
@PluggableDependency(StringNativeGenerator.class)
|
||||
@DoesNotModifyStaticFields
|
||||
public native TString intern();
|
||||
|
||||
public boolean matches(String regex) {
|
||||
return TPattern.matches(regex, this.toString());
|
||||
|
@ -705,8 +702,4 @@ public class TString extends TObject implements TSerializable, TComparable<TStri
|
|||
}
|
||||
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.interop.Address;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.runtime.Allocator;
|
||||
|
@ -103,6 +104,7 @@ public final class TSystem extends TObject {
|
|||
|
||||
@GeneratedBy(SystemNativeGenerator.class)
|
||||
@DelegateTo("doArrayCopyLowLevel")
|
||||
@DoesNotModifyStaticFields
|
||||
private static native void doArrayCopy(Object src, int srcPos, Object dest, int destPos, int length);
|
||||
|
||||
@Unmanaged
|
||||
|
@ -124,6 +126,7 @@ public final class TSystem extends TObject {
|
|||
|
||||
@DelegateTo("currentTimeMillisLowLevel")
|
||||
@GeneratedBy(SystemNativeGenerator.class)
|
||||
@DoesNotModifyStaticFields
|
||||
public static native long currentTimeMillis();
|
||||
|
||||
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.dependency.PluggableDependency;
|
||||
import org.teavm.interop.DelegateTo;
|
||||
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||
import org.teavm.interop.Unmanaged;
|
||||
import org.teavm.platform.PlatformClass;
|
||||
import org.teavm.runtime.Allocator;
|
||||
|
@ -35,6 +36,7 @@ public final class TArray extends TObject {
|
|||
@GeneratedBy(ArrayNativeGenerator.class)
|
||||
@PluggableDependency(ArrayNativeGenerator.class)
|
||||
@DelegateTo("getLengthLowLevel")
|
||||
@DoesNotModifyStaticFields
|
||||
public static native int getLength(TObject array) throws TIllegalArgumentException;
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -63,6 +65,7 @@ public final class TArray extends TObject {
|
|||
|
||||
@GeneratedBy(ArrayNativeGenerator.class)
|
||||
@DelegateTo("newInstanceLowLevel")
|
||||
@DoesNotModifyStaticFields
|
||||
private static native TObject newInstanceImpl(PlatformClass componentType, int length);
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
|
@ -89,9 +92,11 @@ public final class TArray extends TObject {
|
|||
|
||||
@GeneratedBy(ArrayNativeGenerator.class)
|
||||
@PluggableDependency(ArrayNativeGenerator.class)
|
||||
@DoesNotModifyStaticFields
|
||||
private static native TObject getImpl(TObject array, int index);
|
||||
|
||||
@GeneratedBy(ArrayNativeGenerator.class)
|
||||
@PluggableDependency(ArrayNativeGenerator.class)
|
||||
@DoesNotModifyStaticFields
|
||||
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());
|
||||
classInitializerTransformer = new ClassInitializerTransformer();
|
||||
shadowStackTransformer = new ShadowStackTransformer(characteristics);
|
||||
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
|
||||
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource(),
|
||||
controller.getClassInitializerInfo());
|
||||
nullCheckInsertion = new NullCheckInsertion(characteristics);
|
||||
nullCheckTransformation = new NullCheckTransformation();
|
||||
|
||||
|
|
|
@ -142,7 +142,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
@Override
|
||||
public void setController(TeaVMTargetController controller) {
|
||||
this.controller = controller;
|
||||
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
|
||||
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource(),
|
||||
controller.getClassInitializerInfo());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -350,7 +351,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
|||
RenderingContext renderingContext = new RenderingContext(debugEmitterToUse,
|
||||
controller.getUnprocessedClassSource(), classes,
|
||||
controller.getClassLoader(), controller.getServices(), controller.getProperties(), naming,
|
||||
controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m));
|
||||
controller.getDependencyInfo(), m -> isVirtual(virtualMethodContributorContext, m),
|
||||
controller.getClassInitializerInfo());
|
||||
renderingContext.setMinifying(minifying);
|
||||
Renderer renderer = new Renderer(sourceWriter, asyncMethods, asyncFamilyMethods,
|
||||
controller.getDiagnostics(), renderingContext);
|
||||
|
|
|
@ -76,7 +76,7 @@ public class Renderer implements RenderingManager {
|
|||
private RenderingContext context;
|
||||
private List<PostponedFieldInitializer> postponedFieldInitializers = new ArrayList<>();
|
||||
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 int stringPoolSize;
|
||||
|
@ -379,7 +379,7 @@ public class Renderer implements RenderingManager {
|
|||
try {
|
||||
MethodReader clinit = classSource.get(cls.getName()).getMethod(CLINIT_METHOD);
|
||||
|
||||
if (clinit != null) {
|
||||
if (clinit != null && context.isDynamicInitializer(cls.getName())) {
|
||||
renderCallClinit(clinit, cls);
|
||||
}
|
||||
if (!cls.getClassHolder().getModifiers().contains(ElementModifier.INTERFACE)) {
|
||||
|
@ -541,7 +541,7 @@ public class Renderer implements RenderingManager {
|
|||
writer.append(cls.getClassHolder().getLevel().ordinal()).append(',').ws();
|
||||
|
||||
MethodReader clinit = classSource.get(cls.getName()).getMethod(CLINIT_METHOD);
|
||||
if (clinit != null) {
|
||||
if (clinit != null && context.isDynamicInitializer(cls.getName())) {
|
||||
writer.appendClassInit(cls.getName());
|
||||
} else {
|
||||
writer.append('0');
|
||||
|
@ -1112,6 +1112,11 @@ public class Renderer implements RenderingManager {
|
|||
public void useLongLibrary() {
|
||||
longLibraryUsed = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDynamicInitializer(String className) {
|
||||
return context.isDynamicInitializer(className);
|
||||
}
|
||||
}
|
||||
|
||||
private void appendMonitor(StatementRenderer statementRenderer, MethodNode methodNode) throws IOException {
|
||||
|
@ -1132,7 +1137,7 @@ public class Renderer implements RenderingManager {
|
|||
FieldReference field;
|
||||
String value;
|
||||
|
||||
public PostponedFieldInitializer(FieldReference field, String value) {
|
||||
PostponedFieldInitializer(FieldReference field, String value) {
|
||||
this.field = field;
|
||||
this.value = value;
|
||||
}
|
||||
|
|
|
@ -43,6 +43,7 @@ import org.teavm.model.MethodReader;
|
|||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.TextLocation;
|
||||
import org.teavm.model.ValueType;
|
||||
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||
|
||||
public class RenderingContext {
|
||||
private final DebugInformationEmitter debugEmitter;
|
||||
|
@ -60,12 +61,13 @@ public class RenderingContext {
|
|||
private final List<String> readonlyStringPool = Collections.unmodifiableList(stringPool);
|
||||
private final Map<MethodReference, InjectorHolder> injectorMap = new HashMap<>();
|
||||
private boolean minifying;
|
||||
private ClassInitializerInfo classInitializerInfo;
|
||||
|
||||
public RenderingContext(DebugInformationEmitter debugEmitter,
|
||||
ClassReaderSource initialClassSource, ListableClassReaderSource classSource,
|
||||
ClassLoader classLoader, ServiceRepository services, Properties properties,
|
||||
NamingStrategy naming, DependencyInfo dependencyInfo,
|
||||
Predicate<MethodReference> virtualPredicate) {
|
||||
Predicate<MethodReference> virtualPredicate, ClassInitializerInfo classInitializerInfo) {
|
||||
this.debugEmitter = debugEmitter;
|
||||
this.initialClassSource = initialClassSource;
|
||||
this.classSource = classSource;
|
||||
|
@ -75,6 +77,7 @@ public class RenderingContext {
|
|||
this.naming = naming;
|
||||
this.dependencyInfo = dependencyInfo;
|
||||
this.virtualPredicate = virtualPredicate;
|
||||
this.classInitializerInfo = classInitializerInfo;
|
||||
}
|
||||
|
||||
public ClassReaderSource getInitialClassSource() {
|
||||
|
@ -117,6 +120,10 @@ public class RenderingContext {
|
|||
return virtualPredicate.test(method);
|
||||
}
|
||||
|
||||
public boolean isDynamicInitializer(String className) {
|
||||
return classInitializerInfo.isDynamicInitializer(className);
|
||||
}
|
||||
|
||||
public void pushLocation(TextLocation location) {
|
||||
LocationStackEntry prevEntry = locationStack.peek();
|
||||
if (location != null) {
|
||||
|
|
|
@ -151,16 +151,29 @@ public class RuntimeRenderer {
|
|||
}
|
||||
|
||||
private void renderRuntimeIntern() throws IOException {
|
||||
if (!needInternMethod()) {
|
||||
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.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 {
|
||||
|
|
|
@ -49,4 +49,6 @@ public interface GeneratorContext extends ServiceRepository {
|
|||
void typeToClassString(SourceWriter writer, ValueType type);
|
||||
|
||||
void useLongLibrary();
|
||||
|
||||
boolean isDynamicInitializer(String className);
|
||||
}
|
||||
|
|
|
@ -164,7 +164,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
|||
classInitializerEliminator = new ClassInitializerEliminator(controller.getUnprocessedClassSource());
|
||||
classInitializerTransformer = new ClassInitializerTransformer();
|
||||
shadowStackTransformer = new ShadowStackTransformer(managedMethodRepository);
|
||||
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource());
|
||||
clinitInsertionTransformer = new ClassInitializerInsertionTransformer(controller.getUnprocessedClassSource(),
|
||||
controller.getClassInitializerInfo());
|
||||
}
|
||||
|
||||
@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.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import org.teavm.common.OptionalPredicate;
|
||||
import org.teavm.dependency.DependencyInfo;
|
||||
|
@ -73,8 +74,13 @@ public class Devirtualization {
|
|||
}
|
||||
|
||||
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());
|
||||
Set<MethodReference> methods = new HashSet<>();
|
||||
Set<MethodReference> methods = new LinkedHashSet<>();
|
||||
for (String className : classNames) {
|
||||
if (className.startsWith("[")) {
|
||||
className = "java.lang.Object";
|
||||
|
|
|
@ -47,6 +47,7 @@ import org.teavm.model.ProgramReader;
|
|||
import org.teavm.model.TryCatchBlock;
|
||||
import org.teavm.model.VariableReader;
|
||||
import org.teavm.model.analysis.ClassInference;
|
||||
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||
import org.teavm.model.instructions.AbstractInstructionReader;
|
||||
import org.teavm.model.instructions.AssignInstruction;
|
||||
import org.teavm.model.instructions.ExitInstruction;
|
||||
|
@ -70,15 +71,17 @@ public class Inlining {
|
|||
private MethodUsageCounter usageCounter;
|
||||
private Set<MethodReference> methodsUsedOnce = new HashSet<>();
|
||||
private boolean devirtualization;
|
||||
private ClassInitializerInfo classInitializerInfo;
|
||||
|
||||
public Inlining(ClassHierarchy hierarchy, DependencyInfo dependencyInfo, InliningStrategy strategy,
|
||||
ListableClassReaderSource classes, Predicate<MethodReference> externalMethods,
|
||||
boolean devirtualization) {
|
||||
boolean devirtualization, ClassInitializerInfo classInitializerInfo) {
|
||||
this.hierarchy = hierarchy;
|
||||
this.classes = classes;
|
||||
this.dependencyInfo = dependencyInfo;
|
||||
this.strategy = strategy;
|
||||
this.devirtualization = devirtualization;
|
||||
this.classInitializerInfo = classInitializerInfo;
|
||||
usageCounter = new MethodUsageCounter(externalMethods);
|
||||
|
||||
for (String className : classes.getClassNames()) {
|
||||
|
@ -223,7 +226,8 @@ public class Inlining {
|
|||
splitBlock.getTryCatchBlocks().addAll(ProgramUtils.copyTryCatches(block, program));
|
||||
|
||||
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();
|
||||
clinit.setClassName(invoke.getMethod().getClassName());
|
||||
block.add(clinit);
|
||||
|
|
|
@ -22,20 +22,24 @@ import org.teavm.model.ElementModifier;
|
|||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.Program;
|
||||
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||
import org.teavm.model.instructions.InitClassInstruction;
|
||||
|
||||
public class ClassInitializerInsertionTransformer {
|
||||
private static final MethodDescriptor clinitDescriptor = new MethodDescriptor("<clinit>", void.class);
|
||||
private ClassReaderSource classes;
|
||||
private ClassInitializerInfo classInitializerInfo;
|
||||
|
||||
public ClassInitializerInsertionTransformer(ClassReaderSource classes) {
|
||||
public ClassInitializerInsertionTransformer(ClassReaderSource classes, ClassInitializerInfo classInitializerInfo) {
|
||||
this.classes = classes;
|
||||
this.classInitializerInfo = classInitializerInfo;
|
||||
}
|
||||
|
||||
public void apply(MethodReader method, Program program) {
|
||||
ClassReader cls = classes.get(method.getOwnerName());
|
||||
boolean hasClinit = cls.getMethod(clinitDescriptor) != null;
|
||||
if (needsClinitCall(method) && hasClinit) {
|
||||
boolean hasClinit = cls.getMethod(clinitDescriptor) != null
|
||||
&& classInitializerInfo.isDynamicInitializer(cls.getName());
|
||||
if (hasClinit && needsClinitCall(method)) {
|
||||
BasicBlock entryBlock = program.basicBlockAt(0);
|
||||
InitClassInstruction initInsn = new InitClassInstruction();
|
||||
initInsn.setClassName(method.getOwnerName());
|
||||
|
|
|
@ -51,14 +51,17 @@ import org.teavm.dependency.MethodDependencyInfo;
|
|||
import org.teavm.diagnostics.AccumulationDiagnostics;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.diagnostics.ProblemProvider;
|
||||
import org.teavm.model.BasicBlock;
|
||||
import org.teavm.model.ClassHierarchy;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderSource;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassReader;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldHolder;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.Instruction;
|
||||
import org.teavm.model.ListableClassHolderSource;
|
||||
import org.teavm.model.ListableClassReaderSource;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
|
@ -69,6 +72,10 @@ import org.teavm.model.MutableClassHolderSource;
|
|||
import org.teavm.model.Program;
|
||||
import org.teavm.model.ProgramCache;
|
||||
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.ClassInitElimination;
|
||||
import org.teavm.model.optimization.ConstantConditionElimination;
|
||||
|
@ -154,6 +161,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
private int compileProgressLimit;
|
||||
private int compileProgressValue;
|
||||
private ClassSourcePacker classSourcePacker;
|
||||
private ClassInitializerInfo classInitializerInfo;
|
||||
|
||||
TeaVM(TeaVMBuilder builder) {
|
||||
target = builder.target;
|
||||
|
@ -374,19 +382,19 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
cacheStatus = new AnnotationAwareCacheStatus(rawCacheStatus, dependencyAnalyzer.getIncrementalDependencies(),
|
||||
dependencyAnalyzer.getClassSource());
|
||||
cacheStatus.addSynthesizedClasses(dependencyAnalyzer::isSynthesizedClass);
|
||||
target.setController(targetController);
|
||||
|
||||
if (wasCancelled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
target.analyzeBeforeOptimizations(new ListableClassReaderSourceAdapter(
|
||||
dependencyAnalyzer.getClassSource(),
|
||||
new LinkedHashSet<>(dependencyAnalyzer.getReachableClasses())));
|
||||
|
||||
boolean isLazy = optimizationLevel == TeaVMOptimizationLevel.SIMPLE;
|
||||
ListableClassHolderSource classSet;
|
||||
if (isLazy) {
|
||||
classInitializerInfo = ClassInitializerInfo.EMPTY;
|
||||
target.setController(targetController);
|
||||
target.analyzeBeforeOptimizations(new ListableClassReaderSourceAdapter(
|
||||
dependencyAnalyzer.getClassSource(),
|
||||
new LinkedHashSet<>(dependencyAnalyzer.getReachableClasses())));
|
||||
initCompileProgress(1000);
|
||||
classSet = lazyPipeline();
|
||||
} else {
|
||||
|
@ -439,6 +447,14 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
if (wasCancelled()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ClassInitializerAnalysis classInitializerAnalysis = new ClassInitializerAnalysis(classSet,
|
||||
dependencyAnalyzer.getClassHierarchy());
|
||||
classInitializerAnalysis.analyze(dependencyAnalyzer);
|
||||
classInitializerInfo = classInitializerAnalysis;
|
||||
eliminateClassInit(classSet);
|
||||
} else {
|
||||
classInitializerInfo = ClassInitializerInfo.EMPTY;
|
||||
}
|
||||
|
||||
dependencyAnalyzer.cleanupTypes();
|
||||
|
@ -448,6 +464,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
return null;
|
||||
}
|
||||
|
||||
target.setController(targetController);
|
||||
target.analyzeBeforeOptimizations(new ListableClassReaderSourceAdapter(
|
||||
dependencyAnalyzer.getClassSource(),
|
||||
new LinkedHashSet<>(dependencyAnalyzer.getReachableClasses())));
|
||||
|
||||
// Optimize and allocate registers
|
||||
optimize(classSet);
|
||||
if (wasCancelled()) {
|
||||
|
@ -461,6 +482,53 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
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) {
|
||||
Linker linker = new Linker(dependency);
|
||||
MutableClassHolderSource cutClasses = new MutableClassHolderSource();
|
||||
|
@ -529,7 +597,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
}
|
||||
|
||||
private void inline(ListableClassHolderSource classes) {
|
||||
if (optimizationLevel != TeaVMOptimizationLevel.ADVANCED && optimizationLevel != TeaVMOptimizationLevel.FULL) {
|
||||
if (optimizationLevel == TeaVMOptimizationLevel.SIMPLE) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -541,7 +609,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
}
|
||||
|
||||
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();
|
||||
int classCount = classes.getClassNames().size();
|
||||
int initialValue = compileProgressValue;
|
||||
|
@ -811,6 +879,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
|||
public void addVirtualMethods(Predicate<MethodReference> methods) {
|
||||
TeaVM.this.addVirtualMethods(methods);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassInitializerInfo getClassInitializerInfo() {
|
||||
return classInitializerInfo;
|
||||
}
|
||||
};
|
||||
|
||||
class PostProcessingClassHolderSource implements ListableClassHolderSource {
|
||||
|
|
|
@ -25,6 +25,7 @@ import org.teavm.dependency.DependencyInfo;
|
|||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||
|
||||
public interface TeaVMTargetController {
|
||||
boolean wasCancelled();
|
||||
|
@ -54,4 +55,6 @@ public interface TeaVMTargetController {
|
|||
TeaVMProgressFeedback reportProgress(int progress);
|
||||
|
||||
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.cache.IncrementalDependencyRegistration;
|
||||
import org.teavm.diagnostics.Diagnostics;
|
||||
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||
import org.teavm.interop.Sync;
|
||||
import org.teavm.jso.JSBody;
|
||||
import org.teavm.jso.JSByRef;
|
||||
|
@ -653,6 +654,7 @@ class JSClassProcessor {
|
|||
MethodHolder proxyMethod = new MethodHolder(proxyRef.getDescriptor());
|
||||
proxyMethod.getModifiers().add(ElementModifier.NATIVE);
|
||||
proxyMethod.getModifiers().add(ElementModifier.STATIC);
|
||||
proxyMethod.getAnnotations().add(new AnnotationHolder(DoesNotModifyStaticFields.class.getName()));
|
||||
boolean inline = repository.inlineMethods.contains(methodRef);
|
||||
AnnotationHolder generatorAnnot = new AnnotationHolder(inline
|
||||
? DynamicInjector.class.getName() : DynamicGenerator.class.getName());
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.platform.plugin;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||
import org.teavm.interop.DoesNotModifyStaticFields;
|
||||
import org.teavm.model.AccessLevel;
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
import org.teavm.model.AnnotationValue;
|
||||
|
@ -67,6 +68,9 @@ class MetadataProviderTransformer implements ClassHolderTransformer {
|
|||
MetadataProviderNativeGenerator.class.getName())));
|
||||
createMethod.getAnnotations().add(genAnnot);
|
||||
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());
|
||||
refAnnot.getValues().put("value", new AnnotationValue(method.getReference().toString()));
|
||||
|
|
Loading…
Reference in New Issue
Block a user