diff --git a/core/src/main/java/org/teavm/model/analysis/ClassInitializerAnalysis.java b/core/src/main/java/org/teavm/model/analysis/ClassInitializerAnalysis.java index 37b74152f..6d6940a2d 100644 --- a/core/src/main/java/org/teavm/model/analysis/ClassInitializerAnalysis.java +++ b/core/src/main/java/org/teavm/model/analysis/ClassInitializerAnalysis.java @@ -174,6 +174,9 @@ public class ClassInitializerAnalysis implements ClassInitializerInfo { } private boolean hasSideEffects(MethodReader method) { + if (method.hasModifier(ElementModifier.ABSTRACT)) { + return false; + } if (method.getAnnotations().get(NoSideEffects.class.getName()) != null) { return false; } diff --git a/core/src/main/java/org/teavm/model/optimization/ClassInitElimination.java b/core/src/main/java/org/teavm/model/optimization/ClassInitElimination.java index 05ac456f0..aa8642919 100644 --- a/core/src/main/java/org/teavm/model/optimization/ClassInitElimination.java +++ b/core/src/main/java/org/teavm/model/optimization/ClassInitElimination.java @@ -71,11 +71,11 @@ public class ClassInitElimination implements MethodOptimization { return false; } - class Step { + static class Step { int node; Set initializedClasses = new HashSet<>(); - public Step(int node) { + Step(int node) { this.node = node; } } diff --git a/core/src/main/java/org/teavm/model/optimization/DefaultInliningStrategy.java b/core/src/main/java/org/teavm/model/optimization/DefaultInliningStrategy.java index cf67fdbd1..1e3d7f489 100644 --- a/core/src/main/java/org/teavm/model/optimization/DefaultInliningStrategy.java +++ b/core/src/main/java/org/teavm/model/optimization/DefaultInliningStrategy.java @@ -115,6 +115,11 @@ public class DefaultInliningStrategy implements InliningStrategy { complexity--; } + @Override + public void assign(VariableReader receiver, VariableReader assignee) { + complexity--; + } + @Override public void invoke(VariableReader receiver, VariableReader instance, MethodReference method, List arguments, InvocationType type) { diff --git a/jso/apis/src/main/java/org/teavm/jso/core/JSArrayReader.java b/jso/apis/src/main/java/org/teavm/jso/core/JSArrayReader.java index 5291de9a6..706d92e09 100644 --- a/jso/apis/src/main/java/org/teavm/jso/core/JSArrayReader.java +++ b/jso/apis/src/main/java/org/teavm/jso/core/JSArrayReader.java @@ -15,14 +15,17 @@ */ package org.teavm.jso.core; +import org.teavm.interop.NoSideEffects; import org.teavm.jso.JSIndexer; import org.teavm.jso.JSObject; import org.teavm.jso.JSProperty; public interface JSArrayReader extends JSObject { @JSProperty + @NoSideEffects int getLength(); @JSIndexer + @NoSideEffects T get(int index); } diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JS.java b/jso/impl/src/main/java/org/teavm/jso/impl/JS.java index 2ef5e5c4b..25e6f507f 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JS.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JS.java @@ -20,6 +20,7 @@ import java.util.function.Function; import org.teavm.backend.javascript.spi.GeneratedBy; import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.dependency.PluggableDependency; +import org.teavm.interop.NoSideEffects; import org.teavm.jso.JSBody; import org.teavm.jso.JSObject; import org.teavm.jso.core.JSArray; @@ -33,83 +34,107 @@ final class JS { } @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native JSObject arrayData(Object array); @InjectedBy(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class) + @NoSideEffects public static native byte[] dataToByteArray(JSObject obj); @InjectedBy(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class) + @NoSideEffects public static native char[] dataToCharArray(JSObject obj); @InjectedBy(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class) + @NoSideEffects public static native short[] dataToShortArray(JSObject obj); @InjectedBy(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class) + @NoSideEffects public static native int[] dataToIntArray(JSObject obj); @InjectedBy(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class) + @NoSideEffects public static native float[] dataToFloatArray(JSObject obj); @InjectedBy(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class) + @NoSideEffects public static native double[] dataToDoubleArray(JSObject obj); @InjectedBy(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class) + @NoSideEffects public static native JSObject[] dataToArray(JSObject obj); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native JSObject wrap(byte value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native JSObject wrap(short value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native JSObject wrap(int value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native JSObject wrap(char value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native JSObject wrap(float value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native JSObject wrap(double value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native JSObject wrap(boolean value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native JSObject wrap(String value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native byte unwrapByte(JSObject value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native char unwrapCharacter(JSObject value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native short unwrapShort(JSObject value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native int unwrapInt(JSObject value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native float unwrapFloat(JSObject value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native double unwrapDouble(JSObject value); @InjectedBy(JSNativeGenerator.class) + @NoSideEffects public static native boolean unwrapBoolean(JSObject value); @InjectedBy(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class) + @NoSideEffects public static native String unwrapString(JSObject value); public static JSArray wrap(T[] array) { @@ -486,10 +511,20 @@ final class JS { @JSBody(params = { "instance", "index" }, script = "return instance[index];") public static native JSObject get(JSObject instance, JSObject index); + @InjectedBy(JSNativeGenerator.class) + @JSBody(params = { "instance", "index" }, script = "return instance[index];") + @NoSideEffects + public static native JSObject getPure(JSObject instance, JSObject index); + @InjectedBy(JSNativeGenerator.class) @JSBody(params = { "instance", "index", "obj" }, script = "instance[index] = obj;") public static native void set(JSObject instance, JSObject index, JSObject obj); + @InjectedBy(JSNativeGenerator.class) + @JSBody(params = { "instance", "index", "obj" }, script = "instance[index] = obj;") + @NoSideEffects + public static native void setPure(JSObject instance, JSObject index, JSObject obj); + @GeneratedBy(JSNativeGenerator.class) @PluggableDependency(JSNativeGenerator.class) public static native JSObject function(JSObject instance, JSObject property); diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java index 104fb999c..1997b8632 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSClassProcessor.java @@ -69,6 +69,7 @@ import org.teavm.model.util.ModelUtils; import org.teavm.model.util.ProgramUtils; class JSClassProcessor { + private static final String NO_SIDE_EFFECTS = NoSideEffects.class.getName(); private final ClassReaderSource classSource; private final JSBodyRepository repository; private final JavaInvocationProcessor javaInvocationProcessor; @@ -359,6 +360,7 @@ class JSClassProcessor { } private boolean processProperty(MethodReader method, CallLocation callLocation, InvokeInstruction invoke) { + boolean pure = method.getAnnotations().get(NO_SIDE_EFFECTS) != null; if (isProperGetter(method)) { String propertyName = extractSuggestedPropertyName(method); if (propertyName == null) { @@ -366,7 +368,7 @@ class JSClassProcessor { : cutPrefix(method.getName(), 3); } Variable result = invoke.getReceiver() != null ? program.createVariable() : null; - addPropertyGet(propertyName, invoke.getInstance(), result, invoke.getLocation()); + addPropertyGet(propertyName, invoke.getInstance(), result, invoke.getLocation(), pure); if (result != null) { result = marshaller.unwrapReturnValue(callLocation, result, method.getResultType(), false); copyVar(result, invoke.getReceiver(), invoke.getLocation()); @@ -380,7 +382,7 @@ class JSClassProcessor { } Variable wrapped = marshaller.wrapArgument(callLocation, invoke.getArguments().get(0), method.parameterType(0), false); - addPropertySet(propertyName, invoke.getInstance(), wrapped, invoke.getLocation()); + addPropertySet(propertyName, invoke.getInstance(), wrapped, invoke.getLocation(), pure); return true; } diagnostics.error(callLocation, "Method {{m0}} is not a proper native JavaScript property " @@ -674,22 +676,23 @@ class JSClassProcessor { } private void addPropertyGet(String propertyName, Variable instance, Variable receiver, - TextLocation location) { + TextLocation location, boolean pure) { Variable nameVar = marshaller.addStringWrap(marshaller.addString(propertyName, location), location); InvokeInstruction insn = new InvokeInstruction(); insn.setType(InvocationType.SPECIAL); - insn.setMethod(JSMethods.GET); + insn.setMethod(pure ? JSMethods.GET_PURE : JSMethods.GET); insn.setReceiver(receiver); insn.setArguments(instance, nameVar); insn.setLocation(location); replacement.add(insn); } - private void addPropertySet(String propertyName, Variable instance, Variable value, TextLocation location) { + private void addPropertySet(String propertyName, Variable instance, Variable value, TextLocation location, + boolean pure) { Variable nameVar = marshaller.addStringWrap(marshaller.addString(propertyName, location), location); InvokeInstruction insn = new InvokeInstruction(); insn.setType(InvocationType.SPECIAL); - insn.setMethod(JSMethods.SET); + insn.setMethod(pure ? JSMethods.SET_PURE : JSMethods.SET); insn.setArguments(instance, nameVar, value); insn.setLocation(location); replacement.add(insn); diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSMethods.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSMethods.java index 20d19a3f4..f652e1dca 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSMethods.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSMethods.java @@ -26,8 +26,12 @@ import org.teavm.model.ValueType; final class JSMethods { public static final MethodReference GET = new MethodReference(JS.class, "get", JSObject.class, JSObject.class, JSObject.class); + public static final MethodReference GET_PURE = new MethodReference(JS.class, "getPure", JSObject.class, + JSObject.class, JSObject.class); public static final MethodReference SET = new MethodReference(JS.class, "set", JSObject.class, JSObject.class, JSObject.class, void.class); + public static final MethodReference SET_PURE = new MethodReference(JS.class, "setPure", JSObject.class, + JSObject.class, JSObject.class, void.class); public static final MethodReference FUNCTION = new MethodReference(JS.class, "function", JSObject.class, JSObject.class, JSObject.class); public static final MethodReference ARRAY_DATA = new MethodReference(JS.class, "arrayData", diff --git a/jso/impl/src/main/java/org/teavm/jso/impl/JSNativeGenerator.java b/jso/impl/src/main/java/org/teavm/jso/impl/JSNativeGenerator.java index 71e97fce9..947ea27c8 100644 --- a/jso/impl/src/main/java/org/teavm/jso/impl/JSNativeGenerator.java +++ b/jso/impl/src/main/java/org/teavm/jso/impl/JSNativeGenerator.java @@ -98,10 +98,12 @@ public class JSNativeGenerator implements Injector, DependencyPlugin, Generator writer.append(".data"); break; case "get": + case "getPure": context.writeExpr(context.getArgument(0), Precedence.MEMBER_ACCESS); renderProperty(context.getArgument(1), context); break; case "set": + case "setPure": context.writeExpr(context.getArgument(0), Precedence.ASSIGNMENT.next()); renderProperty(context.getArgument(1), context); writer.ws().append('=').ws(); diff --git a/platform/src/main/java/org/teavm/platform/PlatformClass.java b/platform/src/main/java/org/teavm/platform/PlatformClass.java index 42b112fcf..1d00d3252 100644 --- a/platform/src/main/java/org/teavm/platform/PlatformClass.java +++ b/platform/src/main/java/org/teavm/platform/PlatformClass.java @@ -15,6 +15,7 @@ */ package org.teavm.platform; +import org.teavm.interop.NoSideEffects; import org.teavm.interop.Unmanaged; import org.teavm.jso.JSObject; import org.teavm.jso.JSProperty; @@ -22,13 +23,16 @@ import org.teavm.jso.JSProperty; public interface PlatformClass extends JSObject { @JSProperty("$meta") @Unmanaged + @NoSideEffects PlatformClassMetadata getMetadata(); @JSProperty("classObject") @Unmanaged + @NoSideEffects void setJavaClass(PlatformObject obj); @JSProperty("classObject") @Unmanaged + @NoSideEffects PlatformObject getJavaClass(); } diff --git a/platform/src/main/java/org/teavm/platform/PlatformClassMetadata.java b/platform/src/main/java/org/teavm/platform/PlatformClassMetadata.java index b6ff7f97c..4f60862d9 100644 --- a/platform/src/main/java/org/teavm/platform/PlatformClassMetadata.java +++ b/platform/src/main/java/org/teavm/platform/PlatformClassMetadata.java @@ -15,6 +15,7 @@ */ package org.teavm.platform; +import org.teavm.interop.NoSideEffects; import org.teavm.interop.Unmanaged; import org.teavm.jso.JSObject; import org.teavm.jso.JSProperty; @@ -22,40 +23,51 @@ import org.teavm.jso.JSProperty; public interface PlatformClassMetadata extends JSObject { @JSProperty("item") @Unmanaged + @NoSideEffects PlatformClass getArrayItem(); @JSProperty + @NoSideEffects PlatformSequence getSupertypes(); @JSProperty @Unmanaged + @NoSideEffects PlatformClass getSuperclass(); @JSProperty @Unmanaged + @NoSideEffects String getName(); @JSProperty + @NoSideEffects boolean isPrimitive(); @JSProperty + @NoSideEffects boolean isEnum(); @JSProperty + @NoSideEffects int getFlags(); @JSProperty + @NoSideEffects int getAccessLevel(); @JSProperty @Unmanaged + @NoSideEffects String getSimpleName(); @JSProperty @Unmanaged + @NoSideEffects PlatformClass getEnclosingClass(); @JSProperty @Unmanaged + @NoSideEffects PlatformClass getDeclaringClass(); } diff --git a/platform/src/main/java/org/teavm/platform/PlatformObject.java b/platform/src/main/java/org/teavm/platform/PlatformObject.java index 5da60dc2e..9d01aff1e 100644 --- a/platform/src/main/java/org/teavm/platform/PlatformObject.java +++ b/platform/src/main/java/org/teavm/platform/PlatformObject.java @@ -15,6 +15,7 @@ */ package org.teavm.platform; +import org.teavm.interop.NoSideEffects; import org.teavm.interop.Unmanaged; import org.teavm.jso.JSObject; import org.teavm.jso.JSProperty; @@ -22,13 +23,16 @@ import org.teavm.jso.JSProperty; public interface PlatformObject extends JSObject { @JSProperty("constructor") @Unmanaged + @NoSideEffects PlatformClass getPlatformClass(); @JSProperty("$id$") @Unmanaged + @NoSideEffects int getId(); @JSProperty("$id$") @Unmanaged + @NoSideEffects void setId(int id); } diff --git a/platform/src/main/java/org/teavm/platform/PlatformQueue.java b/platform/src/main/java/org/teavm/platform/PlatformQueue.java index fb7b853a7..58219a95c 100644 --- a/platform/src/main/java/org/teavm/platform/PlatformQueue.java +++ b/platform/src/main/java/org/teavm/platform/PlatformQueue.java @@ -17,12 +17,14 @@ package org.teavm.platform; import org.teavm.backend.javascript.spi.InjectedBy; import org.teavm.dependency.PluggableDependency; +import org.teavm.interop.NoSideEffects; import org.teavm.jso.JSObject; import org.teavm.jso.JSProperty; import org.teavm.platform.plugin.PlatformQueueGenerator; public abstract class PlatformQueue implements JSObject { @JSProperty + @NoSideEffects public abstract int getLength(); public final boolean isEmpty() { @@ -47,5 +49,6 @@ public abstract class PlatformQueue implements JSObject { @InjectedBy(PlatformQueueGenerator.class) @PluggableDependency(PlatformQueueGenerator.class) + @NoSideEffects private static native S unwrap(PlatformObject obj); }