/*
* Copyright 2008-2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
package visage.reflect;
import java.lang.reflect.*;
import java.lang.annotation.Annotation;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
import org.visage.functions.*;
import org.visage.runtime.VisageObject;
import org.visage.runtime.TypeInfo;
import org.visage.runtime.sequence.Sequence;
import org.visage.runtime.sequence.Sequences;
/**
* Implement Visage reflection on top of {@java.lang.reflect}.
* Hence, this implementation can only reflect/mirror values and classes
* in the same VM that is doing the reflection.
*
* @author Per Bothner
* @profile desktop
*/
public class VisageLocal {
public static Context getContext() { return Context.instance; }
/** Implementation of {@link VisageContext} using Java reflection.
* Can only access objects and types in the current JVM.
* Normally, this is a singleton, though in the future there might
* be variants with different class search paths (similar to
* {@code com.sun.jdi.PathSearchingVirtualMachine}).
*
* @profile desktop
*/
public static class Context extends VisageContext {
static Context instance = new Context();
private Context () {
}
/** Get the default instance. */
public static Context getInstance() { return instance; }
/** Create a reference to a given Object. */
public ObjectValue mirrorOf(Object obj) {
return new ObjectValue(obj, this);
}
public Value mirrorOf(final Object val, final VisageType type) {
// FIXME Perhaps if val==null we should use MiscValue?
if (type instanceof ClassType)
return new VisageLocal.ObjectValue(val, (ClassType) type);
else if (type instanceof VisagePrimitiveType) {
return ((VisagePrimitiveType) type).mirrorOf(val);
}
else if (type instanceof VisageSequenceType && val instanceof Sequence) {
Sequence seq = (Sequence) val;
return new SequenceValue(seq, (VisageSequenceType) type, this);
}
else if (type instanceof VisageFunctionType && val instanceof Function) {
final VisageFunctionType ftype = (VisageFunctionType) type;
return new FunctionValue((Function) val, ftype, this);
} else {
return new MiscValue(val, type);
}
}
public ObjectValue mirrorOf(String val) {
return new ObjectValue(val, this);
}
/** Get the {@code VisageClassType} for the class with the given name. */
public ClassType findClass(String cname) {
ClassLoader loader;
try {
loader = Thread.currentThread().getContextClassLoader();
}
catch (java.lang.SecurityException ex) {
loader = getClass().getClassLoader();
}
return findClass(cname, loader);
}
/** Get the {@code VisageClassType} for the class with the given name. */
public ClassType findClass(String cname, ClassLoader loader) {
String n = cname;
Exception ex0 = null;
for (;;) {
try {
Class cl = Class.forName(n, false, loader);
// if (! cl.getCanonicalName().equals(cname)) break;
return makeClassRef(cl);
} catch (Exception ex) {
if (ex0 == null)
ex0 = ex;
int dot = n.lastIndexOf('.');
if (dot < 0)
break;
n = n.substring(0, dot)+'$'+n.substring(dot+1);
}
}
throw new RuntimeException(ex0);
}
static final String LOCATION_PREFIX = "org.visage.runtime.location.";
static final int LOCATION_PREFIX_LENGTH = LOCATION_PREFIX.length();
static final String VARIABLE_STRING = "Variable";
static final int VARIABLE_STRING_LENGTH = VARIABLE_STRING.length();
public VisageType makeTypeRef(Type typ) {
Object t = PlatformUtils.resolveGeneric(this, typ);
if (t instanceof VisageType)
return (VisageType) t;
Class clas = (Class) t;
if (clas.isArray()) {
VisageType elType = makeTypeRef(clas.getComponentType());
return new VisageJavaArrayType(elType);
}
String rawName = clas.getName();
int rawLength = rawName.length();
if (rawLength > LOCATION_PREFIX_LENGTH + VARIABLE_STRING_LENGTH &&
rawName.startsWith(LOCATION_PREFIX) &&
rawName.endsWith(VARIABLE_STRING)) {
rawName = rawName.substring(LOCATION_PREFIX_LENGTH,
rawLength-VARIABLE_STRING_LENGTH);
if (rawName.endsWith("Sequence")) {
int newLength = rawName.length() - "Sequence".length();
if (newLength != 0) rawName = rawName.substring(0, newLength - 1);
VisageType ptype = getPrimitiveType(rawName);
if (ptype != null)
return new VisageSequenceType(ptype);
}
VisageType ptype = getPrimitiveType(rawName);
if (ptype != null)
return ptype;
}
if (typ == Byte.TYPE)
return VisagePrimitiveType.byteType;
if (typ == Short.TYPE)
return VisagePrimitiveType.shortType;
if (typ == Integer.TYPE)
return VisagePrimitiveType.integerType;
if (typ == Long.TYPE)
return VisagePrimitiveType.longType;
if (typ == Float.TYPE)
return VisagePrimitiveType.floatType;
if (typ == Double.TYPE)
return VisagePrimitiveType.doubleType;
if (typ == Character.TYPE)
return VisagePrimitiveType.charType;
if (typ == Boolean.TYPE)
return VisagePrimitiveType.booleanType;
if (typ == Void.TYPE)
return VisagePrimitiveType.voidType;
return makeClassRef(clas);
}
/** Create a reference to a given Class. */
public ClassType makeClassRef(Class cls) {
int modifiers = 0;
try {
Class[] interfaces = cls.getInterfaces();
for (int i = 0; i < interfaces.length; i++ ) {
String iname = interfaces[i].getName();
if (iname.equals(VISAGEOBJECT_NAME)) {
modifiers |= VisageClassType.VISAGE_CLASS;
} else if (iname.equals(VISAGEMIXIN_NAME)) {
modifiers |= VisageClassType.VISAGE_MIXIN;
}
}
Class clsInterface = null;
if ((modifiers & VisageClassType.VISAGE_MIXIN) != 0) {
String cname = cls.getName();
if (cname.endsWith(MIXIN_SUFFIX)) {
cname = cname.substring(0, cname.length() - MIXIN_SUFFIX.length());
clsInterface = cls;
cls = Class.forName(cname, false, cls.getClassLoader());
if (cls == null) throw new RuntimeException("Missing mixin class " + cname);
} else {
String intfName = cname + MIXIN_SUFFIX;
clsInterface = Class.forName(intfName, false, cls.getClassLoader());
if (clsInterface == null) throw new RuntimeException("Missing mixin interface " + intfName);
}
}
return new ClassType(this, modifiers, cls, clsInterface);
}
catch (RuntimeException ex) {
throw ex;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public static Class asClass (VisageType type) {
if (type instanceof VisagePrimitiveType)
return ((VisagePrimitiveType) type).clas;
else if (type instanceof JavaArrayType)
return ((JavaArrayType) type).getJavaClass();
else if (type instanceof VisageSequenceType)
return Sequence.class;
else { // FIXME - handle other cases
ClassType ctyp = (ClassType) type;
return ctyp.isMixin() ? ctyp.refInterface : ctyp.refClass;
}
}
@Override
public Value makeSequenceValue(VisageValue[] values, int nvalues, VisageType elementType) {
return new SequenceValue(values, nvalues, elementType, this);
}
}
static class JavaArrayType extends VisageJavaArrayType {
Class cls;
JavaArrayType(VisageType componentType, Class cls) {
super(componentType);
this.cls = cls;
}
public Class getJavaClass() { return cls; }
}
/** A mirror of a {@code Class} in the current JVM.
* @profile desktop
*/
public static class ClassType extends VisageClassType {
Class refClass;
Class refInterface;
protected static int VOFF_INITIALIZED = 1 << 16; // high to avoid collisions with masks added in parent class.
public ClassType(Context context, int modifiers,
Class refClass, Class refInterface) {
super(context, modifiers);
this.refClass = refClass;
this.refInterface = refInterface;
this.name = PlatformUtils.getCanonicalName(refClass);
}
public Class getJavaImplementationClass() { return refClass; }
public Class getJavaInterfaceClass() { return refInterface; }
@Override
public Context getReflectionContext() {
return (Context) super.getReflectionContext();
}
/** Returns a hash-code.
* @return the hash-code of the name.
*/
@Override
public int hashCode() {
return (name != null ? name : refClass.getName()).hashCode();
}
@Override
public boolean equals (Object obj) {
return obj instanceof ClassType
&& refClass == ((ClassType) obj).refClass;
}
void getSuperClasses(boolean all, SortedClassArray result) {
boolean isMixin = this.isMixin();
Class cls = isMixin ? refInterface : refClass;
Class[] interfaces = cls.getInterfaces();
Context context = getReflectionContext();
if (! isMixin) {
Class s = cls.getSuperclass();
if (s != null) {
ClassType cl = (ClassType) context.makeClassRef(s);
if (result.insert(cl) && all)
cl.getSuperClasses(all, result);
}
}
for (int i = 0; i < interfaces.length; i++) {
Class iface = interfaces[i];
String iname = iface.getName();
if (iname.equals(Context.VISAGEOBJECT_NAME) || iname.equals(Context.VISAGEMIXIN_NAME))
continue;
ClassType cl = (ClassType) context.makeClassRef(iface);
if (result.insert(cl) && all)
cl.getSuperClasses(all, result);
}
}
public List<VisageClassType> getSuperClasses(boolean all) {
SortedClassArray result = new SortedClassArray();
if (all)
result.insert(this);
getSuperClasses(all, result);
return result;
}
public VisageFunctionMember getFunction(String name, VisageType... argType) {
int nargs = argType.length;
Class[] ctypes = new Class[nargs];
for (int i = 0; i < nargs; i++) {
ctypes[i] = Context.asClass(argType[i]);
}
try {
Method meth;
try {
meth = refClass.getMethod(name, ctypes);
} catch (NoSuchMethodException ex) {
if (isMixin())
meth = null;
else
throw ex;
}
if (isMixin())
if (meth == null ||
(meth.getModifiers() & Modifier.STATIC) == 0) {
meth = refInterface.getMethod(name, ctypes);
}
return asFunctionMember(meth, getReflectionContext());
}
catch (RuntimeException ex) {
throw ex;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
private Method[] filter(Method[] methods, Class declaringClass) {
List<Method> result = new ArrayList<Method>();
for (Method m : methods) {
if (m.getDeclaringClass() == declaringClass) {
result.add(m);
}
}
return result.toArray(new Method[0]);
}
static final String[] SYSTEM_METHOD_EXCLUDES = {
// keep in alphabetical order
"addDependent$",
"complete$",
"count$",
"getAsBoolean$",
"getAsByte$",
"getAsChar$",
"getAsDouble$",
"getAsFloat$",
"getAsInt$",
"getAsLong$",
"getAsShort$",
"getDepChain$internal$",
"getThisRef$internal$",
"getListenerCount$",
"getType$",
"initialize$",
"isInitialized$",
"isInitialized$internal$",
"visage$run$",
"makeInitMap$",
"notifyDependents$",
"postInit$",
"printBits$",
"printBitsAction$",
"removeDependent$",
"restrictSet$",
"setDepChain$internal$",
"setInitialized$internal$",
"setThisRef$internal$",
"switchDependence$",
"userInit$",
"getFlags$",
"setFlags$",
"varChangeBits$",
"varTestBits$",
"VCNT$"
};
static final String[] SYSTEM_METHOD_PREFIXES = {
"applyDefaults$",
"get$",
"elem$",
"initVars$",
"invalidate$",
"invoke$",
"onReplace$",
"seq$",
"set$",
"size$",
"update$",
"DCNT$",
"DEP$",
"FCNT$",
"GETMAP$",
"VOFF$"
};
static final String[] SYSTEM_METHOD_SUFFIXES = {
"$impl"
};
protected void getFunctions(VisageMemberFilter filter, SortedMemberArray<? super VisageFunctionMember> result) {
Class cls = refClass;
Context context = getReflectionContext();
Method[] methods;
try {
methods = cls.getDeclaredMethods();
} catch (SecurityException e) {
methods = filter(cls.getMethods(), cls);
}
skip: for (int i = 0; i < methods.length; i++) {
Method m = methods[i];
if (PlatformUtils.isSynthetic(m))
continue;
if (PlatformUtils.checkInherited(m) > 0)
continue;
String mname = m.getName();
for (String exclude : SYSTEM_METHOD_EXCLUDES) {
if (mname.equals(exclude))
continue skip;
}
for (String prefix : SYSTEM_METHOD_PREFIXES) {
if (mname.startsWith(prefix))
continue skip;
}
for (String suffix : SYSTEM_METHOD_SUFFIXES) {
if (mname.endsWith(suffix))
continue skip;
}
if (isMixin()) {
try {
m = refInterface.getDeclaredMethod(m.getName(), m.getParameterTypes());
}
catch (Exception ex) {
// Just ignore ???
}
}
VisageFunctionMember mref = asFunctionMember(m, context);
if (filter != null && filter.accept(mref))
result.insert(mref);
}
}
VisageFunctionMember asFunctionMember(Method m, Context context) {
java.lang.reflect.Type[] ptypes = PlatformUtils.getGenericParameterTypes(m);
/*
if (m.isVarArgs()) {
// ????
}
*/
VisageType[] prtypes = new VisageType[ptypes.length];
for (int j = 0; j < ptypes.length; j++)
prtypes[j] = context.makeTypeRef(ptypes[j]);
java.lang.reflect.Type gret = PlatformUtils.getGenericReturnType(m);
VisageFunctionType type = new VisageFunctionType(prtypes, context.makeTypeRef(gret));
return new VisageLocal.FunctionMember(m, this, type);
}
private Field[] filter(Field[] fields, Class declaringClass) {
List<Field> result = new ArrayList<Field>();
for (Field f : fields) {
if (f.getDeclaringClass() == declaringClass) {
result.add(f);
}
}
return result.toArray(new Field[0]);
}
static protected java.lang.reflect.Method getMethodOrNull(Class cls, String name, Class... types) {
java.lang.reflect.Method method = null;
try {
method = cls.getMethod(name, types);
} catch (Throwable ex) {
}
return method;
}
static Object[] NO_ARGS = { };
static protected int callMethodIntOrDefault(java.lang.reflect.Method method, int deflt) {
if (method != null) {
try {
deflt = ((Integer)method.invoke(null, NO_ARGS)).intValue();
} catch (Throwable ex) {
}
}
return deflt;
}
static final String[] SYSTEM_VAR_PREFIXES = {
"DCNT$",
"DEP$",
"FCNT$",
"VFLG$",
"VCNT$",
"VOFF$",
"MAP$",
"$script$"
};
private void ensureVOffInitialized() {
// if no instances of this class have been created, there's no guarantee that VOFF$xxx are initialized,
// force initialization.
if ((this.modifiers & VOFF_INITIALIZED) == 0) {
try {
refClass.getMethod("VCNT$").invoke(null, NO_ARGS);
} catch (Throwable e) {
}
this.modifiers |= VOFF_INITIALIZED;
}
}
protected void getVariables(VisageMemberFilter filter, SortedMemberArray<? super VisageVarMember> result) {
VarMember[] varTable = this.variables;
if (varTable != null) {
String requiredName = filter.getRequiredName();
if (requiredName != null) {
VarMember v = varTable[searchVariable(varTable, requiredName)];
if (v != null)
result.insert(v);
} else {
for (int i = varTable.length; --i >= 0; ) {
VarMember v = varTable[i];
if (v != null && filter.accept(v))
result.insert(v);
}
}
return;
}
Context ctxt = getReflectionContext();
Class cls = refClass;
java.lang.reflect.Field[] fields;
ArrayList<VarMember> varList = new ArrayList<VarMember>();
try {
fields = cls.getDeclaredFields();
} catch (SecurityException e) {
fields = filter(cls.getFields(), cls);
}
fieldLoop: for (java.lang.reflect.Field fld : fields) {
if (PlatformUtils.isSynthetic(fld)) {
continue;
}
String fname = fld.getName();
String sname = PlatformUtils.getSourceNameFromAnnotation(fld);
if (sname == null) {
int dollar = fname.lastIndexOf('$');
if (dollar == -1) {
sname = fname;
} else {
for (String prefix : SYSTEM_VAR_PREFIXES) {
if (fname.startsWith(prefix)) {
continue fieldLoop;
}
}
if (fname.endsWith("$internal$"))
continue fieldLoop;
sname = fname.substring(dollar + 1);
}
}
VisageType tr = ctxt.makeTypeRef(fld.getGenericType());
ensureVOffInitialized();
int offset;
try {
java.lang.reflect.Field field = cls.getField("VOFF" + fname);
offset = field.getInt(null);
} catch (Throwable ex) {
offset = -1;
}
VarMember ref = new VarMember(sname, this, tr, offset);
ref.fld = fld;
varList.add(ref);
if (filter != null && filter.accept(ref))
result.insert(ref);
}
// Enter varList into the varTable hash-table.
int nvars = varList.size();
// We want the varTable hash-table to be at most 75% full.
int varTableSize = 8;
while (4 * nvars > 3 * varTableSize)
varTableSize += varTableSize;
varTable = new VarMember[varTableSize];
while (--nvars >= 0) {
VarMember v = varList.get(nvars);
varTable[searchVariable(varTable, v.name)] = v;
}
this.variables = varTable;
}
// A simple hash-table keyed vt variable name.
VarMember[] variables;
/** A simple hash-table search algorithm.
* @param variables The hash-table. Its length must be a power of two,
* and there must be at least one unused slot.
* @param name The name of the VarMember to search for.
* @return The index of a slot in {@code variables} that matches the name,
* or null if there is no such slot.
*/
static int searchVariable(VarMember[] variables, String name) {
int hash = name.hashCode();
int size = variables.length;
int h = 12347 * hash + hash;
int mask = size - 1;
int i = h & mask;
for (;;) {
VarMember v = variables[i];
if (v == null || v.name.equals(name))
return i;
i = (i + 53) & mask;
}
}
public ObjectValue allocate () {
Class cls = refClass;
Context context = getReflectionContext();
try {
Object instance;
if (isVisageType()) {
// Note that Java5-style varargs are not supported on CDC.
Class[] types = { Boolean.TYPE };
Constructor cons = cls.getDeclaredConstructor(types);
Object[] args = { Boolean.TRUE };
instance = cons.newInstance(args);
}
else {
instance = cls.newInstance();
}
return new ObjectValue(instance, this);
}
catch (RuntimeException ex) {
throw ex;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public VisageClassType getDeclaringClass() {
return null;
}
public boolean isStatic() {
return true;
}
Annotation getAnnotation (Class clas) {
Class cls = refClass;
return cls.getAnnotation(clas);
}
public boolean isPublic() {
int p = PlatformUtils.checkPublic(this);
if (p >= 0)
return p > 0;
Class cls = refClass;
return (cls.getModifiers() & Modifier.PUBLIC) != 0;
}
public boolean isProtected() {
return PlatformUtils.isProtected(this);
}
public boolean isPackage() {
int p = PlatformUtils.checkPackage(this);
if (p >= 0)
return p > 0;
Class cls = refClass;
return (cls.getModifiers() & Modifier.PUBLIC) == 0;
}
}
static class SortedClassArray extends AbstractList<VisageClassType> {
ClassType[] buffer = new ClassType[4];
int sz;
public VisageClassType get(int index) {
if (index >= sz)
throw new IndexOutOfBoundsException();
return buffer[index];
}
public int size() { return sz; }
// This is basically 'add' under a different non-public name.
boolean insert(ClassType cl) {
String clname = cl.getName();
// We could use binary search, but the lack of a total order
// for ClassLoaders complicates that. Linear search should be ok.
int i = 0;
for (; i < sz; i++) {
ClassType c = buffer[i];
String cname = c.getName();
int cmp = cname == clname ? 0
: cname == null ? -1 : clname == null ? 1
: cname.compareTo(clname);
if (cmp > 0)
break;
if (cmp == 0) {
if (c.refClass == cl.refClass)
return false;
// Arbitrary order if same name but different loaders.
break;
}
}
if (sz == buffer.length) {
ClassType[] tmp = new ClassType[2*sz];
System.arraycopy(buffer, 0, tmp, 0, sz);
buffer = tmp;
}
System.arraycopy(buffer, i, buffer, i+1, sz-i);
buffer[i] = cl;
sz++;
return true;
}
}
static class VarMember extends VisageVarMember {
Field fld;
Method getter;
Method setter;
VisageType type;
String name;
ClassType owner;
int offset;
static final int GETTER_SETTER_SET = 1;
static final int ACCESS_FLAGS_SET = 2;
static final int IS_PUBLIC = 4;
static final int IS_PROTECTED = IS_PUBLIC << 1;
static final int IS_PACKAGE = IS_PUBLIC << 2;
static final int IS_PUBLIC_INIT = IS_PUBLIC << 3;
static final int IS_PUBLIC_READ = IS_PUBLIC << 4;
int flags;
public VarMember(String name, ClassType owner, VisageType type, int offset) {
this.name = name;
this.type = type;
this.owner = owner;
this.offset = offset;
}
@Override
public VisageType getType() {
return type;
}
@Override
public int getOffset() {
return offset;
}
private void checkGetterSetter() {
if ((flags & GETTER_SETTER_SET) != 0)
return;
Class cls = owner.refInterface;
if (cls == null)
cls = owner.refClass;
String get, set;
boolean isVisage = owner.isVisageType();
if (isVisage) {
get = "get$";
set = "set$";
} else {
get = "get";
set = "set";
}
Method g = ClassType.getMethodOrNull(cls, get + name);
String xname = name;
if (g == null && isVisage) {
xname = cls.getSimpleName() + "$" + name;
g = ClassType.getMethodOrNull(cls, get + xname);
}
getter = g;
flags |= GETTER_SETTER_SET;
if (g != null) {
Class rtype = g.getReturnType();
setter = ClassType.getMethodOrNull(cls, set + xname, rtype);
}
}
@Override
public VisageValue getValue(VisageObjectValue obj) {
Object robj = obj == null ? null : ((ObjectValue) obj).obj;
try {
checkGetterSetter();
if (fld != null || getter != null) {
Context context =
(Context) owner.getReflectionContext();
Object val;
if (getter != null) {
val = getter.invoke(robj, new Object[0]);
} else {
fld.setAccessible(true);
val = fld.get(robj);
}
// FIXME: yet to be implemented for compiled binds
return context.mirrorOf(val, type);
}
}
catch (RuntimeException ex) {
throw ex;
}
catch (Exception ex) {
if (fld != null)
throw new RuntimeException("Illegal access of field " + fld);
else
throw new RuntimeException("Illegal access of field getter " + getter);
}
throw new UnsupportedOperationException("Not supported yet - "+type+"["+type.getClass().getName()+"]");
}
public VisageLocation getLocation(VisageObjectValue obj) {
return new VarMemberLocation(obj, this);
}
static final Object[] noObjects = {};
protected void initVar(VisageObjectValue instance, VisageValue value) {
instance.initVar(this, value);
}
@Override
public void initValue(VisageObjectValue instance, VisageValue value) {
instance.initVar(this, value);
}
@Override
public void setValue(VisageObjectValue obj, VisageValue value) {
Object robj = obj == null ? null : ((ObjectValue) obj).obj;
try {
if (type instanceof VisageSequenceType && robj instanceof VisageObject) {
Sequences.set((VisageObject)robj, offset,(Sequence)((Value) value).asObject());
return;
}
checkGetterSetter();
if (fld != null || setter != null) {
if (setter != null) {
Object[] args = { ((Value) value).asObject() };
setter.invoke(robj, args);
return;
} else {
// FIXME: yet to be implemented for compiled binds
if (fld != null) {
fld.setAccessible(true);
fld.set(robj, ((Value) value).asObject());
return;
}
}
}
} catch (RuntimeException ex) {
throw ex;
} catch (Exception ex) {
throw new RuntimeException(ex);
}
throw new UnsupportedOperationException("Not supported yet.");
}
public String getName() {
return name;
}
public VisageClassType getDeclaringClass() {
return owner;
}
private void checkAccessFlags() {
if ((flags & ACCESS_FLAGS_SET) != 0)
return;
flags |= ACCESS_FLAGS_SET;
checkGetterSetter();
if (! getDeclaringClass().isVisageType()
|| ! PlatformUtils.checkAccessAnnotations(this)) {
int mods = getter != null ? getter.getModifiers()
: fld.getModifiers();
if ((mods & Modifier.PUBLIC) != 0)
flags |= IS_PUBLIC;
if ((mods & Modifier.PROTECTED) != 0)
flags |= IS_PROTECTED;
int mask = Modifier.PUBLIC|Modifier.PROTECTED|Modifier.PRIVATE;
if ((mods & mask) == 0)
flags |= IS_PACKAGE;
}
}
public boolean isStatic() {
checkGetterSetter();
int mods = getter != null ? getter.getModifiers()
: fld.getModifiers();
return (mods & Modifier.STATIC) != 0;
}
public boolean isPublic() {
checkAccessFlags();
return (flags & IS_PUBLIC) != 0;
}
public boolean isProtected() {
checkAccessFlags();
return (flags & IS_PROTECTED) != 0;
}
public boolean isPackage() {
checkAccessFlags();
return (flags & IS_PACKAGE) != 0;
}
public boolean isPublicInit() {
checkAccessFlags();
return (flags & IS_PUBLIC_INIT) != 0;
}
public boolean isPublicRead() {
checkAccessFlags();
return (flags & IS_PUBLIC_READ) != 0;
}
public boolean isDef() {
int d = PlatformUtils.checkDef(this);
if (d >= 0)
return d > 0;
return fld != null && (fld.getModifiers() & Modifier.FINAL) != 0;
}
static class ListenerAdapter extends org.visage.runtime.VisageBase implements VisageChangeListenerID {
final VisageChangeListener listener;
ListenerAdapter(VisageChangeListener listener) {
this.listener = listener;
}
@Override
public boolean update$(VisageObject src, final int depNum, int startPos, int endPos, int newLength, int phase) {
// varNum does not matter, there is one change listener per <src, varNum> tuple.
if ((phase & PHASE_TRANS$PHASE) == PHASE$TRIGGER) {
this.listener.onChange();
}
return true;
}
}
public VisageChangeListenerID addChangeListener(VisageObjectValue instance, VisageChangeListener listener) {
if (!this.owner.isAssignableFrom(instance.getType()))
throw new IllegalArgumentException("not an instance of " + this.owner);
// check if instance acually has a variable represented by this
VisageObject src = (VisageObject)((Value)instance).asObject();
ListenerAdapter adapter = new ListenerAdapter(listener);
src.addDependent$(this.offset, adapter, 0);
return adapter;
}
public void removeChangeListener(VisageObjectValue instance, VisageChangeListenerID id) {
if (!this.owner.isAssignableFrom(instance.getType()))
throw new IllegalArgumentException("not an instance of " + this.owner);
VisageObject src = (VisageObject)((Value)instance).asObject();
src.removeDependent$(this.offset, (ListenerAdapter)id);
}
}
static class FunctionMember extends VisageFunctionMember {
Method method;
VisageClassType owner;
String name;
VisageFunctionType type;
FunctionMember(Method method, ClassType owner, VisageFunctionType type) {
this.method = method;
this.owner = owner;
this.name = method.getName();
this.type = type;
}
public String getName() { return name; }
public VisageClassType getDeclaringClass() { return owner; }
public boolean isStatic() {
return (method.getModifiers() & Modifier.STATIC) != 0;
}
public VisageFunctionType getType() {
return type;
}
Object unwrap(VisageValue value) {
if (value == null)
return null;
return ((Value) value).asObject();
}
/** Invoke this method on the given receiver and arguments. */
public VisageValue invoke(VisageObjectValue obj, VisageValue... arg) {
int alen = arg.length;
Object[] rargs = new Object[alen];
for (int i = 0; i < alen; i++) {
rargs[i] = unwrap(arg[i]);
}
try {
Object result = method.invoke(unwrap(obj), rargs);
Context context =
(Context) owner.getReflectionContext();
if (result == null && getType().getReturnType() == VisagePrimitiveType.voidType)
return null;
return context.mirrorOf(result, getType().getReturnType());
}
catch (RuntimeException ex) {
throw ex;
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
}
public boolean isPublic() {
int p = PlatformUtils.checkPublic(this);
if (p >= 0)
return p > 0;
else
return (method.getModifiers() & Modifier.PUBLIC) != 0;
}
public boolean isProtected() {
int p = PlatformUtils.checkProtected(this);
if (p >= 0)
return p > 0;
else
return (method.getModifiers() & Modifier.PROTECTED) != 0;
}
public boolean isPackage() {
int p = PlatformUtils.checkPackage(this);
if (p >= 0)
return p > 0;
int mods = method.getModifiers();
int mask = Modifier.PUBLIC|Modifier.PROTECTED|Modifier.PRIVATE;
return (mods & mask) == 0;
}
}
/** A value in the current JVM.
*
* @profile desktop
*/
public static interface Value extends VisageValue {
public abstract Object asObject();
}
static class MiscValue implements VisageLocal.Value {
Object val;
VisageType type;
public MiscValue(Object value, VisageType type) {
this.val = value;
this.type = type;
}
public String getValueString() { return val == null ? "(null)" : val.toString(); }
public VisageType getType() { return type; }
public boolean isNull() { return val == null; }
public Object asObject() { return val; }
public VisageValue getItem(int index) { return this; }
public int getItemCount() { return isNull() ? 0 : 1; }
};
/** A mirror of an {@code Object} in the current JVM.
*
* @profile desktop
*/
public static class ObjectValue extends VisageObjectValue implements VisageLocal.Value {
// FIXME It might be cleaner to require obj!=null,
// and instead use MiscValue for null.
Object obj;
ClassType type;
ClassType classType;
int count;
VisageVarMember[] initMembers;
VisageValue[] initValues;
public ObjectValue(Object obj, Context context) {
type = obj == null ? (ClassType) context.anyType
: context.makeClassRef(obj.getClass());
this.obj = obj;
if (obj instanceof VisageObject)
count = ((VisageObject) obj).count$();
}
public ObjectValue(Object obj, ClassType type) {
this.type = type;
this.obj = obj;
if (obj instanceof VisageObject)
count = ((VisageObject) obj).count$();
}
public VisageClassType getType() {
return type;
}
public VisageClassType getClassType() {
if (classType == null) {
if (obj == null)
classType = type;
else {
Class cls = obj.getClass();
classType = type.getJavaImplementationClass() == cls ? type
: type.getReflectionContext().makeClassRef(cls);
}
}
return classType;
}
public boolean isNull() {
return obj == null;
}
public String getValueString() {
if (obj == null)
return null;
else
return obj.toString();
}
public ObjectValue initialize() {
if (obj instanceof VisageObject) {
VisageObject instance = (VisageObject)obj;
if (initMembers == null) {
instance.initialize$(true);
} else {
int count = count();
instance.initVars$();
for (int offset = 0; offset < count; offset++ ) {
instance.varChangeBits$(offset, 0, VisageObject.VFLGS$INIT$READY);
if (initMembers[offset] != null) {
initMembers[offset].setValue(this, initValues[offset]);
} else {
instance.applyDefaults$(offset);
}
}
instance.complete$();
}
}
return this;
}
public Object asObject() { return obj; }
private int count() {
return obj instanceof VisageObject ? ((VisageObject) obj).count$() : 0;
}
public void initVar(VisageVarMember attr, VisageValue value) {
int offset = attr.getOffset();
if (offset == -1) {
attr.setValue(this, value);
} else {
if (initMembers == null) {
int count = count();
initMembers = new VisageVarMember[count];
initValues = new VisageValue[count];
}
initMembers[offset] = attr;
initValues[offset] = value;
int flag = attr.getType() instanceof VisageSequenceType ? VisageObject.VFLGS$INIT_OBJ_LIT_SEQUENCE
: VisageObject.VFLGS$INIT_OBJ_LIT;
((VisageObject)obj).setFlags$(offset, flag);
}
}
}
static class SequenceValue extends VisageSequenceValue implements VisageLocal.Value {
Sequence seq;
Context context;
public SequenceValue(VisageValue[] values, int nvalues, VisageType elementType,
Context context) {
super(values, nvalues, elementType);
this.context = context;
}
public SequenceValue(Sequence seq, VisageSequenceType sequenceType,
Context context) {
super(seq.size(), sequenceType);
this.seq = seq;
this.context = context;
}
public VisageValue getItem(int index) {
if (index < 0 || index >= nvalues )
return null;
if (values == null)
values = new VisageValue[nvalues];
if (values[index] == null && seq != null)
values[index] =
context.mirrorOf(seq.get(index), type.getComponentType());
return values[index];
}
public Sequence asObject() {
if (seq == null) {
VisageType elementType = type.getComponentType();
Object[] objs = new Object[nvalues];
for (int i = 0; i < nvalues; i++)
objs[i] = ((VisageLocal.Value) values[i]).asObject();
return Sequences.make(TypeInfo.getTypeInfo(context.asClass(elementType)), objs);
}
return seq;
}
}
/** Mirror a {@code Function} value in the current JVM.
*
* @profile desktop
*/
public static class FunctionValue extends VisageFunctionValue implements VisageLocal.Value {
Function val;
VisageFunctionType ftype;
Context context;
public FunctionValue(Function val, VisageFunctionType ftype, Context context) {
this.val = val;
this.ftype = ftype;
this.context = context;
}
public VisageValue apply(VisageValue... arg) {
Object result;
int nargs = arg.length;
if (nargs > 8) throw new IllegalArgumentException();
Object[] rargs = nargs > 2 ? new Object[nargs] : null;
Object arg1 = null, arg2 = null;
for (int i = 0; i < nargs; i++) {
Object targ = ((VisageLocal.Value) arg[i]).asObject();
if (i == 0)
arg1 = targ;
else if (i == 1)
arg2 = targ;
else
rargs[i-2] = targ;
}
result = ((Function) val).invoke$(arg1, arg2, rargs);
return context.mirrorOf(result, ftype.getReturnType());
}
public VisageFunctionType getType() {
return ftype;
}
public boolean isNull() { return false; }
public String getValueString() { return ftype.toString()+"{...}"; };
public Function asObject() { return val; }
}
/**
*
* @profile desktop
*/
public static class VarMemberLocation extends VisageVarMemberLocation {
VarMember var;
public VarMemberLocation(VisageObjectValue object, VarMember var) {
super(object, var);
this.var = var;
}
// FIXME: yet to be implemented for compiled binds
// public AbstractVariable getAbstractVariable(VisageObjectValue obj) {...}
}
}