1 /*
   2  * Copyright (c) 2001, 2014, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package com.sun.tools.javac.util;
  27 
  28 import java.util.*;
  29 
  30 /**
  31  * Support for an abstract context, modelled loosely after ThreadLocal
  32  * but using a user-provided context instead of the current thread.
  33  *
  34  * <p>Within the compiler, a single Context is used for each
  35  * invocation of the compiler.  The context is then used to ensure a
  36  * single copy of each compiler phase exists per compiler invocation.
  37  *
  38  * <p>The context can be used to assist in extending the compiler by
  39  * extending its components.  To do that, the extended component must
  40  * be registered before the base component.  We break initialization
  41  * cycles by (1) registering a factory for the component rather than
  42  * the component itself, and (2) a convention for a pattern of usage
  43  * in which each base component registers itself by calling an
  44  * instance method that is overridden in extended components.  A base
  45  * phase supporting extension would look something like this:
  46  *
  47  * <pre>{@code
  48  * public class Phase {
  49  *     protected static final Context.Key<Phase> phaseKey =
  50  *         new Context.Key<Phase>();
  51  *
  52  *     public static Phase instance(Context context) {
  53  *         Phase instance = context.get(phaseKey);
  54  *         if (instance == null)
  55  *             // the phase has not been overridden
  56  *             instance = new Phase(context);
  57  *         return instance;
  58  *     }
  59  *
  60  *     protected Phase(Context context) {
  61  *         context.put(phaseKey, this);
  62  *         // other initialization follows...
  63  *     }
  64  * }
  65  * }</pre>
  66  *
  67  * <p>In the compiler, we simply use Phase.instance(context) to get
  68  * the reference to the phase.  But in extensions of the compiler, we
  69  * must register extensions of the phases to replace the base phase,
  70  * and this must be done before any reference to the phase is accessed
  71  * using Phase.instance().  An extended phase might be declared thus:
  72  *
  73  * <pre>{@code
  74  * public class NewPhase extends Phase {
  75  *     protected NewPhase(Context context) {
  76  *         super(context);
  77  *     }
  78  *     public static void preRegister(final Context context) {
  79  *         context.put(phaseKey, new Context.Factory<Phase>() {
  80  *             public Phase make() {
  81  *                 return new NewPhase(context);
  82  *             }
  83  *         });
  84  *     }
  85  * }
  86  * }</pre>
  87  *
  88  * <p>And is registered early in the extended compiler like this
  89  *
  90  * <pre>
  91  *     NewPhase.preRegister(context);
  92  * </pre>
  93  *
  94  *  <p><b>This is NOT part of any supported API.
  95  *  If you write code that depends on this, you do so at your own risk.
  96  *  This code and its internal interfaces are subject to change or
  97  *  deletion without notice.</b>
  98  */
  99 public class Context {
 100     /** The client creates an instance of this class for each key.
 101      */
 102     public static class Key<T> {
 103         // note: we inherit identity equality from Object.
 104     }
 105 
 106     /**
 107      * The client can register a factory for lazy creation of the
 108      * instance.
 109      */
 110     public static interface Factory<T> {
 111         T make(Context c);
 112     }
 113 
 114     /**
 115      * The underlying map storing the data.
 116      * We maintain the invariant that this table contains only
 117      * mappings of the form
 118      * {@literal Key<T> -> T }
 119      * or
 120      * {@literal Key<T> -> Factory<T> }
 121      */
 122     protected final Map<Key<?>,Object> ht = new HashMap<>();
 123 
 124     /** Set the factory for the key in this context. */
 125     public <T> void put(Key<T> key, Factory<T> fac) {
 126         checkState(ht);
 127         Object old = ht.put(key, fac);
 128         if (old != null)
 129             throw new AssertionError("duplicate context value");
 130         checkState(ft);
 131         ft.put(key, fac); // cannot be duplicate if unique in ht
 132     }
 133 
 134     /** Set the value for the key in this context. */
 135     public <T> void put(Key<T> key, T data) {
 136         if (data instanceof Factory<?>)
 137             throw new AssertionError("T extends Context.Factory");
 138         checkState(ht);
 139         Object old = ht.put(key, data);
 140         if (old != null && !(old instanceof Factory<?>) && old != data && data != null)
 141             throw new AssertionError("duplicate context value");
 142     }
 143 
 144     /** Get the value for the key in this context. */
 145     public <T> T get(Key<T> key) {
 146         checkState(ht);
 147         Object o = ht.get(key);
 148         if (o instanceof Factory<?>) {
 149             Factory<?> fac = (Factory<?>)o;
 150             o = fac.make(this);
 151             if (o instanceof Factory<?>)
 152                 throw new AssertionError("T extends Context.Factory");
 153             Assert.check(ht.get(key) == o);
 154         }
 155 
 156         /* The following cast can't fail unless there was
 157          * cheating elsewhere, because of the invariant on ht.
 158          * Since we found a key of type Key<T>, the value must
 159          * be of type T.
 160          */
 161         return Context.uncheckedCast(o);
 162     }
 163 
 164     public Context() {}
 165 
 166     /**
 167      * The table of preregistered factories.
 168      */
 169     private final Map<Key<?>,Factory<?>> ft = new HashMap<>();
 170 
 171     /*
 172      * The key table, providing a unique Key<T> for each Class<T>.
 173      */
 174     private final Map<Class<?>, Key<?>> kt = new HashMap<>();
 175 
 176     protected <T> Key<T> key(Class<T> clss) {
 177         checkState(kt);
 178         Key<T> k = uncheckedCast(kt.get(clss));
 179         if (k == null) {
 180             k = new Key<>();
 181             kt.put(clss, k);
 182         }
 183         return k;
 184     }
 185 
 186     public <T> T get(Class<T> clazz) {
 187         return get(key(clazz));
 188     }
 189 
 190     public <T> void put(Class<T> clazz, T data) {
 191         put(key(clazz), data);
 192     }
 193     public <T> void put(Class<T> clazz, Factory<T> fac) {
 194         put(key(clazz), fac);
 195     }
 196 
 197     /**
 198      * TODO: This method should be removed and Context should be made type safe.
 199      * This can be accomplished by using class literals as type tokens.
 200      */
 201     @SuppressWarnings("unchecked")
 202     private static <T> T uncheckedCast(Object o) {
 203         return (T)o;
 204     }
 205 
 206     public void dump() {
 207         for (Object value : ht.values())
 208             System.err.println(value == null ? null : value.getClass());
 209     }
 210 
 211     private static void checkState(Map<?,?> t) {
 212         if (t == null)
 213             throw new IllegalStateException();
 214     }
 215 }