/* * Copyright 2010 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 org.visage.jdi; import org.visage.jdi.event.VisageEventQueue; import com.sun.jdi.AbsentInformationException; import com.sun.jdi.ClassLoaderReference; import com.sun.jdi.ClassObjectReference; import com.sun.jdi.Field; import com.sun.jdi.Location; import com.sun.jdi.Method; import com.sun.jdi.ObjectReference; import com.sun.jdi.ReferenceType; import com.sun.jdi.ClassType; import com.sun.jdi.Value; import com.sun.jdi.ShortValue; import com.sun.jdi.InvalidTypeException; import com.sun.jdi.InvocationException; import com.sun.jdi.ClassNotLoadedException; import com.sun.jdi.IncompatibleThreadStateException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.regex.Pattern; /** * * @author sundar */ public class VisageReferenceType extends VisageType implements ReferenceType { public VisageReferenceType(VisageVirtualMachine visagevm, ReferenceType underlying) { super(visagevm, underlying); } public byte[] constantPool() { return underlying().constantPool(); } public int constantPoolCount() { return underlying().constantPoolCount(); } public List<ObjectReference> instances(long count) { return VisageWrapper.wrapObjectReferences(virtualMachine(), underlying().instances(count)); } public int majorVersion() { return underlying().majorVersion(); } public int minorVersion() { return underlying().minorVersion(); } public List<Field> allFields() { return VisageWrapper.wrapFields(virtualMachine(), underlying().allFields()); } public List<Location> allLineLocations() throws AbsentInformationException { return VisageWrapper.wrapLocations(virtualMachine(), underlying().allLineLocations()); } public List<Location> allLineLocations(String stratum, String sourceName) throws AbsentInformationException { return VisageWrapper.wrapLocations(virtualMachine(), underlying().allLineLocations(stratum, sourceName)); } public List<Method> allMethods() { return VisageWrapper.wrapMethods(virtualMachine(), underlying().allMethods()); } public List<String> availableStrata() { return underlying().availableStrata(); } public ClassLoaderReference classLoader() { return VisageWrapper.wrap(virtualMachine(), underlying().classLoader()); } public ClassObjectReference classObject() { return VisageWrapper.wrap(virtualMachine(), underlying().classObject()); } public String defaultStratum() { return underlying().defaultStratum(); } public boolean failedToInitialize() { return underlying().failedToInitialize(); } // return null if there is no field or the name is ambigous. public VisageField fieldByName(String name) { // There could be both an Visage field $xxx and a java field xxx Field javaField = underlying().fieldByName(name); Field visageField = underlying().fieldByName("$" + name); if (javaField == null) { if (visageField == null ) { // an ivar that is a referenced in an outer class can be prefixed with // 'classname$' return null; } // we'll return visageField } else { if (visageField != null) { // we found both name and $name return null; } visageField = javaField; } return VisageWrapper.wrap(virtualMachine(), visageField); } public List<Field> fields() { return VisageWrapper.wrapFields(virtualMachine(), underlying().fields()); } public String genericSignature() { return underlying().genericSignature(); } // The RefType for the ....$Script class for this class if there is one private ReferenceType scriptType ; public int getFlagWord(Field field) { // could this be a java field inherited by an visage class?? if (!isVisageType()) { return 0; } if (scriptType == null) { ReferenceType jdiRefType = underlying(); String jdiRefTypeName = jdiRefType.name(); String scriptClassName = jdiRefTypeName; int lastDot = scriptClassName.lastIndexOf('.'); if (lastDot != -1) { scriptClassName = scriptClassName.substring(lastDot + 1); } scriptClassName = jdiRefTypeName + "$" + scriptClassName + "$Script"; List<ReferenceType> rtx = virtualMachine().classesByName(scriptClassName); if (rtx.size() != 1) { System.out.println("--VisageJDI Error: Can't find the class: " + scriptClassName); return 0; } scriptType = rtx.get(0); } Field jdiField = VisageWrapper.unwrap(field); String jdiFieldName = jdiField.name(); String vflgFieldName = "VFLG" + jdiFieldName; Field vflgField = scriptType.fieldByName(vflgFieldName); if (vflgField == null) { // not all fields have a VFLG, eg, a private field that isn't accessed return 0; } Value vflgValue = VisageWrapper.unwrap(scriptType).getValue(VisageWrapper.unwrap(vflgField)); return((ShortValue)vflgValue).value(); } private boolean areFlagBitsSet(Field field, int mask) { return (getFlagWord(field) & mask) == mask; } private Map<Field, Value> getValuesCommon(List<? extends Field> wrappedFields, boolean doInvokes) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException, InvocationException { // We will find fields which have no getters, and call the underlying // getValues to get values for all of them in one fell swoop. Map<Field, Field> unwrappedToWrappedMap = new HashMap<Field, Field>(); List<Field> noGetterUnwrappedFields = new ArrayList<Field>(); // fields that don't have getters // For fields that do have getters, we will just return VoidValue for them if // or we will call VisageGetValue for each, depending on doInvokes Map<Field, Value> result = new HashMap<Field, Value>(); // Create the above Maps and lists for (Field wrappedField : wrappedFields) { Field unwrapped = VisageWrapper.unwrap(wrappedField); if (isVisageType()) { List<Method> mth = underlying().methodsByName("get" + unwrapped.name()); if (mth.size() == 0) { // No getter unwrappedToWrappedMap.put(unwrapped, wrappedField); noGetterUnwrappedFields.add(unwrapped); } else { // Field has a getter if (doInvokes) { result.put(wrappedField, getValue(wrappedField)); } else { result.put(wrappedField, virtualMachine().voidValue()); } } } else { // Java type unwrappedToWrappedMap.put(unwrapped, wrappedField); noGetterUnwrappedFields.add(unwrapped); } } // Get values for all the noGetter fields. Note that this gets them in a single JDWP trip Map<Field, Value> unwrappedFieldValues = underlying().getValues(noGetterUnwrappedFields); // for each input Field, create a result map entry with that field as the // key, and the value returned by getValues, or null if the field is invalid. // Make a pass over the unwrapped no getter fields and for each, put its // wrapped version, and wrapped value into the result Map. for (Map.Entry<Field, Field> unwrappedEntry: unwrappedToWrappedMap.entrySet()) { Field wrappedField = unwrappedEntry.getValue(); Value resultValue = VisageWrapper.wrap(virtualMachine(), unwrappedFieldValues.get(unwrappedEntry.getKey())); result.put(wrappedField, resultValue); } return result; } /** * JDI addition: Determines if the value of a field of this reference type is invalid. A value * is invalid if a new value has been specified for the field, but not yet * stored into the field, for example, because the field is lazily bound. * * @return <code>true</code> if the value of the specified field is invalid; false otherwise. */ public boolean isInvalid(Field field) { return areFlagBitsSet(field, virtualMachine().VisageInvalidFlagMask()); } /** * JDI addition: Determines if a field of this reference type can be modified. For example, * an field declared with a bind cannot be modified. * * @return <code>true</code> if the specified field is read only; false otherwise. */ public boolean isReadOnly(Field field) { return areFlagBitsSet(field, virtualMachine().VisageReadOnlyFlagMask()); } /** * JDI addition: Determines if a field was declared with a bind clause. * * @return <code>true</code> if the specified field was declared with a bind clause; false otherwise. */ public boolean isBound(Field field) { return areFlagBitsSet(field, virtualMachine().VisageBoundFlagMask()); } /** * JDI addition: Determines if this is a Visage class. * * @return <code>true</code> if this is a Visage class; false otherwise. */ public boolean isVisageType() { return false; } // Each internal class name is the name of its containing class followed by $digit. // (Except for the $Script class) private Pattern visagePat1 = Pattern.compile("\\$[0-9].*"); private boolean isUserClassSet = false; private VisageClassType userClass = null; /** * JDI addition: Return the Visage user class associated with this reference type. * * The Visage compiler can generate several classes for a given class defined by the user. * Given one of these internal classes, this method will return the ReferenceType for the * associated user class. * * @return the Visage user class associated with this reference type if there is one, else null. */ public VisageClassType visageUserClass() { if (isUserClassSet) { return userClass; } isUserClassSet = true; if (!isVisageType()) { return null; } String className = name(); int firstDollar = className.indexOf('$'); if (firstDollar == -1) { return null; } String[] hit = visagePat1.split(className, 0); if (hit.length != 1) { return null; } if (!hit[0].equals(className)) { List<ReferenceType> userClasses = virtualMachine().classesByName(hit[0]); if (userClasses.size() != 1) { // can't happen return null; } userClass = (VisageClassType)userClasses.get(0); return userClass; } if (className.indexOf("$Script") == -1) { return null; } // This is a $Script class, so we want the scriptClass userClass = scriptClass(); return userClass; } private boolean isTopClassSet = false; private VisageClassType topClass = null; /** * JDI addition: Return the script class associated with this reference type. * * The Visage compiler can generate several classes for a given Visage file. * Given one of these classes, this method will return the associated ReferenceType * for the containing script class. * * @return the Visage class that contains this class if there is one, else null. */ public VisageClassType scriptClass() { if (isTopClassSet) { return topClass; } isTopClassSet = true; if (!isVisageType()) { return null; } if (!(this instanceof VisageClassType)) { return null; } // Get the name of the top class. First choice is the filename. 2nd choice // is the part of this name before the first $. String topClassName ; String thisName = name(); int firstDollar = thisName.indexOf('$'); if (firstDollar == -1) { topClass = (VisageClassType)this; return topClass; } topClassName = thisName.substring(0, firstDollar); List<ReferenceType> xx = virtualMachine().classesByName(topClassName); if (xx.size() != 1) { // shouldn't happen return null; } if (!(xx.get(0) instanceof VisageClassType)) { // shouldn't happen return null; } topClass = (VisageClassType)xx.get(0); return topClass; } /** * JDI extension: This will call the get function for the field if one exists via invokeMethod. * The call to invokeMethod is preceded by a call to {@link VisageEventQueue#setEventControl(boolean)} passing true * and is followed by a call to {@link VisageEventQueue#setEventControl(boolean)} passing false. * * If an invokeMethod Exception occurs, it is saved and can be accessed by calling * {@link VisageVirtualMachine#lastFieldAccessException()}. In this case, * the default value for the type of the field is returned for a PrimitiveType, * while null is returned for a non PrimitiveType. */ public Value getValue(Field field) { virtualMachine().setLastFieldAccessException(null); Field jdiField = VisageWrapper.unwrap(field); if (!isVisageType()) { return VisageWrapper.wrap(virtualMachine(), underlying().getValue(jdiField)); } //get$xxxx methods exist for fields except private fields which have no binders List<Method> mth = underlying().methodsByName("get" + jdiField.name()); if (mth.size() == 0) { return VisageWrapper.wrap(virtualMachine(), underlying().getValue(jdiField)); } Exception theExc = null; VisageEventQueue eq = virtualMachine().eventQueue(); try { eq.setEventControl(true); return ((VisageClassType)this).invokeMethod(virtualMachine().uiThread(), mth.get(0), new ArrayList<Value>(0), ClassType.INVOKE_SINGLE_THREADED); } catch(InvalidTypeException ee) { theExc = ee; } catch(ClassNotLoadedException ee) { theExc = ee; } catch(IncompatibleThreadStateException ee) { theExc = ee; } catch(InvocationException ee) { theExc = ee; } finally { eq.setEventControl(false); } // We don't have to catch IllegalArgumentException. It is an unchecked exception for invokeMethod // and for getValue virtualMachine().setLastFieldAccessException(theExc); try { return virtualMachine().defaultValue(field.type()); } catch(ClassNotLoadedException ee) { // The type has to be a ReferenceType for which we return null; return null; } } /** * JDI extension: This will call the get function for a field if one exists via invokeMethod. * The call to invokeMethod is preceded by a call to {@link VisageEventQueue#setEventControl(boolean)} * passing true and is followed by a call to {@link VisageEventQueue#setEventControl(boolean)} passing false. * * If an invokeMethod Exception occurs, it is saved and can be accessed by calling * {@link VisageVirtualMachine#lastFieldAccessException()}. In this case, * the default value for the type of the field is returned for a PrimitiveType, * while null is returned for a non PrimitiveType. */ public Map<Field, Value> getValues(List<? extends Field> wrappedFields) { virtualMachine().setLastFieldAccessException(null); // We will find fields which have no getters, and call the underlying // getValues to get values for all of them in one fell swoop. Map<Field, Field> unwrappedToWrappedMap = new HashMap<Field, Field>(); List<Field> noGetterUnwrappedFields = new ArrayList<Field>(); // fields that don't have getters // But first, for fields that do have getters, call invokeMethod // or we will call VisageGetValue for each, depending on doInvokes Map<Field, Value> result = new HashMap<Field, Value>(); // Create the above Maps and lists for (Field wrappedField : wrappedFields) { Field unwrapped = VisageWrapper.unwrap(wrappedField); if (isVisageType()) { List<Method> mth = underlying().methodsByName("get" + unwrapped.name()); if (mth.size() == 0) { // No getter unwrappedToWrappedMap.put(unwrapped, wrappedField); noGetterUnwrappedFields.add(unwrapped); } else { // Field has a getter result.put(wrappedField, getValue(wrappedField)); } } else { // Java type unwrappedToWrappedMap.put(unwrapped, wrappedField); noGetterUnwrappedFields.add(unwrapped); } } // Get values for all the noGetter fields. Note that this gets them in a single JDWP trip Map<Field, Value> unwrappedFieldValues = underlying().getValues(noGetterUnwrappedFields); // for each input Field, create a result map entry with that field as the // key, and the value returned by getValues, or null if the field is invalid. // Make a pass over the unwrapped no getter fields and for each, put its // wrapped version, and wrapped value into the result Map. for (Map.Entry<Field, Field> unwrappedEntry: unwrappedToWrappedMap.entrySet()) { Field wrappedField = unwrappedEntry.getValue(); Value resultValue = VisageWrapper.wrap(virtualMachine(), unwrappedFieldValues.get(unwrappedEntry.getKey())); result.put(wrappedField, resultValue); } return result; } public boolean isAbstract() { return underlying().isAbstract(); } public boolean isFinal() { return underlying().isFinal(); } public boolean isInitialized() { return underlying().isInitialized(); } public boolean isPrepared() { return underlying().isPrepared(); } public boolean isStatic() { return underlying().isStatic(); } public boolean isVerified() { return underlying().isVerified(); } public List<Location> locationsOfLine(int lineNumber) throws AbsentInformationException { return VisageWrapper.wrapLocations(virtualMachine(), underlying().locationsOfLine(lineNumber)); } public List<Location> locationsOfLine(String stratum, String sourceName, int line) throws AbsentInformationException { return VisageWrapper.wrapLocations(virtualMachine(), underlying().locationsOfLine(stratum, sourceName, line)); } public List<Method> methods() { return VisageWrapper.wrapMethods(virtualMachine(), underlying().methods()); } public List<Method> methodsByName(String name) { return VisageWrapper.wrapMethods(virtualMachine(), underlying().methodsByName(name)); } public List<Method> methodsByName(String name, String signature) { return VisageWrapper.wrapMethods(virtualMachine(), underlying().methodsByName(name, signature)); } public List<ReferenceType> nestedTypes() { return VisageWrapper.wrapReferenceTypes(virtualMachine(), underlying().nestedTypes()); } public String sourceDebugExtension() throws AbsentInformationException { return underlying().sourceDebugExtension(); } public String sourceName() throws AbsentInformationException { return underlying().sourceName(); } public List<String> sourceNames(String stratum) throws AbsentInformationException { return underlying().sourceNames(stratum); } public List<String> sourcePaths(String stratum) throws AbsentInformationException { return underlying().sourcePaths(stratum); } public List<Field> visibleFields() { return VisageWrapper.wrapFields(virtualMachine(), underlying().visibleFields()); } public List<Method> visibleMethods() { return VisageWrapper.wrapMethods(virtualMachine(), underlying().visibleMethods()); } public int compareTo(ReferenceType o) { return underlying().compareTo(VisageWrapper.unwrap(o)); } public boolean isPackagePrivate() { return underlying().isPackagePrivate(); } public boolean isPrivate() { return underlying().isPrivate(); } public boolean isProtected() { return underlying().isProtected(); } public boolean isPublic() { return underlying().isPublic(); } public int modifiers() { return underlying().modifiers(); } @Override protected ReferenceType underlying() { return (ReferenceType) super.underlying(); } public ReferenceType _underlying() { return (ReferenceType)super.underlying(); } private boolean isInternalJavaTypeSet = false; private boolean internalJavaType = false; public boolean isInternalJavaType() { if (!isInternalJavaTypeSet) { String myName = name(); if ("org.visage.runtime.VisageBase".equals(myName) || "org.visage.runtime.VisageObject".equals(myName) || myName.startsWith("org.visage.functions.Function")) { internalJavaType = true; isInternalJavaTypeSet = true; } } return internalJavaType; } }