mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
wasm gc: draft Wasm GC backend
This commit is contained in:
parent
9f12917de9
commit
a281c19363
|
@ -32,6 +32,11 @@ public final class PlatformDetector {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PlatformMarker(Platforms.WEBASSEMBLY_GC)
|
||||||
|
public static boolean isWebAssemblyGC() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@PlatformMarker(Platforms.JAVASCRIPT)
|
@PlatformMarker(Platforms.JAVASCRIPT)
|
||||||
public static boolean isJavaScript() {
|
public static boolean isJavaScript() {
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package org.teavm.classlib.impl.console;
|
package org.teavm.classlib.impl.console;
|
||||||
|
|
||||||
import org.teavm.backend.c.intrinsic.RuntimeInclude;
|
import org.teavm.backend.c.intrinsic.RuntimeInclude;
|
||||||
|
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||||
import org.teavm.backend.wasm.runtime.WasmSupport;
|
import org.teavm.backend.wasm.runtime.WasmSupport;
|
||||||
import org.teavm.classlib.PlatformDetector;
|
import org.teavm.classlib.PlatformDetector;
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
|
@ -35,6 +36,10 @@ public final class Console {
|
||||||
}
|
}
|
||||||
} else if (PlatformDetector.isWebAssembly()) {
|
} else if (PlatformDetector.isWebAssembly()) {
|
||||||
WasmSupport.putCharsStderr(Address.ofData(data).add(off), len);
|
WasmSupport.putCharsStderr(Address.ofData(data).add(off), len);
|
||||||
|
} else if (PlatformDetector.isWebAssemblyGC()) {
|
||||||
|
for (var i = 0; i < len; ++i) {
|
||||||
|
WasmGCSupport.putCharStderr((char) data[off + i]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < len; ++i) {
|
for (int i = 0; i < len; ++i) {
|
||||||
byte b = data[i + off];
|
byte b = data[i + off];
|
||||||
|
@ -51,6 +56,10 @@ public final class Console {
|
||||||
}
|
}
|
||||||
} else if (PlatformDetector.isWebAssembly()) {
|
} else if (PlatformDetector.isWebAssembly()) {
|
||||||
WasmSupport.putCharsStdout(Address.ofData(data).add(off), len);
|
WasmSupport.putCharsStdout(Address.ofData(data).add(off), len);
|
||||||
|
} else if (PlatformDetector.isWebAssemblyGC()) {
|
||||||
|
for (var i = 0; i < len; ++i) {
|
||||||
|
WasmGCSupport.putCharStdout((char) data[off + i]);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
for (int i = 0; i < len; ++i) {
|
for (int i = 0; i < len; ++i) {
|
||||||
byte b = data[i + off];
|
byte b = data[i + off];
|
||||||
|
|
|
@ -15,13 +15,21 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.impl.console;
|
package org.teavm.classlib.impl.console;
|
||||||
|
|
||||||
|
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||||
|
import org.teavm.classlib.PlatformDetector;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
|
|
||||||
public class JSStderrPrintStream extends JsConsolePrintStream {
|
public class JSStderrPrintStream extends JsConsolePrintStream {
|
||||||
@Override
|
@Override
|
||||||
public void print(String s) {
|
public void print(String s) {
|
||||||
|
if (PlatformDetector.isWebAssemblyGC()) {
|
||||||
|
for (int i = 0; i < s.length(); ++i) {
|
||||||
|
WasmGCSupport.putCharStderr(s.charAt(i));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
writeJs(s);
|
writeJs(s);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@JSBody(params = "b", script = "$rt_putStderr(b);")
|
@JSBody(params = "b", script = "$rt_putStderr(b);")
|
||||||
private static native void writeJs(String s);
|
private static native void writeJs(String s);
|
||||||
|
|
|
@ -15,13 +15,21 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.classlib.impl.console;
|
package org.teavm.classlib.impl.console;
|
||||||
|
|
||||||
|
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||||
|
import org.teavm.classlib.PlatformDetector;
|
||||||
import org.teavm.jso.JSBody;
|
import org.teavm.jso.JSBody;
|
||||||
|
|
||||||
public class JSStdoutPrintStream extends JsConsolePrintStream {
|
public class JSStdoutPrintStream extends JsConsolePrintStream {
|
||||||
@Override
|
@Override
|
||||||
public void print(String s) {
|
public void print(String s) {
|
||||||
|
if (PlatformDetector.isWebAssemblyGC()) {
|
||||||
|
for (int i = 0; i < s.length(); ++i) {
|
||||||
|
WasmGCSupport.putCharStderr(s.charAt(i));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
writeJs(s);
|
writeJs(s);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@JSBody(params = "b", script = "$rt_putStdout(b);")
|
@JSBody(params = "b", script = "$rt_putStdout(b);")
|
||||||
private static native void writeJs(String s);
|
private static native void writeJs(String s);
|
||||||
|
|
|
@ -56,7 +56,7 @@ public final class TSystem extends TObject {
|
||||||
|
|
||||||
public static TPrintStream out() {
|
public static TPrintStream out() {
|
||||||
if (outCache == null) {
|
if (outCache == null) {
|
||||||
if (PlatformDetector.isJavaScript()) {
|
if (PlatformDetector.isJavaScript() || PlatformDetector.isWebAssemblyGC()) {
|
||||||
outCache = (TPrintStream) (Object) new JSStdoutPrintStream();
|
outCache = (TPrintStream) (Object) new JSStdoutPrintStream();
|
||||||
} else {
|
} else {
|
||||||
outCache = new TPrintStream(StdoutOutputStream.INSTANCE, false);
|
outCache = new TPrintStream(StdoutOutputStream.INSTANCE, false);
|
||||||
|
@ -67,7 +67,7 @@ public final class TSystem extends TObject {
|
||||||
|
|
||||||
public static TPrintStream err() {
|
public static TPrintStream err() {
|
||||||
if (errCache == null) {
|
if (errCache == null) {
|
||||||
if (PlatformDetector.isJavaScript()) {
|
if (PlatformDetector.isJavaScript() || PlatformDetector.isWebAssemblyGC()) {
|
||||||
errCache = (TPrintStream) (Object) new JSStderrPrintStream();
|
errCache = (TPrintStream) (Object) new JSStderrPrintStream();
|
||||||
} else {
|
} else {
|
||||||
errCache = new TPrintStream(StderrOutputStream.INSTANCE, false);
|
errCache = new TPrintStream(StderrOutputStream.INSTANCE, false);
|
||||||
|
|
|
@ -19,14 +19,20 @@ import org.teavm.model.util.VariableType;
|
||||||
|
|
||||||
public class VariableNode {
|
public class VariableNode {
|
||||||
private int index;
|
private int index;
|
||||||
|
private int originalIndex;
|
||||||
private VariableType type;
|
private VariableType type;
|
||||||
private String name;
|
private String name;
|
||||||
|
|
||||||
public VariableNode(int index, VariableType type) {
|
public VariableNode(int index, VariableType type) {
|
||||||
this.index = index;
|
this.index = index;
|
||||||
|
this.originalIndex = index;
|
||||||
this.type = type;
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getOriginalIndex() {
|
||||||
|
return originalIndex;
|
||||||
|
}
|
||||||
|
|
||||||
public int getIndex() {
|
public int getIndex() {
|
||||||
return index;
|
return index;
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,18 +92,15 @@ import org.teavm.dependency.DependencyAnalyzer;
|
||||||
import org.teavm.dependency.DependencyListener;
|
import org.teavm.dependency.DependencyListener;
|
||||||
import org.teavm.interop.Address;
|
import org.teavm.interop.Address;
|
||||||
import org.teavm.interop.Platforms;
|
import org.teavm.interop.Platforms;
|
||||||
import org.teavm.model.BasicBlock;
|
|
||||||
import org.teavm.model.ClassHierarchy;
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
import org.teavm.model.ClassHolderTransformer;
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.FieldReader;
|
import org.teavm.model.FieldReader;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.Instruction;
|
|
||||||
import org.teavm.model.ListableClassHolderSource;
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
import org.teavm.model.ListableClassReaderSource;
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodHolder;
|
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
|
@ -111,9 +108,6 @@ import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.classes.TagRegistry;
|
import org.teavm.model.classes.TagRegistry;
|
||||||
import org.teavm.model.classes.VirtualTableBuilder;
|
import org.teavm.model.classes.VirtualTableBuilder;
|
||||||
import org.teavm.model.classes.VirtualTableProvider;
|
import org.teavm.model.classes.VirtualTableProvider;
|
||||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
|
||||||
import org.teavm.model.instructions.InvocationType;
|
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
|
||||||
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
import org.teavm.model.lowlevel.CallSiteDescriptor;
|
||||||
import org.teavm.model.lowlevel.Characteristics;
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||||
|
@ -623,40 +617,11 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
|
||||||
|
|
||||||
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
||||||
VirtualTableBuilder builder = new VirtualTableBuilder(classes);
|
VirtualTableBuilder builder = new VirtualTableBuilder(classes);
|
||||||
builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes));
|
builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes));
|
||||||
builder.setMethodCalledVirtually(controller::isVirtual);
|
builder.setMethodCalledVirtually(controller::isVirtual);
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<MethodReference> getMethodsUsedOnCallSites(ListableClassHolderSource classes) {
|
|
||||||
Set<MethodReference> virtualMethods = new HashSet<>();
|
|
||||||
|
|
||||||
for (String className : classes.getClassNames()) {
|
|
||||||
ClassHolder cls = classes.get(className);
|
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
|
||||||
Program program = method.getProgram();
|
|
||||||
if (program == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
|
||||||
for (Instruction insn : block) {
|
|
||||||
if (insn instanceof InvokeInstruction) {
|
|
||||||
InvokeInstruction invoke = (InvokeInstruction) insn;
|
|
||||||
if (invoke.getType() == InvocationType.VIRTUAL) {
|
|
||||||
virtualMethods.add(invoke.getMethod());
|
|
||||||
}
|
|
||||||
} else if (insn instanceof CloneArrayInstruction) {
|
|
||||||
virtualMethods.add(new MethodReference(Object.class, "clone", Object.class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return virtualMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) {
|
private void generateSpecialFunctions(GenerationContext context, CodeWriter writer) {
|
||||||
IncludeManager includes = new SimpleIncludeManager(context.getFileNames(), writer);
|
IncludeManager includes = new SimpleIncludeManager(context.getFileNames(), writer);
|
||||||
includes.init("special.c");
|
includes.init("special.c");
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm;
|
||||||
|
|
||||||
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public interface BaseWasmFunctionRepository {
|
||||||
|
WasmFunction forStaticMethod(MethodReference methodReference);
|
||||||
|
|
||||||
|
WasmFunction forInstanceMethod(MethodReference methodReference);
|
||||||
|
}
|
|
@ -1,323 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2016 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.backend.wasm;
|
|
||||||
|
|
||||||
import java.lang.reflect.Array;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import org.teavm.model.ValueType;
|
|
||||||
|
|
||||||
public final class Example {
|
|
||||||
private Example() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void main(String[] args) {
|
|
||||||
testFibonacci();
|
|
||||||
testClasses();
|
|
||||||
testVirtualCall();
|
|
||||||
testInstanceOf();
|
|
||||||
testPrimitiveArray();
|
|
||||||
testLazyInitialization();
|
|
||||||
testHashCode();
|
|
||||||
testArrayList();
|
|
||||||
testArrayCopy();
|
|
||||||
testArrayIsObject();
|
|
||||||
testIsAssignableFrom();
|
|
||||||
testExceptions();
|
|
||||||
testArrayReflection();
|
|
||||||
testBigInteger();
|
|
||||||
testGC();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testBigInteger() {
|
|
||||||
System.out.println("Running BigInteger benchmark");
|
|
||||||
BigInteger result = BigInteger.ONE;
|
|
||||||
for (int j = 0; j < 100; ++j) {
|
|
||||||
long start = System.currentTimeMillis();
|
|
||||||
|
|
||||||
for (int k = 0; k < 5000; ++k) {
|
|
||||||
BigInteger a = BigInteger.ZERO;
|
|
||||||
BigInteger b = BigInteger.ONE;
|
|
||||||
for (int i = 0; i < 1000; ++i) {
|
|
||||||
BigInteger c = a.add(b);
|
|
||||||
a = b;
|
|
||||||
b = c;
|
|
||||||
}
|
|
||||||
result = a;
|
|
||||||
}
|
|
||||||
|
|
||||||
long end = System.currentTimeMillis();
|
|
||||||
|
|
||||||
System.out.println("Operation took " + (end - start) + " milliseconds");
|
|
||||||
}
|
|
||||||
System.out.println(result.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testFibonacci() {
|
|
||||||
int a = 0;
|
|
||||||
int b = 1;
|
|
||||||
System.out.println("Fibonacci numbers:");
|
|
||||||
for (int i = 0; i < 30; ++i) {
|
|
||||||
int c = a + b;
|
|
||||||
a = b;
|
|
||||||
b = c;
|
|
||||||
System.out.println(String.valueOf(a));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testClasses() {
|
|
||||||
System.out.println("A(2) + A(3) = " + (new A(2).getValue() + new A(3).getValue()));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testVirtualCall() {
|
|
||||||
for (int i = 0; i < 4; ++i) {
|
|
||||||
System.out.println("instance(" + i + ") = " + instance(i).foo());
|
|
||||||
}
|
|
||||||
|
|
||||||
Base[] array = { new Derived1(), new Derived2() };
|
|
||||||
System.out.println("array.length = " + array.length);
|
|
||||||
for (Base elem : array) {
|
|
||||||
System.out.println("array[i] = " + elem.foo());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testInstanceOf() {
|
|
||||||
System.out.println("Derived2 instanceof Base = " + (new Derived2() instanceof Base));
|
|
||||||
System.out.println("Derived3 instanceof Base = " + (new Derived3() instanceof Base));
|
|
||||||
System.out.println("Derived2 instanceof Derived1 = " + ((Object) new Derived2() instanceof Derived1));
|
|
||||||
System.out.println("Derived2 instanceof A = " + ((Object) new Derived2() instanceof A));
|
|
||||||
System.out.println("A instanceof Base = " + (new A(23) instanceof Base));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testPrimitiveArray() {
|
|
||||||
byte[] bytes = { 5, 6, 10, 15 };
|
|
||||||
for (byte bt : bytes) {
|
|
||||||
System.out.println("bytes[i] = " + bt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testLazyInitialization() {
|
|
||||||
Initialized.foo();
|
|
||||||
Initialized.foo();
|
|
||||||
Initialized.foo();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testHashCode() {
|
|
||||||
Object o = new Object();
|
|
||||||
System.out.println("hashCode1 = " + o.hashCode());
|
|
||||||
System.out.println("hashCode1 = " + o.hashCode());
|
|
||||||
System.out.println("hashCode2 = " + new Object().hashCode());
|
|
||||||
System.out.println("hashCode3 = " + new Object().hashCode());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testArrayList() {
|
|
||||||
List<Integer> list = new ArrayList<>(Arrays.asList(333, 444, 555));
|
|
||||||
list.add(1234);
|
|
||||||
list.remove((Integer) 444);
|
|
||||||
|
|
||||||
for (int item : list) {
|
|
||||||
System.out.println("list[i] = " + item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testArrayCopy() {
|
|
||||||
byte[] array = new byte[100];
|
|
||||||
byte[] negatives = new byte[100];
|
|
||||||
for (int i = 0; i < array.length; ++i) {
|
|
||||||
array[i] = (byte) i;
|
|
||||||
negatives[i] = (byte) (-i - 1);
|
|
||||||
}
|
|
||||||
System.arraycopy(negatives, 0, array, 4, 12);
|
|
||||||
System.arraycopy(negatives, 1, array, 21, 12);
|
|
||||||
System.arraycopy(negatives, 2, array, 35, 12);
|
|
||||||
System.arraycopy(negatives, 1, array, 8, 3);
|
|
||||||
System.arraycopy(array, 50, array, 54, 12);
|
|
||||||
StringBuilder sb = new StringBuilder("arrayCopy result:");
|
|
||||||
for (int i = 0; i < array.length; ++i) {
|
|
||||||
sb.append(" ").append(array[i]);
|
|
||||||
}
|
|
||||||
System.out.println(sb.toString());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testArrayIsObject() {
|
|
||||||
byte[] array = new byte[] { 1, 2, 3 };
|
|
||||||
byte[] copy = array.clone();
|
|
||||||
System.out.println("array.hashCode() = " + array.hashCode());
|
|
||||||
System.out.println("copy.hashCode() = " + copy.hashCode());
|
|
||||||
System.out.println("array.equals(array) = " + array.equals(array));
|
|
||||||
System.out.println("array.equals(copy) = " + array.equals(copy));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testIsAssignableFrom() {
|
|
||||||
System.out.println("Object.isAssignableFrom(byte[]) = "
|
|
||||||
+ Object.class.isAssignableFrom(new byte[0].getClass()));
|
|
||||||
System.out.println("Object[].isAssignableFrom(Integer[]) = "
|
|
||||||
+ Object[].class.isAssignableFrom(new Integer[0].getClass()));
|
|
||||||
System.out.println("Object[].isAssignableFrom(Integer) = "
|
|
||||||
+ Object[].class.isAssignableFrom(Integer.class));
|
|
||||||
System.out.println("byte[].isAssignableFrom(Object) = "
|
|
||||||
+ byte[].class.isAssignableFrom(ValueType.Object.class));
|
|
||||||
System.out.println("Base.isAssignableFrom(Derived1) = "
|
|
||||||
+ Base.class.isAssignableFrom(Derived1.class));
|
|
||||||
System.out.println("Base.isAssignableFrom(A) = "
|
|
||||||
+ Base.class.isAssignableFrom(A.class));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testGC() {
|
|
||||||
List<Integer> list = new ArrayList<>();
|
|
||||||
for (int i = 0; i < 4000000; ++i) {
|
|
||||||
list.add(i);
|
|
||||||
if (list.size() == 1000) {
|
|
||||||
list = new ArrayList<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
System.out.println("GC complete");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testExceptions() {
|
|
||||||
try {
|
|
||||||
throwsException();
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
System.out.println("Caught 1: " + e.getMessage());
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
int x = 0;
|
|
||||||
try {
|
|
||||||
x = 1;
|
|
||||||
doesNotThrow();
|
|
||||||
x = 2;
|
|
||||||
throwsException();
|
|
||||||
x = 3;
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
System.out.println("Caught 2: " + e.getMessage());
|
|
||||||
System.out.println("x = " + x);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
throwsWithFinally();
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
System.out.println("Caught 3: " + e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
Object[] objects = { "a", null };
|
|
||||||
for (Object o : objects) {
|
|
||||||
try {
|
|
||||||
System.out.println(o.toString());
|
|
||||||
} catch (RuntimeException e) {
|
|
||||||
System.out.println("Caught NPE");
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void testArrayReflection() {
|
|
||||||
Object[] arrays = new Object[] {
|
|
||||||
new int[] { 23, 42 },
|
|
||||||
new byte[] { (byte) 1, (byte) 2, (byte) 3 },
|
|
||||||
new String[] { "foo", "bar" },
|
|
||||||
};
|
|
||||||
|
|
||||||
for (Object array : arrays) {
|
|
||||||
int sz = Array.getLength(array);
|
|
||||||
System.out.println("Array type: " + array.getClass().getName() + ", length " + sz);
|
|
||||||
for (int i = 0; i < sz; ++i) {
|
|
||||||
Object item = Array.get(array, i);
|
|
||||||
System.out.println(" [" + i + "] = " + item + ": " + item.getClass().getName());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void doesNotThrow() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void throwsWithFinally() {
|
|
||||||
try {
|
|
||||||
throwsException();
|
|
||||||
} finally {
|
|
||||||
System.out.println("Finally reached");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void throwsException() {
|
|
||||||
throw new IllegalStateException("exception message");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Base instance(int index) {
|
|
||||||
switch (index) {
|
|
||||||
case 0:
|
|
||||||
return new Derived1();
|
|
||||||
case 1:
|
|
||||||
return new Derived2();
|
|
||||||
case 2:
|
|
||||||
return new Derived3();
|
|
||||||
default:
|
|
||||||
return new Derived4();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static class A {
|
|
||||||
private int value;
|
|
||||||
|
|
||||||
public A(int value) {
|
|
||||||
this.value = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getValue() {
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Base {
|
|
||||||
int foo();
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Derived1 implements Base {
|
|
||||||
@Override
|
|
||||||
public int foo() {
|
|
||||||
return 234;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Derived2 implements Base {
|
|
||||||
@Override
|
|
||||||
public int foo() {
|
|
||||||
return 345;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Derived3 extends Derived2 {
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Derived4 extends Derived1 {
|
|
||||||
@Override
|
|
||||||
public int foo() {
|
|
||||||
return 123;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static class Initialized {
|
|
||||||
static {
|
|
||||||
System.out.println("Initialized.<clinit>()");
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void foo() {
|
|
||||||
System.out.println("Initialized.foo()");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -27,7 +27,7 @@ import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
public class WasmFunctionRepository {
|
public class WasmFunctionRepository implements BaseWasmFunctionRepository {
|
||||||
private WasmModule module;
|
private WasmModule module;
|
||||||
private WasmFunctionTypes functionTypes;
|
private WasmFunctionTypes functionTypes;
|
||||||
private NameProvider nameProvider;
|
private NameProvider nameProvider;
|
||||||
|
@ -50,6 +50,7 @@ public class WasmFunctionRepository {
|
||||||
return isStatic ? forStaticMethod(reference) : forInstanceMethod(reference);
|
return isStatic ? forStaticMethod(reference) : forInstanceMethod(reference);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public WasmFunction forStaticMethod(MethodReference reference) {
|
public WasmFunction forStaticMethod(MethodReference reference) {
|
||||||
return staticMethods.computeIfAbsent(reference, key -> {
|
return staticMethods.computeIfAbsent(reference, key -> {
|
||||||
var wasmParams = new WasmType[key.parameterCount()];
|
var wasmParams = new WasmType[key.parameterCount()];
|
||||||
|
@ -65,6 +66,7 @@ public class WasmFunctionRepository {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public WasmFunction forInstanceMethod(MethodReference reference) {
|
public WasmFunction forInstanceMethod(MethodReference reference) {
|
||||||
return instanceMethods.computeIfAbsent(reference, key -> {
|
return instanceMethods.computeIfAbsent(reference, key -> {
|
||||||
var wasmParams = new WasmType[key.parameterCount() + 1];
|
var wasmParams = new WasmType[key.parameterCount() + 1];
|
||||||
|
|
|
@ -0,0 +1,98 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm;
|
||||||
|
|
||||||
|
import org.teavm.backend.wasm.generate.TemporaryVariablePool;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.methods.WasmGCGenerationUtil;
|
||||||
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
import org.teavm.backend.wasm.model.WasmGlobal;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmCallReference;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmReturn;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
|
public class WasmGCModuleGenerator {
|
||||||
|
private final WasmGCTarget wasmGCTarget;
|
||||||
|
private WasmGCDeclarationsGenerator declarationsGenerator;
|
||||||
|
private WasmFunction initializer;
|
||||||
|
private WasmGlobal initializerRef;
|
||||||
|
|
||||||
|
public WasmGCModuleGenerator(WasmGCTarget wasmGCTarget, WasmGCDeclarationsGenerator declarationsGenerator) {
|
||||||
|
this.wasmGCTarget = wasmGCTarget;
|
||||||
|
this.declarationsGenerator = declarationsGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generate() {
|
||||||
|
declarationsGenerator.generate();
|
||||||
|
if (initializer != null) {
|
||||||
|
fillInitializer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillInitializer() {
|
||||||
|
declarationsGenerator.contributeToInitializer(initializer);
|
||||||
|
initializer.getBody().add(new WasmReturn());
|
||||||
|
}
|
||||||
|
|
||||||
|
public WasmFunction generateMainFunction(String entryPoint) {
|
||||||
|
var mainFunction = declarationsGenerator.functions().forStaticMethod(new MethodReference(entryPoint,
|
||||||
|
"main", ValueType.parse(String[].class), ValueType.VOID));
|
||||||
|
var mainFunctionCaller = new WasmFunction(declarationsGenerator.functionTypes.of(null));
|
||||||
|
mainFunctionCaller.getBody().add(callInitializer());
|
||||||
|
|
||||||
|
var tempVars = new TemporaryVariablePool(mainFunctionCaller);
|
||||||
|
var genUtil = new WasmGCGenerationUtil(declarationsGenerator.classInfoProvider(), tempVars);
|
||||||
|
var stringArrayType = declarationsGenerator.typeMapper()
|
||||||
|
.mapType(ValueType.parse(String[].class))
|
||||||
|
.asUnpackedType();
|
||||||
|
var arrayVar = tempVars.acquire(stringArrayType);
|
||||||
|
genUtil.allocateArray(ValueType.parse(String.class), new WasmInt32Constant(0), null,
|
||||||
|
arrayVar, mainFunctionCaller.getBody());
|
||||||
|
var callToMainFunction = new WasmCall(mainFunction, new WasmGetLocal(arrayVar));
|
||||||
|
mainFunctionCaller.getBody().add(callToMainFunction);
|
||||||
|
mainFunctionCaller.getBody().add(new WasmReturn());
|
||||||
|
tempVars.release(arrayVar);
|
||||||
|
|
||||||
|
return mainFunctionCaller;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createInitializer() {
|
||||||
|
if (initializer != null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var functionType = declarationsGenerator.functionTypes.of(null);
|
||||||
|
initializer = new WasmFunction(functionType);
|
||||||
|
declarationsGenerator.module.functions.add(initializer);
|
||||||
|
initializerRef = new WasmGlobal("teavm_initializer", functionType.getReference(),
|
||||||
|
new WasmFunctionReference(initializer));
|
||||||
|
declarationsGenerator.module.globals.add(initializerRef);
|
||||||
|
initializer.getBody().add(new WasmSetGlobal(initializerRef,
|
||||||
|
new WasmFunctionReference(declarationsGenerator.dummyInitializer())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private WasmExpression callInitializer() {
|
||||||
|
createInitializer();
|
||||||
|
return new WasmCallReference(new WasmGetGlobal(initializerRef), declarationsGenerator.functionTypes.of(null));
|
||||||
|
}
|
||||||
|
}
|
143
core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java
Normal file
143
core/src/main/java/org/teavm/backend/wasm/WasmGCTarget.java
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.backend.wasm.gc.WasmGCDependencies;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.WasmGCDeclarationsGenerator;
|
||||||
|
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerators;
|
||||||
|
import org.teavm.backend.wasm.model.WasmModule;
|
||||||
|
import org.teavm.backend.wasm.render.WasmBinaryRenderer;
|
||||||
|
import org.teavm.backend.wasm.render.WasmBinaryStatsCollector;
|
||||||
|
import org.teavm.backend.wasm.render.WasmBinaryVersion;
|
||||||
|
import org.teavm.backend.wasm.render.WasmBinaryWriter;
|
||||||
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
|
import org.teavm.dependency.DependencyListener;
|
||||||
|
import org.teavm.interop.Platforms;
|
||||||
|
import org.teavm.model.ClassHolderTransformer;
|
||||||
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.transformation.BoundCheckInsertion;
|
||||||
|
import org.teavm.model.transformation.NullCheckFilter;
|
||||||
|
import org.teavm.model.transformation.NullCheckInsertion;
|
||||||
|
import org.teavm.model.util.VariableCategoryProvider;
|
||||||
|
import org.teavm.vm.BuildTarget;
|
||||||
|
import org.teavm.vm.TeaVMTarget;
|
||||||
|
import org.teavm.vm.TeaVMTargetController;
|
||||||
|
import org.teavm.vm.spi.TeaVMHostExtension;
|
||||||
|
|
||||||
|
public class WasmGCTarget implements TeaVMTarget {
|
||||||
|
private TeaVMTargetController controller;
|
||||||
|
private NullCheckInsertion nullCheckInsertion;
|
||||||
|
private BoundCheckInsertion boundCheckInsertion = new BoundCheckInsertion();
|
||||||
|
private boolean obfuscated;
|
||||||
|
|
||||||
|
public void setObfuscated(boolean obfuscated) {
|
||||||
|
this.obfuscated = obfuscated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setController(TeaVMTargetController controller) {
|
||||||
|
this.controller = controller;
|
||||||
|
nullCheckInsertion = new NullCheckInsertion(NullCheckFilter.EMPTY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public VariableCategoryProvider variableCategoryProvider() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<DependencyListener> getDependencyListeners() {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<ClassHolderTransformer> getTransformers() {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contributeDependencies(DependencyAnalyzer dependencyAnalyzer) {
|
||||||
|
new WasmGCDependencies(dependencyAnalyzer).contribute();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<TeaVMHostExtension> getHostExtensions() {
|
||||||
|
return List.of();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beforeOptimizations(Program program, MethodReader method) {
|
||||||
|
/*
|
||||||
|
nullCheckInsertion.transformProgram(program, method.getReference());
|
||||||
|
boundCheckInsertion.transformProgram(program, method.getReference());
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterOptimizations(Program program, MethodReader method) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getPlatformTags() {
|
||||||
|
return new String[] { Platforms.WEBASSEMBLY_GC };
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAsyncSupported() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void emit(ListableClassHolderSource classes, BuildTarget buildTarget, String outputName) throws IOException {
|
||||||
|
var module = new WasmModule();
|
||||||
|
var customGenerators = new WasmGCCustomGenerators();
|
||||||
|
var declarationsGenerator = new WasmGCDeclarationsGenerator(
|
||||||
|
module,
|
||||||
|
classes,
|
||||||
|
controller::isVirtual,
|
||||||
|
controller.getClassInitializerInfo(),
|
||||||
|
controller.getDependencyInfo(),
|
||||||
|
controller.getDiagnostics(),
|
||||||
|
customGenerators
|
||||||
|
);
|
||||||
|
declarationsGenerator.setFriendlyToDebugger(controller.isFriendlyToDebugger());
|
||||||
|
var moduleGenerator = new WasmGCModuleGenerator(this, declarationsGenerator);
|
||||||
|
var mainFunction = moduleGenerator.generateMainFunction(controller.getEntryPoint());
|
||||||
|
mainFunction.setExportName(controller.getEntryPointName());
|
||||||
|
mainFunction.setName(controller.getEntryPointName());
|
||||||
|
moduleGenerator.generate();
|
||||||
|
|
||||||
|
emitWasmFile(module, buildTarget, outputName);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void emitWasmFile(WasmModule module, BuildTarget buildTarget, String outputName) throws IOException {
|
||||||
|
var binaryWriter = new WasmBinaryWriter();
|
||||||
|
var binaryRenderer = new WasmBinaryRenderer(binaryWriter, WasmBinaryVersion.V_0x1, obfuscated,
|
||||||
|
null, null, null, null, WasmBinaryStatsCollector.EMPTY);
|
||||||
|
binaryRenderer.render(module);
|
||||||
|
var data = binaryWriter.getData();
|
||||||
|
if (!outputName.endsWith(".wasm")) {
|
||||||
|
outputName += ".wasm";
|
||||||
|
}
|
||||||
|
try (var output = buildTarget.createResource(outputName)) {
|
||||||
|
output.write(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -130,7 +130,6 @@ import org.teavm.interop.Import;
|
||||||
import org.teavm.interop.Platforms;
|
import org.teavm.interop.Platforms;
|
||||||
import org.teavm.interop.StaticInit;
|
import org.teavm.interop.StaticInit;
|
||||||
import org.teavm.model.AnnotationHolder;
|
import org.teavm.model.AnnotationHolder;
|
||||||
import org.teavm.model.BasicBlock;
|
|
||||||
import org.teavm.model.CallLocation;
|
import org.teavm.model.CallLocation;
|
||||||
import org.teavm.model.ClassHierarchy;
|
import org.teavm.model.ClassHierarchy;
|
||||||
import org.teavm.model.ClassHolder;
|
import org.teavm.model.ClassHolder;
|
||||||
|
@ -139,7 +138,6 @@ import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.Instruction;
|
|
||||||
import org.teavm.model.ListableClassHolderSource;
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
import org.teavm.model.ListableClassReaderSource;
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
|
@ -152,9 +150,6 @@ import org.teavm.model.analysis.ClassMetadataRequirements;
|
||||||
import org.teavm.model.classes.TagRegistry;
|
import org.teavm.model.classes.TagRegistry;
|
||||||
import org.teavm.model.classes.VirtualTableBuilder;
|
import org.teavm.model.classes.VirtualTableBuilder;
|
||||||
import org.teavm.model.classes.VirtualTableProvider;
|
import org.teavm.model.classes.VirtualTableProvider;
|
||||||
import org.teavm.model.instructions.CloneArrayInstruction;
|
|
||||||
import org.teavm.model.instructions.InvocationType;
|
|
||||||
import org.teavm.model.instructions.InvokeInstruction;
|
|
||||||
import org.teavm.model.lowlevel.Characteristics;
|
import org.teavm.model.lowlevel.Characteristics;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
import org.teavm.model.lowlevel.ClassInitializerEliminator;
|
||||||
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
import org.teavm.model.lowlevel.ClassInitializerTransformer;
|
||||||
|
@ -1075,40 +1070,11 @@ public class WasmTarget implements TeaVMTarget, TeaVMWasmHost {
|
||||||
|
|
||||||
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
private VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes) {
|
||||||
var builder = new VirtualTableBuilder(classes);
|
var builder = new VirtualTableBuilder(classes);
|
||||||
builder.setMethodsUsedAtCallSites(getMethodsUsedOnCallSites(classes));
|
builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes));
|
||||||
builder.setMethodCalledVirtually(controller::isVirtual);
|
builder.setMethodCalledVirtually(controller::isVirtual);
|
||||||
return builder.build();
|
return builder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Set<MethodReference> getMethodsUsedOnCallSites(ListableClassHolderSource classes) {
|
|
||||||
var virtualMethods = new HashSet<MethodReference>();
|
|
||||||
|
|
||||||
for (String className : classes.getClassNames()) {
|
|
||||||
ClassHolder cls = classes.get(className);
|
|
||||||
for (MethodHolder method : cls.getMethods()) {
|
|
||||||
Program program = method.getProgram();
|
|
||||||
if (program == null) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
|
||||||
BasicBlock block = program.basicBlockAt(i);
|
|
||||||
for (Instruction insn : block) {
|
|
||||||
if (insn instanceof InvokeInstruction) {
|
|
||||||
InvokeInstruction invoke = (InvokeInstruction) insn;
|
|
||||||
if (invoke.getType() == InvocationType.VIRTUAL) {
|
|
||||||
virtualMethods.add(invoke.getMethod());
|
|
||||||
}
|
|
||||||
} else if (insn instanceof CloneArrayInstruction) {
|
|
||||||
virtualMethods.add(new MethodReference(Object.class, "clone", Object.class));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return virtualMethods;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String[] getPlatformTags() {
|
public String[] getPlatformTags() {
|
||||||
return new String[] { Platforms.WEBASSEMBLY, Platforms.LOW_LEVEL };
|
return new String[] { Platforms.WEBASSEMBLY, Platforms.LOW_LEVEL };
|
||||||
|
|
|
@ -0,0 +1,129 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm.gc;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
|
import org.teavm.model.ClassReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.analysis.BaseTypeInference;
|
||||||
|
|
||||||
|
public class PreciseTypeInference extends BaseTypeInference<ValueType> {
|
||||||
|
public static final ValueType OBJECT_TYPE = ValueType.object("java.lang.Object");
|
||||||
|
private ClassHierarchy hierarchy;
|
||||||
|
|
||||||
|
public PreciseTypeInference(Program program, MethodReference reference, ClassHierarchy hierarchy) {
|
||||||
|
super(program, reference);
|
||||||
|
this.hierarchy = hierarchy;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ValueType mapType(ValueType type) {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ValueType nullType() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ValueType merge(ValueType a, ValueType b) {
|
||||||
|
if (a == null) {
|
||||||
|
return b;
|
||||||
|
} else if (b == null) {
|
||||||
|
return a;
|
||||||
|
} else {
|
||||||
|
if (a instanceof ValueType.Primitive && b instanceof ValueType.Primitive) {
|
||||||
|
if (a != b) {
|
||||||
|
return OBJECT_TYPE;
|
||||||
|
} else {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
} else if (a instanceof ValueType.Array) {
|
||||||
|
if (b instanceof ValueType.Array) {
|
||||||
|
var p = ((ValueType.Array) a).getItemType();
|
||||||
|
var q = ((ValueType.Array) b).getItemType();
|
||||||
|
return ValueType.arrayOf(merge(p, q));
|
||||||
|
} else {
|
||||||
|
return OBJECT_TYPE;
|
||||||
|
}
|
||||||
|
} else if (b instanceof ValueType.Array) {
|
||||||
|
return OBJECT_TYPE;
|
||||||
|
} else if (a instanceof ValueType.Object) {
|
||||||
|
if (b instanceof ValueType.Object) {
|
||||||
|
var p = ((ValueType.Object) a).getClassName();
|
||||||
|
var q = ((ValueType.Object) b).getClassName();
|
||||||
|
if (p.equals(q)) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
var first = hierarchy.getClassSource().get(p);
|
||||||
|
if (first == null) {
|
||||||
|
p = "java.lang.Object";
|
||||||
|
}
|
||||||
|
var second = hierarchy.getClassSource().get(q);
|
||||||
|
if (second == null) {
|
||||||
|
q = "java.lang.Object";
|
||||||
|
}
|
||||||
|
if (hierarchy.isSuperType(p, q, false)) {
|
||||||
|
return a;
|
||||||
|
} else if (hierarchy.isSuperType(q, p, false)) {
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
return ValueType.object(findCommonSuperclass(first, second));
|
||||||
|
} else {
|
||||||
|
return OBJECT_TYPE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return OBJECT_TYPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private String findCommonSuperclass(ClassReader a, ClassReader b) {
|
||||||
|
var firstPath = findPathToRoot(a);
|
||||||
|
Collections.reverse(firstPath);
|
||||||
|
var secondPath = findPathToRoot(b);
|
||||||
|
Collections.reverse(secondPath);
|
||||||
|
if (firstPath.get(0) != secondPath.get(0)) {
|
||||||
|
return "java.lang.Object";
|
||||||
|
}
|
||||||
|
var max = Math.max(firstPath.size(), secondPath.size());
|
||||||
|
var index = 1;
|
||||||
|
while (index < max && firstPath.get(index) == secondPath.get(index)) {
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
return firstPath.get(index).getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<ClassReader> findPathToRoot(ClassReader cls) {
|
||||||
|
var path = new ArrayList<ClassReader>();
|
||||||
|
while (cls != null) {
|
||||||
|
path.add(cls);
|
||||||
|
cls = cls.getParent() != null ? hierarchy.getClassSource().get(cls.getParent()) : null;
|
||||||
|
}
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ValueType elementType(ValueType valueType) {
|
||||||
|
return ((ValueType.Array) valueType).getItemType();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm.gc;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import org.teavm.backend.wasm.WasmRuntime;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
|
||||||
|
import org.teavm.backend.wasm.runtime.WasmGCSupport;
|
||||||
|
import org.teavm.dependency.DependencyAnalyzer;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public class WasmGCDependencies {
|
||||||
|
private DependencyAnalyzer analyzer;
|
||||||
|
|
||||||
|
public WasmGCDependencies(DependencyAnalyzer analyzer) {
|
||||||
|
this.analyzer = analyzer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void contribute() {
|
||||||
|
contributeMathUtils();
|
||||||
|
contributeExceptionUtils();
|
||||||
|
contributeInitializerUtils();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void contributeMathUtils() {
|
||||||
|
for (var type : Arrays.asList(int.class, long.class, float.class, double.class)) {
|
||||||
|
var method = new MethodReference(WasmRuntime.class, "compare", type, type, int.class);
|
||||||
|
analyzer.linkMethod(method).use();
|
||||||
|
}
|
||||||
|
for (var type : Arrays.asList(int.class, long.class)) {
|
||||||
|
var method = new MethodReference(WasmRuntime.class, "compareUnsigned", type, type, int.class);
|
||||||
|
analyzer.linkMethod(method).use();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var type : Arrays.asList(float.class, double.class)) {
|
||||||
|
var method = new MethodReference(WasmRuntime.class, "remainder", type, type, type);
|
||||||
|
analyzer.linkMethod(method).use();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void contributeExceptionUtils() {
|
||||||
|
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "npe", NullPointerException.class));
|
||||||
|
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "aiiobe", ArrayIndexOutOfBoundsException.class));
|
||||||
|
analyzer.linkMethod(new MethodReference(WasmGCSupport.class, "cce", ClassCastException.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void contributeInitializerUtils() {
|
||||||
|
analyzer.linkMethod(new MethodReference(WasmGCStringPool.class, "nextCharArray", char[].class));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm.gc;
|
||||||
|
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.Program;
|
||||||
|
import org.teavm.model.util.VariableCategoryProvider;
|
||||||
|
|
||||||
|
public class WasmGCVariableCategoryProvider implements VariableCategoryProvider {
|
||||||
|
private ClassHierarchy hierarchy;
|
||||||
|
private PreciseTypeInference inference;
|
||||||
|
|
||||||
|
public WasmGCVariableCategoryProvider(ClassHierarchy hierarchy) {
|
||||||
|
this.hierarchy = hierarchy;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PreciseTypeInference getTypeInference() {
|
||||||
|
return inference;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object[] getCategories(Program program, MethodReference method) {
|
||||||
|
inference = new PreciseTypeInference(program, method, hierarchy);
|
||||||
|
var result = new Object[program.variableCount()];
|
||||||
|
for (int i = 0; i < program.variableCount(); ++i) {
|
||||||
|
var type = inference.typeOf(program.variableAt(i));
|
||||||
|
result[i] = type != null ? type : PreciseTypeInference.OBJECT_TYPE;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,7 +90,7 @@ public class WasmGenerationContext implements BaseWasmGenerationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClassReaderSource classSource() {
|
public ClassReaderSource classes() {
|
||||||
return classSource;
|
return classSource;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,17 +15,17 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.generate.common.methods;
|
package org.teavm.backend.wasm.generate.common.methods;
|
||||||
|
|
||||||
import org.teavm.backend.wasm.WasmFunctionRepository;
|
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
||||||
import org.teavm.backend.wasm.WasmFunctionTypes;
|
import org.teavm.backend.wasm.WasmFunctionTypes;
|
||||||
import org.teavm.backend.wasm.model.WasmTag;
|
import org.teavm.backend.wasm.model.WasmTag;
|
||||||
import org.teavm.model.ClassReaderSource;
|
import org.teavm.model.ClassReaderSource;
|
||||||
|
|
||||||
public interface BaseWasmGenerationContext {
|
public interface BaseWasmGenerationContext {
|
||||||
WasmFunctionRepository functions();
|
BaseWasmFunctionRepository functions();
|
||||||
|
|
||||||
WasmFunctionTypes functionTypes();
|
WasmFunctionTypes functionTypes();
|
||||||
|
|
||||||
WasmTag getExceptionTag();
|
WasmTag getExceptionTag();
|
||||||
|
|
||||||
ClassReaderSource classSource();
|
ClassReaderSource classes();
|
||||||
}
|
}
|
||||||
|
|
|
@ -832,9 +832,11 @@ public abstract class BaseWasmGenerationVisitor implements StatementVisitor, Exp
|
||||||
|
|
||||||
protected WasmExpression generateInvocation(InvocationExpr expr, CallSiteIdentifier callSiteId) {
|
protected WasmExpression generateInvocation(InvocationExpr expr, CallSiteIdentifier callSiteId) {
|
||||||
if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) {
|
if (expr.getType() == InvocationType.STATIC || expr.getType() == InvocationType.SPECIAL) {
|
||||||
var method = context.classSource().resolve(expr.getMethod());
|
var method = context.classes().resolve(expr.getMethod());
|
||||||
var reference = method != null ? method.getReference() : expr.getMethod();
|
var reference = method != null ? method.getReference() : expr.getMethod();
|
||||||
var function = context.functions().forMethod(reference, expr.getType() == InvocationType.STATIC);
|
var function = expr.getType() == InvocationType.STATIC
|
||||||
|
? context.functions().forStaticMethod(reference)
|
||||||
|
: context.functions().forInstanceMethod(reference);
|
||||||
|
|
||||||
var call = new WasmCall(function);
|
var call = new WasmCall(function);
|
||||||
for (var argument : expr.getArguments()) {
|
for (var argument : expr.getArguments()) {
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm.generate.gc;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.function.Predicate;
|
||||||
|
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
||||||
|
import org.teavm.backend.wasm.WasmFunctionTypes;
|
||||||
|
import org.teavm.backend.wasm.generate.WasmNameProvider;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassGenerator;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCSupertypeFunctionProvider;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.methods.WasmGCMethodGenerator;
|
||||||
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
import org.teavm.backend.wasm.model.WasmModule;
|
||||||
|
import org.teavm.dependency.DependencyInfo;
|
||||||
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||||
|
import org.teavm.model.analysis.ClassMetadataRequirements;
|
||||||
|
import org.teavm.model.classes.TagRegistry;
|
||||||
|
import org.teavm.model.classes.VirtualTableBuilder;
|
||||||
|
import org.teavm.model.classes.VirtualTableProvider;
|
||||||
|
|
||||||
|
public class WasmGCDeclarationsGenerator {
|
||||||
|
public final ClassHierarchy hierarchy;
|
||||||
|
public final WasmModule module;
|
||||||
|
public final WasmFunctionTypes functionTypes;
|
||||||
|
private final WasmGCClassGenerator classGenerator;
|
||||||
|
private final WasmGCMethodGenerator methodGenerator;
|
||||||
|
|
||||||
|
public WasmGCDeclarationsGenerator(
|
||||||
|
WasmModule module,
|
||||||
|
ListableClassHolderSource classes,
|
||||||
|
Predicate<MethodReference> virtualMethods,
|
||||||
|
ClassInitializerInfo classInitializerInfo,
|
||||||
|
DependencyInfo dependencyInfo,
|
||||||
|
Diagnostics diagnostics,
|
||||||
|
WasmGCCustomGeneratorProvider customGenerators
|
||||||
|
) {
|
||||||
|
this.module = module;
|
||||||
|
hierarchy = new ClassHierarchy(classes);
|
||||||
|
var virtualTables = createVirtualTableProvider(classes, virtualMethods);
|
||||||
|
functionTypes = new WasmFunctionTypes(module);
|
||||||
|
var names = new WasmNameProvider();
|
||||||
|
methodGenerator = new WasmGCMethodGenerator(
|
||||||
|
module,
|
||||||
|
hierarchy,
|
||||||
|
classes,
|
||||||
|
virtualTables,
|
||||||
|
classInitializerInfo,
|
||||||
|
functionTypes,
|
||||||
|
names,
|
||||||
|
diagnostics,
|
||||||
|
customGenerators
|
||||||
|
);
|
||||||
|
var tags = new TagRegistry(classes, hierarchy);
|
||||||
|
var metadataRequirements = new ClassMetadataRequirements(dependencyInfo);
|
||||||
|
classGenerator = new WasmGCClassGenerator(
|
||||||
|
module,
|
||||||
|
classes,
|
||||||
|
functionTypes,
|
||||||
|
tags,
|
||||||
|
metadataRequirements,
|
||||||
|
virtualTables,
|
||||||
|
methodGenerator,
|
||||||
|
names,
|
||||||
|
classInitializerInfo
|
||||||
|
);
|
||||||
|
methodGenerator.setClassInfoProvider(classGenerator);
|
||||||
|
methodGenerator.setStrings(classGenerator.strings);
|
||||||
|
methodGenerator.setSupertypeFunctions(classGenerator.getSupertypeProvider());
|
||||||
|
methodGenerator.setStandardClasses(classGenerator.standardClasses);
|
||||||
|
methodGenerator.setTypeMapper(classGenerator.typeMapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFriendlyToDebugger(boolean friendlyToDebugger) {
|
||||||
|
methodGenerator.setFriendlyToDebugger(friendlyToDebugger);
|
||||||
|
}
|
||||||
|
|
||||||
|
public WasmGCClassInfoProvider classInfoProvider() {
|
||||||
|
return classGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WasmGCTypeMapper typeMapper() {
|
||||||
|
return classGenerator.typeMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseWasmFunctionRepository functions() {
|
||||||
|
return methodGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WasmGCSupertypeFunctionProvider supertypeFunctions() {
|
||||||
|
return classGenerator.getSupertypeProvider();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void generate() {
|
||||||
|
boolean somethingGenerated;
|
||||||
|
do {
|
||||||
|
somethingGenerated = false;
|
||||||
|
somethingGenerated |= methodGenerator.process();
|
||||||
|
somethingGenerated |= classGenerator.process();
|
||||||
|
} while (somethingGenerated);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void contributeToInitializer(WasmFunction function) {
|
||||||
|
classGenerator.contributeToInitializer(function);
|
||||||
|
var contributors = List.of(classGenerator, classGenerator.strings);
|
||||||
|
for (var contributor : contributors) {
|
||||||
|
contributor.contributeToInitializerDefinitions(function);
|
||||||
|
contributor.contributeToInitializer(function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static VirtualTableProvider createVirtualTableProvider(ListableClassHolderSource classes,
|
||||||
|
Predicate<MethodReference> virtualMethods) {
|
||||||
|
var builder = new VirtualTableBuilder(classes);
|
||||||
|
builder.setMethodsUsedAtCallSites(VirtualTableBuilder.getMethodsUsedOnCallSites(classes));
|
||||||
|
builder.setMethodCalledVirtually(virtualMethods);
|
||||||
|
return builder.build();
|
||||||
|
}
|
||||||
|
|
||||||
|
public WasmFunction dummyInitializer() {
|
||||||
|
return methodGenerator.getDummyInitializer();
|
||||||
|
}
|
||||||
|
}
|
|
@ -13,7 +13,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.generate.gc.initialization;
|
package org.teavm.backend.wasm.generate.gc;
|
||||||
|
|
||||||
import org.teavm.backend.wasm.model.WasmFunction;
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
|
|
@ -17,13 +17,18 @@ package org.teavm.backend.wasm.generate.gc.classes;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
import com.carrotsearch.hppc.ObjectIntHashMap;
|
||||||
import com.carrotsearch.hppc.ObjectIntMap;
|
import com.carrotsearch.hppc.ObjectIntMap;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import org.teavm.backend.lowlevel.generate.NameProvider;
|
import org.teavm.backend.lowlevel.generate.NameProvider;
|
||||||
|
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
||||||
import org.teavm.backend.wasm.WasmFunctionTypes;
|
import org.teavm.backend.wasm.WasmFunctionTypes;
|
||||||
import org.teavm.backend.wasm.generate.gc.initialization.WasmGCInitializerContributor;
|
import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor;
|
||||||
import org.teavm.backend.wasm.generate.gc.methods.WasmGCFunctionProvider;
|
|
||||||
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
|
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
|
||||||
import org.teavm.backend.wasm.model.WasmArray;
|
import org.teavm.backend.wasm.model.WasmArray;
|
||||||
import org.teavm.backend.wasm.model.WasmFunction;
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
@ -70,17 +75,20 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
private TagRegistry tagRegistry;
|
private TagRegistry tagRegistry;
|
||||||
private ClassMetadataRequirements metadataRequirements;
|
private ClassMetadataRequirements metadataRequirements;
|
||||||
private VirtualTableProvider virtualTables;
|
private VirtualTableProvider virtualTables;
|
||||||
private WasmGCFunctionProvider functionProvider;
|
private BaseWasmFunctionRepository functionProvider;
|
||||||
private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>();
|
private Map<ValueType, WasmGCClassInfo> classInfoMap = new LinkedHashMap<>();
|
||||||
|
private Queue<WasmGCClassInfo> classInfoQueue = new ArrayDeque<>();
|
||||||
private ObjectIntMap<FieldReference> fieldIndexes = new ObjectIntHashMap<>();
|
private ObjectIntMap<FieldReference> fieldIndexes = new ObjectIntHashMap<>();
|
||||||
private ObjectIntMap<MethodReference> methodIndexes = new ObjectIntHashMap<>();
|
private ObjectIntMap<MethodReference> methodIndexes = new ObjectIntHashMap<>();
|
||||||
|
private Map<FieldReference, WasmGlobal> staticFieldLocations = new HashMap<>();
|
||||||
|
private List<Consumer<WasmFunction>> staticFieldInitializers = new ArrayList<>();
|
||||||
private ClassInitializerInfo classInitializerInfo;
|
private ClassInitializerInfo classInitializerInfo;
|
||||||
|
|
||||||
public final WasmGCStringPool strings;
|
public final WasmGCStringPool strings;
|
||||||
public final WasmGCStandardClasses standardClasses;
|
public final WasmGCStandardClasses standardClasses;
|
||||||
private final WasmGCTypeMapper typeMapper;
|
public final WasmGCTypeMapper typeMapper;
|
||||||
private final NameProvider names;
|
private final NameProvider names;
|
||||||
private WasmFunction initializer;
|
private List<WasmExpression> initializerFunctionStatements = new ArrayList<>();
|
||||||
private WasmFunction createPrimitiveClassFunction;
|
private WasmFunction createPrimitiveClassFunction;
|
||||||
private WasmFunction createArrayClassFunction;
|
private WasmFunction createArrayClassFunction;
|
||||||
private final WasmGCSupertypeFunctionGenerator supertypeGenerator;
|
private final WasmGCSupertypeFunctionGenerator supertypeGenerator;
|
||||||
|
@ -97,7 +105,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
|
public WasmGCClassGenerator(WasmModule module, ClassReaderSource classSource,
|
||||||
WasmFunctionTypes functionTypes, TagRegistry tagRegistry,
|
WasmFunctionTypes functionTypes, TagRegistry tagRegistry,
|
||||||
ClassMetadataRequirements metadataRequirements, VirtualTableProvider virtualTables,
|
ClassMetadataRequirements metadataRequirements, VirtualTableProvider virtualTables,
|
||||||
WasmGCFunctionProvider functionProvider, NameProvider names,
|
BaseWasmFunctionRepository functionProvider, NameProvider names,
|
||||||
ClassInitializerInfo classInitializerInfo) {
|
ClassInitializerInfo classInitializerInfo) {
|
||||||
this.module = module;
|
this.module = module;
|
||||||
this.classSource = classSource;
|
this.classSource = classSource;
|
||||||
|
@ -114,6 +122,22 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
typeMapper = new WasmGCTypeMapper(this);
|
typeMapper = new WasmGCTypeMapper(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WasmGCSupertypeFunctionProvider getSupertypeProvider() {
|
||||||
|
return supertypeGenerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean process() {
|
||||||
|
if (classInfoQueue.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while (!classInfoQueue.isEmpty()) {
|
||||||
|
var classInfo = classInfoQueue.remove();
|
||||||
|
classInfo.initializer.accept(initializerFunctionStatements);
|
||||||
|
classInfo.initializer = null;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contributeToInitializerDefinitions(WasmFunction function) {
|
public void contributeToInitializerDefinitions(WasmFunction function) {
|
||||||
for (var classInfo : classInfoMap.values()) {
|
for (var classInfo : classInfoMap.values()) {
|
||||||
|
@ -125,8 +149,9 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
@Override
|
@Override
|
||||||
public void contributeToInitializer(WasmFunction function) {
|
public void contributeToInitializer(WasmFunction function) {
|
||||||
var classClass = standardClasses.classClass();
|
var classClass = standardClasses.classClass();
|
||||||
|
function.getBody().addAll(initializerFunctionStatements);
|
||||||
|
initializerFunctionStatements.clear();
|
||||||
for (var classInfo : classInfoMap.values()) {
|
for (var classInfo : classInfoMap.values()) {
|
||||||
classInfo.initializer.accept(function.getBody());
|
|
||||||
var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType());
|
var supertypeFunction = supertypeGenerator.getIsSupertypeFunction(classInfo.getValueType());
|
||||||
function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset,
|
function.getBody().add(setClassField(classInfo, classSupertypeFunctionOffset,
|
||||||
new WasmFunctionReference(supertypeFunction)));
|
new WasmFunctionReference(supertypeFunction)));
|
||||||
|
@ -134,12 +159,15 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
new WasmGetGlobal(classClass.pointer)));
|
new WasmGetGlobal(classClass.pointer)));
|
||||||
if (classInfo.initializerPointer != null) {
|
if (classInfo.initializerPointer != null) {
|
||||||
var className = ((ValueType.Object) classInfo.getValueType()).getClassName();
|
var className = ((ValueType.Object) classInfo.getValueType()).getClassName();
|
||||||
var initFunction = functionProvider.getStaticFunction(new MethodReference(className,
|
var initFunction = functionProvider.forStaticMethod(new MethodReference(className,
|
||||||
CLINIT_METHOD_DESC));
|
CLINIT_METHOD_DESC));
|
||||||
function.getBody().add(new WasmSetGlobal(classInfo.initializerPointer,
|
function.getBody().add(new WasmSetGlobal(classInfo.initializerPointer,
|
||||||
new WasmFunctionReference(initFunction)));
|
new WasmFunctionReference(initFunction)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (var consumer : staticFieldInitializers) {
|
||||||
|
consumer.accept(function);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -147,6 +175,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
var classInfo = classInfoMap.get(type);
|
var classInfo = classInfoMap.get(type);
|
||||||
if (classInfo == null) {
|
if (classInfo == null) {
|
||||||
classInfo = new WasmGCClassInfo(type);
|
classInfo = new WasmGCClassInfo(type);
|
||||||
|
classInfoQueue.add(classInfo);
|
||||||
classInfoMap.put(type, classInfo);
|
classInfoMap.put(type, classInfo);
|
||||||
if (!(type instanceof ValueType.Primitive)) {
|
if (!(type instanceof ValueType.Primitive)) {
|
||||||
var name = type instanceof ValueType.Object
|
var name = type instanceof ValueType.Object
|
||||||
|
@ -154,6 +183,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
: null;
|
: null;
|
||||||
classInfo.structure = new WasmStructure(name);
|
classInfo.structure = new WasmStructure(name);
|
||||||
classInfo.structure.getFields().add(standardClasses.classClass().getType().asStorage());
|
classInfo.structure.getFields().add(standardClasses.classClass().getType().asStorage());
|
||||||
|
module.types.add(classInfo.structure);
|
||||||
fillFields(classInfo.structure.getFields(), type);
|
fillFields(classInfo.structure.getFields(), type);
|
||||||
}
|
}
|
||||||
var pointerName = names.forClassInstance(type);
|
var pointerName = names.forClassInstance(type);
|
||||||
|
@ -241,6 +271,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
var clinitType = functionTypes.of(null);
|
var clinitType = functionTypes.of(null);
|
||||||
classInfo.initializerPointer = new WasmGlobal(null, clinitType.getReference(),
|
classInfo.initializerPointer = new WasmGlobal(null, clinitType.getReference(),
|
||||||
new WasmNullConstant(clinitType.getReference()));
|
new WasmNullConstant(clinitType.getReference()));
|
||||||
|
module.globals.add(classInfo.initializerPointer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
classInfo.initializer = target -> {
|
classInfo.initializer = target -> {
|
||||||
|
@ -276,7 +307,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
for (var method : virtualTable.getMethods()) {
|
for (var method : virtualTable.getMethods()) {
|
||||||
var entry = virtualTable.getEntry(method);
|
var entry = virtualTable.getEntry(method);
|
||||||
if (entry != null && entry.getImplementor() != null) {
|
if (entry != null && entry.getImplementor() != null) {
|
||||||
var function = functionProvider.getMemberFunction(entry.getImplementor());
|
var function = functionProvider.forInstanceMethod(entry.getImplementor());
|
||||||
if (!origin.equals(entry.getImplementor().getClassName())) {
|
if (!origin.equals(entry.getImplementor().getClassName())) {
|
||||||
var functionType = getFunctionType(virtualTable.getClassName(), method);
|
var functionType = getFunctionType(virtualTable.getClassName(), method);
|
||||||
var wrapperFunction = new WasmFunction(functionType);
|
var wrapperFunction = new WasmFunction(functionType);
|
||||||
|
@ -361,6 +392,33 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WasmGlobal getStaticFieldLocation(FieldReference fieldRef) {
|
||||||
|
return staticFieldLocations.computeIfAbsent(fieldRef, this::generateStaticFieldLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private WasmGlobal generateStaticFieldLocation(FieldReference fieldRef) {
|
||||||
|
ValueType javaType = null;
|
||||||
|
Object initValue = null;
|
||||||
|
var cls = classSource.get(fieldRef.getClassName());
|
||||||
|
if (cls != null) {
|
||||||
|
var field = cls.getField(fieldRef.getFieldName());
|
||||||
|
if (field != null) {
|
||||||
|
javaType = field.getType();
|
||||||
|
initValue = field.getInitialValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (javaType == null) {
|
||||||
|
javaType = ValueType.object("java.lang.Object");
|
||||||
|
}
|
||||||
|
|
||||||
|
var type = typeMapper.mapType(javaType).asUnpackedType();
|
||||||
|
var global = new WasmGlobal(names.forStaticField(fieldRef), type, WasmExpression.defaultValueOfType(type));
|
||||||
|
module.globals.add(global);
|
||||||
|
|
||||||
|
return global;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getVirtualMethodIndex(MethodReference methodRef) {
|
public int getVirtualMethodIndex(MethodReference methodRef) {
|
||||||
var result = methodIndexes.getOrDefault(methodRef, -1);
|
var result = methodIndexes.getOrDefault(methodRef, -1);
|
||||||
|
@ -442,13 +500,14 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
);
|
);
|
||||||
var function = new WasmFunction(functionType);
|
var function = new WasmFunction(functionType);
|
||||||
function.setName("_teavm_fill_primitive_class_");
|
function.setName("_teavm_fill_primitive_class_");
|
||||||
|
module.functions.add(function);
|
||||||
|
|
||||||
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
|
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
|
||||||
var nameVar = new WasmLocal(standardClasses.objectClass().getType(), "name");
|
var nameVar = new WasmLocal(standardClasses.objectClass().getType(), "name");
|
||||||
var kindVar = new WasmLocal(WasmType.INT32, "kind");
|
var kindVar = new WasmLocal(WasmType.INT32, "kind");
|
||||||
function.getLocalVariables().add(targetVar);
|
function.add(targetVar);
|
||||||
function.getLocalVariables().add(nameVar);
|
function.add(nameVar);
|
||||||
function.getLocalVariables().add(kindVar);
|
function.add(kindVar);
|
||||||
|
|
||||||
var flagsExpr = new WasmIntBinary(
|
var flagsExpr = new WasmIntBinary(
|
||||||
WasmIntType.INT32,
|
WasmIntType.INT32,
|
||||||
|
@ -497,12 +556,13 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
standardClasses.classClass().getType()
|
standardClasses.classClass().getType()
|
||||||
);
|
);
|
||||||
var function = new WasmFunction(functionType);
|
var function = new WasmFunction(functionType);
|
||||||
|
module.functions.add(function);
|
||||||
function.setName("_teavm_fill_array_class_");
|
function.setName("_teavm_fill_array_class_");
|
||||||
|
|
||||||
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
|
var targetVar = new WasmLocal(standardClasses.classClass().getType(), "target");
|
||||||
var itemVar = new WasmLocal(standardClasses.classClass().getType(), "item");
|
var itemVar = new WasmLocal(standardClasses.classClass().getType(), "item");
|
||||||
function.getLocalVariables().add(targetVar);
|
function.add(targetVar);
|
||||||
function.getLocalVariables().add(itemVar);
|
function.add(itemVar);
|
||||||
|
|
||||||
function.getBody().add(new WasmStructSet(
|
function.getBody().add(new WasmStructSet(
|
||||||
standardClasses.classClass().getStructure(),
|
standardClasses.classClass().getStructure(),
|
||||||
|
@ -538,15 +598,6 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private WasmFunction getInitializer() {
|
|
||||||
if (initializer == null) {
|
|
||||||
initializer = new WasmFunction(functionTypes.of(null));
|
|
||||||
initializer.setName("_teavm_init_classes_");
|
|
||||||
module.functions.add(initializer);
|
|
||||||
}
|
|
||||||
return initializer;
|
|
||||||
}
|
|
||||||
|
|
||||||
private WasmExpression setClassField(WasmGCClassInfo classInfo, int fieldIndex, WasmExpression value) {
|
private WasmExpression setClassField(WasmGCClassInfo classInfo, int fieldIndex, WasmExpression value) {
|
||||||
return new WasmStructSet(
|
return new WasmStructSet(
|
||||||
standardClasses.classClass().getStructure(),
|
standardClasses.classClass().getStructure(),
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.generate.gc.classes;
|
package org.teavm.backend.wasm.generate.gc.classes;
|
||||||
|
|
||||||
|
import org.teavm.backend.wasm.model.WasmGlobal;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
|
@ -22,12 +23,15 @@ import org.teavm.model.ValueType;
|
||||||
public interface WasmGCClassInfoProvider {
|
public interface WasmGCClassInfoProvider {
|
||||||
int CLASS_FIELD_OFFSET = 0;
|
int CLASS_FIELD_OFFSET = 0;
|
||||||
int MONITOR_FIELD_OFFSET = 1;
|
int MONITOR_FIELD_OFFSET = 1;
|
||||||
|
int CUSTOM_FIELD_OFFSETS = 2;
|
||||||
int ARRAY_DATA_FIELD_OFFSET = 2;
|
int ARRAY_DATA_FIELD_OFFSET = 2;
|
||||||
|
|
||||||
WasmGCClassInfo getClassInfo(ValueType type);
|
WasmGCClassInfo getClassInfo(ValueType type);
|
||||||
|
|
||||||
int getFieldIndex(FieldReference fieldRef);
|
int getFieldIndex(FieldReference fieldRef);
|
||||||
|
|
||||||
|
WasmGlobal getStaticFieldLocation(FieldReference fieldRef);
|
||||||
|
|
||||||
int getVirtualMethodIndex(MethodReference methodRef);
|
int getVirtualMethodIndex(MethodReference methodRef);
|
||||||
|
|
||||||
default WasmGCClassInfo getClassInfo(String name) {
|
default WasmGCClassInfo getClassInfo(String name) {
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.generate.gc.classes;
|
package org.teavm.backend.wasm.generate.gc.classes;
|
||||||
|
|
||||||
import com.carrotsearch.hppc.ObjectIntHashMap;
|
|
||||||
import com.carrotsearch.hppc.ObjectIntMap;
|
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -45,8 +43,7 @@ import org.teavm.backend.wasm.model.expression.WasmStructGet;
|
||||||
import org.teavm.model.ValueType;
|
import org.teavm.model.ValueType;
|
||||||
import org.teavm.model.classes.TagRegistry;
|
import org.teavm.model.classes.TagRegistry;
|
||||||
|
|
||||||
public class WasmGCSupertypeFunctionGenerator {
|
public class WasmGCSupertypeFunctionGenerator implements WasmGCSupertypeFunctionProvider {
|
||||||
private ObjectIntMap<ValueType> tableIndexes = new ObjectIntHashMap<>();
|
|
||||||
private Map<ValueType, WasmFunction> functions = new HashMap<>();
|
private Map<ValueType, WasmFunction> functions = new HashMap<>();
|
||||||
private WasmModule module;
|
private WasmModule module;
|
||||||
private WasmGCClassGenerator classGenerator;
|
private WasmGCClassGenerator classGenerator;
|
||||||
|
@ -69,6 +66,7 @@ public class WasmGCSupertypeFunctionGenerator {
|
||||||
this.functionTypes = functionTypes;
|
this.functionTypes = functionTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public WasmFunction getIsSupertypeFunction(ValueType type) {
|
public WasmFunction getIsSupertypeFunction(ValueType type) {
|
||||||
var result = functions.get(type);
|
var result = functions.get(type);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm.generate.gc.classes;
|
||||||
|
|
||||||
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
|
public interface WasmGCSupertypeFunctionProvider {
|
||||||
|
WasmFunction getIsSupertypeFunction(ValueType type);
|
||||||
|
}
|
|
@ -15,11 +15,9 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.generate.gc.methods;
|
package org.teavm.backend.wasm.generate.gc.methods;
|
||||||
|
|
||||||
import org.teavm.backend.wasm.model.WasmFunction;
|
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public interface WasmGCFunctionProvider {
|
public interface WasmGCCustomGeneratorProvider {
|
||||||
WasmFunction getMemberFunction(MethodReference methodRef);
|
WasmGCCustomGenerator get(MethodReference method);
|
||||||
|
|
||||||
WasmFunction getStaticFunction(MethodReference methodRef);
|
|
||||||
}
|
}
|
|
@ -13,13 +13,14 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.generate.gc;
|
package org.teavm.backend.wasm.generate.gc.methods;
|
||||||
|
|
||||||
import org.teavm.backend.wasm.WasmFunctionRepository;
|
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
||||||
import org.teavm.backend.wasm.WasmFunctionTypes;
|
import org.teavm.backend.wasm.WasmFunctionTypes;
|
||||||
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationContext;
|
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationContext;
|
||||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
||||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCSupertypeFunctionProvider;
|
||||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
|
||||||
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
|
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
|
||||||
import org.teavm.backend.wasm.model.WasmFunction;
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
@ -39,20 +40,33 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
||||||
private WasmGCStringProvider strings;
|
private WasmGCStringProvider strings;
|
||||||
private VirtualTableProvider virtualTables;
|
private VirtualTableProvider virtualTables;
|
||||||
private WasmGCTypeMapper typeMapper;
|
private WasmGCTypeMapper typeMapper;
|
||||||
|
private WasmFunctionTypes functionTypes;
|
||||||
|
private ClassReaderSource classes;
|
||||||
|
private BaseWasmFunctionRepository functions;
|
||||||
|
private WasmGCSupertypeFunctionProvider supertypeFunctions;
|
||||||
|
private WasmGCCustomGeneratorProvider customGenerators;
|
||||||
private WasmFunction npeMethod;
|
private WasmFunction npeMethod;
|
||||||
private WasmFunction aaiobeMethod;
|
private WasmFunction aaiobeMethod;
|
||||||
private WasmFunction cceMethod;
|
private WasmFunction cceMethod;
|
||||||
private WasmGlobal exceptionGlobal;
|
private WasmGlobal exceptionGlobal;
|
||||||
|
private WasmTag exceptionTag;
|
||||||
|
|
||||||
public WasmGCGenerationContext(WasmModule module, WasmGCClassInfoProvider classInfoProvider,
|
public WasmGCGenerationContext(WasmModule module, VirtualTableProvider virtualTables,
|
||||||
WasmGCStandardClasses standardClasses, WasmGCStringProvider strings, VirtualTableProvider virtualTables,
|
WasmGCTypeMapper typeMapper, WasmFunctionTypes functionTypes, ClassReaderSource classes,
|
||||||
WasmGCTypeMapper typeMapper) {
|
BaseWasmFunctionRepository functions, WasmGCSupertypeFunctionProvider supertypeFunctions,
|
||||||
|
WasmGCClassInfoProvider classInfoProvider, WasmGCStandardClasses standardClasses,
|
||||||
|
WasmGCStringProvider strings, WasmGCCustomGeneratorProvider customGenerators) {
|
||||||
this.module = module;
|
this.module = module;
|
||||||
|
this.virtualTables = virtualTables;
|
||||||
|
this.typeMapper = typeMapper;
|
||||||
|
this.functionTypes = functionTypes;
|
||||||
|
this.classes = classes;
|
||||||
|
this.functions = functions;
|
||||||
|
this.supertypeFunctions = supertypeFunctions;
|
||||||
this.classInfoProvider = classInfoProvider;
|
this.classInfoProvider = classInfoProvider;
|
||||||
this.standardClasses = standardClasses;
|
this.standardClasses = standardClasses;
|
||||||
this.strings = strings;
|
this.strings = strings;
|
||||||
this.virtualTables = virtualTables;
|
this.customGenerators = customGenerators;
|
||||||
this.typeMapper = typeMapper;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public WasmGCClassInfoProvider classInfoProvider() {
|
public WasmGCClassInfoProvider classInfoProvider() {
|
||||||
|
@ -76,23 +90,31 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WasmFunctionRepository functions() {
|
public BaseWasmFunctionRepository functions() {
|
||||||
return null;
|
return functions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WasmGCSupertypeFunctionProvider supertypeFunctions() {
|
||||||
|
return supertypeFunctions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WasmFunctionTypes functionTypes() {
|
public WasmFunctionTypes functionTypes() {
|
||||||
return null;
|
return functionTypes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WasmTag getExceptionTag() {
|
public WasmTag getExceptionTag() {
|
||||||
return null;
|
if (exceptionTag == null) {
|
||||||
|
exceptionTag = new WasmTag(functionTypes.of(null));
|
||||||
|
module.tags.add(exceptionTag);
|
||||||
|
}
|
||||||
|
return exceptionTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClassReaderSource classSource() {
|
public ClassReaderSource classes() {
|
||||||
return null;
|
return classes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public WasmFunction npeMethod() {
|
public WasmFunction npeMethod() {
|
||||||
|
@ -127,4 +149,8 @@ public class WasmGCGenerationContext implements BaseWasmGenerationContext {
|
||||||
}
|
}
|
||||||
return exceptionGlobal;
|
return exceptionGlobal;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public WasmGCCustomGeneratorProvider customGenerators() {
|
||||||
|
return customGenerators;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm.generate.gc.methods;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import org.teavm.backend.wasm.generate.TemporaryVariablePool;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
||||||
|
import org.teavm.backend.wasm.model.WasmArray;
|
||||||
|
import org.teavm.backend.wasm.model.WasmLocal;
|
||||||
|
import org.teavm.backend.wasm.model.WasmType;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmBlock;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmStructSet;
|
||||||
|
import org.teavm.model.TextLocation;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
|
public class WasmGCGenerationUtil {
|
||||||
|
private WasmGCClassInfoProvider classInfoProvider;
|
||||||
|
private TemporaryVariablePool tempVars;
|
||||||
|
|
||||||
|
public WasmGCGenerationUtil(WasmGCClassInfoProvider classInfoProvider, TemporaryVariablePool tempVars) {
|
||||||
|
this.classInfoProvider = classInfoProvider;
|
||||||
|
this.tempVars = tempVars;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void allocateArray(ValueType itemType, WasmExpression length, TextLocation location, WasmLocal local,
|
||||||
|
List<WasmExpression> target) {
|
||||||
|
var classInfo = classInfoProvider.getClassInfo(ValueType.arrayOf(itemType));
|
||||||
|
var block = new WasmBlock(false);
|
||||||
|
block.setType(classInfo.getType());
|
||||||
|
var targetVar = local;
|
||||||
|
if (targetVar == null) {
|
||||||
|
targetVar = tempVars.acquire(classInfo.getType());
|
||||||
|
}
|
||||||
|
|
||||||
|
var structNew = new WasmSetLocal(targetVar, new WasmStructNewDefault(classInfo.getStructure()));
|
||||||
|
structNew.setLocation(location);
|
||||||
|
target.add(structNew);
|
||||||
|
|
||||||
|
var initClassField = new WasmStructSet(classInfo.getStructure(), new WasmGetLocal(targetVar),
|
||||||
|
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET, new WasmGetGlobal(classInfo.getPointer()));
|
||||||
|
initClassField.setLocation(location);
|
||||||
|
target.add(initClassField);
|
||||||
|
|
||||||
|
var wasmArrayType = (WasmType.CompositeReference) classInfo.getStructure().getFields()
|
||||||
|
.get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET)
|
||||||
|
.asUnpackedType();
|
||||||
|
var wasmArray = (WasmArray) wasmArrayType.composite;
|
||||||
|
var initArrayField = new WasmStructSet(
|
||||||
|
classInfo.getStructure(),
|
||||||
|
new WasmGetLocal(targetVar),
|
||||||
|
WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET,
|
||||||
|
new WasmArrayNewDefault(wasmArray, length)
|
||||||
|
);
|
||||||
|
initArrayField.setLocation(location);
|
||||||
|
target.add(initArrayField);
|
||||||
|
|
||||||
|
if (local == null) {
|
||||||
|
var getLocal = new WasmGetLocal(targetVar);
|
||||||
|
getLocal.setLocation(location);
|
||||||
|
target.add(getLocal);
|
||||||
|
tempVars.release(targetVar);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,7 +23,6 @@ import org.teavm.ast.QualificationExpr;
|
||||||
import org.teavm.ast.SubscriptExpr;
|
import org.teavm.ast.SubscriptExpr;
|
||||||
import org.teavm.ast.UnwrapArrayExpr;
|
import org.teavm.ast.UnwrapArrayExpr;
|
||||||
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationVisitor;
|
import org.teavm.backend.wasm.generate.common.methods.BaseWasmGenerationVisitor;
|
||||||
import org.teavm.backend.wasm.generate.gc.WasmGCGenerationContext;
|
|
||||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
||||||
import org.teavm.backend.wasm.model.WasmArray;
|
import org.teavm.backend.wasm.model.WasmArray;
|
||||||
import org.teavm.backend.wasm.model.WasmFunction;
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
@ -33,7 +32,6 @@ import org.teavm.backend.wasm.model.WasmStructure;
|
||||||
import org.teavm.backend.wasm.model.WasmType;
|
import org.teavm.backend.wasm.model.WasmType;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmArrayGet;
|
import org.teavm.backend.wasm.model.expression.WasmArrayGet;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmArrayLength;
|
import org.teavm.backend.wasm.model.expression.WasmArrayLength;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmArrayNewDefault;
|
|
||||||
import org.teavm.backend.wasm.model.expression.WasmArraySet;
|
import org.teavm.backend.wasm.model.expression.WasmArraySet;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmBlock;
|
import org.teavm.backend.wasm.model.expression.WasmBlock;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmCall;
|
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||||
|
@ -57,11 +55,13 @@ import org.teavm.model.ValueType;
|
||||||
|
|
||||||
public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
private WasmGCGenerationContext context;
|
private WasmGCGenerationContext context;
|
||||||
|
private WasmGCGenerationUtil generationUtil;
|
||||||
|
|
||||||
public WasmGCGenerationVisitor(WasmGCGenerationContext context, WasmFunction function,
|
public WasmGCGenerationVisitor(WasmGCGenerationContext context, WasmFunction function,
|
||||||
int firstVariable, boolean async) {
|
int firstVariable, boolean async) {
|
||||||
super(context, function, firstVariable, async);
|
super(context, function, firstVariable, async);
|
||||||
this.context = context;
|
this.context = context;
|
||||||
|
generationUtil = new WasmGCGenerationUtil(context.classInfoProvider(), tempVars);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -130,6 +130,14 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) {
|
protected void storeField(Expr qualified, FieldReference field, Expr value, TextLocation location) {
|
||||||
|
if (qualified == null) {
|
||||||
|
accept(value);
|
||||||
|
var wasmValue = result;
|
||||||
|
var global = context.classInfoProvider().getStaticFieldLocation(field);
|
||||||
|
var result = new WasmSetGlobal(global, wasmValue);
|
||||||
|
result.setLocation(location);
|
||||||
|
resultConsumer.add(result);
|
||||||
|
} else {
|
||||||
accept(qualified);
|
accept(qualified);
|
||||||
var target = result;
|
var target = result;
|
||||||
accept(value);
|
accept(value);
|
||||||
|
@ -145,6 +153,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
expr.setLocation(location);
|
expr.setLocation(location);
|
||||||
resultConsumer.add(expr);
|
resultConsumer.add(expr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected WasmExpression stringLiteral(String s) {
|
protected WasmExpression stringLiteral(String s) {
|
||||||
|
@ -230,42 +239,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
@Override
|
@Override
|
||||||
protected void allocateArray(ValueType itemType, WasmExpression length, TextLocation location, WasmLocal local,
|
protected void allocateArray(ValueType itemType, WasmExpression length, TextLocation location, WasmLocal local,
|
||||||
List<WasmExpression> target) {
|
List<WasmExpression> target) {
|
||||||
var classInfo = context.classInfoProvider().getClassInfo(ValueType.arrayOf(itemType));
|
generationUtil.allocateArray(itemType, length, location, local, target);
|
||||||
var block = new WasmBlock(false);
|
|
||||||
block.setType(classInfo.getType());
|
|
||||||
var targetVar = local;
|
|
||||||
if (targetVar == null) {
|
|
||||||
targetVar = tempVars.acquire(classInfo.getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
var structNew = new WasmSetLocal(targetVar, new WasmStructNewDefault(classInfo.getStructure()));
|
|
||||||
structNew.setLocation(location);
|
|
||||||
target.add(structNew);
|
|
||||||
|
|
||||||
var initClassField = new WasmStructSet(classInfo.getStructure(), new WasmGetLocal(targetVar),
|
|
||||||
WasmGCClassInfoProvider.CLASS_FIELD_OFFSET, new WasmGetGlobal(classInfo.getPointer()));
|
|
||||||
initClassField.setLocation(location);
|
|
||||||
target.add(initClassField);
|
|
||||||
|
|
||||||
var wasmArrayType = (WasmType.CompositeReference) classInfo.getStructure().getFields()
|
|
||||||
.get(WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET)
|
|
||||||
.asUnpackedType();
|
|
||||||
var wasmArray = (WasmArray) wasmArrayType.composite;
|
|
||||||
var initArrayField = new WasmStructSet(
|
|
||||||
classInfo.getStructure(),
|
|
||||||
new WasmGetLocal(targetVar),
|
|
||||||
WasmGCClassInfoProvider.ARRAY_DATA_FIELD_OFFSET,
|
|
||||||
new WasmArrayNewDefault(wasmArray, length)
|
|
||||||
);
|
|
||||||
initArrayField.setLocation(location);
|
|
||||||
target.add(initArrayField);
|
|
||||||
|
|
||||||
if (local == null) {
|
|
||||||
var getLocal = new WasmGetLocal(targetVar);
|
|
||||||
getLocal.setLocation(location);
|
|
||||||
target.add(getLocal);
|
|
||||||
tempVars.release(targetVar);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -277,7 +251,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
@Override
|
@Override
|
||||||
protected WasmExpression generateInstanceOf(WasmExpression expression, ValueType type) {
|
protected WasmExpression generateInstanceOf(WasmExpression expression, ValueType type) {
|
||||||
context.classInfoProvider().getClassInfo(type);
|
context.classInfoProvider().getClassInfo(type);
|
||||||
var supertypeCall = new WasmCall(context.functions().forSupertype(type));
|
var supertypeCall = new WasmCall(context.supertypeFunctions().getIsSupertypeFunction(type));
|
||||||
var classRef = new WasmStructGet(
|
var classRef = new WasmStructGet(
|
||||||
context.standardClasses().objectClass().getStructure(),
|
context.standardClasses().objectClass().getStructure(),
|
||||||
expression,
|
expression,
|
||||||
|
@ -352,6 +326,11 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(QualificationExpr expr) {
|
public void visit(QualificationExpr expr) {
|
||||||
|
if (expr.getQualified() == null) {
|
||||||
|
var global = context.classInfoProvider().getStaticFieldLocation(expr.getField());
|
||||||
|
result = new WasmGetGlobal(global);
|
||||||
|
result.setLocation(expr.getLocation());
|
||||||
|
} else {
|
||||||
accept(expr.getQualified());
|
accept(expr.getQualified());
|
||||||
var target = result;
|
var target = result;
|
||||||
|
|
||||||
|
@ -364,6 +343,7 @@ public class WasmGCGenerationVisitor extends BaseWasmGenerationVisitor {
|
||||||
result = new WasmStructGet(struct, target, fieldIndex);
|
result = new WasmStructGet(struct, target, fieldIndex);
|
||||||
result.setLocation(expr.getLocation());
|
result.setLocation(expr.getLocation());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private class SimpleCallSite extends CallSiteIdentifier {
|
private class SimpleCallSite extends CallSiteIdentifier {
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,319 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm.generate.gc.methods;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.Set;
|
||||||
|
import org.teavm.ast.decompilation.Decompiler;
|
||||||
|
import org.teavm.backend.lowlevel.generate.NameProvider;
|
||||||
|
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
||||||
|
import org.teavm.backend.wasm.WasmFunctionTypes;
|
||||||
|
import org.teavm.backend.wasm.gc.WasmGCVariableCategoryProvider;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCSupertypeFunctionProvider;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCTypeMapper;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringProvider;
|
||||||
|
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGenerator;
|
||||||
|
import org.teavm.backend.wasm.generators.gc.WasmGCCustomGeneratorContext;
|
||||||
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
import org.teavm.backend.wasm.model.WasmLocal;
|
||||||
|
import org.teavm.backend.wasm.model.WasmModule;
|
||||||
|
import org.teavm.backend.wasm.model.WasmType;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmFunctionReference;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmReturn;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
||||||
|
import org.teavm.diagnostics.Diagnostics;
|
||||||
|
import org.teavm.interop.Import;
|
||||||
|
import org.teavm.model.CallLocation;
|
||||||
|
import org.teavm.model.ClassHierarchy;
|
||||||
|
import org.teavm.model.ClassHolderSource;
|
||||||
|
import org.teavm.model.ElementModifier;
|
||||||
|
import org.teavm.model.MethodHolder;
|
||||||
|
import org.teavm.model.MethodReader;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
import org.teavm.model.analysis.ClassInitializerInfo;
|
||||||
|
import org.teavm.model.classes.VirtualTableProvider;
|
||||||
|
import org.teavm.model.util.RegisterAllocator;
|
||||||
|
|
||||||
|
public class WasmGCMethodGenerator implements BaseWasmFunctionRepository {
|
||||||
|
private WasmModule module;
|
||||||
|
private ClassHierarchy hierarchy;
|
||||||
|
private ClassHolderSource classes;
|
||||||
|
private VirtualTableProvider virtualTables;
|
||||||
|
private ClassInitializerInfo classInitInfo;
|
||||||
|
private WasmFunctionTypes functionTypes;
|
||||||
|
private WasmGCSupertypeFunctionProvider supertypeFunctions;
|
||||||
|
private NameProvider names;
|
||||||
|
private Diagnostics diagnostics;
|
||||||
|
private WasmGCTypeMapper typeMapper;
|
||||||
|
private WasmGCCustomGeneratorProvider customGenerators;
|
||||||
|
private Queue<Runnable> queue = new ArrayDeque<>();
|
||||||
|
private Map<MethodReference, WasmFunction> staticMethods = new HashMap<>();
|
||||||
|
private Map<MethodReference, WasmFunction> instanceMethods = new HashMap<>();
|
||||||
|
private boolean friendlyToDebugger;
|
||||||
|
private Decompiler decompiler;
|
||||||
|
private WasmGCGenerationContext context;
|
||||||
|
private WasmFunction dummyInitializer;
|
||||||
|
private WasmGCClassInfoProvider classInfoProvider;
|
||||||
|
private WasmGCStandardClasses standardClasses;
|
||||||
|
private WasmGCStringProvider strings;
|
||||||
|
|
||||||
|
public WasmGCMethodGenerator(
|
||||||
|
WasmModule module,
|
||||||
|
ClassHierarchy hierarchy,
|
||||||
|
ClassHolderSource classes,
|
||||||
|
VirtualTableProvider virtualTables,
|
||||||
|
ClassInitializerInfo classInitInfo,
|
||||||
|
WasmFunctionTypes functionTypes,
|
||||||
|
NameProvider names,
|
||||||
|
Diagnostics diagnostics,
|
||||||
|
WasmGCCustomGeneratorProvider customGenerators
|
||||||
|
) {
|
||||||
|
this.module = module;
|
||||||
|
this.hierarchy = hierarchy;
|
||||||
|
this.classes = classes;
|
||||||
|
this.virtualTables = virtualTables;
|
||||||
|
this.classInitInfo = classInitInfo;
|
||||||
|
this.functionTypes = functionTypes;
|
||||||
|
this.names = names;
|
||||||
|
this.diagnostics = diagnostics;
|
||||||
|
this.customGenerators = customGenerators;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTypeMapper(WasmGCTypeMapper typeMapper) {
|
||||||
|
this.typeMapper = typeMapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFriendlyToDebugger(boolean friendlyToDebugger) {
|
||||||
|
this.friendlyToDebugger = friendlyToDebugger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setClassInfoProvider(WasmGCClassInfoProvider classInfoProvider) {
|
||||||
|
this.classInfoProvider = classInfoProvider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStandardClasses(WasmGCStandardClasses standardClasses) {
|
||||||
|
this.standardClasses = standardClasses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setSupertypeFunctions(WasmGCSupertypeFunctionProvider supertypeFunctions) {
|
||||||
|
this.supertypeFunctions = supertypeFunctions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStrings(WasmGCStringProvider strings) {
|
||||||
|
this.strings = strings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean process() {
|
||||||
|
if (queue.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
while (!queue.isEmpty()) {
|
||||||
|
queue.remove().run();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WasmFunction forStaticMethod(MethodReference methodReference) {
|
||||||
|
return staticMethods.computeIfAbsent(methodReference, this::createStaticFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private WasmFunction createStaticFunction(MethodReference methodReference) {
|
||||||
|
var returnType = typeMapper.mapType(methodReference.getReturnType()).asUnpackedType();
|
||||||
|
var parameterTypes = new WasmType[methodReference.parameterCount()];
|
||||||
|
for (var i = 0; i < parameterTypes.length; ++i) {
|
||||||
|
parameterTypes[i] = typeMapper.mapType(methodReference.parameterType(i)).asUnpackedType();
|
||||||
|
}
|
||||||
|
var function = new WasmFunction(functionTypes.of(returnType, parameterTypes));
|
||||||
|
function.setName(names.forMethod(methodReference));
|
||||||
|
module.functions.add(function);
|
||||||
|
function.setJavaMethod(methodReference);
|
||||||
|
|
||||||
|
var cls = classes.get(methodReference.getClassName());
|
||||||
|
if (cls != null) {
|
||||||
|
var method = cls.getMethod(methodReference.getDescriptor());
|
||||||
|
if (method != null && method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
queue.add(() -> generateMethodBody(method, function));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WasmFunction forInstanceMethod(MethodReference methodReference) {
|
||||||
|
return instanceMethods.computeIfAbsent(methodReference, this::createInstanceFunction);
|
||||||
|
}
|
||||||
|
|
||||||
|
private WasmFunction createInstanceFunction(MethodReference methodReference) {
|
||||||
|
var returnType = typeMapper.mapType(methodReference.getReturnType()).asUnpackedType();
|
||||||
|
var parameterTypes = new WasmType[methodReference.parameterCount() + 1];
|
||||||
|
parameterTypes[0] = typeMapper.mapType(ValueType.object(methodReference.getClassName())).asUnpackedType();
|
||||||
|
for (var i = 0; i < methodReference.parameterCount(); ++i) {
|
||||||
|
parameterTypes[i + 1] = typeMapper.mapType(methodReference.parameterType(i)).asUnpackedType();
|
||||||
|
}
|
||||||
|
var function = new WasmFunction(functionTypes.of(returnType, parameterTypes));
|
||||||
|
function.setName(names.forMethod(methodReference));
|
||||||
|
module.functions.add(function);
|
||||||
|
function.setJavaMethod(methodReference);
|
||||||
|
|
||||||
|
var cls = classes.get(methodReference.getClassName());
|
||||||
|
if (cls != null) {
|
||||||
|
var method = cls.getMethod(methodReference.getDescriptor());
|
||||||
|
if (method != null && !method.hasModifier(ElementModifier.STATIC)) {
|
||||||
|
queue.add(() -> generateMethodBody(method, function));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateMethodBody(MethodHolder method, WasmFunction function) {
|
||||||
|
var customGenerator = customGenerators.get(method.getReference());
|
||||||
|
if (customGenerator != null) {
|
||||||
|
generateCustomMethodBody(customGenerator, method.getReference(), function);
|
||||||
|
} else if (!method.hasModifier(ElementModifier.NATIVE)) {
|
||||||
|
generateRegularMethodBody(method, function);
|
||||||
|
} else {
|
||||||
|
generateNativeMethodBody(method, function);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateCustomMethodBody(WasmGCCustomGenerator customGenerator, MethodReference method,
|
||||||
|
WasmFunction function) {
|
||||||
|
customGenerator.apply(method, function, customGeneratorContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateRegularMethodBody(MethodHolder method, WasmFunction function) {
|
||||||
|
var decompiler = getDecompiler();
|
||||||
|
var categoryProvider = new WasmGCVariableCategoryProvider(hierarchy);
|
||||||
|
var allocator = new RegisterAllocator(categoryProvider);
|
||||||
|
allocator.allocateRegisters(method.getReference(), method.getProgram(), friendlyToDebugger);
|
||||||
|
var ast = decompiler.decompileRegular(method);
|
||||||
|
var firstVar = method.hasModifier(ElementModifier.STATIC) ? 1 : 0;
|
||||||
|
var typeInference = categoryProvider.getTypeInference();
|
||||||
|
|
||||||
|
var registerCount = 0;
|
||||||
|
for (var i = 0; i < method.getProgram().variableCount(); ++i) {
|
||||||
|
registerCount = Math.max(registerCount, method.getProgram().variableAt(i).getRegister() + 1);
|
||||||
|
}
|
||||||
|
var originalIndexToIndex = new int[registerCount];
|
||||||
|
Arrays.fill(originalIndexToIndex, -1);
|
||||||
|
for (var varNode : ast.getVariables()) {
|
||||||
|
originalIndexToIndex[varNode.getOriginalIndex()] = varNode.getIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
var variableRepresentatives = new int[registerCount];
|
||||||
|
Arrays.fill(variableRepresentatives, -1);
|
||||||
|
for (var i = 0; i < method.getProgram().variableCount(); ++i) {
|
||||||
|
var variable = method.getProgram().variableAt(i);
|
||||||
|
var varNodeIndex = variable.getRegister() >= 0 ? originalIndexToIndex[variable.getRegister()] : -1;
|
||||||
|
if (varNodeIndex >= 0 && variableRepresentatives[varNodeIndex] < 0) {
|
||||||
|
variableRepresentatives[varNodeIndex] = variable.getIndex();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = firstVar; i < ast.getVariables().size(); ++i) {
|
||||||
|
var localVar = ast.getVariables().get(i);
|
||||||
|
var representative = method.getProgram().variableAt(variableRepresentatives[i]);
|
||||||
|
var type = typeMapper.mapType(typeInference.typeOf(representative)).asUnpackedType();
|
||||||
|
var wasmLocal = new WasmLocal(type, localVar.getName());
|
||||||
|
function.add(wasmLocal);
|
||||||
|
}
|
||||||
|
|
||||||
|
addInitializerErase(method, function);
|
||||||
|
var visitor = new WasmGCGenerationVisitor(getGenerationContext(), function, firstVar, false);
|
||||||
|
visitor.generate(ast.getBody(), function.getBody());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateNativeMethodBody(MethodHolder method, WasmFunction function) {
|
||||||
|
var importAnnot = method.getAnnotations().get(Import.class.getName());
|
||||||
|
if (importAnnot == null) {
|
||||||
|
diagnostics.error(new CallLocation(method.getReference()), "Method is not annotated with {{c0}}",
|
||||||
|
Import.class.getName());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
function.setImportName(importAnnot.getValue("name").getString());
|
||||||
|
var moduleName = importAnnot.getValue("module");
|
||||||
|
function.setImportModule(moduleName != null ? moduleName.getString() : "teavm");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addInitializerErase(MethodReader method, WasmFunction function) {
|
||||||
|
if (method.hasModifier(ElementModifier.STATIC) && method.getName().equals("<clinit>")
|
||||||
|
&& method.parameterCount() == 0 && classInitInfo.isDynamicInitializer(method.getOwnerName())) {
|
||||||
|
var classInfo = classInfoProvider.getClassInfo(method.getOwnerName());
|
||||||
|
var erase = new WasmSetGlobal(classInfo.getInitializerPointer(),
|
||||||
|
new WasmFunctionReference(getDummyInitializer()));
|
||||||
|
function.getBody().add(erase);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Decompiler getDecompiler() {
|
||||||
|
if (decompiler == null) {
|
||||||
|
decompiler = new Decompiler(classes, Set.of(), friendlyToDebugger);
|
||||||
|
}
|
||||||
|
return decompiler;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WasmGCGenerationContext getGenerationContext() {
|
||||||
|
if (context == null) {
|
||||||
|
context = new WasmGCGenerationContext(
|
||||||
|
module,
|
||||||
|
virtualTables,
|
||||||
|
typeMapper,
|
||||||
|
functionTypes,
|
||||||
|
classes,
|
||||||
|
this,
|
||||||
|
supertypeFunctions,
|
||||||
|
classInfoProvider,
|
||||||
|
standardClasses,
|
||||||
|
strings,
|
||||||
|
customGenerators
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
public WasmFunction getDummyInitializer() {
|
||||||
|
if (dummyInitializer == null) {
|
||||||
|
dummyInitializer = new WasmFunction(functionTypes.of(null));
|
||||||
|
dummyInitializer.getBody().add(new WasmReturn());
|
||||||
|
dummyInitializer.setName("teavm_dummy_initializer");
|
||||||
|
module.functions.add(dummyInitializer);
|
||||||
|
}
|
||||||
|
return dummyInitializer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WasmGCCustomGeneratorContext customGeneratorContext = new WasmGCCustomGeneratorContext() {
|
||||||
|
@Override
|
||||||
|
public WasmModule module() {
|
||||||
|
return module;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WasmFunctionTypes functionTypes() {
|
||||||
|
return functionTypes;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
|
@ -18,15 +18,20 @@ package org.teavm.backend.wasm.generate.gc.strings;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import org.teavm.backend.wasm.BaseWasmFunctionRepository;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.WasmGCInitializerContributor;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
||||||
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCStandardClasses;
|
||||||
import org.teavm.backend.wasm.generate.gc.initialization.WasmGCInitializerContributor;
|
|
||||||
import org.teavm.backend.wasm.generate.gc.methods.WasmGCFunctionProvider;
|
|
||||||
import org.teavm.backend.wasm.model.WasmFunction;
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
import org.teavm.backend.wasm.model.WasmGlobal;
|
import org.teavm.backend.wasm.model.WasmGlobal;
|
||||||
|
import org.teavm.backend.wasm.model.WasmMemorySegment;
|
||||||
import org.teavm.backend.wasm.model.WasmModule;
|
import org.teavm.backend.wasm.model.WasmModule;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
import org.teavm.backend.wasm.model.expression.WasmExpression;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
import org.teavm.backend.wasm.model.expression.WasmSetGlobal;
|
||||||
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
|
import org.teavm.backend.wasm.model.expression.WasmStructNewDefault;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmStructSet;
|
||||||
import org.teavm.backend.wasm.render.WasmBinaryWriter;
|
import org.teavm.backend.wasm.render.WasmBinaryWriter;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
@ -35,10 +40,10 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
|
||||||
private WasmModule module;
|
private WasmModule module;
|
||||||
private WasmBinaryWriter binaryWriter = new WasmBinaryWriter();
|
private WasmBinaryWriter binaryWriter = new WasmBinaryWriter();
|
||||||
private Map<String, WasmGCStringConstant> stringMap = new LinkedHashMap<>();
|
private Map<String, WasmGCStringConstant> stringMap = new LinkedHashMap<>();
|
||||||
private WasmGCFunctionProvider functionProvider;
|
private BaseWasmFunctionRepository functionProvider;
|
||||||
|
|
||||||
public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module,
|
public WasmGCStringPool(WasmGCStandardClasses standardClasses, WasmModule module,
|
||||||
WasmGCFunctionProvider functionProvider) {
|
BaseWasmFunctionRepository functionProvider) {
|
||||||
this.standardClasses = standardClasses;
|
this.standardClasses = standardClasses;
|
||||||
this.module = module;
|
this.module = module;
|
||||||
this.functionProvider = functionProvider;
|
this.functionProvider = functionProvider;
|
||||||
|
@ -46,6 +51,9 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contributeToInitializerDefinitions(WasmFunction function) {
|
public void contributeToInitializerDefinitions(WasmFunction function) {
|
||||||
|
var segment = new WasmMemorySegment();
|
||||||
|
module.getSegments().add(segment);
|
||||||
|
segment.setData(binaryWriter.getData());
|
||||||
for (var str : stringMap.values()) {
|
for (var str : stringMap.values()) {
|
||||||
var newStruct = new WasmStructNewDefault(standardClasses.stringClass().getStructure());
|
var newStruct = new WasmStructNewDefault(standardClasses.stringClass().getStructure());
|
||||||
function.getBody().add(new WasmSetGlobal(str.global, newStruct));
|
function.getBody().add(new WasmSetGlobal(str.global, newStruct));
|
||||||
|
@ -54,8 +62,14 @@ public class WasmGCStringPool implements WasmGCStringProvider, WasmGCInitializer
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void contributeToInitializer(WasmFunction function) {
|
public void contributeToInitializer(WasmFunction function) {
|
||||||
var nextCharArrayFunction = functionProvider.getStaticFunction(new MethodReference(WasmGCStringPool.class,
|
var nextCharArrayFunction = functionProvider.forStaticMethod(new MethodReference(WasmGCStringPool.class,
|
||||||
"nextCharArray", char[].class));
|
"nextCharArray", char[].class));
|
||||||
|
var stringStruct = standardClasses.stringClass().getStructure();
|
||||||
|
for (var str : stringMap.values()) {
|
||||||
|
var value = new WasmCall(nextCharArrayFunction);
|
||||||
|
function.getBody().add(new WasmStructSet(stringStruct, new WasmGetGlobal(str.global),
|
||||||
|
WasmGCClassInfoProvider.CUSTOM_FIELD_OFFSETS, value));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm.generators.gc;
|
||||||
|
|
||||||
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public interface WasmGCCustomGenerator {
|
||||||
|
void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm.generators.gc;
|
||||||
|
|
||||||
|
import org.teavm.backend.wasm.WasmFunctionTypes;
|
||||||
|
import org.teavm.backend.wasm.model.WasmModule;
|
||||||
|
|
||||||
|
public interface WasmGCCustomGeneratorContext {
|
||||||
|
WasmModule module();
|
||||||
|
|
||||||
|
WasmFunctionTypes functionTypes();
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm.generators.gc;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.methods.WasmGCCustomGeneratorProvider;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.strings.WasmGCStringPool;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
|
||||||
|
private Map<MethodReference, WasmGCCustomGenerator> generators = new HashMap<>();
|
||||||
|
|
||||||
|
public WasmGCCustomGenerators() {
|
||||||
|
fillStringPool();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void fillStringPool() {
|
||||||
|
generators.put(
|
||||||
|
new MethodReference(WasmGCStringPool.class, "nextByte", byte.class),
|
||||||
|
new WasmGCStringPoolGenerator()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public WasmGCCustomGenerator get(MethodReference method) {
|
||||||
|
return generators.get(method);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 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.backend.wasm.generators.gc;
|
||||||
|
|
||||||
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
import org.teavm.backend.wasm.model.WasmGlobal;
|
||||||
|
import org.teavm.backend.wasm.model.WasmLocal;
|
||||||
|
import org.teavm.backend.wasm.model.WasmType;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmGetGlobal;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmInt32Constant;
|
||||||
|
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.WasmReturn;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmSetLocal;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
|
public class WasmGCStringPoolGenerator implements WasmGCCustomGenerator {
|
||||||
|
@Override
|
||||||
|
public void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context) {
|
||||||
|
var module = context.module();
|
||||||
|
var pointer = new WasmGlobal("teavm_string_pool_pointer", WasmType.INT32, new WasmInt32Constant(0));
|
||||||
|
module.globals.add(pointer);
|
||||||
|
|
||||||
|
var resultLocal = new WasmLocal(WasmType.INT32);
|
||||||
|
function.add(resultLocal);
|
||||||
|
|
||||||
|
var increment = new WasmIntBinary(WasmIntType.INT32, WasmIntBinaryOperation.ADD,
|
||||||
|
new WasmGetGlobal(pointer), new WasmInt32Constant(1));
|
||||||
|
function.getBody().add(new WasmSetLocal(resultLocal, increment));
|
||||||
|
function.getBody().add(new WasmReturn(new WasmGetGlobal(pointer)));
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,10 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.model;
|
package org.teavm.backend.wasm.model;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class WasmStructure extends WasmCompositeType {
|
public class WasmStructure extends WasmCompositeType {
|
||||||
private List<WasmStorageType> fields;
|
private List<WasmStorageType> fields = new ArrayList<>();
|
||||||
|
|
||||||
public WasmStructure(String name) {
|
public WasmStructure(String name) {
|
||||||
super(name);
|
super(name);
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.teavm.backend.wasm.model.WasmArray;
|
||||||
import org.teavm.backend.wasm.model.WasmCompositeTypeVisitor;
|
import org.teavm.backend.wasm.model.WasmCompositeTypeVisitor;
|
||||||
import org.teavm.backend.wasm.model.WasmFunctionType;
|
import org.teavm.backend.wasm.model.WasmFunctionType;
|
||||||
import org.teavm.backend.wasm.model.WasmModule;
|
import org.teavm.backend.wasm.model.WasmModule;
|
||||||
|
import org.teavm.backend.wasm.model.WasmStorageType;
|
||||||
import org.teavm.backend.wasm.model.WasmStructure;
|
import org.teavm.backend.wasm.model.WasmStructure;
|
||||||
|
|
||||||
public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor {
|
public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor {
|
||||||
|
@ -26,17 +27,24 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor
|
||||||
private WasmBinaryWriter section;
|
private WasmBinaryWriter section;
|
||||||
|
|
||||||
public WasmCompositeTypeBinaryRenderer(WasmModule module, WasmBinaryWriter section) {
|
public WasmCompositeTypeBinaryRenderer(WasmModule module, WasmBinaryWriter section) {
|
||||||
|
this.module = module;
|
||||||
this.section = section;
|
this.section = section;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmStructure type) {
|
public void visit(WasmStructure type) {
|
||||||
|
section.writeByte(0x5F);
|
||||||
|
section.writeLEB(type.getFields().size());
|
||||||
|
for (var fieldType : type.getFields()) {
|
||||||
|
writeStorageType(fieldType);
|
||||||
|
section.writeLEB(0x01); // mutable
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void visit(WasmArray type) {
|
public void visit(WasmArray type) {
|
||||||
|
writeStorageType(type.getElementType());
|
||||||
|
section.writeLEB(0x01); // mutable
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -53,4 +61,19 @@ public class WasmCompositeTypeBinaryRenderer implements WasmCompositeTypeVisitor
|
||||||
section.writeByte(0);
|
section.writeByte(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void writeStorageType(WasmStorageType storageType) {
|
||||||
|
if (storageType instanceof WasmStorageType.Packed) {
|
||||||
|
switch (((WasmStorageType.Packed) storageType).type) {
|
||||||
|
case INT8:
|
||||||
|
section.writeByte(0x78);
|
||||||
|
break;
|
||||||
|
case INT16:
|
||||||
|
section.writeByte(0x77);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
section.writeType(storageType.asUnpackedType(), module);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.runtime;
|
package org.teavm.backend.wasm.runtime;
|
||||||
|
|
||||||
|
import org.teavm.interop.Import;
|
||||||
|
|
||||||
public class WasmGCSupport {
|
public class WasmGCSupport {
|
||||||
private WasmGCSupport() {
|
private WasmGCSupport() {
|
||||||
}
|
}
|
||||||
|
@ -30,4 +32,10 @@ public class WasmGCSupport {
|
||||||
public static ClassCastException cce() {
|
public static ClassCastException cce() {
|
||||||
return new ClassCastException();
|
return new ClassCastException();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Import(name = "putcharStdout")
|
||||||
|
public static native void putCharStdout(char c);
|
||||||
|
|
||||||
|
@Import(name = "putcharStderr")
|
||||||
|
public static native void putCharStderr(char c);
|
||||||
}
|
}
|
||||||
|
|
|
@ -64,7 +64,7 @@ public class MethodDescriptor implements Serializable {
|
||||||
|
|
||||||
public ValueType parameterType(int index) {
|
public ValueType parameterType(int index) {
|
||||||
if (index >= signature.length - 1) {
|
if (index >= signature.length - 1) {
|
||||||
throw new IndexOutOfBoundsException(String.valueOf(index) + "/" + (signature.length - 1));
|
throw new IndexOutOfBoundsException(index + "/" + (signature.length - 1));
|
||||||
}
|
}
|
||||||
return signature[index];
|
return signature[index];
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,10 +36,14 @@ import org.teavm.common.LCATree;
|
||||||
import org.teavm.model.AccessLevel;
|
import org.teavm.model.AccessLevel;
|
||||||
import org.teavm.model.ClassReader;
|
import org.teavm.model.ClassReader;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
|
import org.teavm.model.ListableClassHolderSource;
|
||||||
import org.teavm.model.ListableClassReaderSource;
|
import org.teavm.model.ListableClassReaderSource;
|
||||||
import org.teavm.model.MethodDescriptor;
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodReader;
|
import org.teavm.model.MethodReader;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.instructions.CloneArrayInstruction;
|
||||||
|
import org.teavm.model.instructions.InvocationType;
|
||||||
|
import org.teavm.model.instructions.InvokeInstruction;
|
||||||
import org.teavm.model.util.GraphColorer;
|
import org.teavm.model.util.GraphColorer;
|
||||||
|
|
||||||
public class VirtualTableBuilder {
|
public class VirtualTableBuilder {
|
||||||
|
@ -518,4 +522,33 @@ public class VirtualTableBuilder {
|
||||||
IntArrayList colors = new IntArrayList();
|
IntArrayList colors = new IntArrayList();
|
||||||
List<MethodDescriptor> methods = new ArrayList<>();
|
List<MethodDescriptor> methods = new ArrayList<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Set<MethodReference> getMethodsUsedOnCallSites(ListableClassHolderSource classes) {
|
||||||
|
var virtualMethods = new HashSet<MethodReference>();
|
||||||
|
|
||||||
|
for (var className : classes.getClassNames()) {
|
||||||
|
var cls = classes.get(className);
|
||||||
|
for (var method : cls.getMethods()) {
|
||||||
|
var program = method.getProgram();
|
||||||
|
if (program == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
for (int i = 0; i < program.basicBlockCount(); ++i) {
|
||||||
|
var block = program.basicBlockAt(i);
|
||||||
|
for (var insn : block) {
|
||||||
|
if (insn instanceof InvokeInstruction) {
|
||||||
|
var invoke = (InvokeInstruction) insn;
|
||||||
|
if (invoke.getType() == InvocationType.VIRTUAL) {
|
||||||
|
virtualMethods.add(invoke.getMethod());
|
||||||
|
}
|
||||||
|
} else if (insn instanceof CloneArrayInstruction) {
|
||||||
|
virtualMethods.add(new MethodReference(Object.class, "clone", Object.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return virtualMethods;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
package org.teavm.model.util;
|
package org.teavm.model.util;
|
||||||
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ProgramReader;
|
import org.teavm.model.Program;
|
||||||
|
|
||||||
public class DefaultVariableCategoryProvider implements VariableCategoryProvider {
|
public class DefaultVariableCategoryProvider implements VariableCategoryProvider {
|
||||||
@Override
|
@Override
|
||||||
public Object[] getCategories(ProgramReader program, MethodReference method) {
|
public Object[] getCategories(Program program, MethodReference method) {
|
||||||
TypeInferer inferer = new TypeInferer();
|
TypeInferer inferer = new TypeInferer();
|
||||||
inferer.inferTypes(program, method);
|
inferer.inferTypes(program, method);
|
||||||
var categories = new Object[program.variableCount()];
|
var categories = new Object[program.variableCount()];
|
||||||
|
|
|
@ -16,8 +16,8 @@
|
||||||
package org.teavm.model.util;
|
package org.teavm.model.util;
|
||||||
|
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.ProgramReader;
|
import org.teavm.model.Program;
|
||||||
|
|
||||||
public interface VariableCategoryProvider {
|
public interface VariableCategoryProvider {
|
||||||
Object[] getCategories(ProgramReader program, MethodReference method);
|
Object[] getCategories(Program program, MethodReference method);
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,5 +60,4 @@ public interface TeaVMTargetController {
|
||||||
void addVirtualMethods(Predicate<MethodReference> methods);
|
void addVirtualMethods(Predicate<MethodReference> methods);
|
||||||
|
|
||||||
ClassInitializerInfo getClassInitializerInfo();
|
ClassInitializerInfo getClassInitializerInfo();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,4 +23,5 @@ public final class Platforms {
|
||||||
public static final String WEBASSEMBLY = "webassembly";
|
public static final String WEBASSEMBLY = "webassembly";
|
||||||
public static final String C = "c";
|
public static final String C = "c";
|
||||||
public static final String LOW_LEVEL = "low_level";
|
public static final String LOW_LEVEL = "low_level";
|
||||||
|
public static final String WEBASSEMBLY_GC = "webassembly-gc";
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user