From 401fcabeae22689fb62307563cf8f20ae4ab16c3 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 28 Aug 2023 19:32:22 +0200 Subject: [PATCH] wasm: support ServiceLoader --- .../org/teavm/classlib/impl/JCLPlugin.java | 5 + .../impl/ServiceLoaderWasmSupport.java | 136 ++++++++++++++++++ .../org/teavm/classlib/java/util/TDate.java | 3 +- .../org/teavm/backend/wasm/WasmRuntime.java | 25 ++++ .../org/teavm/backend/wasm/WasmTarget.java | 6 + .../backend/wasm/binary/BinaryWriter.java | 68 +++++++++ .../wasm/generate/WasmClassGenerator.java | 32 +++-- .../wasm/generate/WasmGenerationVisitor.java | 10 ++ .../wasm/intrinsics/WasmIntrinsicFactory.java | 5 + .../wasm/intrinsics/WasmIntrinsicManager.java | 5 + .../wasm/intrinsics/WasmRuntimeIntrinsic.java | 7 + .../platform/plugin/MetadataIntrinsic.java | 69 +-------- 12 files changed, 292 insertions(+), 79 deletions(-) create mode 100644 classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderWasmSupport.java diff --git a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java index 742213cfd..dc072eaa9 100644 --- a/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java +++ b/classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java @@ -84,6 +84,11 @@ public class JCLPlugin implements TeaVMPlugin { if (cHost != null) { cHost.addGenerator(new ServiceLoaderCSupport()); } + + var wasmHost = host.getExtension(TeaVMWasmHost.class); + if (wasmHost != null) { + wasmHost.add(new ServiceLoaderWasmSupport()); + } } if (!isBootstrap()) { diff --git a/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderWasmSupport.java b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderWasmSupport.java new file mode 100644 index 000000000..445902175 --- /dev/null +++ b/classlib/src/main/java/org/teavm/classlib/impl/ServiceLoaderWasmSupport.java @@ -0,0 +1,136 @@ +/* + * Copyright 2023 Alexey Andreev. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.teavm.classlib.impl; + +import java.util.ServiceLoader; +import org.teavm.ast.InvocationExpr; +import org.teavm.backend.wasm.WasmRuntime; +import org.teavm.backend.wasm.binary.DataPrimitives; +import org.teavm.backend.wasm.binary.DataStructure; +import org.teavm.backend.wasm.intrinsics.WasmIntrinsic; +import org.teavm.backend.wasm.intrinsics.WasmIntrinsicFactory; +import org.teavm.backend.wasm.intrinsics.WasmIntrinsicFactoryContext; +import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager; +import org.teavm.backend.wasm.model.expression.WasmCall; +import org.teavm.backend.wasm.model.expression.WasmExpression; +import org.teavm.backend.wasm.model.expression.WasmInt32Constant; +import org.teavm.dependency.DependencyAnalyzer; +import org.teavm.interop.Address; +import org.teavm.interop.Structure; +import org.teavm.model.MethodReference; +import org.teavm.model.ValueType; +import org.teavm.runtime.Allocator; +import org.teavm.runtime.GC; +import org.teavm.runtime.RuntimeArray; +import org.teavm.runtime.RuntimeClass; +import org.teavm.runtime.RuntimeObject; + +public class ServiceLoaderWasmSupport implements WasmIntrinsicFactory { + private static final DataStructure ENTRY = new DataStructure( + (byte) 4, + DataPrimitives.ADDRESS, + DataPrimitives.INT + ); + private static final MethodReference CREATE_SERVICES_METHOD = new MethodReference( + ServiceLoaderWasmSupport.class, "createServices", Address.class, Address.class); + + @Override + public WasmIntrinsic create(WasmIntrinsicFactoryContext context) { + return new ServiceLoaderIntrinsic(context.getServices().getService(ServiceLoaderInformation.class)); + } + + @Override + public void contributeDependencies(DependencyAnalyzer analyzer) { + analyzer.linkMethod(CREATE_SERVICES_METHOD); + } + + private static class ServiceLoaderIntrinsic implements WasmIntrinsic { + private ServiceLoaderInformation information; + + ServiceLoaderIntrinsic(ServiceLoaderInformation information) { + this.information = information; + } + + @Override + public boolean isApplicable(MethodReference methodReference) { + return methodReference.getClassName().equals(ServiceLoader.class.getName()) + && methodReference.getName().equals("loadServices"); + } + + @Override + public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) { + var table = createServiceData(manager); + var tableArg = new WasmInt32Constant(table); + var serviceClassAddress = manager.generate(invocation.getArguments().get(0)); + return new WasmCall(manager.getNames().forMethod(CREATE_SERVICES_METHOD), tableArg, + serviceClassAddress); + } + + private int createServiceData(WasmIntrinsicManager manager) { + var writer = manager.getBinaryWriter(); + return writer.writeMap( + information.serviceTypes().toArray(new String[0]), + cls -> manager.getClassPointer(ValueType.object(cls)), + cls -> manager.getClassPointer(ValueType.object(cls)), + cls -> { + var implementations = information.serviceImplementations(cls); + var result = writer.getAddress(); + var count = DataPrimitives.INT.createValue(); + writer.append(count); + count.setInt(0, implementations.size()); + + for (var implementation : implementations) { + var entry = ENTRY.createValue(); + writer.append(entry); + + var implPointer = manager.getClassPointer(ValueType.object(implementation)); + entry.setAddress(0, implPointer); + + var constructorName = manager.getNames().forMethod(new MethodReference( + implementation, "", ValueType.VOID + )); + entry.setInt(1, manager.getFunctionPointer(constructorName)); + } + + return result; + } + ); + } + } + + static RuntimeObject createServices(Address table, RuntimeClass cls) { + var entry = WasmRuntime.lookupResource(table, cls.toAddress()); + if (entry == null) { + return null; + } + entry = entry.add(Address.sizeOf()).getAddress(); + var size = entry.getInt(); + entry = entry.add(4); + RuntimeArray result = Allocator.allocateArray(cls, size).toStructure(); + var resultData = WasmRuntime.align(result.toAddress().add(Structure.sizeOf(RuntimeArray.class)), + Address.sizeOf()); + for (var i = 0; i < size; ++i) { + RuntimeObject obj = Allocator.allocate(entry.getAddress().toStructure()).toStructure(); + entry = entry.add(Address.sizeOf()); + WasmRuntime.callFunctionFromTable(entry.getInt(), obj); + entry = entry.add(4); + resultData.putAddress(obj.toAddress()); + resultData = resultData.add(Address.sizeOf()); + GC.writeBarrier(result); + } + return result; + } +} diff --git a/classlib/src/main/java/org/teavm/classlib/java/util/TDate.java b/classlib/src/main/java/org/teavm/classlib/java/util/TDate.java index e9c14b093..4f6273494 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/util/TDate.java +++ b/classlib/src/main/java/org/teavm/classlib/java/util/TDate.java @@ -31,14 +31,13 @@ public class TDate implements TComparable { private long value; static { - if (PlatformDetector.isLowLevel()) { + if (PlatformDetector.isC()) { initLowLevel(); } } @Import(name = "teavm_date_init") @RuntimeInclude("date.h") - @UnsupportedOn(Platforms.WEBASSEMBLY) @NoSideEffects @Unmanaged private static native void initLowLevel(); diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java index 1ecc707e4..8e78e53ca 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java @@ -234,6 +234,7 @@ public final class WasmRuntime { return resource.add(Address.sizeOf()); } + @Unmanaged public static Address lookupResource(Address map, String string) { RuntimeString runtimeString = Address.ofObject(string).toStructure(); int hashCode = hashCode(runtimeString); @@ -256,6 +257,30 @@ public final class WasmRuntime { return null; } + @Unmanaged + public static Address lookupResource(Address map, Address key) { + int sz = map.getInt(); + Address content = contentStart(map); + var hash = key.toInt(); + for (int i = 0; i < sz; ++i) { + int index = (hash + i) % sz; + if (index < 0) { + index += sz; + } + var entry = content.add(index * Address.sizeOf() * 2); + var entryKey = entry.getAddress(); + if (entryKey == null) { + return null; + } + if (key == entryKey) { + return entry; + } + } + return null; + } + + public static native void callFunctionFromTable(int index, RuntimeObject instance); + static class RuntimeString extends RuntimeObject { char[] characters; } diff --git a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java index 1db3f02d3..52cdcbcc4 100644 --- a/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java +++ b/core/src/main/java/org/teavm/backend/wasm/WasmTarget.java @@ -348,6 +348,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { String[].class)).use(); dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "lookupResource", Address.class, String.class, Address.class)).use(); + dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "lookupResource", Address.class, + int.class, Address.class)).use(); dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printString", String.class, void.class)).use(); dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printInt", @@ -408,6 +410,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost { } dependencyAnalyzer.addDependencyListener(new StringsDependencyListener()); + + for (var intrinsic : additionalIntrinsics) { + intrinsic.contributeDependencies(dependencyAnalyzer); + } } @Override diff --git a/core/src/main/java/org/teavm/backend/wasm/binary/BinaryWriter.java b/core/src/main/java/org/teavm/backend/wasm/binary/BinaryWriter.java index 32d7e8d23..ad6a54ff1 100644 --- a/core/src/main/java/org/teavm/backend/wasm/binary/BinaryWriter.java +++ b/core/src/main/java/org/teavm/backend/wasm/binary/BinaryWriter.java @@ -18,6 +18,8 @@ package org.teavm.backend.wasm.binary; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; public class BinaryWriter { private int address; @@ -173,4 +175,70 @@ public class BinaryWriter { result[offset++] = (byte) (v >> 56); return offset; } + + public int writeMap(T[] keys, ToIntFunction hashCodeF, ToLongFunction keyWriter, + ToLongFunction valueWriter) { + int tableSize = keys.length * 2; + int maxTableSize = Math.min(keys.length * 5 / 2, tableSize + 10); + + Object[] bestTable = null; + int bestCollisionRatio = 0; + while (tableSize <= maxTableSize) { + var table = new Object[tableSize]; + int maxCollisionRatio = 0; + for (var key : keys) { + int hashCode = hashCodeF.applyAsInt(key); + int collisionRatio = 0; + while (true) { + int index = mod(hashCode++, table.length); + if (table[index] == null) { + table[index] = key; + break; + } + collisionRatio++; + } + maxCollisionRatio = Math.max(maxCollisionRatio, collisionRatio); + } + + if (bestTable == null || bestCollisionRatio > maxCollisionRatio) { + bestCollisionRatio = maxCollisionRatio; + bestTable = table; + } + + tableSize++; + } + + var sizeValue = DataPrimitives.ADDRESS.createValue(); + int start = append(sizeValue); + sizeValue.setAddress(0, bestTable.length); + + var keyValues = new DataValue[bestTable.length]; + var valueValues = new DataValue[bestTable.length]; + for (int i = 0; i < bestTable.length; ++i) { + var keyValue = DataPrimitives.ADDRESS.createValue(); + var valueValue = DataPrimitives.ADDRESS.createValue(); + append(keyValue); + append(valueValue); + keyValues[i] = keyValue; + valueValues[i] = valueValue; + } + for (int i = 0; i < bestTable.length; ++i) { + @SuppressWarnings("unchecked") + var key = (T) bestTable[i]; + if (key != null) { + keyValues[i].setAddress(0, keyWriter.applyAsLong(key)); + valueValues[i].setAddress(0, valueWriter.applyAsLong(key)); + } + } + + return start; + } + + private static int mod(int a, int b) { + a %= b; + if (a < 0) { + a += b; + } + return a; + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java index 2c93fcec3..1238c6cba 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmClassGenerator.java @@ -65,6 +65,7 @@ public class WasmClassGenerator { private BinaryWriter binaryWriter; private Map functions = new HashMap<>(); private List functionTable = new ArrayList<>(); + private ObjectIntMap functionIdMap = new ObjectIntHashMap<>(); private VirtualTableProvider vtableProvider; private TagRegistry tagRegistry; private WasmStringPool stringPool; @@ -222,9 +223,8 @@ public class WasmClassGenerator { binaryData.data = wrapper.getValue(0); binaryData.data.setInt(CLASS_SIZE, 4); binaryData.data.setAddress(CLASS_ITEM_TYPE, itemBinaryData.start); - binaryData.data.setInt(CLASS_IS_INSTANCE, functionTable.size()); + binaryData.data.setInt(CLASS_IS_INSTANCE, getFunctionPointer(names.forSupertypeFunction(type))); binaryData.data.setInt(CLASS_CANARY, RuntimeClass.computeCanary(4, 0)); - functionTable.add(names.forSupertypeFunction(type)); binaryData.data.setAddress(CLASS_NAME, stringPool.getStringPointer(type.toString().replace('/', '.'))); binaryData.data.setAddress(CLASS_SIMPLE_NAME, 0); binaryData.data.setInt(CLASS_INIT, -1); @@ -238,10 +238,9 @@ public class WasmClassGenerator { private DataValue createPrimitiveClassData(DataValue value, int size, ValueType type) { value.setInt(CLASS_SIZE, size); value.setInt(CLASS_FLAGS, RuntimeClass.PRIMITIVE); - value.setInt(CLASS_IS_INSTANCE, functionTable.size()); + value.setInt(CLASS_IS_INSTANCE, getFunctionPointer(names.forSupertypeFunction(type))); value.setAddress(CLASS_SIMPLE_NAME, 0); value.setInt(CLASS_INIT, -1); - functionTable.add(names.forSupertypeFunction(type)); String name; if (type == ValueType.VOID) { @@ -282,10 +281,20 @@ public class WasmClassGenerator { return value; } - public List getFunctionTable() { + public Iterable getFunctionTable() { return functionTable; } + public int getFunctionPointer(String name) { + var result = functionIdMap.getOrDefault(name, -1); + if (result < 0) { + result = functionTable.size(); + functionTable.add(name); + functionIdMap.put(name, result); + } + return result; + } + private DataValue createStructure(ClassBinaryData binaryData) { String parent = binaryData.cls.getParent(); int parentPtr = !binaryData.isInferface && parent != null @@ -316,8 +325,7 @@ public class WasmClassGenerator { header.setInt(CLASS_CANARY, RuntimeClass.computeCanary(occupiedSize, tag)); int nameAddress = requirements.name() ? stringPool.getStringPointer(name) : 0; header.setAddress(CLASS_NAME, nameAddress); - header.setInt(CLASS_IS_INSTANCE, functionTable.size()); - functionTable.add(names.forSupertypeFunction(ValueType.object(name))); + header.setInt(CLASS_IS_INSTANCE, getFunctionPointer(names.forSupertypeFunction(ValueType.object(name)))); header.setAddress(CLASS_PARENT, parentPtr); ClassReader cls = processedClassSource.get(name); @@ -372,8 +380,7 @@ public class WasmClassGenerator { if (cls != null && binaryData.start >= 0 && cls.getMethod(new MethodDescriptor("", ValueType.VOID)) != null && classInitializerInfo.isDynamicInitializer(name)) { - header.setInt(CLASS_INIT, functionTable.size()); - functionTable.add(names.forClassInitializer(name)); + header.setInt(CLASS_INIT, getFunctionPointer(names.forClassInitializer(name))); } else { header.setInt(CLASS_INIT, -1); } @@ -450,11 +457,8 @@ public class WasmClassGenerator { if (method != null) { VirtualTableEntry entry = vtable.getEntry(method); if (entry != null) { - methodIndex = functions.computeIfAbsent(entry.getImplementor(), implementor -> { - int result = functionTable.size(); - functionTable.add(names.forMethod(implementor)); - return result; - }); + methodIndex = functions.computeIfAbsent(entry.getImplementor(), + implementor -> getFunctionPointer(names.forMethod(implementor))); } } diff --git a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java index 90e975b63..48d5c65d4 100644 --- a/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java +++ b/core/src/main/java/org/teavm/backend/wasm/generate/WasmGenerationVisitor.java @@ -1654,6 +1654,16 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor { public int getStaticField(FieldReference field) { return classGenerator.getFieldOffset(field); } + + @Override + public int getClassPointer(ValueType type) { + return classGenerator.getClassPointer(type); + } + + @Override + public int getFunctionPointer(String name) { + return classGenerator.getFunctionPointer(name); + } }; private WasmLocal getTemporary(WasmType type) { diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicFactory.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicFactory.java index 7d016acfa..6d726d16a 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicFactory.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicFactory.java @@ -15,7 +15,12 @@ */ package org.teavm.backend.wasm.intrinsics; +import org.teavm.dependency.DependencyAnalyzer; + @FunctionalInterface public interface WasmIntrinsicFactory { WasmIntrinsic create(WasmIntrinsicFactoryContext context); + + default void contributeDependencies(DependencyAnalyzer analyzer) { + } } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java index 4b68d538a..f42b0393d 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmIntrinsicManager.java @@ -24,6 +24,7 @@ import org.teavm.backend.wasm.model.WasmType; import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.diagnostics.Diagnostics; import org.teavm.model.FieldReference; +import org.teavm.model.ValueType; public interface WasmIntrinsicManager { WasmExpression generate(Expr expr); @@ -40,5 +41,9 @@ public interface WasmIntrinsicManager { int getStaticField(FieldReference field); + int getClassPointer(ValueType type); + + int getFunctionPointer(String name); + void releaseTemporary(WasmLocal local); } diff --git a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmRuntimeIntrinsic.java b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmRuntimeIntrinsic.java index 9410148fe..09d8d07bf 100644 --- a/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmRuntimeIntrinsic.java +++ b/core/src/main/java/org/teavm/backend/wasm/intrinsics/WasmRuntimeIntrinsic.java @@ -23,6 +23,7 @@ import org.teavm.backend.wasm.model.expression.WasmExpression; import org.teavm.backend.wasm.model.expression.WasmFloatBinary; import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmFloatType; +import org.teavm.backend.wasm.model.expression.WasmIndirectCall; import org.teavm.backend.wasm.model.expression.WasmIntBinary; import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation; import org.teavm.backend.wasm.model.expression.WasmIntType; @@ -40,6 +41,7 @@ public class WasmRuntimeIntrinsic implements WasmIntrinsic { case "gtu": case "ltu": case "initStack": + case "callFunctionFromTable": return true; default: return false; @@ -61,6 +63,11 @@ public class WasmRuntimeIntrinsic implements WasmIntrinsic { case "gtu": return comparison(WasmIntBinaryOperation.GT_UNSIGNED, WasmFloatBinaryOperation.GT, invocation, manager); + case "callFunctionFromTable": { + var call = new WasmIndirectCall(manager.generate(invocation.getArguments().get(0))); + call.getArguments().add(manager.generate(invocation.getArguments().get(1))); + return call; + } default: throw new IllegalArgumentException(invocation.getMethod().getName()); } diff --git a/platform/src/main/java/org/teavm/platform/plugin/MetadataIntrinsic.java b/platform/src/main/java/org/teavm/platform/plugin/MetadataIntrinsic.java index c5b4dde0f..bd78e9639 100644 --- a/platform/src/main/java/org/teavm/platform/plugin/MetadataIntrinsic.java +++ b/platform/src/main/java/org/teavm/platform/plugin/MetadataIntrinsic.java @@ -120,61 +120,12 @@ class MetadataIntrinsic implements WasmIntrinsic { } private int writeResource(BinaryWriter writer, WasmStringPool stringPool, ResourceMap resourceMap) { - String[] keys = resourceMap.keys(); - int tableSize = keys.length * 2; - int maxTableSize = Math.min(keys.length * 5 / 2, tableSize + 10); - - String[] bestTable = null; - int bestCollisionRatio = 0; - while (tableSize <= maxTableSize) { - String[] table = new String[tableSize]; - int maxCollisionRatio = 0; - for (String key : keys) { - int hashCode = key.hashCode(); - int collisionRatio = 0; - while (true) { - int index = mod(hashCode++, table.length); - if (table[index] == null) { - table[index] = key; - break; - } - collisionRatio++; - } - maxCollisionRatio = Math.max(maxCollisionRatio, collisionRatio); - } - - if (bestTable == null || bestCollisionRatio > maxCollisionRatio) { - bestCollisionRatio = maxCollisionRatio; - bestTable = table; - } - - tableSize++; - } - - - DataValue sizeValue = DataPrimitives.ADDRESS.createValue(); - int start = writer.append(sizeValue); - sizeValue.setAddress(0, bestTable.length); - - DataValue[] keyValues = new DataValue[bestTable.length]; - DataValue[] valueValues = new DataValue[bestTable.length]; - for (int i = 0; i < bestTable.length; ++i) { - DataValue keyValue = DataPrimitives.ADDRESS.createValue(); - DataValue valueValue = DataPrimitives.ADDRESS.createValue(); - writer.append(keyValue); - writer.append(valueValue); - keyValues[i] = keyValue; - valueValues[i] = valueValue; - } - for (int i = 0; i < bestTable.length; ++i) { - String key = bestTable[i]; - if (key != null) { - keyValues[i].setAddress(0, stringPool.getStringPointer(key)); - valueValues[i].setAddress(0, writeValue(writer, stringPool, resourceMap.get(key))); - } - } - - return start; + return writer.writeMap( + resourceMap.keys(), + String::hashCode, + stringPool::getStringPointer, + key -> writeValue(writer, stringPool, resourceMap.get(key)) + ); } private int writeResource(BinaryWriter writer, WasmStringPool stringPool, ResourceArray resourceArray) { @@ -195,14 +146,6 @@ class MetadataIntrinsic implements WasmIntrinsic { return start; } - private static int mod(int a, int b) { - a %= b; - if (a < 0) { - a += b; - } - return a; - } - private void writeValueTo(BinaryWriter writer, WasmStringPool stringPool, Class type, DataValue target, int index, Object value) { if (type == String.class) {