/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package flash.tools.debugger.concrete; import flash.tools.debugger.NoResponseException; import flash.tools.debugger.NotConnectedException; import flash.tools.debugger.NotSuspendedException; import flash.tools.debugger.PlayerDebugException; import flash.tools.debugger.Session; import flash.tools.debugger.SessionManager; import flash.tools.debugger.Value; import flash.tools.debugger.Variable; import flash.tools.debugger.VariableAttribute; import flash.tools.debugger.VariableType; import flash.tools.debugger.events.FaultEvent; import flash.tools.debugger.expression.Context; public class DVariable implements Variable, Comparable { /** * The raw name, exactly as it came back from the Player. For example, this * might be <code>mynamespace@12345678::myvar</code>, which indicates that * the variable is in namespace "mynamespace", which has atom 12345678. */ private String m_rawName; /** Just name, without namespace */ private String m_name; /** @see Variable#getNamespace() */ private String m_namespace = ""; //$NON-NLS-1$ /** @see VariableAttribute */ private int m_attribs; /** * The variable's value. */ protected Value m_value; /** * Whether we have fired the getter for this value. Only applicable if * the VariableAttribute.HAS_GETTER attribute is set. */ private boolean m_firedGetter; /** * The class in which this member was actually defined. For example, if class * B extends class A, and class A has member variable V, then for variable * V, the defining class is always "A", even though the parent variable might * be an instance of class B. */ private String m_definingClass; /** * The variable's "level" -- see <code>Variable.getLevel()</code> * @see Variable#getLevel() */ private byte m_level; /** * The session object that was used when creating this variable, if known. */ private Session m_session; /** * My parent's <code>m_nonProtoId</code>. In other words, either my * parent's ID, or else my parent's parent's ID if my parent is <code>__proto__</code>. */ long m_nonProtoParentId; /** * The worker to which this variable belongs. */ private int m_isolateId; /** * Create a variable and its value. * * @param name * the name of the variable within the context of its parent. For example, * when resolving member "bar" of object "foo", the name will be "bar". * @param value * the variable's value. */ public DVariable(String name, DValue value, int isolateId) { m_rawName = name; m_attribs = value.getAttributes(); // If the name contains "::", then the name is of the form "namespace::name" if (name != null) { /** * anirudhs - Compute namespace only for non-public variables. * This check helps us avoid cases where public variables have * dynamic keys with :: in them. See FB-26126. */ if (!isAttributeSet(VariableAttribute.PUBLIC_SCOPE)) { int doubleColon = name.lastIndexOf("::"); //$NON-NLS-1$ if (doubleColon >= 0) { m_namespace = name.substring(0, doubleColon); int at = m_namespace.indexOf('@'); if (at != -1) m_namespace = m_namespace.substring(0, at); name = name.substring(doubleColon+2); } } } m_name = name; m_nonProtoParentId = Value.UNKNOWN_ID; m_value = value; value.setSession(m_session); m_isolateId = isolateId; } /* getters/setters */ public String getName() { return m_name; } public int getAttributes() { return m_attribs; } public String getNamespace() { return m_namespace; } public int getScope() { return m_attribs & VariableAttribute.SCOPE_MASK; } public int getLevel() { return m_level; } public String getDefiningClass() { return m_definingClass; } public int getIsolateId() { return m_isolateId; } public void makePublic() { int attributes = getAttributes(); attributes &= ~VariableAttribute.SCOPE_MASK; attributes |= VariableAttribute.PUBLIC_SCOPE; setAttributes(attributes); m_namespace = ""; //$NON-NLS-1$ } /* * @see flash.tools.debugger.Variable#getValue() */ public Value getValue() { if (m_session != null && m_session.getPreference(SessionManager.PREF_INVOKE_GETTERS) != 0) { try { invokeGetter(m_session); } catch (NotSuspendedException e) { // fall through -- return raw value without invoking getter } catch (NoResponseException e) { // fall through -- return raw value without invoking getter } catch (NotConnectedException e) { // fall through -- return raw value without invoking getter } } return m_value; } /* * @see flash.tools.debugger.Variable#hasValueChanged(flash.tools.debugger.Session) */ public boolean hasValueChanged(Session s) { boolean hasValueChanged = false; if (s instanceof PlayerSession) { Value previousParent = ((PlayerSession)s).getPreviousValue(m_nonProtoParentId, m_isolateId); if (previousParent != null) { try { Variable previousMember = previousParent.getMemberNamed(null, getName()); // If the old variable had a getter but never invoked that getter, // then it's too late, we don't know the old value. if (previousMember instanceof DVariable && !previousMember.needsToInvokeGetter()) { Value previousValue = ((DVariable)previousMember).m_value; if (previousValue != null) { String previousValueAsString = previousValue.getValueAsString(); if (previousValueAsString != null) { if (!previousValueAsString.equals(getValue().getValueAsString())) { hasValueChanged = true; } } } } } catch (PlayerDebugException e) { // ignore } } } return hasValueChanged; } /* * @see flash.tools.debugger.Session#setScalarMember(int, java.lang.String, int, java.lang.String) */ public FaultEvent setValue(Session s, int type, String value) throws NotSuspendedException, NoResponseException, NotConnectedException { return ((PlayerSession)s).setScalarMember(m_nonProtoParentId, m_rawName, type, value, m_isolateId); } /* * @see flash.tools.debugger.Variable#isAttributeSet(int) */ public boolean isAttributeSet(int att) { if ((att & VariableAttribute.SCOPE_MASK) == att) return (getScope() == att); else return ( ( (getAttributes() & att) == att) ? true : false ); } public void clearAttribute(int att) { if ((att & VariableAttribute.SCOPE_MASK) == att) m_attribs = (m_attribs & ~VariableAttribute.SCOPE_MASK) | VariableAttribute.PUBLIC_SCOPE; else m_attribs &= ~att; } public void setAttribute(int att) { if ((att & VariableAttribute.SCOPE_MASK) == att) m_attribs = (m_attribs & ~VariableAttribute.SCOPE_MASK) | att; else m_attribs |= att; } public String getRawName() { return m_rawName; } /* * @see flash.tools.debugger.Variable#getQualifiedName() */ public String getQualifiedName() { if (m_namespace.length() > 0) return m_namespace + "::" + m_name; //$NON-NLS-1$ else return m_name; } /** * Comparator interface for sorting Variables */ public int compareTo(Object o2) { Variable v2 = (Variable)o2; String n1 = getName(); String n2 = v2.getName(); return String.CASE_INSENSITIVE_ORDER.compare(n1, n2); } /* * @see flash.tools.debugger.Variable#needsToFireGetter() */ public boolean needsToInvokeGetter() { // If this variable has a getter, and the getter has not yet been invoked return (isAttributeSet(VariableAttribute.HAS_GETTER) && m_value.getId() != Value.UNKNOWN_ID && !m_firedGetter); } /* * @see flash.tools.debugger.Value#invokeGetter(flash.tools.debugger.Session) */ public void invokeGetter(Session s) throws NotSuspendedException, NoResponseException, NotConnectedException { if (needsToInvokeGetter()) { PlayerSession playerSession = (PlayerSession) s; // If this Variable is stale (that is, the program has run since this Variable // was created), then we can't invoke the getter. if (playerSession.getRawValue(m_value.getId(), m_isolateId) == m_value) { // temporarily turn on "invoke getters" preference int oldInvokeGetters = playerSession.getPreference(SessionManager.PREF_INVOKE_GETTERS); playerSession.setPreference(SessionManager.PREF_INVOKE_GETTERS, 1); try { // fire the getter using the original object id. make sure we get something reasonable back Value v = playerSession.getValue(m_nonProtoParentId, getRawName(), m_isolateId); if (v != null) { m_value = v; m_firedGetter = true; if (m_value instanceof DValue) ((DValue)m_value).setSession(s); } } finally { playerSession.setPreference(SessionManager.PREF_INVOKE_GETTERS, oldInvokeGetters); } } } } public void setName(String s) { m_name = s; } public void setAttributes(int f) { m_attribs = f; ((DValue)getValue()).setAttributes(f); } public void setSession(Session s) { m_session = s; if (m_value instanceof DValue) ((DValue)m_value).setSession(s); } public void setDefiningClass(int level, String definingClass) { m_level = (byte) Math.min(level, 255); m_definingClass = definingClass; } /** * Added so that expressions such as <code>a.b.c = e.f</code> work in the command-line interface. * @see Context#lookup(Object) */ @Override public String toString() { return getValue().getValueAsString(); } /** * Return the internal player string type representation for this variable. * Currently used for passing in the type to the Player when doing * a set variable command */ public static String typeNameFor(int type) { String s = "string"; //$NON-NLS-1$ switch(type) { case VariableType.NUMBER: s = "number"; //$NON-NLS-1$ break; case VariableType.BOOLEAN: s = "boolean"; //$NON-NLS-1$ break; case VariableType.STRING: s = "string"; //$NON-NLS-1$ break; case VariableType.OBJECT: s = "object"; //$NON-NLS-1$ break; case VariableType.FUNCTION: s = "function"; //$NON-NLS-1$ break; case VariableType.MOVIECLIP: s = "movieclip"; //$NON-NLS-1$ break; case VariableType.NULL: s = "null"; //$NON-NLS-1$ break; case VariableType.UNDEFINED: case VariableType.UNKNOWN: default: s = "undefined"; //$NON-NLS-1$ break; } return s; } /** * These values are obtained directly from the Player. * See ScriptObject in splay.h. */ public static final int kNormalObjectType = 0; public static final int kXMLSocketObjectType = 1; public static final int kTextFieldObjectType = 2; public static final int kButtonObjectType = 3; public static final int kNumberObjectType = 4; public static final int kBooleanObjectType = 5; public static final int kNativeStringObject = 6; public static final int kNativeArrayObject = 7; public static final int kDateObjectType = 8; public static final int kSoundObjectType = 9; public static final int kNativeXMLDoc = 10; public static final int kNativeXMLNode = 11; public static final int kNativeCameraObject = 12; public static final int kNativeMicrophoneObject = 13; public static final int kNativeCommunicationObject = 14; public static final int kNetConnectionObjectType = 15; public static final int kNetStreamObjectType = 16; public static final int kVideoObjectType = 17; public static final int kTextFormatObjectType = 18; public static final int kSharedObjectType = 19; public static final int kSharedObjectDataType = 20; public static final int kPrintJobObjectType = 21; public static final int kMovieClipLoaderObjectType = 22; public static final int kStyleSheetObjectType = 23; public static final int kFapPacketDummyObject = 24; public static final int kLoadVarsObject = 25; public static final int kTextSnapshotType = 26; public static String classNameFor(long clsType, boolean isMc) { String clsName; switch ((int)clsType) { case kNormalObjectType: clsName = (isMc) ? "MovieClip" : "Object"; //$NON-NLS-1$ //$NON-NLS-2$ break; case kXMLSocketObjectType: clsName = "XMLSocket"; //$NON-NLS-1$ break; case kTextFieldObjectType: clsName = "TextField"; //$NON-NLS-1$ break; case kButtonObjectType: clsName = "Button"; //$NON-NLS-1$ break; case kNumberObjectType: clsName = "Number"; //$NON-NLS-1$ break; case kBooleanObjectType: clsName = "Boolean"; //$NON-NLS-1$ break; case kNativeStringObject: clsName = "String"; //$NON-NLS-1$ break; case kNativeArrayObject: clsName = "Array"; //$NON-NLS-1$ break; case kDateObjectType: clsName = "Date"; //$NON-NLS-1$ break; case kSoundObjectType: clsName = "Sound"; //$NON-NLS-1$ break; case kNativeXMLDoc: clsName = "XML"; //$NON-NLS-1$ break; case kNativeXMLNode: clsName = "XMLNode"; //$NON-NLS-1$ break; case kNativeCameraObject: clsName = "Camera"; //$NON-NLS-1$ break; case kNativeMicrophoneObject: clsName = "Microphone"; //$NON-NLS-1$ break; case kNativeCommunicationObject: clsName = "Communication"; //$NON-NLS-1$ break; case kNetConnectionObjectType: clsName = "Connection"; //$NON-NLS-1$ break; case kNetStreamObjectType: clsName = "Stream"; //$NON-NLS-1$ break; case kVideoObjectType: clsName = "Video"; //$NON-NLS-1$ break; case kTextFormatObjectType: clsName = "TextFormat"; //$NON-NLS-1$ break; case kSharedObjectType: clsName = "SharedObject"; //$NON-NLS-1$ break; case kSharedObjectDataType: clsName = "SharedObjectData"; //$NON-NLS-1$ break; case kPrintJobObjectType: clsName = "PrintJob"; //$NON-NLS-1$ break; case kMovieClipLoaderObjectType: clsName = "MovieClipLoader"; //$NON-NLS-1$ break; case kStyleSheetObjectType: clsName = "StyleSheet"; //$NON-NLS-1$ break; case kFapPacketDummyObject: clsName = "FapPacket"; //$NON-NLS-1$ break; case kLoadVarsObject: clsName = "LoadVars"; //$NON-NLS-1$ break; case kTextSnapshotType: clsName = "TextSnapshot"; //$NON-NLS-1$ break; default: clsName = PlayerSessionManager.getLocalizationManager().getLocalizedTextString("unknown") + "<" + clsType + ">"; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ break; } return clsName; } }