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();
|
||||
contributeExceptionUtils();
|
||||
contributeInitializerUtils();
|
||||
analyzer.addDependencyListener(new WasmGCReferenceQueueDependency());
|
||||
}
|
||||
|
||||
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,20 +1118,25 @@ public class WasmGCClassGenerator implements WasmGCClassInfoProvider, WasmGCInit
|
|||
if (classReader.getParent() != null) {
|
||||
fillClassFields(fields, classReader.getParent());
|
||||
}
|
||||
for (var field : classReader.getFields()) {
|
||||
if (className.equals("java.lang.Object") && field.getName().equals("monitor")) {
|
||||
continue;
|
||||
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()) {
|
||||
if (className.equals("java.lang.Object") && field.getName().equals("monitor")) {
|
||||
continue;
|
||||
}
|
||||
if (className.equals("java.lang.Class") && field.getName().equals("platformClass")) {
|
||||
continue;
|
||||
}
|
||||
if (field.hasModifier(ElementModifier.STATIC)) {
|
||||
continue;
|
||||
}
|
||||
fieldIndexes.putIfAbsent(field.getReference(), fields.size());
|
||||
var wasmField = new WasmField(typeMapper.mapStorageType(field.getType()),
|
||||
names.forMemberField(field.getReference()));
|
||||
fields.add(wasmField);
|
||||
}
|
||||
if (className.equals("java.lang.Class") && field.getName().equals("platformClass")) {
|
||||
continue;
|
||||
}
|
||||
if (field.hasModifier(ElementModifier.STATIC)) {
|
||||
continue;
|
||||
}
|
||||
fieldIndexes.putIfAbsent(field.getReference(), fields.size());
|
||||
var wasmField = new WasmField(typeMapper.mapStorageType(field.getType()),
|
||||
names.forMemberField(field.getReference()));
|
||||
fields.add(wasmField);
|
||||
}
|
||||
if (className.equals("java.lang.Class")) {
|
||||
var cls = classSource.get("java.lang.Class");
|
||||
|
|
|
@ -26,6 +26,7 @@ public interface WasmGCClassInfoProvider {
|
|||
int MONITOR_FIELD_OFFSET = 1;
|
||||
int CUSTOM_FIELD_OFFSETS = 2;
|
||||
int ARRAY_DATA_FIELD_OFFSET = 2;
|
||||
int WEAK_REFERENCE_OFFSET = 2;
|
||||
|
||||
WasmGCClassInfo getClassInfo(ValueType type);
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package org.teavm.backend.wasm.generators.gc;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Array;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -41,6 +43,7 @@ public class WasmGCCustomGenerators implements WasmGCCustomGeneratorProvider {
|
|||
fillStringPool();
|
||||
fillSystem();
|
||||
fillArray();
|
||||
fillWeakReference();
|
||||
for (var entry : generators.entrySet()) {
|
||||
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);
|
||||
}
|
||||
|
||||
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
|
||||
public WasmGCCustomGenerator get(MethodReference 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;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import org.teavm.interop.Import;
|
||||
import org.teavm.model.AccessLevel;
|
||||
import org.teavm.model.AnnotationHolder;
|
||||
|
@ -24,6 +25,7 @@ import org.teavm.model.ClassHolderTransformer;
|
|||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.ElementModifier;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodDescriptor;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReference;
|
||||
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 || {};
|
||||
TeaVM.wasm = function() {
|
||||
let exports;
|
||||
function defaults(imports) {
|
||||
let stderr = "";
|
||||
let stdout = "";
|
||||
let exports;
|
||||
let finalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||
if (typeof exports.reportGarbageCollectedValue === "function") {
|
||||
exports.reportGarbageCollectedValue(heldValue)
|
||||
}
|
||||
});
|
||||
imports.teavm = {
|
||||
putcharStderr(c) {
|
||||
if (c === 10) {
|
||||
|
@ -40,8 +45,18 @@ TeaVM.wasm = function() {
|
|||
currentTimeMillis() {
|
||||
return new Date().getTime();
|
||||
},
|
||||
dateToString(timestamp, controller) {
|
||||
dateToString(timestamp) {
|
||||
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;
|
||||
|
@ -83,7 +98,6 @@ TeaVM.wasm = function() {
|
|||
exports = instance.exports;
|
||||
let javaArgs = exports.createStringArray(args.length);
|
||||
for (let i = 0; i < args.length; ++i) {
|
||||
let arg = args[i];
|
||||
exports.setToStringArray(javaArgs, i, stringToJava(args[i]));
|
||||
}
|
||||
try {
|
||||
|
|
|
@ -322,7 +322,7 @@ public class StringTest {
|
|||
private String turkish = "istanbul";
|
||||
|
||||
@Test
|
||||
@SkipPlatform({ TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI })
|
||||
@SkipPlatform({ TestPlatform.C, TestPlatform.WEBASSEMBLY, TestPlatform.WASI, TestPlatform.WEBASSEMBLY_GC })
|
||||
public void convertsCaseLocaled() {
|
||||
assertEquals(turkish, "İstanbul".toLowerCase(new Locale("tr", "TR")));
|
||||
assertEquals(common, "İstanbul".toLowerCase(Locale.US));
|
||||
|
|
|
@ -51,7 +51,7 @@ import org.teavm.junit.TeaVMTestRunner;
|
|||
import org.teavm.junit.TestPlatform;
|
||||
|
||||
@RunWith(TeaVMTestRunner.class)
|
||||
@SkipPlatform({TestPlatform.WEBASSEMBLY, TestPlatform.WASI})
|
||||
@SkipPlatform({TestPlatform.WEBASSEMBLY, TestPlatform.WASI, TestPlatform.WEBASSEMBLY_GC})
|
||||
public class WeakHashMapTest {
|
||||
static class MockMap<K, V> extends AbstractMap<K, V> {
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue
Block a user