mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
C: implement ServiceLoader
This commit is contained in:
parent
4a62b58f82
commit
73bd139b7e
|
@ -67,15 +67,22 @@ public class JCLPlugin implements TeaVMPlugin {
|
||||||
host.add(new ObfuscationHacks());
|
host.add(new ObfuscationHacks());
|
||||||
|
|
||||||
if (!isBootstrap()) {
|
if (!isBootstrap()) {
|
||||||
ServiceLoaderSupport serviceLoaderSupp = new ServiceLoaderSupport(host.getClassLoader());
|
ServiceLoaderSupport serviceLoaderSupport = new ServiceLoaderSupport(host.getClassLoader());
|
||||||
host.add(serviceLoaderSupp);
|
host.add(serviceLoaderSupport);
|
||||||
|
host.registerService(ServiceLoaderInformation.class, serviceLoaderSupport);
|
||||||
MethodReference loadServicesMethod = new MethodReference(ServiceLoader.class, "loadServices",
|
MethodReference loadServicesMethod = new MethodReference(ServiceLoader.class, "loadServices",
|
||||||
PlatformClass.class, Object[].class);
|
PlatformClass.class, Object[].class);
|
||||||
|
|
||||||
TeaVMJavaScriptHost jsExtension = host.getExtension(TeaVMJavaScriptHost.class);
|
TeaVMJavaScriptHost jsExtension = host.getExtension(TeaVMJavaScriptHost.class);
|
||||||
if (jsExtension != null) {
|
if (jsExtension != null) {
|
||||||
jsExtension.add(loadServicesMethod, serviceLoaderSupp);
|
jsExtension.add(loadServicesMethod, new ServiceLoaderJSSupport());
|
||||||
jsExtension.addVirtualMethods(new AnnotationVirtualMethods());
|
jsExtension.addVirtualMethods(new AnnotationVirtualMethods());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TeaVMCHost cHost = host.getExtension(TeaVMCHost.class);
|
||||||
|
if (cHost != null) {
|
||||||
|
cHost.addGenerator(new ServiceLoaderCSupport());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isBootstrap()) {
|
if (!isBootstrap()) {
|
||||||
|
|
|
@ -0,0 +1,150 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.util.Collection;
|
||||||
|
import java.util.ServiceLoader;
|
||||||
|
import org.teavm.backend.c.generate.CodeWriter;
|
||||||
|
import org.teavm.backend.c.generators.Generator;
|
||||||
|
import org.teavm.backend.c.generators.GeneratorContext;
|
||||||
|
import org.teavm.backend.c.generators.GeneratorFactory;
|
||||||
|
import org.teavm.backend.c.generators.GeneratorFactoryContext;
|
||||||
|
import org.teavm.backend.lowlevel.generate.NameProvider;
|
||||||
|
import org.teavm.interop.Address;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||||
|
import org.teavm.model.lowlevel.CallSiteLocation;
|
||||||
|
import org.teavm.model.lowlevel.ExceptionHandlerDescriptor;
|
||||||
|
import org.teavm.runtime.Allocator;
|
||||||
|
import org.teavm.runtime.RuntimeClass;
|
||||||
|
|
||||||
|
public class ServiceLoaderCSupport implements GeneratorFactory {
|
||||||
|
static final MethodReference ALLOC_ARRAY_METHOD = new MethodReference(Allocator.class,
|
||||||
|
"allocateArray", RuntimeClass.class, int.class, Address.class);
|
||||||
|
static final MethodReference ALLOC_METHOD = new MethodReference(Allocator.class,
|
||||||
|
"allocate", RuntimeClass.class, Address.class);
|
||||||
|
static final MethodDescriptor INIT_METHOD = new MethodDescriptor("<init>", ValueType.VOID);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Generator createGenerator(GeneratorFactoryContext context) {
|
||||||
|
return new ServiceLoaderIntrinsic(context.getServices().getService(ServiceLoaderInformation.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
static class ServiceLoaderIntrinsic implements Generator {
|
||||||
|
private ServiceLoaderInformation information;
|
||||||
|
|
||||||
|
ServiceLoaderIntrinsic(ServiceLoaderInformation information) {
|
||||||
|
this.information = information;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canHandle(MethodReference method) {
|
||||||
|
if (!method.getClassName().equals(ServiceLoader.class.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return method.getName().equals("loadServices");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generate(GeneratorContext context, MethodReference method) {
|
||||||
|
CodeWriter writer = context.writer();
|
||||||
|
CodeWriter beforeWriter = context.writerBefore();
|
||||||
|
NameProvider names = context.names();
|
||||||
|
context.includes().addInclude("<stdbool.h>");
|
||||||
|
Collection<? extends String> serviceTypes = information.serviceTypes();
|
||||||
|
|
||||||
|
writer.println("static bool initialized = false;");
|
||||||
|
writer.println("if (!initialized) {").indent();
|
||||||
|
String methodName = names.forMethod(method);
|
||||||
|
int index = 0;
|
||||||
|
for (String serviceType : serviceTypes) {
|
||||||
|
Collection<? extends String> implementations = information.serviceImplementations(serviceType);
|
||||||
|
if (implementations.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
String staticFieldName = methodName + "_" + index++;
|
||||||
|
context.includes().includeClass(serviceType);
|
||||||
|
writer.print(names.forClassInstance(ValueType.object(serviceType)))
|
||||||
|
.print(".services = (TeaVM_Services*) &").print(staticFieldName).println(";");
|
||||||
|
|
||||||
|
beforeWriter.print("static struct { int32_t size; ")
|
||||||
|
.print("TeaVM_Service entries[" + implementations.size() + "]; } ")
|
||||||
|
.print(staticFieldName + " = { .size = " + implementations.size() + ", ")
|
||||||
|
.print(".entries = {").indent();
|
||||||
|
boolean first = true;
|
||||||
|
for (String implementation : implementations) {
|
||||||
|
if (!first) {
|
||||||
|
beforeWriter.print(",");
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
context.includes().includeClass(implementation);
|
||||||
|
MethodReference constructor = new MethodReference(implementation, INIT_METHOD);
|
||||||
|
context.importMethod(constructor, false);
|
||||||
|
beforeWriter.println().print("{ .cls = (TeaVM_Class*) &")
|
||||||
|
.print(names.forClassInstance(ValueType.object(implementation)))
|
||||||
|
.print(", .constructor = &").print(names.forMethod(constructor))
|
||||||
|
.print(" }");
|
||||||
|
}
|
||||||
|
if (!first) {
|
||||||
|
beforeWriter.println();
|
||||||
|
}
|
||||||
|
beforeWriter.outdent().println("}};");
|
||||||
|
}
|
||||||
|
writer.outdent().println("}");
|
||||||
|
|
||||||
|
CallSiteLocation location = new CallSiteLocation(null, method.getClassName(), method.getName(), -1);
|
||||||
|
CallSiteDescriptor callSite = context.createCallSite(new CallSiteLocation[] { location },
|
||||||
|
new ExceptionHandlerDescriptor[0]);
|
||||||
|
writer.println("TEAVM_ALLOC_STACK(INT32_C(2));");
|
||||||
|
writer.println("TEAVM_CALL_SITE(" + callSite.getId() + ");");
|
||||||
|
|
||||||
|
writer.println("TeaVM_Array* result = NULL;");
|
||||||
|
writer.print("TeaVM_Services* services = ((TeaVM_Class*) ").print(context.parameterName(1))
|
||||||
|
.println(")->services;");
|
||||||
|
writer.println("if (services == NULL) goto exit;");
|
||||||
|
writer.println("TEAVM_GC_ROOT_RELEASE(0);");
|
||||||
|
writer.println("TEAVM_GC_ROOT_RELEASE(1);");
|
||||||
|
writer.print("result = ")
|
||||||
|
.print(names.forMethod(ALLOC_ARRAY_METHOD)).print("(&")
|
||||||
|
.print(names.forClassInstance(ValueType.parse(Object[].class))).print(", ")
|
||||||
|
.println("services->size);");
|
||||||
|
if (!context.usesLongjmp()) {
|
||||||
|
writer.println("if (TEAVM_EXCEPTION_HANDLER != " + callSite.getId() + ") goto exit;");
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.println("TEAVM_GC_ROOT(0, result);");
|
||||||
|
writer.println("void** arrayData = (void**) TEAVM_ARRAY_DATA(result, void*);");
|
||||||
|
writer.println("for (int32_t i = 0; i < services->size; ++i) {").indent();
|
||||||
|
writer.print("void* obj = ").print(names.forMethod(ALLOC_METHOD)).println("(services->entries[i].cls);");
|
||||||
|
if (!context.usesLongjmp()) {
|
||||||
|
writer.println("if (TEAVM_EXCEPTION_HANDLER != " + callSite.getId() + ") goto exit;");
|
||||||
|
}
|
||||||
|
writer.println("TEAVM_GC_ROOT(1, obj);");
|
||||||
|
writer.println("services->entries[i].constructor(obj);");
|
||||||
|
if (!context.usesLongjmp()) {
|
||||||
|
writer.println("if (TEAVM_EXCEPTION_HANDLER != " + callSite.getId() + ") goto exit;");
|
||||||
|
}
|
||||||
|
writer.println("arrayData[i] = obj;");
|
||||||
|
writer.outdent().println("}");
|
||||||
|
|
||||||
|
writer.println("exit: TEAVM_RELEASE_STACK;");
|
||||||
|
writer.println("return result;");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.util.Collection;
|
||||||
|
|
||||||
|
public interface ServiceLoaderInformation {
|
||||||
|
Collection<? extends String> serviceTypes();
|
||||||
|
|
||||||
|
Collection<? extends String> serviceImplementations(String type);
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.IOException;
|
||||||
|
import java.util.Collection;
|
||||||
|
import org.teavm.backend.javascript.codegen.SourceWriter;
|
||||||
|
import org.teavm.backend.javascript.spi.Generator;
|
||||||
|
import org.teavm.backend.javascript.spi.GeneratorContext;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public class ServiceLoaderJSSupport implements Generator {
|
||||||
|
private static final MethodDescriptor INIT_METHOD = new MethodDescriptor("<init>", void.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
||||||
|
ServiceLoaderInformation information = context.getService(ServiceLoaderInformation.class);
|
||||||
|
writer.append("if (!").appendClass("java.util.ServiceLoader").append(".$$services$$) {").indent()
|
||||||
|
.softNewLine();
|
||||||
|
writer.appendClass("java.util.ServiceLoader").append(".$$services$$ = true;").softNewLine();
|
||||||
|
for (String serviceType : information.serviceTypes()) {
|
||||||
|
writer.appendClass(serviceType).append(".$$serviceList$$ = [");
|
||||||
|
Collection<? extends String> implementations = information.serviceImplementations(serviceType);
|
||||||
|
boolean first = true;
|
||||||
|
for (String implName : implementations) {
|
||||||
|
if (context.getClassSource().getClassNames().contains(implName)) {
|
||||||
|
if (!first) {
|
||||||
|
writer.append(", ");
|
||||||
|
}
|
||||||
|
first = false;
|
||||||
|
writer.append("[").appendClass(implName).append(", ").appendMethodBody(
|
||||||
|
new MethodReference(implName, INIT_METHOD))
|
||||||
|
.append("]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
writer.append("];").softNewLine();
|
||||||
|
}
|
||||||
|
writer.outdent().append("}").softNewLine();
|
||||||
|
String param = context.getParameterName(1);
|
||||||
|
writer.append("var cls = " + param + ";").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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,8 @@ import java.lang.reflect.InvocationTargetException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Enumeration;
|
import java.util.Enumeration;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
@ -30,9 +32,6 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import java.util.Set;
|
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.classlib.ServiceLoaderFilter;
|
||||||
import org.teavm.dependency.AbstractDependencyListener;
|
import org.teavm.dependency.AbstractDependencyListener;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
|
@ -42,7 +41,7 @@ import org.teavm.model.CallLocation;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class ServiceLoaderSupport extends AbstractDependencyListener implements Generator {
|
public class ServiceLoaderSupport extends AbstractDependencyListener implements ServiceLoaderInformation {
|
||||||
private static final MethodReference LOAD_METHOD = new MethodReference(ServiceLoader.class, "load", Class.class,
|
private static final MethodReference LOAD_METHOD = new MethodReference(ServiceLoader.class, "load", Class.class,
|
||||||
ServiceLoader.class);
|
ServiceLoader.class);
|
||||||
private static final MethodDescriptor INIT_METHOD = new MethodDescriptor("<init>", void.class);
|
private static final MethodDescriptor INIT_METHOD = new MethodDescriptor("<init>", void.class);
|
||||||
|
@ -54,40 +53,17 @@ public class ServiceLoaderSupport extends AbstractDependencyListener implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void generate(GeneratorContext context, SourceWriter writer, MethodReference methodRef) throws IOException {
|
public Collection<? extends String> serviceTypes() {
|
||||||
writer.append("if (!").appendClass("java.util.ServiceLoader").append(".$$services$$) {").indent()
|
return serviceMap.keySet();
|
||||||
.softNewLine();
|
}
|
||||||
writer.appendClass("java.util.ServiceLoader").append(".$$services$$ = true;").softNewLine();
|
|
||||||
for (Map.Entry<String, List<String>> entry : serviceMap.entrySet()) {
|
@Override
|
||||||
writer.appendClass(entry.getKey()).append(".$$serviceList$$ = [");
|
public Collection<? extends String> serviceImplementations(String type) {
|
||||||
List<String> implementations = entry.getValue();
|
Collection<? extends String> result = serviceMap.get(type);
|
||||||
boolean first = true;
|
if (result == null) {
|
||||||
for (String implName : implementations) {
|
result = Collections.emptyList();
|
||||||
if (context.getClassSource().getClassNames().contains(implName)) {
|
|
||||||
if (!first) {
|
|
||||||
writer.append(", ");
|
|
||||||
}
|
|
||||||
first = false;
|
|
||||||
writer.append("[").appendClass(implName).append(", ").appendMethodBody(
|
|
||||||
new MethodReference(implName, INIT_METHOD))
|
|
||||||
.append("]");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
writer.append("];").softNewLine();
|
|
||||||
}
|
}
|
||||||
writer.outdent().append("}").softNewLine();
|
return result;
|
||||||
String param = context.getParameterName(1);
|
|
||||||
writer.append("var cls = " + param + ";").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
|
@Override
|
||||||
|
|
|
@ -18,11 +18,6 @@ package org.teavm.classlib.java.util;
|
||||||
import org.teavm.classlib.java.lang.*;
|
import org.teavm.classlib.java.lang.*;
|
||||||
import org.teavm.platform.PlatformClass;
|
import org.teavm.platform.PlatformClass;
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @author Alexey Andreev
|
|
||||||
* @param <S>
|
|
||||||
*/
|
|
||||||
public final class TServiceLoader<S> extends TObject implements TIterable<S> {
|
public final class TServiceLoader<S> extends TObject implements TIterable<S> {
|
||||||
private Object[] services;
|
private Object[] services;
|
||||||
|
|
||||||
|
@ -50,7 +45,7 @@ public final class TServiceLoader<S> extends TObject implements TIterable<S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <S> TServiceLoader<S> load(TClass<S> service) {
|
public static <S> TServiceLoader<S> load(TClass<S> service) {
|
||||||
return new TServiceLoader<>(loadServices(service.getPlatformClass()));
|
return new TServiceLoader<>(doLoadServices(service.getPlatformClass()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static <S> TServiceLoader<S> load(TClass<S> service, @SuppressWarnings("unused") TClassLoader loader) {
|
public static <S> TServiceLoader<S> load(TClass<S> service, @SuppressWarnings("unused") TClassLoader loader) {
|
||||||
|
@ -61,7 +56,15 @@ public final class TServiceLoader<S> extends TObject implements TIterable<S> {
|
||||||
return load(service);
|
return load(service);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static native <T> T[] loadServices(PlatformClass cls);
|
private static Object[] doLoadServices(PlatformClass cls) {
|
||||||
|
Object[] result = loadServices(cls);
|
||||||
|
if (result == null) {
|
||||||
|
result = new Object[0];
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static native Object[] loadServices(PlatformClass cls);
|
||||||
|
|
||||||
public void reload() {
|
public void reload() {
|
||||||
// Do nothing, services are bound at build time
|
// Do nothing, services are bound at build time
|
||||||
|
|
|
@ -1272,7 +1272,7 @@ public class ClassGenerator {
|
||||||
codeWriter.outdent().println("}");
|
codeWriter.outdent().println("}");
|
||||||
|
|
||||||
GeneratorContextImpl generatorContext = new GeneratorContextImpl(codeGenerator.getClassContext(),
|
GeneratorContextImpl generatorContext = new GeneratorContextImpl(codeGenerator.getClassContext(),
|
||||||
bodyWriter, writerBefore, codeWriter, includes);
|
bodyWriter, writerBefore, codeWriter, includes, callSites, context.isLongjmp());
|
||||||
generator.generate(generatorContext, methodRef);
|
generator.generate(generatorContext, methodRef);
|
||||||
try {
|
try {
|
||||||
generatorContext.flush();
|
generatorContext.flush();
|
||||||
|
|
|
@ -17,6 +17,7 @@ package org.teavm.backend.c.generate;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.teavm.backend.c.generators.GeneratorContext;
|
import org.teavm.backend.c.generators.GeneratorContext;
|
||||||
import org.teavm.backend.lowlevel.generate.NameProvider;
|
import org.teavm.backend.lowlevel.generate.NameProvider;
|
||||||
|
@ -24,6 +25,9 @@ import org.teavm.dependency.DependencyInfo;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||||
|
import org.teavm.model.lowlevel.CallSiteLocation;
|
||||||
|
import org.teavm.model.lowlevel.ExceptionHandlerDescriptor;
|
||||||
|
|
||||||
class GeneratorContextImpl implements GeneratorContext {
|
class GeneratorContextImpl implements GeneratorContext {
|
||||||
private GenerationContext context;
|
private GenerationContext context;
|
||||||
|
@ -33,15 +37,20 @@ class GeneratorContextImpl implements GeneratorContext {
|
||||||
private CodeWriter writerAfter;
|
private CodeWriter writerAfter;
|
||||||
private IncludeManager includes;
|
private IncludeManager includes;
|
||||||
private List<FileGeneratorImpl> fileGenerators = new ArrayList<>();
|
private List<FileGeneratorImpl> fileGenerators = new ArrayList<>();
|
||||||
|
private List<CallSiteDescriptor> callSites;
|
||||||
|
private boolean longjmp;
|
||||||
|
|
||||||
public GeneratorContextImpl(ClassGenerationContext classContext, CodeWriter bodyWriter,
|
public GeneratorContextImpl(ClassGenerationContext classContext, CodeWriter bodyWriter,
|
||||||
CodeWriter writerBefore, CodeWriter writerAfter, IncludeManager includes) {
|
CodeWriter writerBefore, CodeWriter writerAfter, IncludeManager includes,
|
||||||
|
List<CallSiteDescriptor> callSites, boolean longjmp) {
|
||||||
this.context = classContext.getContext();
|
this.context = classContext.getContext();
|
||||||
this.classContext = classContext;
|
this.classContext = classContext;
|
||||||
this.bodyWriter = bodyWriter;
|
this.bodyWriter = bodyWriter;
|
||||||
this.writerBefore = writerBefore;
|
this.writerBefore = writerBefore;
|
||||||
this.writerAfter = writerAfter;
|
this.writerAfter = writerAfter;
|
||||||
this.includes = includes;
|
this.includes = includes;
|
||||||
|
this.callSites = callSites;
|
||||||
|
this.longjmp = longjmp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -126,6 +135,20 @@ class GeneratorContextImpl implements GeneratorContext {
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CallSiteDescriptor createCallSite(CallSiteLocation[] locations,
|
||||||
|
ExceptionHandlerDescriptor[] exceptionHandlers) {
|
||||||
|
CallSiteDescriptor callSite = new CallSiteDescriptor(callSites.size(), locations);
|
||||||
|
callSite.getHandlers().addAll(Arrays.asList(exceptionHandlers));
|
||||||
|
callSites.add(callSite);
|
||||||
|
return callSite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean usesLongjmp() {
|
||||||
|
return longjmp;
|
||||||
|
}
|
||||||
|
|
||||||
void flush() throws IOException {
|
void flush() throws IOException {
|
||||||
for (FileGeneratorImpl generator : fileGenerators) {
|
for (FileGeneratorImpl generator : fileGenerators) {
|
||||||
OutputFileUtil.write(generator.writer, generator.path, context.getBuildTarget());
|
OutputFileUtil.write(generator.writer, generator.path, context.getBuildTarget());
|
||||||
|
|
|
@ -24,6 +24,9 @@ import org.teavm.dependency.DependencyInfo;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||||
|
import org.teavm.model.lowlevel.CallSiteLocation;
|
||||||
|
import org.teavm.model.lowlevel.ExceptionHandlerDescriptor;
|
||||||
|
|
||||||
public interface GeneratorContext {
|
public interface GeneratorContext {
|
||||||
CodeWriter writer();
|
CodeWriter writer();
|
||||||
|
@ -53,4 +56,8 @@ public interface GeneratorContext {
|
||||||
String escapeFileName(String name);
|
String escapeFileName(String name);
|
||||||
|
|
||||||
void importMethod(MethodReference method, boolean isStatic);
|
void importMethod(MethodReference method, boolean isStatic);
|
||||||
|
|
||||||
|
boolean usesLongjmp();
|
||||||
|
|
||||||
|
CallSiteDescriptor createCallSite(CallSiteLocation[] locations, ExceptionHandlerDescriptor[] exceptionHandlers);
|
||||||
}
|
}
|
||||||
|
|
55
core/src/main/java/org/teavm/common/HashUtils.java
Normal file
55
core/src/main/java/org/teavm/common/HashUtils.java
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2021 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.common;
|
||||||
|
|
||||||
|
public final class HashUtils {
|
||||||
|
private HashUtils() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] createHashTable(String[] values) {
|
||||||
|
int tableSize = values.length * 2;
|
||||||
|
int maxTableSize = Math.min(values.length * 5 / 2, tableSize + 10);
|
||||||
|
|
||||||
|
String[] bestTable = null;
|
||||||
|
int bestCollisionRatio = 0;
|
||||||
|
while (tableSize <= maxTableSize) {
|
||||||
|
String[] table = new String[tableSize];
|
||||||
|
int maxCollisionRatio = 0;
|
||||||
|
for (String key : values) {
|
||||||
|
int hashCode = key.hashCode();
|
||||||
|
int collisionRatio = 0;
|
||||||
|
while (true) {
|
||||||
|
int index = Integer.remainderUnsigned(hashCode++, table.length);
|
||||||
|
if (table[index] == null) {
|
||||||
|
table[index] = key;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
collisionRatio++;
|
||||||
|
}
|
||||||
|
maxCollisionRatio = Math.max(maxCollisionRatio, collisionRatio);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bestTable == null || bestCollisionRatio > maxCollisionRatio) {
|
||||||
|
bestCollisionRatio = maxCollisionRatio;
|
||||||
|
bestTable = table;
|
||||||
|
}
|
||||||
|
|
||||||
|
tableSize++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestTable;
|
||||||
|
}
|
||||||
|
}
|
|
@ -122,7 +122,10 @@ public final class ExceptionHandling {
|
||||||
if (stackFrame == null) {
|
if (stackFrame == null) {
|
||||||
stackFrame = ShadowStack.getStackTop();
|
stackFrame = ShadowStack.getStackTop();
|
||||||
while (stackFrame != null) {
|
while (stackFrame != null) {
|
||||||
ShadowStack.setExceptionHandlerId(stackFrame, ShadowStack.getCallSiteId(stackFrame) + 1);
|
int callSiteId = ShadowStack.getCallSiteId(stackFrame);
|
||||||
|
if (callSiteId >= 0) {
|
||||||
|
ShadowStack.setExceptionHandlerId(stackFrame, callSiteId + 1);
|
||||||
|
}
|
||||||
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
|
stackFrame = ShadowStack.getNextStackFrame(stackFrame);
|
||||||
}
|
}
|
||||||
printStack();
|
printStack();
|
||||||
|
|
|
@ -70,6 +70,7 @@ void teavm_initClasses() {
|
||||||
int32_t classHeader = TEAVM_PACK_CLASS(teavm_classClass) | (int32_t) INT32_C(0x80000000);
|
int32_t classHeader = TEAVM_PACK_CLASS(teavm_classClass) | (int32_t) INT32_C(0x80000000);
|
||||||
for (int i = 0; i < teavm_classReferencesCount; ++i) {
|
for (int i = 0; i < teavm_classReferencesCount; ++i) {
|
||||||
teavm_classReferences[i]->parent.header = classHeader;
|
teavm_classReferences[i]->parent.header = classHeader;
|
||||||
|
teavm_classReferences[i]->services = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,8 @@ typedef struct TeaVM_Array {
|
||||||
int32_t size;
|
int32_t size;
|
||||||
} TeaVM_Array;
|
} TeaVM_Array;
|
||||||
|
|
||||||
|
struct TeaVM_Services;
|
||||||
|
|
||||||
typedef struct TeaVM_Class {
|
typedef struct TeaVM_Class {
|
||||||
TeaVM_Object parent;
|
TeaVM_Object parent;
|
||||||
int32_t size;
|
int32_t size;
|
||||||
|
@ -44,12 +46,23 @@ typedef struct TeaVM_Class {
|
||||||
TeaVM_Object** simpleName;
|
TeaVM_Object** simpleName;
|
||||||
TeaVM_Object* simpleNameCache;
|
TeaVM_Object* simpleNameCache;
|
||||||
TeaVM_Object* canonicalName;
|
TeaVM_Object* canonicalName;
|
||||||
|
struct TeaVM_Services* services;
|
||||||
#if TEAVM_HEAP_DUMP
|
#if TEAVM_HEAP_DUMP
|
||||||
TeaVM_FieldDescriptors* fieldDescriptors;
|
TeaVM_FieldDescriptors* fieldDescriptors;
|
||||||
TeaVM_StaticFieldDescriptors* staticFieldDescriptors;
|
TeaVM_StaticFieldDescriptors* staticFieldDescriptors;
|
||||||
#endif
|
#endif
|
||||||
} TeaVM_Class;
|
} TeaVM_Class;
|
||||||
|
|
||||||
|
typedef struct TeaVM_Service {
|
||||||
|
TeaVM_Class* cls;
|
||||||
|
void (*constructor)(void*);
|
||||||
|
} TeaVM_Service;
|
||||||
|
|
||||||
|
typedef struct TeaVM_Services {
|
||||||
|
int32_t size;
|
||||||
|
TeaVM_Service entries[1];
|
||||||
|
} TeaVM_Services;
|
||||||
|
|
||||||
typedef struct TeaVM_String {
|
typedef struct TeaVM_String {
|
||||||
TeaVM_Object parent;
|
TeaVM_Object parent;
|
||||||
TeaVM_Array* characters;
|
TeaVM_Array* characters;
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Set;
|
||||||
import org.teavm.backend.c.generate.FileGenerator;
|
import org.teavm.backend.c.generate.FileGenerator;
|
||||||
import org.teavm.backend.c.generators.Generator;
|
import org.teavm.backend.c.generators.Generator;
|
||||||
import org.teavm.backend.c.generators.GeneratorContext;
|
import org.teavm.backend.c.generators.GeneratorContext;
|
||||||
|
import org.teavm.common.HashUtils;
|
||||||
import org.teavm.common.ServiceRepository;
|
import org.teavm.common.ServiceRepository;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
@ -191,37 +192,7 @@ class MetadataCIntrinsic implements Generator {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeResourceMap(GeneratorContext context, ResourceMap<?> resourceMap) {
|
private void writeResourceMap(GeneratorContext context, ResourceMap<?> resourceMap) {
|
||||||
String[] keys = resourceMap.keys();
|
String[] bestTable = HashUtils.createHashTable(resourceMap.keys());
|
||||||
int tableSize = keys.length * 2;
|
|
||||||
int maxTableSize = Math.min(keys.length * 5 / 2, tableSize + 10);
|
|
||||||
|
|
||||||
String[] bestTable = null;
|
|
||||||
int bestCollisionRatio = 0;
|
|
||||||
while (tableSize <= maxTableSize) {
|
|
||||||
String[] table = new String[tableSize];
|
|
||||||
int maxCollisionRatio = 0;
|
|
||||||
for (String key : keys) {
|
|
||||||
int hashCode = key.hashCode();
|
|
||||||
int collisionRatio = 0;
|
|
||||||
while (true) {
|
|
||||||
int index = Integer.remainderUnsigned(hashCode++, table.length);
|
|
||||||
if (table[index] == null) {
|
|
||||||
table[index] = key;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
collisionRatio++;
|
|
||||||
}
|
|
||||||
maxCollisionRatio = Math.max(maxCollisionRatio, collisionRatio);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestTable == null || bestCollisionRatio > maxCollisionRatio) {
|
|
||||||
bestCollisionRatio = maxCollisionRatio;
|
|
||||||
bestTable = table;
|
|
||||||
}
|
|
||||||
|
|
||||||
tableSize++;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.includes().includePath("resource.h");
|
context.includes().includePath("resource.h");
|
||||||
context.writerBefore().println("&(struct { int32_t size; TeaVM_ResourceMapEntry entries["
|
context.writerBefore().println("&(struct { int32_t size; TeaVM_ResourceMapEntry entries["
|
||||||
+ bestTable.length + "]; }) {").indent();
|
+ bestTable.length + "]; }) {").indent();
|
||||||
|
|
Loading…
Reference in New Issue
Block a user