diff --git a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java index e3af5aa47..dc1941c25 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java @@ -67,15 +67,22 @@ public class JCLPlugin implements TeaVMPlugin { host.add(new ObfuscationHacks()); if (!isBootstrap()) { - ServiceLoaderSupport serviceLoaderSupp = new ServiceLoaderSupport(host.getClassLoader()); - host.add(serviceLoaderSupp); + ServiceLoaderSupport serviceLoaderSupport = new ServiceLoaderSupport(host.getClassLoader()); + host.add(serviceLoaderSupport); + host.registerService(ServiceLoaderInformation.class, serviceLoaderSupport); MethodReference loadServicesMethod = new MethodReference(ServiceLoader.class, "loadServices", PlatformClass.class, Object[].class); + TeaVMJavaScriptHost jsExtension = host.getExtension(TeaVMJavaScriptHost.class); if (jsExtension != null) { - jsExtension.add(loadServicesMethod, serviceLoaderSupp); + jsExtension.add(loadServicesMethod, new ServiceLoaderJSSupport()); jsExtension.addVirtualMethods(new AnnotationVirtualMethods()); } + + TeaVMCHost cHost = host.getExtension(TeaVMCHost.class); + if (cHost != null) { + cHost.addGenerator(new ServiceLoaderCSupport()); + } } if (!isBootstrap()) { diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderCSupport.java b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderCSupport.java new file mode 100644 index 000000000..eba531420 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderCSupport.java @@ -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("", 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(""); + Collection 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 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;"); + } + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderInformation.java b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderInformation.java new file mode 100644 index 000000000..e8ec2a7da --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderInformation.java @@ -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 serviceTypes(); + + Collection serviceImplementations(String type); +} diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderJSSupport.java b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderJSSupport.java new file mode 100644 index 000000000..f4f9a5c97 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderJSSupport.java @@ -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("", 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 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(); + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java index 8d740005c..9c1d9d7ff 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderSupport.java @@ -23,6 +23,8 @@ import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedHashSet; @@ -30,9 +32,6 @@ 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; @@ -42,7 +41,7 @@ import org.teavm.model.CallLocation; import org.teavm.model.MethodDescriptor; 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, ServiceLoader.class); private static final MethodDescriptor INIT_METHOD = new MethodDescriptor("", void.class); @@ -54,40 +53,17 @@ public class ServiceLoaderSupport extends AbstractDependencyListener implements } @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> entry : serviceMap.entrySet()) { - writer.appendClass(entry.getKey()).append(".$$serviceList$$ = ["); - List implementations = entry.getValue(); - 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(); + public Collection serviceTypes() { + return serviceMap.keySet(); + } + + @Override + public Collection serviceImplementations(String type) { + Collection result = serviceMap.get(type); + if (result == null) { + result = Collections.emptyList(); } - 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(); + return result; } @Override diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TServiceLoader.java b/classlib/src/main/java/org/teavm/classlib/java/util/TServiceLoader.java index 6d917f816..4267d0fcc 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TServiceLoader.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TServiceLoader.java @@ -18,11 +18,6 @@ package org.teavm.classlib.java.util; import org.teavm.classlib.java.lang.*; import org.teavm.platform.PlatformClass; -/** - * - * @author Alexey Andreev - * @param - */ public final class TServiceLoader extends TObject implements TIterable { private Object[] services; @@ -50,7 +45,7 @@ public final class TServiceLoader extends TObject implements TIterable { } public static TServiceLoader load(TClass service) { - return new TServiceLoader<>(loadServices(service.getPlatformClass())); + return new TServiceLoader<>(doLoadServices(service.getPlatformClass())); } public static TServiceLoader load(TClass service, @SuppressWarnings("unused") TClassLoader loader) { @@ -61,7 +56,15 @@ public final class TServiceLoader extends TObject implements TIterable { return load(service); } - private static native 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() { // Do nothing, services are bound at build time diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index 5bdc84c7d..1c4f04d01 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -1272,7 +1272,7 @@ public class ClassGenerator { codeWriter.outdent().println("}"); GeneratorContextImpl generatorContext = new GeneratorContextImpl(codeGenerator.getClassContext(), - bodyWriter, writerBefore, codeWriter, includes); + bodyWriter, writerBefore, codeWriter, includes, callSites, context.isLongjmp()); generator.generate(generatorContext, methodRef); try { generatorContext.flush(); diff --git a/core/src/main/java/org/teavm/backend/c/generate/GeneratorContextImpl.java b/core/src/main/java/org/teavm/backend/c/generate/GeneratorContextImpl.java index 46fe11c44..876902c84 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/GeneratorContextImpl.java +++ b/core/src/main/java/org/teavm/backend/c/generate/GeneratorContextImpl.java @@ -17,6 +17,7 @@ package org.teavm.backend.c.generate; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import org.teavm.backend.c.generators.GeneratorContext; import org.teavm.backend.lowlevel.generate.NameProvider; @@ -24,6 +25,9 @@ import org.teavm.dependency.DependencyInfo; import org.teavm.diagnostics.Diagnostics; import org.teavm.model.ClassReaderSource; 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 { private GenerationContext context; @@ -33,15 +37,20 @@ class GeneratorContextImpl implements GeneratorContext { private CodeWriter writerAfter; private IncludeManager includes; private List fileGenerators = new ArrayList<>(); + private List callSites; + private boolean longjmp; public GeneratorContextImpl(ClassGenerationContext classContext, CodeWriter bodyWriter, - CodeWriter writerBefore, CodeWriter writerAfter, IncludeManager includes) { + CodeWriter writerBefore, CodeWriter writerAfter, IncludeManager includes, + List callSites, boolean longjmp) { this.context = classContext.getContext(); this.classContext = classContext; this.bodyWriter = bodyWriter; this.writerBefore = writerBefore; this.writerAfter = writerAfter; this.includes = includes; + this.callSites = callSites; + this.longjmp = longjmp; } @Override @@ -126,6 +135,20 @@ class GeneratorContextImpl implements GeneratorContext { 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 { for (FileGeneratorImpl generator : fileGenerators) { OutputFileUtil.write(generator.writer, generator.path, context.getBuildTarget()); diff --git a/core/src/main/java/org/teavm/backend/c/generators/GeneratorContext.java b/core/src/main/java/org/teavm/backend/c/generators/GeneratorContext.java index dc3d52df2..303d9505f 100644 --- a/core/src/main/java/org/teavm/backend/c/generators/GeneratorContext.java +++ b/core/src/main/java/org/teavm/backend/c/generators/GeneratorContext.java @@ -24,6 +24,9 @@ import org.teavm.dependency.DependencyInfo; import org.teavm.diagnostics.Diagnostics; import org.teavm.model.ClassReaderSource; 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 { CodeWriter writer(); @@ -53,4 +56,8 @@ public interface GeneratorContext { String escapeFileName(String name); void importMethod(MethodReference method, boolean isStatic); + + boolean usesLongjmp(); + + CallSiteDescriptor createCallSite(CallSiteLocation[] locations, ExceptionHandlerDescriptor[] exceptionHandlers); } diff --git a/core/src/main/java/org/teavm/common/HashUtils.java b/core/src/main/java/org/teavm/common/HashUtils.java new file mode 100644 index 000000000..77fea67f4 --- /dev/null +++ b/core/src/main/java/org/teavm/common/HashUtils.java @@ -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; + } +} diff --git a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java index 3db031fa5..57fd506ce 100644 --- a/core/src/main/java/org/teavm/runtime/ExceptionHandling.java +++ b/core/src/main/java/org/teavm/runtime/ExceptionHandling.java @@ -122,7 +122,10 @@ public final class ExceptionHandling { if (stackFrame == null) { stackFrame = ShadowStack.getStackTop(); 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); } printStack(); diff --git a/core/src/main/resources/org/teavm/backend/c/core.c b/core/src/main/resources/org/teavm/backend/c/core.c index 485c8922c..9daf47941 100644 --- a/core/src/main/resources/org/teavm/backend/c/core.c +++ b/core/src/main/resources/org/teavm/backend/c/core.c @@ -70,6 +70,7 @@ void teavm_initClasses() { int32_t classHeader = TEAVM_PACK_CLASS(teavm_classClass) | (int32_t) INT32_C(0x80000000); for (int i = 0; i < teavm_classReferencesCount; ++i) { teavm_classReferences[i]->parent.header = classHeader; + teavm_classReferences[i]->services = NULL; } } diff --git a/core/src/main/resources/org/teavm/backend/c/core.h b/core/src/main/resources/org/teavm/backend/c/core.h index 940d3d0d7..a0d6669db 100644 --- a/core/src/main/resources/org/teavm/backend/c/core.h +++ b/core/src/main/resources/org/teavm/backend/c/core.h @@ -22,6 +22,8 @@ typedef struct TeaVM_Array { int32_t size; } TeaVM_Array; +struct TeaVM_Services; + typedef struct TeaVM_Class { TeaVM_Object parent; int32_t size; @@ -44,12 +46,23 @@ typedef struct TeaVM_Class { TeaVM_Object** simpleName; TeaVM_Object* simpleNameCache; TeaVM_Object* canonicalName; + struct TeaVM_Services* services; #if TEAVM_HEAP_DUMP TeaVM_FieldDescriptors* fieldDescriptors; TeaVM_StaticFieldDescriptors* staticFieldDescriptors; #endif } 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 { TeaVM_Object parent; TeaVM_Array* characters; diff --git a/platform/src/main/java/org/teavm/platform/plugin/MetadataCIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/MetadataCIntrinsic.java index 3f97dfc5f..3f62c883d 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/MetadataCIntrinsic.java +++ b/platform/src/main/java/org/teavm/platform/plugin/MetadataCIntrinsic.java @@ -23,6 +23,7 @@ import java.util.Set; import org.teavm.backend.c.generate.FileGenerator; import org.teavm.backend.c.generators.Generator; import org.teavm.backend.c.generators.GeneratorContext; +import org.teavm.common.HashUtils; import org.teavm.common.ServiceRepository; import org.teavm.model.ClassReaderSource; import org.teavm.model.MethodReference; @@ -191,37 +192,7 @@ class MetadataCIntrinsic implements Generator { } private void writeResourceMap(GeneratorContext context, ResourceMap resourceMap) { - String[] keys = 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++; - } - + String[] bestTable = HashUtils.createHashTable(resourceMap.keys()); context.includes().includePath("resource.h"); context.writerBefore().println("&(struct { int32_t size; TeaVM_ResourceMapEntry entries[" + bestTable.length + "]; }) {").indent();