/* * Copyright 2016 Google Inc. All Rights Reserved. * * 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.turbine.binder.env; import com.google.common.base.Joiner; import com.google.common.collect.ImmutableMap; import com.google.turbine.binder.sym.Symbol; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.Map; import org.jspecify.nullness.Nullable; /** * An env that permits an analysis pass to access information about symbols from the current pass, * recursively. Cycles are detected, and result in an {@link LazyBindingError} being thrown. * *

This is used primarily for resolving the supertype hierarchy in {@code HierarchyBinder}. The * supertype hierarchy forms a directed acyclic graph, and {@code HierarchyBinder} needs to process * classes in a topological sort order of that graph. Unfortuntately, we can't produce a suitable * sort order until the graph exists. * * @param the interface type of the bound node {@link V}, shared by any underlying environments. * @param a specific implementation of {@code T}. For example, during hierarchy binding {@code * SourceHeaderBoundClass} nodes are being completed from the sources being compiled, and the * analysis of a given symbol may require looking up {@code HeaderBoundClass} nodes that will * either be backed by other {@code SourceHeaderBoundClass} nodes or {@code BytecodeBoundClass} * nodes. So the phase uses an {@code LazyEnv}. */ public class LazyEnv implements Env { /** The list of symbols that are currently being processed, used to check for cycles. */ private final LinkedHashSet seen = new LinkedHashSet<>(); /** Lazy value providers for the symbols in the environment. */ private final ImmutableMap> completers; /** Values that have already been computed. */ private final Map cache = new LinkedHashMap<>(); /** An underlying env of already-computed {@code T}s that can be queried during completion. */ private final Env rec; public LazyEnv(ImmutableMap> completers, Env base) { this.completers = completers; this.rec = CompoundEnv.of(base).append(this); } @Override public @Nullable V get(S sym) { V v = cache.get(sym); if (v != null) { return v; } Completer completer = completers.get(sym); if (completer != null) { if (!seen.add(sym)) { throw new LazyBindingError(Joiner.on(" -> ").join(seen) + " -> " + sym); } v = completer.complete(rec, sym); seen.remove(sym); cache.put(sym, v); return v; } return null; } /** A lazy value provider which is given access to the current environment. */ public interface Completer { /** Provides the value for the given symbol in the current environment. */ @Nullable V complete(Env env, S k); } /** Indicates that a completer tried to complete itself, possibly transitively. */ public static class LazyBindingError extends Error { public LazyBindingError(String message) { super(message); } } }