Refactor Ambient.Reference to be CompositionReference

Also removes Ambient.Portal in favor of compositionReference effect

Change-Id: I5f3540bda7d8fe72f76579094ab895d8dd042194
diff --git a/compose/plugins/cli-tests/src/test/java/org/jetbrains/kotlin/r4a/KtxCodegenTests.kt b/compose/plugins/cli-tests/src/test/java/org/jetbrains/kotlin/r4a/KtxCodegenTests.kt
index fa69e1c..3749294 100644
--- a/compose/plugins/cli-tests/src/test/java/org/jetbrains/kotlin/r4a/KtxCodegenTests.kt
+++ b/compose/plugins/cli-tests/src/test/java/org/jetbrains/kotlin/r4a/KtxCodegenTests.kt
@@ -488,25 +488,18 @@
         val tvId = 345
         var text = "Hello, world!"
 
-        // NOTE(lmr): The fact that "bust" is needed here is actually an issue with the fact that
-        // changes to providers don't invalidate consumers from other composers via Ambient.Portal.
-        // When that gets fixed, we should update this test to show that.
         compose(
             """
             val StringAmbient = Ambient.of<String> { "default" }
 
-            fun buildPortal() = effectOf<Ambient.Reference> {
-                context.buildReference()
-            }
-
             @Composable fun App(value: String) {
                 <StringAmbient.Provider value>
-                    <Parent bust=Math.random() />
+                    <Parent />
                 </StringAmbient.Provider>
             }
 
-            @Composable fun Parent(bust: Double) {
-                val ambientRef = +buildPortal()
+            @Composable fun Parent() {
+                val compositionRef = +compositionReference()
                 val viewRef = +memo { Ref<LinearLayout>() }
 
                 <LinearLayout id=$llId ref=viewRef />
@@ -514,14 +507,14 @@
                 +onCommit {
                     R4a.composeInto(
                         container = viewRef.value ?: error("No View Ref!"),
-                        parent = ambientRef
+                        parent = compositionRef
                     ) {
-                        <Child bust=Math.random() />
+                        <Child />
                     }
                 }
             }
 
-            @Composable fun Child(bust: Double) {
+            @Composable fun Child() {
                 <StringAmbient.Consumer> value ->
                     <TextView id=$tvId text=value />
                 </StringAmbient.Consumer>
@@ -554,37 +547,33 @@
         val tvId = 345
         var text = "Hello, world!"
 
-        // NOTE(lmr): The fact that "bust" is needed here is actually an issue with the fact that
-        // changes to providers don't invalidate consumers from other composers via Ambient.Portal.
-        // When that gets fixed, we should update this test to show that.
         compose(
             """
             val StringAmbient = Ambient.of<String> { "default" }
 
             @Composable fun App(value: String) {
                 <StringAmbient.Provider value>
-                    <Parent bust=Math.random() />
+                    <Parent />
                 </StringAmbient.Provider>
             }
 
-            @Composable fun Parent(bust: Double) {
-                <Ambient.Portal> ambientRef ->
-                    val viewRef = +memo { Ref<LinearLayout>() }
+            @Composable fun Parent() {
+                val compositionRef = +compositionReference()
+                val viewRef = +memo { Ref<LinearLayout>() }
 
-                    <LinearLayout id=$llId ref=viewRef />
+                <LinearLayout id=$llId ref=viewRef />
 
-                    +onCommit {
-                        R4a.composeInto(
-                            container = viewRef.value ?: error("No View Ref!"),
-                            parent = ambientRef
-                        ) {
-                            <Child bust=Math.random() />
-                        }
+                +onCommit {
+                    R4a.composeInto(
+                        container = viewRef.value ?: error("No View Ref!"),
+                        parent = compositionRef
+                    ) {
+                        <Child />
                     }
-                </Ambient.Portal>
+                }
             }
 
-            @Composable fun Child(bust: Double) {
+            @Composable fun Child() {
                 <StringAmbient.Consumer> value ->
                     <TextView id=$tvId text=value />
                 </StringAmbient.Consumer>
@@ -681,10 +670,6 @@
 
         compose(
             """
-                fun buildPortal() = effectOf<Ambient.Reference> {
-                    context.buildReference()
-                }
-
                 fun <T> refFor() = memo { Ref<T>() }
 
                 val textAmbient = Ambient.of { "default" }
@@ -695,7 +680,7 @@
                 }
 
                 @Composable fun PortalTest() {
-                    val portal = +buildPortal()
+                    val portal = +compositionReference()
                     val ref = +refFor<LinearLayout>()
                     <DisplayTest id=$outerId />
 
diff --git a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/ComposeFragment.kt b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/ComposeFragment.kt
index 18b9348..863fc7c 100644
--- a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/ComposeFragment.kt
+++ b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/ComposeFragment.kt
@@ -13,7 +13,7 @@
  * A fragment that uses a Component as it's UI.
  */
 abstract class ComposeFragment : Fragment() {
-    var reference: Ambient.Reference? = null
+    var reference: CompositionReference? = null
     abstract fun compose()
 
     override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
diff --git a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/ComposePagerAdapter.kt b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/ComposePagerAdapter.kt
index bf4704c..3e81ef3 100644
--- a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/ComposePagerAdapter.kt
+++ b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/ComposePagerAdapter.kt
@@ -31,7 +31,7 @@
 
     private val instantiated = SparseArray<FrameLayout>()
 
-    var reference: Ambient.Reference? = null
+    var reference: CompositionReference? = null
     abstract fun composeItem(position: Int)
 
     fun recomposeAll() {
@@ -90,7 +90,7 @@
     var children: (Int) -> Unit = {}
     var getCount: () -> Int = { 0 }
     var getPageTitle: (Int) -> CharSequence? = { null }
-    var reference: Ambient.Reference? = null
+    var reference: CompositionReference? = null
     var offscreenPageLimit: Int = 1
     lateinit var layoutParams: ViewGroup.LayoutParams
 
@@ -112,14 +112,13 @@
         // TODO(lmr): I think we realistically should call myAdapter.notifyDatasetChanged() here or in a
         // componentDidUpdate() like lifecycle
         with(composer) {
-            portal(0) { ref ->
-                myAdapter.reference = ref
-                emitView(0, ::ViewPager) {
-                    set(R.id.view_pager_id) { id = it }
-                    set(myAdapter) { adapter = it }
-                    set(refToForward) { this.setRef(it) }
-                    set(offscreenPageLimit) { setOffscreenPageLimit(it) }
-                }
+            val ref = +compositionReference()
+            myAdapter.reference = ref
+            emitView(0, ::ViewPager) {
+                set(R.id.view_pager_id) { id = it }
+                set(myAdapter) { adapter = it }
+                set(refToForward) { this.setRef(it) }
+                set(offscreenPageLimit) { setOffscreenPageLimit(it) }
             }
         }
         myAdapter.recomposeAll()
@@ -182,16 +181,15 @@
                         set(tabRef) { setRef(it) }
                     }
                 }, { composeTab ->
-                    portal(0) { ambients ->
-                        emitComponent(0, ::ComposeViewPager) { f ->
-                            set(pagerLayoutParams) { f.layoutParams = it } or
-                            set(ambients) { f.reference = it } or
-                            set(composeTab) { f.children = it } or
-                            set({ titles.size }) { f.getCount = it } or
-                            set({ position: Int -> titles[position] }) { f.getPageTitle = it } or
-                            set(pagerRef) { f.ref = it } or
-                            set(offscreenPageLimit) { offscreenPageLimit = it }
-                        }
+                    val ambients = +compositionReference()
+                    emitComponent(0, ::ComposeViewPager) { f ->
+                        set(pagerLayoutParams) { f.layoutParams = it } or
+                        set(ambients) { f.reference = it } or
+                        set(composeTab) { f.children = it } or
+                        set({ titles.size }) { f.getCount = it } or
+                        set({ position: Int -> titles[position] }) { f.getPageTitle = it } or
+                        set(pagerRef) { f.ref = it } or
+                        set(offscreenPageLimit) { offscreenPageLimit = it }
                     }
                 })
             }
diff --git a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/CompositionContextExtensions.kt b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/CompositionContextExtensions.kt
index ba7b2b1..361bd89 100644
--- a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/CompositionContextExtensions.kt
+++ b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/CompositionContextExtensions.kt
@@ -92,11 +92,4 @@
 //    set(el, children) { this.children = it }
 //    compose()
 //    end()
-//}
-//
-//
-//fun CompositionContext.portal(children: (Ambient.Reference) -> Unit) {
-//    emitComponent({ Ambient.Portal() }) {
-//        set(it, children) { this.children = it }
-//    }
 //}
\ No newline at end of file
diff --git a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/FragmentView.kt b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/FragmentView.kt
index 2102b4f..678415f 100644
--- a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/FragmentView.kt
+++ b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/FragmentView.kt
@@ -13,11 +13,10 @@
     with(composer) {
         // NOTE(lmr): if we use R4aContext as ambient reference we can probably get rid of this component
         // entirely
-        portal(0) { ref ->
-            emitView(0, { FragmentView(it, construct, id, ref) }) {
-                set(layoutParams) { this.layoutParams = it }
-                set(id) { this.id = it }
-            }
+        val ref = +compositionReference()
+        emitView(0, { FragmentView(it, construct, id, ref) }) {
+            set(layoutParams) { this.layoutParams = it }
+            set(id) { this.id = it }
         }
     }
 }
@@ -26,7 +25,7 @@
         context: Context,
         construct: () -> Fragment,
         thisId: Int,
-        reference: Ambient.Reference?
+        reference: CompositionReference?
 ) : FrameLayout(context) {
     private var fragmentManager = CompositionContext.current.getAmbient(Ambients.FragmentManager)
     init {
@@ -46,15 +45,15 @@
 }
 
 
-internal fun View.setAmbientReference(ref: Ambient.Reference) {
+internal fun View.setAmbientReference(ref: CompositionReference) {
     setTag(R.id.ambient_reference_tag_key, ref)
 }
 
-internal fun View.findAmbientReference(): Ambient.Reference? {
+internal fun View.findAmbientReference(): CompositionReference? {
     var node: View? = this
 
     while (node != null) {
-        val value = node.getTag(R.id.ambient_reference_tag_key) as? Ambient.Reference
+        val value = node.getTag(R.id.ambient_reference_tag_key) as? CompositionReference
         if (value != null) return value
         node = node.parent as? View
     }
diff --git a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/MyNavHostFragment.kt b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/MyNavHostFragment.kt
index cce5a86..1959622 100644
--- a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/MyNavHostFragment.kt
+++ b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/adapters/MyNavHostFragment.kt
@@ -44,7 +44,7 @@
 class MyNavHostFragment : Fragment(), NavHost {
 
     var controller: NavController? = null
-    var reference: Ambient.Reference? = null
+    var reference: CompositionReference? = null
 
     // State that will be saved and restored
     private var mDefaultNavHost: Boolean = false
diff --git a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HeaderFooterListAdapter.kt b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HeaderFooterListAdapter.kt
index 43a17b9..45d8218 100644
--- a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HeaderFooterListAdapter.kt
+++ b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HeaderFooterListAdapter.kt
@@ -63,7 +63,7 @@
 
     // TODO(lmr): we end up requiring a reference here, but if we move to an Ambient via Context model, we
     // might be able to get rid of it.
-    lateinit var reference: Ambient.Reference
+    lateinit var reference: CompositionReference
 
     var headerCount: Int = 0
         set(value) {
diff --git a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HeaderFooterPagedListAdapter.kt b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HeaderFooterPagedListAdapter.kt
index 89c15b9..27d87a3 100644
--- a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HeaderFooterPagedListAdapter.kt
+++ b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HeaderFooterPagedListAdapter.kt
@@ -49,7 +49,7 @@
         const val TYPE_FOOTER = 3
     }
 
-    lateinit var reference: Ambient.Reference
+    lateinit var reference: CompositionReference
 
     var headerCount: Int = 0
         set(value) {
diff --git a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HomogeneousList.kt b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HomogeneousList.kt
index ceb1ec9..4aed803 100644
--- a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HomogeneousList.kt
+++ b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HomogeneousList.kt
@@ -83,28 +83,27 @@
     @Composable
     operator fun invoke() {
         with(composer) {
-            portal(0) { ref ->
-                adapter.reference = ref
-                // NOTE(lmr): Realistically, we should call notifyDataSetChanged() on every compose. Otherwise, there
-                // may be updates that some of the items of the recycler view. This ideally should be cheap if things
-                // are working correctly, as it will essentially call "recompose()" on only the items that are in the
-                // window which is what we want.
-                adapter.notifyItemRangeChanged(0, adapter.itemCount)
-                emitView(0, ::RecyclerView) {
-                    set(adapter) { adapter = it }
-                    set(layoutManager) { layoutManager = it }
-                    set(paddingTop) { setPaddingTop(it) }
-                    val layoutParams = layoutParams
-                    if (layoutParams != null) {
-                        set(layoutParams) { this.layoutParams = it }
-                    }
-                    // TODO(lmr): this is problematic with making components composable. I think this
-                    // goes away if we make this a View instead of a Component, but handling this differently
-                    // is something to consider.
-                    val backgroundColor = backgroundColor
-                    if (backgroundColor != null) {
-                        set(backgroundColor) { this.setBackgroundColor(it) }
-                    }
+            val ref = +compositionReference()
+            adapter.reference = ref
+            // NOTE(lmr): Realistically, we should call notifyDataSetChanged() on every compose. Otherwise, there
+            // may be updates that some of the items of the recycler view. This ideally should be cheap if things
+            // are working correctly, as it will essentially call "recompose()" on only the items that are in the
+            // window which is what we want.
+            adapter.notifyItemRangeChanged(0, adapter.itemCount)
+            emitView(0, ::RecyclerView) {
+                set(adapter) { adapter = it }
+                set(layoutManager) { layoutManager = it }
+                set(paddingTop) { setPaddingTop(it) }
+                val layoutParams = layoutParams
+                if (layoutParams != null) {
+                    set(layoutParams) { this.layoutParams = it }
+                }
+                // TODO(lmr): this is problematic with making components composable. I think this
+                // goes away if we make this a View instead of a Component, but handling this differently
+                // is something to consider.
+                val backgroundColor = backgroundColor
+                if (backgroundColor != null) {
+                    set(backgroundColor) { this.setBackgroundColor(it) }
                 }
             }
         }
diff --git a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HomogeneousPagedList.kt b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HomogeneousPagedList.kt
index 9305fca..77c2ee2 100644
--- a/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HomogeneousPagedList.kt
+++ b/compose/r4a-runtime/testData/projects/ExplorerApp/common/src/main/java/com/google/r4a/examples/explorerapp/common/components/HomogeneousPagedList.kt
@@ -81,22 +81,21 @@
     @Composable
     operator fun invoke() {
         with(composer) {
-            portal(0) { ref ->
-                adapter.reference = ref
-                emitView(0, ::RecyclerView) {
-                    set(true) { isNestedScrollingEnabled = it }
-                    set(adapter) { adapter = it }
-                    set(layoutManager) { layoutManager = it }
-                    set(paddingTop) { setPaddingTop(it) }
+            val ref = +compositionReference()
+            adapter.reference = ref
+            emitView(0, ::RecyclerView) {
+                set(true) { isNestedScrollingEnabled = it }
+                set(adapter) { adapter = it }
+                set(layoutManager) { layoutManager = it }
+                set(paddingTop) { setPaddingTop(it) }
 //                    set(it, true) { setHasFixedSize(it) }
-                    val layoutParams = layoutParams
-                    if (layoutParams != null) {
-                        set(layoutParams) { this.layoutParams = it }
-                    }
-                    val backgroundColor = backgroundColor
-                    if (backgroundColor != null) {
-                        set(backgroundColor) { this.setBackgroundColor(it) }
-                    }
+                val layoutParams = layoutParams
+                if (layoutParams != null) {
+                    set(layoutParams) { this.layoutParams = it }
+                }
+                val backgroundColor = backgroundColor
+                if (backgroundColor != null) {
+                    set(backgroundColor) { this.setBackgroundColor(it) }
                 }
             }
         }
diff --git a/compose/runtime/src/main/java/com/google/r4a/Ambient.kt b/compose/runtime/src/main/java/com/google/r4a/Ambient.kt
index e32018c..9b2deb0 100644
--- a/compose/runtime/src/main/java/com/google/r4a/Ambient.kt
+++ b/compose/runtime/src/main/java/com/google/r4a/Ambient.kt
@@ -50,21 +50,4 @@
 
         val ambient = this@Ambient
     }
-
-    interface Reference {
-        fun <T> getAmbient(key: Ambient<T>): T
-        fun invalidate()
-        fun <T> invalidateConsumers(key: Ambient<T>)
-        fun <N> registerComposer(composer: Composer<N>)
-    }
-
-    class Portal(
-        @Children
-        var children: (ref: Reference) -> Unit
-    ) : Component() {
-
-        override fun compose() {
-            children(composer.composer.buildReference())
-        }
-    }
 }
diff --git a/compose/runtime/src/main/java/com/google/r4a/Composer.kt b/compose/runtime/src/main/java/com/google/r4a/Composer.kt
index f4efacf..b7ea62d 100644
--- a/compose/runtime/src/main/java/com/google/r4a/Composer.kt
+++ b/compose/runtime/src/main/java/com/google/r4a/Composer.kt
@@ -190,7 +190,7 @@
     private val insertedParents = Stack<Recomposable>()
     private val insertedProviders = Stack<Ambient<*>.Provider>()
     private val invalidateStack = Stack<RecomposeScope>()
-    internal var ambientReference: Ambient.Reference? = null
+    internal var ambientReference: CompositionReference? = null
 
     // Temporary to allow staged changes. This will move into a sub-object that represents an active
     // composition created by startRoot() and recomposeComponentRange()
@@ -454,14 +454,13 @@
     }
 
     /**
-     * Create or use a memoized `Ambient.Reference` instance at this position in the slot table.
-     * Used to implement Ambient.Portal.
+     * Create or use a memoized `CompositionReference` instance at this position in the slot table.
      */
-    fun buildReference(): Ambient.Reference {
+    fun buildReference(): CompositionReference {
         startGroup(reference)
 
         // NOTE(lmr): VERY important to call nextValue() here instead of nextSlot()
-        var ref = nextValue() as? Ambient.Reference
+        var ref = nextValue() as? CompositionReference
         if (ref != null && !inserting) {
             skipValue()
         } else {
@@ -581,7 +580,7 @@
                         sentinel === reference -> {
                             val element = slots.get(index + 1)
                             if (element is CompositionLifecycleObserverHolder) {
-                                val subElement = element.instance as Ambient.Reference
+                                val subElement = element.instance as CompositionReference
                                 subElement.invalidateConsumers(key)
                             }
                         }
@@ -1222,7 +1221,7 @@
         }
     }
 
-    private inner class AmbientReferenceImpl(val scope: RecomposeScope) : Ambient.Reference,
+    private inner class AmbientReferenceImpl(val scope: RecomposeScope) : CompositionReference,
         CompositionLifecycleObserver {
 
         val composers = mutableSetOf<Composer<*>>()
diff --git a/compose/runtime/src/main/java/com/google/r4a/ComposerCompositionContext.kt b/compose/runtime/src/main/java/com/google/r4a/ComposerCompositionContext.kt
index d8e8889..1fce9a5 100644
--- a/compose/runtime/src/main/java/com/google/r4a/ComposerCompositionContext.kt
+++ b/compose/runtime/src/main/java/com/google/r4a/ComposerCompositionContext.kt
@@ -8,20 +8,20 @@
     private val rootComponent: Component
 ) : CompositionContext(), Recomposer {
     companion object {
-        val factory: Function4<Context, Any, Component, Ambient.Reference?, CompositionContext>
+        val factory: Function4<Context, Any, Component, CompositionReference?, CompositionContext>
                 by lazy {
                     object : Function4<
                             Context,
                             Any,
                             Component,
-                            Ambient.Reference?,
+                            CompositionReference?,
                             CompositionContext
                             > {
                         override fun invoke(
                             context: Context,
                             root: Any,
                             component: Component,
-                            ambientReference: Ambient.Reference?
+                            ambientReference: CompositionReference?
                         ): CompositionContext {
                             val result = ComposerCompositionContext(root, component)
                             result.context = context
diff --git a/compose/runtime/src/main/java/com/google/r4a/CompositionContext.kt b/compose/runtime/src/main/java/com/google/r4a/CompositionContext.kt
index 0a5f1d9..3082475 100644
--- a/compose/runtime/src/main/java/com/google/r4a/CompositionContext.kt
+++ b/compose/runtime/src/main/java/com/google/r4a/CompositionContext.kt
@@ -12,7 +12,7 @@
         private val EMITTABLE_ROOT_COMPONENT = WeakHashMap<Emittable, Component>()
         private val COMPONENTS_TO_CONTEXT = WeakHashMap<Component, CompositionContext>()
 
-        val factory: Function4<Context, Any, Component, Ambient.Reference?, CompositionContext>
+        val factory: Function4<Context, Any, Component, CompositionReference?, CompositionContext>
             get() = ComposerCompositionContext.factory
 
         var current: CompositionContext = EmptyCompositionContext()
@@ -21,7 +21,7 @@
             context: Context,
             group: Any,
             component: Component,
-            reference: Ambient.Reference?
+            reference: CompositionReference?
         ): CompositionContext {
             val cc = factory(context, group, component, reference)
             when (group) {
@@ -63,14 +63,14 @@
 
         fun disposeComposition(
             container: ViewGroup,
-            @Suppress("UNUSED_PARAMETER") parent: Ambient.Reference? = null
+            @Suppress("UNUSED_PARAMETER") parent: CompositionReference? = null
         ) {
             container.setTag(TAG_ROOT_COMPONENT, null)
         }
 
         fun disposeComposition(
             container: Emittable,
-            @Suppress("UNUSED_PARAMETER") parent: Ambient.Reference? = null
+            @Suppress("UNUSED_PARAMETER") parent: CompositionReference? = null
         ) {
             // TODO(lmr): clear the ambient reference?
             EMITTABLE_ROOT_COMPONENT.remove(container)
@@ -231,13 +231,3 @@
     { key.Consumer(children) },
     { consumer -> update(children) { consumer.children = it } }
 )
-
-@Suppress("NOTHING_TO_INLINE")
-inline fun ViewComposition.portal(
-    location: Int,
-    noinline children: @Composable() (Ambient.Reference) -> Unit
-) = emitComponent(
-    location,
-    { Ambient.Portal(children) },
-    { portal -> update(children) { portal.children = it } }
-)
diff --git a/compose/runtime/src/main/java/com/google/r4a/CompositionReference.kt b/compose/runtime/src/main/java/com/google/r4a/CompositionReference.kt
new file mode 100644
index 0000000..a8b5b84
--- /dev/null
+++ b/compose/runtime/src/main/java/com/google/r4a/CompositionReference.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2019 The Android Open Source Project
+ *
+ * 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 com.google.r4a
+
+interface CompositionReference {
+    fun <T> getAmbient(key: Ambient<T>): T
+    fun invalidate()
+    fun <T> invalidateConsumers(key: Ambient<T>)
+    fun <N> registerComposer(composer: Composer<N>)
+}
\ No newline at end of file
diff --git a/compose/runtime/src/main/java/com/google/r4a/Effects.kt b/compose/runtime/src/main/java/com/google/r4a/Effects.kt
index 2f5bbaa..e741978 100644
--- a/compose/runtime/src/main/java/com/google/r4a/Effects.kt
+++ b/compose/runtime/src/main/java/com/google/r4a/Effects.kt
@@ -702,6 +702,15 @@
 }
 
 /**
+ * An Effect to construct a CompositionReference at the current point of composition. This can be used
+ * to run a separate composition in the context of the current one, preserving ambients and propagating
+ * invalidations.
+ */
+fun compositionReference() = effectOf<CompositionReference> {
+    context.buildReference()
+}
+
+/**
  * IMPORTANT:
  * This global operator is TEMPORARY, and should be removed whenever an answer for contextual composers is reached. At that time, the
  * unaryPlus operator on the composer itself is the one that should be used.
diff --git a/compose/runtime/src/main/java/com/google/r4a/R4a.kt b/compose/runtime/src/main/java/com/google/r4a/R4a.kt
index f8d284a..bde9291 100644
--- a/compose/runtime/src/main/java/com/google/r4a/R4a.kt
+++ b/compose/runtime/src/main/java/com/google/r4a/R4a.kt
@@ -20,7 +20,7 @@
 
     fun composeInto(
         container: ViewGroup,
-        parent: Ambient.Reference? = null,
+        parent: CompositionReference? = null,
         composable: @Composable() () -> Unit
     ) {
         var root = CompositionContext.getRootComponent(container) as? Root
@@ -36,7 +36,7 @@
         }
     }
 
-    fun disposeComposition(container: ViewGroup, parent: Ambient.Reference? = null) {
+    fun disposeComposition(container: ViewGroup, parent: CompositionReference? = null) {
         // temporary easy way to call correct lifecycles on everything
         composeInto(container, parent) { }
         CompositionContext.disposeComposition(container, parent)
@@ -45,7 +45,7 @@
     fun composeInto(
         container: Emittable,
         context: Context,
-        parent: Ambient.Reference? = null,
+        parent: CompositionReference? = null,
         composable: @Composable() () -> Unit
     ) {
         var root = CompositionContext.getRootComponent(container) as? Root
@@ -64,7 +64,7 @@
     fun disposeComposition(
         container: Emittable,
         context: Context,
-        parent: Ambient.Reference? = null
+        parent: CompositionReference? = null
     ) {
         // temporary easy way to call correct lifecycles on everything
         composeInto(container, context, parent) {}
diff --git a/ui/framework/src/main/java/androidx/ui/core/Layout.kt b/ui/framework/src/main/java/androidx/ui/core/Layout.kt
index 5d85238..f7a7f8d 100644
--- a/ui/framework/src/main/java/androidx/ui/core/Layout.kt
+++ b/ui/framework/src/main/java/androidx/ui/core/Layout.kt
@@ -22,6 +22,7 @@
 import com.google.r4a.R4a
 import com.google.r4a.ambient
 import com.google.r4a.composer
+import com.google.r4a.compositionReference
 import com.google.r4a.memo
 import com.google.r4a.onCommit
 import com.google.r4a.unaryPlus
@@ -413,10 +414,8 @@
  */
 @Composable
 fun WithConstraints(@Children children: (Constraints) -> Unit) {
-    var ambients: Ambient.Reference? = null
-    <Ambient.Portal> value ->
-        ambients = value
-    </Ambient.Portal>
+    val ref = +compositionReference()
+    val context = +ambient(ContextAmbient)
 
     <Layout
         layoutBlock = { _, constraints ->
@@ -424,8 +423,8 @@
             // Start subcomposition from the current node.
             R4a.composeInto(
                 root,
-                ambients!!.getAmbient(ContextAmbient),
-                ambients
+                context,
+                ref
             ) {
                 <children p1=constraints />
             }
diff --git a/ui/framework/src/main/java/androidx/ui/core/Wrapper.kt b/ui/framework/src/main/java/androidx/ui/core/Wrapper.kt
index be8fd68b..1932174f 100644
--- a/ui/framework/src/main/java/androidx/ui/core/Wrapper.kt
+++ b/ui/framework/src/main/java/androidx/ui/core/Wrapper.kt
@@ -23,6 +23,7 @@
 import com.google.r4a.R4a
 import com.google.r4a.ambient
 import com.google.r4a.composer
+import com.google.r4a.compositionReference
 import com.google.r4a.effectOf
 import com.google.r4a.memo
 import com.google.r4a.unaryPlus
@@ -32,17 +33,16 @@
     val rootRef = +memo { Ref<AndroidCraneView>() }
 
     <AndroidCraneView ref=rootRef>
-        <Ambient.Portal> reference ->
-            val rootLayoutNode = rootRef.value?.root ?: error("Failed to create root platform view")
-            val context = rootRef.value?.context ?: composer.composer.context
-            R4a.composeInto(container = rootLayoutNode, context = context, parent = reference) {
-                <ContextAmbient.Provider value=context>
-                    <DensityAmbient.Provider value=Density(context)>
-                        <children />
-                    </DensityAmbient.Provider>
-                </ContextAmbient.Provider>
-            }
-        </Ambient.Portal>
+        val reference = +compositionReference()
+        val rootLayoutNode = rootRef.value?.root ?: error("Failed to create root platform view")
+        val context = rootRef.value?.context ?: composer.composer.context
+        R4a.composeInto(container = rootLayoutNode, context = context, parent = reference) {
+            <ContextAmbient.Provider value=context>
+                <DensityAmbient.Provider value=Density(context)>
+                    <children />
+                </DensityAmbient.Provider>
+            </ContextAmbient.Provider>
+        }
     </AndroidCraneView>
 }