mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 16:14:10 -08:00
Further bootstrapping
This commit is contained in:
parent
5fb1623c4e
commit
ff19dc15c9
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.impl;
|
package org.teavm.classlib.impl;
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
|
@ -22,12 +23,12 @@ import org.teavm.backend.javascript.TeaVMJavaScriptHost;
|
||||||
import org.teavm.classlib.ReflectionSupplier;
|
import org.teavm.classlib.ReflectionSupplier;
|
||||||
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
|
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
|
||||||
import org.teavm.classlib.impl.unicode.CLDRReader;
|
import org.teavm.classlib.impl.unicode.CLDRReader;
|
||||||
import org.teavm.classlib.java.lang.SystemNativeGenerator;
|
|
||||||
import org.teavm.classlib.java.lang.reflect.AnnotationDependencyListener;
|
import org.teavm.classlib.java.lang.reflect.AnnotationDependencyListener;
|
||||||
import org.teavm.interop.PlatformMarker;
|
import org.teavm.interop.PlatformMarker;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.platform.PlatformClass;
|
import org.teavm.platform.PlatformClass;
|
||||||
|
import org.teavm.vm.TeaVMPluginUtil;
|
||||||
import org.teavm.vm.spi.TeaVMHost;
|
import org.teavm.vm.spi.TeaVMHost;
|
||||||
import org.teavm.vm.spi.TeaVMPlugin;
|
import org.teavm.vm.spi.TeaVMPlugin;
|
||||||
|
|
||||||
|
@ -86,9 +87,11 @@ public class JCLPlugin implements TeaVMPlugin {
|
||||||
host.add(new PlatformMarkerSupport());
|
host.add(new PlatformMarkerSupport());
|
||||||
}
|
}
|
||||||
|
|
||||||
TeaVMJavaScriptHost jsHost = host.getExtension(TeaVMJavaScriptHost.class);
|
TeaVMPluginUtil.handleNatives(host, Class.class);
|
||||||
jsHost.add(new MethodReference("java.lang.System", "currentTimeMillis", ValueType.LONG),
|
TeaVMPluginUtil.handleNatives(host, ClassLoader.class);
|
||||||
new SystemNativeGenerator());
|
TeaVMPluginUtil.handleNatives(host, System.class);
|
||||||
|
TeaVMPluginUtil.handleNatives(host, Array.class);
|
||||||
|
TeaVMPluginUtil.handleNatives(host, Math.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PlatformMarker
|
@PlatformMarker
|
||||||
|
|
|
@ -102,6 +102,7 @@ public final class TSystem extends TObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
@DelegateTo("currentTimeMillisLowLevel")
|
@DelegateTo("currentTimeMillisLowLevel")
|
||||||
|
@GeneratedBy(SystemNativeGenerator.class)
|
||||||
public static native long currentTimeMillis();
|
public static native long currentTimeMillis();
|
||||||
|
|
||||||
private static long currentTimeMillisLowLevel() {
|
private static long currentTimeMillisLowLevel() {
|
||||||
|
|
|
@ -53,6 +53,7 @@ import org.teavm.common.GraphIndexer;
|
||||||
import org.teavm.common.Loop;
|
import org.teavm.common.Loop;
|
||||||
import org.teavm.common.LoopGraph;
|
import org.teavm.common.LoopGraph;
|
||||||
import org.teavm.common.RangeTree;
|
import org.teavm.common.RangeTree;
|
||||||
|
import org.teavm.interop.PlatformMarker;
|
||||||
import org.teavm.model.AnnotationHolder;
|
import org.teavm.model.AnnotationHolder;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderSource;
|
import org.teavm.model.ClassHolderSource;
|
||||||
|
@ -200,7 +201,7 @@ public class Decompiler {
|
||||||
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
|
if (method.getModifiers().contains(ElementModifier.ABSTRACT)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (method.getAnnotations().get(InjectedBy.class.getName()) != null
|
if ((!isBootstrap() && method.getAnnotations().get(InjectedBy.class.getName()) != null)
|
||||||
|| methodsToSkip.contains(method.getReference())) {
|
|| methodsToSkip.contains(method.getReference())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -220,7 +221,7 @@ public class Decompiler {
|
||||||
|
|
||||||
public NativeMethodNode decompileNative(MethodHolder method) {
|
public NativeMethodNode decompileNative(MethodHolder method) {
|
||||||
Generator generator = generators.get(method.getReference());
|
Generator generator = generators.get(method.getReference());
|
||||||
if (generator == null) {
|
if (generator == null && !isBootstrap()) {
|
||||||
AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName());
|
AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName());
|
||||||
if (annotHolder == null) {
|
if (annotHolder == null) {
|
||||||
throw new DecompilationException("Method " + method.getOwnerName() + "." + method.getDescriptor()
|
throw new DecompilationException("Method " + method.getOwnerName() + "." + method.getDescriptor()
|
||||||
|
@ -244,6 +245,11 @@ public class Decompiler {
|
||||||
return methodNode;
|
return methodNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PlatformMarker
|
||||||
|
private static boolean isBootstrap() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
public RegularMethodNode decompileRegular(MethodHolder method) {
|
public RegularMethodNode decompileRegular(MethodHolder method) {
|
||||||
if (regularMethodCache == null || method.getAnnotations().get(NoCache.class.getName()) != null) {
|
if (regularMethodCache == null || method.getAnnotations().get(NoCache.class.getName()) != null) {
|
||||||
return decompileRegularCacheMiss(method);
|
return decompileRegularCacheMiss(method);
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
import java.util.function.Function;
|
||||||
import org.teavm.ast.ClassNode;
|
import org.teavm.ast.ClassNode;
|
||||||
import org.teavm.ast.cache.EmptyRegularMethodNodeCache;
|
import org.teavm.ast.cache.EmptyRegularMethodNodeCache;
|
||||||
import org.teavm.ast.cache.MethodNodeCache;
|
import org.teavm.ast.cache.MethodNodeCache;
|
||||||
|
@ -48,10 +49,12 @@ import org.teavm.debugging.information.SourceLocation;
|
||||||
import org.teavm.dependency.DependencyAnalyzer;
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
import org.teavm.dependency.DependencyListener;
|
import org.teavm.dependency.DependencyListener;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
|
import org.teavm.interop.PlatformMarker;
|
||||||
import org.teavm.model.BasicBlock;
|
import org.teavm.model.BasicBlock;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.ListableClassHolderSource;
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
import org.teavm.model.ListableClassReaderSource;
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
|
@ -83,6 +86,8 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
private boolean minifying = true;
|
private boolean minifying = true;
|
||||||
private final Map<MethodReference, Generator> methodGenerators = new HashMap<>();
|
private final Map<MethodReference, Generator> methodGenerators = new HashMap<>();
|
||||||
private final Map<MethodReference, Injector> methodInjectors = new HashMap<>();
|
private final Map<MethodReference, Injector> methodInjectors = new HashMap<>();
|
||||||
|
private final List<Function<ProviderContext, Generator>> generatorProviders = new ArrayList<>();
|
||||||
|
private final List<Function<ProviderContext, Injector>> injectorProviders = new ArrayList<>();
|
||||||
private final List<RendererListener> rendererListeners = new ArrayList<>();
|
private final List<RendererListener> rendererListeners = new ArrayList<>();
|
||||||
private DebugInformationEmitter debugEmitter;
|
private DebugInformationEmitter debugEmitter;
|
||||||
private MethodNodeCache astCache = new EmptyRegularMethodNodeCache();
|
private MethodNodeCache astCache = new EmptyRegularMethodNodeCache();
|
||||||
|
@ -121,6 +126,16 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
methodInjectors.put(methodRef, injector);
|
methodInjectors.put(methodRef, injector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addGeneratorProvider(Function<ProviderContext, Generator> provider) {
|
||||||
|
generatorProviders.add(provider);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addInjectorProvider(Function<ProviderContext, Injector> provider) {
|
||||||
|
injectorProviders.add(provider);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reports whether this TeaVM instance uses obfuscation when generating the JavaScript code.
|
* Reports whether this TeaVM instance uses obfuscation when generating the JavaScript code.
|
||||||
*
|
*
|
||||||
|
@ -309,7 +324,7 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
for (String className : classOrder) {
|
for (String className : classOrder) {
|
||||||
ClassHolder cls = classes.get(className);
|
ClassHolder cls = classes.get(className);
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
for (MethodHolder method : cls.getMethods()) {
|
||||||
preprocessNativeMethod(method);
|
preprocessNativeMethod(method, decompiler);
|
||||||
if (controller.wasCancelled()) {
|
if (controller.wasCancelled()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -319,14 +334,44 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
return classNodes;
|
return classNodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void preprocessNativeMethod(MethodHolder method) {
|
private void preprocessNativeMethod(MethodHolder method, Decompiler decompiler) {
|
||||||
if (!method.getModifiers().contains(ElementModifier.NATIVE)
|
if (!method.getModifiers().contains(ElementModifier.NATIVE)
|
||||||
|| methodGenerators.get(method.getReference()) != null
|
|| methodGenerators.get(method.getReference()) != null
|
||||||
|| methodInjectors.get(method.getReference()) != null
|
|| methodInjectors.get(method.getReference()) != null) {
|
||||||
|| method.getAnnotations().get(GeneratedBy.class.getName()) != null
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean found = false;
|
||||||
|
ProviderContext context = new ProviderContextImpl(method.getReference());
|
||||||
|
for (Function<ProviderContext, Generator> provider : generatorProviders) {
|
||||||
|
Generator generator = provider.apply(context);
|
||||||
|
if (generator != null) {
|
||||||
|
methodGenerators.put(method.getReference(), generator);
|
||||||
|
decompiler.addGenerator(method.getReference(), generator);
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (Function<ProviderContext, Injector> provider : injectorProviders) {
|
||||||
|
Injector injector = provider.apply(context);
|
||||||
|
if (injector != null) {
|
||||||
|
methodInjectors.put(method.getReference(), injector);
|
||||||
|
decompiler.addMethodToSkip(method.getReference());
|
||||||
|
found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isBootstrap()) {
|
||||||
|
if (method.getAnnotations().get(GeneratedBy.class.getName()) != null
|
||||||
|| method.getAnnotations().get(InjectedBy.class.getName()) != null) {
|
|| method.getAnnotations().get(InjectedBy.class.getName()) != null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
method.getModifiers().remove(ElementModifier.NATIVE);
|
method.getModifiers().remove(ElementModifier.NATIVE);
|
||||||
|
|
||||||
Program program = new Program();
|
Program program = new Program();
|
||||||
|
@ -364,6 +409,29 @@ public class JavaScriptTarget implements TeaVMTarget, TeaVMJavaScriptHost {
|
||||||
"Native method {{m0}} has no implementation", method.getReference());
|
"Native method {{m0}} has no implementation", method.getReference());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ProviderContextImpl implements ProviderContext {
|
||||||
|
private MethodReference method;
|
||||||
|
|
||||||
|
ProviderContextImpl(MethodReference method) {
|
||||||
|
this.method = method;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodReference getMethod() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassReaderSource getClassSource() {
|
||||||
|
return controller.getUnprocessedClassSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@PlatformMarker
|
||||||
|
private static boolean isBootstrap() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private void emitCFG(DebugInformationEmitter emitter, Program program) {
|
private void emitCFG(DebugInformationEmitter emitter, Program program) {
|
||||||
Map<TextLocation, TextLocation[]> cfg = ProgramUtils.getLocationCFG(program);
|
Map<TextLocation, TextLocation[]> cfg = ProgramUtils.getLocationCFG(program);
|
||||||
for (Map.Entry<TextLocation, TextLocation[]> entry : cfg.entrySet()) {
|
for (Map.Entry<TextLocation, TextLocation[]> entry : cfg.entrySet()) {
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* 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.backend.javascript;
|
||||||
|
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public interface ProviderContext {
|
||||||
|
MethodReference getMethod();
|
||||||
|
|
||||||
|
ClassReaderSource getClassSource();
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.javascript;
|
package org.teavm.backend.javascript;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.Injector;
|
import org.teavm.backend.javascript.spi.Injector;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
@ -26,5 +27,9 @@ public interface TeaVMJavaScriptHost extends TeaVMHostExtension {
|
||||||
|
|
||||||
void add(MethodReference methodRef, Injector injector);
|
void add(MethodReference methodRef, Injector injector);
|
||||||
|
|
||||||
|
void addGeneratorProvider(Function<ProviderContext, Generator> provider);
|
||||||
|
|
||||||
|
void addInjectorProvider(Function<ProviderContext, Injector> provider);
|
||||||
|
|
||||||
void add(RendererListener listener);
|
void add(RendererListener listener);
|
||||||
}
|
}
|
||||||
|
|
|
@ -176,7 +176,7 @@ public class Renderer implements RenderingManager {
|
||||||
private void renderSetCloneMethod() throws IOException {
|
private void renderSetCloneMethod() throws IOException {
|
||||||
writer.append("function $rt_setCloneMethod(target, f)").ws().append("{").softNewLine().indent();
|
writer.append("function $rt_setCloneMethod(target, f)").ws().append("{").softNewLine().indent();
|
||||||
writer.append("target.").appendMethod("clone", Object.class).ws().append('=').ws().append("f;").
|
writer.append("target.").appendMethod("clone", Object.class).ws().append('=').ws().append("f;").
|
||||||
softNewLine().indent();
|
softNewLine();
|
||||||
writer.outdent().append("}").newLine();
|
writer.outdent().append("}").newLine();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,6 +30,7 @@ import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
import org.teavm.backend.javascript.spi.Injector;
|
import org.teavm.backend.javascript.spi.Injector;
|
||||||
import org.teavm.common.ServiceRepository;
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.debugging.information.DebugInformationEmitter;
|
import org.teavm.debugging.information.DebugInformationEmitter;
|
||||||
|
import org.teavm.interop.PlatformMarker;
|
||||||
import org.teavm.model.AnnotationReader;
|
import org.teavm.model.AnnotationReader;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ListableClassReaderSource;
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
|
@ -243,6 +244,7 @@ public class RenderingContext {
|
||||||
InjectorHolder holder = injectorMap.get(ref);
|
InjectorHolder holder = injectorMap.get(ref);
|
||||||
if (holder == null) {
|
if (holder == null) {
|
||||||
holder = new InjectorHolder(null);
|
holder = new InjectorHolder(null);
|
||||||
|
if (!isBootstrap()) {
|
||||||
ClassReader cls = classSource.get(ref.getClassName());
|
ClassReader cls = classSource.get(ref.getClassName());
|
||||||
if (cls != null) {
|
if (cls != null) {
|
||||||
MethodReader method = cls.getMethod(ref.getDescriptor());
|
MethodReader method = cls.getMethod(ref.getDescriptor());
|
||||||
|
@ -254,11 +256,17 @@ public class RenderingContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
injectorMap.put(ref, holder);
|
injectorMap.put(ref, holder);
|
||||||
}
|
}
|
||||||
return holder.injector;
|
return holder.injector;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PlatformMarker
|
||||||
|
private static boolean isBootstrap() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
private Injector instantiateInjector(String type) {
|
private Injector instantiateInjector(String type) {
|
||||||
try {
|
try {
|
||||||
Class<? extends Injector> cls = Class.forName(type, true, classLoader).asSubclass(Injector.class);
|
Class<? extends Injector> cls = Class.forName(type, true, classLoader).asSubclass(Injector.class);
|
||||||
|
|
|
@ -35,6 +35,7 @@ import org.teavm.common.CachedMapper;
|
||||||
import org.teavm.common.Mapper;
|
import org.teavm.common.Mapper;
|
||||||
import org.teavm.common.ServiceRepository;
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
import org.teavm.interop.PlatformMarker;
|
||||||
import org.teavm.model.AnnotationReader;
|
import org.teavm.model.AnnotationReader;
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
|
@ -83,6 +84,7 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
DefaultCallGraph callGraph = new DefaultCallGraph();
|
DefaultCallGraph callGraph = new DefaultCallGraph();
|
||||||
private DependencyAgent agent;
|
private DependencyAgent agent;
|
||||||
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
|
Map<MethodReference, BootstrapMethodSubstitutor> bootstrapMethodSubstitutors = new HashMap<>();
|
||||||
|
Map<MethodReference, DependencyPlugin> dependencyPlugins = new HashMap<>();
|
||||||
private boolean completing;
|
private boolean completing;
|
||||||
private Map<String, SuperClassFilter> superClassFilters = new HashMap<>();
|
private Map<String, SuperClassFilter> superClassFilters = new HashMap<>();
|
||||||
|
|
||||||
|
@ -320,6 +322,10 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
|
|
||||||
private Set<String> classesAddedByRoot = new HashSet<>();
|
private Set<String> classesAddedByRoot = new HashSet<>();
|
||||||
|
|
||||||
|
public void defer(Runnable task) {
|
||||||
|
deferredTasks.add(task);
|
||||||
|
}
|
||||||
|
|
||||||
public ClassDependency linkClass(String className, CallLocation callLocation) {
|
public ClassDependency linkClass(String className, CallLocation callLocation) {
|
||||||
if (completing && getClass(className) == null) {
|
if (completing && getClass(className) == null) {
|
||||||
throw new IllegalStateException("Can't link class during completion phase");
|
throw new IllegalStateException("Can't link class during completion phase");
|
||||||
|
@ -526,6 +532,12 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
methodDep.dependencyPluginAttached = true;
|
methodDep.dependencyPluginAttached = true;
|
||||||
|
|
||||||
|
methodDep.dependencyPlugin = dependencyPlugins.get(methodDep.getReference());
|
||||||
|
if (methodDep.dependencyPlugin != null || isBootstrap()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
AnnotationReader depAnnot = methodDep.getMethod().getAnnotations().get(PluggableDependency.class.getName());
|
AnnotationReader depAnnot = methodDep.getMethod().getAnnotations().get(PluggableDependency.class.getName());
|
||||||
if (depAnnot == null) {
|
if (depAnnot == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -545,6 +557,11 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PlatformMarker
|
||||||
|
private static boolean isBootstrap() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public MethodDependency getMethod(MethodReference methodRef) {
|
public MethodDependency getMethod(MethodReference methodRef) {
|
||||||
return methodCache.getKnown(methodRef);
|
return methodCache.getKnown(methodRef);
|
||||||
|
@ -660,6 +677,10 @@ public class DependencyAnalyzer implements DependencyInfo {
|
||||||
bootstrapMethodSubstitutors.put(method, substitutor);
|
bootstrapMethodSubstitutors.put(method, substitutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addDependencyPlugin(MethodReference method, DependencyPlugin dependencyPlugin) {
|
||||||
|
dependencyPlugins.put(method, dependencyPlugin);
|
||||||
|
}
|
||||||
|
|
||||||
SuperClassFilter getSuperClassFilter(String superClass) {
|
SuperClassFilter getSuperClassFilter(String superClass) {
|
||||||
return superClassFilters.computeIfAbsent(superClass, s -> new SuperClassFilter(classSource, s));
|
return superClassFilters.computeIfAbsent(superClass, s -> new SuperClassFilter(classSource, s));
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,7 +164,7 @@ public class AsyncMethodFinder {
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
diagnostics.warning(new CallLocation(methodRef), "Error as Warning because "
|
diagnostics.warning(new CallLocation(methodRef), "Error as Warning because "
|
||||||
+ " Method {{m0}} has @SuppressSyncErrors annoation. Method {{m0}} "
|
+ " Method {{m0}} has @SuppressSyncErrors annotation. Method {{m0}} "
|
||||||
+ "is claimed to be synchronous, but it is has invocations of "
|
+ "is claimed to be synchronous, but it is has invocations of "
|
||||||
+ "asynchronous methods:" + stack.toString(), methodRef);
|
+ "asynchronous methods:" + stack.toString(), methodRef);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ import org.teavm.dependency.BootstrapMethodSubstitutor;
|
||||||
import org.teavm.dependency.DependencyAnalyzer;
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
import org.teavm.dependency.DependencyInfo;
|
import org.teavm.dependency.DependencyInfo;
|
||||||
import org.teavm.dependency.DependencyListener;
|
import org.teavm.dependency.DependencyListener;
|
||||||
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
import org.teavm.dependency.Linker;
|
import org.teavm.dependency.Linker;
|
||||||
import org.teavm.diagnostics.AccumulationDiagnostics;
|
import org.teavm.diagnostics.AccumulationDiagnostics;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
@ -164,6 +165,11 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
dependencyAnalyzer.addBootstrapMethodSubstitutor(methodRef, substitutor);
|
dependencyAnalyzer.addBootstrapMethodSubstitutor(methodRef, substitutor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void add(MethodReference methodRef, DependencyPlugin dependencyPlugin) {
|
||||||
|
dependencyAnalyzer.addDependencyPlugin(methodRef, dependencyPlugin);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClassLoader getClassLoader() {
|
public ClassLoader getClassLoader() {
|
||||||
return classLoader;
|
return classLoader;
|
||||||
|
@ -249,7 +255,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TeaVMEntryPoint entryPoint = new TeaVMEntryPoint(name, ref, dependencyAnalyzer.linkMethod(ref, null));
|
TeaVMEntryPoint entryPoint = new TeaVMEntryPoint(name, ref, dependencyAnalyzer.linkMethod(ref, null));
|
||||||
|
dependencyAnalyzer.defer(() -> {
|
||||||
dependencyAnalyzer.linkClass(ref.getClassName(), null).initClass(null);
|
dependencyAnalyzer.linkClass(ref.getClassName(), null).initClass(null);
|
||||||
|
});
|
||||||
if (name != null) {
|
if (name != null) {
|
||||||
entryPoints.put(name, entryPoint);
|
entryPoints.put(name, entryPoint);
|
||||||
}
|
}
|
||||||
|
@ -274,7 +282,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
|
|
||||||
public TeaVMEntryPoint linkMethod(MethodReference ref) {
|
public TeaVMEntryPoint linkMethod(MethodReference ref) {
|
||||||
TeaVMEntryPoint entryPoint = new TeaVMEntryPoint("", ref, dependencyAnalyzer.linkMethod(ref, null));
|
TeaVMEntryPoint entryPoint = new TeaVMEntryPoint("", ref, dependencyAnalyzer.linkMethod(ref, null));
|
||||||
|
dependencyAnalyzer.defer(() -> {
|
||||||
dependencyAnalyzer.linkClass(ref.getClassName(), null).initClass(null);
|
dependencyAnalyzer.linkClass(ref.getClassName(), null).initClass(null);
|
||||||
|
});
|
||||||
return entryPoint;
|
return entryPoint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -283,7 +293,9 @@ public class TeaVM implements TeaVMHost, ServiceRepository {
|
||||||
throw new IllegalArgumentException("Class with public name `" + name + "' already defined for class "
|
throw new IllegalArgumentException("Class with public name `" + name + "' already defined for class "
|
||||||
+ className);
|
+ className);
|
||||||
}
|
}
|
||||||
|
dependencyAnalyzer.defer(() -> {
|
||||||
dependencyAnalyzer.linkClass(className, null).initClass(null);
|
dependencyAnalyzer.linkClass(className, null).initClass(null);
|
||||||
|
});
|
||||||
exportedClasses.put(name, className);
|
exportedClasses.put(name, className);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.backend.javascript.spi.Generator;
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
import org.teavm.backend.javascript.spi.InjectedBy;
|
||||||
import org.teavm.backend.javascript.spi.Injector;
|
import org.teavm.backend.javascript.spi.Injector;
|
||||||
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
import org.teavm.dependency.PluggableDependency;
|
import org.teavm.dependency.PluggableDependency;
|
||||||
import org.teavm.interop.PlatformMarker;
|
import org.teavm.interop.PlatformMarker;
|
||||||
import org.teavm.metaprogramming.CompileTime;
|
import org.teavm.metaprogramming.CompileTime;
|
||||||
|
@ -58,8 +59,6 @@ public final class TeaVMPluginUtil {
|
||||||
|
|
||||||
private static void handleNativesImpl(Value<TeaVMHost> host, Value<TeaVMJavaScriptHost> jsHost,
|
private static void handleNativesImpl(Value<TeaVMHost> host, Value<TeaVMJavaScriptHost> jsHost,
|
||||||
ReflectClass<?> cls) {
|
ReflectClass<?> cls) {
|
||||||
|
|
||||||
|
|
||||||
for (ReflectMethod method : cls.getDeclaredMethods()) {
|
for (ReflectMethod method : cls.getDeclaredMethods()) {
|
||||||
if (!Modifier.isNative(method.getModifiers())) {
|
if (!Modifier.isNative(method.getModifiers())) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -89,7 +88,7 @@ public final class TeaVMPluginUtil {
|
||||||
ReflectMethod generatorConstructor = generatorClass.getMethod("<init>");
|
ReflectMethod generatorConstructor = generatorClass.getMethod("<init>");
|
||||||
|
|
||||||
Value<MethodReference> methodRef = methodToReference(method);
|
Value<MethodReference> methodRef = methodToReference(method);
|
||||||
//emit(() -> host.get().add(methodRef.get(), (DependencyPlugin) generatorConstructor.construct()));
|
emit(() -> host.get().add(methodRef.get(), (DependencyPlugin) generatorConstructor.construct()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -102,6 +101,8 @@ public final class TeaVMPluginUtil {
|
||||||
int index = i;
|
int index = i;
|
||||||
emit(() -> signature.get()[index] = paramType.get());
|
emit(() -> signature.get()[index] = paramType.get());
|
||||||
}
|
}
|
||||||
|
Value<ValueType> returnType = classToValueType(method.getReturnType());
|
||||||
|
emit(() -> signature.get()[signatureSize - 1] = returnType.get());
|
||||||
|
|
||||||
String className = method.getDeclaringClass().getName();
|
String className = method.getDeclaringClass().getName();
|
||||||
String name = method.getName();
|
String name = method.getName();
|
||||||
|
|
|
@ -18,6 +18,7 @@ package org.teavm.vm.spi;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import org.teavm.dependency.BootstrapMethodSubstitutor;
|
import org.teavm.dependency.BootstrapMethodSubstitutor;
|
||||||
import org.teavm.dependency.DependencyListener;
|
import org.teavm.dependency.DependencyListener;
|
||||||
|
import org.teavm.dependency.DependencyPlugin;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.vm.TeaVM;
|
import org.teavm.vm.TeaVM;
|
||||||
|
@ -36,6 +37,8 @@ public interface TeaVMHost {
|
||||||
|
|
||||||
void add(MethodReference methodRef, BootstrapMethodSubstitutor substitutor);
|
void add(MethodReference methodRef, BootstrapMethodSubstitutor substitutor);
|
||||||
|
|
||||||
|
void add(MethodReference methodRef, DependencyPlugin dependencyPlugin);
|
||||||
|
|
||||||
<T extends TeaVMHostExtension> T getExtension(Class<T> extensionType);
|
<T extends TeaVMHostExtension> T getExtension(Class<T> extensionType);
|
||||||
|
|
||||||
<T> void registerService(Class<T> type, T instance);
|
<T> void registerService(Class<T> type, T instance);
|
||||||
|
|
|
@ -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.jso.impl;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@interface DynamicGenerator {
|
||||||
|
}
|
|
@ -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.jso.impl;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
@Retention(RetentionPolicy.CLASS)
|
||||||
|
@Target(ElementType.METHOD)
|
||||||
|
@interface DynamicInjector {
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* 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.jso.impl;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
import org.teavm.backend.javascript.ProviderContext;
|
||||||
|
import org.teavm.model.ClassReader;
|
||||||
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
class GeneratorAnnotationInstaller<T> implements Function<ProviderContext, T> {
|
||||||
|
private T generator;
|
||||||
|
private String annotationName;
|
||||||
|
|
||||||
|
GeneratorAnnotationInstaller(T generator, String annotationName) {
|
||||||
|
this.generator = generator;
|
||||||
|
this.annotationName = annotationName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public T apply(ProviderContext providerContext) {
|
||||||
|
ClassReaderSource classSource = providerContext.getClassSource();
|
||||||
|
MethodReference methodRef = providerContext.getMethod();
|
||||||
|
ClassReader cls = classSource.get(methodRef.getClassName());
|
||||||
|
if (cls == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MethodReader method = cls.getMethod(methodRef.getDescriptor());
|
||||||
|
if (method == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return method.getAnnotations().get(annotationName) != null ? generator : null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -30,8 +30,6 @@ import org.mozilla.javascript.Context;
|
||||||
import org.mozilla.javascript.ast.AstNode;
|
import org.mozilla.javascript.ast.AstNode;
|
||||||
import org.mozilla.javascript.ast.AstRoot;
|
import org.mozilla.javascript.ast.AstRoot;
|
||||||
import org.mozilla.javascript.ast.FunctionNode;
|
import org.mozilla.javascript.ast.FunctionNode;
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
|
||||||
import org.teavm.backend.javascript.spi.InjectedBy;
|
|
||||||
import org.teavm.cache.NoCache;
|
import org.teavm.cache.NoCache;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.interop.Sync;
|
import org.teavm.interop.Sync;
|
||||||
|
@ -592,9 +590,7 @@ class JSClassProcessor {
|
||||||
proxyMethod.getModifiers().add(ElementModifier.STATIC);
|
proxyMethod.getModifiers().add(ElementModifier.STATIC);
|
||||||
boolean inline = repository.inlineMethods.contains(methodRef);
|
boolean inline = repository.inlineMethods.contains(methodRef);
|
||||||
AnnotationHolder generatorAnnot = new AnnotationHolder(inline
|
AnnotationHolder generatorAnnot = new AnnotationHolder(inline
|
||||||
? InjectedBy.class.getName() : GeneratedBy.class.getName());
|
? DynamicInjector.class.getName() : DynamicGenerator.class.getName());
|
||||||
generatorAnnot.getValues().put("value",
|
|
||||||
new AnnotationValue(ValueType.parse(JSBodyGenerator.class)));
|
|
||||||
proxyMethod.getAnnotations().add(generatorAnnot);
|
proxyMethod.getAnnotations().add(generatorAnnot);
|
||||||
cls.addMethod(proxyMethod);
|
cls.addMethod(proxyMethod);
|
||||||
|
|
||||||
|
|
|
@ -16,13 +16,15 @@
|
||||||
package org.teavm.jso.impl;
|
package org.teavm.jso.impl;
|
||||||
|
|
||||||
import org.teavm.backend.javascript.TeaVMJavaScriptHost;
|
import org.teavm.backend.javascript.TeaVMJavaScriptHost;
|
||||||
|
import org.teavm.vm.TeaVMPluginUtil;
|
||||||
import org.teavm.vm.spi.TeaVMHost;
|
import org.teavm.vm.spi.TeaVMHost;
|
||||||
import org.teavm.vm.spi.TeaVMPlugin;
|
import org.teavm.vm.spi.TeaVMPlugin;
|
||||||
|
|
||||||
public class JSOPlugin implements TeaVMPlugin {
|
public class JSOPlugin implements TeaVMPlugin {
|
||||||
@Override
|
@Override
|
||||||
public void install(TeaVMHost host) {
|
public void install(TeaVMHost host) {
|
||||||
if (host.getExtension(TeaVMJavaScriptHost.class) == null) {
|
TeaVMJavaScriptHost jsHost = host.getExtension(TeaVMJavaScriptHost.class);
|
||||||
|
if (jsHost == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,6 +34,14 @@ public class JSOPlugin implements TeaVMPlugin {
|
||||||
JSDependencyListener dependencyListener = new JSDependencyListener(repository);
|
JSDependencyListener dependencyListener = new JSDependencyListener(repository);
|
||||||
JSAliasRenderer aliasRenderer = new JSAliasRenderer();
|
JSAliasRenderer aliasRenderer = new JSAliasRenderer();
|
||||||
host.add(dependencyListener);
|
host.add(dependencyListener);
|
||||||
host.getExtension(TeaVMJavaScriptHost.class).add(aliasRenderer);
|
|
||||||
|
|
||||||
|
jsHost.add(aliasRenderer);
|
||||||
|
jsHost.addGeneratorProvider(new GeneratorAnnotationInstaller<>(new JSBodyGenerator(),
|
||||||
|
DynamicGenerator.class.getName()));
|
||||||
|
jsHost.addInjectorProvider(new GeneratorAnnotationInstaller<>(new JSBodyGenerator(),
|
||||||
|
DynamicInjector.class.getName()));
|
||||||
|
|
||||||
|
TeaVMPluginUtil.handleNatives(host, JS.class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ public class ReflectClassImpl<T> implements ReflectClass<T> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isPrimitive() {
|
public boolean isPrimitive() {
|
||||||
return type instanceof ValueType.Primitive;
|
return type instanceof ValueType.Primitive || type == ValueType.VOID;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -22,10 +22,9 @@ import org.teavm.backend.wasm.intrinsics.WasmIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager;
|
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
import org.teavm.interop.PlatformMarker;
|
import org.teavm.interop.PlatformMarker;
|
||||||
import org.teavm.metaprogramming.Meta;
|
|
||||||
import org.teavm.metaprogramming.Value;
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.platform.Platform;
|
import org.teavm.platform.Platform;
|
||||||
|
import org.teavm.platform.PlatformQueue;
|
||||||
import org.teavm.vm.TeaVMPluginUtil;
|
import org.teavm.vm.TeaVMPluginUtil;
|
||||||
import org.teavm.vm.spi.TeaVMHost;
|
import org.teavm.vm.spi.TeaVMHost;
|
||||||
import org.teavm.vm.spi.TeaVMPlugin;
|
import org.teavm.vm.spi.TeaVMPlugin;
|
||||||
|
@ -71,6 +70,7 @@ public class PlatformPlugin implements TeaVMPlugin {
|
||||||
host.add(new PlatformDependencyListener());
|
host.add(new PlatformDependencyListener());
|
||||||
|
|
||||||
TeaVMPluginUtil.handleNatives(host, Platform.class);
|
TeaVMPluginUtil.handleNatives(host, Platform.class);
|
||||||
|
TeaVMPluginUtil.handleNatives(host, PlatformQueue.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@PlatformMarker
|
@PlatformMarker
|
||||||
|
|
Loading…
Reference in New Issue
Block a user