From 2eafb902f41555ada0d881cbb1f3393dec1c707e Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Fri, 17 May 2019 17:40:45 +0300 Subject: [PATCH] C: support weak references --- .../classlib/java/lang/ref/TReference.java | 5 - .../java/lang/ref/TReferenceQueue.java | 5 - .../java/lang/ref/TWeakReference.java | 10 ++ .../java/org/teavm/backend/c/CTarget.java | 22 +++- .../backend/c/generate/ClassGenerator.java | 17 ++- .../backend/c/generate/NameProvider.java | 26 +++- .../c/generators/ReferenceQueueGenerator.java | 47 +++++++ .../c/generators/WeakReferenceGenerator.java | 74 +++++++++++ .../WeakReferenceDependencyListener.java | 84 ++++++++++++ .../WeakReferenceTransformation.java | 44 +++++++ core/src/main/java/org/teavm/runtime/GC.java | 120 ++++++++++++++---- .../java/org/teavm/runtime/RuntimeClass.java | 7 + .../org/teavm/runtime/RuntimeReference.java | 22 ++++ .../teavm/runtime/RuntimeReferenceQueue.java | 21 +++ .../org/teavm/backend/c/references.c | 47 +++++++ .../resources/org/teavm/backend/c/runtime.h | 29 ++++- 16 files changed, 533 insertions(+), 47 deletions(-) create mode 100644 core/src/main/java/org/teavm/backend/c/generators/ReferenceQueueGenerator.java create mode 100644 core/src/main/java/org/teavm/backend/c/generators/WeakReferenceGenerator.java create mode 100644 core/src/main/java/org/teavm/backend/lowlevel/dependency/WeakReferenceDependencyListener.java create mode 100644 core/src/main/java/org/teavm/backend/lowlevel/transform/WeakReferenceTransformation.java create mode 100644 core/src/main/java/org/teavm/runtime/RuntimeReference.java create mode 100644 core/src/main/java/org/teavm/runtime/RuntimeReferenceQueue.java create mode 100644 core/src/main/resources/org/teavm/backend/c/references.c diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/ref/TReference.java b/classlib/src/main/java/org/teavm/classlib/java/lang/ref/TReference.java index 8319691d3..930e9d8d0 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/ref/TReference.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/ref/TReference.java @@ -17,11 +17,6 @@ package org.teavm.classlib.java.lang.ref; import org.teavm.classlib.java.lang.TObject; -/** - * - * @author Alexey Andreev - * @param type of an object to which this reference points. - */ public abstract class TReference extends TObject { public T get() { return null; diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/ref/TReferenceQueue.java b/classlib/src/main/java/org/teavm/classlib/java/lang/ref/TReferenceQueue.java index 341572776..a653ad4e5 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/ref/TReferenceQueue.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/ref/TReferenceQueue.java @@ -15,11 +15,6 @@ */ package org.teavm.classlib.java.lang.ref; -/** - * - * @author Alexey Andreev - * @param - */ public class TReferenceQueue { public TReference poll() { return null; diff --git a/classlib/src/main/java/org/teavm/classlib/java/lang/ref/TWeakReference.java b/classlib/src/main/java/org/teavm/classlib/java/lang/ref/TWeakReference.java index 51bd9d723..eada940f0 100644 --- a/classlib/src/main/java/org/teavm/classlib/java/lang/ref/TWeakReference.java +++ b/classlib/src/main/java/org/teavm/classlib/java/lang/ref/TWeakReference.java @@ -35,4 +35,14 @@ public class TWeakReference extends TReference { public void clear() { value = null; } + + @Override + public boolean isEnqueued() { + return false; + } + + @Override + public boolean enqueue() { + return true; + } } diff --git a/core/src/main/java/org/teavm/backend/c/CTarget.java b/core/src/main/java/org/teavm/backend/c/CTarget.java index cfb846b00..64bebfb76 100644 --- a/core/src/main/java/org/teavm/backend/c/CTarget.java +++ b/core/src/main/java/org/teavm/backend/c/CTarget.java @@ -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 transformers = new ArrayList<>(); transformers.add(new ClassPatch()); transformers.add(new CDependencyListener()); + transformers.add(new WeakReferenceTransformation()); return transformers; } @Override public List 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 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( diff --git a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java index 0ca44822b..006050004 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java +++ b/core/src/main/java/org/teavm/backend/c/generate/ClassGenerator.java @@ -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 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")); diff --git a/core/src/main/java/org/teavm/backend/c/generate/NameProvider.java b/core/src/main/java/org/teavm/backend/c/generate/NameProvider.java index 94c7a961d..d36373821 100644 --- a/core/src/main/java/org/teavm/backend/c/generate/NameProvider.java +++ b/core/src/main/java/org/teavm/backend/c/generate/NameProvider.java @@ -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 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 getKeywords() { return keywords; diff --git a/core/src/main/java/org/teavm/backend/c/generators/ReferenceQueueGenerator.java b/core/src/main/java/org/teavm/backend/c/generators/ReferenceQueueGenerator.java new file mode 100644 index 000000000..e6e7ee842 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/generators/ReferenceQueueGenerator.java @@ -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, "", 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 "": + 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; + } + } +} diff --git a/core/src/main/java/org/teavm/backend/c/generators/WeakReferenceGenerator.java b/core/src/main/java/org/teavm/backend/c/generators/WeakReferenceGenerator.java new file mode 100644 index 000000000..1ee96dfb5 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/c/generators/WeakReferenceGenerator.java @@ -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, "", 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 "": + 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; + } + } +} diff --git a/core/src/main/java/org/teavm/backend/lowlevel/dependency/WeakReferenceDependencyListener.java b/core/src/main/java/org/teavm/backend/lowlevel/dependency/WeakReferenceDependencyListener.java new file mode 100644 index 000000000..cefb2d7c5 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/lowlevel/dependency/WeakReferenceDependencyListener.java @@ -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 "": + reachReferenceInit(agent, method); + break; + case "get": + reachReferenceGet(method); + break; + } + } else if (methodRef.getClassName().equals(ReferenceQueue.class.getName())) { + switch (methodRef.getName()) { + case "": + 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, "", 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, "", void.class)); + method.getVariable(0).connect(superMethod.getVariable(0)); + superMethod.use(); + } +} diff --git a/core/src/main/java/org/teavm/backend/lowlevel/transform/WeakReferenceTransformation.java b/core/src/main/java/org/teavm/backend/lowlevel/transform/WeakReferenceTransformation.java new file mode 100644 index 000000000..777601b56 --- /dev/null +++ b/core/src/main/java/org/teavm/backend/lowlevel/transform/WeakReferenceTransformation.java @@ -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); + } + } +} diff --git a/core/src/main/java/org/teavm/runtime/GC.java b/core/src/main/java/org/teavm/runtime/GC.java index 640179dc9..5b761a8c7 100644 --- a/core/src/main/java/org/teavm/runtime/GC.java +++ b/core/src/main/java/org/teavm/runtime/GC.java @@ -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; diff --git a/core/src/main/java/org/teavm/runtime/RuntimeClass.java b/core/src/main/java/org/teavm/runtime/RuntimeClass.java index 65ce88254..aa46f0cee 100644 --- a/core/src/main/java/org/teavm/runtime/RuntimeClass.java +++ b/core/src/main/java/org/teavm/runtime/RuntimeClass.java @@ -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; diff --git a/core/src/main/java/org/teavm/runtime/RuntimeReference.java b/core/src/main/java/org/teavm/runtime/RuntimeReference.java new file mode 100644 index 000000000..4064d02cd --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/RuntimeReference.java @@ -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; +} diff --git a/core/src/main/java/org/teavm/runtime/RuntimeReferenceQueue.java b/core/src/main/java/org/teavm/runtime/RuntimeReferenceQueue.java new file mode 100644 index 000000000..c869c3c42 --- /dev/null +++ b/core/src/main/java/org/teavm/runtime/RuntimeReferenceQueue.java @@ -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; +} diff --git a/core/src/main/resources/org/teavm/backend/c/references.c b/core/src/main/resources/org/teavm/backend/c/references.c new file mode 100644 index 000000000..cf4ec53dc --- /dev/null +++ b/core/src/main/resources/org/teavm/backend/c/references.c @@ -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; +} \ No newline at end of file diff --git a/core/src/main/resources/org/teavm/backend/c/runtime.h b/core/src/main/resources/org/teavm/backend/c/runtime.h index 44991ad1d..ce3f007c6 100644 --- a/core/src/main/resources/org/teavm/backend/c/runtime.h +++ b/core/src/main/resources/org/teavm/backend/c/runtime.h @@ -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; -} \ No newline at end of file +} + +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*); \ No newline at end of file