diff --git a/.github/workflows/pullrequest.yml b/.github/workflows/pullrequest.yml index 8dbe226bc0..e623301dae 100644 --- a/.github/workflows/pullrequest.yml +++ b/.github/workflows/pullrequest.yml @@ -34,6 +34,12 @@ jobs: - uses: joschi/setup-jdk@v1 with: java-version: ${{ matrix.java }} + - uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- - run: wget https://apt.llvm.org/llvm.sh - run: chmod +x llvm.sh - run: sudo ./llvm.sh 10 @@ -49,6 +55,12 @@ jobs: - uses: joschi/setup-jdk@v1 with: java-version: ${{ matrix.java }} + - uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- - run: wget https://apt.llvm.org/llvm.sh - run: chmod +x llvm.sh - run: sudo ./llvm.sh 10 @@ -64,6 +76,12 @@ jobs: - uses: joschi/setup-jdk@v1 with: java-version: ${{ matrix.java }} + - uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- - run: wget https://apt.llvm.org/llvm.sh - run: chmod +x llvm.sh - run: sudo ./llvm.sh 10 @@ -79,6 +97,12 @@ jobs: - uses: joschi/setup-jdk@v1 with: java-version: ${{ matrix.java }} + - uses: actions/cache@v1 + with: + path: ~/.m2/repository + key: ${{ runner.os }}-maven-${{ hashFiles('**/pom.xml') }} + restore-keys: | + ${{ runner.os }}-maven- - run: wget https://apt.llvm.org/llvm.sh - run: chmod +x llvm.sh - run: sudo ./llvm.sh 10 diff --git a/classlib/java.base/src/main/java/de/mirkosertic/bytecoder/classlib/MemoryManager.java b/classlib/java.base/src/main/java/de/mirkosertic/bytecoder/classlib/MemoryManager.java index 50d51d3034..ee7e63f954 100644 --- a/classlib/java.base/src/main/java/de/mirkosertic/bytecoder/classlib/MemoryManager.java +++ b/classlib/java.base/src/main/java/de/mirkosertic/bytecoder/classlib/MemoryManager.java @@ -98,7 +98,7 @@ private static void internalFree(final int current) { final int currentNext = Address.getIntValue(current, 4); if (currentPrev != 0) { - Address.setIntValue(current, 4, currentNext); + Address.setIntValue(currentPrev, 4, currentNext); } else { Address.setIntValue(heapBase, 8, currentNext); } @@ -339,12 +339,14 @@ public static boolean isUsedByStaticData(final int aOwningBlock) { public static boolean isUsedByStaticDataUserSpace(final int aPtrToObject) { int dataStart = 0; final int theDataEnd = Address.getDataEnd(); + while(dataStart <= theDataEnd) { if (Address.getIntValue(dataStart, 0) == aPtrToObject) { return true; } dataStart += 4; } + return false; } @@ -357,6 +359,7 @@ public static boolean isUsedByStackUserSpace(final int aPtrToObject) { if (Address.systemHasStack() == 0) { return false; } + int stackStart = Address.getStackTop(); final int memorySize = Address.getMemorySize(); while(stackStart + 4 < memorySize) { @@ -379,6 +382,8 @@ public static boolean isUsedByHeapUserSpace(final int aPtrToObject) { int current = Address.getIntValue(heapBase, 8); while(current != 0) { + final int next = Address.getIntValue(current, 4); + // Ignore self reference if (allocationPtr != current) { final int theSize = Address.getIntValue(current, 0); @@ -388,7 +393,7 @@ public static boolean isUsedByHeapUserSpace(final int aPtrToObject) { } } } - current = Address.getIntValue(current, 4); + current = next; } return false; @@ -423,7 +428,7 @@ public static int IncrementalGC(final int blockLimit) { final int next = Address.getIntValue(current, 4); final int survivorCount = Address.getIntValue(current, 8); if (currentEpoch % survivorCount == 0) { - if (!isUsedByHeap(current) && !isUsedByStack(current) && !(isUsedByStaticData(current)) && (!isUsedAsCallback(current + 12))) { + if (!isUsedByHeap(current) && !isUsedByStack(current) && !(isUsedByStaticData(current)) && (!isUsedAsCallback(current + 16))) { internalFree(current); freeCounter++; } else { @@ -436,6 +441,7 @@ public static int IncrementalGC(final int blockLimit) { // We have reached the limit for the current run // We save the next block to proceed and exit here Address.setIntValue(heapBase, 12, next); + return stepCounter; } } diff --git a/core/src/test/java/de/mirkosertic/bytecoder/classlib/GCTest.java b/core/src/test/java/de/mirkosertic/bytecoder/classlib/GCTest.java new file mode 100644 index 0000000000..2b39884de4 --- /dev/null +++ b/core/src/test/java/de/mirkosertic/bytecoder/classlib/GCTest.java @@ -0,0 +1,67 @@ +/* + * Copyright 2020 Mirko Sertic + * + * 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 de.mirkosertic.bytecoder.classlib; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import de.mirkosertic.bytecoder.api.web.ClickEvent; +import de.mirkosertic.bytecoder.api.web.EventListener; +import de.mirkosertic.bytecoder.api.web.EventTarget; +import de.mirkosertic.bytecoder.api.web.Window; +import de.mirkosertic.bytecoder.backend.CompileTarget; +import de.mirkosertic.bytecoder.unittest.BytecoderTestOption; +import de.mirkosertic.bytecoder.unittest.BytecoderTestOptions; +import de.mirkosertic.bytecoder.unittest.BytecoderUnitTestRunner; + +@RunWith(BytecoderUnitTestRunner.class) +@BytecoderTestOptions(value = { + @BytecoderTestOption(backend = CompileTarget.BackendType.wasm, preferStackifier = true), +}, includeJVM = false) +public class GCTest { + + private static EventListener registerClicklistenerOn(final EventTarget target) { + final EventListener listener = new EventListener() { + @Override + public void run(ClickEvent aEvent) { + + } + }; + target.addEventListener("click", listener); + return listener; + } + + @Test + public void testListenerGC() { + final Window w = Window.window(); + final EventListener listener = registerClicklistenerOn(w); + final int ptr = Address.ptrOf(listener); + Assert.assertTrue(MemoryManager.isUsedAsCallback(ptr)); + Assert.assertFalse(MemoryManager.isUsedByHeapUserSpace(ptr)); + Assert.assertTrue(MemoryManager.isUsedByStackUserSpace(ptr)); + Assert.assertFalse(MemoryManager.isUsedByStaticDataUserSpace(ptr)); + Assert.assertTrue(MemoryManager.indexInAllocationList(ptr) >= 0); + + MemoryManager.GC(); + + Assert.assertTrue(MemoryManager.isUsedAsCallback(ptr)); + Assert.assertFalse(MemoryManager.isUsedByHeapUserSpace(ptr)); + Assert.assertTrue(MemoryManager.isUsedByStackUserSpace(ptr)); + Assert.assertFalse(MemoryManager.isUsedByStaticDataUserSpace(ptr)); + Assert.assertTrue(MemoryManager.indexInAllocationList(ptr) >= 0); + } +} \ No newline at end of file diff --git a/core/src/test/java/de/mirkosertic/bytecoder/classlib/GCTestStackless.java b/core/src/test/java/de/mirkosertic/bytecoder/classlib/GCTestStackless.java new file mode 100644 index 0000000000..fca26419c1 --- /dev/null +++ b/core/src/test/java/de/mirkosertic/bytecoder/classlib/GCTestStackless.java @@ -0,0 +1,67 @@ +/* + * Copyright 2020 Mirko Sertic + * + * 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 de.mirkosertic.bytecoder.classlib; + +import org.junit.Assert; +import org.junit.Test; +import org.junit.runner.RunWith; + +import de.mirkosertic.bytecoder.api.web.ClickEvent; +import de.mirkosertic.bytecoder.api.web.EventListener; +import de.mirkosertic.bytecoder.api.web.EventTarget; +import de.mirkosertic.bytecoder.api.web.Window; +import de.mirkosertic.bytecoder.backend.CompileTarget; +import de.mirkosertic.bytecoder.unittest.BytecoderTestOption; +import de.mirkosertic.bytecoder.unittest.BytecoderTestOptions; +import de.mirkosertic.bytecoder.unittest.BytecoderUnitTestRunner; + +@RunWith(BytecoderUnitTestRunner.class) +@BytecoderTestOptions(value = { + @BytecoderTestOption(backend = CompileTarget.BackendType.wasm_llvm, preferStackifier = true), +}, includeJVM = false) +public class GCTestStackless { + + private static EventListener registerClicklistenerOn(final EventTarget target) { + final EventListener listener = new EventListener() { + @Override + public void run(ClickEvent aEvent) { + + } + }; + target.addEventListener("click", listener); + return listener; + } + + @Test + public void testListenerGC() { + final Window w = Window.window(); + final EventListener listener = registerClicklistenerOn(w); + final int ptr = Address.ptrOf(listener); + Assert.assertTrue(MemoryManager.isUsedAsCallback(ptr)); + Assert.assertFalse(MemoryManager.isUsedByHeapUserSpace(ptr)); + Assert.assertFalse(MemoryManager.isUsedByStackUserSpace(ptr)); + Assert.assertFalse(MemoryManager.isUsedByStaticDataUserSpace(ptr)); + Assert.assertTrue(MemoryManager.indexInAllocationList(ptr) >= 0); + + MemoryManager.GC(); + + Assert.assertTrue(MemoryManager.isUsedAsCallback(ptr)); + Assert.assertFalse(MemoryManager.isUsedByHeapUserSpace(ptr)); + Assert.assertFalse(MemoryManager.isUsedByStackUserSpace(ptr)); + Assert.assertFalse(MemoryManager.isUsedByStaticDataUserSpace(ptr)); + Assert.assertTrue(MemoryManager.indexInAllocationList(ptr) >= 0); + } +} \ No newline at end of file diff --git a/maven/pom.xml b/maven/pom.xml index 6c22d851d2..af823ac1b0 100644 --- a/maven/pom.xml +++ b/maven/pom.xml @@ -70,7 +70,7 @@ com.google.guava guava - 23.0 + [24.1.1,)