From 44e6feef0c188120d503e8e991dbbe57b1ae67ca Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Thu, 16 Nov 2017 18:30:04 +0300 Subject: [PATCH] Add support for functions that return true or false depending on whether they run from TeaVM --- .../org/teavm/classlib/impl/JCLPlugin.java | 2 + .../classlib/impl/PlatformMarkerSupport.java | 128 ++++++++++++++++++ .../main/java/org/teavm/vm/TeaVMBuilder.java | 8 +- .../org/teavm/interop/PlatformMarker.java | 26 ++++ 4 files changed, 163 insertions(+), 1 deletion(-) create mode 100644 classlib/src/main/java/org/teavm/classlib/impl/PlatformMarkerSupport.java create mode 100644 interop/core/src/main/java/org/teavm/interop/PlatformMarker.java diff --git a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java index 08274f4e0..66c844bb3 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java @@ -71,5 +71,7 @@ public class JCLPlugin implements TeaVMPlugin { ReflectionDependencyListener reflection = new ReflectionDependencyListener(reflectionSuppliers); host.registerService(ReflectionDependencyListener.class, reflection); host.add(reflection); + + host.add(new PlatformMarkerSupport()); } } diff --git a/classlib/src/main/java/org/teavm/classlib/impl/PlatformMarkerSupport.java b/classlib/src/main/java/org/teavm/classlib/impl/PlatformMarkerSupport.java new file mode 100644 index 000000000..64a0b2eaa --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/PlatformMarkerSupport.java @@ -0,0 +1,128 @@ +/* + * Copyright 2017 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.impl; + +import org.teavm.diagnostics.Diagnostics; +import org.teavm.interop.PlatformMarker; +import org.teavm.model.BasicBlock; +import org.teavm.model.CallLocation; +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.ElementModifier; +import org.teavm.model.FieldReader; +import org.teavm.model.FieldReference; +import org.teavm.model.Instruction; +import org.teavm.model.MemberReader; +import org.teavm.model.MethodHolder; +import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; +import org.teavm.model.Program; +import org.teavm.model.ValueType; +import org.teavm.model.Variable; +import org.teavm.model.instructions.GetFieldInstruction; +import org.teavm.model.instructions.IntegerConstantInstruction; +import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.model.optimization.ConstantConditionElimination; +import org.teavm.model.optimization.GlobalValueNumbering; +import org.teavm.model.optimization.UnreachableBasicBlockElimination; + +public class PlatformMarkerSupport implements ClassHolderTransformer { + @Override + public void transformClass(ClassHolder cls, ClassReaderSource innerSource, Diagnostics diagnostics) { + for (MethodHolder method : cls.getMethods()) { + if (method.getProgram() != null) { + transformProgram(method.getReference(), method.getProgram(), innerSource, diagnostics); + } + } + } + + private void transformProgram(MethodReference containingMethod, Program program, + ClassReaderSource innerSource, Diagnostics diagnostics) { + boolean hasChanges = false; + + for (BasicBlock block : program.getBasicBlocks()) { + for (Instruction instruction : block) { + Variable receiver; + if (instruction instanceof InvokeInstruction) { + MethodReference methodRef = ((InvokeInstruction) instruction).getMethod(); + MethodReader method = innerSource.resolve(methodRef); + if (method == null || !isMarker(method)) { + continue; + } + if (!method.hasModifier(ElementModifier.STATIC)) { + diagnostics.error(new CallLocation(containingMethod, instruction.getLocation()), + "Method '{{m0}}' is marked with '{{c1}}' and should be static", + methodRef, PlatformMarker.class.getName()); + } + if (method.getResultType() != ValueType.BOOLEAN) { + diagnostics.error(new CallLocation(containingMethod, instruction.getLocation()), + "Method '{{m0}}' is marked with '{{c1}}' and should return boolean", + methodRef, PlatformMarker.class.getName()); + continue; + } + + receiver = ((InvokeInstruction) instruction).getReceiver(); + } else if (instruction instanceof GetFieldInstruction) { + FieldReference fieldRef = ((GetFieldInstruction) instruction).getField(); + FieldReader field = innerSource.resolve(fieldRef); + if (field == null || !isMarker(field)) { + continue; + } + if (!field.hasModifier(ElementModifier.STATIC)) { + diagnostics.error(new CallLocation(containingMethod, instruction.getLocation()), + "Field '{{f0}}' is marked with '{{c1}}' and should be static", + fieldRef, PlatformMarker.class.getName()); + } + if (field.getType() != ValueType.BOOLEAN) { + diagnostics.error(new CallLocation(containingMethod, instruction.getLocation()), + "Field '{{f0}}' is marked with '{{c1}}' and should be boolean", + fieldRef, PlatformMarker.class.getName()); + continue; + } + + receiver = ((GetFieldInstruction) instruction).getReceiver(); + } else { + continue; + } + + hasChanges = true; + if (receiver == null) { + instruction.delete(); + } else { + IntegerConstantInstruction trueResult = new IntegerConstantInstruction(); + trueResult.setReceiver(receiver); + trueResult.setConstant(1); + trueResult.setLocation(instruction.getLocation()); + instruction.replace(trueResult); + } + } + } + + if (hasChanges) { + boolean changed; + do { + changed = new GlobalValueNumbering(true).optimize(program) + | new ConstantConditionElimination().optimize(null, program) + | new UnreachableBasicBlockElimination().optimize(null, program); + } while (changed); + } + } + + private boolean isMarker(MemberReader member) { + return member.getAnnotations().get(PlatformMarker.class.getName()) != null; + } +} diff --git a/core/src/main/java/org/teavm/vm/TeaVMBuilder.java b/core/src/main/java/org/teavm/vm/TeaVMBuilder.java index fbc7599c4..c6d52cd9d 100644 --- a/core/src/main/java/org/teavm/vm/TeaVMBuilder.java +++ b/core/src/main/java/org/teavm/vm/TeaVMBuilder.java @@ -15,6 +15,7 @@ */ package org.teavm.vm; +import org.teavm.interop.PlatformMarker; import org.teavm.model.ClassHolderSource; import org.teavm.parsing.ClasspathClassHolderSource; @@ -26,7 +27,7 @@ public class TeaVMBuilder { public TeaVMBuilder(TeaVMTarget target) { this.target = target; classLoader = TeaVMBuilder.class.getClassLoader(); - classSource = new ClasspathClassHolderSource(classLoader); + classSource = !isBootstrap() ? new ClasspathClassHolderSource(classLoader) : name -> null; } public ClassHolderSource getClassSource() { @@ -50,4 +51,9 @@ public class TeaVMBuilder { public TeaVM build() { return new TeaVM(this); } + + @PlatformMarker + private static boolean isBootstrap() { + return false; + } } diff --git a/interop/core/src/main/java/org/teavm/interop/PlatformMarker.java b/interop/core/src/main/java/org/teavm/interop/PlatformMarker.java new file mode 100644 index 000000000..9374bd93b --- /dev/null +++ b/interop/core/src/main/java/org/teavm/interop/PlatformMarker.java @@ -0,0 +1,26 @@ +/* + * Copyright 2017 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.FIELD, ElementType.METHOD }) +public @interface PlatformMarker { +}