C: support weak references

This commit is contained in:
Alexey Andreev 2019-05-17 17:40:45 +03:00
parent 4c50ed8714
commit 2eafb902f4
16 changed files with 533 additions and 47 deletions

View File

@ -17,11 +17,6 @@ package org.teavm.classlib.java.lang.ref;
import org.teavm.classlib.java.lang.TObject;
/**
*
* @author Alexey Andreev
* @param <T> type of an object to which this reference points.
*/
public abstract class TReference<T> extends TObject {
public T get() {
return null;

View File

@ -15,11 +15,6 @@
*/
package org.teavm.classlib.java.lang.ref;
/**
*
* @author Alexey Andreev
* @param <T>
*/
public class TReferenceQueue<T> {
public TReference<T> poll() {
return null;

View File

@ -35,4 +35,14 @@ public class TWeakReference<T> extends TReference<T> {
public void clear() {
value = null;
}
@Override
public boolean isEnqueued() {
return false;
}
@Override
public boolean enqueue() {
return true;
}
}

View File

@ -51,6 +51,8 @@ import org.teavm.backend.c.generate.StringPoolGenerator;
import org.teavm.backend.c.generators.ArrayGenerator;
import org.teavm.backend.c.generators.Generator;
import org.teavm.backend.c.generators.GeneratorFactory;
import org.teavm.backend.c.generators.ReferenceQueueGenerator;
import org.teavm.backend.c.generators.WeakReferenceGenerator;
import org.teavm.backend.c.intrinsic.AddressIntrinsic;
import org.teavm.backend.c.intrinsic.AllocatorIntrinsic;
import org.teavm.backend.c.intrinsic.ConsoleIntrinsic;
@ -72,7 +74,9 @@ import org.teavm.backend.c.intrinsic.ShadowStackIntrinsic;
import org.teavm.backend.c.intrinsic.StringsIntrinsic;
import org.teavm.backend.c.intrinsic.StructureIntrinsic;
import org.teavm.backend.lowlevel.dependency.ExceptionHandlingDependencyListener;
import org.teavm.backend.lowlevel.dependency.WeakReferenceDependencyListener;
import org.teavm.backend.lowlevel.transform.CoroutineTransformation;
import org.teavm.backend.lowlevel.transform.WeakReferenceTransformation;
import org.teavm.dependency.ClassDependency;
import org.teavm.dependency.DependencyAnalyzer;
import org.teavm.dependency.DependencyListener;
@ -163,12 +167,14 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
List<ClassHolderTransformer> transformers = new ArrayList<>();
transformers.add(new ClassPatch());
transformers.add(new CDependencyListener());
transformers.add(new WeakReferenceTransformation());
return transformers;
}
@Override
public List<DependencyListener> getDependencyListeners() {
return Arrays.asList(new CDependencyListener(), exportDependencyListener, new InteropDependencyListener());
return Arrays.asList(new CDependencyListener(), exportDependencyListener, new InteropDependencyListener(),
new WeakReferenceDependencyListener());
}
@Override
@ -323,6 +329,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
List<Generator> generators = new ArrayList<>();
generators.add(new ArrayGenerator());
generators.add(new WeakReferenceGenerator());
generators.add(new ReferenceQueueGenerator());
stringPool = new SimpleStringPool();
GenerationContext context = new GenerationContext(vtableProvider, characteristics,
@ -355,10 +363,8 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
generateSpecialFunctions(context, runtimeWriter);
OutputFileUtil.write(runtimeWriter, "runtime.c", buildTarget);
OutputFileUtil.write(runtimeHeaderWriter, "runtime.h", buildTarget);
BufferedCodeWriter stringhashWriter = new BufferedCodeWriter(false);
emitResource(stringhashWriter, "stringhash.c");
OutputFileUtil.write(stringhashWriter, "stringhash.c", buildTarget);
copyResource("stringhash.c", buildTarget);
copyResource("references.c", buildTarget);
generateCallSites(buildTarget, context, classes.getClassNames());
generateStrings(buildTarget, context);
@ -369,6 +375,12 @@ public class CTarget implements TeaVMTarget, TeaVMCHost {
generateAllFile(classes, types, buildTarget);
}
private void copyResource(String name, BuildTarget buildTarget) throws IOException {
BufferedCodeWriter writer = new BufferedCodeWriter(false);
emitResource(writer, name);
OutputFileUtil.write(writer, name, buildTarget);
}
private void emitResource(CodeWriter writer, String resourceName) {
ClassLoader classLoader = CTarget.class.getClassLoader();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(

View File

@ -16,6 +16,8 @@
package org.teavm.backend.c.generate;
import java.io.IOException;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@ -64,11 +66,15 @@ import org.teavm.runtime.CallSite;
import org.teavm.runtime.RuntimeArray;
import org.teavm.runtime.RuntimeClass;
import org.teavm.runtime.RuntimeObject;
import org.teavm.runtime.RuntimeReference;
import org.teavm.runtime.RuntimeReferenceQueue;
public class ClassGenerator {
private static final Set<String> classesWithDeclaredStructures = new HashSet<>(Arrays.asList(
"java.lang.Object", "java.lang.String", "java.lang.Class",
RuntimeArray.class.getName(), RuntimeClass.class.getName(), RuntimeObject.class.getName()
RuntimeArray.class.getName(), RuntimeClass.class.getName(), RuntimeObject.class.getName(),
WeakReference.class.getName(), ReferenceQueue.class.getName(),
RuntimeReferenceQueue.class.getName(), RuntimeReference.class.getName()
));
private GenerationContext context;
@ -566,6 +572,15 @@ public class ClassGenerator {
superinterfaces = sb.append(" }").toString();
}
switch (className) {
case "java.lang.ref.WeakReference":
flags |= RuntimeClass.VM_TYPE_WEAKREFERENCE << RuntimeClass.VM_TYPE_SHIFT;
break;
case "java.lang.ref.ReferenceQueue":
flags |= RuntimeClass.VM_TYPE_REFERENCEQUEUE << RuntimeClass.VM_TYPE_SHIFT;
break;
}
} else if (type instanceof ValueType.Array) {
includes.includeClass("java.lang.Object");
parent = "(TeaVM_Class*) &" + context.getNames().forClassInstance(ValueType.object("java.lang.Object"));

View File

@ -15,6 +15,8 @@
*/
package org.teavm.backend.c.generate;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
@ -25,6 +27,8 @@ import org.teavm.model.FieldReference;
import org.teavm.runtime.RuntimeArray;
import org.teavm.runtime.RuntimeClass;
import org.teavm.runtime.RuntimeObject;
import org.teavm.runtime.RuntimeReference;
import org.teavm.runtime.RuntimeReferenceQueue;
public class NameProvider extends LowLevelNameProvider {
private static final Set<? extends String> keywords = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
@ -41,12 +45,18 @@ public class NameProvider extends LowLevelNameProvider {
occupiedTopLevelNames.add("TeaVM_Array");
occupiedTopLevelNames.add("TeaVM_String");
occupiedTopLevelNames.add("TeaVM_Class");
occupiedTopLevelNames.add("TeaVM_Reference");
occupiedTopLevelNames.add("TeaVM_ReferenceQueue");
classNames.put(RuntimeObject.class.getName(), "TeaVM_Object");
classNames.put(Object.class.getName(), "TeaVM_Object");
classNames.put(String.class.getName(), "TeaVM_String");
classNames.put(RuntimeClass.class.getName(), "TeaVM_Class");
classNames.put(RuntimeArray.class.getName(), "TeaVM_Array");
classNames.put(WeakReference.class.getName(), "TeaVM_Reference");
classNames.put(ReferenceQueue.class.getName(), "TeaVM_ReferenceQueue");
classNames.put(RuntimeReference.class.getName(), "TeaVM_Reference");
classNames.put(RuntimeReferenceQueue.class.getName(), "TeaVM_ReferenceQueue");
memberFieldNames.put(new FieldReference(RuntimeObject.class.getName(), "classReference"), "header");
memberFieldNames.put(new FieldReference(RuntimeObject.class.getName(), "hashCode"), "hash");
@ -54,17 +64,23 @@ public class NameProvider extends LowLevelNameProvider {
memberFieldNames.put(new FieldReference(String.class.getName(), "characters"), "characters");
memberFieldNames.put(new FieldReference(String.class.getName(), "hashCode"), "hashCode");
for (String name : new String[] { "size", "flags", "tag", "canary", "name", "itemType", "arrayType",
"isSupertypeOf", "init", "enumValues", "layout", "simpleName", "superinterfaceCount",
"superinterfaces" }) {
memberFieldNames.put(new FieldReference(RuntimeClass.class.getName(), name), name);
}
preserveFieldNames(RuntimeClass.class.getName(), "size", "flags", "tag", "canary", "name", "itemType",
"arrayType", "isSupertypeOf", "init", "enumValues", "layout", "simpleName", "superinterfaceCount",
"superinterfaces");
memberFieldNames.put(new FieldReference(RuntimeClass.class.getName(), "parent"), "superclass");
preserveFieldNames(RuntimeReference.class.getName(), "queue", "object", "next");
preserveFieldNames(RuntimeReferenceQueue.class.getName(), "first", "last");
occupiedClassNames.put(RuntimeObject.class.getName(), new HashSet<>(Arrays.asList("header")));
occupiedClassNames.put(RuntimeArray.class.getName(), new HashSet<>(Arrays.asList("length")));
}
private void preserveFieldNames(String className, String... fieldNames) {
for (String name : fieldNames) {
memberFieldNames.put(new FieldReference(className, name), name);
}
}
@Override
protected Set<? extends String> getKeywords() {
return keywords;

View File

@ -0,0 +1,47 @@
/*
* Copyright 2019 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.c.generators;
import java.lang.ref.ReferenceQueue;
import org.teavm.model.MethodReference;
public class ReferenceQueueGenerator implements Generator {
private static final MethodReference OBJECT_INIT = new MethodReference(
Object.class, "<init>", void.class);
@Override
public boolean canHandle(MethodReference method) {
return method.getClassName().equals(ReferenceQueue.class.getName());
}
@Override
public void generate(GeneratorContext context, MethodReference method) {
switch (method.getName()) {
case "<init>":
context.includes().includeClass(OBJECT_INIT.getClassName());
context.writer().print(context.names().forMethod(OBJECT_INIT)).print("(")
.print(context.parameterName(0))
.println(");");
break;
case "poll":
context.writer().print("return teavm_reference_poll(");
context.writer().print("(TeaVM_ReferenceQueue*) ").print(context.parameterName(0));
context.writer().println(");");
break;
}
}
}

View File

@ -0,0 +1,74 @@
/*
* Copyright 2019 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.c.generators;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import org.teavm.model.MethodReference;
public class WeakReferenceGenerator implements Generator {
private static final MethodReference REFERENCE_INIT = new MethodReference(Reference.class, "<init>", void.class);
@Override
public boolean canHandle(MethodReference method) {
return method.getClassName().equals(WeakReference.class.getName());
}
@Override
public void generate(GeneratorContext context, MethodReference method) {
switch (method.getName()) {
case "<init>":
context.includes().includeClass(REFERENCE_INIT.getClassName());
context.writer().print(context.names().forMethod(REFERENCE_INIT)).print("(")
.print(context.parameterName(0))
.println(");");
context.writer().print("teavm_reference_init(");
context.writer().print("(TeaVM_Reference*) ").print(context.parameterName(0)).print(", ");
context.writer().print(context.parameterName(1)).print(", ");
if (method.parameterCount() == 2) {
context.writer().print(context.parameterName(2));
} else {
context.writer().print("NULL");
}
context.writer().println(");");
break;
case "get":
context.writer().print("return teavm_reference_get(");
context.writer().print("(TeaVM_Reference*) ").print(context.parameterName(0));
context.writer().println(");");
break;
case "clear":
context.writer().print("teavm_reference_clear(");
context.writer().print("(TeaVM_Reference*) ").print(context.parameterName(0));
context.writer().println(");");
break;
case "isEnqueued":
context.writer().print("return teavm_reference_isEnqueued(");
context.writer().print("(TeaVM_Reference*) ").print(context.parameterName(0));
context.writer().println(");");
break;
case "enqueue":
context.writer().print("return teavm_reference_enqueue(");
context.writer().print("(TeaVM_Reference*) ").print(context.parameterName(0));
context.writer().println(");");
break;
}
}
}

View File

@ -0,0 +1,84 @@
/*
* Copyright 2019 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.lowlevel.dependency;
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 WeakReferenceDependencyListener extends AbstractDependencyListener {
private DependencyNode referentNode;
private DependencyNode referenceNode;
@Override
public void started(DependencyAgent agent) {
referentNode = agent.createNode();
referenceNode = agent.createNode();
}
@Override
public void methodReached(DependencyAgent agent, MethodDependency method) {
MethodReference methodRef = method.getReference();
if (methodRef.getClassName().equals(WeakReference.class.getName())) {
switch (methodRef.getName()) {
case "<init>":
reachReferenceInit(agent, method);
break;
case "get":
reachReferenceGet(method);
break;
}
} else if (methodRef.getClassName().equals(ReferenceQueue.class.getName())) {
switch (methodRef.getName()) {
case "<init>":
reachQueueInit(agent, method);
break;
case "poll":
reachQueuePoll(method);
break;
}
}
super.methodReached(agent, method);
}
private void reachReferenceInit(DependencyAgent agent, MethodDependency method) {
MethodDependency superMethod = agent.linkMethod(new MethodReference(Reference.class, "<init>", void.class));
method.getVariable(0).connect(superMethod.getVariable(0));
superMethod.use();
method.getVariable(0).connect(referenceNode);
method.getVariable(1).connect(referentNode);
}
private void reachReferenceGet(MethodDependency method) {
referentNode.connect(method.getResult());
}
private void reachQueuePoll(MethodDependency method) {
referenceNode.connect(method.getResult());
}
private void reachQueueInit(DependencyAgent agent, MethodDependency method) {
MethodDependency superMethod = agent.linkMethod(new MethodReference(Object.class, "<init>", void.class));
method.getVariable(0).connect(superMethod.getVariable(0));
superMethod.use();
}
}

View File

@ -0,0 +1,44 @@
/*
* Copyright 2019 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.lowlevel.transform;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import org.teavm.model.ClassHolder;
import org.teavm.model.ClassHolderTransformer;
import org.teavm.model.ClassHolderTransformerContext;
import org.teavm.model.ElementModifier;
import org.teavm.model.FieldHolder;
import org.teavm.model.MethodHolder;
public class WeakReferenceTransformation implements ClassHolderTransformer {
@Override
public void transformClass(ClassHolder cls, ClassHolderTransformerContext context) {
if (!cls.getName().equals(WeakReference.class.getName())
&& !cls.getName().equals(ReferenceQueue.class.getName())) {
return;
}
for (FieldHolder field : cls.getFields().toArray(new FieldHolder[0])) {
cls.removeField(field);
}
for (MethodHolder method : cls.getMethods()) {
method.setProgram(null);
method.getModifiers().add(ElementModifier.NATIVE);
}
}
}

View File

@ -32,6 +32,7 @@ public final class GC {
static FreeChunkHolder currentChunkPointer;
static int freeChunks;
static int freeMemory = (int) availableBytes();
static RuntimeReference firstWeakReference;
static native Address gcStorageAddress();
@ -122,12 +123,14 @@ public final class GC {
public static boolean collectGarbage(int size) {
mark();
processReferences();
sweep();
updateFreeMemory();
return true;
}
private static void mark() {
firstWeakReference = null;
Allocator.fillZero(regionsAddress().toAddress(), regionMaxCount() * Structure.sizeOf(Region.class));
Address staticRoots = Mutator.getStaticGCRoots();
@ -176,37 +179,104 @@ public final class GC {
RuntimeClass cls = RuntimeClass.getClass(object);
if (cls.itemType == null) {
while (cls != null) {
Address layout = cls.layout;
if (layout != null) {
short fieldCount = layout.getShort();
while (fieldCount-- > 0) {
layout = layout.add(2);
int fieldOffset = layout.getShort();
RuntimeObject reference = object.toAddress().add(fieldOffset).getAddress().toStructure();
if (reference != null && !isMarked(reference)) {
MarkQueue.enqueue(reference);
}
}
}
cls = cls.parent;
}
markObject(cls, object);
} else {
if ((cls.itemType.flags & RuntimeClass.PRIMITIVE) == 0) {
RuntimeArray array = (RuntimeArray) object;
Address base = Address.align(array.toAddress().add(RuntimeArray.class, 1), Address.sizeOf());
for (int i = 0; i < array.size; ++i) {
RuntimeObject reference = base.getAddress().toStructure();
if (reference != null && !isMarked(reference)) {
MarkQueue.enqueue(reference);
}
base = base.add(Address.sizeOf());
}
markArray(cls, (RuntimeArray) object);
}
}
}
private static void markObject(RuntimeClass cls, RuntimeObject object) {
while (cls != null) {
int type = (cls.flags >> RuntimeClass.VM_TYPE_SHIFT) & RuntimeClass.VM_TYPE_MASK;
switch (type) {
case RuntimeClass.VM_TYPE_WEAKREFERENCE:
markWeakReference((RuntimeReference) object);
break;
case RuntimeClass.VM_TYPE_REFERENCEQUEUE:
markReferenceQueue((RuntimeReferenceQueue) object);
break;
default:
markFields(cls, object);
break;
}
cls = cls.parent;
}
}
private static void markWeakReference(RuntimeReference object) {
if (object.queue != null) {
mark(object.queue);
if (object.next != null && object.object != null) {
mark(object.object);
}
}
if (object.next == null && object.object != null) {
object.next = firstWeakReference;
firstWeakReference = object;
}
}
private static void markReferenceQueue(RuntimeReferenceQueue object) {
RuntimeReference reference = object.first;
while (reference != null) {
mark(reference);
reference = reference.next;
}
}
private static void markFields(RuntimeClass cls, RuntimeObject object) {
Address layout = cls.layout;
if (layout != null) {
short fieldCount = layout.getShort();
while (fieldCount-- > 0) {
layout = layout.add(2);
int fieldOffset = layout.getShort();
RuntimeObject reference = object.toAddress().add(fieldOffset).getAddress().toStructure();
if (reference != null && !isMarked(reference)) {
MarkQueue.enqueue(reference);
}
}
}
}
private static void markArray(RuntimeClass cls, RuntimeArray array) {
if ((cls.itemType.flags & RuntimeClass.PRIMITIVE) != 0) {
return;
}
Address base = Address.align(array.toAddress().add(RuntimeArray.class, 1), Address.sizeOf());
for (int i = 0; i < array.size; ++i) {
RuntimeObject reference = base.getAddress().toStructure();
if (reference != null && !isMarked(reference)) {
MarkQueue.enqueue(reference);
}
base = base.add(Address.sizeOf());
}
}
private static void processReferences() {
RuntimeReference reference = firstWeakReference;
while (reference != null) {
RuntimeReference next = reference.next;
reference.next = null;
if ((reference.object.classReference & RuntimeObject.GC_MARKED) == 0) {
reference.object = null;
RuntimeReferenceQueue queue = reference.queue;
if (queue != null) {
if (queue.first == null) {
queue.first = reference;
} else {
queue.last.next = reference;
}
queue.last = reference;
}
}
reference = next;
}
}
private static void sweep() {
FreeChunkHolder freeChunkPtr = gcStorageAddress().toStructure();
freeChunks = 0;

View File

@ -25,6 +25,9 @@ public class RuntimeClass extends RuntimeObject {
public static final int PRIMITIVE_SHIFT = 3;
public static final int PRIMITIVE_MASK = 15;
public static final int VM_TYPE_SHIFT = 7;
public static final int VM_TYPE_MASK = 7;
public static final int BOOLEAN_PRIMITIVE = 0;
public static final int BYTE_PRIMITIVE = 1;
public static final int SHORT_PRIMITIVE = 2;
@ -35,6 +38,10 @@ public class RuntimeClass extends RuntimeObject {
public static final int DOUBLE_PRIMITIVE = 7;
public static final int VOID_PRIMITIVE = 8;
public static final int VM_TYPE_REGULAR = 0;
public static final int VM_TYPE_WEAKREFERENCE = 1;
public static final int VM_TYPE_REFERENCEQUEUE = 2;
public int size;
public int flags;
public int tag;

View File

@ -0,0 +1,22 @@
/*
* Copyright 2019 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.runtime;
public class RuntimeReference extends RuntimeObject {
public RuntimeReferenceQueue queue;
public RuntimeObject object;
public RuntimeReference next;
}

View File

@ -0,0 +1,21 @@
/*
* Copyright 2019 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.runtime;
public class RuntimeReferenceQueue extends RuntimeObject {
public RuntimeReference first;
public RuntimeReference last;
}

View File

@ -0,0 +1,47 @@
#include "runtime.h"
int32_t teavm_reference_enqueue(TeaVM_Reference* reference) {
TeaVM_ReferenceQueue* queue = reference->queue;
if (queue == NULL || reference->next != NULL) {
return INT32_C(0);
}
if (queue->last == NULL) {
queue->first = reference;
} else {
queue->last->next = reference;
}
queue->last = reference;
return INT32_C(1);
}
int32_t teavm_reference_isEnqueued(TeaVM_Reference* reference) {
return reference->queue != NULL && reference->next != NULL ? INT32_C(1) : INT32_C(0);
}
void teavm_reference_clear(TeaVM_Reference* reference) {
reference->object = NULL;
}
TeaVM_Object* teavm_reference_get(TeaVM_Reference* reference) {
return reference->object;
}
TeaVM_Reference* teavm_reference_poll(TeaVM_ReferenceQueue* queue) {
if (queue->first == NULL) {
return NULL;
}
TeaVM_Reference* reference = queue->first;
queue->first = reference->next;
if (queue->first == NULL) {
queue->last = NULL;
}
return reference;
}
void teavm_reference_init(TeaVM_Reference* reference, TeaVM_Object* object, TeaVM_ReferenceQueue* queue) {
reference->object = object;
reference->queue = queue;
}

View File

@ -248,4 +248,31 @@ extern TeaVM_String* teavm_registerString(TeaVM_String*);
static inline TeaVM_Object* teavm_dereferenceNullable(TeaVM_Object** o) {
return o != NULL ? *o : NULL;
}
}
struct TeaVM_ReferenceQueue;
typedef struct TeaVM_Reference {
TeaVM_Object parent;
struct TeaVM_ReferenceQueue* queue;
TeaVM_Object* object;
struct TeaVM_Reference* next;
} TeaVM_Reference;
typedef struct TeaVM_ReferenceQueue {
TeaVM_Object parent;
TeaVM_Reference* first;
TeaVM_Reference* last;
} TeaVM_ReferenceQueue;
extern int32_t teavm_reference_enqueue(TeaVM_Reference*);
extern int32_t teavm_reference_isEnqueued(TeaVM_Reference*);
extern void teavm_reference_clear(TeaVM_Reference*);
extern TeaVM_Object* teavm_reference_get(TeaVM_Reference*);
extern TeaVM_Reference* teavm_reference_poll(TeaVM_ReferenceQueue*);
extern void teavm_reference_init(TeaVM_Reference*, TeaVM_Object*, TeaVM_ReferenceQueue*);