Skip to content

Commit

Permalink
Wasm backend: implement remaining types of resources
Browse files Browse the repository at this point in the history
  • Loading branch information
konsoletyper committed May 20, 2018
1 parent 5ce48ce commit 097820c
Show file tree
Hide file tree
Showing 12 changed files with 376 additions and 12 deletions.
6 changes: 6 additions & 0 deletions classlib/src/main/java/org/teavm/classlib/impl/JCLPlugin.java
Expand Up @@ -21,6 +21,7 @@
import java.util.ServiceLoader;
import org.teavm.backend.c.TeaVMCHost;
import org.teavm.backend.javascript.TeaVMJavaScriptHost;
import org.teavm.backend.wasm.TeaVMWasmHost;
import org.teavm.classlib.ReflectionSupplier;
import org.teavm.classlib.impl.lambda.LambdaMetafactorySubstitutor;
import org.teavm.classlib.impl.tz.DateTimeZoneProviderIntrinsic;
Expand Down Expand Up @@ -92,6 +93,11 @@ public void install(TeaVMHost host) {
if (cHost != null) {
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);
Expand Down
Expand Up @@ -26,6 +26,7 @@
import org.teavm.backend.javascript.spi.GeneratedBy;
import org.teavm.classlib.impl.Base46;
import org.teavm.classlib.impl.CharFlow;
import org.teavm.interop.Import;
import org.teavm.jso.JSBody;
import org.teavm.platform.metadata.MetadataProvider;
import org.teavm.platform.metadata.ResourceMap;
Expand Down Expand Up @@ -189,6 +190,7 @@ private static TimeZoneResource getTimeZoneResource(String id) {
}

@JSBody(params = "instant", script = "return new Date(instant).getTimezoneOffset();")
@Import(module = "teavm", name = "getNativeOffset")
private static native int getNativeOffset(double instant);

@MetadataProvider(TimeZoneGenerator.class)
Expand Down
Expand Up @@ -19,9 +19,13 @@
import org.teavm.ast.InvocationExpr;
import org.teavm.backend.c.intrinsic.Intrinsic;
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;

public class DateTimeZoneProviderIntrinsic implements Intrinsic {
public class DateTimeZoneProviderIntrinsic implements Intrinsic, WasmIntrinsic {
private Properties properties;

public DateTimeZoneProviderIntrinsic(Properties properties) {
Expand All @@ -43,6 +47,20 @@ public boolean canHandle(MethodReference method) {
}
}

@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
public void apply(IntrinsicContext context, InvocationExpr invocation) {
switch (invocation.getMethod().getName()) {
Expand All @@ -56,4 +74,16 @@ public void apply(IntrinsicContext context, InvocationExpr invocation) {
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();
}
}
}
94 changes: 94 additions & 0 deletions core/src/main/java/org/teavm/backend/wasm/WasmRuntime.java
Expand Up @@ -19,6 +19,7 @@
import org.teavm.interop.Import;
import org.teavm.interop.StaticInit;
import org.teavm.interop.Unmanaged;
import org.teavm.runtime.RuntimeObject;

@StaticInit
@Unmanaged
Expand Down Expand Up @@ -282,4 +283,97 @@ public static int getCallSiteId(Address stackFrame) {
public static void setExceptionHandlerId(Address stackFrame, int 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;
}
}
8 changes: 6 additions & 2 deletions core/src/main/java/org/teavm/backend/wasm/WasmTarget.java
Expand Up @@ -266,6 +266,10 @@ public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
int.class, void.class), null).use();
dependencyAnalyzer.linkMethod(new MethodReference(WasmRuntime.class, "getCallSiteId", Address.class,
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",
RuntimeClass.class, Address.class), null).use();
Expand Down Expand Up @@ -466,7 +470,7 @@ private void emitWast(WasmModule module, BuildTarget buildTarget, String outputN
renderer.setLineNumbersEmitted(debugging);
renderer.render(module);
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());
}
}
Expand All @@ -477,7 +481,7 @@ private void emitC(WasmModule module, BuildTarget buildTarget, String outputName
renderer.setMemoryAccessChecked(Boolean.parseBoolean(System.getProperty("wasm.c.assertMemory", "false")));
renderer.render(module);
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());
}
}
Expand Down
Expand Up @@ -41,6 +41,7 @@
import org.teavm.interop.Address;
import org.teavm.model.MethodReference;
import org.teavm.model.ValueType;
import org.teavm.runtime.RuntimeArray;

public class AddressIntrinsic implements WasmIntrinsic {
private WasmClassGenerator classGenerator;
Expand Down Expand Up @@ -159,13 +160,40 @@ public WasmExpression apply(InvocationExpr invocation, WasmIntrinsicManager mana
.collect(Collectors.toList()));
return call;
}
case "isLessThan": {
case "isLessThan":
return new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.LT_UNSIGNED,
manager.generate(invocation.getArguments().get(0)),
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:
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;
}
}
Expand Up @@ -34,6 +34,9 @@ TeaVM.wasm = function() {
function currentTimeMillis() {
return new Date().getTime();
}
function getNativeOffset(instant) {
return new Date(instant).getTimezoneOffset();
}

function importDefaults(obj) {
obj.teavm = {
Expand All @@ -44,7 +47,8 @@ TeaVM.wasm = function() {
isfinite: isFinite,
putwchar: putwchar,
towlower: towlower,
towupper: towupper
towupper: towupper,
getNativeOffset: getNativeOffset
};

obj.teavmMath = Math;
Expand Down
2 changes: 2 additions & 0 deletions interop/core/src/main/java/org/teavm/interop/Address.java
Expand Up @@ -82,6 +82,8 @@ public final class Address {

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 int sizeOf();
Expand Down
Expand Up @@ -53,13 +53,17 @@ private static class ProxyFactoryCreation {

public ProxyFactoryCreation(ResourceTypeDescriptor descriptor) {
this.descriptor = descriptor;
int index = 0;
for (String propertyName : descriptor.getPropertyTypes().keySet()) {
propertyIndexes.put(propertyName, index++);
}
}

BuildTimeResourceProxyFactory create() {
for (Map.Entry<Method, ResourceMethodDescriptor> entry : descriptor.getMethods().entrySet()) {
Method method = entry.getKey();
ResourceMethodDescriptor methodDescriptor = entry.getValue();
int index = getPropertyIndex(methodDescriptor.getPropertyName());
int index = propertyIndexes.get(methodDescriptor.getPropertyName());
switch (methodDescriptor.getType()) {
case GETTER:
methods.put(method, new BuildTimeResourceGetter(index));
Expand Down Expand Up @@ -109,9 +113,5 @@ BuildTimeResourceProxyFactory create() {

return new BuildTimeResourceProxyFactory(methods, initialData, descriptor);
}

private int getPropertyIndex(String propertyName) {
return propertyIndexes.computeIfAbsent(propertyName, k -> propertyIndexes.size());
}
}
}
Expand Up @@ -166,13 +166,13 @@ private void writeResourceStructure(IntrinsicContext context, ResourceTypeDescri

for (String propertyName : structure.getPropertyTypes().keySet()) {
Class<?> propertyType = structure.getPropertyTypes().get(propertyName);
structuresWriter.println(typeToString(context, propertyType) + " " + propertyName + ";");
structuresWriter.println(typeToString(propertyType) + " " + propertyName + ";");
}

structuresWriter.outdent().println("} " + structureName + ";");
}

private String typeToString(IntrinsicContext context, Class<?> cls) {
private String typeToString(Class<?> cls) {
if (cls == boolean.class || cls == byte.class) {
return "int8_t";
} else if (cls == short.class || cls == char.class) {
Expand Down

0 comments on commit 097820c

Please sign in to comment.