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 87bbb1ae4..da341b096 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java @@ -16,10 +16,17 @@ package org.teavm.backend.wasm; import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; +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.WasmGCCustomGenerators; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicFactory; import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsics; import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.render.WasmBinaryRenderer; @@ -33,6 +40,7 @@ import org.teavm.interop.Platforms; import org.teavm.model.ClassHolderTransformer; import org.teavm.model.ListableClassHolderSource; import org.teavm.model.MethodReader; +import org.teavm.model.MethodReference; import org.teavm.model.Program; import org.teavm.model.transformation.BoundCheckInsertion; import org.teavm.model.transformation.NullCheckFilter; @@ -43,16 +51,34 @@ import org.teavm.vm.TeaVMTarget; import org.teavm.vm.TeaVMTargetController; import org.teavm.vm.spi.TeaVMHostExtension; -public class WasmGCTarget implements TeaVMTarget { +public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost { private TeaVMTargetController controller; private NullCheckInsertion nullCheckInsertion; private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion(); private boolean obfuscated; + private List intrinsicFactories = new ArrayList<>(); + private Map customIntrinsics = new HashMap<>(); + private List customTypeMapperFactories = new ArrayList<>(); public void setObfuscated(boolean obfuscated) { this.obfuscated = obfuscated; } + @Override + public void addIntrinsicFactory(WasmGCIntrinsicFactory intrinsicFactory) { + intrinsicFactories.add(intrinsicFactory); + } + + @Override + public void addIntrinsic(MethodReference method, WasmGCIntrinsic intrinsic) { + customIntrinsics.put(method, intrinsic); + } + + @Override + public void addCustomTypeMapperFactory(WasmGCCustomTypeMapperFactory customTypeMapperFactory) { + customTypeMapperFactories.add(customTypeMapperFactory); + } + @Override public void setController(TeaVMTargetController controller) { this.controller = controller; @@ -85,7 +111,7 @@ public class WasmGCTarget implements TeaVMTarget { @Override public List getHostExtensions() { - return List.of(); + return List.of(this); } @Override @@ -114,15 +140,17 @@ public class WasmGCTarget implements TeaVMTarget { public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException { var module = new WasmModule(); var customGenerators = new WasmGCCustomGenerators(); - var intrinsics = new WasmGCIntrinsics(); + var intrinsics = new WasmGCIntrinsics(classes, intrinsicFactories, customIntrinsics); var declarationsGenerator = new WasmGCDeclarationsGenerator( module, classes, + controller.getClassLoader(), controller.getClassInitializerInfo(), controller.getDependencyInfo(), controller.getDiagnostics(), customGenerators, intrinsics, + customTypeMapperFactories, controller::isVirtual ); declarationsGenerator.setFriendlyToDebugger(controller.isFriendlyToDebugger()); diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/BaseDisassemblyListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/BaseDisassemblyListener.java index 21c0a31cd..7570dc1da 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/BaseDisassemblyListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/BaseDisassemblyListener.java @@ -75,6 +75,9 @@ public abstract class BaseDisassemblyListener { case ANY: writer.write("anyref"); return; + case EQ: + writer.write("eqref"); + return; case FUNC: writer.write("funcref"); return; @@ -99,6 +102,9 @@ public abstract class BaseDisassemblyListener { case ANY: writer.write("any"); return; + case EQ: + writer.write("eq"); + return; case FUNC: writer.write("func"); return; diff --git a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeListener.java b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeListener.java index fc3db8228..ea65aee70 100644 --- a/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/disasm/DisassemblyCodeListener.java @@ -815,6 +815,13 @@ public class DisassemblyCodeListener extends BaseDisassemblyListener implements writer.eol(); } + @Override + public void arrayNewFixed(int typeIndex, int size) { + writer.address().write("array.new_fixed "); + writeTypeRef(typeIndex); + writer.write(" ").write(Integer.toString(size)).eol(); + } + @Override public void arrayGet(WasmSignedType signedType, int typeIndex) { writer.address(); 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 new file mode 100644 index 000000000..30e0c2479 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/gc/TeaVMWasmGCHost.java @@ -0,0 +1,30 @@ +/* + * 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.gc; + +import org.teavm.backend.wasm.generate.gc.classes.WasmGCCustomTypeMapperFactory; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicFactory; +import org.teavm.model.MethodReference; +import org.teavm.vm.spi.TeaVMHostExtension; + +public interface TeaVMWasmGCHost extends TeaVMHostExtension { + void addIntrinsicFactory(WasmGCIntrinsicFactory intrinsicFactory); + + void addIntrinsic(MethodReference method, WasmGCIntrinsic intrinsic); + + 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 acca35b92..60f0febb6 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 @@ -22,6 +22,7 @@ import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTableProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassGenerator; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; +import org.teavm.backend.wasm.generate.gc.classes.WasmGCCustomTypeMapperFactory; import org.teavm.backend.wasm.generate.gc.classes.WasmGCSupertypeFunctionProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper; import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider; @@ -49,11 +50,13 @@ public class WasmGCDeclarationsGenerator { public WasmGCDeclarationsGenerator( WasmModule module, ListableClassHolderSource classes, + ClassLoader classLoader, ClassInitializerInfo classInitializerInfo, DependencyInfo dependencyInfo, Diagnostics diagnostics, WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics, + List customTypeMapperFactories, Predicate isVirtual ) { this.module = module; @@ -65,6 +68,7 @@ public class WasmGCDeclarationsGenerator { module, hierarchy, classes, + classLoader, virtualTables, classInitializerInfo, functionTypes, @@ -84,7 +88,8 @@ public class WasmGCDeclarationsGenerator { virtualTables, methodGenerator, names, - classInitializerInfo + classInitializerInfo, + customTypeMapperFactories ); methodGenerator.setClassInfoProvider(classGenerator); methodGenerator.setStrings(classGenerator.strings); 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 6150eae8d..0a29442a0 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 @@ -25,6 +25,7 @@ import java.util.List; import java.util.Map; import java.util.Queue; import java.util.function.Consumer; +import java.util.stream.Collectors; import org.teavm.backend.wasm.BaseWasmFunctionRepository; import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.gc.vtable.WasmGCVirtualTable; @@ -137,7 +138,8 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit WasmFunctionTypes functionTypes, TagRegistry tagRegistry, ClassMetadataRequirements metadataRequirements, WasmGCVirtualTableProvider virtualTables, BaseWasmFunctionRepository functionProvider, WasmGCNameProvider names, - ClassInitializerInfo classInitializerInfo) { + ClassInitializerInfo classInitializerInfo, + List customTypeMapperFactories) { this.module = module; this.classSource = classSource; this.functionTypes = functionTypes; @@ -152,6 +154,39 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit supertypeGenerator = new WasmGCSupertypeFunctionGenerator(module, this, names, tagRegistry, functionTypes); newArrayGenerator = new WasmGCNewArrayFunctionGenerator(module, functionTypes, this, names); typeMapper = new WasmGCTypeMapper(classSource, this, functionTypes, module); + var customTypeMapperFactoryContext = customTypeMapperFactoryContext(); + typeMapper.setCustomTypeMappers(customTypeMapperFactories.stream() + .map(factory -> factory.createTypeMapper(customTypeMapperFactoryContext)) + .collect(Collectors.toList())); + } + + private WasmGCCustomTypeMapperFactoryContext customTypeMapperFactoryContext() { + return new WasmGCCustomTypeMapperFactoryContext() { + @Override + public ClassReaderSource classes() { + return classSource; + } + + @Override + public WasmModule module() { + return module; + } + + @Override + public WasmGCClassInfoProvider classInfoProvider() { + return WasmGCClassGenerator.this; + } + + @Override + public WasmGCNameProvider names() { + return names; + } + + @Override + public WasmGCTypeMapper typeMapper() { + return typeMapper; + } + }; } public WasmGCSupertypeFunctionProvider getSupertypeProvider() { diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCCustomTypeMapper.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCCustomTypeMapper.java new file mode 100644 index 000000000..03a5bc66f --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCCustomTypeMapper.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.generate.gc.classes; + +import org.teavm.backend.wasm.model.WasmType; + +public interface WasmGCCustomTypeMapper { + WasmType map(String className); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCCustomTypeMapperFactory.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCCustomTypeMapperFactory.java new file mode 100644 index 000000000..27dee4cf2 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCCustomTypeMapperFactory.java @@ -0,0 +1,20 @@ +/* + * 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.generate.gc.classes; + +public interface WasmGCCustomTypeMapperFactory { + WasmGCCustomTypeMapper createTypeMapper(WasmGCCustomTypeMapperFactoryContext context); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCCustomTypeMapperFactoryContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCCustomTypeMapperFactoryContext.java new file mode 100644 index 000000000..50bf875df --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCCustomTypeMapperFactoryContext.java @@ -0,0 +1,32 @@ +/* + * 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.generate.gc.classes; + +import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider; +import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.model.ClassReaderSource; + +public interface WasmGCCustomTypeMapperFactoryContext { + ClassReaderSource classes(); + + WasmModule module(); + + WasmGCClassInfoProvider classInfoProvider(); + + WasmGCTypeMapper typeMapper(); + + WasmGCNameProvider names(); +} diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCTypeMapper.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCTypeMapper.java index 8f56403fa..83d80c0ed 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCTypeMapper.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/classes/WasmGCTypeMapper.java @@ -15,7 +15,9 @@ */ package org.teavm.backend.wasm.generate.gc.classes; +import java.util.HashMap; import java.util.List; +import java.util.Map; import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.model.WasmFunctionType; import org.teavm.backend.wasm.model.WasmModule; @@ -31,6 +33,8 @@ public class WasmGCTypeMapper { private WasmGCClassInfoProvider classInfoProvider; private WasmFunctionTypes functionTypes; private WasmModule module; + private List customTypeMappers; + private Map typeCache = new HashMap<>(); WasmGCTypeMapper(ClassReaderSource classes, WasmGCClassInfoProvider classInfoProvider, WasmFunctionTypes functionTypes, WasmModule module) { @@ -40,6 +44,10 @@ public class WasmGCTypeMapper { this.module = module; } + void setCustomTypeMappers(List customTypeMappers) { + this.customTypeMappers = List.copyOf(customTypeMappers); + } + public WasmStorageType mapStorageType(ValueType type) { if (type instanceof ValueType.Primitive) { switch (((ValueType.Primitive) type).getKind()) { @@ -61,7 +69,7 @@ public class WasmGCTypeMapper { throw new IllegalArgumentException(); } } else { - return classInfoProvider.getClassInfo(type).getType().asStorage(); + return mapType(type).asStorage(); } } @@ -86,12 +94,7 @@ public class WasmGCTypeMapper { } else if (type instanceof ValueType.Void) { return null; } else if (type instanceof ValueType.Object) { - var className = ((ValueType.Object) type).getClassName(); - var cls = classes.get(className); - if (cls == null) { - className = "java.lang.Object"; - } - return classInfoProvider.getClassInfo(className).getType(); + return mapClassType(((ValueType.Object) type).getClassName()); } else if (type instanceof ValueType.Array) { var degree = 0; while (type instanceof ValueType.Array) { @@ -114,6 +117,27 @@ public class WasmGCTypeMapper { } } + private WasmType mapClassType(String className) { + var result = typeCache.get(className); + if (result == null) { + for (var customMapper : customTypeMappers) { + result = customMapper.map(className); + if (result != null) { + break; + } + } + if (result == null) { + var cls = classes.get(className); + if (cls == null) { + className = "java.lang.Object"; + } + result = classInfoProvider.getClassInfo(className).getType(); + typeCache.put(className, result); + } + } + return result; + } + public WasmFunctionType getFunctionType(WasmType receiverType, MethodDescriptor methodDesc, boolean fresh) { var returnType = mapType(methodDesc.getResultType()); var javaParamTypes = methodDesc.getParameterTypes(); diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java index 10207e39e..e892d868a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationContext.java @@ -52,6 +52,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { private WasmGCTypeMapper typeMapper; private WasmFunctionTypes functionTypes; private ListableClassReaderSource classes; + private ClassLoader classLoader; private ClassHierarchy hierarchy; private BaseWasmFunctionRepository functions; private WasmGCSupertypeFunctionProvider supertypeFunctions; @@ -67,7 +68,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { public WasmGCGenerationContext(WasmModule module, WasmGCVirtualTableProvider virtualTables, WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ListableClassReaderSource classes, - ClassHierarchy hierarchy, BaseWasmFunctionRepository functions, + ClassLoader classLoader, ClassHierarchy hierarchy, BaseWasmFunctionRepository functions, WasmGCSupertypeFunctionProvider supertypeFunctions, WasmGCClassInfoProvider classInfoProvider, WasmGCStandardClasses standardClasses, WasmGCStringProvider strings, WasmGCCustomGeneratorProvider customGenerators, WasmGCIntrinsicProvider intrinsics, @@ -77,6 +78,7 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { this.typeMapper = typeMapper; this.functionTypes = functionTypes; this.classes = classes; + this.classLoader = classLoader; this.hierarchy = hierarchy; this.functions = functions; this.supertypeFunctions = supertypeFunctions; @@ -92,6 +94,10 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { return classInfoProvider; } + public WasmGCNameProvider names() { + return names; + } + public WasmGCStandardClasses standardClasses() { return standardClasses; } @@ -137,6 +143,10 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext { return classes; } + public ClassLoader classLoader() { + return classLoader; + } + public ClassHierarchy hierarchy() { return hierarchy; } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java index 7ca417716..927fd6db5 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/gc/methods/WasmGCGenerationVisitor.java @@ -30,8 +30,10 @@ import org.teavm.backend.wasm.gc.PreciseTypeInference; import org.teavm.backend.wasm.generate.ExpressionCache; import org.teavm.backend.wasm.generate.TemporaryVariablePool; import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationVisitor; +import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper; +import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider; import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext; import org.teavm.backend.wasm.model.WasmArray; import org.teavm.backend.wasm.model.WasmFunction; @@ -585,6 +587,11 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { return result; } + @Override + public ClassLoader classLoader() { + return context.classLoader(); + } + @Override public WasmModule module() { return context.module(); @@ -629,5 +636,15 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor { public ExpressionCache exprCache() { return exprCache; } + + @Override + public WasmGCNameProvider names() { + return context.names(); + } + + @Override + public WasmGCStringProvider strings() { + return context.strings(); + } }; } 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 d127a58bc..9df9788f6 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 @@ -60,6 +60,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { private WasmModule module; private ClassHierarchy hierarchy; private ListableClassHolderSource classes; + private ClassLoader classLoader; private WasmGCVirtualTableProvider virtualTables; private ClassInitializerInfo classInitInfo; private WasmFunctionTypes functionTypes; @@ -84,6 +85,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { WasmModule module, ClassHierarchy hierarchy, ListableClassHolderSource classes, + ClassLoader classLoader, WasmGCVirtualTableProvider virtualTables, ClassInitializerInfo classInitInfo, WasmFunctionTypes functionTypes, @@ -95,6 +97,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { this.module = module; this.hierarchy = hierarchy; this.classes = classes; + this.classLoader = classLoader; this.virtualTables = virtualTables; this.classInitInfo = classInitInfo; this.functionTypes = functionTypes; @@ -310,6 +313,7 @@ public class WasmGCMethodGenerator implements BaseWasmFunctionRepository { typeMapper, functionTypes, classes, + classLoader, hierarchy, this, supertypeFunctions, diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java index 1cc0d2cc4..0f7ca7422 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicContext.java @@ -21,8 +21,10 @@ import org.teavm.backend.wasm.WasmFunctionTypes; import org.teavm.backend.wasm.gc.PreciseTypeInference; import org.teavm.backend.wasm.generate.ExpressionCache; import org.teavm.backend.wasm.generate.TemporaryVariablePool; +import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider; import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper; +import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider; import org.teavm.backend.wasm.model.WasmModule; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.model.ClassHierarchy; @@ -47,4 +49,10 @@ public interface WasmGCIntrinsicContext { TemporaryVariablePool tempVars(); ExpressionCache exprCache(); + + WasmGCNameProvider names(); + + WasmGCStringProvider strings(); + + ClassLoader classLoader(); } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicFactory.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicFactory.java new file mode 100644 index 000000000..134699c92 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicFactory.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.intrinsics.gc; + +import org.teavm.model.MethodReference; + +public interface WasmGCIntrinsicFactory { + WasmGCIntrinsic createIntrinsic(MethodReference methodRef, WasmGCIntrinsicFactoryContext context); +} 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 new file mode 100644 index 000000000..3bda398ea --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/gc/WasmGCIntrinsicFactoryContext.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.intrinsics.gc; + +import org.teavm.model.ClassReaderSource; + +public interface WasmGCIntrinsicFactoryContext { + ClassReaderSource classes(); +} 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 29cd8f21d..5dd989000 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,13 +22,20 @@ 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.model.ClassReaderSource; import org.teavm.model.MethodReference; import org.teavm.model.ValueType; public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { - private Map intrinsics = new HashMap<>(); + private Map intrinsics = new HashMap<>(); + private List factories; + private ClassReaderSource classes; - public WasmGCIntrinsics() { + public WasmGCIntrinsics(ClassReaderSource classes, List factories, + Map customIntrinsics) { + this.classes = classes; + this.factories = List.copyOf(factories); + factories = List.copyOf(factories); fillWasmRuntime(); fillObject(); fillClass(); @@ -37,49 +44,50 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { fillFloat(); fillDouble(); fillArray(); + for (var entry : customIntrinsics.entrySet()) { + add(entry.getKey(), entry.getValue()); + } } private void fillWasmRuntime() { var intrinsic = new WasmRuntimeIntrinsic(); for (var cls : List.of(int.class, long.class, float.class, double.class)) { - intrinsics.put(new MethodReference(WasmRuntime.class, "lt", cls, cls, boolean.class), intrinsic); - intrinsics.put(new MethodReference(WasmRuntime.class, "gt", cls, cls, boolean.class), intrinsic); + add(new MethodReference(WasmRuntime.class, "lt", cls, cls, boolean.class), intrinsic); + add(new MethodReference(WasmRuntime.class, "gt", cls, cls, boolean.class), intrinsic); } for (var cls : List.of(int.class, long.class)) { - intrinsics.put(new MethodReference(WasmRuntime.class, "ltu", cls, cls, boolean.class), intrinsic); - intrinsics.put(new MethodReference(WasmRuntime.class, "gtu", cls, cls, boolean.class), intrinsic); + add(new MethodReference(WasmRuntime.class, "ltu", cls, cls, boolean.class), intrinsic); + add(new MethodReference(WasmRuntime.class, "gtu", cls, cls, boolean.class), intrinsic); } for (var cls : List.of(float.class, double.class)) { - intrinsics.put(new MethodReference(WasmRuntime.class, "min", cls, cls, cls), intrinsic); - intrinsics.put(new MethodReference(WasmRuntime.class, "max", cls, cls, cls), intrinsic); + add(new MethodReference(WasmRuntime.class, "min", cls, cls, cls), intrinsic); + add(new MethodReference(WasmRuntime.class, "max", cls, cls, cls), intrinsic); } } private void fillObject() { - var objectIntrinsics = new ObjectIntrinsic(); - intrinsics.put(new MethodReference(Object.class, "getClass", Class.class), objectIntrinsics); - intrinsics.put(new MethodReference(Object.class.getName(), "getMonitor", - ValueType.object("java.lang.Object$Monitor")), objectIntrinsics); - intrinsics.put(new MethodReference(Object.class.getName(), "setMonitor", - ValueType.object("java.lang.Object$Monitor"), ValueType.VOID), objectIntrinsics); - intrinsics.put(new MethodReference(Object.class.getName(), "wasmGCIdentity", ValueType.INTEGER), - objectIntrinsics); - intrinsics.put(new MethodReference(Object.class.getName(), "setWasmGCIdentity", ValueType.INTEGER, - ValueType.VOID), objectIntrinsics); + var intrinsic = new ObjectIntrinsic(); + add(new MethodReference(Object.class, "getClass", Class.class), intrinsic); + add(new MethodReference(Object.class.getName(), "getMonitor", + ValueType.object("java.lang.Object$Monitor")), intrinsic); + add(new MethodReference(Object.class.getName(), "setMonitor", + ValueType.object("java.lang.Object$Monitor"), ValueType.VOID), intrinsic); + add(new MethodReference(Object.class.getName(), "wasmGCIdentity", ValueType.INTEGER), intrinsic); + add(new MethodReference(Object.class.getName(), "setWasmGCIdentity", ValueType.INTEGER, + ValueType.VOID), intrinsic); } private void fillClass() { var intrinsic = new ClassIntrinsics(); - intrinsics.put(new MethodReference(Class.class, "getComponentType", Class.class), intrinsic); - intrinsics.put(new MethodReference(Class.class, "getNameImpl", String.class), intrinsic); - intrinsics.put(new MethodReference(Class.class, "setNameImpl", String.class, void.class), intrinsic); + add(new MethodReference(Class.class, "getComponentType", Class.class), intrinsic); + add(new MethodReference(Class.class, "getNameImpl", String.class), intrinsic); + add(new MethodReference(Class.class, "setNameImpl", String.class, void.class), intrinsic); } private void fillSystem() { - intrinsics.put(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class, + add(new MethodReference(System.class, "arraycopy", Object.class, int.class, Object.class, int.class, int.class, void.class), new SystemArrayCopyIntrinsic()); - intrinsics.put(new MethodReference(System.class, "currentTimeMillis", long.class), - new SystemIntrinsic()); + add(new MethodReference(System.class, "currentTimeMillis", long.class), new SystemIntrinsic()); } private void fillLongAndInteger() { @@ -89,42 +97,70 @@ public class WasmGCIntrinsics implements WasmGCIntrinsicProvider { private void fillIntNum(Class javaClass, Class wrapperClass, WasmIntType wasmType) { var intrinsic = new IntNumIntrinsic(javaClass, wasmType); - intrinsics.put(new MethodReference(wrapperClass, "divideUnsigned", javaClass, javaClass, javaClass), - intrinsic); - intrinsics.put(new MethodReference(wrapperClass, "remainderUnsigned", javaClass, javaClass, javaClass), - intrinsic); - intrinsics.put(new MethodReference(wrapperClass, "compareUnsigned", javaClass, javaClass, int.class), - intrinsic); + add(new MethodReference(wrapperClass, "divideUnsigned", javaClass, javaClass, javaClass), intrinsic); + add(new MethodReference(wrapperClass, "remainderUnsigned", javaClass, javaClass, javaClass), intrinsic); + add(new MethodReference(wrapperClass, "compareUnsigned", javaClass, javaClass, int.class), intrinsic); } private void fillFloat() { var intrinsic = new FloatIntrinsic(); - intrinsics.put(new MethodReference(Float.class, "getNaN", float.class), intrinsic); - intrinsics.put(new MethodReference(Float.class, "isNaN", float.class, boolean.class), intrinsic); - intrinsics.put(new MethodReference(Float.class, "isInfinite", float.class, boolean.class), intrinsic); - intrinsics.put(new MethodReference(Float.class, "isFinite", float.class, boolean.class), intrinsic); - intrinsics.put(new MethodReference(Float.class, "floatToRawIntBits", float.class, int.class), intrinsic); - intrinsics.put(new MethodReference(Float.class, "intBitsToFloat", int.class, float.class), intrinsic); + add(new MethodReference(Float.class, "getNaN", float.class), intrinsic); + add(new MethodReference(Float.class, "isNaN", float.class, boolean.class), intrinsic); + add(new MethodReference(Float.class, "isInfinite", float.class, boolean.class), intrinsic); + add(new MethodReference(Float.class, "isFinite", float.class, boolean.class), intrinsic); + add(new MethodReference(Float.class, "floatToRawIntBits", float.class, int.class), intrinsic); + add(new MethodReference(Float.class, "intBitsToFloat", int.class, float.class), intrinsic); } private void fillDouble() { var intrinsic = new DoubleIntrinsic(); - intrinsics.put(new MethodReference(Double.class, "getNaN", double.class), intrinsic); - intrinsics.put(new MethodReference(Double.class, "isNaN", double.class, boolean.class), intrinsic); - intrinsics.put(new MethodReference(Double.class, "isInfinite", double.class, boolean.class), intrinsic); - intrinsics.put(new MethodReference(Double.class, "isFinite", double.class, boolean.class), intrinsic); - intrinsics.put(new MethodReference(Double.class, "doubleToRawLongBits", double.class, long.class), intrinsic); - intrinsics.put(new MethodReference(Double.class, "longBitsToDouble", long.class, double.class), intrinsic); + add(new MethodReference(Double.class, "getNaN", double.class), intrinsic); + add(new MethodReference(Double.class, "isNaN", double.class, boolean.class), intrinsic); + add(new MethodReference(Double.class, "isInfinite", double.class, boolean.class), intrinsic); + add(new MethodReference(Double.class, "isFinite", double.class, boolean.class), intrinsic); + add(new MethodReference(Double.class, "doubleToRawLongBits", double.class, long.class), intrinsic); + add(new MethodReference(Double.class, "longBitsToDouble", long.class, double.class), intrinsic); } private void fillArray() { var intrinsic = new ArrayIntrinsic(); - intrinsics.put(new MethodReference(Array.class, "getLength", Object.class, int.class), intrinsic); - intrinsics.put(new MethodReference(Array.class, "getImpl", Object.class, int.class, Object.class), intrinsic); + add(new MethodReference(Array.class, "getLength", Object.class, int.class), intrinsic); + add(new MethodReference(Array.class, "getImpl", Object.class, int.class, Object.class), intrinsic); + } + + private void add(MethodReference methodRef, WasmGCIntrinsic intrinsic) { + intrinsics.put(methodRef, new IntrinsicContainer(intrinsic)); } @Override public WasmGCIntrinsic get(MethodReference method) { - return intrinsics.get(method); + var result = intrinsics.get(method); + if (result == null) { + WasmGCIntrinsic intrinsic = null; + for (var factory : factories) { + intrinsic = factory.createIntrinsic(method, factoryContext); + if (intrinsic != null) { + break; + } + } + intrinsics.put(method, new IntrinsicContainer(intrinsic)); + result = new IntrinsicContainer(intrinsic); + } + return result.intrinsic; } + + static class IntrinsicContainer { + final WasmGCIntrinsic intrinsic; + + IntrinsicContainer(WasmGCIntrinsic intrinsic) { + this.intrinsic = intrinsic; + } + } + + private final WasmGCIntrinsicFactoryContext factoryContext = new WasmGCIntrinsicFactoryContext() { + @Override + public ClassReaderSource classes() { + return classes; + } + }; } diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmArray.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmArray.java index 746706c51..6564d968d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmArray.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmArray.java @@ -20,6 +20,7 @@ import java.util.function.Supplier; public class WasmArray extends WasmCompositeType { private WasmStorageType elementType; + private boolean immutable; private Supplier elementTypeSupplier; public WasmArray(String name, WasmStorageType elementType) { @@ -40,6 +41,14 @@ public class WasmArray extends WasmCompositeType { return elementType; } + public boolean isImmutable() { + return immutable; + } + + public void setImmutable(boolean immutable) { + this.immutable = immutable; + } + @Override public void acceptVisitor(WasmCompositeTypeVisitor visitor) { visitor.visit(this); diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmField.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmField.java index 464f3fcef..08e445a4c 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmField.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmField.java @@ -22,6 +22,7 @@ public class WasmField { int index; private String name; private WasmStorageType type; + private boolean immutable; public WasmField(WasmStorageType type, String name) { this(type); @@ -56,6 +57,14 @@ public class WasmField { this.name = name; } + public boolean isImmutable() { + return immutable; + } + + public void setImmutable(boolean immutable) { + this.immutable = immutable; + } + public int getIndex() { if (structure == null) { return -1; diff --git a/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java b/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java index 825ba384a..fa765b644 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/WasmType.java @@ -59,6 +59,7 @@ public abstract class WasmType { public static abstract class Reference extends WasmType { public static final SpecialReference FUNC = SpecialReferenceKind.FUNC.asType(); public static final SpecialReference ANY = SpecialReferenceKind.ANY.asType(); + public static final SpecialReference EQ = SpecialReferenceKind.EQ.asType(); public static final SpecialReference EXTERN = SpecialReferenceKind.EXTERN.asType(); public static final SpecialReference STRUCT = SpecialReferenceKind.STRUCT.asType(); public static final SpecialReference ARRAY = SpecialReferenceKind.ARRAY.asType(); @@ -96,6 +97,7 @@ public abstract class WasmType { public enum SpecialReferenceKind { FUNC, ANY, + EQ, EXTERN, STRUCT, ARRAY, diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmArrayNewFixed.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmArrayNewFixed.java new file mode 100644 index 000000000..692691c62 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmArrayNewFixed.java @@ -0,0 +1,47 @@ +/* + * 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.model.expression; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import org.teavm.backend.wasm.model.WasmArray; + +public class WasmArrayNewFixed extends WasmExpression { + private WasmArray type; + private List elements = new ArrayList<>(); + + public WasmArrayNewFixed(WasmArray type) { + this.type = Objects.requireNonNull(type); + } + + public WasmArray getType() { + return type; + } + + public void setType(WasmArray type) { + this.type = Objects.requireNonNull(type); + } + + public List getElements() { + return elements; + } + + @Override + public void acceptVisitor(WasmExpressionVisitor visitor) { + visitor.visit(this); + } +} diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java index ab7a7eae5..226ccbb67 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmDefaultExpressionVisitor.java @@ -283,6 +283,13 @@ public class WasmDefaultExpressionVisitor implements WasmExpressionVisitor { expression.getLength().acceptVisitor(this); } + @Override + public void visit(WasmArrayNewFixed expression) { + for (var element : expression.getElements()) { + element.acceptVisitor(this); + } + } + @Override public void visit(WasmArrayGet expression) { expression.getInstance().acceptVisitor(this); diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java index e05f65d44..da7894653 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmExpressionVisitor.java @@ -108,6 +108,8 @@ public interface WasmExpressionVisitor { void visit(WasmArrayNewDefault expression); + void visit(WasmArrayNewFixed expression); + void visit(WasmArrayGet expression); void visit(WasmArraySet expression); diff --git a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java index 2c634203b..021750804 100644 --- a/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/model/expression/WasmReplacingExpressionVisitor.java @@ -336,6 +336,11 @@ public class WasmReplacingExpressionVisitor implements WasmExpressionVisitor { expression.setLength(mapper.apply(expression.getLength())); } + @Override + public void visit(WasmArrayNewFixed expression) { + replaceExpressions(expression.getElements()); + } + @Override public void visit(WasmArrayGet expression) { expression.getInstance().acceptVisitor(this); diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java index f1f065feb..03ebfbc16 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeListener.java @@ -156,6 +156,9 @@ public interface CodeListener { default void arrayNewDefault(int typeIndex) { } + default void arrayNewFixed(int typeIndex, int size) { + } + default void arrayGet(WasmSignedType signedType, int typeIndex) { } diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/CodeParser.java b/core/src/main/java/org/teavm/backend/wasm/parser/CodeParser.java index 59c496c80..a121193a8 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/CodeParser.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/CodeParser.java @@ -660,6 +660,9 @@ public class CodeParser extends BaseSectionParser { case 7: codeListener.arrayNewDefault(readLEB()); return true; + case 8: + codeListener.arrayNewFixed(readLEB(), readLEB()); + return true; case 11: codeListener.arrayGet(null, readLEB()); diff --git a/core/src/main/java/org/teavm/backend/wasm/parser/WasmBinaryReader.java b/core/src/main/java/org/teavm/backend/wasm/parser/WasmBinaryReader.java index e2a2b1ea0..cd4a6d83e 100644 --- a/core/src/main/java/org/teavm/backend/wasm/parser/WasmBinaryReader.java +++ b/core/src/main/java/org/teavm/backend/wasm/parser/WasmBinaryReader.java @@ -92,6 +92,8 @@ public class WasmBinaryReader { return special(WasmType.SpecialReferenceKind.EXTERN, nullable); case 0x6E: return special(WasmType.SpecialReferenceKind.ANY, nullable); + case 0x6D: + return special(WasmType.SpecialReferenceKind.EQ, nullable); case 0x6C: return special(WasmType.SpecialReferenceKind.I31, nullable); case 0x6B: diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java index bc907bcfe..53b0e4845 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryRenderingVisitor.java @@ -31,6 +31,7 @@ import org.teavm.backend.wasm.model.expression.WasmArrayCopy; import org.teavm.backend.wasm.model.expression.WasmArrayGet; import org.teavm.backend.wasm.model.expression.WasmArrayLength; import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault; +import org.teavm.backend.wasm.model.expression.WasmArrayNewFixed; import org.teavm.backend.wasm.model.expression.WasmArraySet; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; @@ -1155,6 +1156,19 @@ class WasmBinaryRenderingVisitor implements WasmExpressionVisitor { popLocation(); } + @Override + public void visit(WasmArrayNewFixed expression) { + pushLocation(expression); + for (var element : expression.getElements()) { + element.acceptVisitor(this); + } + writer.writeByte(0xfb); + writer.writeByte(8); + writer.writeLEB(module.types.indexOf(expression.getType())); + writer.writeLEB(expression.getElements().size()); + popLocation(); + } + @Override public void visit(WasmArrayGet expression) { pushLocation(expression); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java index a0ac6b624..fafab3939 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmBinaryWriter.java @@ -65,6 +65,9 @@ public class WasmBinaryWriter { case ANY: writeByte(0x6e); break; + case EQ: + writeByte(0x6d); + break; case EXTERN: writeByte(0x6f); break; diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java index 87d8042d4..6ffde3ba2 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCRenderingVisitor.java @@ -31,6 +31,7 @@ import org.teavm.backend.wasm.model.expression.WasmArrayCopy; import org.teavm.backend.wasm.model.expression.WasmArrayGet; import org.teavm.backend.wasm.model.expression.WasmArrayLength; import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault; +import org.teavm.backend.wasm.model.expression.WasmArrayNewFixed; import org.teavm.backend.wasm.model.expression.WasmArraySet; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; @@ -1203,6 +1204,11 @@ class WasmCRenderingVisitor implements WasmExpressionVisitor { unsupported(); } + @Override + public void visit(WasmArrayNewFixed expression) { + unsupported(); + } + @Override public void visit(WasmArrayGet expression) { unsupported(); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java index 746e7b500..aad9e3a97 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmCompositeTypeBinaryRenderer.java @@ -44,7 +44,7 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor section.writeLEB(type.getFields().size()); for (var fieldType : type.getFields()) { writeStorageType(fieldType.getType()); - section.writeLEB(0x01); // mutable + section.writeLEB(fieldType.isImmutable() ? 0 : 1); } } @@ -52,7 +52,7 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor public void visit(WasmArray type) { section.writeByte(0x5E); writeStorageType(type.getElementType()); - section.writeLEB(0x01); // mutable + section.writeLEB(type.isImmutable() ? 0 : 1); } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java index e634b3d9a..d0e341d4a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmRenderingVisitor.java @@ -27,6 +27,7 @@ import org.teavm.backend.wasm.model.expression.WasmArrayCopy; import org.teavm.backend.wasm.model.expression.WasmArrayGet; import org.teavm.backend.wasm.model.expression.WasmArrayLength; import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault; +import org.teavm.backend.wasm.model.expression.WasmArrayNewFixed; import org.teavm.backend.wasm.model.expression.WasmArraySet; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; @@ -765,6 +766,17 @@ class WasmRenderingVisitor implements WasmExpressionVisitor { close(); } + @Override + public void visit(WasmArrayNewFixed expression) { + open().append("array.new_fixed"); + append(" ").append(typeName(expression.getType())); + append(" ").append(Integer.toString(expression.getElements().size())); + for (var element : expression.getElements()) { + line(element); + } + close(); + } + @Override public void visit(WasmArrayGet expression) { open(); diff --git a/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java b/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java index ab0abcb04..ebf6903b5 100644 --- a/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java +++ b/core/src/main/java/org/teavm/backend/wasm/render/WasmTypeInference.java @@ -20,6 +20,7 @@ import org.teavm.backend.wasm.model.expression.WasmArrayCopy; import org.teavm.backend.wasm.model.expression.WasmArrayGet; import org.teavm.backend.wasm.model.expression.WasmArrayLength; import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault; +import org.teavm.backend.wasm.model.expression.WasmArrayNewFixed; import org.teavm.backend.wasm.model.expression.WasmArraySet; import org.teavm.backend.wasm.model.expression.WasmBlock; import org.teavm.backend.wasm.model.expression.WasmBranch; @@ -311,6 +312,11 @@ public class WasmTypeInference implements WasmExpressionVisitor { result = expression.getType().getReference(); } + @Override + public void visit(WasmArrayNewFixed expression) { + result = expression.getType().getReference(); + } + @Override public void visit(WasmArrayGet expression) { result = expression.getType().getElementType().asUnpackedType(); diff --git a/platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java b/platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java index 802bede5a..8c8c15743 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java +++ b/platform/src/main/java/org/teavm/platform/plugin/DefaultMetadataGeneratorContext.java @@ -26,14 +26,14 @@ import org.teavm.platform.metadata.ResourceArray; import org.teavm.platform.metadata.ResourceMap; import org.teavm.platform.metadata.StaticFieldResource; -class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { +public class DefaultMetadataGeneratorContext implements MetadataGeneratorContext { private ClassReaderSource classSource; private ClassLoader classLoader; private Properties properties; private BuildTimeResourceProxyBuilder proxyBuilder; private ServiceRepository services; - DefaultMetadataGeneratorContext(ClassReaderSource classSource, ClassLoader classLoader, + public DefaultMetadataGeneratorContext(ClassReaderSource classSource, ClassLoader classLoader, Properties properties, ServiceRepository services) { this.classSource = classSource; this.classLoader = classLoader; diff --git a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java index 788f0f32b..dbe4a7d62 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java +++ b/platform/src/main/java/org/teavm/platform/plugin/PlatformPlugin.java @@ -23,8 +23,10 @@ import org.teavm.backend.c.intrinsic.Intrinsic; import org.teavm.backend.c.intrinsic.IntrinsicContext; import org.teavm.backend.javascript.TeaVMJavaScriptHost; import org.teavm.backend.wasm.TeaVMWasmHost; +import org.teavm.backend.wasm.gc.TeaVMWasmGCHost; import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.interop.Async; import org.teavm.interop.PlatformMarker; @@ -34,6 +36,18 @@ import org.teavm.model.MethodReference; import org.teavm.platform.Platform; import org.teavm.platform.PlatformQueue; import org.teavm.platform.metadata.MetadataGenerator; +import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceArray; +import org.teavm.platform.metadata.ResourceMap; +import org.teavm.platform.plugin.wasmgc.ResourceCustomTypeMapper; +import org.teavm.platform.plugin.wasmgc.ResourceDependencySupport; +import org.teavm.platform.plugin.wasmgc.ResourceInterfaceToClassTransformer; +import org.teavm.platform.plugin.wasmgc.ResourceMapEntry; +import org.teavm.platform.plugin.wasmgc.ResourceMapHelper; +import org.teavm.platform.plugin.wasmgc.WasmGCResourceArrayIntrinsic; +import org.teavm.platform.plugin.wasmgc.WasmGCResourceMapHelperIntrinsic; +import org.teavm.platform.plugin.wasmgc.WasmGCResourceMetadataIntrinsicFactory; +import org.teavm.platform.plugin.wasmgc.WasmGCResourceMethodIntrinsicFactory; import org.teavm.vm.TeaVMPluginUtil; import org.teavm.vm.spi.TeaVMHost; import org.teavm.vm.spi.TeaVMPlugin; @@ -44,8 +58,8 @@ public class PlatformPlugin implements TeaVMPlugin, MetadataRegistration { @Override public void install(TeaVMHost host) { - host.add(metadataTransformer); if (host.getExtension(TeaVMJavaScriptHost.class) != null) { + host.add(metadataTransformer); host.add(new ResourceTransformer()); host.add(new ResourceAccessorTransformer(host)); host.add(new ResourceAccessorDependencyListener()); @@ -67,55 +81,23 @@ public class PlatformPlugin implements TeaVMPlugin, MetadataRegistration { metadataGeneratorConsumers.add((method, constructor, generator) -> jsHost.add(method, new MetadataProviderNativeGenerator(generator, constructor))); - } else if (!isBootstrap()) { host.add(new StringAmplifierTransformer()); } if (!isBootstrap()) { - TeaVMWasmHost wasmHost = host.getExtension(TeaVMWasmHost.class); + var wasmHost = host.getExtension(TeaVMWasmHost.class); if (wasmHost != null) { - host.add(new ResourceLowLevelTransformer()); - metadataGeneratorConsumers.add((constructor, method, generator) -> { - wasmHost.add(ctx -> new MetadataIntrinsic(ctx.getClassSource(), ctx.getClassLoader(), - ctx.getServices(), ctx.getProperties(), constructor, method, generator)); - }); - wasmHost.add(ctx -> new ResourceReadIntrinsic(ctx.getClassSource(), ctx.getClassLoader())); - - wasmHost.add(ctx -> new WasmIntrinsic() { - @Override - public boolean isApplicable(MethodReference methodReference) { - return methodReference.getClassName().equals(StringAmplifier.class.getName()); - } - - @Override - public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { - return manager.generate(invocation.getArguments().get(0)); - } - }); + installWasm(host, wasmHost); } - TeaVMCHost cHost = host.getExtension(TeaVMCHost.class); + var cHost = host.getExtension(TeaVMCHost.class); if (cHost != null) { - host.add(new ResourceLowLevelTransformer()); - MetadataCIntrinsic metadataCIntrinsic = new MetadataCIntrinsic(); - cHost.addGenerator(ctx -> { - metadataCIntrinsic.init(ctx.getClassSource(), ctx.getClassLoader(), - ctx.getServices(), ctx.getProperties()); - return metadataCIntrinsic; - }); - metadataGeneratorConsumers.add(metadataCIntrinsic::addGenerator); - cHost.addIntrinsic(ctx -> new ResourceReadCIntrinsic(ctx.getClassSource())); - cHost.addIntrinsic(ctx -> new Intrinsic() { - @Override - public boolean canHandle(MethodReference method) { - return method.getClassName().equals(StringAmplifier.class.getName()); - } + installC(host, cHost); + } - @Override - public void apply(IntrinsicContext context, InvocationExpr invocation) { - context.emit(invocation.getArguments().get(0)); - } - }); + var wasmGCHost = host.getExtension(TeaVMWasmGCHost.class); + if (wasmGCHost != null) { + installWasmGC(host, wasmGCHost); } } @@ -131,6 +113,91 @@ public class PlatformPlugin implements TeaVMPlugin, MetadataRegistration { host.registerService(MetadataRegistration.class, this); } + private void installWasm(TeaVMHost host, TeaVMWasmHost wasmHost) { + host.add(metadataTransformer); + host.add(new StringAmplifierTransformer()); + host.add(new ResourceLowLevelTransformer()); + metadataGeneratorConsumers.add((constructor, method, generator) -> { + wasmHost.add(ctx -> new MetadataIntrinsic(ctx.getClassSource(), ctx.getClassLoader(), + ctx.getServices(), ctx.getProperties(), constructor, method, generator)); + }); + wasmHost.add(ctx -> new ResourceReadIntrinsic(ctx.getClassSource(), ctx.getClassLoader())); + + wasmHost.add(ctx -> new WasmIntrinsic() { + @Override + public boolean isApplicable(MethodReference methodReference) { + return methodReference.getClassName().equals(StringAmplifier.class.getName()); + } + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { + return manager.generate(invocation.getArguments().get(0)); + } + }); + } + + private void installC(TeaVMHost host, TeaVMCHost cHost) { + host.add(metadataTransformer); + host.add(new StringAmplifierTransformer()); + host.add(new ResourceLowLevelTransformer()); + MetadataCIntrinsic metadataCIntrinsic = new MetadataCIntrinsic(); + cHost.addGenerator(ctx -> { + metadataCIntrinsic.init(ctx.getClassSource(), ctx.getClassLoader(), + ctx.getServices(), ctx.getProperties()); + return metadataCIntrinsic; + }); + metadataGeneratorConsumers.add(metadataCIntrinsic::addGenerator); + cHost.addIntrinsic(ctx -> new ResourceReadCIntrinsic(ctx.getClassSource())); + cHost.addIntrinsic(ctx -> new Intrinsic() { + @Override + public boolean canHandle(MethodReference method) { + return method.getClassName().equals(StringAmplifier.class.getName()); + } + + @Override + public void apply(IntrinsicContext context, InvocationExpr invocation) { + context.emit(invocation.getArguments().get(0)); + } + }); + } + + private void installWasmGC(TeaVMHost host, TeaVMWasmGCHost wasmGCHost) { + WasmGCIntrinsic amplifierIntrinsic = + (invocation, context) -> context.generate(invocation.getArguments().get(0)); + wasmGCHost.addIntrinsic(new MethodReference(StringAmplifier.class, "amplify", String.class, String.class), + amplifierIntrinsic); + wasmGCHost.addIntrinsic(new MethodReference(StringAmplifier.class, "amplifyArray", + String[].class, String[].class), amplifierIntrinsic); + + host.add(new ResourceInterfaceToClassTransformer()); + var dependencySupport = new ResourceDependencySupport(); + host.add(dependencySupport); + wasmGCHost.addCustomTypeMapperFactory(context -> new ResourceCustomTypeMapper( + context.module(), + context.typeMapper(), + context.classes(), + context.names() + )); + wasmGCHost.addIntrinsicFactory(new WasmGCResourceMethodIntrinsicFactory()); + var metadataIntrinsicFactory = new WasmGCResourceMetadataIntrinsicFactory(host.getProperties(), host); + wasmGCHost.addIntrinsicFactory(metadataIntrinsicFactory); + + var arrayIntrinsic = new WasmGCResourceArrayIntrinsic(); + wasmGCHost.addIntrinsic(new MethodReference(ResourceArray.class, "size", int.class), arrayIntrinsic); + wasmGCHost.addIntrinsic(new MethodReference(ResourceArray.class, "get", int.class, Resource.class), + arrayIntrinsic); + var mapHelperIntrinsic = new WasmGCResourceMapHelperIntrinsic(); + wasmGCHost.addIntrinsic(new MethodReference(ResourceMapHelper.class, "entryCount", ResourceMap.class, + int.class), mapHelperIntrinsic); + wasmGCHost.addIntrinsic(new MethodReference(ResourceMapHelper.class, "entry", ResourceMap.class, int.class, + ResourceMapEntry.class), mapHelperIntrinsic); + + metadataGeneratorConsumers.add((constructor, target, generator) -> { + dependencySupport.addMetadataMethod(target); + metadataIntrinsicFactory.addGenerator(target, generator); + }); + } + @Override public void register(MethodReference method, MetadataGenerator generator) { MethodReference constructor = new MethodReference(method.getClassName(), method.getName() + "$$create", diff --git a/platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java b/platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java index be7101645..51df6b8ef 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java +++ b/platform/src/main/java/org/teavm/platform/plugin/ResourceProgramTransformer.java @@ -78,11 +78,7 @@ class ResourceProgramTransformer { private void removeCastToResource(CastInstruction cast) { if (!cast.isWeak() && hierarchy.isSuperType(RESOURCE, cast.getTargetType(), false)) { - AssignInstruction assign = new AssignInstruction(); - assign.setReceiver(cast.getReceiver()); - assign.setAssignee(cast.getValue()); - assign.setLocation(cast.getLocation()); - cast.replace(assign); + cast.setWeak(true); } } diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/FieldMarker.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/FieldMarker.java new file mode 100644 index 000000000..6ac861321 --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/FieldMarker.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.platform.plugin.wasmgc; + +@interface FieldMarker { + String value(); + + int index(); +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/MetadataIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/MetadataIntrinsic.java new file mode 100644 index 000000000..1502fe376 --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/MetadataIntrinsic.java @@ -0,0 +1,216 @@ +/* + * 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.platform.plugin.wasmgc; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.HashMap; +import java.util.List; +import java.util.Properties; +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext; +import org.teavm.backend.wasm.model.WasmArray; +import org.teavm.backend.wasm.model.WasmGlobal; +import org.teavm.backend.wasm.model.WasmStructure; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmArrayNewFixed; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmFloat32Constant; +import org.teavm.backend.wasm.model.expression.WasmFloat64Constant; +import org.teavm.backend.wasm.model.expression.WasmGetGlobal; +import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +import org.teavm.backend.wasm.model.expression.WasmInt64Constant; +import org.teavm.backend.wasm.model.expression.WasmNullConstant; +import org.teavm.backend.wasm.model.expression.WasmStructNew; +import org.teavm.common.HashUtils; +import org.teavm.common.ServiceRepository; +import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; +import org.teavm.platform.metadata.MetadataGenerator; +import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceArray; +import org.teavm.platform.metadata.ResourceMap; +import org.teavm.platform.plugin.DefaultMetadataGeneratorContext; +import org.teavm.platform.plugin.ResourceAccessorType; +import org.teavm.platform.plugin.ResourceTypeDescriptor; +import org.teavm.platform.plugin.ResourceTypeDescriptorProvider; + +class MetadataIntrinsic implements WasmGCIntrinsic { + private Properties properties; + private ServiceRepository services; + private MetadataGenerator generator; + private WasmGlobal global; + + MetadataIntrinsic(Properties properties, ServiceRepository services, + MetadataGenerator generator) { + this.properties = properties; + this.services = services; + this.generator = generator; + } + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { + var global = getGlobal(invocation.getMethod(), context); + return new WasmGetGlobal(global); + } + + private WasmGlobal getGlobal(MethodReference method, WasmGCIntrinsicContext context) { + if (global == null) { + var genContext = new DefaultMetadataGeneratorContext(context.hierarchy().getClassSource(), + context.classLoader(), properties, services); + var metadata = generator.generateMetadata(genContext, method); + var type = context.typeMapper().mapType(method.getReturnType()); + var name = context.names().topLevel(context.names().suggestForMethod(method)); + var initialValue = generateMetadata(context, metadata, type); + global = new WasmGlobal(name, type, initialValue); + context.module().globals.add(global); + } + return global; + } + + private WasmExpression generateMetadata(WasmGCIntrinsicContext context, Object value, WasmType expectedType) { + if (value == null) { + return new WasmNullConstant((WasmType.Reference) expectedType); + } else if (value instanceof String) { + return new WasmGetGlobal(context.strings().getStringConstant((String) value).global); + } else if (value instanceof Boolean) { + return new WasmInt32Constant((Boolean) value ? 1 : 0); + } else if (value instanceof Integer) { + return new WasmInt32Constant((Integer) value); + } else if (value instanceof Long) { + return new WasmInt64Constant((Long) value); + } else if (value instanceof Byte) { + return new WasmInt32Constant((Byte) value); + } else if (value instanceof Short) { + return new WasmInt32Constant((Short) value); + } else if (value instanceof Character) { + return new WasmInt32Constant((Character) value); + } else if (value instanceof Float) { + return new WasmFloat32Constant((Float) value); + } else if (value instanceof Double) { + return new WasmFloat64Constant((Double) value); + } else if (value instanceof ResourceArray) { + var array = (ResourceArray) value; + var type = (WasmType.CompositeReference) context.typeMapper().mapType( + ValueType.object(ResourceArray.class.getName())); + var arrayType = (WasmArray) type.composite; + var result = new WasmArrayNewFixed(arrayType); + for (var i = 0; i < array.size(); ++i) { + result.getElements().add(generateMetadata(context, array.get(i), + arrayType.getElementType().asUnpackedType())); + } + return result; + } else if (value instanceof ResourceMap) { + return generateMapResource(context, (ResourceMap) value); + } else if (value instanceof ResourceTypeDescriptorProvider) { + var descriptor = ((ResourceTypeDescriptorProvider) value).getDescriptor(); + return generateObjectResource(context, value, descriptor); + } else { + throw new IllegalArgumentException("Don't know how to write resource: " + value); + } + } + + private WasmExpression generateMapResource(WasmGCIntrinsicContext context, ResourceMap map) { + var hashTable = HashUtils.createHashTable(map.keys()); + var type = (WasmType.CompositeReference) context.typeMapper().mapType( + ValueType.object(ResourceMap.class.getName())); + var arrayType = (WasmArray) type.composite; + var entryType = (WasmType.CompositeReference) context.typeMapper().mapType( + ValueType.object(ResourceMapEntry.class.getName())); + var entryStruct = (WasmStructure) entryType.composite; + + var expr = new WasmArrayNewFixed(arrayType); + for (var key : hashTable) { + if (key == null) { + expr.getElements().add(new WasmNullConstant(entryType)); + } else { + var value = map.get(key); + var wasmValue = generateMetadata(context, value, WasmType.Reference.EQ); + var entryExpr = new WasmStructNew(entryStruct); + var keyConstant = context.strings().getStringConstant(key); + entryExpr.getInitializers().add(new WasmGetGlobal(keyConstant.global)); + entryExpr.getInitializers().add(wasmValue); + expr.getElements().add(entryExpr); + } + } + return expr; + } + + private WasmExpression generateObjectResource(WasmGCIntrinsicContext context, Object value, + ResourceTypeDescriptor descriptor) { + var javaItf = descriptor.getRootInterface(); + var cls = context.hierarchy().getClassSource().get(javaItf.getName()); + var getterMap = new HashMap(); + for (var entry : descriptor.getMethods().entrySet()) { + if (entry.getValue().getType() == ResourceAccessorType.GETTER) { + getterMap.put(entry.getValue().getPropertyName(), entry.getKey()); + } + } + + var wasmType = (WasmType.CompositeReference) context.typeMapper().mapType(ValueType.object(cls.getName())); + var wasmStruct = (WasmStructure) wasmType.composite; + var expr = new WasmStructNew(wasmStruct); + for (var field : collectFields(context.hierarchy().getClassSource(), cls)) { + var getter = getterMap.get(field.name); + Object fieldValue; + try { + fieldValue = getter.invoke(value); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + expr.getInitializers().add(generateMetadata(context, fieldValue, + context.typeMapper().mapType(field.type))); + } + + return expr; + } + + private List collectFields(ClassReaderSource classes, ClassReader cls) { + var fields = new ArrayList(); + while (cls != null) { + for (var method : cls.getMethods()) { + var annot = method.getAnnotations().get(FieldMarker.class.getName()); + if (annot != null) { + fields.add(new FieldDescriptor(annot.getValue("index").getInt(), + annot.getValue("value").getString(), method.getResultType())); + } + } + cls = classes.get(cls.getParent()); + if (cls.getName().equals(Resource.class.getName())) { + break; + } + } + fields.sort(Comparator.comparingInt(f -> f.index)); + return fields; + } + + private static class FieldDescriptor { + final int index; + final String name; + final ValueType type; + + FieldDescriptor(int index, String name, ValueType type) { + this.index = index; + this.name = name; + this.type = type; + } + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceCustomTypeMapper.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceCustomTypeMapper.java new file mode 100644 index 000000000..3d2f5d49c --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceCustomTypeMapper.java @@ -0,0 +1,121 @@ +/* + * 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.platform.plugin.wasmgc; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.teavm.backend.wasm.generate.gc.WasmGCNameProvider; +import org.teavm.backend.wasm.generate.gc.classes.WasmGCCustomTypeMapper; +import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper; +import org.teavm.backend.wasm.model.WasmArray; +import org.teavm.backend.wasm.model.WasmField; +import org.teavm.backend.wasm.model.WasmModule; +import org.teavm.backend.wasm.model.WasmStructure; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.model.ClassReader; +import org.teavm.model.ClassReaderSource; +import org.teavm.model.ValueType; +import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceArray; +import org.teavm.platform.metadata.ResourceMap; + +public class ResourceCustomTypeMapper implements WasmGCCustomTypeMapper { + private WasmModule module; + private WasmGCTypeMapper typeMapper; + private ClassReaderSource classSource; + private WasmGCNameProvider names; + private Map structures = new HashMap<>(); + private WasmArray array; + private WasmArray mapArray; + + public ResourceCustomTypeMapper(WasmModule module, WasmGCTypeMapper typeMapper, ClassReaderSource classSource, + WasmGCNameProvider names) { + this.module = module; + this.typeMapper = typeMapper; + this.classSource = classSource; + this.names = names; + } + + @Override + public WasmType map(String className) { + var cls = classSource.get(className); + if (cls == null) { + return null; + } + if (cls.getAnnotations().get(ResourceMarker.class.getName()) == null) { + return null; + } + + if (className.equals(Resource.class.getName())) { + return WasmType.Reference.EQ; + } + if (className.equals(ResourceArray.class.getName())) { + if (array == null) { + array = new WasmArray(names.topLevel(names.suggestForClass(className)), + WasmType.Reference.EQ.asStorage()); + array.setImmutable(true); + module.types.add(array); + } + return array.getReference(); + } else if (className.equals(ResourceMap.class.getName())) { + if (mapArray == null) { + mapArray = new WasmArray(names.topLevel(names.suggestForClass(className)), + typeMapper.mapStorageType(ValueType.object(ResourceMapEntry.class.getName()))); + mapArray.setImmutable(true); + module.types.add(mapArray); + } + return mapArray.getReference(); + } else { + return getStructure(className).getReference(); + } + } + + private WasmStructure getStructure(String className) { + var struct = structures.get(className); + if (struct == null) { + var name = names.topLevel(names.suggestForClass(className)); + var cls = classSource.get(className); + struct = new WasmStructure(name, fields -> addFieldsFromClass(cls, fields)); + module.types.add(struct); + structures.put(className, struct); + if (!cls.getParent().equals(Resource.class.getName())) { + struct.setSupertype(getStructure(cls.getParent())); + } + } + return struct; + } + + private void addFieldsFromClass(ClassReader cls, List fields) { + if (!cls.getName().equals(Resource.class.getName())) { + var parentCls = classSource.get(cls.getParent()); + if (parentCls != null) { + addFieldsFromClass(parentCls, fields); + } + } + for (var method : cls.getMethods()) { + var annot = method.getAnnotations().get(FieldMarker.class.getName()); + if (annot == null) { + continue; + } + var fieldName = annot.getValue("value").getString(); + var fieldType = typeMapper.mapStorageType(method.getResultType()); + var field = new WasmField(fieldType, names.structureField(fieldName)); + field.setImmutable(true); + fields.add(field); + } + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceDependencySupport.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceDependencySupport.java new file mode 100644 index 000000000..89df2831d --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceDependencySupport.java @@ -0,0 +1,49 @@ +/* + * 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.platform.plugin.wasmgc; + +import java.util.HashSet; +import java.util.Set; +import org.teavm.dependency.AbstractDependencyListener; +import org.teavm.dependency.DependencyAgent; +import org.teavm.dependency.MethodDependency; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; + +public class ResourceDependencySupport extends AbstractDependencyListener { + private Set metadataMethods = new HashSet<>(); + + @Override + public void methodReached(DependencyAgent agent, MethodDependency method) { + var annot = method.getMethod().getAnnotations().get(FieldMarker.class.getName()); + if (annot != null) { + var type = method.getMethod().getResultType(); + if (type instanceof ValueType.Object) { + method.getResult().propagate(agent.getType(((ValueType.Object) type).getClassName())); + } + } + if (metadataMethods.contains(method.getMethod().getReference())) { + var resultType = method.getMethod().getResultType(); + if (resultType instanceof ValueType.Object) { + method.getResult().propagate(agent.getType(((ValueType.Object) resultType).getClassName())); + } + } + } + + public void addMetadataMethod(MethodReference method) { + metadataMethods.add(method); + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceInterfaceToClassTransformer.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceInterfaceToClassTransformer.java new file mode 100644 index 000000000..799430e41 --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceInterfaceToClassTransformer.java @@ -0,0 +1,153 @@ +/* + * 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.platform.plugin.wasmgc; + +import org.teavm.model.AnnotationHolder; +import org.teavm.model.AnnotationValue; +import org.teavm.model.ClassHierarchy; +import org.teavm.model.ClassHolder; +import org.teavm.model.ClassHolderTransformer; +import org.teavm.model.ClassHolderTransformerContext; +import org.teavm.model.ElementModifier; +import org.teavm.model.Program; +import org.teavm.model.ValueType; +import org.teavm.model.emit.ProgramEmitter; +import org.teavm.model.instructions.CastInstruction; +import org.teavm.model.instructions.InvocationType; +import org.teavm.model.instructions.InvokeInstruction; +import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceMap; + +public class ResourceInterfaceToClassTransformer implements ClassHolderTransformer { + private static final String RESOURCE_CLASS = Resource.class.getName(); + + @Override + public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) { + if (context.getHierarchy().isSuperType(RESOURCE_CLASS, cls.getName(), false) + && cls.hasModifier(ElementModifier.INTERFACE)) { + cls.getModifiers().remove(ElementModifier.INTERFACE); + cls.getModifiers().add(ElementModifier.ABSTRACT); + + int index = 0; + if (!cls.getInterfaces().isEmpty()) { + var parent = cls.getInterfaces().iterator().next(); + cls.getInterfaces().clear(); + cls.setParent(parent); + var parentCls = context.getHierarchy().getClassSource().get(parent); + if (parentCls != null) { + var resourceAnnot = parentCls.getAnnotations().get(ResourceMarker.class.getName()); + if (resourceAnnot != null) { + index = resourceAnnot.getValue("fieldCount").getInt(); + } + } + } + + for (var method : cls.getMethods()) { + method.getModifiers().remove(ElementModifier.ABSTRACT); + method.getModifiers().add(ElementModifier.NATIVE); + if (method.getName().startsWith("get") && method.getName().length() > 3 + && Character.isUpperCase(method.getName().charAt(3)) + && method.getResultType() != ValueType.VOID) { + var fieldName = Character.toLowerCase(method.getName().charAt(3)) + method.getName().substring(4); + var annot = new AnnotationHolder(FieldMarker.class.getName()); + annot.getValues().put("value", new AnnotationValue(fieldName)); + annot.getValues().put("index", new AnnotationValue(index++)); + method.getAnnotations().add(annot); + } else if (method.getName().startsWith("is") && method.getName().length() > 2 + && Character.isUpperCase(method.getName().charAt(2)) + && method.getResultType() == ValueType.BOOLEAN) { + var fieldName = Character.toLowerCase(method.getName().charAt(2)) + method.getName().substring(3); + var annot = new AnnotationHolder(FieldMarker.class.getName()); + annot.getValues().put("value", new AnnotationValue(fieldName)); + annot.getValues().put("index", new AnnotationValue(index++)); + method.getAnnotations().add(annot); + } + } + + if (cls.getName().equals(ResourceMap.class.getName())) { + transformResourceMapMethods(cls, context.getHierarchy()); + } + + var resourceAnnot = new AnnotationHolder(ResourceMarker.class.getName()); + resourceAnnot.getValues().put("fieldCount", new AnnotationValue(index)); + cls.getAnnotations().add(resourceAnnot); + } else { + for (var method : cls.getMethods()) { + if (method.getProgram() != null) { + transformResourceUsages(method.getProgram(), context); + } + } + } + } + + private void transformResourceMapMethods(ClassHolder cls, ClassHierarchy hierarchy) { + for (var method : cls.getMethods()) { + switch (method.getName()) { + case "has": { + method.getModifiers().remove(ElementModifier.NATIVE); + var pe = ProgramEmitter.create(method, hierarchy); + pe.invoke(ResourceMapHelper.class, "has", boolean.class, pe.var(0, ResourceMap.class), + pe.var(1, String.class)).returnValue(); + break; + } + case "get": { + method.getModifiers().remove(ElementModifier.NATIVE); + var pe = ProgramEmitter.create(method, hierarchy); + pe.invoke(ResourceMapHelper.class, "get", Resource.class, pe.var(0, ResourceMap.class), + pe.var(1, String.class)).returnValue(); + break; + } + case "keys": { + method.getModifiers().remove(ElementModifier.NATIVE); + var pe = ProgramEmitter.create(method, hierarchy); + pe.invoke(ResourceMapHelper.class, "keys", String[].class, pe.var(0, ResourceMap.class)) + .returnValue(); + break; + } + } + } + } + + private void transformResourceUsages(Program program, ClassHolderTransformerContext context) { + for (var block : program.getBasicBlocks()) { + for (var instruction : block) { + if (instruction instanceof CastInstruction) { + var cast = (CastInstruction) instruction; + if (cast.isWeak()) { + continue; + } + if (!(cast.getTargetType() instanceof ValueType.Object)) { + continue; + } + var className = ((ValueType.Object) cast.getTargetType()).getClassName(); + if (context.getHierarchy().isSuperType(RESOURCE_CLASS, className, false)) { + cast.setWeak(true); + } + } else if (instruction instanceof InvokeInstruction) { + var invoke = (InvokeInstruction) instruction; + if (invoke.getInstance() == null || invoke.getType() == InvocationType.SPECIAL) { + continue; + } + if (!context.getHierarchy().isSuperType(RESOURCE_CLASS, invoke.getMethod().getClassName(), + false)) { + continue; + } + invoke.setType(InvocationType.SPECIAL); + } + } + } + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMapEntry.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMapEntry.java new file mode 100644 index 000000000..4a2a9d484 --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMapEntry.java @@ -0,0 +1,24 @@ +/* + * 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.platform.plugin.wasmgc; + +import org.teavm.platform.metadata.Resource; + +public interface ResourceMapEntry extends Resource { + String getKey(); + + Resource getValue(); +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMapHelper.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMapHelper.java new file mode 100644 index 000000000..abf404f00 --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMapHelper.java @@ -0,0 +1,78 @@ +/* + * 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.platform.plugin.wasmgc; + +import org.teavm.platform.metadata.Resource; +import org.teavm.platform.metadata.ResourceMap; + +public final class ResourceMapHelper { + private ResourceMapHelper() { + } + + private static native ResourceMapEntry entry(ResourceMap map, int index); + + private static native int entryCount(ResourceMap map); + + public static Resource get(ResourceMap map, String key) { + var count = entryCount(map); + var initialIndex = Math.abs(key.hashCode()) % count; + for (var i = initialIndex; i < count; ++i) { + var entry = entry(map, i); + if (entry == null) { + return null; + } + if (entry.getKey().equals(key)) { + return entry.getValue(); + } + } + for (var i = 0; i < initialIndex; ++i) { + var entry = entry(map, i); + if (entry == null) { + return null; + } + if (entry.getKey().equals(key)) { + return entry.getValue(); + } + } + return null; + } + + public static boolean has(ResourceMap map, String key) { + return get(map, key) != null; + } + + public static String[] keys(ResourceMap map) { + var count = 0; + var entryCount = entryCount(map); + for (var i = 0; i < entryCount; ++i) { + var entry = entry(map, i); + if (entry != null) { + ++count; + } + } + + var result = new String[count]; + var index = 0; + for (var i = 0; i < entryCount; ++i) { + var entry = entry(map, i); + if (entry != null) { + result[index++] = entry.getKey(); + } + } + + return result; + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMarker.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMarker.java new file mode 100644 index 000000000..0edf3a42a --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMarker.java @@ -0,0 +1,20 @@ +/* + * 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.platform.plugin.wasmgc; + +@interface ResourceMarker { + int fieldCount(); +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMethodIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMethodIntrinsic.java new file mode 100644 index 000000000..4a30f574d --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/ResourceMethodIntrinsic.java @@ -0,0 +1,41 @@ +/* + * 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.platform.plugin.wasmgc; + +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext; +import org.teavm.backend.wasm.model.WasmStructure; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmStructGet; +import org.teavm.model.ValueType; + +class ResourceMethodIntrinsic implements WasmGCIntrinsic { + private int fieldIndex; + + ResourceMethodIntrinsic(int fieldIndex) { + this.fieldIndex = fieldIndex; + } + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { + var structType = (WasmType.CompositeReference) context.typeMapper().mapType( + ValueType.object(invocation.getMethod().getClassName())); + var struct = (WasmStructure) structType.composite; + return new WasmStructGet(struct, context.generate(invocation.getArguments().get(0)), fieldIndex); + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/WasmGCResourceArrayIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/WasmGCResourceArrayIntrinsic.java new file mode 100644 index 000000000..02de2c0ec --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/WasmGCResourceArrayIntrinsic.java @@ -0,0 +1,49 @@ +/* + * 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.platform.plugin.wasmgc; + +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext; +import org.teavm.backend.wasm.model.WasmArray; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmArrayGet; +import org.teavm.backend.wasm.model.expression.WasmArrayLength; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.model.ValueType; +import org.teavm.platform.metadata.ResourceArray; + +public class WasmGCResourceArrayIntrinsic implements WasmGCIntrinsic { + @Override + public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { + switch (invocation.getMethod().getName()) { + case "size": + return new WasmArrayLength(context.generate(invocation.getArguments().get(0))); + case "get": + return new WasmArrayGet(getArrayType(context), + context.generate(invocation.getArguments().get(0)), + context.generate(invocation.getArguments().get(1))); + default: + throw new IllegalArgumentException(); + } + } + + private WasmArray getArrayType(WasmGCIntrinsicContext context) { + var type = (WasmType.CompositeReference) context.typeMapper().mapType( + ValueType.object(ResourceArray.class.getName())); + return (WasmArray) type.composite; + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/WasmGCResourceMapHelperIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/WasmGCResourceMapHelperIntrinsic.java new file mode 100644 index 000000000..3fadfe2db --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/WasmGCResourceMapHelperIntrinsic.java @@ -0,0 +1,49 @@ +/* + * 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.platform.plugin.wasmgc; + +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicContext; +import org.teavm.backend.wasm.model.WasmArray; +import org.teavm.backend.wasm.model.WasmType; +import org.teavm.backend.wasm.model.expression.WasmArrayGet; +import org.teavm.backend.wasm.model.expression.WasmArrayLength; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.model.ValueType; +import org.teavm.platform.metadata.ResourceMap; + +public class WasmGCResourceMapHelperIntrinsic implements WasmGCIntrinsic { + @Override + public WasmExpression apply(InvocationExpr invocation, WasmGCIntrinsicContext context) { + switch (invocation.getMethod().getName()) { + case "entryCount": + return new WasmArrayLength(context.generate(invocation.getArguments().get(0))); + case "entry": + return new WasmArrayGet(getArrayType(context), + context.generate(invocation.getArguments().get(0)), + context.generate(invocation.getArguments().get(1))); + default: + throw new IllegalArgumentException(); + } + } + + private WasmArray getArrayType(WasmGCIntrinsicContext context) { + var type = (WasmType.CompositeReference) context.typeMapper().mapType( + ValueType.object(ResourceMap.class.getName())); + return (WasmArray) type.composite; + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/WasmGCResourceMetadataIntrinsicFactory.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/WasmGCResourceMetadataIntrinsicFactory.java new file mode 100644 index 000000000..fe6817cd4 --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/WasmGCResourceMetadataIntrinsicFactory.java @@ -0,0 +1,49 @@ +/* + * 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.platform.plugin.wasmgc; + +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicFactory; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicFactoryContext; +import org.teavm.common.ServiceRepository; +import org.teavm.model.MethodReference; +import org.teavm.platform.metadata.MetadataGenerator; + +public class WasmGCResourceMetadataIntrinsicFactory implements WasmGCIntrinsicFactory { + private Properties properties; + private ServiceRepository services; + private Map generators = new HashMap<>(); + + public WasmGCResourceMetadataIntrinsicFactory(Properties properties, ServiceRepository services) { + this.properties = properties; + this.services = services; + } + + public void addGenerator(MethodReference method, MetadataGenerator generator) { + generators.put(method, generator); + } + + @Override + public WasmGCIntrinsic createIntrinsic(MethodReference methodRef, WasmGCIntrinsicFactoryContext context) { + var generator = generators.get(methodRef); + return generator != null + ? new MetadataIntrinsic(properties, services, generator) + : null; + } +} diff --git a/platform/src/main/java/org/teavm/platform/plugin/wasmgc/WasmGCResourceMethodIntrinsicFactory.java b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/WasmGCResourceMethodIntrinsicFactory.java new file mode 100644 index 000000000..2bc9aef38 --- /dev/null +++ b/platform/src/main/java/org/teavm/platform/plugin/wasmgc/WasmGCResourceMethodIntrinsicFactory.java @@ -0,0 +1,41 @@ +/* + * 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.platform.plugin.wasmgc; + +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsic; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicFactory; +import org.teavm.backend.wasm.intrinsics.gc.WasmGCIntrinsicFactoryContext; +import org.teavm.model.MethodReference; + +public class WasmGCResourceMethodIntrinsicFactory implements WasmGCIntrinsicFactory { + @Override + public WasmGCIntrinsic createIntrinsic(MethodReference methodRef, WasmGCIntrinsicFactoryContext context) { + var cls = context.classes().get(methodRef.getClassName()); + if (cls == null) { + return null; + } + var method = cls.getMethod(methodRef.getDescriptor()); + if (method == null) { + return null; + } + var annot = method.getAnnotations().get(FieldMarker.class.getName()); + if (annot == null) { + return null; + } + var index = annot.getValue("index").getInt(); + return new ResourceMethodIntrinsic(index); + } +}