duke@1: /* xdono@117: * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved. duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@1: * duke@1: * This code is free software; you can redistribute it and/or modify it duke@1: * under the terms of the GNU General Public License version 2 only, as duke@1: * published by the Free Software Foundation. Sun designates this duke@1: * particular file as subject to the "Classpath" exception as provided duke@1: * by Sun in the LICENSE file that accompanied this code. duke@1: * duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@1: * version 2 for more details (a copy is included in the LICENSE file that duke@1: * accompanied this code). duke@1: * duke@1: * You should have received a copy of the GNU General Public License version duke@1: * 2 along with this work; if not, write to the Free Software Foundation, duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@1: * duke@1: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@1: * CA 95054 USA or visit www.sun.com if you need additional information or duke@1: * have any questions. duke@1: */ duke@1: duke@1: package com.sun.tools.javac.util; duke@1: duke@1: import java.util.*; duke@1: duke@1: /** duke@1: * Support for an abstract context, modelled loosely after ThreadLocal duke@1: * but using a user-provided context instead of the current thread. duke@1: * duke@1: *

Within the compiler, a single Context is used for each duke@1: * invocation of the compiler. The context is then used to ensure a duke@1: * single copy of each compiler phase exists per compiler invocation. duke@1: * duke@1: *

The context can be used to assist in extending the compiler by duke@1: * extending its components. To do that, the extended component must duke@1: * be registered before the base component. We break initialization duke@1: * cycles by (1) registering a factory for the component rather than duke@1: * the component itself, and (2) a convention for a pattern of usage duke@1: * in which each base component registers itself by calling an duke@1: * instance method that is overridden in extended components. A base duke@1: * phase supporting extension would look something like this: duke@1: * duke@1: *

duke@1:  * public class Phase {
duke@1:  *     protected static final Context.Key phaseKey =
duke@1:  *         new Context.Key();
duke@1:  *
duke@1:  *     public static Phase instance(Context context) {
duke@1:  *         Phase instance = context.get(phaseKey);
duke@1:  *         if (instance == null)
duke@1:  *             // the phase has not been overridden
duke@1:  *             instance = new Phase(context);
duke@1:  *         return instance;
duke@1:  *     }
duke@1:  *
duke@1:  *     protected Phase(Context context) {
duke@1:  *         context.put(phaseKey, this);
duke@1:  *         // other intitialization follows...
duke@1:  *     }
duke@1:  * }
duke@1:  * 
duke@1: * duke@1: *

In the compiler, we simply use Phase.instance(context) to get duke@1: * the reference to the phase. But in extensions of the compiler, we duke@1: * must register extensions of the phases to replace the base phase, duke@1: * and this must be done before any reference to the phase is accessed duke@1: * using Phase.instance(). An extended phase might be declared thus: duke@1: * duke@1: *

duke@1:  * public class NewPhase extends Phase {
duke@1:  *     protected NewPhase(Context context) {
duke@1:  *         super(context);
duke@1:  *     }
duke@1:  *     public static void preRegister(final Context context) {
duke@1:  *         context.put(phaseKey, new Context.Factory() {
duke@1:  *             public Phase make() {
duke@1:  *                 return new NewPhase(context);
duke@1:  *             }
duke@1:  *         });
duke@1:  *     }
duke@1:  * }
duke@1:  * 
duke@1: * duke@1: *

And is registered early in the extended compiler like this duke@1: * duke@1: *

duke@1:  *     NewPhase.preRegister(context);
duke@1:  * 
duke@1: * duke@1: *

This is NOT part of any API supported by Sun Microsystems. If duke@1: * you write code that depends on this, you do so at your own risk. duke@1: * This code and its internal interfaces are subject to change or duke@1: * deletion without notice. duke@1: */ duke@1: public class Context { duke@1: /** The client creates an instance of this class for each key. duke@1: */ duke@1: public static class Key { duke@1: // note: we inherit identity equality from Object. duke@1: } duke@1: duke@1: /** duke@1: * The client can register a factory for lazy creation of the duke@1: * instance. duke@1: */ duke@1: public static interface Factory { duke@1: T make(); duke@1: }; duke@1: duke@1: /** duke@1: * The underlying map storing the data. duke@1: * We maintain the invariant that this table contains only duke@1: * mappings of the form duke@1: * Key -> T or Key -> Factory */ duke@1: private Map ht = new HashMap(); duke@1: duke@1: /** Set the factory for the key in this context. */ duke@1: public void put(Key key, Factory fac) { duke@1: checkState(ht); duke@1: Object old = ht.put(key, fac); duke@1: if (old != null) duke@1: throw new AssertionError("duplicate context value"); duke@1: } duke@1: duke@1: /** Set the value for the key in this context. */ duke@1: public void put(Key key, T data) { duke@1: if (data instanceof Factory) duke@1: throw new AssertionError("T extends Context.Factory"); duke@1: checkState(ht); duke@1: Object old = ht.put(key, data); duke@1: if (old != null && !(old instanceof Factory) && old != data && data != null) duke@1: throw new AssertionError("duplicate context value"); duke@1: } duke@1: duke@1: /** Get the value for the key in this context. */ duke@1: public T get(Key key) { duke@1: checkState(ht); duke@1: Object o = ht.get(key); duke@1: if (o instanceof Factory) { duke@1: Factory fac = (Factory)o; duke@1: o = fac.make(); duke@1: if (o instanceof Factory) duke@1: throw new AssertionError("T extends Context.Factory"); duke@1: assert ht.get(key) == o; duke@1: } duke@1: duke@1: /* The following cast can't fail unless there was duke@1: * cheating elsewhere, because of the invariant on ht. duke@1: * Since we found a key of type Key, the value must duke@1: * be of type T. duke@1: */ duke@1: return Context.uncheckedCast(o); duke@1: } duke@1: duke@1: public Context() {} duke@1: duke@1: private Map, Key> kt = new HashMap, Key>(); duke@1: duke@1: private Key key(Class clss) { duke@1: checkState(kt); duke@1: Key k = uncheckedCast(kt.get(clss)); duke@1: if (k == null) { duke@1: k = new Key(); duke@1: kt.put(clss, k); duke@1: } duke@1: return k; duke@1: } duke@1: duke@1: public T get(Class clazz) { duke@1: return get(key(clazz)); duke@1: } duke@1: duke@1: public void put(Class clazz, T data) { duke@1: put(key(clazz), data); duke@1: } duke@1: public void put(Class clazz, Factory fac) { duke@1: put(key(clazz), fac); duke@1: } duke@1: duke@1: /** duke@1: * TODO: This method should be removed and Context should be made type safe. duke@1: * This can be accomplished by using class literals as type tokens. duke@1: */ duke@1: @SuppressWarnings("unchecked") duke@1: private static T uncheckedCast(Object o) { duke@1: return (T)o; duke@1: } duke@1: duke@1: public void dump() { duke@1: for (Object value : ht.values()) duke@1: System.err.println(value == null ? null : value.getClass()); duke@1: } duke@1: duke@1: public void clear() { duke@1: ht = null; duke@1: kt = null; duke@1: } duke@1: duke@1: private static void checkState(Map t) { duke@1: if (t == null) duke@1: throw new IllegalStateException(); duke@1: } duke@1: }