From 86b5daa3cf1ae89518501bd68334790944783a5d Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 17 May 2019 11:44:35 +0300 Subject: [PATCH] Add annotations to mark classes and methods supported on some platforms --- .../org/teavm/classlib/PlatformDetector.java | 10 +-- .../java/org/teavm/backend/c/CTarget.java | 4 +- .../backend/javascript/JavaScriptTarget.java | 4 +- .../org/teavm/backend/wasm/WasmTarget.java | 4 +- .../model/util/MissingItemsProcessor.java | 64 +++++++++++++++++-- core/src/main/java/org/teavm/vm/TeaVM.java | 4 +- .../{PlatformMarkers.java => Platforms.java} | 4 +- .../java/org/teavm/interop/SupportedOn.java | 27 ++++++++ .../java/org/teavm/interop/UnsupportedOn.java | 27 ++++++++ .../java/org/teavm/platform/Platform.java | 4 +- 10 files changed, 131 insertions(+), 21 deletions(-) rename interop/core/src/main/java/org/teavm/interop/{PlatformMarkers.java => Platforms.java} (92%) create mode 100644 interop/core/src/main/java/org/teavm/interop/SupportedOn.java create mode 100644 interop/core/src/main/java/org/teavm/interop/UnsupportedOn.java diff --git a/classlib/src/main/java/org/teavm/classlib/PlatformDetector.java b/classlib/src/main/java/org/teavm/classlib/PlatformDetector.java index 4d87827ad..f634b0626 100644 --- a/classlib/src/main/java/org/teavm/classlib/PlatformDetector.java +++ b/classlib/src/main/java/org/teavm/classlib/PlatformDetector.java @@ -16,28 +16,28 @@ package org.teavm.classlib; import org.teavm.interop.PlatformMarker; -import org.teavm.interop.PlatformMarkers; +import org.teavm.interop.Platforms; public final class PlatformDetector { private PlatformDetector() { } - @PlatformMarker(PlatformMarkers.WEBASSEMBLY) + @PlatformMarker(Platforms.WEBASSEMBLY) public static boolean isWebAssembly() { return false; } - @PlatformMarker(PlatformMarkers.JAVASCRIPT) + @PlatformMarker(Platforms.JAVASCRIPT) public static boolean isJavaScript() { return false; } - @PlatformMarker(PlatformMarkers.C) + @PlatformMarker(Platforms.C) public static boolean isC() { return false; } - @PlatformMarker(PlatformMarkers.LOW_LEVEL) + @PlatformMarker(Platforms.LOW_LEVEL) public static boolean isLowLevel() { return false; } diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index 20dfbb769..cfb846b00 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -77,7 +77,7 @@ import org.teavm.dependency.ClassDependency; import org.teavm.dependency.DependencyAnalyzer; import org.teavm.dependency.DependencyListener; import org.teavm.interop.Address; -import org.teavm.interop.PlatformMarkers; +import org.teavm.interop.Platforms; import org.teavm.interop.Structure; import org.teavm.model.BasicBlock; import org.teavm.model.ClassHierarchy; @@ -775,7 +775,7 @@ public class CTarget implements TeaVMTarget, TeaVMCHost { @Override public String[] getPlatformTags() { - return new String[] { PlatformMarkers.C, PlatformMarkers.LOW_LEVEL }; + return new String[] { Platforms.C, Platforms.LOW_LEVEL }; } @Override diff --git a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java index 315027d5f..0c9b60639 100644 --- a/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java +++ b/core/src/main/java/org/teavm/backend/javascript/JavaScriptTarget.java @@ -72,7 +72,7 @@ import org.teavm.dependency.DependencyListener; import org.teavm.dependency.DependencyType; import org.teavm.dependency.MethodDependency; import org.teavm.interop.PlatformMarker; -import org.teavm.interop.PlatformMarkers; +import org.teavm.interop.Platforms; import org.teavm.model.AnnotationHolder; import org.teavm.model.BasicBlock; import org.teavm.model.CallLocation; @@ -726,7 +726,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost { @Override public String[] getPlatformTags() { - return new String[] { PlatformMarkers.JAVASCRIPT }; + return new String[] { Platforms.JAVASCRIPT }; } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index d804b1ade..be771a14a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -100,7 +100,7 @@ import org.teavm.diagnostics.Diagnostics; import org.teavm.interop.Address; import org.teavm.interop.DelegateTo; import org.teavm.interop.Import; -import org.teavm.interop.PlatformMarkers; +import org.teavm.interop.Platforms; import org.teavm.interop.StaticInit; import org.teavm.model.AnnotationHolder; import org.teavm.model.BasicBlock; @@ -806,7 +806,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { @Override public String[] getPlatformTags() { - return new String[] { PlatformMarkers.WEBASSEMBLY, PlatformMarkers.LOW_LEVEL }; + return new String[] { Platforms.WEBASSEMBLY, Platforms.LOW_LEVEL }; } @Override diff --git a/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java b/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java index 40c0b350f..15d9c741c 100644 --- a/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java +++ b/core/src/main/java/org/teavm/model/util/MissingItemsProcessor.java @@ -16,11 +16,16 @@ package org.teavm.model.util; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.teavm.dependency.DependencyInfo; import org.teavm.dependency.MethodDependencyInfo; import org.teavm.diagnostics.Diagnostics; +import org.teavm.interop.SupportedOn; +import org.teavm.interop.UnsupportedOn; import org.teavm.model.*; import org.teavm.model.instructions.*; import org.teavm.model.optimization.UnreachableBasicBlockEliminator; @@ -35,14 +40,17 @@ public class MissingItemsProcessor { private Collection reachableClasses; private Collection reachableMethods; private Collection reachableFields; + private Set platformTags = new HashSet<>(); - public MissingItemsProcessor(DependencyInfo dependencyInfo, ClassHierarchy hierarchy, Diagnostics diagnostics) { + public MissingItemsProcessor(DependencyInfo dependencyInfo, ClassHierarchy hierarchy, Diagnostics diagnostics, + String[] platformTags) { this.dependencyInfo = dependencyInfo; this.diagnostics = diagnostics; this.hierarchy = hierarchy; reachableClasses = dependencyInfo.getReachableClasses(); reachableMethods = dependencyInfo.getReachableMethods(); reachableFields = dependencyInfo.getReachableFields(); + this.platformTags.addAll(Arrays.asList(platformTags)); } public void processClass(ClassHolder cls) { @@ -132,9 +140,21 @@ public class MissingItemsProcessor { } private boolean checkClass(TextLocation location, String className) { - if (!reachableClasses.contains(className) || !dependencyInfo.getClass(className).isMissing()) { + if (!reachableClasses.contains(className)) { + return false; + } + + if (!dependencyInfo.getClass(className).isMissing()) { + ClassReader cls = dependencyInfo.getClassSource().get(className); + if (cls != null && !checkPlatformSupported(cls.getAnnotations())) { + diagnostics.error(new CallLocation(methodRef, location), "Class {{c0}} is not supported on " + + "current target", className); + emitExceptionThrow(location, NoClassDefFoundError.class.getName(), "Class not found: " + className); + return false; + } return true; } + diagnostics.error(new CallLocation(methodRef, location), "Class {{c0}} was not found", className); emitExceptionThrow(location, NoClassDefFoundError.class.getName(), "Class not found: " + className); @@ -159,14 +179,28 @@ public class MissingItemsProcessor { return true; } MethodDependencyInfo methodDep = dependencyInfo.getMethod(method); - if (!methodDep.isMissing() || !methodDep.isUsed()) { + if (!methodDep.isUsed()) { + return true; + } + + if (!methodDep.isMissing()) { + ClassReader cls = dependencyInfo.getClassSource().get(method.getClassName()); + if (cls != null) { + MethodReader methodReader = cls.getMethod(method.getDescriptor()); + if (methodReader != null && !checkPlatformSupported(methodReader.getAnnotations())) { + diagnostics.error(new CallLocation(methodRef, location), "Method {{m0}} is not supported on " + + "current target", method); + emitExceptionThrow(location, NoSuchMethodError.class.getName(), "Method not found: " + method); + return false; + } + } return true; } diagnostics.error(new CallLocation(methodRef, location), "Method {{m0}} was not found", method); emitExceptionThrow(location, NoSuchMethodError.class.getName(), "Method not found: " + method); - return true; + return false; } private boolean checkVirtualMethod(TextLocation location, MethodReference method) { @@ -200,6 +234,28 @@ public class MissingItemsProcessor { return true; } + private boolean checkPlatformSupported(AnnotationContainerReader annotations) { + AnnotationReader supportedAnnot = annotations.get(SupportedOn.class.getName()); + AnnotationReader unsupportedAnnot = annotations.get(UnsupportedOn.class.getName()); + if (supportedAnnot != null) { + for (AnnotationValue value : supportedAnnot.getValue("value").getList()) { + if (platformTags.contains(value.getString())) { + return true; + } + } + return false; + } + if (unsupportedAnnot != null) { + for (AnnotationValue value : unsupportedAnnot.getValue("value").getList()) { + if (platformTags.contains(value.getString())) { + return false; + } + } + return true; + } + return true; + } + private InstructionVisitor instructionProcessor = new AbstractInstructionVisitor() { @Override public void visit(InitClassInstruction insn) { diff --git a/core/src/main/java/org/teavm/vm/TeaVM.java b/core/src/main/java/org/teavm/vm/TeaVM.java index 386441e54..27ae7e75b 100644 --- a/core/src/main/java/org/teavm/vm/TeaVM.java +++ b/core/src/main/java/org/teavm/vm/TeaVM.java @@ -551,7 +551,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { Linker linker = new Linker(dependency); MutableClassHolderSource cutClasses = new MutableClassHolderSource(); MissingItemsProcessor missingItemsProcessor = new MissingItemsProcessor(dependency, - dependency.getClassHierarchy(), diagnostics); + dependency.getClassHierarchy(), diagnostics, target.getPlatformTags()); if (wasCancelled()) { return cutClasses; } @@ -913,7 +913,7 @@ public class TeaVM implements TeaVMHost, ServiceRepository { class PostProcessingClassHolderSource implements ListableClassHolderSource { private Linker linker = new Linker(dependencyAnalyzer); private MissingItemsProcessor missingItemsProcessor = new MissingItemsProcessor(dependencyAnalyzer, - dependencyAnalyzer.getClassHierarchy(), diagnostics); + dependencyAnalyzer.getClassHierarchy(), diagnostics, target.getPlatformTags()); private Map cache = new HashMap<>(); private Set classNames = Collections.unmodifiableSet(new HashSet<>( dependencyAnalyzer.getReachableClasses().stream() diff --git a/interop/core/src/main/java/org/teavm/interop/PlatformMarkers.java b/interop/core/src/main/java/org/teavm/interop/Platforms.java similarity index 92% rename from interop/core/src/main/java/org/teavm/interop/PlatformMarkers.java rename to interop/core/src/main/java/org/teavm/interop/Platforms.java index c53ef5f55..521bb0381 100644 --- a/interop/core/src/main/java/org/teavm/interop/PlatformMarkers.java +++ b/interop/core/src/main/java/org/teavm/interop/Platforms.java @@ -15,8 +15,8 @@ */ package org.teavm.interop; -public final class PlatformMarkers { - private PlatformMarkers() { +public final class Platforms { + private Platforms() { } public static final String JAVASCRIPT = "javascript"; diff --git a/interop/core/src/main/java/org/teavm/interop/SupportedOn.java b/interop/core/src/main/java/org/teavm/interop/SupportedOn.java new file mode 100644 index 000000000..cb1a29c63 --- /dev/null +++ b/interop/core/src/main/java/org/teavm/interop/SupportedOn.java @@ -0,0 +1,27 @@ +/* + * 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.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR}) +public @interface SupportedOn { + String[] value(); +} diff --git a/interop/core/src/main/java/org/teavm/interop/UnsupportedOn.java b/interop/core/src/main/java/org/teavm/interop/UnsupportedOn.java new file mode 100644 index 000000000..bbefe4af5 --- /dev/null +++ b/interop/core/src/main/java/org/teavm/interop/UnsupportedOn.java @@ -0,0 +1,27 @@ +/* + * 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.TYPE, ElementType.METHOD, ElementType.CONSTRUCTOR}) +public @interface UnsupportedOn { + String[] value(); +} diff --git a/platform/src/main/java/org/teavm/platform/Platform.java b/platform/src/main/java/org/teavm/platform/Platform.java index f1eff2919..45b50473b 100644 --- a/platform/src/main/java/org/teavm/platform/Platform.java +++ b/platform/src/main/java/org/teavm/platform/Platform.java @@ -23,7 +23,7 @@ import org.teavm.interop.Address; import org.teavm.interop.DelegateTo; import org.teavm.interop.NoSideEffects; import org.teavm.interop.PlatformMarker; -import org.teavm.interop.PlatformMarkers; +import org.teavm.interop.Platforms; import org.teavm.interop.Unmanaged; import org.teavm.jso.JSBody; import org.teavm.jso.JSObject; @@ -251,7 +251,7 @@ public final class Platform { return cls.getMetadata().getName(); } - @PlatformMarker(PlatformMarkers.LOW_LEVEL) + @PlatformMarker(Platforms.LOW_LEVEL) private static boolean isLowLevel() { return false; }