mirror of
https://github.com/Eaglercraft-TeaVM-Fork/eagler-teavm.git
synced 2024-12-22 00:04:10 -08:00
wasm gc: support ReferenceQueue
This commit is contained in:
parent
797ceb9cd7
commit
ce862b9eaa
|
@ -16,6 +16,8 @@
|
|||
package org.teavm.backend.wasm;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
|
@ -55,6 +57,7 @@ import org.teavm.backend.wasm.runtime.StringInternPool;
|
|||
import org.teavm.backend.wasm.transformation.gc.BaseClassesTransformation;
|
||||
import org.teavm.backend.wasm.transformation.gc.ClassLoaderResourceTransformation;
|
||||
import org.teavm.backend.wasm.transformation.gc.EntryPointTransformation;
|
||||
import org.teavm.backend.wasm.transformation.gc.ReferenceQueueTransformation;
|
||||
import org.teavm.dependency.DependencyAnalyzer;
|
||||
import org.teavm.dependency.DependencyListener;
|
||||
import org.teavm.interop.Platforms;
|
||||
|
@ -176,6 +179,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
return List.of(
|
||||
new BaseClassesTransformation(),
|
||||
new ClassLoaderResourceTransformation(),
|
||||
new ReferenceQueueTransformation(),
|
||||
entryPointTransformation
|
||||
);
|
||||
}
|
||||
|
@ -267,6 +271,12 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||
exceptionMessageFunction.setExportName("teavm.exceptionMessage");
|
||||
}
|
||||
|
||||
var refQueueSupplyRef = new MethodReference(ReferenceQueue.class, "supply", Reference.class, void.class);
|
||||
if (controller.getDependencyInfo().getMethod(refQueueSupplyRef) != null) {
|
||||
var refQueueSupplyFunction = declarationsGenerator.functions().forInstanceMethod(refQueueSupplyRef);
|
||||
refQueueSupplyFunction.setExportName("teavm.reportGarbageCollectedValue");
|
||||
}
|
||||
|
||||
moduleGenerator.generate();
|
||||
customGenerators.contributeToModule(module);
|
||||
adjustModuleMemory(module);
|
||||
|
|
|
@ -15,13 +15,19 @@
|
|||
*/
|
||||
package org.teavm.backend.wasm.gc;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import org.teavm.dependency.AbstractDependencyListener;
|
||||
import org.teavm.dependency.DependencyAgent;
|
||||
import org.teavm.dependency.DependencyNode;
|
||||
import org.teavm.dependency.MethodDependency;
|
||||
import org.teavm.model.MethodReference;
|
||||
|
||||
public class WasmGCReferenceQueueDependency extends AbstractDependencyListener {
|
||||
private DependencyNode valueNode;
|
||||
private boolean refQueuePassedToRef;
|
||||
private boolean refQueuePoll;
|
||||
|
||||
@Override
|
||||
public void started(DependencyAgent agent) {
|
||||
|
@ -33,12 +39,30 @@ public class WasmGCReferenceQueueDependency extends AbstractDependencyListener {
|
|||
if (method.getMethod().getOwnerName().equals("java.lang.ref.WeakReference")) {
|
||||
switch (method.getMethod().getName()) {
|
||||
case "<init>":
|
||||
if (method.getMethod().parameterCount() == 2) {
|
||||
refQueuePassedToRef = true;
|
||||
checkRefQueue(agent);
|
||||
}
|
||||
method.getVariable(1).connect(valueNode);
|
||||
break;
|
||||
case "get":
|
||||
valueNode.connect(method.getResult());
|
||||
break;
|
||||
}
|
||||
} else if (method.getMethod().getOwnerName().equals(ReferenceQueue.class.getName())) {
|
||||
if (method.getMethod().getName().equals("poll")) {
|
||||
refQueuePoll = true;
|
||||
checkRefQueue(agent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkRefQueue(DependencyAgent agent) {
|
||||
if (refQueuePassedToRef && refQueuePoll) {
|
||||
agent.linkMethod(new MethodReference(ReferenceQueue.class, "supply", Reference.class, void.class))
|
||||
.propagate(0, ReferenceQueue.class)
|
||||
.propagate(1, WeakReference.class)
|
||||
.use();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -61,7 +61,8 @@ public class WeakReferenceGenerator implements WasmGCCustomGenerator {
|
|||
function.add(queueLocal);
|
||||
|
||||
var weakRefConstructor = getCreateWeakReferenceFunction(context);
|
||||
var weakRef = new WasmCall(weakRefConstructor, new WasmGetLocal(valueLocal), new WasmGetLocal(thisLocal));
|
||||
var weakRef = new WasmCall(weakRefConstructor, new WasmGetLocal(valueLocal), new WasmGetLocal(thisLocal),
|
||||
new WasmGetLocal(queueLocal));
|
||||
function.getBody().add(new WasmStructSet(weakRefStruct, new WasmGetLocal(thisLocal),
|
||||
WasmGCClassInfoProvider.WEAK_REFERENCE_OFFSET, weakRef));
|
||||
}
|
||||
|
@ -97,7 +98,8 @@ public class WeakReferenceGenerator implements WasmGCCustomGenerator {
|
|||
var function = new WasmFunction(context.functionTypes().of(
|
||||
WasmType.Reference.EXTERN,
|
||||
context.typeMapper().mapType(ValueType.parse(Object.class)),
|
||||
context.typeMapper().mapType(ValueType.parse(WeakReference.class))
|
||||
context.typeMapper().mapType(ValueType.parse(WeakReference.class)),
|
||||
context.typeMapper().mapType(ValueType.parse(ReferenceQueue.class))
|
||||
));
|
||||
function.setName(context.names().topLevel("teavm@createWeakReference"));
|
||||
function.setImportName("createWeakRef");
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.transformation.gc;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
|
||||
class ReferenceQueueEntry<T> {
|
||||
final Reference<T> reference;
|
||||
ReferenceQueueEntry<T> next;
|
||||
|
||||
ReferenceQueueEntry(Reference<T> reference) {
|
||||
this.reference = reference;
|
||||
}
|
||||
}
|
|
@ -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.transformation.gc;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
|
||||
class ReferenceQueueTemplate<T> {
|
||||
private ReferenceQueueEntry<T> start;
|
||||
private ReferenceQueueEntry<T> end;
|
||||
|
||||
public Reference<T> poll() {
|
||||
var result = start;
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
start = result.next;
|
||||
if (start == null) {
|
||||
end = null;
|
||||
}
|
||||
return result.reference;
|
||||
}
|
||||
|
||||
public void supply(Reference<T> reference) {
|
||||
var entry = new ReferenceQueueEntry<>(reference);
|
||||
if (start == null) {
|
||||
start = entry;
|
||||
} else {
|
||||
end.next = entry;
|
||||
}
|
||||
end = entry;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* 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.transformation.gc;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import org.teavm.model.ClassHolder;
|
||||
import org.teavm.model.ClassHolderTransformer;
|
||||
import org.teavm.model.ClassHolderTransformerContext;
|
||||
import org.teavm.model.FieldReference;
|
||||
import org.teavm.model.MethodHolder;
|
||||
import org.teavm.model.MethodReader;
|
||||
import org.teavm.model.instructions.GetFieldInstruction;
|
||||
import org.teavm.model.instructions.PutFieldInstruction;
|
||||
import org.teavm.model.util.ModelUtils;
|
||||
import org.teavm.model.util.ProgramUtils;
|
||||
|
||||
public class ReferenceQueueTransformation implements ClassHolderTransformer {
|
||||
@Override
|
||||
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
if (cls.getName().equals(ReferenceQueue.class.getName())) {
|
||||
transformReferenceQueue(cls, context);
|
||||
}
|
||||
}
|
||||
|
||||
private void transformReferenceQueue(ClassHolder cls, ClassHolderTransformerContext context) {
|
||||
var templateClass = context.getHierarchy().getClassSource().get(ReferenceQueueTemplate.class.getName());
|
||||
for (var method : templateClass.getMethods()) {
|
||||
if (!method.getName().equals("<init>")) {
|
||||
copyMethod(cls, method);
|
||||
}
|
||||
}
|
||||
for (var field : templateClass.getFields()) {
|
||||
cls.addField(ModelUtils.copyField(field));
|
||||
}
|
||||
}
|
||||
|
||||
private void copyMethod(ClassHolder cls, MethodReader method) {
|
||||
var targetMethod = cls.getMethod(method.getDescriptor());
|
||||
if (targetMethod == null) {
|
||||
targetMethod = new MethodHolder(method.getDescriptor());
|
||||
cls.addMethod(targetMethod);
|
||||
targetMethod.getModifiers().addAll(method.readModifiers());
|
||||
targetMethod.setLevel(method.getLevel());
|
||||
}
|
||||
|
||||
var targetProgram = ProgramUtils.copy(method.getProgram());
|
||||
targetMethod.setProgram(targetProgram);
|
||||
for (var block : targetProgram.getBasicBlocks()) {
|
||||
for (var instruction : block) {
|
||||
if (instruction instanceof GetFieldInstruction) {
|
||||
var getField = (GetFieldInstruction) instruction;
|
||||
getField.setField(mapField(getField.getField()));
|
||||
} else if (instruction instanceof PutFieldInstruction) {
|
||||
var putField = (PutFieldInstruction) instruction;
|
||||
putField.setField(mapField(putField.getField()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private FieldReference mapField(FieldReference field) {
|
||||
if (field.getClassName().equals(ReferenceQueueTemplate.class.getName())) {
|
||||
return new FieldReference(ReferenceQueue.class.getName(), field.getFieldName());
|
||||
}
|
||||
return field;
|
||||
}
|
||||
}
|
|
@ -124,8 +124,8 @@ function consoleImports(imports) {
|
|||
function coreImports(imports, context) {
|
||||
let finalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||
let report = context.exports["teavm.reportGarbageCollectedValue"];
|
||||
if (typeof report === "function") {
|
||||
report(heldValue)
|
||||
if (typeof report !== "undefined") {
|
||||
report(heldValue.queue, heldValue.ref);
|
||||
}
|
||||
});
|
||||
let stringFinalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||
|
@ -135,18 +135,16 @@ function coreImports(imports, context) {
|
|||
}
|
||||
});
|
||||
imports.teavm = {
|
||||
createWeakRef(value, heldValue) {
|
||||
let weakRef = new WeakRef(value);
|
||||
if (heldValue !== null) {
|
||||
finalizationRegistry.register(value, heldValue)
|
||||
createWeakRef(value, ref, queue) {
|
||||
if (queue !== null) {
|
||||
finalizationRegistry.register(value, { ref: ref, queue: queue });
|
||||
}
|
||||
return weakRef;
|
||||
return new WeakRef(value);
|
||||
},
|
||||
deref: weakRef => weakRef.deref(),
|
||||
createStringWeakRef(value, heldValue) {
|
||||
let weakRef = new WeakRef(value);
|
||||
stringFinalizationRegistry.register(value, heldValue)
|
||||
return weakRef;
|
||||
return new WeakRef(value);
|
||||
},
|
||||
stringDeref: weakRef => weakRef.deref(),
|
||||
takeStackTrace() {
|
||||
|
|
Loading…
Reference in New Issue
Block a user