/* Copyright (c) Jython Developers */
package org.python.core;
import java.io.Serializable;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReferenceArray;
import org.python.expose.ExposeAsSuperclass;
import org.python.expose.ExposedDelete;
import org.python.expose.ExposedGet;
import org.python.expose.ExposedMethod;
import org.python.expose.ExposedNew;
import org.python.expose.ExposedSet;
import org.python.expose.ExposedType;
import org.python.expose.TypeBuilder;
import org.python.modules._weakref.WeakrefModule;
import org.python.util.Generic;
import com.google.common.collect.MapMaker;
/**
* The Python Type object implementation.
*/
@ExposedType(name = "type", doc = BuiltinDocs.type_doc)
public class PyType extends PyObject implements Serializable {
public static PyType TYPE = fromClass(PyType.class);
/**
* The type's name. builtin types include their fully qualified name, e.g.:
* time.struct_time.
*/
protected String name;
/** __base__, the direct base type or null. */
protected PyType base;
/** __bases__, the base classes. */
protected PyObject[] bases = new PyObject[0];
/** The real, internal __dict__. */
protected PyObject dict;
/** __mro__, the method resolution. order */
protected PyObject[] mro;
/** __flags__, the type's options. */
private long tp_flags;
/**
* The Java Class instances of this type will be represented as, or null if it's
* determined by a base type.
*/
protected Class<?> underlying_class;
/** Whether it's a builtin type. */
protected boolean builtin;
/** Whether new instances of this type can be instantiated */
protected boolean instantiable = true;
/** Whether this type implements descriptor __get/set/delete__ methods. */
boolean hasGet;
boolean hasSet;
boolean hasDelete;
/** Whether this type allows subclassing. */
private boolean isBaseType = true;
/** Whether this type has a __dict__. */
protected boolean needs_userdict;
/** Whether this type has a __weakref__ slot (however all types are weakrefable). */
protected boolean needs_weakref;
/** Whether finalization is required for this type's instances (implements __del__). */
private boolean needs_finalizer;
/** Whether this type's __getattribute__ is object.__getattribute__. */
private volatile boolean usesObjectGetattribute;
/** MethodCacheEntry version tag. */
private volatile Object versionTag = new Object();
/** The number of __slots__ defined. */
private int numSlots;
private transient ReferenceQueue<PyType> subclasses_refq = new ReferenceQueue<PyType>();
private Set<WeakReference<PyType>> subclasses = Generic.set();
/** Global mro cache. */
private static final MethodCache methodCache = new MethodCache();
/** Mapping of Java classes to their PyTypes. */
private static Map<Class<?>, PyType> class_to_type;
private static Set<PyType> exposedTypes;
/** Mapping of Java classes to their TypeBuilders. */
private static Map<Class<?>, TypeBuilder> classToBuilder;
protected PyType(PyType subtype) {
super(subtype);
}
private PyType() {
}
/**
* Creates the PyType instance for type itself. The argument just exists to make the constructor
* distinct.
*/
private PyType(boolean ignored) {
super(ignored);
}
@ExposedNew
public static PyObject type___new__(PyNewWrapper new_, boolean init, PyType subtype,
PyObject[] args, String[] keywords) {
// Special case: type(x) should return x.getType()
if (args.length == 1 && keywords.length == 0) {
PyObject obj = args[0];
PyType objType = obj.getType();
// special case for PyStringMap so that it types as a dict
PyType psmType = PyType.fromClass(PyStringMap.class);
if (objType == psmType) {
return PyDictionary.TYPE;
}
return objType;
}
// If that didn't trigger, we need 3 arguments. but ArgParser below may give a msg
// saying type() needs exactly 3.
if (args.length + keywords.length != 3) {
throw Py.TypeError("type() takes 1 or 3 arguments");
}
ArgParser ap = new ArgParser("type()", args, keywords, "name", "bases", "dict");
String name = ap.getString(0);
PyTuple bases = (PyTuple)ap.getPyObjectByType(1, PyTuple.TYPE);
PyObject dict = ap.getPyObject(2);
if (!(dict instanceof PyDictionary || dict instanceof PyStringMap)) {
throw Py.TypeError("type(): argument 3 must be dict, not " + dict.getType());
}
return newType(new_, subtype, name, bases, dict);
}
public static PyObject newType(PyNewWrapper new_, PyType metatype, String name, PyTuple bases,
PyObject dict) {
PyObject[] tmpBases = bases.getArray();
PyType winner = findMostDerivedMetatype(tmpBases, metatype);
if (winner != metatype) {
PyObject winnerNew = winner.lookup("__new__");
if (winnerNew != null && winnerNew != new_) {
return invokeNew(winnerNew, winner, false,
new PyObject[] {new PyString(name), bases, dict}, Py.NoKeywords);
}
metatype = winner;
}
// Use PyType as the metaclass for Python subclasses of Java classes rather than
// PyJavaType. Using PyJavaType as metaclass exposes the java.lang.Object methods
// on the type, which doesn't make sense for python subclasses.
if (metatype == PyType.fromClass(Class.class)) {
metatype = TYPE;
}
PyType type;
if (new_.for_type == metatype) {
// XXX: set metatype
type = new PyType();
} else {
type = new PyTypeDerived(metatype);
}
if (dict instanceof PyStringMap) {
dict = ((PyStringMap)dict).copy();
} else {
dict = ((PyDictionary)dict).copy();
}
type.name = name;
type.bases = tmpBases.length == 0 ? new PyObject[] {PyObject.TYPE} : tmpBases;
type.dict = dict;
type.tp_flags = Py.TPFLAGS_HEAPTYPE | Py.TPFLAGS_BASETYPE;
// immediately setup the javaProxy if applicable. may modify bases
List<Class<?>> interfaces = Generic.list();
Class<?> baseProxyClass = getJavaLayout(type.bases, interfaces);
type.setupProxy(baseProxyClass, interfaces);
PyType base = type.base = best_base(type.bases);
if (!base.isBaseType) {
throw Py.TypeError(String.format("type '%.100s' is not an acceptable base type",
base.name));
}
type.createAllSlots(!base.needs_userdict, !base.needs_weakref);
type.ensureAttributes();
type.invalidateMethodCache();
for (PyObject cur : type.bases) {
if (cur instanceof PyType)
((PyType)cur).attachSubclass(type);
}
return type;
}
/**
* Create all slots and related descriptors.
*
* @param mayAddDict whether a __dict__ descriptor is allowed on this type
* @param mayAddWeak whether a __weakref__ descriptor is allowed on this type
*/
private void createAllSlots(boolean mayAddDict, boolean mayAddWeak) {
numSlots = base.numSlots;
boolean wantDict = false;
boolean wantWeak = false;
PyObject slots = dict.__finditem__("__slots__");
if (slots == null) {
wantDict = mayAddDict;
wantWeak = mayAddWeak;
} else {
if (slots instanceof PyString) {
slots = new PyTuple(slots);
}
// Check for valid slot names and create them. Handle two special cases
for (PyObject slot : slots.asIterable()) {
String slotName = confirmIdentifier(slot);
if (slotName.equals("__dict__")) {
if (!mayAddDict || wantDict) {
throw Py.TypeError("__dict__ slot disallowed: we already got one");
}
wantDict = true;
} else if (slotName.equals("__weakref__")) {
if (!mayAddWeak || wantWeak) {
throw Py.TypeError("__weakref__ slot disallowed: we already got one");
}
wantWeak = true;
} else {
slotName = mangleName(name, slotName);
if (dict.__finditem__(slotName) == null) {
dict.__setitem__(slotName, new PySlot(this, slotName, numSlots++));
}
}
}
// Secondary bases may provide weakrefs or dict
if (bases.length > 1
&& ((mayAddDict && !wantDict) || (mayAddWeak && !wantWeak))) {
for (PyObject base : bases) {
if (base == this.base) {
// Skip primary base
continue;
}
if (base instanceof PyClass) {
// Classic base class provides both
if (mayAddDict && !wantDict) {
wantDict = true;
}
if (mayAddWeak && !wantWeak) {
wantWeak = true;
}
break;
}
PyType baseType = (PyType)base;
if (mayAddDict && !wantDict && baseType.needs_userdict) {
wantDict = true;
}
if (mayAddWeak && !wantWeak && baseType.needs_weakref) {
wantWeak = true;
}
if ((!mayAddDict || wantDict) && (!mayAddWeak || wantWeak)) {
// Nothing more to check
break;
}
}
}
}
if (wantDict) {
createDictSlot();
}
if (wantWeak) {
createWeakrefSlot();
}
needs_finalizer = lookup_mro("__del__") != null;
}
/**
* Create the __dict__ descriptor.
*/
private void createDictSlot() {
String doc = "dictionary for instance variables (if defined)";
dict.__setitem__("__dict__", new PyDataDescr(this, "__dict__", PyObject.class, doc) {
@Override
public boolean implementsDescrGet() {
return true;
}
@Override
public Object invokeGet(PyObject obj) {
return obj.getDict();
}
@Override
public boolean implementsDescrSet() {
return true;
}
@Override
public void invokeSet(PyObject obj, Object value) {
obj.setDict((PyObject)value);
}
@Override
public boolean implementsDescrDelete() {
return true;
}
@Override
public void invokeDelete(PyObject obj) {
obj.delDict();
}
});
needs_userdict = true;
}
/**
* Create the __weakref__ descriptor.
*/
private void createWeakrefSlot() {
String doc = "list of weak references to the object (if defined)";
dict.__setitem__("__weakref__", new PyDataDescr(this, "__weakref__", PyObject.class, doc) {
private static final String writeMsg =
"attribute '%s' of '%s' objects is not writable";
private void notWritable(PyObject obj) {
throw Py.AttributeError(String.format(writeMsg, "__weakref__",
obj.getType().fastGetName()));
}
@Override
public boolean implementsDescrGet() {
return true;
}
@Override
public Object invokeGet(PyObject obj) {
PyList weakrefs = WeakrefModule.getweakrefs(obj);
switch (weakrefs.size()) {
case 0:
return Py.None;
case 1:
return weakrefs.pyget(0);
default:
return weakrefs;
}
}
@Override
public boolean implementsDescrSet() {
return true;
}
@Override
public void invokeSet(PyObject obj, Object value) {
// XXX: Maybe have PyDataDescr do notWritable() for us
notWritable(obj);
}
@Override
public boolean implementsDescrDelete() {
return true;
}
@Override
public void invokeDelete(PyObject obj) {
notWritable(obj);
}
});
needs_weakref = true;
}
/**
* Setup this type's special attributes.
*/
private void ensureAttributes() {
inheritSpecial();
// special case __new__, if function => static method
PyObject new_ = dict.__finditem__("__new__");
// XXX: java functions?
if (new_ != null && new_ instanceof PyFunction) {
dict.__setitem__("__new__", new PyStaticMethod(new_));
}
ensureDoc(dict);
ensureModule(dict);
// Calculate method resolution order
mro_internal();
cacheDescrBinds();
}
/**
* Inherit special attributes from the dominant base.
*/
private void inheritSpecial() {
if (!needs_userdict && base.needs_userdict) {
needs_userdict = true;
}
if (!needs_weakref && base.needs_weakref) {
needs_weakref = true;
}
}
/**
* Ensure dict contains a __doc__.
*
* @param dict a PyObject mapping
*/
public static void ensureDoc(PyObject dict) {
if (dict.__finditem__("__doc__") == null) {
dict.__setitem__("__doc__", Py.None);
}
}
/**
* Ensure dict contains a __module__, retrieving it from the current frame if it
* doesn't exist.
*
* @param dict a PyObject mapping
*/
public static void ensureModule(PyObject dict) {
if (dict.__finditem__("__module__") != null) {
return;
}
PyFrame frame = Py.getFrame();
if (frame == null) {
return;
}
PyObject name = frame.f_globals.__finditem__("__name__");
if (name != null) {
dict.__setitem__("__module__", name);
}
}
private static PyObject invokeNew(PyObject new_, PyType type, boolean init, PyObject[] args,
String[] keywords) {
PyObject obj;
if (new_ instanceof PyNewWrapper) {
obj = ((PyNewWrapper)new_).new_impl(init, type, args, keywords);
} else {
int n = args.length;
PyObject[] typePrepended = new PyObject[n + 1];
System.arraycopy(args, 0, typePrepended, 1, n);
typePrepended[0] = type;
obj = new_.__get__(null, type).__call__(typePrepended, keywords);
}
return obj;
}
/**
* Called on builtin types for a particular class. Should fill in dict, name, mro, base and
* bases from the class.
*/
protected void init(Class<?> forClass, Set<PyJavaType> needsInners) {
underlying_class = forClass;
if (underlying_class == PyObject.class) {
mro = new PyType[] {this};
} else {
Class<?> baseClass;
if (!Py.BOOTSTRAP_TYPES.contains(underlying_class)) {
baseClass = classToBuilder.get(underlying_class).getBase();
} else {
baseClass = PyObject.class;
}
if (baseClass == Object.class) {
baseClass = underlying_class.getSuperclass();
}
computeLinearMro(baseClass);
}
if (Py.BOOTSTRAP_TYPES.contains(underlying_class)) {
// init will be called again from addBuilder which also removes underlying_class from
// BOOTSTRAP_TYPES
return;
}
TypeBuilder builder = classToBuilder.get(underlying_class);
name = builder.getName();
dict = builder.getDict(this);
String doc = builder.getDoc();
// XXX: Can't create a __doc__ str until the PyBaseString/PyString types are
// created
if (dict.__finditem__("__doc__") == null && forClass != PyBaseString.class
&& forClass != PyString.class) {
PyObject docObj;
if (doc != null) {
docObj = new PyString(doc);
} else {
// XXX: Hack: Py.None may be null during bootstrapping. Most types
// encountered then should have docstrings anyway
docObj = Py.None == null ? new PyString() : Py.None;
}
dict.__setitem__("__doc__", docObj);
}
setIsBaseType(builder.getIsBaseType());
needs_userdict = dict.__finditem__("__dict__") != null;
instantiable = dict.__finditem__("__new__") != null;
cacheDescrBinds();
}
/**
* Fills the base and bases of this type with the type of baseClass as sets its mro to this type
* followed by the mro of baseClass.
*/
protected void computeLinearMro(Class<?> baseClass) {
base = PyType.fromClass(baseClass, false);
mro = new PyType[base.mro.length + 1];
System.arraycopy(base.mro, 0, mro, 1, base.mro.length);
mro[0] = this;
bases = new PyObject[] {base};
}
/**
* Determine if this type is a descriptor, and if so what kind.
*/
private void cacheDescrBinds() {
hasGet = lookup_mro("__get__") != null;
hasSet = lookup_mro("__set__") != null;
hasDelete = lookup_mro("__delete__") != null;
}
public PyObject getStatic() {
PyType cur = this;
while (cur.underlying_class == null) {
cur = cur.base;
}
return cur;
}
/**
* Ensures that the physical layout between this type and <code>other</code> are compatible.
* Raises a TypeError if not.
*/
public void compatibleForAssignment(PyType other, String attribute) {
if (!getLayout().equals(other.getLayout()) || needs_userdict != other.needs_userdict
|| needs_finalizer != other.needs_finalizer) {
throw Py.TypeError(String.format("%s assignment: '%s' object layout differs from '%s'",
attribute, other.fastGetName(), fastGetName()));
}
}
/**
* Gets the most parent PyType that determines the layout of this type, ie it has slots or an
* underlying_class. Can be this PyType.
*/
private PyType getLayout() {
if (underlying_class != null) {
return this;
} else if (numSlots != base.numSlots) {
return this;
}
return base.getLayout();
}
/**
* Get the most parent Java proxy Class from bases, tallying any encountered Java
* interfaces.
*
* @param bases array of base Jython classes
* @param interfaces List for collecting interfaces to
* @return base Java proxy Class
* @raises Py.TypeError if multiple Java inheritance was attempted
*/
private static Class<?> getJavaLayout(PyObject[] bases, List<Class<?>> interfaces) {
Class<?> baseProxy = null;
for (PyObject base : bases) {
if (!(base instanceof PyType)) {
continue;
}
Class<?> proxy = ((PyType)base).getProxyType();
if (proxy == null) {
continue;
}
if (proxy.isInterface()) {
interfaces.add(proxy);
} else {
if (baseProxy != null) {
String msg = "no multiple inheritance for Java classes: %s and %s";
throw Py.TypeError(String.format(msg, proxy.getName(), baseProxy.getName()));
}
baseProxy = proxy;
}
}
return baseProxy;
}
/**
* Setup the javaProxy for this type.
*
* @param baseProxyClass this type's base proxyClass
* @param interfaces a list of Java interfaces in bases
*/
private void setupProxy(Class<?> baseProxyClass, List<Class<?>> interfaces) {
if (baseProxyClass == null && interfaces.size() == 0) {
// javaProxy not applicable
return;
}
String proxyName = name;
PyObject module = dict.__finditem__("__module__");
if (module != null) {
proxyName = module.toString() + "$" + proxyName;
}
Class<?> proxyClass = MakeProxies.makeProxy(baseProxyClass, interfaces, name, proxyName,
dict);
javaProxy = proxyClass;
PyType proxyType = PyType.fromClass(proxyClass, false);
List<PyObject> cleanedBases = Generic.list();
boolean addedProxyType = false;
for (PyObject base : bases) {
if (!(base instanceof PyType)) {
cleanedBases.add(base);
continue;
}
Class<?> proxy = ((PyType)base).getProxyType();
if (proxy == null) {
// non-proxy types go straight into our lookup
cleanedBases.add(base);
} else {
if (!(base instanceof PyJavaType)) {
// python subclasses of proxy types need to be added as a base so their
// version of methods will show up
cleanedBases.add(base);
} else if (!addedProxyType) {
// Only add a single Java type, since everything's going to go through the
// proxy type
cleanedBases.add(proxyType);
addedProxyType = true;
}
}
}
bases = cleanedBases.toArray(new PyObject[cleanedBases.size()]);
}
@ExposedGet(name = "__base__")
public PyObject getBase() {
if (base == null)
return Py.None;
return base;
}
@ExposedGet(name = "__bases__")
public PyObject getBases() {
return new PyTuple(bases);
}
@ExposedDelete(name = "__bases__")
public void delBases() {
throw Py.TypeError("Can't delete __bases__ attribute");
}
@ExposedSet(name = "__bases__")
public void setBases(PyObject newBasesTuple) {
if (!(newBasesTuple instanceof PyTuple)) {
throw Py.TypeError("bases must be a tuple");
}
PyObject[] newBases = ((PyTuple)newBasesTuple).getArray();
if (newBases.length == 0) {
throw Py.TypeError("can only assign non-empty tuple to __bases__, not "
+ newBasesTuple);
}
for (int i = 0; i < newBases.length; i++) {
if (!(newBases[i] instanceof PyType)) {
if (!(newBases[i] instanceof PyClass)) {
throw Py.TypeError(name + ".__bases__ must be a tuple of old- or new-style "
+ "classes, not " + newBases[i]);
}
} else {
if (((PyType)newBases[i]).isSubType(this)) {
throw Py.TypeError("a __bases__ item causes an inheritance cycle");
}
}
}
PyType newBase = best_base(newBases);
base.compatibleForAssignment(newBase, "__bases__");
PyObject[] savedBases = bases;
PyType savedBase = base;
PyObject[] savedMro = mro;
List<Object> savedSubMros = Generic.list();
try {
bases = newBases;
base = newBase;
mro_internal();
mro_subclasses(savedSubMros);
for (PyObject saved : savedBases) {
if (saved instanceof PyType) {
((PyType)saved).detachSubclass(this);
}
}
for (PyObject newb : newBases) {
if (newb instanceof PyType) {
((PyType)newb).attachSubclass(this);
}
}
} catch (PyException t) {
for (Iterator<Object> it = savedSubMros.iterator(); it.hasNext();) {
PyType subtype = (PyType)it.next();
PyObject[] subtypeSavedMro = (PyObject[])it.next();
subtype.mro = subtypeSavedMro;
}
bases = savedBases;
base = savedBase;
mro = savedMro;
throw t;
}
postSetattr("__getattribute__");
}
private void setIsBaseType(boolean isBaseType) {
this.isBaseType = isBaseType;
tp_flags = isBaseType ? tp_flags | Py.TPFLAGS_BASETYPE : tp_flags & ~Py.TPFLAGS_BASETYPE;
}
private void mro_internal() {
if (getType() == TYPE) {
mro = computeMro();
} else {
PyObject mroDescr = getType().lookup("mro");
if (mroDescr == null) {
throw Py.AttributeError("mro");
}
PyObject[] result = Py.make_array(mroDescr.__get__(null, getType()).__call__(this));
PyType solid = solid_base(this);
for (PyObject cls : result) {
if (cls instanceof PyClass) {
continue;
}
if (!(cls instanceof PyType)) {
throw Py.TypeError(String.format("mro() returned a non-class ('%.500s')",
cls.getType().fastGetName()));
}
PyType t = (PyType)cls;
if (!solid.isSubType(solid_base(t))) {
throw Py.TypeError(String.format("mro() returned base with unsuitable layout "
+ "('%.500s')", t.fastGetName()));
}
}
mro = result;
}
}
/**
* Collects the subclasses and current mro of this type in mroCollector. If this type has
* subclasses C and D, and D has a subclass E current mroCollector will equal [C, C.__mro__, D,
* D.__mro__, E, E.__mro__] after this call.
*/
private void mro_subclasses(List<Object> mroCollector) {
for (WeakReference<PyType> ref : subclasses) {
PyType subtype = ref.get();
if (subtype == null) {
continue;
}
mroCollector.add(subtype);
mroCollector.add(subtype.mro);
subtype.mro_internal();
subtype.mro_subclasses(mroCollector);
}
}
public PyObject instDict() {
if (needs_userdict) {
return new PyStringMap();
}
return null;
}
private void cleanup_subclasses() {
Reference<?> ref;
while ((ref = subclasses_refq.poll()) != null) {
subclasses.remove(ref);
}
}
@ExposedGet(name = "__mro__")
public PyTuple getMro() {
return mro == null ? Py.EmptyTuple : new PyTuple(mro);
}
@ExposedGet(name = "__flags__")
public PyLong getFlags() {
return new PyLong(tp_flags);
}
@ExposedMethod(doc = BuiltinDocs.type___subclasses___doc)
public synchronized final PyObject type___subclasses__() {
PyList result = new PyList();
cleanup_subclasses();
for (WeakReference<PyType> ref : subclasses) {
PyType subtype = ref.get();
if (subtype == null)
continue;
result.append(subtype);
}
return result;
}
/**
* Returns the Java Class that this type inherits from, or null if this type is Python-only.
*/
public Class<?> getProxyType() {
return (Class<?>)javaProxy;
}
private synchronized void attachSubclass(PyType subtype) {
cleanup_subclasses();
subclasses.add(new WeakReference<PyType>(subtype, subclasses_refq));
}
private synchronized void detachSubclass(PyType subtype) {
cleanup_subclasses();
for (WeakReference<PyType> ref : subclasses) {
if (ref.get() == subtype) {
subclasses.remove(ref);
break;
}
}
}
private synchronized void traverse_hierarchy(boolean top, OnType behavior) {
boolean stop = false;
if (!top) {
stop = behavior.onType(this);
}
if (stop) {
return;
}
for (WeakReference<PyType> ref : subclasses) {
PyType subtype = ref.get();
if (subtype == null) {
continue;
}
subtype.traverse_hierarchy(false, behavior);
}
}
private static void fill_classic_mro(List<PyObject> acc, PyClass classic_cl) {
if (!acc.contains(classic_cl)) {
acc.add(classic_cl);
}
PyObject[] bases = classic_cl.__bases__.getArray();
for (PyObject base : bases) {
fill_classic_mro(acc,(PyClass)base);
}
}
private static PyObject[] classic_mro(PyClass classic_cl) {
List<PyObject> acc = Generic.list();
fill_classic_mro(acc, classic_cl);
return acc.toArray(new PyObject[acc.size()]);
}
@ExposedMethod(defaults = "null", doc = BuiltinDocs.type_mro_doc)
final PyList type_mro(PyObject o) {
if (o == null) {
return new PyList(computeMro());
}
return new PyList(((PyType)o).computeMro());
}
PyObject[] computeMro() {
for (int i = 0; i < bases.length; i++) {
PyObject cur = bases[i];
for (int j = i + 1; j < bases.length; j++) {
if (bases[j] == cur) {
PyObject name = cur.__findattr__("__name__");
throw Py.TypeError("duplicate base class " +
(name == null ? "?" : name.toString()));
}
}
}
MROMergeState[] toMerge = new MROMergeState[bases.length + 1];
for (int i = 0; i < bases.length; i++) {
toMerge[i] = new MROMergeState();
if (bases[i] instanceof PyType) {
toMerge[i].mro = ((PyType)bases[i]).mro;
} else if (bases[i] instanceof PyClass) {
toMerge[i].mro = classic_mro((PyClass)bases[i]);
}
}
toMerge[bases.length] = new MROMergeState();
toMerge[bases.length].mro = bases;
List<PyObject> mro = Generic.list();
mro.add(this);
return computeMro(toMerge, mro);
}
PyObject[] computeMro(MROMergeState[] toMerge, List<PyObject> mro) {
boolean addedProxy = false;
PyType proxyAsType = javaProxy == null ? null : PyType.fromClass(((Class<?>)javaProxy), false);
scan : for (int i = 0; i < toMerge.length; i++) {
if (toMerge[i].isMerged()) {
continue scan;
}
PyObject candidate = toMerge[i].getCandidate();
for (MROMergeState mergee : toMerge) {
if (mergee.pastnextContains(candidate)) {
continue scan;
}
}
if (!addedProxy && !(this instanceof PyJavaType) && candidate instanceof PyJavaType
&& candidate.javaProxy != null
&& PyProxy.class.isAssignableFrom(((Class<?>)candidate.javaProxy))
&& candidate.javaProxy != javaProxy) {
// If this is a subclass of a Python class that subclasses a Java class, slip the
// proxy for this class in before the proxy class in the superclass' mro.
// This exposes the methods from the proxy generated for this class in addition to
// those generated for the superclass while allowing methods from the superclass to
// remain visible from the proxies.
mro.add(proxyAsType);
addedProxy = true;
}
mro.add(candidate);
// Was that our own proxy?
addedProxy |= candidate == proxyAsType;
for (MROMergeState element : toMerge) {
element.noteMerged(candidate);
}
i = -1; // restart scan
}
for (MROMergeState mergee : toMerge) {
if (!mergee.isMerged()) {
handleMroError(toMerge, mro);
}
}
return mro.toArray(new PyObject[mro.size()]);
}
/**
* Must either throw an exception, or bring the merges in <code>toMerge</code> to completion by
* finishing filling in <code>mro</code>.
*/
void handleMroError(MROMergeState[] toMerge, List<PyObject> mro) {
StringBuilder msg = new StringBuilder("Cannot create a consistent method resolution\n"
+ "order (MRO) for bases ");
Set<PyObject> set = Generic.set();
for (MROMergeState mergee : toMerge) {
if(!mergee.isMerged()) {
set.add(mergee.mro[0]);
}
}
boolean first = true;
for (PyObject unmerged : set) {
PyObject name = unmerged.__findattr__("__name__");
if (first) {
first = false;
} else {
msg.append(", ");
}
msg.append(name == null ? "?" : name.toString() + new PyList(((PyType)unmerged).bases));
}
throw Py.TypeError(msg.toString());
}
/**
* Finds the parent of type with an underlying_class or with slots sans a __dict__
* slot.
*/
private static PyType solid_base(PyType type) {
do {
if (isSolidBase(type)) {
return type;
}
type = type.base;
} while (type != null);
return PyObject.TYPE;
}
private static boolean isSolidBase(PyType type) {
return type.underlying_class != null || (type.numSlots != 0 && !type.needs_userdict);
}
/**
* Finds the base in bases with the most derived solid_base, ie the most base type
*
* @throws Py.TypeError if the bases don't all derive from the same solid_base
* @throws Py.TypeError if at least one of the bases isn't a new-style class
*/
private static PyType best_base(PyObject[] bases) {
PyType winner = null;
PyType candidate = null;
PyType best = null;
for (PyObject base : bases) {
if (base instanceof PyClass) {
continue;
}
if (!(base instanceof PyType)) {
throw Py.TypeError("bases must be types");
}
candidate = solid_base((PyType)base);
if (winner == null) {
winner = candidate;
best = (PyType)base;
} else if (winner.isSubType(candidate)) {
;
} else if (candidate.isSubType(winner)) {
winner = candidate;
best = (PyType)base;
} else {
throw Py.TypeError("multiple bases have instance lay-out conflict");
}
}
if (best == null) {
throw Py.TypeError("a new-style class can't have only classic bases");
}
return best;
}
/**
* Finds the most derived subtype of initialMetatype in the types
* of bases, or initialMetatype if it is already the most derived.
*
* @raises Py.TypeError if the all the metaclasses don't descend
* from the same base
* @raises Py.TypeError if one of the bases is a PyJavaClass or a
* PyClass with no proxyClass
*/
private static PyType findMostDerivedMetatype(PyObject[] bases_list, PyType initialMetatype) {
PyType winner = initialMetatype;
for (PyObject base : bases_list) {
if (base instanceof PyClass) {
continue;
}
PyType curtype = base.getType();
if (winner.isSubType(curtype)) {
continue;
}
if (curtype.isSubType(winner)) {
winner = curtype;
continue;
}
throw Py.TypeError("metaclass conflict: the metaclass of a derived class must be a "
+ "(non-strict) subclass of the metaclasses of all its bases");
}
return winner;
}
public boolean isSubType(PyType supertype) {
if (mro != null) {
for (PyObject base : mro) {
if (base == supertype) {
return true;
}
}
return false;
}
// we're not completely initialized yet; follow tp_base
PyType type = this;
do {
if (type == supertype) {
return true;
}
type = type.base;
} while (type != null);
return supertype == PyObject.TYPE;
}
/**
* Attribute lookup for name through mro objects' dicts. Lookups are cached.
*
* @param name attribute name (must be interned)
* @return found object or null
*/
public PyObject lookup(String name) {
return lookup_where(name, null);
}
/**
* Attribute lookup for name directly through mro objects' dicts. This isn't cached,
* and should generally only be used during the bootstrapping of a type.
*
* @param name attribute name (must be interned)
* @return found object or null
*/
protected PyObject lookup_mro(String name) {
return lookup_where_mro(name, null);
}
/**
* Attribute lookup for name through mro objects' dicts. Lookups are cached.
*
* Returns where in the mro the attribute was found at where[0].
*
* @param name attribute name (must be interned)
* @param where Where in the mro the attribute was found is written to index 0
* @return found object or null
*/
public PyObject lookup_where(String name, PyObject[] where) {
return methodCache.lookup_where(this, name, where);
}
/**
* Attribute lookup for name through mro objects' dicts. This isn't cached, and should
* generally only be used during the bootstrapping of a type.
*
* Returns where in the mro the attribute was found at where[0].
*
* @param name attribute name (must be interned)
* @param where Where in the mro the attribute was found is written to index 0
* @return found object or null
*/
protected PyObject lookup_where_mro(String name, PyObject[] where) {
PyObject[] mro = this.mro;
if (mro == null) {
return null;
}
for (PyObject t : mro) {
PyObject dict = t.fastGetDict();
if (dict != null) {
PyObject obj = dict.__finditem__(name);
if (obj != null) {
if (where != null) {
where[0] = t;
}
return obj;
}
}
}
return null;
}
public PyObject super_lookup(PyType ref, String name) {
PyObject[] mro = this.mro;
if (mro == null) {
return null;
}
int i;
for (i = 0; i < mro.length; i++) {
if (mro[i] == ref)
break;
}
i++;
for (; i < mro.length; i++) {
PyObject dict = mro[i].fastGetDict();
if (dict != null) {
PyObject obj = dict.__finditem__(name);
if (obj != null)
return obj;
}
}
return null;
}
public synchronized static void addBuilder(Class<?> forClass, TypeBuilder builder) {
if (classToBuilder == null) {
classToBuilder = Generic.map();
}
classToBuilder.put(forClass, builder);
if (class_to_type.containsKey(forClass)) {
if (!Py.BOOTSTRAP_TYPES.remove(forClass)) {
Py.writeWarning("init", "Bootstrapping class not in Py.BOOTSTRAP_TYPES[class="
+ forClass + "]");
}
// The types in Py.BOOTSTRAP_TYPES are initialized before their builders are assigned,
// so do the work of addFromClass & fillFromClass after the fact
fromClass(builder.getTypeClass()).init(builder.getTypeClass(), null);
}
}
private synchronized static PyType addFromClass(Class<?> c, Set<PyJavaType> needsInners) {
if (ExposeAsSuperclass.class.isAssignableFrom(c)) {
PyType exposedAs = fromClass(c.getSuperclass(), false);
class_to_type.put(c, exposedAs);
return exposedAs;
}
return createType(c, needsInners);
}
static boolean hasBuilder(Class<?> c) {
return classToBuilder != null && classToBuilder.containsKey(c);
}
private static TypeBuilder getBuilder(Class<?> c) {
if (classToBuilder == null) {
// PyType itself has yet to be initialized. This should be a bootstrap type, so it'll
// go through the builder process in a second
return null;
}
if (c.isPrimitive() || !PyObject.class.isAssignableFrom(c)) {
// If this isn't a PyObject, don't bother forcing it to be initialized to load its
// builder
return null;
}
// This is a PyObject, call forName to force static initialization on the class so if it has
// a builder, it'll be filled in
SecurityException exc = null;
try {
Class.forName(c.getName(), true, c.getClassLoader());
} catch (ClassNotFoundException e) {
// Well, this is certainly surprising.
throw new RuntimeException("Got ClassNotFound calling Class.forName on an already "
+ " found class.", e);
} catch (ExceptionInInitializerError e) {
throw Py.JavaError(e);
} catch (SecurityException e) {
exc = e;
}
TypeBuilder builder = classToBuilder.get(c);
if (builder == null && exc != null) {
Py.writeComment("type",
"Unable to initialize " + c.getName() + ", a PyObject subclass, due to a " +
"security exception, and no type builder could be found for it. If it's an " +
"exposed type, it may not work properly. Security exception: " +
exc.getMessage());
}
return builder;
}
private synchronized static PyType createType(Class<?> c, Set<PyJavaType> needsInners) {
PyType newtype;
if (c == PyType.class) {
newtype = new PyType(false);
} else if (Py.BOOTSTRAP_TYPES.contains(c) || getBuilder(c) != null) {
newtype = new PyType();
} else {
newtype = new PyJavaType();
}
// If filling in the type above filled the type under creation, use that one
PyType type = class_to_type.get(c);
if (type != null) {
return type;
}
class_to_type.put(c, newtype);
newtype.builtin = true;
newtype.init(c, needsInners);
newtype.invalidateMethodCache();
return newtype;
}
// re the synchronization used here: this result is cached in each type obj,
// so we just need to prevent data races. all public methods that access class_to_type
// are themselves synchronized. However, if we use Google Collections/Guava,
// MapMaker only uses ConcurrentMap anyway
public static synchronized PyType fromClass(Class<?> c) {
return fromClass(c, true);
}
public static synchronized PyType fromClass(Class<?> c, boolean hardRef) {
if (class_to_type == null) {
class_to_type = new MapMaker().weakKeys().weakValues().makeMap();
addFromClass(PyType.class, null);
}
PyType type = class_to_type.get(c);
if (type != null) {
return type;
}
// We haven't seen this class before, so its type needs to be created. If it's being
// exposed as a Java class, defer processing its inner types until it's completely
// created in case the inner class references a class that references this class.
Set<PyJavaType> needsInners = Generic.set();
PyType result = addFromClass(c, needsInners);
for (PyJavaType javaType : needsInners) {
Class<?> forClass = javaType.getProxyType();
if (forClass == null) {
continue;
}
for (Class<?> inner : forClass.getClasses()) {
// Only add the class if there isn't something else with that name and it came from this
// class
if (inner.getDeclaringClass() == forClass &&
javaType.dict.__finditem__(inner.getSimpleName()) == null) {
// If this class is currently being loaded, any exposed types it contains won't have
// set their builder in PyType yet, so add them to BOOTSTRAP_TYPES so they're
// created as PyType instead of PyJavaType
if (inner.getAnnotation(ExposedType.class) != null
|| ExposeAsSuperclass.class.isAssignableFrom(inner)) {
Py.BOOTSTRAP_TYPES.add(inner);
}
javaType.dict.__setitem__(inner.getSimpleName(), PyType.fromClass(inner, hardRef));
}
}
}
if (hardRef && result != null) {
if (exposedTypes == null) {
exposedTypes = Generic.set();
}
exposedTypes.add(result) ;
}
return result;
}
static PyType fromClassSkippingInners(Class<?> c, Set<PyJavaType> needsInners) {
PyType type = class_to_type.get(c);
if (type != null) {
return type;
}
return addFromClass(c, needsInners);
}
@ExposedMethod(doc = BuiltinDocs.type___getattribute___doc)
final PyObject type___getattribute__(PyObject name) {
String n = asName(name);
PyObject ret = type___findattr_ex__(n);
if (ret == null) {
noAttributeError(n);
}
return ret;
}
public PyObject __findattr_ex__(String name) {
return type___findattr_ex__(name);
}
// name must be interned
final PyObject type___findattr_ex__(String name) {
PyType metatype = getType();
PyObject metaattr = metatype.lookup(name);
boolean get = false;
if (metaattr != null) {
get = metaattr.implementsDescrGet();
if (useMetatypeFirst(metaattr) && get && metaattr.isDataDescr()) {
PyObject res = metaattr.__get__(this, metatype);
if (res != null)
return res;
}
}
PyObject attr = lookup(name);
if (attr != null) {
PyObject res = attr.__get__(null, this);
if (res != null) {
return res;
}
}
if (get) {
return metaattr.__get__(this, metatype);
}
if (metaattr != null) {
return metaattr;
}
return null;
}
/**
* Returns true if the given attribute retrieved from an object's metatype should be used before
* looking for the object on the actual object.
*/
protected boolean useMetatypeFirst(PyObject attr) {
return true;
}
@ExposedMethod(doc = BuiltinDocs.type___setattr___doc)
final void type___setattr__(PyObject name, PyObject value) {
type___setattr__(asName(name), value);
}
public void __setattr__(String name, PyObject value) {
type___setattr__(name, value);
}
/**
* Adds the given method to this type's dict under its name in its descriptor. If there's an
* existing item in the dict, it's replaced.
*/
public void addMethod(PyBuiltinMethod meth) {
PyMethodDescr pmd = meth.makeDescriptor(this);
__setattr__(pmd.getName(), pmd);
}
/**
* Removes the given method from this type's dict or raises a KeyError.
*/
public void removeMethod(PyBuiltinMethod meth) {
__delattr__(meth.info.getName());
}
void type___setattr__(String name, PyObject value) {
if (builtin) {
throw Py.TypeError(String.format("can't set attributes of built-in/extension type "
+ "'%s'", this.name));
}
super.__setattr__(name, value);
postSetattr(name);
}
void postSetattr(String name) {
invalidateMethodCache();
if (name == "__get__") {
if (!hasGet && lookup("__get__") != null) {
traverse_hierarchy(false, new OnType() {
public boolean onType(PyType type) {
boolean old = type.hasGet;
type.hasGet = true;
return old;
}
});
}
} else if (name == "__set__") {
if (!hasSet && lookup("__set__") != null) {
traverse_hierarchy(false, new OnType() {
public boolean onType(PyType type) {
boolean old = type.hasSet;
type.hasSet = true;
return old;
}
});
}
} else if (name == "__delete__") {
if (!hasDelete && lookup("__delete__") != null) {
traverse_hierarchy(false, new OnType() {
public boolean onType(PyType type) {
boolean old = type.hasDelete;
type.hasDelete = true;
return old;
}
});
}
} else if (name == "__getattribute__") {
traverse_hierarchy(false, new OnType() {
public boolean onType(PyType type) {
return (type.usesObjectGetattribute = false);
}
});
}
}
public void __delattr__(String name) {
type___delattr__(name);
}
@ExposedMethod(doc = BuiltinDocs.type___delattr___doc)
final void type___delattr__(PyObject name) {
type___delattr__(asName(name));
}
protected void checkDelattr() {
}
void type___delattr__(String name) {
if (builtin) {
throw Py.TypeError(String.format("can't set attributes of built-in/extension type "
+ "'%s'", this.name));
}
super.__delattr__(name);
postDelattr(name);
}
void postDelattr(String name) {
invalidateMethodCache();
if (name == "__get__") {
if (hasGet && lookup("__get__") == null) {
traverse_hierarchy(false, new OnType() {
public boolean onType(PyType type) {
boolean absent = type.getDict().__finditem__("__get__") == null;
if (absent) {
type.hasGet = false;
return false;
}
return true;
}
});
}
} else if (name == "__set__") {
if (hasSet && lookup("__set__") == null) {
traverse_hierarchy(false, new OnType() {
public boolean onType(PyType type) {
boolean absent = type.getDict().__finditem__("__set__") == null;
if (absent) {
type.hasSet = false;
return false;
}
return true;
}
});
}
} else if (name == "__delete__") {
if (hasDelete && lookup("__delete__") == null) {
traverse_hierarchy(false, new OnType() {
public boolean onType(PyType type) {
boolean absent = type.getDict().__finditem__("__delete__") == null;
if (absent) {
type.hasDelete = false;
return false;
}
return true;
}
});
}
} else if (name == "__getattribute__") {
traverse_hierarchy(false, new OnType() {
public boolean onType(PyType type) {
return (type.usesObjectGetattribute = false);
}
});
}
}
/**
* Invalidate this type's MethodCache entries. *Must* be called after any modification
* to __dict__ (or anything else affecting attribute lookups).
*/
protected void invalidateMethodCache() {
traverse_hierarchy(false, new OnType() {
public boolean onType(PyType type) {
type.versionTag = new Object();
return false;
}
});
}
public PyObject __call__(PyObject[] args, String[] keywords) {
return type___call__(args, keywords);
}
@ExposedMethod(doc = BuiltinDocs.type___call___doc)
final PyObject type___call__(PyObject[] args, String[] keywords) {
PyObject new_ = lookup("__new__");
if (!instantiable || new_ == null) {
throw Py.TypeError(String.format("cannot create '%.100s' instances", name));
}
PyObject obj = invokeNew(new_, this, true, args, keywords);
// When the call was type(something) or the returned object is not an instance of
// type, it won't be initialized
if ((this == TYPE && args.length == 1 && keywords.length == 0)
|| !obj.getType().isSubType(this)) {
return obj;
}
obj.dispatch__init__(args, keywords);
return obj;
}
protected void __rawdir__(PyDictionary accum) {
mergeClassDict(accum, this);
}
public String fastGetName() {
return name;
}
@ExposedGet(name = "__name__")
public PyObject pyGetName() {
return Py.newString(getName());
}
public String getName() {
if (!builtin) {
return name;
}
int lastDot = name.lastIndexOf('.');
if (lastDot != -1) {
return name.substring(lastDot + 1);
}
return name;
}
@ExposedSet(name = "__name__")
public void pySetName(PyObject name) {
// guarded by __setattr__ to prevent modification of builtins
if (!(name instanceof PyString)) {
throw Py.TypeError(String.format("can only assign string to %s.__name__, not '%s'",
this.name, name.getType().fastGetName()));
}
String nameStr = name.toString();
if (nameStr.indexOf((char)0) > -1) {
throw Py.ValueError("__name__ must not contain null bytes");
}
setName(nameStr);
invalidateMethodCache();
}
public void setName(String name) {
this.name = name;
}
@ExposedDelete(name = "__name__")
public void pyDelName() {
throw Py.TypeError(String.format("can't delete %s.__name__", name));
}
/**
* Returns the actual dict underlying this type instance. Changes to Java types should go
* through {@link #addMethod} and {@link #removeMethod}, or unexpected mro errors can occur.
*/
public PyObject fastGetDict() {
return dict;
}
@ExposedGet(name = "__dict__")
public PyObject getDict() {
return new PyDictProxy(dict);
}
@ExposedSet(name = "__dict__")
public void setDict(PyObject newDict) {
// Analogous to CPython's descrobject:getset_set
throw Py.AttributeError(String.format("attribute '__dict__' of '%s' objects is not "
+ "writable", getType().fastGetName()));
}
@ExposedDelete(name = "__dict__")
public void delDict() {
setDict(null);
}
/**
* Equivalent of CPython's typeobject.c::type_get_doc; handles __doc__ descriptors.
*/
@ExposedGet(name = "__doc__")
public PyObject getDoc() {
PyObject doc = dict.__finditem__("__doc__");
if (doc == null) {
return Py.None;
}
return doc.__get__(null, this);
}
boolean getUsesObjectGetattribute() {
return usesObjectGetattribute;
}
void setUsesObjectGetattribute(boolean usesObjectGetattribute) {
this.usesObjectGetattribute = usesObjectGetattribute;
}
public Object __tojava__(Class<?> c) {
if (underlying_class != null && (c == Object.class || c == Class.class ||
c == Serializable.class)) {
return underlying_class;
}
return super.__tojava__(c);
}
@ExposedGet(name = "__module__")
public PyObject getModule() {
if (!builtin) {
return dict.__finditem__("__module__");
}
int lastDot = name.lastIndexOf('.');
if (lastDot != -1) {
return Py.newString(name.substring(0, lastDot));
}
return Py.newString("__builtin__");
}
@ExposedDelete(name = "__module__")
public void delModule() {
throw Py.TypeError(String.format("can't delete %s.__module__", name));
}
public int getNumSlots() {
return numSlots;
}
@ExposedMethod(names = "__repr__", doc = BuiltinDocs.type___repr___doc)
final String type_toString() {
String kind;
if (!builtin) {
kind = "class";
} else {
kind = "type";
}
PyObject module = getModule();
if (module instanceof PyString && !module.toString().equals("__builtin__")) {
return String.format("<%s '%s.%s'>", kind, module.toString(), getName());
}
return String.format("<%s '%s'>", kind, getName());
}
public String toString() {
return type_toString();
}
/**
* Raises AttributeError on type objects. The message differs from
* PyObject#noAttributeError, to mimic CPython behaviour.
*/
public void noAttributeError(String name) {
throw Py.AttributeError(String.format("type object '%.50s' has no attribute '%.400s'",
fastGetName(), name));
}
//XXX: consider pulling this out into a generally accessible place
// I bet this is duplicated more or less in other places.
private static String confirmIdentifier(PyObject obj) {
String identifier;
if (!(obj instanceof PyString)) {
throw Py.TypeError(String.format("__slots__ items must be strings, not '%.200s'",
obj.getType().fastGetName()));
} else if (obj instanceof PyUnicode) {
identifier = ((PyUnicode)obj).encode();
} else {
identifier = obj.toString();
}
String msg = "__slots__ must be identifiers";
if (identifier.length() == 0
|| (!Character.isLetter(identifier.charAt(0)) && identifier.charAt(0) != '_')) {
throw Py.TypeError(msg);
}
for (char c : identifier.toCharArray()) {
if (!Character.isLetterOrDigit(c) && c != '_') {
throw Py.TypeError(msg);
}
}
return identifier;
}
//XXX: copied from CodeCompiler.java and changed variable names.
// Maybe this should go someplace for all classes to use.
private static String mangleName(String classname, String methodname) {
if (classname != null && methodname.startsWith("__") && !methodname.endsWith("__")) {
//remove leading '_' from classname
int i = 0;
while (classname.charAt(i) == '_') {
i++;
}
return ("_" + classname.substring(i) + methodname).intern();
}
return methodname;
}
/** Used when serializing this type. */
protected Object writeReplace() {
return new TypeResolver(underlying_class, getModule().toString(), getName());
}
private interface OnType {
boolean onType(PyType type);
}
static class TypeResolver implements Serializable {
private Class<?> underlying_class;
String module;
private String name;
TypeResolver(Class<?> underlying_class, String module, String name) {
// Don't store the underlying_class for PyProxies as the proxy type needs to fill in
// based on the class, not be the class
if (underlying_class != null && !PyProxy.class.isAssignableFrom(underlying_class)) {
this.underlying_class = underlying_class;
}
this.module = module;
this.name = name;
}
private Object readResolve() {
if (underlying_class != null) {
return PyType.fromClass(underlying_class, false);
}
PyObject mod = imp.importName(module.intern(), false);
PyObject pytyp = mod.__getattr__(name.intern());
if (!(pytyp instanceof PyType)) {
throw Py.TypeError(module + "." + name + " must be a type for deserialization");
}
return pytyp;
}
}
/**
* Tracks the status of merging a single base into a subclass' mro in computeMro.
*/
static class MROMergeState {
/** The mro of the base type we're representing. */
public PyObject[] mro;
/**
* The index of the next item to be merged from mro, or mro.length if this base has been
* completely merged.
*/
public int next;
public boolean isMerged() {
return mro.length == next;
}
public PyObject getCandidate() {
return mro[next];
}
/**
* Marks candidate as merged for this base if it's the next item to be merged.
*/
public void noteMerged(PyObject candidate) {
if (!isMerged() && getCandidate() == candidate) {
next++;
}
}
/**
* Returns true if candidate is in the items past this state's next item to be merged.
*/
public boolean pastnextContains(PyObject candidate) {
for (int i = next + 1; i < mro.length; i++) {
if (mro[i] == candidate) {
return true;
}
}
return false;
}
/**
* Removes the given item from this state's mro if it isn't already finished.
*/
public void removeFromUnmerged(PyJavaType winner) {
if (isMerged()) {
return;
}
List<PyObject> newMro = Generic.list();
for (PyObject mroEntry : mro) {
if (mroEntry != winner) {
newMro.add(mroEntry);
}
}
mro = newMro.toArray(new PyObject[newMro.size()]);
}
}
/**
* A thead safe, non-blocking version of Armin Rigo's mro cache.
*/
static class MethodCache {
/** The fixed size cache. */
private final AtomicReferenceArray<MethodCacheEntry> table;
/** Size of the cache exponent (2 ** SIZE_EXP). */
public static final int SIZE_EXP = 11;
public MethodCache() {
table = new AtomicReferenceArray<MethodCacheEntry>(1 << SIZE_EXP);
clear();
}
public void clear() {
int length = table.length();
for (int i = 0; i < length; i++) {
table.set(i, MethodCacheEntry.EMPTY);
}
}
public PyObject lookup_where(PyType type, String name, PyObject where[]) {
Object versionTag = type.versionTag;
int index = indexFor(versionTag, name);
MethodCacheEntry entry = table.get(index);
if (entry.isValid(versionTag, name)) {
return entry.get(where);
}
// Always cache where
if (where == null) {
where = new PyObject[1];
}
PyObject value = type.lookup_where_mro(name, where);
if (isCacheableName(name)) {
// CAS isn't totally necessary here but is possibly more correct. Cache by
// the original version before the lookup, if it's changed since then
// we'll cache a bad entry. Bad entries and CAS failures aren't a concern
// as subsequent lookups will sort themselves out
table.compareAndSet(index, entry, new MethodCacheEntry(versionTag, name, where[0],
value));
}
return value;
}
/**
* Return the table index for type version/name.
*/
private static int indexFor(Object version, String name) {
return (version.hashCode() * name.hashCode()) >>> (Integer.SIZE - SIZE_EXP);
}
/**
* Determine if name is cacheable.
*
* Since the cache can keep references to names alive longer than usual, it avoids
* caching unusually large strings.
*/
private static boolean isCacheableName(String name) {
return name.length() <= 100;
}
static class MethodCacheEntry extends WeakReference<PyObject> {
/** Version of the entry, a PyType.versionTag. */
private final Object version;
/** The name of the attribute. */
private final String name;
/** Where in the mro the value was found. */
private final WeakReference<PyObject> where;
static final MethodCacheEntry EMPTY = new MethodCacheEntry();
private MethodCacheEntry() {
this(null, null, null, null);
}
public MethodCacheEntry(Object version, String name, PyObject where, PyObject value) {
super(value);
this.version = version;
this.name = name;
this.where = new WeakReference<PyObject>(where);
}
public boolean isValid(Object version, String name) {
return this.version == version && this.name == name;
}
public PyObject get(PyObject[] where) {
if (where != null) {
where[0] = this.where.get();
}
return get();
}
}
}
}