From bbdff45246cbe64411af3c66a6f5ab911cd18e9e Mon Sep 17 00:00:00 2001 From: Andrey Breslav Date: Thu, 26 Sep 2013 12:40:19 -0700 Subject: [PATCH] Support recursion-tolerant nullable values All other values are made recursion-intolerant --- .../lazy/storage/LockBasedStorageManager.java | 33 ++++++++++++++++++- .../resolve/lazy/storage/StorageManager.java | 6 ++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/lazy/storage/LockBasedStorageManager.java b/core/descriptors/src/org/jetbrains/jet/lang/resolve/lazy/storage/LockBasedStorageManager.java index bfc5ee12506..eedd0bd4535 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/lazy/storage/LockBasedStorageManager.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/lazy/storage/LockBasedStorageManager.java @@ -82,6 +82,17 @@ public class LockBasedStorageManager implements StorageManager { return new LockBasedLazyValue(lock, computable); } + @NotNull + @Override + public NullableLazyValue createRecursionTolerantNullableLazyValue(@NotNull Computable computable) { + return new LockBasedLazyValue(lock, computable) { + @Override + protected Object recursionDetected() { + return WrappedValues.escapeNull(null); + } + }; + } + @NotNull @Override public NullableLazyValue createNullableLazyValueWithPostCompute( @@ -103,6 +114,9 @@ public class LockBasedStorageManager implements StorageManager { } private static class LockBasedLazyValue implements NullableLazyValue { + + private static final Object COMPUTING = new Object(); + private final Object lock; private final Computable computable; @@ -117,12 +131,20 @@ public class LockBasedStorageManager implements StorageManager { @Override public T compute() { Object _value = value; - if (_value != null) return WrappedValues.unescapeExceptionOrNull(_value); + if (_value != null && _value != COMPUTING) return WrappedValues.unescapeExceptionOrNull(_value); synchronized (lock) { _value = value; + if (_value == COMPUTING) { + Object result = recursionDetected(); + if (result != null) { + return WrappedValues.unescapeExceptionOrNull(result); + } + } + if (_value != null) return WrappedValues.unescapeExceptionOrNull(_value); + value = COMPUTING; try { T typedValue = computable.compute(); value = WrappedValues.escapeNull(typedValue); @@ -136,6 +158,15 @@ public class LockBasedStorageManager implements StorageManager { } } + /** + * @return {@code null} to proceed, a wrapped value otherwise, see WrappedValues + * @throws DO NOT throw exceptions from implementations of this method, instead return WrappedValues.escapeThrowable(exception) + */ + @Nullable + protected Object recursionDetected() { + return WrappedValues.escapeThrowable(new IllegalStateException("Recursive call in a lazy value")); + } + protected void postCompute(T value) { // Doing something in post-compute helps prevent infinite recursion } diff --git a/core/descriptors/src/org/jetbrains/jet/lang/resolve/lazy/storage/StorageManager.java b/core/descriptors/src/org/jetbrains/jet/lang/resolve/lazy/storage/StorageManager.java index 2314cc14d77..ee74708a1cd 100644 --- a/core/descriptors/src/org/jetbrains/jet/lang/resolve/lazy/storage/StorageManager.java +++ b/core/descriptors/src/org/jetbrains/jet/lang/resolve/lazy/storage/StorageManager.java @@ -48,6 +48,12 @@ public interface StorageManager { @NotNull NullableLazyValue createNullableLazyValue(@NotNull Computable computable); + /** + * If recursion is detected, respective calls to compute() simply return null + */ + @NotNull + NullableLazyValue createRecursionTolerantNullableLazyValue(@NotNull Computable computable); + /** * {@code postCompute} is called after the value is computed, but before any other thread sees it (the current thread may * see it in between)