From 55ac5d032113258e855615975898fdb1721d5f71 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Tue, 17 Sep 2024 15:19:59 +0200 Subject: [PATCH] wasm gc: implement ServiceLoader --- .../org/teavm/classlib/impl/JCLPlugin.java | 6 + .../impl/ServiceLoaderWasmGCSupport.java | 179 ++++++++++++++++++ .../classlib/java/util/TServiceLoader.java | 15 +- .../org/teavm/backend/wasm/WasmGCTarget.java | 19 +- .../backend/wasm/gc/TeaVMWasmGCHost.java | 6 + .../gc/WasmGCDeclarationsGenerator.java | 1 + .../gc/classes/WasmGCClassGenerator.java | 23 ++- .../gc/classes/WasmGCClassInfoProvider.java | 2 + .../gc/methods/WasmGCMethodGenerator.java | 4 + .../gc/WasmGCCustomGeneratorFactory.java | 22 +++ .../WasmGCCustomGeneratorFactoryContext.java | 25 +++ .../generators/gc/WasmGCCustomGenerators.java | 66 +++++-- .../gc/WasmGCIntrinsicFactoryContext.java | 3 + .../wasm/intrinsics/gc/WasmGCIntrinsics.java | 12 +- 14 files changed, 365 insertions(+), 18 deletions(-) create mode 100644 classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderWasmGCSupport.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorFactory.java create mode 100644 core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorFactoryContext.java 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 9a39e7e63..a2fad720f 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java @@ -22,6 +22,7 @@ import java.util.ServiceLoader; import org.teavm.backend.c.TeaVMCHost; import org.teavm.backend.javascript.TeaVMJavaScriptHost; import org.teavm.backend.wasm.TeaVMWasmHost; +import org.teavm.backend.wasm.gc.TeaVMWasmGCHost; import org.teavm.classlib.ReflectionSupplier; import org.teavm.classlib.impl.currency.CountriesGenerator; import org.teavm.classlib.impl.currency.CurrenciesGenerator; @@ -94,6 +95,11 @@ public class JCLPlugin implements TeaVMPlugin { if (wasmHost != null) { wasmHost.add(new ServiceLoaderWasmSupport()); } + + var wasmGCHost = host.getExtension(TeaVMWasmGCHost.class); + if (wasmGCHost != null) { + wasmGCHost.addGeneratorFactory(new ServiceLoaderWasmGCSupport()); + } } if (!isBootstrap()) { diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderWasmGCSupport.java b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderWasmGCSupport.java new file mode 100644 index 000000000..c17d55797 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderWasmGCSupport.java @@ -0,0 +1,179 @@ +/* + * 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.ArrayList; +import java.util.Collection; +import java.util.ServiceLoader; +import org.teavm.backend.wasm.generate.TemporaryVariablePool; +import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; +import org.teavm.backend.wasm.generate.gc.methods.WasmGCGenerationUtil; +import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator; +import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorContext; +import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorFactory; +import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorFactoryContext; +import org.teavm.backend.wasm.model.WasmFunction; +import org.teavm.backend.wasm.model.WasmGlobal; +import org.teavm.backend.wasm.model.WasmLocal; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmBlock; +import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmCallReference; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmFunctionReference; +import org.teavm.backend.wasm.model.expression.WasmGetGlobal; +import org.teavm.backend.wasm.model.expression.WasmGetLocal; +import org.teavm.backend.wasm.model.expression.WasmNullBranch; +import org.teavm.backend.wasm.model.expression.WasmNullCondition; +import org.teavm.backend.wasm.model.expression.WasmNullConstant; +import org.teavm.backend.wasm.model.expression.WasmReturn; +import org.teavm.backend.wasm.model.expression.WasmSetGlobal; +import org.teavm.backend.wasm.model.expression.WasmSetLocal; +import org.teavm.backend.wasm.model.expression.WasmStructGet; +import org.teavm.backend.wasm.model.expression.WasmStructNewDefault; +import org.teavm.backend.wasm.model.expression.WasmStructSet; +import org.teavm.model.MethodDescriptor; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; + +public class ServiceLoaderWasmGCSupport implements WasmGCCustomGeneratorFactory { + static final MethodDescriptor INIT_METHOD = new MethodDescriptor("", ValueType.VOID); + + @Override + public WasmGCCustomGenerator createGenerator(MethodReference methodRef, + WasmGCCustomGeneratorFactoryContext context) { + if (methodRef.getClassName().equals(ServiceLoader.class.getName()) + && methodRef.getName().equals("loadServices")) { + return new ServiceLoaderIntrinsic(context.services().getService(ServiceLoaderInformation.class)); + } + return null; + } + + static class ServiceLoaderIntrinsic implements WasmGCCustomGenerator { + private ServiceLoaderInformation information; + + ServiceLoaderIntrinsic(ServiceLoaderInformation information) { + this.information = information; + } + + @Override + public void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context) { + var initializer = generateInitializer(context); + var emptyInitializer = generateEmptyInitializer(context); + var arrayType = (WasmType.Reference) context.typeMapper().mapType(ValueType.parse(Object[].class)); + var servicesFunctionType = context.functionTypes().of(arrayType); + var classLocal = new WasmLocal(context.typeMapper().mapType(ValueType.parse(Class.class))); + function.add(classLocal); + + var classStruct = context.classInfoProvider().getClassInfo("java.lang.Class").getStructure(); + + var initializerGlobalName = context.names().topLevel("teavm@initializeServicesRef"); + var global = new WasmGlobal(initializerGlobalName, initializer.getType().getReference(), + new WasmFunctionReference(initializer)); + context.module().globals.add(global); + initializer.getBody().add(0, new WasmSetGlobal(global, new WasmFunctionReference(emptyInitializer))); + + function.getBody().add(new WasmCallReference(new WasmGetGlobal(global), initializer.getType())); + + var block = new WasmBlock(false); + var servicesFunctionRef = new WasmStructGet(classStruct, new WasmGetLocal(classLocal), + context.classInfoProvider().getServicesOffset()); + var nullCheckedRef = new WasmNullBranch(WasmNullCondition.NULL, servicesFunctionRef, block); + var getServices = new WasmCallReference(nullCheckedRef, servicesFunctionType); + block.getBody().add(new WasmReturn(getServices)); + function.getBody().add(block); + + function.getBody().add(new WasmNullConstant(arrayType)); + } + + private WasmFunction generateInitializer(WasmGCCustomGeneratorContext context) { + var function = new WasmFunction(context.functionTypes().of(null)); + function.setReferenced(true); + function.setName(context.names().topLevel("teavm@initializeServices")); + context.module().functions.add(function); + + var serviceTypes = information.serviceTypes(); + var classStruct = context.classInfoProvider().getClassInfo("java.lang.Class").getStructure(); + var fieldIndex = context.classInfoProvider().getServicesOffset(); + + for (var serviceType : serviceTypes) { + var implementations = information.serviceImplementations(serviceType); + var providerFunction = generateServiceProvider(context, serviceType, implementations); + var classInfo = context.classInfoProvider().getClassInfo(serviceType); + var classRef = new WasmGetGlobal(classInfo.getPointer()); + var providerRef = new WasmFunctionReference(providerFunction); + function.getBody().add(new WasmStructSet(classStruct, classRef, fieldIndex, providerRef)); + } + + return function; + } + + private WasmFunction generateServiceProvider(WasmGCCustomGeneratorContext context, + String interfaceName, Collection implementations) { + var functionType = context.functionTypes().of(context.typeMapper().mapType( + ValueType.parse(Object[].class))); + var function = new WasmFunction(functionType); + function.setName(context.names().topLevel(context.names().suggestForClass(interfaceName) + "@services")); + function.setReferenced(true); + context.module().functions.add(function); + var tempVars = new TemporaryVariablePool(function); + var util = new WasmGCGenerationUtil(context.classInfoProvider(), tempVars); + var block = new WasmBlock(false); + block.setType(context.typeMapper().mapType(ValueType.parse(Object[].class))); + util.allocateArrayWithElements(ValueType.parse(Object.class), () -> { + var items = new ArrayList(); + for (var implementationName : implementations) { + items.add(instantiateService(context, function, implementationName)); + } + return items; + }, null, null, function.getBody()); + + return function; + } + + private WasmExpression instantiateService(WasmGCCustomGeneratorContext context, + WasmFunction function, String implementationName) { + var implementationInfo = context.classInfoProvider().getClassInfo(implementationName); + var block = new WasmBlock(false); + block.setType(context.typeMapper().mapType(ValueType.parse(Object.class))); + var tmpVar = new WasmLocal(implementationInfo.getType()); + function.add(tmpVar); + var structNew = new WasmSetLocal(tmpVar, new WasmStructNewDefault( + implementationInfo.getStructure())); + block.getBody().add(structNew); + + var initClassField = new WasmStructSet(implementationInfo.getStructure(), new WasmGetLocal(tmpVar), + WasmGCClassInfoProvider.CLASS_FIELD_OFFSET, new WasmGetGlobal(implementationInfo.getPointer())); + block.getBody().add(initClassField); + + var constructor = context.functions().forInstanceMethod( + new MethodReference(implementationName, INIT_METHOD)); + block.getBody().add(new WasmCall(constructor, new WasmGetLocal(tmpVar))); + block.getBody().add(new WasmGetLocal(tmpVar)); + + return block; + } + + private WasmFunction generateEmptyInitializer(WasmGCCustomGeneratorContext context) { + var function = new WasmFunction(context.functionTypes().of(null)); + function.setReferenced(true); + function.setName(context.names().topLevel("teavm@emptyServicesInitializer")); + context.module().functions.add(function); + function.getBody().add(new WasmReturn()); + return function; + } + } +} 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 4267d0fcc..47ebd91cf 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 @@ -15,6 +15,7 @@ */ package org.teavm.classlib.java.util; +import org.teavm.classlib.PlatformDetector; import org.teavm.classlib.java.lang.*; import org.teavm.platform.PlatformClass; @@ -45,7 +46,9 @@ public final class TServiceLoader extends TObject implements TIterable { } public static TServiceLoader load(TClass service) { - return new TServiceLoader<>(doLoadServices(service.getPlatformClass())); + return new TServiceLoader<>(PlatformDetector.isWebAssemblyGC() + ? doLoadServices(service) + : doLoadServices(service.getPlatformClass())); } public static TServiceLoader load(TClass service, @SuppressWarnings("unused") TClassLoader loader) { @@ -66,6 +69,16 @@ public final class TServiceLoader extends TObject implements TIterable { private static native Object[] loadServices(PlatformClass cls); + private static Object[] doLoadServices(TClass cls) { + Object[] result = loadServices(cls); + if (result == null) { + result = new Object[0]; + } + return result; + } + + private static native Object[] loadServices(TClass cls); + public void reload() { // Do nothing, services are bound at build time } diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java index 88b281251..d44cc7fed 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java @@ -25,6 +25,8 @@ import org.teavm.backend.wasm.gc.TeaVMWasmGCHost; import org.teavm.backend.wasm.gc.WasmGCDependencies; import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator; import org.teavm.backend.wasm.generate.gc.classes.WasmGCCustomTypeMapperFactory; +import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator; +import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorFactory; import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerators; import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic; import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicFactory; @@ -62,6 +64,8 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost { private List intrinsicFactories = new ArrayList<>(); private Map customIntrinsics = new HashMap<>(); private List customTypeMapperFactories = new ArrayList<>(); + private Map customCustomGenerators = new HashMap<>(); + private List customGeneratorFactories = new ArrayList<>(); public void setObfuscated(boolean obfuscated) { this.obfuscated = obfuscated; @@ -81,6 +85,16 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost { customIntrinsics.put(method, intrinsic); } + @Override + public void addGeneratorFactory(WasmGCCustomGeneratorFactory factory) { + customGeneratorFactories.add(factory); + } + + @Override + public void addGenerator(MethodReference method, WasmGCCustomGenerator generator) { + customCustomGenerators.put(method, generator); + } + @Override public void addCustomTypeMapperFactory(WasmGCCustomTypeMapperFactory customTypeMapperFactory) { customTypeMapperFactories.add(customTypeMapperFactory); @@ -151,8 +165,9 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost { @Override public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException { var module = new WasmModule(); - var customGenerators = new WasmGCCustomGenerators(); - var intrinsics = new WasmGCIntrinsics(classes, intrinsicFactories, customIntrinsics); + var customGenerators = new WasmGCCustomGenerators(classes, controller.getServices(), + customGeneratorFactories, customCustomGenerators); + var intrinsics = new WasmGCIntrinsics(classes, controller.getServices(), intrinsicFactories, customIntrinsics); var declarationsGenerator = new WasmGCDeclarationsGenerator( module, classes, diff --git a/core/src/main/java/org/teavm/backend/wasm/gc/TeaVMWasmGCHost.java b/core/src/main/java/org/teavm/backend/wasm/gc/TeaVMWasmGCHost.java index 30e0c2479..8dcd28933 100644 --- a/core/src/main/java/org/teavm/backend/wasm/gc/TeaVMWasmGCHost.java +++ b/core/src/main/java/org/teavm/backend/wasm/gc/TeaVMWasmGCHost.java @@ -16,6 +16,8 @@ package org.teavm.backend.wasm.gc; import org.teavm.backend.wasm.generate.gc.classes.WasmGCCustomTypeMapperFactory; +import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator; +import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorFactory; import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic; import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicFactory; import org.teavm.model.MethodReference; @@ -26,5 +28,9 @@ public interface TeaVMWasmGCHost extends TeaVMHostExtension { void addIntrinsic(MethodReference method, WasmGCIntrinsic intrinsic); + void addGeneratorFactory(WasmGCCustomGeneratorFactory factory); + + void addGenerator(MethodReference method, WasmGCCustomGenerator generator); + void addCustomTypeMapperFactory(WasmGCCustomTypeMapperFactory customTypeMapperFactory); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java index 5327892f8..b5883d192 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/WasmGCDeclarationsGenerator.java @@ -83,6 +83,7 @@ public class WasmGCDeclarationsGenerator { module, classes, hierarchy, + dependencyInfo, functionTypes, tags, metadataRequirements, diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java index f740a41f9..7c3afeb83 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassGenerator.java @@ -24,6 +24,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Queue; +import java.util.ServiceLoader; import java.util.function.Consumer; import java.util.stream.Collectors; import org.teavm.backend.wasm.BaseWasmFunctionRepository; @@ -75,6 +76,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructNew; import org.teavm.backend.wasm.model.expression.WasmStructNewDefault; import org.teavm.backend.wasm.model.expression.WasmStructSet; import org.teavm.backend.wasm.runtime.WasmGCSupport; +import org.teavm.dependency.DependencyInfo; import org.teavm.model.ClassHierarchy; import org.teavm.model.ClassReader; import org.teavm.model.ClassReaderSource; @@ -143,6 +145,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private int arrayLengthOffset = -1; private int arrayGetOffset = -1; private int cloneOffset = -1; + private int servicesOffset = -1; private WasmStructure arrayVirtualTableStruct; private WasmFunction arrayGetObjectFunction; private WasmFunction arrayLengthObjectFunction; @@ -150,9 +153,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit private WasmFunctionType arrayLengthType; private List nonInitializedStructures = new ArrayList<>(); private WasmArray objectArrayType; + private boolean hasLoadServices; public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource, - ClassHierarchy hierarchy, + ClassHierarchy hierarchy, DependencyInfo dependencyInfo, WasmFunctionTypes functionTypes, TagRegistry tagRegistry, ClassMetadataRequirements metadataRequirements, WasmGCVirtualTableProvider virtualTables, BaseWasmFunctionRepository functionProvider, WasmGCNameProvider names, @@ -177,6 +181,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit typeMapper.setCustomTypeMappers(customTypeMapperFactories.stream() .map(factory -> factory.createTypeMapper(customTypeMapperFactoryContext)) .collect(Collectors.toList())); + + var loadServicesMethod = dependencyInfo.getMethod(new MethodReference(ServiceLoader.class, "loadServices", + Class.class, Object[].class)); + if (loadServicesMethod != null && loadServicesMethod.isUsed()) { + hasLoadServices = true; + } } private WasmGCCustomTypeMapperFactoryContext customTypeMapperFactoryContext() { @@ -443,6 +453,12 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit return cloneOffset; } + @Override + public int getServicesOffset() { + standardClasses.classClass().getStructure().init(); + return servicesOffset; + } + private void initPrimitiveClass(WasmGCClassInfo classInfo, ValueType.Primitive type) { classInfo.initializer = target -> { int kind; @@ -1152,6 +1168,11 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit cloneOffset = fields.size(); fields.add(createClassField(functionTypes.of(standardClasses.objectClass().getType(), standardClasses.objectClass().getType()).getReference().asStorage(), "clone")); + if (hasLoadServices) { + servicesOffset = fields.size(); + var serviceFunctionType = functionTypes.of(getClassInfo(ValueType.parse(Object[].class)).getType()); + fields.add(createClassField(serviceFunctionType.getReference().asStorage(), "services")); + } if (metadataRequirements.hasEnumConstants()) { enumConstantsFunctionOffset = fields.size(); var enumArrayType = getClassInfo(ValueType.arrayOf(ValueType.object("java.lang.Enum"))).getType(); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java index 5dee166d6..0c6becf3c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCClassInfoProvider.java @@ -67,6 +67,8 @@ public interface WasmGCClassInfoProvider { int getCloneOffset(); + int getServicesOffset(); + default WasmGCClassInfo getClassInfo(String name) { return getClassInfo(ValueType.object(name)); } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java index 79e8e2bd7..bd5e38d19 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCMethodGenerator.java @@ -28,6 +28,7 @@ import org.teavm.ast.decompilation.Decompiler; import org.teavm.backend.wasm.BaseWasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.gc.PreciseTypeInference; +import org.teavm.backend.wasm.gc.PreciseValueType; import org.teavm.backend.wasm.gc.WasmGCVariableCategoryProvider; import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableProvider; import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider; @@ -269,6 +270,9 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { var localVar = ast.getVariables().get(i); var representative = method.getProgram().variableAt(variableRepresentatives[i]); var inferredType = typeInference.typeOf(representative); + if (inferredType == null) { + inferredType = new PreciseValueType(ValueType.object("java.lang.Object"), false); + } var type = !inferredType.isArrayUnwrap ? typeMapper.mapType(inferredType.valueType) : classInfoProvider.getClassInfo(inferredType.valueType).getArray().getReference(); diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorFactory.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorFactory.java new file mode 100644 index 000000000..a999bf101 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorFactory.java @@ -0,0 +1,22 @@ +/* + * Copyright 2024 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.generators.gc; + +import org.teavm.model.MethodReference; + +public interface WasmGCCustomGeneratorFactory { + WasmGCCustomGenerator createGenerator(MethodReference methodRef, WasmGCCustomGeneratorFactoryContext context); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorFactoryContext.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorFactoryContext.java new file mode 100644 index 000000000..25b47cbc5 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGeneratorFactoryContext.java @@ -0,0 +1,25 @@ +/* + * Copyright 2024 konsoletyper. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.backend.wasm.generators.gc; + +import org.teavm.common.ServiceRepository; +import org.teavm.model.ClassReaderSource; + +public interface WasmGCCustomGeneratorFactoryContext { + ClassReaderSource classes(); + + ServiceRepository services(); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java index d7f4fbbaa..8ef4bb8d1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java +++ b/core/src/main/java/org/teavm/backend/wasm/generators/gc/WasmGCCustomGenerators.java @@ -17,36 +17,46 @@ package org.teavm.backend.wasm.generators.gc; import java.lang.reflect.Array; import java.util.HashMap; +import java.util.List; import java.util.Map; import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider; import org.teavm.backend.wasm.runtime.WasmGCSupport; +import org.teavm.common.ServiceRepository; +import org.teavm.model.ClassReaderSource; import org.teavm.model.MethodReference; public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider { - private Map generators = new HashMap<>(); + private List factories; + private Map generators = new HashMap<>(); + private ClassReaderSource classes; + private ServiceRepository services; - public WasmGCCustomGenerators() { + public WasmGCCustomGenerators(ClassReaderSource classes, ServiceRepository services, + List factories, + Map generators) { + this.factories = List.copyOf(factories); + this.classes = classes; + this.services = services; fillClass(); fillStringPool(); fillSystem(); fillArray(); + for (var entry : generators.entrySet()) { + add(entry.getKey(), entry.getValue()); + } } private void fillClass() { var classGenerators = new ClassGenerators(); - generators.put(new MethodReference(Class.class, "isAssignableFrom", Class.class, boolean.class), - classGenerators); + add(new MethodReference(Class.class, "isAssignableFrom", Class.class, boolean.class), classGenerators); } private void fillStringPool() { - generators.put( - new MethodReference(WasmGCSupport.class, "nextByte", byte.class), - new WasmGCStringPoolGenerator() - ); + add(new MethodReference(WasmGCSupport.class, "nextByte", byte.class), new WasmGCStringPoolGenerator()); } private void fillSystem() { - generators.put( + add( new MethodReference(System.class, "doArrayCopy", Object.class, int.class, Object.class, int.class, int.class, void.class), new SystemDoArrayCopyGenerator() @@ -55,12 +65,44 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider { private void fillArray() { var arrayGenerator = new ArrayGenerator(); - generators.put(new MethodReference(Array.class, "newInstanceImpl", Class.class, int.class, Object.class), - arrayGenerator); + add(new MethodReference(Array.class, "newInstanceImpl", Class.class, int.class, Object.class), arrayGenerator); } @Override public WasmGCCustomGenerator get(MethodReference method) { - return generators.get(method); + var result = generators.get(method); + if (result == null) { + WasmGCCustomGenerator generator = null; + for (var factory : factories) { + generator = factory.createGenerator(method, factoryContext); + } + result = new Container(generator); + generators.put(method, result); + } + return result.generator; } + + private void add(MethodReference method, WasmGCCustomGenerator generator) { + generators.put(method, new Container(generator)); + } + + private static class Container { + final WasmGCCustomGenerator generator; + + Container(WasmGCCustomGenerator generator) { + this.generator = generator; + } + } + + private WasmGCCustomGeneratorFactoryContext factoryContext = new WasmGCCustomGeneratorFactoryContext() { + @Override + public ClassReaderSource classes() { + return classes; + } + + @Override + public ServiceRepository services() { + return services; + } + }; } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicFactoryContext.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicFactoryContext.java index 3bda398ea..b0b460aa8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicFactoryContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicFactoryContext.java @@ -15,8 +15,11 @@ */ package org.teavm.backend.wasm.intrinsics.gc; +import org.teavm.common.ServiceRepository; import org.teavm.model.ClassReaderSource; public interface WasmGCIntrinsicFactoryContext { ClassReaderSource classes(); + + ServiceRepository services(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java index 89f88411b..0879bbda1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsics.java @@ -22,6 +22,7 @@ import java.util.Map; import org.teavm.backend.wasm.WasmRuntime; import org.teavm.backend.wasm.generate.gc.methods.WasmGCIntrinsicProvider; import org.teavm.backend.wasm.model.expression.WasmIntType; +import org.teavm.common.ServiceRepository; import org.teavm.model.ClassReaderSource; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; @@ -30,10 +31,12 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { private Map intrinsics = new HashMap<>(); private List factories; private ClassReaderSource classes; + private ServiceRepository services; - public WasmGCIntrinsics(ClassReaderSource classes, List factories, - Map customIntrinsics) { + public WasmGCIntrinsics(ClassReaderSource classes, ServiceRepository services, + List factories, Map customIntrinsics) { this.classes = classes; + this.services = services; this.factories = List.copyOf(factories); factories = List.copyOf(factories); fillWasmRuntime(); @@ -179,5 +182,10 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { public ClassReaderSource classes() { return classes; } + + @Override + public ServiceRepository services() { + return services; + } }; }