mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Adds java.util.ServiceLoader support
This commit is contained in:
parent
3195879467
commit
d34e26e970
|
@ -17,6 +17,9 @@ package org.teavm.classlib.impl;
|
|||
|
||||
import org.teavm.javascript.JavascriptBuilderHost;
|
||||
import org.teavm.javascript.JavascriptBuilderPlugin;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -28,5 +31,11 @@ public class JCLPlugin implements JavascriptBuilderPlugin {
|
|||
host.add(new EnumDependencySupport());
|
||||
host.add(new EnumTransformer());
|
||||
host.add(new NewInstanceDependencySupport());
|
||||
ServiceLoaderSupport serviceLoaderSupp = new ServiceLoaderSupport(host.getClassLoader());
|
||||
host.add(serviceLoaderSupp);
|
||||
MethodReference loadServicesMethod = new MethodReference("java.util.ServiceLoader", new MethodDescriptor(
|
||||
"loadServices", ValueType.object("java.lang.Class"),
|
||||
ValueType.arrayOf(ValueType.object("java.lang.Object"))));
|
||||
host.add(loadServicesMethod, serviceLoaderSupp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,144 @@
|
|||
/*
|
||||
* Copyright 2014 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 java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import org.teavm.codegen.SourceWriter;
|
||||
import org.teavm.dependency.*;
|
||||
import org.teavm.javascript.ni.Generator;
|
||||
import org.teavm.javascript.ni.GeneratorContext;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodReference;
|
||||
import org.teavm.model.ValueType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class ServiceLoaderSupport implements Generator, DependencyListener {
|
||||
private Map<String, List<String>> serviceMap = new HashMap<>();
|
||||
private DependencyNode allClassesNode;
|
||||
private ClassLoader classLoader;
|
||||
private DependencyStack stack;
|
||||
|
||||
public ServiceLoaderSupport(ClassLoader classLoader) {
|
||||
this.classLoader = classLoader;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||
writer.append("if (!").appendClass("java.util.ServiceLoader").append(".$$services$$) {").indent()
|
||||
.softNewLine();
|
||||
writer.appendClass("java.util.ServiceLoader").append("$$services$$ = true;").softNewLine();
|
||||
for (Map.Entry<String, List<String>> entry : serviceMap.entrySet()) {
|
||||
writer.appendClass(entry.getKey()).append(".$$serviceList$$ = [");
|
||||
List<String> implementations = entry.getValue();
|
||||
for (int i = 0; i < implementations.size(); ++i) {
|
||||
if (i > 0) {
|
||||
writer.append(", ");
|
||||
}
|
||||
String implName = implementations.get(i);
|
||||
writer.append("[").appendClass(implName).append(", ").appendMethodBody(
|
||||
new MethodReference(implName, new MethodDescriptor("<init>", ValueType.VOID)))
|
||||
.append("]");
|
||||
}
|
||||
writer.append("];").softNewLine();
|
||||
}
|
||||
writer.outdent().append("}").softNewLine();
|
||||
String param = context.getParameterName(1);
|
||||
writer.append("var cls = " + param + ".$data;").softNewLine();
|
||||
writer.append("if (!cls.$$serviceList$$) {").indent().softNewLine();
|
||||
writer.append("return $rt_createArray($rt_objcls(), 0);").softNewLine();
|
||||
writer.outdent().append("}").softNewLine();
|
||||
writer.append("var result = $rt_createArray($rt_objcls(), cls.$$serviceList$$.length);").softNewLine();
|
||||
writer.append("for (var i = 0; i < result.data.length; ++i) {").indent().softNewLine();
|
||||
writer.append("var serviceDesc = cls.$$serviceList$$[i];").softNewLine();
|
||||
writer.append("result.data[i] = new serviceDesc[0]();").softNewLine();
|
||||
writer.append("serviceDesc[1](result.data[i]);").softNewLine();
|
||||
writer.outdent().append("}").softNewLine();
|
||||
writer.append("return result;").softNewLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void started(DependencyChecker dependencyChecker) {
|
||||
allClassesNode = dependencyChecker.createNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void classAchieved(DependencyChecker dependencyChecker, String className) {
|
||||
try {
|
||||
Enumeration<URL> resources = classLoader.getResources("META-INF/services/" + className);
|
||||
while (resources.hasMoreElements()) {
|
||||
URL resource = resources.nextElement();
|
||||
try (InputStream stream = resource.openStream()) {
|
||||
parseServiceFile(className, stream);
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void parseServiceFile(String service, InputStream input) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(input, "UTF-8"));
|
||||
while (true) {
|
||||
String line = reader.readLine();
|
||||
if (line == null) {
|
||||
break;
|
||||
}
|
||||
line = line.trim();
|
||||
if (line.startsWith("#") || line.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
List<String> implementors = serviceMap.get(service);
|
||||
if (implementors == null) {
|
||||
implementors = new ArrayList<>();
|
||||
serviceMap.put(service, implementors);
|
||||
}
|
||||
implementors.add(line);
|
||||
allClassesNode.propagate(line);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void methodAchieved(final DependencyChecker dependencyChecker, MethodDependency method) {
|
||||
MethodReference ref = method.getReference();
|
||||
if (ref.getClassName().equals("java.util.ServiceLoader") && ref.getName().equals("loadServices")) {
|
||||
method.getResult().propagate("[java.lang.Object");
|
||||
stack = method.getStack();
|
||||
allClassesNode.connect(method.getResult().getArrayItem());
|
||||
method.getResult().getArrayItem().addConsumer(new DependencyConsumer() {
|
||||
@Override public void consume(String type) {
|
||||
initConstructor(dependencyChecker, type);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void initConstructor(DependencyChecker dependencyChecker, String type) {
|
||||
MethodReference ctor = new MethodReference(type, new MethodDescriptor("<init>", ValueType.VOID));
|
||||
dependencyChecker.linkMethod(ctor, stack).use();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fieldAchieved(DependencyChecker dependencyChecker, FieldDependency field) {
|
||||
}
|
||||
}
|
|
@ -23,7 +23,7 @@ import org.teavm.javascript.ni.InjectedBy;
|
|||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class TClass<T extends TObject> extends TObject {
|
||||
public class TClass<T> extends TObject {
|
||||
TString name;
|
||||
boolean primitive;
|
||||
boolean array;
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2014 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.util;
|
||||
|
||||
import org.teavm.classlib.java.lang.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public final class TServiceLoader<S> extends TObject implements TIterable<S> {
|
||||
private Object[] services;
|
||||
|
||||
private TServiceLoader(Object[] services) {
|
||||
this.services = services;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TIterator<S> iterator() {
|
||||
return new TIterator<S>() {
|
||||
private int index;
|
||||
@Override public boolean hasNext() {
|
||||
return index < services.length;
|
||||
}
|
||||
@SuppressWarnings("unchecked") @Override public S next() {
|
||||
if (index == services.length) {
|
||||
throw new TNoSuchElementException();
|
||||
}
|
||||
return (S)services[index++];
|
||||
}
|
||||
@Override public void remove() {
|
||||
throw new TUnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static <S> TServiceLoader<S> load(TClass<S> service) {
|
||||
return new TServiceLoader<>(loadServices(service));
|
||||
}
|
||||
|
||||
public static <S> TServiceLoader<S> load(TClass<S> service, @SuppressWarnings("unused") TClassLoader loader) {
|
||||
return load(service);
|
||||
}
|
||||
|
||||
public static <S> TServiceLoader<S> loadInstalled(TClass<S> service) {
|
||||
return load(service);
|
||||
}
|
||||
|
||||
private static native <T> T[] loadServices(TClass<T> serviceType);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2014 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.util;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import java.util.ServiceLoader;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class ServiceLoaderTest {
|
||||
@Test
|
||||
public void loadsService() {
|
||||
TestService instance = ServiceLoader.load(TestService.class).iterator().next();
|
||||
instance.foo();
|
||||
assertEquals(TestServiceImpl.class, instance.getClass());
|
||||
assertEquals(1, ((TestServiceImpl)instance).getCounter());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2014 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.util;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public interface TestService {
|
||||
void foo();
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2014 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.util;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Alexey Andreev <konsoletyper@gmail.com>
|
||||
*/
|
||||
public class TestServiceImpl implements TestService {
|
||||
private int counter;
|
||||
|
||||
@Override
|
||||
public void foo() {
|
||||
++counter;
|
||||
}
|
||||
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.teavm.classlib.java.lang.util.TestServiceImpl
|
|
@ -43,6 +43,7 @@ public class Decompiler {
|
|||
private RangeTree.Node currentNode;
|
||||
private RangeTree.Node parentNode;
|
||||
private FiniteExecutor executor;
|
||||
private Map<MethodReference, Generator> generators = new HashMap<>();
|
||||
|
||||
public Decompiler(ClassHolderSource classSource, ClassLoader classLoader, FiniteExecutor executor) {
|
||||
this.classSource = classSource;
|
||||
|
@ -82,6 +83,7 @@ public class Decompiler {
|
|||
executor.execute(new Runnable() {
|
||||
@Override public void run() {
|
||||
Decompiler copy = new Decompiler(classSource, classLoader, executor);
|
||||
copy.generators = generators;
|
||||
result.set(index, copy.decompile(classSource.get(className)));
|
||||
}
|
||||
});
|
||||
|
@ -90,6 +92,10 @@ public class Decompiler {
|
|||
return result;
|
||||
}
|
||||
|
||||
public void addGenerator(MethodReference method, Generator generator) {
|
||||
generators.put(method, generator);
|
||||
}
|
||||
|
||||
private void orderClasses(String className, Set<String> visited, List<String> order) {
|
||||
if (!visited.add(className)) {
|
||||
return;
|
||||
|
@ -139,20 +145,22 @@ public class Decompiler {
|
|||
}
|
||||
|
||||
public NativeMethodNode decompileNative(MethodHolder method) {
|
||||
AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName());
|
||||
if (annotHolder == null) {
|
||||
throw new DecompilationException("Method " + method.getOwnerName() + "." + method.getDescriptor() +
|
||||
" is native, but no " + GeneratedBy.class.getName() + " annotation found");
|
||||
}
|
||||
ValueType annotValue = annotHolder.getValues().get("value").getJavaClass();
|
||||
String generatorClassName = ((ValueType.Object)annotValue).getClassName();
|
||||
Generator generator;
|
||||
try {
|
||||
Class<?> generatorClass = Class.forName(generatorClassName, true, classLoader);
|
||||
generator = (Generator)generatorClass.newInstance();
|
||||
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
throw new DecompilationException("Error instantiating generator " + generatorClassName +
|
||||
" for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
||||
Generator generator = generators.get(method.getReference());
|
||||
if (generator == null) {
|
||||
AnnotationHolder annotHolder = method.getAnnotations().get(GeneratedBy.class.getName());
|
||||
if (annotHolder == null) {
|
||||
throw new DecompilationException("Method " + method.getOwnerName() + "." + method.getDescriptor() +
|
||||
" is native, but no " + GeneratedBy.class.getName() + " annotation found");
|
||||
}
|
||||
ValueType annotValue = annotHolder.getValues().get("value").getJavaClass();
|
||||
String generatorClassName = ((ValueType.Object)annotValue).getClassName();
|
||||
try {
|
||||
Class<?> generatorClass = Class.forName(generatorClassName, true, classLoader);
|
||||
generator = (Generator)generatorClass.newInstance();
|
||||
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
|
||||
throw new DecompilationException("Error instantiating generator " + generatorClassName +
|
||||
" for native method " + method.getOwnerName() + "." + method.getDescriptor());
|
||||
}
|
||||
}
|
||||
NativeMethodNode methodNode = new NativeMethodNode(new MethodReference(method.getOwnerName(),
|
||||
method.getDescriptor()));
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.teavm.codegen.*;
|
|||
import org.teavm.common.FiniteExecutor;
|
||||
import org.teavm.dependency.*;
|
||||
import org.teavm.javascript.ast.ClassNode;
|
||||
import org.teavm.javascript.ni.Generator;
|
||||
import org.teavm.model.*;
|
||||
import org.teavm.model.util.ListingBuilder;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
|
@ -43,6 +44,7 @@ public class JavascriptBuilder implements JavascriptBuilderHost {
|
|||
private Map<String, JavascriptEntryPoint> entryPoints = new HashMap<>();
|
||||
private Map<String, String> exportedClasses = new HashMap<>();
|
||||
private List<JavascriptResourceRenderer> ressourceRenderers = new ArrayList<>();
|
||||
private Map<MethodReference, Generator> methodGenerators = new HashMap<>();
|
||||
|
||||
JavascriptBuilder(ClassHolderSource classSource, ClassLoader classLoader, FiniteExecutor executor) {
|
||||
this.classSource = new JavascriptProcessedClassSource(classSource);
|
||||
|
@ -66,6 +68,16 @@ public class JavascriptBuilder implements JavascriptBuilderHost {
|
|||
ressourceRenderers.add(resourceRenderer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void add(MethodReference methodRef, Generator generator) {
|
||||
methodGenerators.put(methodRef, generator);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClassLoader getClassLoader() {
|
||||
return classLoader;
|
||||
}
|
||||
|
||||
public boolean isMinifying() {
|
||||
return minifying;
|
||||
}
|
||||
|
@ -160,6 +172,9 @@ public class JavascriptBuilder implements JavascriptBuilderHost {
|
|||
}
|
||||
Renderer renderer = new Renderer(sourceWriter, classSet, classLoader);
|
||||
renderer.renderRuntime();
|
||||
for (Map.Entry<MethodReference, Generator> entry : methodGenerators.entrySet()) {
|
||||
decompiler.addGenerator(entry.getKey(), entry.getValue());
|
||||
}
|
||||
List<ClassNode> clsNodes = decompiler.decompile(classSet.getClassNames());
|
||||
for (ClassNode clsNode : clsNodes) {
|
||||
renderer.render(clsNode);
|
||||
|
|
|
@ -16,7 +16,9 @@
|
|||
package org.teavm.javascript;
|
||||
|
||||
import org.teavm.dependency.DependencyListener;
|
||||
import org.teavm.javascript.ni.Generator;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
/**
|
||||
*
|
||||
|
@ -28,4 +30,8 @@ public interface JavascriptBuilderHost {
|
|||
void add(ClassHolderTransformer classTransformer);
|
||||
|
||||
void add(JavascriptResourceRenderer resourceRenderer);
|
||||
|
||||
void add(MethodReference methodRef, Generator generator);
|
||||
|
||||
ClassLoader getClassLoader();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user