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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -151,16 +151,29 @@ public class RuntimeRenderer {
}
private void renderRuntimeIntern() throws IOException {
writer.append("function $rt_intern(str) {").indent().softNewLine();
ClassReader stringCls = classSource.get(STRING_CLASS);
if (stringCls != null && stringCls.getMethod(STRING_INTERN_METHOD) != null) {
writer.append("return ").appendMethodBody(new MethodReference(STRING_CLASS, STRING_INTERN_METHOD))
.append("(str);").softNewLine();
} else {
if (!needInternMethod()) {
writer.append("function $rt_intern(str) {").indent().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 {

View File

@ -49,4 +49,6 @@ public interface GeneratorContext extends ServiceRepository {
void typeToClassString(SourceWriter writer, ValueType type);
void useLongLibrary();
boolean isDynamicInitializer(String className);
}

View File

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

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

View File

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

View File

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

View File

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

View File

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

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

View File

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