mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Fix bootstrap mode
This commit is contained in:
parent
b754c7401e
commit
48227b24a0
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* 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.classlib;
|
||||
|
||||
public interface ServiceLoaderFilter {
|
||||
boolean apply(String serviceType, String implementationType);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* 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.classlib;
|
||||
|
||||
import org.teavm.model.ClassReaderSource;
|
||||
|
||||
public interface ServiceLoaderFilterContext {
|
||||
ClassLoader getClassLoader();
|
||||
|
||||
ClassReaderSource getClassSource();
|
||||
}
|
|
@ -19,17 +19,21 @@ import java.io.BufferedReader;
|
|||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||
import org.teavm.backend.javascript.spi.Generator;
|
||||
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||
import org.teavm.classlib.ServiceLoaderFilter;
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyNode;
|
||||
|
@ -85,8 +89,48 @@ public class ServiceLoaderSupport extends AbstractDependencyListener implements
|
|||
writer.append("return result;").softNewLine();
|
||||
}
|
||||
|
||||
private void parseServiceFile(DependencyAgent agent, DependencyNode targetNode, String service,
|
||||
InputStream input, CallLocation location) throws IOException {
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
MethodReference ref = method.getReference();
|
||||
if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) {
|
||||
List<ServiceLoaderFilter> filters = getFilters(agent);
|
||||
method.getResult().propagate(agent.getType("[Ljava/lang/Object;"));
|
||||
DependencyNode sourceNode = agent.linkMethod(LOAD_METHOD).getVariable(1).getClassValueNode();
|
||||
sourceNode.connect(method.getResult().getArrayItem());
|
||||
sourceNode.addConsumer(type -> {
|
||||
CallLocation location = new CallLocation(LOAD_METHOD);
|
||||
for (String implementationType : getImplementations(type.getName())) {
|
||||
if (filters.stream().anyMatch(filter -> !filter.apply(type.getName(), implementationType))) {
|
||||
continue;
|
||||
}
|
||||
serviceMap.computeIfAbsent(type.getName(), k -> new ArrayList<>()).add(implementationType);
|
||||
|
||||
MethodReference ctor = new MethodReference(implementationType,
|
||||
new MethodDescriptor("<init>", ValueType.VOID));
|
||||
agent.linkMethod(ctor).addLocation(location).use();
|
||||
method.getResult().getArrayItem().propagate(agent.getType(implementationType));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private Set<String> getImplementations(String type) {
|
||||
Set<String> result = new LinkedHashSet<>();
|
||||
try {
|
||||
Enumeration<URL> resources = classLoader.getResources("META-INF/services/" + type);
|
||||
while (resources.hasMoreElements()) {
|
||||
URL resource = resources.nextElement();
|
||||
try (InputStream stream = resource.openStream()) {
|
||||
parseServiceFile(stream, result);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void parseServiceFile(InputStream input, Set<String> consumer) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
|
@ -97,38 +141,37 @@ public class ServiceLoaderSupport extends AbstractDependencyListener implements
|
|||
if (line.startsWith("#") || line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
serviceMap.computeIfAbsent(service, k -> new ArrayList<>()).add(line);
|
||||
|
||||
MethodReference ctor = new MethodReference(line, new MethodDescriptor("<init>", ValueType.VOID));
|
||||
agent.linkMethod(ctor).addLocation(location).use();
|
||||
targetNode.propagate(agent.getType(line));
|
||||
consumer.add(line);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||
MethodReference ref = method.getReference();
|
||||
if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) {
|
||||
method.getResult().propagate(agent.getType("[Ljava/lang/Object;"));
|
||||
DependencyNode sourceNode = agent.linkMethod(LOAD_METHOD).getVariable(1).getClassValueNode();
|
||||
sourceNode.connect(method.getResult().getArrayItem());
|
||||
sourceNode.addConsumer(type -> initConstructor(agent, method.getResult().getArrayItem(),
|
||||
type.getName(), new CallLocation(LOAD_METHOD)));
|
||||
}
|
||||
}
|
||||
|
||||
private void initConstructor(DependencyAgent agent, DependencyNode targetNode, String type,
|
||||
CallLocation location) {
|
||||
try {
|
||||
Enumeration<URL> resources = classLoader.getResources("META-INF/services/" + type);
|
||||
while (resources.hasMoreElements()) {
|
||||
URL resource = resources.nextElement();
|
||||
try (InputStream stream = resource.openStream()) {
|
||||
parseServiceFile(agent, targetNode, type, stream, location);
|
||||
}
|
||||
private List<ServiceLoaderFilter> getFilters(DependencyAgent agent) {
|
||||
List<ServiceLoaderFilter> filters = new ArrayList<>();
|
||||
for (String filterTypeName : getImplementations(ServiceLoaderFilter.class.getName())) {
|
||||
Class<?> filterType;
|
||||
try {
|
||||
filterType = Class.forName(filterTypeName, true, classLoader);
|
||||
} catch (ClassNotFoundException e) {
|
||||
agent.getDiagnostics().error(null, "Could not load ServiceLoader filter class '{{c0}}'",
|
||||
filterTypeName);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!ServiceLoaderFilter.class.isAssignableFrom(filterType)) {
|
||||
agent.getDiagnostics().error(null, "Class '{{c0}}' does not implement ServiceLoaderFilter interface",
|
||||
filterTypeName);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
filters.add((ServiceLoaderFilter) filterType.getConstructor().newInstance());
|
||||
} catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException
|
||||
| InstantiationException e) {
|
||||
agent.getDiagnostics().error(null, "Could not instantiate ServiceLoader filter '{{c0}}'",
|
||||
filterTypeName);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return filters;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* 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.classlib.java.lang.reflect;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
public interface TInvocationHandler {
|
||||
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
|
||||
}
|
|
@ -18,12 +18,6 @@ package org.teavm.classlib.java.util;
|
|||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev
|
||||
* @param <K>
|
||||
* @param <V>
|
||||
*/
|
||||
public interface TMap<K, V> {
|
||||
interface Entry<K1, V1> {
|
||||
K1 getKey();
|
||||
|
@ -43,6 +37,10 @@ public interface TMap<K, V> {
|
|||
|
||||
V get(Object key);
|
||||
|
||||
default V getOrDefault(K key, V defaultValue) {
|
||||
return containsKey(key) ? get(key) : defaultValue;
|
||||
}
|
||||
|
||||
V put(K key, V value);
|
||||
|
||||
V remove(Object key);
|
||||
|
|
|
@ -453,68 +453,15 @@ public class Renderer implements RenderingManager {
|
|||
|
||||
int start = writer.getOffset();
|
||||
try {
|
||||
writer.append("$rt_metadata([");
|
||||
writer.append("$rt_packages([");
|
||||
ObjectIntMap<String> packageIndexes = generatePackageMetadata(classes, classesRequiringName);
|
||||
|
||||
boolean first = true;
|
||||
for (ClassNode cls : classes) {
|
||||
if (!first) {
|
||||
writer.append(',').softNewLine();
|
||||
}
|
||||
first = false;
|
||||
writer.appendClass(cls.getName()).append(",").ws();
|
||||
|
||||
if (classesRequiringName.contains(cls.getName())) {
|
||||
String className = cls.getName();
|
||||
int dotIndex = className.lastIndexOf('.') + 1;
|
||||
String packageName = className.substring(0, dotIndex);
|
||||
className = className.substring(dotIndex);
|
||||
writer.append("\"").append(RenderingUtil.escapeString(className)).append("\"").append(",").ws();
|
||||
writer.append(String.valueOf(packageIndexes.getOrDefault(packageName, -1)));
|
||||
} else {
|
||||
writer.append("0");
|
||||
}
|
||||
writer.append(",").ws();
|
||||
|
||||
if (cls.getParentName() != null) {
|
||||
writer.appendClass(cls.getParentName());
|
||||
} else {
|
||||
writer.append("0");
|
||||
}
|
||||
writer.append(',').ws();
|
||||
writer.append("[");
|
||||
for (int i = 0; i < cls.getInterfaces().size(); ++i) {
|
||||
String iface = cls.getInterfaces().get(i);
|
||||
if (i > 0) {
|
||||
writer.append(",").ws();
|
||||
}
|
||||
writer.appendClass(iface);
|
||||
}
|
||||
writer.append("],").ws();
|
||||
|
||||
writer.append(ElementModifier.pack(cls.getModifiers())).append(',').ws();
|
||||
writer.append(cls.getAccessLevel().ordinal()).append(',').ws();
|
||||
|
||||
MethodReader clinit = classSource.get(cls.getName()).getMethod(
|
||||
new MethodDescriptor("<clinit>", ValueType.VOID));
|
||||
if (clinit != null) {
|
||||
writer.append(naming.getNameForClassInit(cls.getName()));
|
||||
} else {
|
||||
writer.append('0');
|
||||
}
|
||||
writer.append(',').ws();
|
||||
|
||||
List<MethodReference> virtualMethods = new ArrayList<>();
|
||||
for (MethodNode method : cls.getMethods()) {
|
||||
if (!method.getModifiers().contains(ElementModifier.STATIC)) {
|
||||
virtualMethods.add(method.getReference());
|
||||
}
|
||||
}
|
||||
collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods);
|
||||
|
||||
renderVirtualDeclarations(virtualMethods);
|
||||
}
|
||||
writer.append("]);").newLine();
|
||||
|
||||
for (int i = 0; i < classes.size(); i += 50) {
|
||||
int j = Math.min(i + 50, classes.size());
|
||||
renderClassMetadataPortion(classes.subList(i, j), packageIndexes, classesRequiringName);
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RenderingException("IO error occurred", e);
|
||||
}
|
||||
|
@ -522,6 +469,70 @@ public class Renderer implements RenderingManager {
|
|||
metadataSize = writer.getOffset() - start;
|
||||
}
|
||||
|
||||
private void renderClassMetadataPortion(List<ClassNode> classes, ObjectIntMap<String> packageIndexes,
|
||||
Set<String> classesRequiringName) throws IOException {
|
||||
writer.append("$rt_metadata([");
|
||||
boolean first = true;
|
||||
for (ClassNode cls : classes) {
|
||||
if (!first) {
|
||||
writer.append(',').softNewLine();
|
||||
}
|
||||
first = false;
|
||||
writer.appendClass(cls.getName()).append(",").ws();
|
||||
|
||||
if (classesRequiringName.contains(cls.getName())) {
|
||||
String className = cls.getName();
|
||||
int dotIndex = className.lastIndexOf('.') + 1;
|
||||
String packageName = className.substring(0, dotIndex);
|
||||
className = className.substring(dotIndex);
|
||||
writer.append("\"").append(RenderingUtil.escapeString(className)).append("\"").append(",").ws();
|
||||
writer.append(String.valueOf(packageIndexes.getOrDefault(packageName, -1)));
|
||||
} else {
|
||||
writer.append("0");
|
||||
}
|
||||
writer.append(",").ws();
|
||||
|
||||
if (cls.getParentName() != null) {
|
||||
writer.appendClass(cls.getParentName());
|
||||
} else {
|
||||
writer.append("0");
|
||||
}
|
||||
writer.append(',').ws();
|
||||
writer.append("[");
|
||||
for (int i = 0; i < cls.getInterfaces().size(); ++i) {
|
||||
String iface = cls.getInterfaces().get(i);
|
||||
if (i > 0) {
|
||||
writer.append(",").ws();
|
||||
}
|
||||
writer.appendClass(iface);
|
||||
}
|
||||
writer.append("],").ws();
|
||||
|
||||
writer.append(ElementModifier.pack(cls.getModifiers())).append(',').ws();
|
||||
writer.append(cls.getAccessLevel().ordinal()).append(',').ws();
|
||||
|
||||
MethodReader clinit = classSource.get(cls.getName()).getMethod(
|
||||
new MethodDescriptor("<clinit>", ValueType.VOID));
|
||||
if (clinit != null) {
|
||||
writer.append(naming.getNameForClassInit(cls.getName()));
|
||||
} else {
|
||||
writer.append('0');
|
||||
}
|
||||
writer.append(',').ws();
|
||||
|
||||
List<MethodReference> virtualMethods = new ArrayList<>();
|
||||
for (MethodNode method : cls.getMethods()) {
|
||||
if (!method.getModifiers().contains(ElementModifier.STATIC)) {
|
||||
virtualMethods.add(method.getReference());
|
||||
}
|
||||
}
|
||||
collectMethodsToCopyFromInterfaces(classSource.get(cls.getName()), virtualMethods);
|
||||
|
||||
renderVirtualDeclarations(virtualMethods);
|
||||
}
|
||||
writer.append("]);").newLine();
|
||||
}
|
||||
|
||||
private ObjectIntMap<String> generatePackageMetadata(List<ClassNode> classes, Set<String> classesRequiringName)
|
||||
throws IOException {
|
||||
PackageNode root = new PackageNode(null);
|
||||
|
@ -541,7 +552,6 @@ public class Renderer implements RenderingManager {
|
|||
}
|
||||
|
||||
ObjectIntMap<String> indexes = new ObjectIntHashMap<>();
|
||||
writer.append(String.valueOf(root.count())).append(",").ws();
|
||||
writePackageStructure(root, -1, "", indexes);
|
||||
writer.softNewLine();
|
||||
return indexes;
|
||||
|
@ -551,8 +561,11 @@ public class Renderer implements RenderingManager {
|
|||
throws IOException {
|
||||
int index = startIndex;
|
||||
for (PackageNode child : node.children.values()) {
|
||||
if (index >= 0) {
|
||||
writer.append(",").ws();
|
||||
}
|
||||
writer.append(String.valueOf(startIndex)).append(",").ws()
|
||||
.append("\"").append(RenderingUtil.escapeString(child.name)).append("\",").ws();
|
||||
.append("\"").append(RenderingUtil.escapeString(child.name)).append("\"");
|
||||
String fullName = prefix + child.name + ".";
|
||||
++index;
|
||||
indexes.put(fullName, index);
|
||||
|
|
|
@ -419,16 +419,20 @@ var $rt_putStderr = typeof $rt_putStderrCustom === "function" ? $rt_putStderrCus
|
|||
$rt_stderrBuffer += String.fromCharCode(ch);
|
||||
}
|
||||
};
|
||||
function $rt_metadata(data) {
|
||||
var $rt_packageData = null;
|
||||
function $rt_packages(data) {
|
||||
var i = 0;
|
||||
var packageCount = data[i++];
|
||||
var packages = new Array(packageCount);
|
||||
for (var j = 0; j < packageCount; ++j) {
|
||||
var packages = new Array(data.length);
|
||||
for (var j = 0; j < data.length; ++j) {
|
||||
var prefixIndex = data[i++];
|
||||
var prefix = prefixIndex >= 0 ? packages[prefixIndex] : "";
|
||||
packages[j] = prefix + data[i++] + ".";
|
||||
}
|
||||
|
||||
$rt_packageData = packages;
|
||||
}
|
||||
function $rt_metadata(data) {
|
||||
var packages = $rt_packageData;
|
||||
var i = 0;
|
||||
while (i < data.length) {
|
||||
var cls = data[i++];
|
||||
cls.$meta = {};
|
||||
|
@ -468,7 +472,7 @@ function $rt_metadata(data) {
|
|||
|
||||
var virtualMethods = data[i++];
|
||||
if (virtualMethods !== 0) {
|
||||
for (j = 0; j < virtualMethods.length; j += 2) {
|
||||
for (var j = 0; j < virtualMethods.length; j += 2) {
|
||||
var name = virtualMethods[j];
|
||||
var func = virtualMethods[j + 1];
|
||||
if (typeof name === 'string') {
|
||||
|
|
|
@ -23,7 +23,7 @@ class BuildTimeResourceGetter implements BuildTimeResourceMethod {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(BuildTimeResourceProxy proxy, Object[] args) throws Throwable {
|
||||
public Object invoke(BuildTimeResourceProxy proxy, Object[] args) {
|
||||
return proxy.data[index];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ class BuildTimeResourceProxy implements InvocationHandler {
|
|||
private Map<Method, BuildTimeResourceMethod> methods;
|
||||
Object[] data;
|
||||
|
||||
public BuildTimeResourceProxy(Map<Method, BuildTimeResourceMethod> methods, Object[] initialData) {
|
||||
BuildTimeResourceProxy(Map<Method, BuildTimeResourceMethod> methods, Object[] initialData) {
|
||||
this.methods = methods;
|
||||
data = Arrays.copyOf(initialData, initialData.length);
|
||||
}
|
||||
|
|
|
@ -51,7 +51,7 @@ class BuildTimeResourceProxyBuilder {
|
|||
private Object[] initialData;
|
||||
private ResourceTypeDescriptor descriptor;
|
||||
|
||||
public ProxyFactoryCreation(ResourceTypeDescriptor descriptor) {
|
||||
ProxyFactoryCreation(ResourceTypeDescriptor descriptor) {
|
||||
this.descriptor = descriptor;
|
||||
int index = 0;
|
||||
for (String propertyName : descriptor.getPropertyTypes().keySet()) {
|
||||
|
|
|
@ -23,7 +23,7 @@ class BuildTimeResourceProxyFactory {
|
|||
private Object[] initialData;
|
||||
ResourceTypeDescriptor typeDescriptor;
|
||||
|
||||
public BuildTimeResourceProxyFactory(Map<Method, BuildTimeResourceMethod> methods, Object[] initialData,
|
||||
BuildTimeResourceProxyFactory(Map<Method, BuildTimeResourceMethod> methods, Object[] initialData,
|
||||
ResourceTypeDescriptor typeDescriptor) {
|
||||
this.methods = methods;
|
||||
this.initialData = initialData;
|
||||
|
|
|
@ -31,7 +31,7 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext {
|
|||
private ClassReaderSource classSource;
|
||||
private ClassLoader classLoader;
|
||||
private Properties properties;
|
||||
private BuildTimeResourceProxyBuilder proxyBuilder = new BuildTimeResourceProxyBuilder();
|
||||
private BuildTimeResourceProxyBuilder proxyBuilder;
|
||||
private ServiceRepository services;
|
||||
|
||||
DefaultMetadataGeneratorContext(ClassReaderSource classSource, ClassLoader classLoader,
|
||||
|
@ -42,6 +42,13 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext {
|
|||
this.services = services;
|
||||
}
|
||||
|
||||
private BuildTimeResourceProxyBuilder getProxyBuilder() {
|
||||
if (proxyBuilder == null) {
|
||||
proxyBuilder = new BuildTimeResourceProxyBuilder();
|
||||
}
|
||||
return proxyBuilder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassReaderSource getClassSource() {
|
||||
return classSource;
|
||||
|
@ -61,12 +68,12 @@ class DefaultMetadataGeneratorContext implements MetadataGeneratorContext {
|
|||
public <T extends Resource> T createResource(Class<T> resourceType) {
|
||||
return resourceType.cast(Proxy.newProxyInstance(classLoader,
|
||||
new Class<?>[] { resourceType, ResourceWriter.class, ResourceTypeDescriptorProvider.class },
|
||||
proxyBuilder.buildProxy(resourceType)));
|
||||
getProxyBuilder().buildProxy(resourceType)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceTypeDescriptor getTypeDescriptor(Class<? extends Resource> type) {
|
||||
return proxyBuilder.getProxyFactory(type).typeDescriptor;
|
||||
return getProxyBuilder().getProxyFactory(type).typeDescriptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue
Block a user