mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
Wasm backend: implement remaining types of resources
This commit is contained in:
parent
5ce48ce866
commit
097820cc2b
|
@ -21,6 +21,7 @@ import java.util.List;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
import org.teavm.backend.c.TeaVMCHost;
|
import org.teavm.backend.c.TeaVMCHost;
|
||||||
import org.teavm.backend.javascript.TeaVMJavaScriptHost;
|
import org.teavm.backend.javascript.TeaVMJavaScriptHost;
|
||||||
|
import org.teavm.backend.wasm.TeaVMWasmHost;
|
||||||
import org.teavm.classlib.ReflectionSupplier;
|
import org.teavm.classlib.ReflectionSupplier;
|
||||||
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
|
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
|
||||||
import org.teavm.classlib.impl.tz.DateTimeZoneProviderIntrinsic;
|
import org.teavm.classlib.impl.tz.DateTimeZoneProviderIntrinsic;
|
||||||
|
@ -92,6 +93,11 @@ public class JCLPlugin implements TeaVMPlugin {
|
||||||
if (cHost != null) {
|
if (cHost != null) {
|
||||||
cHost.addIntrinsic(context -> new DateTimeZoneProviderIntrinsic(context.getProperties()));
|
cHost.addIntrinsic(context -> new DateTimeZoneProviderIntrinsic(context.getProperties()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TeaVMWasmHost wasmHost = host.getExtension(TeaVMWasmHost.class);
|
||||||
|
if (wasmHost != null) {
|
||||||
|
wasmHost.add(context -> new DateTimeZoneProviderIntrinsic(context.getProperties()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TeaVMPluginUtil.handleNatives(host, Class.class);
|
TeaVMPluginUtil.handleNatives(host, Class.class);
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.util.Set;
|
||||||
import org.teavm.backend.javascript.spi.GeneratedBy;
|
import org.teavm.backend.javascript.spi.GeneratedBy;
|
||||||
import org.teavm.classlib.impl.Base46;
|
import org.teavm.classlib.impl.Base46;
|
||||||
import org.teavm.classlib.impl.CharFlow;
|
import org.teavm.classlib.impl.CharFlow;
|
||||||
|
import org.teavm.interop.Import;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
import org.teavm.platform.metadata.MetadataProvider;
|
import org.teavm.platform.metadata.MetadataProvider;
|
||||||
import org.teavm.platform.metadata.ResourceMap;
|
import org.teavm.platform.metadata.ResourceMap;
|
||||||
|
@ -189,6 +190,7 @@ public final class DateTimeZoneProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
@JSBody(params = "instant", script = "return new Date(instant).getTimezoneOffset();")
|
@JSBody(params = "instant", script = "return new Date(instant).getTimezoneOffset();")
|
||||||
|
@Import(module = "teavm", name = "getNativeOffset")
|
||||||
private static native int getNativeOffset(double instant);
|
private static native int getNativeOffset(double instant);
|
||||||
|
|
||||||
@MetadataProvider(TimeZoneGenerator.class)
|
@MetadataProvider(TimeZoneGenerator.class)
|
||||||
|
|
|
@ -19,9 +19,13 @@ import java.util.Properties;
|
||||||
import org.teavm.ast.InvocationExpr;
|
import org.teavm.ast.InvocationExpr;
|
||||||
import org.teavm.backend.c.intrinsic.Intrinsic;
|
import org.teavm.backend.c.intrinsic.Intrinsic;
|
||||||
import org.teavm.backend.c.intrinsic.IntrinsicContext;
|
import org.teavm.backend.c.intrinsic.IntrinsicContext;
|
||||||
|
import org.teavm.backend.wasm.intrinsics.WasmIntrinsic;
|
||||||
|
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class DateTimeZoneProviderIntrinsic implements Intrinsic {
|
public class DateTimeZoneProviderIntrinsic implements Intrinsic, WasmIntrinsic {
|
||||||
private Properties properties;
|
private Properties properties;
|
||||||
|
|
||||||
public DateTimeZoneProviderIntrinsic(Properties properties) {
|
public DateTimeZoneProviderIntrinsic(Properties properties) {
|
||||||
|
@ -43,6 +47,20 @@ public class DateTimeZoneProviderIntrinsic implements Intrinsic {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isApplicable(MethodReference methodReference) {
|
||||||
|
if (!methodReference.getClassName().equals(DateTimeZoneProvider.class.getName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (methodReference.getName()) {
|
||||||
|
case "timeZoneDetectionEnabled":
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
public void apply(IntrinsicContext context, InvocationExpr invocation) {
|
||||||
switch (invocation.getMethod().getName()) {
|
switch (invocation.getMethod().getName()) {
|
||||||
|
@ -56,4 +74,16 @@ public class DateTimeZoneProviderIntrinsic implements Intrinsic {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||||
|
switch (invocation.getMethod().getName()) {
|
||||||
|
case "timeZoneDetectionEnabled": {
|
||||||
|
boolean enabled = properties.getProperty("java.util.TimeZone.autodetect", "false").equals("true");
|
||||||
|
return new WasmInt32Constant(enabled ? 1 : 0);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
import org.teavm.interop.StaticInit;
|
import org.teavm.interop.StaticInit;
|
||||||
import org.teavm.interop.Unmanaged;
|
import org.teavm.interop.Unmanaged;
|
||||||
|
import org.teavm.runtime.RuntimeObject;
|
||||||
|
|
||||||
@StaticInit
|
@StaticInit
|
||||||
@Unmanaged
|
@Unmanaged
|
||||||
|
@ -282,4 +283,97 @@ public final class WasmRuntime {
|
||||||
public static void setExceptionHandlerId(Address stackFrame, int id) {
|
public static void setExceptionHandlerId(Address stackFrame, int id) {
|
||||||
getExceptionHandlerPtr(stackFrame).putInt(id);
|
getExceptionHandlerPtr(stackFrame).putInt(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int hashCode(RuntimeString string) {
|
||||||
|
int hashCode = 0;
|
||||||
|
int length = string.characters.length;
|
||||||
|
Address chars = Address.ofData(string.characters);
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
hashCode = 31 * hashCode + chars.getChar();
|
||||||
|
chars = chars.add(2);
|
||||||
|
}
|
||||||
|
return hashCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean equals(RuntimeString first, RuntimeString second) {
|
||||||
|
if (first.characters.length != second.characters.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Address firstChars = Address.ofData(first.characters);
|
||||||
|
Address secondChars = Address.ofData(second.characters);
|
||||||
|
int length = first.characters.length;
|
||||||
|
for (int i = 0; i < length; ++i) {
|
||||||
|
if (firstChars.getChar() != secondChars.getChar()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
firstChars = firstChars.add(2);
|
||||||
|
secondChars = secondChars.add(2);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String[] resourceMapKeys(Address map) {
|
||||||
|
String[] result = new String[resourceMapSize(map)];
|
||||||
|
fillResourceMapKeys(map, result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int resourceMapSize(Address map) {
|
||||||
|
int result = 0;
|
||||||
|
int sz = map.getInt();
|
||||||
|
Address data = contentStart(map);
|
||||||
|
for (int i = 0; i < sz; ++i) {
|
||||||
|
if (data.getAddress() != null) {
|
||||||
|
result++;
|
||||||
|
}
|
||||||
|
data = data.add(Address.sizeOf() * 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void fillResourceMapKeys(Address map, String[] target) {
|
||||||
|
int sz = map.getInt();
|
||||||
|
Address data = contentStart(map);
|
||||||
|
Address targetData = Address.ofData(target);
|
||||||
|
for (int i = 0; i < sz; ++i) {
|
||||||
|
Address entry = data.getAddress();
|
||||||
|
if (entry != null) {
|
||||||
|
targetData.putAddress(entry);
|
||||||
|
targetData = targetData.add(Address.sizeOf());
|
||||||
|
}
|
||||||
|
data = data.add(Address.sizeOf());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Address contentStart(Address resource) {
|
||||||
|
return resource.add(Address.sizeOf());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Address lookupResource(Address map, String string) {
|
||||||
|
RuntimeString runtimeString = Address.ofObject(string).toStructure();
|
||||||
|
int hashCode = hashCode(runtimeString);
|
||||||
|
int sz = map.getInt();
|
||||||
|
Address content = contentStart(map);
|
||||||
|
for (int i = 0; i < sz; ++i) {
|
||||||
|
int index = (hashCode + i) % sz;
|
||||||
|
if (index < 0) {
|
||||||
|
index += sz;
|
||||||
|
}
|
||||||
|
Address entry = content.add(index * Address.sizeOf() * 2);
|
||||||
|
Address key = entry.getAddress();
|
||||||
|
if (key == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (equals(key.toStructure(), runtimeString)) {
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
static class RuntimeString extends RuntimeObject {
|
||||||
|
char[] characters;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,6 +266,10 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
int.class, void.class), null).use();
|
int.class, void.class), null).use();
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getCallSiteId", Address.class,
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getCallSiteId", Address.class,
|
||||||
int.class), null).use();
|
int.class), null).use();
|
||||||
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "resourceMapKeys", Address.class,
|
||||||
|
String[].class), null).use();
|
||||||
|
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "lookupResource", Address.class,
|
||||||
|
String.class, Address.class), null).use();
|
||||||
|
|
||||||
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocate",
|
dependencyAnalyzer.linkMethod(new MethodReference(Allocator.class, "allocate",
|
||||||
RuntimeClass.class, Address.class), null).use();
|
RuntimeClass.class, Address.class), null).use();
|
||||||
|
@ -466,7 +470,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
renderer.setLineNumbersEmitted(debugging);
|
renderer.setLineNumbersEmitted(debugging);
|
||||||
renderer.render(module);
|
renderer.render(module);
|
||||||
try (OutputStream output = buildTarget.createResource(outputName);
|
try (OutputStream output = buildTarget.createResource(outputName);
|
||||||
Writer writer = new OutputStreamWriter(output, "UTF-8")) {
|
Writer writer = new OutputStreamWriter(output, StandardCharsets.UTF_8)) {
|
||||||
writer.write(renderer.toString());
|
writer.write(renderer.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -477,7 +481,7 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
renderer.setMemoryAccessChecked(Boolean.parseBoolean(System.getProperty("wasm.c.assertMemory", "false")));
|
renderer.setMemoryAccessChecked(Boolean.parseBoolean(System.getProperty("wasm.c.assertMemory", "false")));
|
||||||
renderer.render(module);
|
renderer.render(module);
|
||||||
try (OutputStream output = buildTarget.createResource(outputName);
|
try (OutputStream output = buildTarget.createResource(outputName);
|
||||||
Writer writer = new OutputStreamWriter(output, "UTF-8")) {
|
Writer writer = new OutputStreamWriter(output, StandardCharsets.UTF_8)) {
|
||||||
writer.write(renderer.toString());
|
writer.write(renderer.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,7 @@ import org.teavm.backend.wasm.model.expression.WasmStoreInt64;
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.runtime.RuntimeArray;
|
||||||
|
|
||||||
public class AddressIntrinsic implements WasmIntrinsic {
|
public class AddressIntrinsic implements WasmIntrinsic {
|
||||||
private WasmClassGenerator classGenerator;
|
private WasmClassGenerator classGenerator;
|
||||||
|
@ -159,13 +160,40 @@ public class AddressIntrinsic implements WasmIntrinsic {
|
||||||
.collect(Collectors.toList()));
|
.collect(Collectors.toList()));
|
||||||
return call;
|
return call;
|
||||||
}
|
}
|
||||||
case "isLessThan": {
|
case "isLessThan":
|
||||||
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_UNSIGNED,
|
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_UNSIGNED,
|
||||||
manager.generate(invocation.getArguments().get(0)),
|
manager.generate(invocation.getArguments().get(0)),
|
||||||
manager.generate(invocation.getArguments().get(1)));
|
manager.generate(invocation.getArguments().get(1)));
|
||||||
|
case "ofData": {
|
||||||
|
ValueType.Array type = (ValueType.Array) invocation.getMethod().parameterType(0);
|
||||||
|
int alignment = getAlignment(type.getItemType());
|
||||||
|
int start = WasmClassGenerator.align(classGenerator.getClassSize(RuntimeArray.class.getName()),
|
||||||
|
alignment);
|
||||||
|
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
|
||||||
|
manager.generate(invocation.getArguments().get(0)), new WasmInt32Constant(start));
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
throw new IllegalArgumentException(invocation.getMethod().toString());
|
throw new IllegalArgumentException(invocation.getMethod().toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getAlignment(ValueType type) {
|
||||||
|
if (type instanceof ValueType.Primitive) {
|
||||||
|
switch (((ValueType.Primitive) type).getKind()) {
|
||||||
|
case BOOLEAN:
|
||||||
|
case BYTE:
|
||||||
|
return 1;
|
||||||
|
case SHORT:
|
||||||
|
case CHARACTER:
|
||||||
|
return 2;
|
||||||
|
case INTEGER:
|
||||||
|
case FLOAT:
|
||||||
|
return 4;
|
||||||
|
case LONG:
|
||||||
|
case DOUBLE:
|
||||||
|
return 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,6 +34,9 @@ TeaVM.wasm = function() {
|
||||||
function currentTimeMillis() {
|
function currentTimeMillis() {
|
||||||
return new Date().getTime();
|
return new Date().getTime();
|
||||||
}
|
}
|
||||||
|
function getNativeOffset(instant) {
|
||||||
|
return new Date(instant).getTimezoneOffset();
|
||||||
|
}
|
||||||
|
|
||||||
function importDefaults(obj) {
|
function importDefaults(obj) {
|
||||||
obj.teavm = {
|
obj.teavm = {
|
||||||
|
@ -44,7 +47,8 @@ TeaVM.wasm = function() {
|
||||||
isfinite: isFinite,
|
isfinite: isFinite,
|
||||||
putwchar: putwchar,
|
putwchar: putwchar,
|
||||||
towlower: towlower,
|
towlower: towlower,
|
||||||
towupper: towupper
|
towupper: towupper,
|
||||||
|
getNativeOffset: getNativeOffset
|
||||||
};
|
};
|
||||||
|
|
||||||
obj.teavmMath = Math;
|
obj.teavmMath = Math;
|
||||||
|
|
|
@ -82,6 +82,8 @@ public final class Address {
|
||||||
|
|
||||||
public static native Address ofData(double[] data);
|
public static native Address ofData(double[] data);
|
||||||
|
|
||||||
|
public static native Address ofData(Object[] data);
|
||||||
|
|
||||||
public static native Address align(Address address, int alignment);
|
public static native Address align(Address address, int alignment);
|
||||||
|
|
||||||
public static native int sizeOf();
|
public static native int sizeOf();
|
||||||
|
|
|
@ -53,13 +53,17 @@ class BuildTimeResourceProxyBuilder {
|
||||||
|
|
||||||
public ProxyFactoryCreation(ResourceTypeDescriptor descriptor) {
|
public ProxyFactoryCreation(ResourceTypeDescriptor descriptor) {
|
||||||
this.descriptor = descriptor;
|
this.descriptor = descriptor;
|
||||||
|
int index = 0;
|
||||||
|
for (String propertyName : descriptor.getPropertyTypes().keySet()) {
|
||||||
|
propertyIndexes.put(propertyName, index++);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
BuildTimeResourceProxyFactory create() {
|
BuildTimeResourceProxyFactory create() {
|
||||||
for (Map.Entry<Method, ResourceMethodDescriptor> entry : descriptor.getMethods().entrySet()) {
|
for (Map.Entry<Method, ResourceMethodDescriptor> entry : descriptor.getMethods().entrySet()) {
|
||||||
Method method = entry.getKey();
|
Method method = entry.getKey();
|
||||||
ResourceMethodDescriptor methodDescriptor = entry.getValue();
|
ResourceMethodDescriptor methodDescriptor = entry.getValue();
|
||||||
int index = getPropertyIndex(methodDescriptor.getPropertyName());
|
int index = propertyIndexes.get(methodDescriptor.getPropertyName());
|
||||||
switch (methodDescriptor.getType()) {
|
switch (methodDescriptor.getType()) {
|
||||||
case GETTER:
|
case GETTER:
|
||||||
methods.put(method, new BuildTimeResourceGetter(index));
|
methods.put(method, new BuildTimeResourceGetter(index));
|
||||||
|
@ -109,9 +113,5 @@ class BuildTimeResourceProxyBuilder {
|
||||||
|
|
||||||
return new BuildTimeResourceProxyFactory(methods, initialData, descriptor);
|
return new BuildTimeResourceProxyFactory(methods, initialData, descriptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int getPropertyIndex(String propertyName) {
|
|
||||||
return propertyIndexes.computeIfAbsent(propertyName, k -> propertyIndexes.size());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,13 +166,13 @@ public class MetadataCIntrinsic implements Intrinsic {
|
||||||
|
|
||||||
for (String propertyName : structure.getPropertyTypes().keySet()) {
|
for (String propertyName : structure.getPropertyTypes().keySet()) {
|
||||||
Class<?> propertyType = structure.getPropertyTypes().get(propertyName);
|
Class<?> propertyType = structure.getPropertyTypes().get(propertyName);
|
||||||
structuresWriter.println(typeToString(context, propertyType) + " " + propertyName + ";");
|
structuresWriter.println(typeToString(propertyType) + " " + propertyName + ";");
|
||||||
}
|
}
|
||||||
|
|
||||||
structuresWriter.outdent().println("} " + structureName + ";");
|
structuresWriter.outdent().println("} " + structureName + ";");
|
||||||
}
|
}
|
||||||
|
|
||||||
private String typeToString(IntrinsicContext context, Class<?> cls) {
|
private String typeToString(Class<?> cls) {
|
||||||
if (cls == boolean.class || cls == byte.class) {
|
if (cls == boolean.class || cls == byte.class) {
|
||||||
return "int8_t";
|
return "int8_t";
|
||||||
} else if (cls == short.class || cls == char.class) {
|
} else if (cls == short.class || cls == char.class) {
|
||||||
|
|
|
@ -39,6 +39,8 @@ import org.teavm.model.MethodReference;
|
||||||
import org.teavm.platform.metadata.MetadataGenerator;
|
import org.teavm.platform.metadata.MetadataGenerator;
|
||||||
import org.teavm.platform.metadata.MetadataProvider;
|
import org.teavm.platform.metadata.MetadataProvider;
|
||||||
import org.teavm.platform.metadata.Resource;
|
import org.teavm.platform.metadata.Resource;
|
||||||
|
import org.teavm.platform.metadata.ResourceArray;
|
||||||
|
import org.teavm.platform.metadata.ResourceMap;
|
||||||
|
|
||||||
public class MetadataIntrinsic implements WasmIntrinsic {
|
public class MetadataIntrinsic implements WasmIntrinsic {
|
||||||
private ClassReaderSource classSource;
|
private ClassReaderSource classSource;
|
||||||
|
@ -85,6 +87,22 @@ public class MetadataIntrinsic implements WasmIntrinsic {
|
||||||
private int writeValue(BinaryWriter writer, WasmStringPool stringPool, Object value) {
|
private int writeValue(BinaryWriter writer, WasmStringPool stringPool, Object value) {
|
||||||
if (value instanceof String) {
|
if (value instanceof String) {
|
||||||
return stringPool.getStringPointer((String) value);
|
return stringPool.getStringPointer((String) value);
|
||||||
|
} else if (value instanceof Boolean) {
|
||||||
|
DataValue dataValue = DataPrimitives.BYTE.createValue();
|
||||||
|
dataValue.setByte(0, (Boolean) value ? (byte) 1 : 0);
|
||||||
|
return writer.append(dataValue);
|
||||||
|
} else if (value instanceof Integer) {
|
||||||
|
DataValue dataValue = DataPrimitives.INT.createValue();
|
||||||
|
dataValue.setInt(0, (Integer) value);
|
||||||
|
return writer.append(dataValue);
|
||||||
|
} else if (value instanceof Long) {
|
||||||
|
DataValue dataValue = DataPrimitives.LONG.createValue();
|
||||||
|
dataValue.setLong(0, (Long) value);
|
||||||
|
return writer.append(dataValue);
|
||||||
|
} else if (value instanceof ResourceMap) {
|
||||||
|
return writeResource(writer, stringPool, (ResourceMap<?>) value);
|
||||||
|
} else if (value instanceof ResourceArray) {
|
||||||
|
return writeResource(writer, stringPool, (ResourceArray<?>) value);
|
||||||
} else if (value instanceof ResourceTypeDescriptorProvider && value instanceof Resource) {
|
} else if (value instanceof ResourceTypeDescriptorProvider && value instanceof Resource) {
|
||||||
return writeResource(writer, stringPool, (ResourceTypeDescriptorProvider) value);
|
return writeResource(writer, stringPool, (ResourceTypeDescriptorProvider) value);
|
||||||
} else {
|
} else {
|
||||||
|
@ -109,6 +127,90 @@ public class MetadataIntrinsic implements WasmIntrinsic {
|
||||||
return address;
|
return address;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int writeResource(BinaryWriter writer, WasmStringPool stringPool, ResourceArray<?> resourceArray) {
|
||||||
|
DataValue sizeValue = DataPrimitives.ADDRESS.createValue();
|
||||||
|
int start = writer.append(sizeValue);
|
||||||
|
sizeValue.setAddress(0, resourceArray.size());
|
||||||
|
|
||||||
|
DataValue[] arrayValues = new DataValue[resourceArray.size()];
|
||||||
|
for (int i = 0; i < resourceArray.size(); ++i) {
|
||||||
|
arrayValues[i] = DataPrimitives.ADDRESS.createValue();
|
||||||
|
writer.append(arrayValues[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < resourceArray.size(); ++i) {
|
||||||
|
arrayValues[i].setAddress(0, writeValue(writer, stringPool, resourceArray.get(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
@ -134,6 +236,10 @@ public class MetadataIntrinsic implements WasmIntrinsic {
|
||||||
target.setAddress(index, address);
|
target.setAddress(index, address);
|
||||||
} else if (value == null) {
|
} else if (value == null) {
|
||||||
target.setAddress(index, 0);
|
target.setAddress(index, 0);
|
||||||
|
} else if (value instanceof ResourceMap) {
|
||||||
|
target.setAddress(index, writeResource(writer, stringPool, (ResourceMap<?>) value));
|
||||||
|
} else if (value instanceof ResourceArray) {
|
||||||
|
target.setAddress(index, writeResource(writer, stringPool, (ResourceArray<?>) value));
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalArgumentException("Don't know how to write resource: " + value);
|
throw new IllegalArgumentException("Don't know how to write resource: " + value);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,23 +21,44 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import org.teavm.ast.InvocationExpr;
|
import org.teavm.ast.InvocationExpr;
|
||||||
|
import org.teavm.backend.wasm.WasmRuntime;
|
||||||
import org.teavm.backend.wasm.binary.BinaryWriter;
|
import org.teavm.backend.wasm.binary.BinaryWriter;
|
||||||
import org.teavm.backend.wasm.generate.WasmClassGenerator;
|
import org.teavm.backend.wasm.generate.WasmClassGenerator;
|
||||||
import org.teavm.backend.wasm.intrinsics.WasmIntrinsic;
|
import org.teavm.backend.wasm.intrinsics.WasmIntrinsic;
|
||||||
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager;
|
import org.teavm.backend.wasm.intrinsics.WasmIntrinsicManager;
|
||||||
|
import org.teavm.backend.wasm.model.WasmLocal;
|
||||||
|
import org.teavm.backend.wasm.model.WasmType;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmBlock;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmBranch;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmDrop;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmInt32Subtype;
|
import org.teavm.backend.wasm.model.expression.WasmInt32Subtype;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmInt64Subtype;
|
import org.teavm.backend.wasm.model.expression.WasmInt64Subtype;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntBinary;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntBinaryOperation;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmIntType;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmLoadFloat32;
|
import org.teavm.backend.wasm.model.expression.WasmLoadFloat32;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
|
import org.teavm.backend.wasm.model.expression.WasmLoadFloat64;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
import org.teavm.backend.wasm.model.expression.WasmLoadInt32;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
|
import org.teavm.backend.wasm.model.expression.WasmLoadInt64;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||||
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.platform.metadata.Resource;
|
import org.teavm.platform.metadata.Resource;
|
||||||
|
import org.teavm.platform.metadata.ResourceArray;
|
||||||
|
import org.teavm.platform.metadata.ResourceMap;
|
||||||
|
|
||||||
public class ResourceReadIntrinsic implements WasmIntrinsic {
|
public class ResourceReadIntrinsic implements WasmIntrinsic {
|
||||||
|
private static final MethodReference LOOKUP_METHOD = new MethodReference(WasmRuntime.class,
|
||||||
|
"lookupResource", Address.class, String.class, Address.class);
|
||||||
|
private static final MethodReference KEYS_METHOD = new MethodReference(WasmRuntime.class,
|
||||||
|
"resourceMapKeys", Address.class, String[].class);
|
||||||
|
|
||||||
private ClassReaderSource classSource;
|
private ClassReaderSource classSource;
|
||||||
private ClassLoader classLoader;
|
private ClassLoader classLoader;
|
||||||
private Map<String, StructureDescriptor> typeDescriptorCache = new HashMap<>();
|
private Map<String, StructureDescriptor> typeDescriptorCache = new HashMap<>();
|
||||||
|
@ -54,6 +75,12 @@ public class ResourceReadIntrinsic implements WasmIntrinsic {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager manager) {
|
||||||
|
if (invocation.getMethod().getClassName().equals(ResourceMap.class.getName())) {
|
||||||
|
return applyForResourceMap(manager, invocation);
|
||||||
|
} else if (invocation.getMethod().getClassName().equals(ResourceArray.class.getName())) {
|
||||||
|
return applyForResourceArray(manager, invocation);
|
||||||
|
}
|
||||||
|
|
||||||
StructureDescriptor typeDescriptor = getTypeDescriptor(invocation.getMethod().getClassName());
|
StructureDescriptor typeDescriptor = getTypeDescriptor(invocation.getMethod().getClassName());
|
||||||
PropertyDescriptor property = typeDescriptor.layout.get(invocation.getMethod());
|
PropertyDescriptor property = typeDescriptor.layout.get(invocation.getMethod());
|
||||||
|
|
||||||
|
@ -82,6 +109,67 @@ public class ResourceReadIntrinsic implements WasmIntrinsic {
|
||||||
return new WasmLoadInt32(4, base, WasmInt32Subtype.INT32, property.offset);
|
return new WasmLoadInt32(4, base, WasmInt32Subtype.INT32, property.offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private WasmExpression applyForResourceArray(WasmIntrinsicManager manager, InvocationExpr invocation) {
|
||||||
|
switch (invocation.getMethod().getName()) {
|
||||||
|
case "get": {
|
||||||
|
WasmExpression map = manager.generate(invocation.getArguments().get(0));
|
||||||
|
WasmExpression index = manager.generate(invocation.getArguments().get(1));
|
||||||
|
WasmExpression offset = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.SHL,
|
||||||
|
index, new WasmInt32Constant(2));
|
||||||
|
WasmExpression address = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
|
||||||
|
map, offset);
|
||||||
|
return new WasmLoadInt32(4, address, WasmInt32Subtype.INT32, 4);
|
||||||
|
}
|
||||||
|
case "size":
|
||||||
|
return new WasmLoadInt32(4, manager.generate(invocation.getArguments().get(0)),
|
||||||
|
WasmInt32Subtype.INT32, 0);
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private WasmExpression applyForResourceMap(WasmIntrinsicManager manager, InvocationExpr invocation) {
|
||||||
|
switch (invocation.getMethod().getName()) {
|
||||||
|
case "keys": {
|
||||||
|
WasmExpression map = manager.generate(invocation.getArguments().get(0));
|
||||||
|
WasmCall call = new WasmCall(manager.getNames().forMethod(KEYS_METHOD));
|
||||||
|
call.getArguments().add(map);
|
||||||
|
return call;
|
||||||
|
}
|
||||||
|
case "has": {
|
||||||
|
WasmExpression map = manager.generate(invocation.getArguments().get(0));
|
||||||
|
WasmExpression key = manager.generate(invocation.getArguments().get(1));
|
||||||
|
WasmCall call = new WasmCall(manager.getNames().forMethod(LOOKUP_METHOD));
|
||||||
|
call.getArguments().add(map);
|
||||||
|
call.getArguments().add(key);
|
||||||
|
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.NE, call,
|
||||||
|
new WasmInt32Constant(0));
|
||||||
|
}
|
||||||
|
case "get": {
|
||||||
|
WasmBlock block = new WasmBlock(false);
|
||||||
|
block.setType(WasmType.INT32);
|
||||||
|
|
||||||
|
WasmExpression map = manager.generate(invocation.getArguments().get(0));
|
||||||
|
WasmExpression key = manager.generate(invocation.getArguments().get(1));
|
||||||
|
WasmCall call = new WasmCall(manager.getNames().forMethod(LOOKUP_METHOD));
|
||||||
|
call.getArguments().add(map);
|
||||||
|
call.getArguments().add(key);
|
||||||
|
WasmLocal entryVar = manager.getTemporary(WasmType.INT32);
|
||||||
|
block.getBody().add(new WasmSetLocal(entryVar, call));
|
||||||
|
|
||||||
|
WasmBranch ifNull = new WasmBranch(new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.EQ,
|
||||||
|
new WasmGetLocal(entryVar), new WasmInt32Constant(0)), block);
|
||||||
|
ifNull.setResult(new WasmInt32Constant(0));
|
||||||
|
block.getBody().add(new WasmDrop(ifNull));
|
||||||
|
|
||||||
|
block.getBody().add(new WasmLoadInt32(4, new WasmGetLocal(entryVar), WasmInt32Subtype.INT32, 4));
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
throw new AssertionError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private StructureDescriptor getTypeDescriptor(String className) {
|
private StructureDescriptor getTypeDescriptor(String className) {
|
||||||
return typeDescriptorCache.computeIfAbsent(className, n -> {
|
return typeDescriptorCache.computeIfAbsent(className, n -> {
|
||||||
Class<?> cls;
|
Class<?> cls;
|
||||||
|
|
Loading…
Reference in New Issue
Block a user