Context.java revision 3019:176472b94f2e
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 26package com.sun.tools.javac.util; 27 28import 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 intitialization 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 */ 99public 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.<T>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} 216