mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 08:14:09 -08:00
wasm gc: basic implementation of WeakReference
This commit is contained in:
parent
28c64ef7d8
commit
8ed8322b17
|
@ -35,6 +35,7 @@ public class WasmGCDependencies {
|
||||||
contributeMathUtils();
|
contributeMathUtils();
|
||||||
contributeExceptionUtils();
|
contributeExceptionUtils();
|
||||||
contributeInitializerUtils();
|
contributeInitializerUtils();
|
||||||
|
analyzer.addDependencyListener(new WasmGCReferenceQueueDependency());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void contributeStandardExports() {
|
public void contributeStandardExports() {
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.dependency.AbstractDependencyListener;
|
||||||
|
import org.teavm.dependency.DependencyAgent;
|
||||||
|
import org.teavm.dependency.DependencyNode;
|
||||||
|
import org.teavm.dependency.MethodDependency;
|
||||||
|
|
||||||
|
public class WasmGCReferenceQueueDependency extends AbstractDependencyListener {
|
||||||
|
private DependencyNode valueNode;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void started(DependencyAgent agent) {
|
||||||
|
valueNode = agent.createNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void methodReached(DependencyAgent agent, MethodDependency method) {
|
||||||
|
if (method.getMethod().getOwnerName().equals("java.lang.ref.WeakReference")) {
|
||||||
|
switch (method.getMethod().getName()) {
|
||||||
|
case "<init>":
|
||||||
|
method.getVariable(1).connect(valueNode);
|
||||||
|
break;
|
||||||
|
case "get":
|
||||||
|
valueNode.connect(method.getResult());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1118,6 +1118,10 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
if (classReader.getParent() != null) {
|
if (classReader.getParent() != null) {
|
||||||
fillClassFields(fields, classReader.getParent());
|
fillClassFields(fields, classReader.getParent());
|
||||||
}
|
}
|
||||||
|
if (className.equals("java.lang.ref.WeakReference")) {
|
||||||
|
var field = new WasmField(WasmType.Reference.EXTERN.asStorage(), "nativeRef");
|
||||||
|
fields.add(field);
|
||||||
|
} else {
|
||||||
for (var field : classReader.getFields()) {
|
for (var field : classReader.getFields()) {
|
||||||
if (className.equals("java.lang.Object") && field.getName().equals("monitor")) {
|
if (className.equals("java.lang.Object") && field.getName().equals("monitor")) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -1133,6 +1137,7 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
||||||
names.forMemberField(field.getReference()));
|
names.forMemberField(field.getReference()));
|
||||||
fields.add(wasmField);
|
fields.add(wasmField);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (className.equals("java.lang.Class")) {
|
if (className.equals("java.lang.Class")) {
|
||||||
var cls = classSource.get("java.lang.Class");
|
var cls = classSource.get("java.lang.Class");
|
||||||
classFlagsOffset = fields.size();
|
classFlagsOffset = fields.size();
|
||||||
|
|
|
@ -26,6 +26,7 @@ public interface WasmGCClassInfoProvider {
|
||||||
int MONITOR_FIELD_OFFSET = 1;
|
int MONITOR_FIELD_OFFSET = 1;
|
||||||
int CUSTOM_FIELD_OFFSETS = 2;
|
int CUSTOM_FIELD_OFFSETS = 2;
|
||||||
int ARRAY_DATA_FIELD_OFFSET = 2;
|
int ARRAY_DATA_FIELD_OFFSET = 2;
|
||||||
|
int WEAK_REFERENCE_OFFSET = 2;
|
||||||
|
|
||||||
WasmGCClassInfo getClassInfo(ValueType type);
|
WasmGCClassInfo getClassInfo(ValueType type);
|
||||||
|
|
||||||
|
|
|
@ -15,6 +15,8 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.generators.gc;
|
package org.teavm.backend.wasm.generators.gc;
|
||||||
|
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
import java.lang.reflect.Array;
|
import java.lang.reflect.Array;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -41,6 +43,7 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
|
||||||
fillStringPool();
|
fillStringPool();
|
||||||
fillSystem();
|
fillSystem();
|
||||||
fillArray();
|
fillArray();
|
||||||
|
fillWeakReference();
|
||||||
for (var entry : generators.entrySet()) {
|
for (var entry : generators.entrySet()) {
|
||||||
add(entry.getKey(), entry.getValue());
|
add(entry.getKey(), entry.getValue());
|
||||||
}
|
}
|
||||||
|
@ -68,6 +71,14 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
|
||||||
add(new MethodReference(Array.class, "newInstanceImpl", Class.class, int.class, Object.class), arrayGenerator);
|
add(new MethodReference(Array.class, "newInstanceImpl", Class.class, int.class, Object.class), arrayGenerator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void fillWeakReference() {
|
||||||
|
var generator = new WeakReferenceGenerator();
|
||||||
|
add(new MethodReference(WeakReference.class, "<init>", Object.class, ReferenceQueue.class,
|
||||||
|
void.class), generator);
|
||||||
|
add(new MethodReference(WeakReference.class, "get", Object.class), generator);
|
||||||
|
add(new MethodReference(WeakReference.class, "clear", void.class), generator);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public WasmGCCustomGenerator get(MethodReference method) {
|
public WasmGCCustomGenerator get(MethodReference method) {
|
||||||
var result = generators.get(method);
|
var result = generators.get(method);
|
||||||
|
|
|
@ -0,0 +1,121 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2024 konsoletyper.
|
||||||
|
*
|
||||||
|
* 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.lang.ref.ReferenceQueue;
|
||||||
|
import java.lang.ref.WeakReference;
|
||||||
|
import org.teavm.backend.wasm.generate.gc.classes.WasmGCClassInfoProvider;
|
||||||
|
import org.teavm.backend.wasm.model.WasmFunction;
|
||||||
|
import org.teavm.backend.wasm.model.WasmLocal;
|
||||||
|
import org.teavm.backend.wasm.model.WasmType;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmBlock;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmCall;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmGetLocal;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmNullBranch;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmNullCondition;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmNullConstant;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmReturn;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmStructGet;
|
||||||
|
import org.teavm.backend.wasm.model.expression.WasmStructSet;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
import org.teavm.model.ValueType;
|
||||||
|
|
||||||
|
public class WeakReferenceGenerator implements WasmGCCustomGenerator {
|
||||||
|
private WasmFunction createWeakReferenceFunction;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void apply(MethodReference method, WasmFunction function, WasmGCCustomGeneratorContext context) {
|
||||||
|
switch (method.getName()) {
|
||||||
|
case "<init>":
|
||||||
|
generateConstructor(context, function);
|
||||||
|
break;
|
||||||
|
case "get":
|
||||||
|
generateDeref(context, function);
|
||||||
|
break;
|
||||||
|
case "clear":
|
||||||
|
generateClear(context, function);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateConstructor(WasmGCCustomGeneratorContext context, WasmFunction function) {
|
||||||
|
var weakRefStruct = context.classInfoProvider().getClassInfo(WeakReference.class.getName()).getStructure();
|
||||||
|
var thisLocal = new WasmLocal(weakRefStruct.getReference(), "this");
|
||||||
|
var valueLocal = new WasmLocal(context.typeMapper().mapType(ValueType.parse(Object.class)), "value");
|
||||||
|
var queueLocal = new WasmLocal(context.typeMapper().mapType(ValueType.parse(ReferenceQueue.class)), "queue");
|
||||||
|
function.add(thisLocal);
|
||||||
|
function.add(valueLocal);
|
||||||
|
function.add(queueLocal);
|
||||||
|
|
||||||
|
var weakRefConstructor = getCreateWeakReferenceFunction(context);
|
||||||
|
var weakRef = new WasmCall(weakRefConstructor, new WasmGetLocal(valueLocal), new WasmGetLocal(thisLocal));
|
||||||
|
function.getBody().add(new WasmStructSet(weakRefStruct, new WasmGetLocal(thisLocal),
|
||||||
|
WasmGCClassInfoProvider.WEAK_REFERENCE_OFFSET, weakRef));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateDeref(WasmGCCustomGeneratorContext context, WasmFunction function) {
|
||||||
|
var weakRefStruct = context.classInfoProvider().getClassInfo(WeakReference.class.getName()).getStructure();
|
||||||
|
var objectType = context.classInfoProvider().getClassInfo("java.lang.Object").getType();
|
||||||
|
var thisLocal = new WasmLocal(weakRefStruct.getReference(), "this");
|
||||||
|
function.add(thisLocal);
|
||||||
|
|
||||||
|
var block = new WasmBlock(false);
|
||||||
|
block.setType(WasmType.Reference.EXTERN);
|
||||||
|
var weakRef = new WasmStructGet(weakRefStruct, new WasmGetLocal(thisLocal),
|
||||||
|
WasmGCClassInfoProvider.WEAK_REFERENCE_OFFSET);
|
||||||
|
var br = new WasmNullBranch(WasmNullCondition.NOT_NULL, weakRef, block);
|
||||||
|
block.getBody().add(br);
|
||||||
|
block.getBody().add(new WasmReturn(new WasmNullConstant(objectType)));
|
||||||
|
|
||||||
|
function.getBody().add(new WasmCall(createDerefFunction(context), block));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void generateClear(WasmGCCustomGeneratorContext context, WasmFunction function) {
|
||||||
|
var weakRefStruct = context.classInfoProvider().getClassInfo(WeakReference.class.getName()).getStructure();
|
||||||
|
var thisLocal = new WasmLocal(weakRefStruct.getReference(), "this");
|
||||||
|
function.add(thisLocal);
|
||||||
|
|
||||||
|
function.getBody().add(new WasmStructSet(weakRefStruct, new WasmGetLocal(thisLocal),
|
||||||
|
WasmGCClassInfoProvider.WEAK_REFERENCE_OFFSET, new WasmNullConstant(WasmType.Reference.EXTERN)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private WasmFunction getCreateWeakReferenceFunction(WasmGCCustomGeneratorContext context) {
|
||||||
|
if (createWeakReferenceFunction == null) {
|
||||||
|
var function = new WasmFunction(context.functionTypes().of(
|
||||||
|
WasmType.Reference.EXTERN,
|
||||||
|
context.typeMapper().mapType(ValueType.parse(Object.class)),
|
||||||
|
context.typeMapper().mapType(ValueType.parse(WeakReference.class))
|
||||||
|
));
|
||||||
|
function.setName(context.names().topLevel("teavm@createWeakReference"));
|
||||||
|
function.setImportName("createWeakRef");
|
||||||
|
function.setImportModule("teavm");
|
||||||
|
context.module().functions.add(function);
|
||||||
|
createWeakReferenceFunction = function;
|
||||||
|
}
|
||||||
|
return createWeakReferenceFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
private WasmFunction createDerefFunction(WasmGCCustomGeneratorContext context) {
|
||||||
|
var function = new WasmFunction(context.functionTypes().of(
|
||||||
|
context.typeMapper().mapType(ValueType.parse(Object.class)),
|
||||||
|
WasmType.Reference.EXTERN));
|
||||||
|
function.setName(context.names().topLevel("teavm@deref"));
|
||||||
|
function.setImportName("deref");
|
||||||
|
function.setImportModule("teavm");
|
||||||
|
context.module().functions.add(function);
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.transformation.gc;
|
package org.teavm.backend.wasm.transformation.gc;
|
||||||
|
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
import org.teavm.interop.Import;
|
import org.teavm.interop.Import;
|
||||||
import org.teavm.model.AccessLevel;
|
import org.teavm.model.AccessLevel;
|
||||||
import org.teavm.model.AnnotationHolder;
|
import org.teavm.model.AnnotationHolder;
|
||||||
|
@ -24,6 +25,7 @@ import org.teavm.model.ClassHolderTransformer;
|
||||||
import org.teavm.model.ClassHolderTransformerContext;
|
import org.teavm.model.ClassHolderTransformerContext;
|
||||||
import org.teavm.model.ElementModifier;
|
import org.teavm.model.ElementModifier;
|
||||||
import org.teavm.model.FieldReference;
|
import org.teavm.model.FieldReference;
|
||||||
|
import org.teavm.model.MethodDescriptor;
|
||||||
import org.teavm.model.MethodHolder;
|
import org.teavm.model.MethodHolder;
|
||||||
import org.teavm.model.MethodReference;
|
import org.teavm.model.MethodReference;
|
||||||
import org.teavm.model.Program;
|
import org.teavm.model.Program;
|
||||||
|
@ -95,6 +97,19 @@ public class BaseClassesTransformation implements ClassHolderTransformer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (cls.getName().equals("java.lang.ref.WeakReference")) {
|
||||||
|
var constructor = cls.getMethod(new MethodDescriptor("<init>", Object.class, ReferenceQueue.class,
|
||||||
|
void.class));
|
||||||
|
constructor.getModifiers().add(ElementModifier.NATIVE);
|
||||||
|
constructor.setProgram(null);
|
||||||
|
|
||||||
|
var get = cls.getMethod(new MethodDescriptor("get", Object.class));
|
||||||
|
get.getModifiers().add(ElementModifier.NATIVE);
|
||||||
|
get.setProgram(null);
|
||||||
|
|
||||||
|
var clear = cls.getMethod(new MethodDescriptor("clear", void.class));
|
||||||
|
clear.getModifiers().add(ElementModifier.NATIVE);
|
||||||
|
clear.setProgram(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,10 +16,15 @@
|
||||||
|
|
||||||
var TeaVM = TeaVM || {};
|
var TeaVM = TeaVM || {};
|
||||||
TeaVM.wasm = function() {
|
TeaVM.wasm = function() {
|
||||||
|
let exports;
|
||||||
function defaults(imports) {
|
function defaults(imports) {
|
||||||
let stderr = "";
|
let stderr = "";
|
||||||
let stdout = "";
|
let stdout = "";
|
||||||
let exports;
|
let finalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||||
|
if (typeof exports.reportGarbageCollectedValue === "function") {
|
||||||
|
exports.reportGarbageCollectedValue(heldValue)
|
||||||
|
}
|
||||||
|
});
|
||||||
imports.teavm = {
|
imports.teavm = {
|
||||||
putcharStderr(c) {
|
putcharStderr(c) {
|
||||||
if (c === 10) {
|
if (c === 10) {
|
||||||
|
@ -40,8 +45,18 @@ TeaVM.wasm = function() {
|
||||||
currentTimeMillis() {
|
currentTimeMillis() {
|
||||||
return new Date().getTime();
|
return new Date().getTime();
|
||||||
},
|
},
|
||||||
dateToString(timestamp, controller) {
|
dateToString(timestamp) {
|
||||||
return stringToJava(new Date(timestamp).toString());
|
return stringToJava(new Date(timestamp).toString());
|
||||||
|
},
|
||||||
|
createWeakRef(value, heldValue) {
|
||||||
|
let weakRef = new WeakRef(value);
|
||||||
|
if (heldValue !== null) {
|
||||||
|
finalizationRegistry.register(value, heldValue)
|
||||||
|
}
|
||||||
|
return weakRef;
|
||||||
|
},
|
||||||
|
deref(weakRef) {
|
||||||
|
return weakRef.deref();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
imports.teavmMath = Math;
|
imports.teavmMath = Math;
|
||||||
|
@ -83,7 +98,6 @@ TeaVM.wasm = function() {
|
||||||
exports = instance.exports;
|
exports = instance.exports;
|
||||||
let javaArgs = exports.createStringArray(args.length);
|
let javaArgs = exports.createStringArray(args.length);
|
||||||
for (let i = 0; i < args.length; ++i) {
|
for (let i = 0; i < args.length; ++i) {
|
||||||
let arg = args[i];
|
|
||||||
exports.setToStringArray(javaArgs, i, stringToJava(args[i]));
|
exports.setToStringArray(javaArgs, i, stringToJava(args[i]));
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -322,7 +322,7 @@ public class StringTest {
|
||||||
private String turkish = "istanbul";
|
private String turkish = "istanbul";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@SkipPlatform({ TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI })
|
@SkipPlatform({ TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI, TestPlatform.WEBASSEMBLY_GC })
|
||||||
public void convertsCaseLocaled() {
|
public void convertsCaseLocaled() {
|
||||||
assertEquals(turkish, "İstanbul".toLowerCase(new Locale("tr", "TR")));
|
assertEquals(turkish, "İstanbul".toLowerCase(new Locale("tr", "TR")));
|
||||||
assertEquals(common, "İstanbul".toLowerCase(Locale.US));
|
assertEquals(common, "İstanbul".toLowerCase(Locale.US));
|
||||||
|
|
|
@ -51,7 +51,7 @@ import org.teavm.junit.TeaVMTestRunner;
|
||||||
import org.teavm.junit.TestPlatform;
|
import org.teavm.junit.TestPlatform;
|
||||||
|
|
||||||
@RunWith(TeaVMTestRunner.class)
|
@RunWith(TeaVMTestRunner.class)
|
||||||
@SkipPlatform({TestPlatform.WEBASSEMBLY, TestPlatform.WASI})
|
@SkipPlatform({TestPlatform.WEBASSEMBLY, TestPlatform.WASI, TestPlatform.WEBASSEMBLY_GC})
|
||||||
public class WeakHashMapTest {
|
public class WeakHashMapTest {
|
||||||
static class MockMap<K, V> extends AbstractMap<K, V> {
|
static class MockMap<K, V> extends AbstractMap<K, V> {
|
||||||
@Override
|
@Override
|
||||||
|
|
Loading…
Reference in New Issue
Block a user