Skip to content

Commit 17bc74b

Browse files
committed
reimplement imported functions for Wasm target
Previously, TeaVM used imported functions for timezone resolution, char case conversion, and floating point tests. Since WASI doesn't support any of those things, we implement them in Java as best we can. Floating point tests are easy; timezone resolution will have to wait for WebAssembly/WASI#467 (we hard-code UTC for now); case conversion can be done entirely in Java, although I've only handled ASCII characters in this commit. Signed-off-by: Joel Dice <[email protected]>
1 parent 35b56d4 commit 17bc74b

File tree

6 files changed

+170
-15
lines changed

6 files changed

+170
-15
lines changed

classlib/src/main/java/org/teavm/classlib/impl/tz/DateTimeZoneProvider.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.util.Map;
2424
import java.util.PriorityQueue;
2525
import java.util.Set;
26+
import org.teavm.classlib.PlatformDetector;
2627
import org.teavm.classlib.impl.Base46;
2728
import org.teavm.classlib.impl.CharFlow;
2829
import org.teavm.interop.Import;
@@ -181,10 +182,21 @@ private static TimeZoneResource getTimeZoneResource(String id) {
181182
return area.has(locationName) ? area.get(locationName) : null;
182183
}
183184

185+
private static int getNativeOffset(double instant) {
186+
if (PlatformDetector.isWebAssembly()) {
187+
// See https://github.com/WebAssembly/WASI/issues/467.
188+
//
189+
// For now, we always return UTC
190+
return 0;
191+
} else {
192+
return getNativeOffsetSystem(instant);
193+
}
194+
}
195+
184196
@JSBody(params = "instant", script = "return new Date(instant).getTimezoneOffset();")
185197
@Import(module = "teavm", name = "getNativeOffset")
186198
@Unmanaged
187-
private static native int getNativeOffset(double instant);
199+
private static native int getNativeOffsetSystem(double instant);
188200

189201
private static native ResourceMap<ResourceMap<TimeZoneResource>> getResource();
190202
}

classlib/src/main/java/org/teavm/classlib/java/lang/TCharacter.java

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.teavm.classlib.java.lang;
1717

18+
import org.teavm.classlib.PlatformDetector;
1819
import org.teavm.classlib.impl.unicode.UnicodeHelper;
1920
import org.teavm.interop.DelegateTo;
2021
import org.teavm.interop.Import;
@@ -235,8 +236,21 @@ public static char toLowerCase(char ch) {
235236
return (char) toLowerCase((int) ch);
236237
}
237238

238-
@DelegateTo("toLowerCaseLowLevel")
239239
public static int toLowerCase(int ch) {
240+
if (PlatformDetector.isWebAssembly()) {
241+
// TODO: Implement this for non-ascii chars.
242+
if (ch >= 'A' && ch <= 'Z') {
243+
return (ch - 'A') + 'a';
244+
} else {
245+
return ch;
246+
}
247+
} else {
248+
return toLowerCasePlatform(ch);
249+
}
250+
}
251+
252+
@DelegateTo("toLowerCaseLowLevel")
253+
private static int toLowerCasePlatform(int ch) {
240254
return Platform.stringFromCharCode(ch).toLowerCase().charCodeAt(0);
241255
}
242256

@@ -253,8 +267,21 @@ public static char toUpperCase(char ch) {
253267
return (char) toUpperCase((int) ch);
254268
}
255269

270+
public static int toUpperCase(int ch) {
271+
if (PlatformDetector.isWebAssembly()) {
272+
// TODO: Implement this for non-ascii chars.
273+
if (ch >= 'a' && ch <= 'z') {
274+
return (ch - 'a') + 'A';
275+
} else {
276+
return ch;
277+
}
278+
} else {
279+
return toLowerCasePlatform(ch);
280+
}
281+
}
282+
256283
@DelegateTo("toUpperCaseLowLevel")
257-
public static int toUpperCase(int codePoint) {
284+
public static int toUpperCasePlatform(int codePoint) {
258285
return Platform.stringFromCharCode(codePoint).toUpperCase().charCodeAt(0);
259286
}
260287

classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.teavm.classlib.java.lang;
1717

1818
import org.teavm.backend.javascript.spi.InjectedBy;
19+
import org.teavm.classlib.PlatformDetector;
1920
import org.teavm.interop.Import;
2021
import org.teavm.interop.NoSideEffects;
2122
import org.teavm.interop.Unmanaged;
@@ -249,29 +250,61 @@ public boolean isInfinite() {
249250
return isInfinite(value);
250251
}
251252

253+
public static boolean isNaN(double v) {
254+
if (PlatformDetector.isWebAssembly()) {
255+
return v != v;
256+
} else {
257+
return nativeIsNaN(v);
258+
}
259+
}
260+
252261
@JSBody(params = "v", script = "return isNaN(v);")
253262
@Import(module = "teavm", name = "isnan")
254263
@NoSideEffects
255264
@Unmanaged
256-
public static native boolean isNaN(double v);
265+
private static native boolean nativeIsNaN(double v);
257266

258-
@JSBody(script = "return NaN;")
259-
@Import(module = "teavm", name = "teavm_getNaN")
260-
@NoSideEffects
261-
@Unmanaged
262-
private static native double getNaN();
267+
public static boolean isInfinite(double v) {
268+
if (PlatformDetector.isWebAssembly()) {
269+
return v == (1d / 0d) || v == (-1d / 0d);
270+
} else {
271+
return nativeIsInfinite(v);
272+
}
273+
}
263274

264275
@JSBody(params = "v", script = "return !isFinite(v);")
265276
@Import(module = "teavm", name = "isinf")
266277
@NoSideEffects
267278
@Unmanaged
268-
public static native boolean isInfinite(double v);
279+
public static native boolean nativeIsInfinite(double v);
280+
281+
public static boolean isFinite(double v) {
282+
if (PlatformDetector.isWebAssembly()) {
283+
return v < (1d / 0d) && v > (-1d / 0d);
284+
} else {
285+
return nativeIsFinite(v);
286+
}
287+
}
269288

270289
@JSBody(params = "v", script = "return isFinite(v);")
271290
@Import(module = "teavm", name = "isfinite")
272291
@NoSideEffects
273292
@Unmanaged
274-
public static native boolean isFinite(double v);
293+
public static native boolean nativeIsFinite(double v);
294+
295+
public static double getNaN() {
296+
if (PlatformDetector.isWebAssembly()) {
297+
return 0d / 0d;
298+
} else {
299+
return nativeGetNaN();
300+
}
301+
}
302+
303+
@JSBody(script = "return NaN;")
304+
@Import(module = "teavm", name = "teavm_getNaN")
305+
@NoSideEffects
306+
@Unmanaged
307+
private static native double nativeGetNaN();
275308

276309
public static long doubleToRawLongBits(double value) {
277310
return doubleToLongBits(value);

classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*/
1616
package org.teavm.classlib.java.lang;
1717

18+
import org.teavm.classlib.PlatformDetector;
1819
import org.teavm.interop.Import;
1920
import org.teavm.interop.NoSideEffects;
2021
import org.teavm.interop.Unmanaged;
@@ -95,29 +96,61 @@ public static int hashCode(float f) {
9596
return floatToIntBits(f);
9697
}
9798

99+
public static boolean isNaN(float v) {
100+
if (PlatformDetector.isWebAssembly()) {
101+
return v != v;
102+
} else {
103+
return nativeIsNaN(v);
104+
}
105+
}
106+
98107
@JSBody(params = "v", script = "return isNaN(v);")
99108
@Import(module = "teavm", name = "isnan")
100109
@NoSideEffects
101110
@Unmanaged
102-
public static native boolean isNaN(float v);
111+
private static native boolean nativeIsNaN(float v);
112+
113+
public static boolean isInfinite(float v) {
114+
if (PlatformDetector.isWebAssembly()) {
115+
return v == (1f / 0f) || v == (-1f / 0f);
116+
} else {
117+
return nativeIsInfinite(v);
118+
}
119+
}
103120

104121
@JSBody(params = "v", script = "return !isFinite(v);")
105122
@Import(module = "teavm", name = "isinf")
106123
@NoSideEffects
107124
@Unmanaged
108-
public static native boolean isInfinite(float v);
125+
public static native boolean nativeIsInfinite(float v);
126+
127+
public static boolean isFinite(float v) {
128+
if (PlatformDetector.isWebAssembly()) {
129+
return v < (1f / 0f) && v > (-1f / 0f);
130+
} else {
131+
return nativeIsFinite(v);
132+
}
133+
}
109134

110135
@JSBody(params = "v", script = "return isFinite(v);")
111136
@Import(module = "teavm", name = "isfinite")
112137
@NoSideEffects
113138
@Unmanaged
114-
public static native boolean isFinite(float v);
139+
public static native boolean nativeIsFinite(float v);
140+
141+
public static float getNaN() {
142+
if (PlatformDetector.isWebAssembly()) {
143+
return 0f / 0f;
144+
} else {
145+
return nativeGetNaN();
146+
}
147+
}
115148

116149
@JSBody(script = "return NaN;")
117150
@Import(module = "teavm", name = "teavm_getNaN")
118151
@NoSideEffects
119152
@Unmanaged
120-
private static native float getNaN();
153+
private static native float nativeGetNaN();
121154

122155
public static float parseFloat(TString string) throws TNumberFormatException {
123156
// TODO: parse infinite and different radix

tests/wasi/src/main/java/wasi/Test.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.io.RandomAccessFile;
1111
import java.nio.charset.StandardCharsets;
1212
import java.util.Arrays;
13+
import java.util.TimeZone;
1314
import org.teavm.interop.Export;
1415

1516
public class Test {
@@ -36,6 +37,43 @@ public static void env() throws IOException {
3637
System.out.print(System.getenv(var2));
3738
}
3839

40+
@Export(name = "floats")
41+
public static void floats() throws IOException {
42+
String string = readString();
43+
int index = string.indexOf('/');
44+
float n1 = Float.parseFloat(string.substring(0, index));
45+
float n2 = Float.parseFloat(string.substring(index + 1));
46+
System.out.print("" + Float.isNaN(n1 / n2));
47+
System.out.print(":" + Float.isFinite(n1 / n2));
48+
System.out.print(":" + Float.isInfinite(n1 / n2));
49+
}
50+
51+
@Export(name = "doubles")
52+
public static void doubles() throws IOException {
53+
String string = readString();
54+
int index = string.indexOf('/');
55+
double n1 = Double.parseDouble(string.substring(0, index));
56+
double n2 = Double.parseDouble(string.substring(index + 1));
57+
System.out.print("" + Double.isNaN(n1 / n2));
58+
System.out.print(":" + Double.isFinite(n1 / n2));
59+
System.out.print(":" + Double.isInfinite(n1 / n2));
60+
}
61+
62+
@Export(name = "upper")
63+
public static void upper() throws IOException {
64+
System.out.print(readString().toUpperCase());
65+
}
66+
67+
@Export(name = "lower")
68+
public static void lower() throws IOException {
69+
System.out.print(readString().toLowerCase());
70+
}
71+
72+
@Export(name = "timezone")
73+
public static void timezone() throws IOException {
74+
System.out.print(TimeZone.getDefault().getOffset(Long.parseLong(readString())));
75+
}
76+
3977
@Export(name = "catch")
4078
public static void doCatch() {
4179
try {

tests/wasi/test.sh

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,18 @@ function expect_nonexistence {
4444

4545
expect_eq "foo bar baz" "$($runtime $wasm foo bar baz)"
4646

47+
for which in floats doubles; do
48+
expect_eq "false:true:false" "$($runtime --invoke $which $wasm <<<1/2)"
49+
expect_eq "false:false:true" "$($runtime --invoke $which $wasm <<<1/0)"
50+
expect_eq "false:false:true" "$($runtime --invoke $which $wasm <<<-1/0)"
51+
expect_eq "true:false:false" "$($runtime --invoke $which $wasm <<<0/0)"
52+
done
53+
54+
expect_eq "alifuelzb89" "$($runtime --invoke lower $wasm <<<alIFUElzB89)"
55+
expect_eq "ALIFUELZB89" "$($runtime --invoke upper $wasm <<<alIFUElzB89)"
56+
57+
expect_eq 0 "$($runtime --invoke timezone $wasm <<<1660079800000)"
58+
4759
expect_eq 42713 "$($runtime --env foo=42 --env bar=713 --invoke env $wasm <<<foo:bar)"
4860

4961
expect_eq hello "$($runtime --invoke catch $wasm <<<hello)"

0 commit comments

Comments
 (0)