1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/classes/com/sun/tools/javac/util/Context.java Sat Dec 01 00:00:00 2007 +0000 1.3 @@ -0,0 +1,208 @@ 1.4 +/* 1.5 + * Copyright 2001-2006 Sun Microsystems, Inc. All Rights Reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. Sun designates this 1.11 + * particular file as subject to the "Classpath" exception as provided 1.12 + * by Sun in the LICENSE file that accompanied this code. 1.13 + * 1.14 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.16 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.17 + * version 2 for more details (a copy is included in the LICENSE file that 1.18 + * accompanied this code). 1.19 + * 1.20 + * You should have received a copy of the GNU General Public License version 1.21 + * 2 along with this work; if not, write to the Free Software Foundation, 1.22 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.23 + * 1.24 + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 1.25 + * CA 95054 USA or visit www.sun.com if you need additional information or 1.26 + * have any questions. 1.27 + */ 1.28 + 1.29 +package com.sun.tools.javac.util; 1.30 + 1.31 +import com.sun.tools.javac.Main; 1.32 +import java.util.*; 1.33 + 1.34 +/** 1.35 + * Support for an abstract context, modelled loosely after ThreadLocal 1.36 + * but using a user-provided context instead of the current thread. 1.37 + * 1.38 + * <p>Within the compiler, a single Context is used for each 1.39 + * invocation of the compiler. The context is then used to ensure a 1.40 + * single copy of each compiler phase exists per compiler invocation. 1.41 + * 1.42 + * <p>The context can be used to assist in extending the compiler by 1.43 + * extending its components. To do that, the extended component must 1.44 + * be registered before the base component. We break initialization 1.45 + * cycles by (1) registering a factory for the component rather than 1.46 + * the component itself, and (2) a convention for a pattern of usage 1.47 + * in which each base component registers itself by calling an 1.48 + * instance method that is overridden in extended components. A base 1.49 + * phase supporting extension would look something like this: 1.50 + * 1.51 + * <p><pre> 1.52 + * public class Phase { 1.53 + * protected static final Context.Key<Phase> phaseKey = 1.54 + * new Context.Key<Phase>(); 1.55 + * 1.56 + * public static Phase instance(Context context) { 1.57 + * Phase instance = context.get(phaseKey); 1.58 + * if (instance == null) 1.59 + * // the phase has not been overridden 1.60 + * instance = new Phase(context); 1.61 + * return instance; 1.62 + * } 1.63 + * 1.64 + * protected Phase(Context context) { 1.65 + * context.put(phaseKey, this); 1.66 + * // other intitialization follows... 1.67 + * } 1.68 + * } 1.69 + * </pre> 1.70 + * 1.71 + * <p>In the compiler, we simply use Phase.instance(context) to get 1.72 + * the reference to the phase. But in extensions of the compiler, we 1.73 + * must register extensions of the phases to replace the base phase, 1.74 + * and this must be done before any reference to the phase is accessed 1.75 + * using Phase.instance(). An extended phase might be declared thus: 1.76 + * 1.77 + * <p><pre> 1.78 + * public class NewPhase extends Phase { 1.79 + * protected NewPhase(Context context) { 1.80 + * super(context); 1.81 + * } 1.82 + * public static void preRegister(final Context context) { 1.83 + * context.put(phaseKey, new Context.Factory<Phase>() { 1.84 + * public Phase make() { 1.85 + * return new NewPhase(context); 1.86 + * } 1.87 + * }); 1.88 + * } 1.89 + * } 1.90 + * </pre> 1.91 + * 1.92 + * <p>And is registered early in the extended compiler like this 1.93 + * 1.94 + * <p><pre> 1.95 + * NewPhase.preRegister(context); 1.96 + * </pre> 1.97 + * 1.98 + * <p><b>This is NOT part of any API supported by Sun Microsystems. If 1.99 + * you write code that depends on this, you do so at your own risk. 1.100 + * This code and its internal interfaces are subject to change or 1.101 + * deletion without notice.</b> 1.102 + */ 1.103 +public class Context { 1.104 + /** The client creates an instance of this class for each key. 1.105 + */ 1.106 + public static class Key<T> { 1.107 + // note: we inherit identity equality from Object. 1.108 + } 1.109 + 1.110 + /** 1.111 + * The client can register a factory for lazy creation of the 1.112 + * instance. 1.113 + */ 1.114 + public static interface Factory<T> { 1.115 + T make(); 1.116 + }; 1.117 + 1.118 + /** 1.119 + * The underlying map storing the data. 1.120 + * We maintain the invariant that this table contains only 1.121 + * mappings of the form 1.122 + * Key<T> -> T or Key<T> -> Factory<T> */ 1.123 + private Map<Key,Object> ht = new HashMap<Key,Object>(); 1.124 + 1.125 + /** Set the factory for the key in this context. */ 1.126 + public <T> void put(Key<T> key, Factory<T> fac) { 1.127 + checkState(ht); 1.128 + Object old = ht.put(key, fac); 1.129 + if (old != null) 1.130 + throw new AssertionError("duplicate context value"); 1.131 + } 1.132 + 1.133 + /** Set the value for the key in this context. */ 1.134 + public <T> void put(Key<T> key, T data) { 1.135 + if (data instanceof Factory) 1.136 + throw new AssertionError("T extends Context.Factory"); 1.137 + checkState(ht); 1.138 + Object old = ht.put(key, data); 1.139 + if (old != null && !(old instanceof Factory) && old != data && data != null) 1.140 + throw new AssertionError("duplicate context value"); 1.141 + } 1.142 + 1.143 + /** Get the value for the key in this context. */ 1.144 + public <T> T get(Key<T> key) { 1.145 + checkState(ht); 1.146 + Object o = ht.get(key); 1.147 + if (o instanceof Factory) { 1.148 + Factory fac = (Factory)o; 1.149 + o = fac.make(); 1.150 + if (o instanceof Factory) 1.151 + throw new AssertionError("T extends Context.Factory"); 1.152 + assert ht.get(key) == o; 1.153 + } 1.154 + 1.155 + /* The following cast can't fail unless there was 1.156 + * cheating elsewhere, because of the invariant on ht. 1.157 + * Since we found a key of type Key<T>, the value must 1.158 + * be of type T. 1.159 + */ 1.160 + return Context.<T>uncheckedCast(o); 1.161 + } 1.162 + 1.163 + public Context() {} 1.164 + 1.165 + private Map<Class<?>, Key<?>> kt = new HashMap<Class<?>, Key<?>>(); 1.166 + 1.167 + private <T> Key<T> key(Class<T> clss) { 1.168 + checkState(kt); 1.169 + Key<T> k = uncheckedCast(kt.get(clss)); 1.170 + if (k == null) { 1.171 + k = new Key<T>(); 1.172 + kt.put(clss, k); 1.173 + } 1.174 + return k; 1.175 + } 1.176 + 1.177 + public <T> T get(Class<T> clazz) { 1.178 + return get(key(clazz)); 1.179 + } 1.180 + 1.181 + public <T> void put(Class<T> clazz, T data) { 1.182 + put(key(clazz), data); 1.183 + } 1.184 + public <T> void put(Class<T> clazz, Factory<T> fac) { 1.185 + put(key(clazz), fac); 1.186 + } 1.187 + 1.188 + /** 1.189 + * TODO: This method should be removed and Context should be made type safe. 1.190 + * This can be accomplished by using class literals as type tokens. 1.191 + */ 1.192 + @SuppressWarnings("unchecked") 1.193 + private static <T> T uncheckedCast(Object o) { 1.194 + return (T)o; 1.195 + } 1.196 + 1.197 + public void dump() { 1.198 + for (Object value : ht.values()) 1.199 + System.err.println(value == null ? null : value.getClass()); 1.200 + } 1.201 + 1.202 + public void clear() { 1.203 + ht = null; 1.204 + kt = null; 1.205 + } 1.206 + 1.207 + private static void checkState(Map<?,?> t) { 1.208 + if (t == null) 1.209 + throw new IllegalStateException(); 1.210 + } 1.211 +}