mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
wasm: support ServiceLoader
This commit is contained in:
parent
70d0658b47
commit
401fcabeae
|
@ -84,6 +84,11 @@ public class JCLPlugin implements TeaVMPlugin {
|
||||||
if (cHost != null) {
|
if (cHost != null) {
|
||||||
cHost.addGenerator(new ServiceLoaderCSupport());
|
cHost.addGenerator(new ServiceLoaderCSupport());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var wasmHost = host.getExtension(TeaVMWasmHost.class);
|
||||||
|
if (wasmHost != null) {
|
||||||
|
wasmHost.add(new ServiceLoaderWasmSupport());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isBootstrap()) {
|
if (!isBootstrap()) {
|
||||||
|
|
|
@ -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, "<init>", 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -31,14 +31,13 @@ public class TDate implements TComparable<TDate> {
|
||||||
private long value;
|
private long value;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
if (PlatformDetector.isLowLevel()) {
|
if (PlatformDetector.isC()) {
|
||||||
initLowLevel();
|
initLowLevel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Import(name = "teavm_date_init")
|
@Import(name = "teavm_date_init")
|
||||||
@RuntimeInclude("date.h")
|
@RuntimeInclude("date.h")
|
||||||
@UnsupportedOn(Platforms.WEBASSEMBLY)
|
|
||||||
@NoSideEffects
|
@NoSideEffects
|
||||||
@Unmanaged
|
@Unmanaged
|
||||||
private static native void initLowLevel();
|
private static native void initLowLevel();
|
||||||
|
|
|
@ -234,6 +234,7 @@ public final class WasmRuntime {
|
||||||
return resource.add(Address.sizeOf());
|
return resource.add(Address.sizeOf());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Unmanaged
|
||||||
public static Address lookupResource(Address map, String string) {
|
public static Address lookupResource(Address map, String string) {
|
||||||
RuntimeString runtimeString = Address.ofObject(string).toStructure();
|
RuntimeString runtimeString = Address.ofObject(string).toStructure();
|
||||||
int hashCode = hashCode(runtimeString);
|
int hashCode = hashCode(runtimeString);
|
||||||
|
@ -256,6 +257,30 @@ public final class WasmRuntime {
|
||||||
return null;
|
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 {
|
static class RuntimeString extends RuntimeObject {
|
||||||
char[] characters;
|
char[] characters;
|
||||||
}
|
}
|
||||||
|
|
|
@ -348,6 +348,8 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
String[].class)).use();
|
String[].class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "lookupResource", Address.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "lookupResource", Address.class,
|
||||||
String.class, Address.class)).use();
|
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",
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printString",
|
||||||
String.class, void.class)).use();
|
String.class, void.class)).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printInt",
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "printInt",
|
||||||
|
@ -408,6 +410,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyAnalyzer.addDependencyListener(new StringsDependencyListener());
|
dependencyAnalyzer.addDependencyListener(new StringsDependencyListener());
|
||||||
|
|
||||||
|
for (var intrinsic : additionalIntrinsics) {
|
||||||
|
intrinsic.contributeDependencies(dependencyAnalyzer);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -18,6 +18,8 @@ package org.teavm.backend.wasm.binary;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.function.ToIntFunction;
|
||||||
|
import java.util.function.ToLongFunction;
|
||||||
|
|
||||||
public class BinaryWriter {
|
public class BinaryWriter {
|
||||||
private int address;
|
private int address;
|
||||||
|
@ -173,4 +175,70 @@ public class BinaryWriter {
|
||||||
result[offset++] = (byte) (v >> 56);
|
result[offset++] = (byte) (v >> 56);
|
||||||
return offset;
|
return offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public <T> int writeMap(T[] keys, ToIntFunction<T> hashCodeF, ToLongFunction<T> keyWriter,
|
||||||
|
ToLongFunction<T> 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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,7 @@ public class WasmClassGenerator {
|
||||||
private BinaryWriter binaryWriter;
|
private BinaryWriter binaryWriter;
|
||||||
private Map<MethodReference, Integer> functions = new HashMap<>();
|
private Map<MethodReference, Integer> functions = new HashMap<>();
|
||||||
private List<String> functionTable = new ArrayList<>();
|
private List<String> functionTable = new ArrayList<>();
|
||||||
|
private ObjectIntMap<String> functionIdMap = new ObjectIntHashMap<>();
|
||||||
private VirtualTableProvider vtableProvider;
|
private VirtualTableProvider vtableProvider;
|
||||||
private TagRegistry tagRegistry;
|
private TagRegistry tagRegistry;
|
||||||
private WasmStringPool stringPool;
|
private WasmStringPool stringPool;
|
||||||
|
@ -222,9 +223,8 @@ public class WasmClassGenerator {
|
||||||
binaryData.data = wrapper.getValue(0);
|
binaryData.data = wrapper.getValue(0);
|
||||||
binaryData.data.setInt(CLASS_SIZE, 4);
|
binaryData.data.setInt(CLASS_SIZE, 4);
|
||||||
binaryData.data.setAddress(CLASS_ITEM_TYPE, itemBinaryData.start);
|
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));
|
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_NAME, stringPool.getStringPointer(type.toString().replace('/', '.')));
|
||||||
binaryData.data.setAddress(CLASS_SIMPLE_NAME, 0);
|
binaryData.data.setAddress(CLASS_SIMPLE_NAME, 0);
|
||||||
binaryData.data.setInt(CLASS_INIT, -1);
|
binaryData.data.setInt(CLASS_INIT, -1);
|
||||||
|
@ -238,10 +238,9 @@ public class WasmClassGenerator {
|
||||||
private DataValue createPrimitiveClassData(DataValue value, int size, ValueType type) {
|
private DataValue createPrimitiveClassData(DataValue value, int size, ValueType type) {
|
||||||
value.setInt(CLASS_SIZE, size);
|
value.setInt(CLASS_SIZE, size);
|
||||||
value.setInt(CLASS_FLAGS, RuntimeClass.PRIMITIVE);
|
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.setAddress(CLASS_SIMPLE_NAME, 0);
|
||||||
value.setInt(CLASS_INIT, -1);
|
value.setInt(CLASS_INIT, -1);
|
||||||
functionTable.add(names.forSupertypeFunction(type));
|
|
||||||
|
|
||||||
String name;
|
String name;
|
||||||
if (type == ValueType.VOID) {
|
if (type == ValueType.VOID) {
|
||||||
|
@ -282,10 +281,20 @@ public class WasmClassGenerator {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getFunctionTable() {
|
public Iterable<? extends String> getFunctionTable() {
|
||||||
return functionTable;
|
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) {
|
private DataValue createStructure(ClassBinaryData binaryData) {
|
||||||
String parent = binaryData.cls.getParent();
|
String parent = binaryData.cls.getParent();
|
||||||
int parentPtr = !binaryData.isInferface && parent != null
|
int parentPtr = !binaryData.isInferface && parent != null
|
||||||
|
@ -316,8 +325,7 @@ public class WasmClassGenerator {
|
||||||
header.setInt(CLASS_CANARY, RuntimeClass.computeCanary(occupiedSize, tag));
|
header.setInt(CLASS_CANARY, RuntimeClass.computeCanary(occupiedSize, tag));
|
||||||
int nameAddress = requirements.name() ? stringPool.getStringPointer(name) : 0;
|
int nameAddress = requirements.name() ? stringPool.getStringPointer(name) : 0;
|
||||||
header.setAddress(CLASS_NAME, nameAddress);
|
header.setAddress(CLASS_NAME, nameAddress);
|
||||||
header.setInt(CLASS_IS_INSTANCE, functionTable.size());
|
header.setInt(CLASS_IS_INSTANCE, getFunctionPointer(names.forSupertypeFunction(ValueType.object(name))));
|
||||||
functionTable.add(names.forSupertypeFunction(ValueType.object(name)));
|
|
||||||
header.setAddress(CLASS_PARENT, parentPtr);
|
header.setAddress(CLASS_PARENT, parentPtr);
|
||||||
|
|
||||||
ClassReader cls = processedClassSource.get(name);
|
ClassReader cls = processedClassSource.get(name);
|
||||||
|
@ -372,8 +380,7 @@ public class WasmClassGenerator {
|
||||||
if (cls != null && binaryData.start >= 0
|
if (cls != null && binaryData.start >= 0
|
||||||
&& cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null
|
&& cls.getMethod(new MethodDescriptor("<clinit>", ValueType.VOID)) != null
|
||||||
&& classInitializerInfo.isDynamicInitializer(name)) {
|
&& classInitializerInfo.isDynamicInitializer(name)) {
|
||||||
header.setInt(CLASS_INIT, functionTable.size());
|
header.setInt(CLASS_INIT, getFunctionPointer(names.forClassInitializer(name)));
|
||||||
functionTable.add(names.forClassInitializer(name));
|
|
||||||
} else {
|
} else {
|
||||||
header.setInt(CLASS_INIT, -1);
|
header.setInt(CLASS_INIT, -1);
|
||||||
}
|
}
|
||||||
|
@ -450,11 +457,8 @@ public class WasmClassGenerator {
|
||||||
if (method != null) {
|
if (method != null) {
|
||||||
VirtualTableEntry entry = vtable.getEntry(method);
|
VirtualTableEntry entry = vtable.getEntry(method);
|
||||||
if (entry != null) {
|
if (entry != null) {
|
||||||
methodIndex = functions.computeIfAbsent(entry.getImplementor(), implementor -> {
|
methodIndex = functions.computeIfAbsent(entry.getImplementor(),
|
||||||
int result = functionTable.size();
|
implementor -> getFunctionPointer(names.forMethod(implementor)));
|
||||||
functionTable.add(names.forMethod(implementor));
|
|
||||||
return result;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1654,6 +1654,16 @@ class WasmGenerationVisitor implements StatementVisitor, ExprVisitor {
|
||||||
public int getStaticField(FieldReference field) {
|
public int getStaticField(FieldReference field) {
|
||||||
return classGenerator.getFieldOffset(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) {
|
private WasmLocal getTemporary(WasmType type) {
|
||||||
|
|
|
@ -15,7 +15,12 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.intrinsics;
|
package org.teavm.backend.wasm.intrinsics;
|
||||||
|
|
||||||
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface WasmIntrinsicFactory {
|
public interface WasmIntrinsicFactory {
|
||||||
WasmIntrinsic create(WasmIntrinsicFactoryContext context);
|
WasmIntrinsic create(WasmIntrinsicFactoryContext context);
|
||||||
|
|
||||||
|
default void contributeDependencies(DependencyAnalyzer analyzer) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.teavm.backend.wasm.model.WasmType;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
import org.teavm.diagnostics.Diagnostics;
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
public interface WasmIntrinsicManager {
|
public interface WasmIntrinsicManager {
|
||||||
WasmExpression generate(Expr expr);
|
WasmExpression generate(Expr expr);
|
||||||
|
@ -40,5 +41,9 @@ public interface WasmIntrinsicManager {
|
||||||
|
|
||||||
int getStaticField(FieldReference field);
|
int getStaticField(FieldReference field);
|
||||||
|
|
||||||
|
int getClassPointer(ValueType type);
|
||||||
|
|
||||||
|
int getFunctionPointer(String name);
|
||||||
|
|
||||||
void releaseTemporary(WasmLocal local);
|
void releaseTemporary(WasmLocal local);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.WasmFloatBinary;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation;
|
import org.teavm.backend.wasm.model.expression.WasmFloatBinaryOperation;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmFloatType;
|
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.WasmIntBinary;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||||
|
@ -40,6 +41,7 @@ public class WasmRuntimeIntrinsic implements WasmIntrinsic {
|
||||||
case "gtu":
|
case "gtu":
|
||||||
case "ltu":
|
case "ltu":
|
||||||
case "initStack":
|
case "initStack":
|
||||||
|
case "callFunctionFromTable":
|
||||||
return true;
|
return true;
|
||||||
default:
|
default:
|
||||||
return false;
|
return false;
|
||||||
|
@ -61,6 +63,11 @@ public class WasmRuntimeIntrinsic implements WasmIntrinsic {
|
||||||
case "gtu":
|
case "gtu":
|
||||||
return comparison(WasmIntBinaryOperation.GT_UNSIGNED, WasmFloatBinaryOperation.GT,
|
return comparison(WasmIntBinaryOperation.GT_UNSIGNED, WasmFloatBinaryOperation.GT,
|
||||||
invocation, manager);
|
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:
|
default:
|
||||||
throw new IllegalArgumentException(invocation.getMethod().getName());
|
throw new IllegalArgumentException(invocation.getMethod().getName());
|
||||||
}
|
}
|
||||||
|
|
|
@ -120,61 +120,12 @@ class MetadataIntrinsic implements WasmIntrinsic {
|
||||||
}
|
}
|
||||||
|
|
||||||
private int writeResource(BinaryWriter writer, WasmStringPool stringPool, ResourceMap<?> resourceMap) {
|
private int writeResource(BinaryWriter writer, WasmStringPool stringPool, ResourceMap<?> resourceMap) {
|
||||||
String[] keys = resourceMap.keys();
|
return writer.writeMap(
|
||||||
int tableSize = keys.length * 2;
|
resourceMap.keys(),
|
||||||
int maxTableSize = Math.min(keys.length * 5 / 2, tableSize + 10);
|
String::hashCode,
|
||||||
|
stringPool::getStringPointer,
|
||||||
String[] bestTable = null;
|
key -> writeValue(writer, stringPool, resourceMap.get(key))
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private int writeResource(BinaryWriter writer, WasmStringPool stringPool, ResourceArray<?> resourceArray) {
|
private int writeResource(BinaryWriter writer, WasmStringPool stringPool, ResourceArray<?> resourceArray) {
|
||||||
|
@ -195,14 +146,6 @@ class MetadataIntrinsic implements WasmIntrinsic {
|
||||||
return start;
|
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,
|
private void writeValueTo(BinaryWriter writer, WasmStringPool stringPool, Class<?> type, DataValue target,
|
||||||
int index, Object value) {
|
int index, Object value) {
|
||||||
if (type == String.class) {
|
if (type == String.class) {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user