aboutsummaryrefslogtreecommitdiff
path: root/test/test
diff options
context:
space:
mode:
authorDmitry Petrashko <dark@d-d.me>2014-05-06 10:25:59 +0200
committerDmitry Petrashko <dark@d-d.me>2014-05-06 10:25:59 +0200
commitbf9ae99e77f179e9f5ad3c2074edbb5aab0fe9f1 (patch)
tree6e7e2e9f5d21566fd9f74e4861a13e32ef375d3b /test/test
parenta782adaf302713a1a049b9a72dacc0483ed67229 (diff)
parentb88b79d03517dad973de2aa81c2e5f702d20b2e1 (diff)
downloaddotty-bf9ae99e77f179e9f5ad3c2074edbb5aab0fe9f1.tar.gz
dotty-bf9ae99e77f179e9f5ad3c2074edbb5aab0fe9f1.tar.bz2
dotty-bf9ae99e77f179e9f5ad3c2074edbb5aab0fe9f1.zip
Merge pull request #119 from DarkDimius/leaks
Context escape detection.
Diffstat (limited to 'test/test')
-rw-r--r--test/test/CompilerTest.scala2
-rw-r--r--test/test/ContextEscapeDetection.java40
-rw-r--r--test/test/ContextEscapeDetector.java109
-rw-r--r--test/test/DottyTest.scala8
4 files changed, 156 insertions, 3 deletions
diff --git a/test/test/CompilerTest.scala b/test/test/CompilerTest.scala
index d25cfb637..d52e74de7 100644
--- a/test/test/CompilerTest.scala
+++ b/test/test/CompilerTest.scala
@@ -13,7 +13,7 @@ class CompilerTest extends DottyTest {
def compileArgs(args: Array[String], xerrors: Int = 0): Unit = {
val allArgs = args ++ defaultOptions
val processor = if (allArgs.exists(_.startsWith("#"))) Bench else Main
- val nerrors = processor.process(allArgs).count(Reporter.ERROR.level)
+ val nerrors = processor.process(allArgs, ctx).count(Reporter.ERROR.level)
assert(nerrors == xerrors, s"Wrong # of errors. Expected: $xerrors, found: $nerrors")
}
diff --git a/test/test/ContextEscapeDetection.java b/test/test/ContextEscapeDetection.java
new file mode 100644
index 000000000..233630eb2
--- /dev/null
+++ b/test/test/ContextEscapeDetection.java
@@ -0,0 +1,40 @@
+package test;
+
+import dotty.tools.dotc.core.Contexts;
+import org.junit.*;
+
+import java.lang.ref.WeakReference;
+import java.util.LinkedList;
+import java.util.List;
+
+
+public abstract class ContextEscapeDetection {
+ public static class TestContext{
+ public TestContext(WeakReference<Contexts.Context> context, String testName) {
+ this.context = context;
+ this.testName = testName;
+ }
+
+ public final WeakReference<Contexts.Context> context;
+ public final String testName;
+
+ }
+ public static final List<TestContext> contexts = new LinkedList<>();
+
+ public abstract Contexts.Context getCtx();
+
+ public abstract void clearCtx();
+
+ @Before
+ public synchronized void stealContext() {
+ contexts.add(new TestContext(new WeakReference<>(this.getCtx()), this.getClass().getName()));
+ }
+
+ @After
+ public synchronized void clearContext() {
+ this.clearCtx();
+ }
+
+
+}
+
diff --git a/test/test/ContextEscapeDetector.java b/test/test/ContextEscapeDetector.java
new file mode 100644
index 000000000..c7768fd57
--- /dev/null
+++ b/test/test/ContextEscapeDetector.java
@@ -0,0 +1,109 @@
+package test;
+
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.notification.RunListener;
+import org.junit.Assert;
+
+import java.lang.ref.WeakReference;
+
+public class ContextEscapeDetector extends RunListener {
+
+ //context can be captured by objects, eg NoDenotation
+ public static final int CONTEXTS_ALLOWED = 1;
+
+ @Override
+ public void testRunFinished(Result result) throws Exception {
+ if (contextsAlive() > CONTEXTS_ALLOWED) {
+ forceGCHeuristic0();
+ if (contextsAlive() > CONTEXTS_ALLOWED) {
+ forceGCHeuristic1();
+ if (contextsAlive() > CONTEXTS_ALLOWED) {
+ forceGCHeuristic2();
+ forceGCHeuristic1();
+ int contextAlive = contextsAlive();
+ if (contextAlive > CONTEXTS_ALLOWED) {
+ StringBuilder names = new StringBuilder();
+ for (ContextEscapeDetection.TestContext ref : ContextEscapeDetection.contexts) {
+ if (ref.context.get() != null) names.append(ref.testName).append(' ');
+ }
+ Assert.fail("Multiple contexts survived test suite: " + names.toString());
+ }
+ }
+ }
+ }
+ super.testRunFinished(result);
+ }
+
+ private static synchronized int contextsAlive() {
+ int count = 0;
+ for (ContextEscapeDetection.TestContext ref : ContextEscapeDetection.contexts) {
+ if (ref.context.get() != null) count++;
+ }
+ return count;
+ }
+
+ private static volatile Object o = null;
+
+ private static synchronized void forceGCHeuristic0() {
+ System.gc();
+ Runtime.getRuntime().gc();
+ System.gc();
+ Runtime.getRuntime().gc();
+ System.gc();
+ Runtime.getRuntime().gc();
+ System.gc();
+ Runtime.getRuntime().gc();
+ System.gc();
+ }
+
+ private static synchronized void forceGCHeuristic1() {
+ Object obj = new Object();
+ WeakReference ref = new WeakReference<Object>(obj);
+ obj = null;
+ while (ref.get() != null) {
+ System.gc();
+ }
+ }
+
+ private static synchronized void forceGCHeuristic2() {
+ try {
+ Object[] arr = new Object[1024]; // upto 8 GB
+ WeakReference ref = new WeakReference<Object>(arr);
+ o = arr; // make sure array isn't optimized away
+
+ Runtime runtime = Runtime.getRuntime();
+ // allocate memory until no more that 64MB is left
+ for (int i = 0; i < 1024 &&
+ runtime.totalMemory() != runtime.maxMemory() ||
+ runtime.freeMemory() < 1024 * 1024 * 64; i++) {
+ int[] data = new int[1024 * 1024]; // 8MB
+ for (int j = 0; j < 1024 * 1024; j++) {
+ data[j] = j; // force actual pages allocation
+ }
+ arr[i] = data;
+ }
+ o = null;
+ arr = new Object[128];
+ o = arr;
+ // allocate 1 more GB
+ for (int i = 0; i < 128; i++) {
+ int[] data = new int[1024 * 1024]; // 8MB
+ for (int j = 0; j < 1024 * 1024; j++) {
+ data[j] = j; // force actual pages allocation
+ }
+ arr[i] = data;
+ }
+ o = null;
+ arr = null;
+
+ forceGCHeuristic0();
+ while (ref.get() != null) {
+ System.gc();
+ }
+ } catch (OutOfMemoryError e) {
+ o = null;
+ // just swallow
+ }
+ }
+}
diff --git a/test/test/DottyTest.scala b/test/test/DottyTest.scala
index 3c308e230..4efb1e615 100644
--- a/test/test/DottyTest.scala
+++ b/test/test/DottyTest.scala
@@ -14,11 +14,11 @@ import dotty.tools.dotc.Compiler
import dotty.tools.dotc
import dotty.tools.dotc.core.Phases.Phase
-class DottyTest {
+class DottyTest extends ContextEscapeDetection{
dotty.tools.dotc.parsing.Scanners // initialize keywords
- implicit val ctx: Context = {
+ implicit var ctx: Contexts.Context = {
val base = new ContextBase
import base.settings._
val ctx = base.initialCtx.fresh
@@ -37,6 +37,10 @@ class DottyTest {
ctx
}
+ override def getCtx: Context = ctx
+ override def clearCtx() = {
+ ctx = null
+ }
private def compilerWithChecker(phase: String)(assertion:(tpd.Tree, Context) => Unit) = new Compiler {
override def phases = {
val allPhases = super.phases