mirror of
https://github.com/konsoletyper/teavm.git
synced 2024-11-27 01:30:35 +08:00
wasm gc: support ReferenceQueue
This commit is contained in:
parent
797ceb9cd7
commit
ce862b9eaa
@ -16,6 +16,8 @@
|
|||||||
package org.teavm.backend.wasm;
|
package org.teavm.backend.wasm;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.lang.ref.Reference;
|
||||||
|
import java.lang.ref.ReferenceQueue;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
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.BaseClassesTransformation;
|
||||||
import org.teavm.backend.wasm.transformation.gc.ClassLoaderResourceTransformation;
|
import org.teavm.backend.wasm.transformation.gc.ClassLoaderResourceTransformation;
|
||||||
import org.teavm.backend.wasm.transformation.gc.EntryPointTransformation;
|
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.DependencyAnalyzer;
|
||||||
import org.teavm.dependency.DependencyListener;
|
import org.teavm.dependency.DependencyListener;
|
||||||
import org.teavm.interop.Platforms;
|
import org.teavm.interop.Platforms;
|
||||||
@ -176,6 +179,7 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||||||
return List.of(
|
return List.of(
|
||||||
new BaseClassesTransformation(),
|
new BaseClassesTransformation(),
|
||||||
new ClassLoaderResourceTransformation(),
|
new ClassLoaderResourceTransformation(),
|
||||||
|
new ReferenceQueueTransformation(),
|
||||||
entryPointTransformation
|
entryPointTransformation
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -267,6 +271,12 @@ public class WasmGCTarget implements TeaVMTarget, TeaVMWasmGCHost {
|
|||||||
exceptionMessageFunction.setExportName("teavm.exceptionMessage");
|
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();
|
moduleGenerator.generate();
|
||||||
customGenerators.contributeToModule(module);
|
customGenerators.contributeToModule(module);
|
||||||
adjustModuleMemory(module);
|
adjustModuleMemory(module);
|
||||||
|
@ -15,13 +15,19 @@
|
|||||||
*/
|
*/
|
||||||
package org.teavm.backend.wasm.gc;
|
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.AbstractDependencyListener;
|
||||||
import org.teavm.dependency.DependencyAgent;
|
import org.teavm.dependency.DependencyAgent;
|
||||||
import org.teavm.dependency.DependencyNode;
|
import org.teavm.dependency.DependencyNode;
|
||||||
import org.teavm.dependency.MethodDependency;
|
import org.teavm.dependency.MethodDependency;
|
||||||
|
import org.teavm.model.MethodReference;
|
||||||
|
|
||||||
public class WasmGCReferenceQueueDependency extends AbstractDependencyListener {
|
public class WasmGCReferenceQueueDependency extends AbstractDependencyListener {
|
||||||
private DependencyNode valueNode;
|
private DependencyNode valueNode;
|
||||||
|
private boolean refQueuePassedToRef;
|
||||||
|
private boolean refQueuePoll;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void started(DependencyAgent agent) {
|
public void started(DependencyAgent agent) {
|
||||||
@ -33,12 +39,30 @@ public class WasmGCReferenceQueueDependency extends AbstractDependencyListener {
|
|||||||
if (method.getMethod().getOwnerName().equals("java.lang.ref.WeakReference")) {
|
if (method.getMethod().getOwnerName().equals("java.lang.ref.WeakReference")) {
|
||||||
switch (method.getMethod().getName()) {
|
switch (method.getMethod().getName()) {
|
||||||
case "<init>":
|
case "<init>":
|
||||||
|
if (method.getMethod().parameterCount() == 2) {
|
||||||
|
refQueuePassedToRef = true;
|
||||||
|
checkRefQueue(agent);
|
||||||
|
}
|
||||||
method.getVariable(1).connect(valueNode);
|
method.getVariable(1).connect(valueNode);
|
||||||
break;
|
break;
|
||||||
case "get":
|
case "get":
|
||||||
valueNode.connect(method.getResult());
|
valueNode.connect(method.getResult());
|
||||||
break;
|
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);
|
function.add(queueLocal);
|
||||||
|
|
||||||
var weakRefConstructor = getCreateWeakReferenceFunction(context);
|
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),
|
function.getBody().add(new WasmStructSet(weakRefStruct, new WasmGetLocal(thisLocal),
|
||||||
WasmGCClassInfoProvider.WEAK_REFERENCE_OFFSET, weakRef));
|
WasmGCClassInfoProvider.WEAK_REFERENCE_OFFSET, weakRef));
|
||||||
}
|
}
|
||||||
@ -97,7 +98,8 @@ public class WeakReferenceGenerator implements WasmGCCustomGenerator {
|
|||||||
var function = new WasmFunction(context.functionTypes().of(
|
var function = new WasmFunction(context.functionTypes().of(
|
||||||
WasmType.Reference.EXTERN,
|
WasmType.Reference.EXTERN,
|
||||||
context.typeMapper().mapType(ValueType.parse(Object.class)),
|
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.setName(context.names().topLevel("teavm@createWeakReference"));
|
||||||
function.setImportName("createWeakRef");
|
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) {
|
function coreImports(imports, context) {
|
||||||
let finalizationRegistry = new FinalizationRegistry(heldValue => {
|
let finalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||||
let report = context.exports["teavm.reportGarbageCollectedValue"];
|
let report = context.exports["teavm.reportGarbageCollectedValue"];
|
||||||
if (typeof report === "function") {
|
if (typeof report !== "undefined") {
|
||||||
report(heldValue)
|
report(heldValue.queue, heldValue.ref);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
let stringFinalizationRegistry = new FinalizationRegistry(heldValue => {
|
let stringFinalizationRegistry = new FinalizationRegistry(heldValue => {
|
||||||
@ -135,18 +135,16 @@ function coreImports(imports, context) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
imports.teavm = {
|
imports.teavm = {
|
||||||
createWeakRef(value, heldValue) {
|
createWeakRef(value, ref, queue) {
|
||||||
let weakRef = new WeakRef(value);
|
if (queue !== null) {
|
||||||
if (heldValue !== null) {
|
finalizationRegistry.register(value, { ref: ref, queue: queue });
|
||||||
finalizationRegistry.register(value, heldValue)
|
|
||||||
}
|
}
|
||||||
return weakRef;
|
return new WeakRef(value);
|
||||||
},
|
},
|
||||||
deref: weakRef => weakRef.deref(),
|
deref: weakRef => weakRef.deref(),
|
||||||
createStringWeakRef(value, heldValue) {
|
createStringWeakRef(value, heldValue) {
|
||||||
let weakRef = new WeakRef(value);
|
|
||||||
stringFinalizationRegistry.register(value, heldValue)
|
stringFinalizationRegistry.register(value, heldValue)
|
||||||
return weakRef;
|
return new WeakRef(value);
|
||||||
},
|
},
|
||||||
stringDeref: weakRef => weakRef.deref(),
|
stringDeref: weakRef => weakRef.deref(),
|
||||||
takeStackTrace() {
|
takeStackTrace() {
|
||||||
|
Loading…
Reference in New Issue
Block a user