Add annotations to mark classes and methods supported on some platforms

This commit is contained in:
Alexey Andreev 2019-05-17 11:44:35 +03:00
parent 816434500c
commit 86b5daa3cf
10 changed files with 131 additions and 21 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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<String> reachableClasses;
private Collection<MethodReference> reachableMethods;
private Collection<FieldReference> reachableFields;
private Set<String> 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) {

View File

@ -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<String, ClassHolder> cache = new HashMap<>();
private Set<String> classNames = Collections.unmodifiableSet(new HashSet<>(
dependencyAnalyzer.getReachableClasses().stream()

View File

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

View File

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

View File

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

View File

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