gt:                          typeHint == Integer.TYPE ||
context: /* -*- Mode: java; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
   *
   * This Source Code Form is subject to the terms of the Mozilla Public
   * License, v. 2.0. If a copy of the MPL was not distributed with this
   * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
  
  // API class
  
  package org.mozilla.javascript;
  
  import java.io.IOException;
  import java.io.ObjectInputStream;
  import java.io.ObjectOutputStream;
  import java.io.Serializable;
  import java.lang.annotation.Annotation;
  import java.lang.reflect.AccessibleObject;
  import java.lang.reflect.Constructor;
  import java.lang.reflect.InvocationTargetException;
  import java.lang.reflect.Member;
  import java.lang.reflect.Method;
  import java.lang.reflect.Modifier;
  import java.util.HashMap;
  import java.util.HashSet;
  import java.util.Map;
  
  import org.mozilla.javascript.debug.DebuggableObject;
  import org.mozilla.javascript.annotations.JSConstructor;
  import org.mozilla.javascript.annotations.JSFunction;
  import org.mozilla.javascript.annotations.JSGetter;
  import org.mozilla.javascript.annotations.JSSetter;
  import org.mozilla.javascript.annotations.JSStaticFunction;
  
  /**
   * This is the default implementation of the Scriptable interface. This
   * class provides convenient default behavior that makes it easier to
   * define host objects.
   * <p>
   * Various properties and methods of JavaScript objects can be conveniently
   * defined using methods of ScriptableObject.
   * <p>
   * Classes extending ScriptableObject must define the getClassName method.
   *
   * @see org.mozilla.javascript.Scriptable
   * @author Norris Boyd
   */
  
  public abstract class ScriptableObject implements Scriptable, Serializable,
                                                    DebuggableObject,
                                                    ConstProperties
  {
  
      static final long serialVersionUID = 2829861078851942586L;
      
      /**
       * The empty property attribute.
       *
       * Used by getAttributes() and setAttributes().
       *
       * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
       * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
       */
      public static final int EMPTY =     0x00;
  
      /**
       * Property attribute indicating assignment to this property is ignored.
       *
       * @see org.mozilla.javascript.ScriptableObject
       *      #put(String, Scriptable, Object)
       * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
       * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
       */
      public static final int READONLY =  0x01;
  
      /**
       * Property attribute indicating property is not enumerated.
       *
       * Only enumerated properties will be returned by getIds().
       *
       * @see org.mozilla.javascript.ScriptableObject#getIds()
       * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
       * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
       */
      public static final int DONTENUM =  0x02;
  
      /**
       * Property attribute indicating property cannot be deleted.
       *
       * @see org.mozilla.javascript.ScriptableObject#delete(String)
       * @see org.mozilla.javascript.ScriptableObject#getAttributes(String)
       * @see org.mozilla.javascript.ScriptableObject#setAttributes(String, int)
       */
      public static final int PERMANENT = 0x04;
  
      /**
       * Property attribute indicating that this is a const property that has not
       * been assigned yet.  The first 'const' assignment to the property will
       * clear this bit.
       */
      public static final int UNINITIALIZED_CONST = 0x08;
  
      public static final int CONST = PERMANENT|READONLY|UNINITIALIZED_CONST;
      /**
       * The prototype of this object.
       */
      private Scriptable prototypeObject;
  
      /**
       * The parent scope of this object.
       */
      private Scriptable parentScopeObject;
  
      private transient Slot[] slots;
      // If count >= 0, it gives number of keys or if count < 0,
      // it indicates sealed object where ~count gives number of keys
      private int count;
  
      // gateways into the definition-order linked list of slots
      private transient Slot firstAdded;
      private transient Slot lastAdded;
  
  
      private volatile Map<Object,Object> associatedValues;
  
      private static final int SLOT_QUERY = 1;
      private static final int SLOT_MODIFY = 2;
      private static final int SLOT_MODIFY_CONST = 3;
      private static final int SLOT_MODIFY_GETTER_SETTER = 4;
      private static final int SLOT_CONVERT_ACCESSOR_TO_DATA = 5;
  
      // initial slot array size, must be a power of 2
      private static final int INITIAL_SLOT_SIZE = 4;
  
      private boolean isExtensible = true;
  
      private static class Slot implements Serializable
      {
          private static final long serialVersionUID = -6090581677123995491L;
          String name; // This can change due to caching
          int indexOrHash;
          private volatile short attributes;
          transient volatile boolean wasDeleted;
          volatile Object value;
          transient Slot next; // next in hash table bucket
          transient volatile Slot orderedNext; // next in linked list
  
          Slot(String name, int indexOrHash, int attributes)
          {
              this.name = name;
              this.indexOrHash = indexOrHash;
              this.attributes = (short)attributes;
          }
  
          private void readObject(ObjectInputStream in)
              throws IOException, ClassNotFoundException
          {
              in.defaultReadObject();
              if (name != null) {
                  indexOrHash = name.hashCode();
              }
          }
  
          boolean setValue(Object value, Scriptable owner, Scriptable start) {
              if ((attributes & READONLY) != 0) {
                  return true;
              }
              if (owner == start) {
                  this.value = value;
                  return true;
              } else {
                  return false;
              }
          }
  
          Object getValue(Scriptable start) {
              return value;
          }
  
          int getAttributes()
          {
              return attributes;
          }
  
          synchronized void setAttributes(int value)
          {
              checkValidAttributes(value);
              attributes = (short)value;
          }
  
          void markDeleted() {
              wasDeleted = true;
              value = null;
              name = null;
          }
  
          ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) {
              return buildDataDescriptor(scope, value, attributes);
          }
  
      }
  
      protected static ScriptableObject buildDataDescriptor(Scriptable scope,
                                                            Object value,
                                                            int attributes) {
          ScriptableObject desc = new NativeObject();
          ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object);
          desc.defineProperty("value", value, EMPTY);
          desc.defineProperty("writable", (attributes & READONLY) == 0, EMPTY);
          desc.defineProperty("enumerable", (attributes & DONTENUM) == 0, EMPTY);
          desc.defineProperty("configurable", (attributes & PERMANENT) == 0, EMPTY);
          return desc;
      }
  
      private static final class GetterSlot extends Slot
      {
          static final long serialVersionUID = -4900574849788797588L;
  
          Object getter;
          Object setter;
  
          GetterSlot(String name, int indexOrHash, int attributes)
          {
              super(name, indexOrHash, attributes);
          }
  
          @Override
          ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) {
              int attr = getAttributes();
              ScriptableObject desc = new NativeObject();
              ScriptRuntime.setBuiltinProtoAndParent(desc, scope, TopLevel.Builtins.Object);
              desc.defineProperty("enumerable", (attr & DONTENUM) == 0, EMPTY);
              desc.defineProperty("configurable", (attr & PERMANENT) == 0, EMPTY);
              if (getter != null) desc.defineProperty("get", getter, EMPTY);
              if (setter != null) desc.defineProperty("set", setter, EMPTY);
              return desc;
          }
  
          @Override
          boolean setValue(Object value, Scriptable owner, Scriptable start) {
              if (setter == null) {
                  if (getter != null) {
                      if (Context.getContext().hasFeature(Context.FEATURE_STRICT_MODE)) {
                          // Based on TC39 ES3.1 Draft of 9-Feb-2009, 8.12.4, step 2,
                          // we should throw a TypeError in this case.
                          throw ScriptRuntime.typeError1("msg.set.prop.no.setter", name);
                      }
                      // Assignment to a property with only a getter defined. The
                      // assignment is ignored. See bug 478047.
                      return true;
                  }
              } else {
                  Context cx = Context.getContext();
                  if (setter instanceof MemberBox) {
                      MemberBox nativeSetter = (MemberBox)setter;
                      Class<?> pTypes[] = nativeSetter.argTypes;
                      // XXX: cache tag since it is already calculated in
                      // defineProperty ?
                      Class<?> valueType = pTypes[pTypes.length - 1];
                      int tag = FunctionObject.getTypeTag(valueType);
                      Object actualArg = FunctionObject.convertArg(cx, start,
                                                                   value, tag);
                      Object setterThis;
                      Object[] args;
                      if (nativeSetter.delegateTo == null) {
                          setterThis = start;
                          args = new Object[] { actualArg };
                      } else {
                          setterThis = nativeSetter.delegateTo;
                          args = new Object[] { start, actualArg };
                      }
                      nativeSetter.invoke(setterThis, args);
                  } else if (setter instanceof Function) {
                      Function f = (Function)setter;
                      f.call(cx, f.getParentScope(), start,
                             new Object[] { value });
                  }
                  return true;
              }
              return super.setValue(value, owner, start);
          }
  
          @Override
          Object getValue(Scriptable start) {
              if (getter != null) {
                  if (getter instanceof MemberBox) {
                      MemberBox nativeGetter = (MemberBox)getter;
                      Object getterThis;
                      Object[] args;
                      if (nativeGetter.delegateTo == null) {
                          getterThis = start;
                          args = ScriptRuntime.emptyArgs;
                      } else {
                          getterThis = nativeGetter.delegateTo;
                          args = new Object[] { start };
                      }
                      return nativeGetter.invoke(getterThis, args);
                  } else if (getter instanceof Function) {
                      Function f = (Function)getter;
                      Context cx = Context.getContext();
                      return f.call(cx, f.getParentScope(), start,
                                    ScriptRuntime.emptyArgs);
                  }
              }
              Object val = this.value;
              if (val instanceof LazilyLoadedCtor) {
                  LazilyLoadedCtor initializer = (LazilyLoadedCtor)val;
                  try {
                      initializer.init();
                  } finally {
                      this.value = val = initializer.getValue();
                  }
              }
              return val;
          }
  
          @Override
          void markDeleted() {
              super.markDeleted();
              getter = null;
              setter = null;
          }
      }
  
      /**
       * A wrapper around a slot that allows the slot to be used in a new slot
       * table while keeping it functioning in its old slot table/linked list
       * context. This is used when linked slots are copied to a new slot table.
       * In a multi-threaded environment, these slots may still be accessed
       * through their old slot table. See bug 688458.
       */
      private static class RelinkedSlot extends Slot {
  
          final Slot slot;
  
          RelinkedSlot(Slot slot) {
              super(slot.name, slot.indexOrHash, slot.attributes);
              // Make sure we always wrap the actual slot, not another relinked one
              this.slot = unwrapSlot(slot);
          }
  
          @Override
          boolean setValue(Object value, Scriptable owner, Scriptable start) {
              return slot.setValue(value, owner, start);
          }
  
          @Override
          Object getValue(Scriptable start) {
              return slot.getValue(start);
          }
  
          @Override
          ScriptableObject getPropertyDescriptor(Context cx, Scriptable scope) {
              return slot.getPropertyDescriptor(cx, scope);
          }
  
          @Override
          int getAttributes() {
              return slot.getAttributes();
          }
  
          @Override
          void setAttributes(int value) {
              slot.setAttributes(value);
          }
  
          @Override
          void markDeleted() {
              super.markDeleted();
              slot.markDeleted();
          }
  
          private void writeObject(ObjectOutputStream out) throws IOException {
              out.writeObject(slot);  // just serialize the wrapped slot
          }
  
      }
  
      static void checkValidAttributes(int attributes)
      {
          final int mask = READONLY | DONTENUM | PERMANENT | UNINITIALIZED_CONST;
          if ((attributes & ~mask) != 0) {
              throw new IllegalArgumentException(String.valueOf(attributes));
          }
      }
  
      public ScriptableObject()
      {
      }
  
      public ScriptableObject(Scriptable scope, Scriptable prototype)
      {
          if (scope == null)
              throw new IllegalArgumentException();
  
          parentScopeObject = scope;
          prototypeObject = prototype;
      }
  
      /**
       * Gets the value that will be returned by calling the typeof operator on this object.
       * @return default is "object" unless {@link #avoidObjectDetection()} is <code>true</code> in which
       * case it returns "undefined"
       */
      public String getTypeOf() {
      	return avoidObjectDetection() ? "undefined" : "object";
      }
  
      /**
       * Return the name of the class.
       *
       * This is typically the same name as the constructor.
       * Classes extending ScriptableObject must implement this abstract
       * method.
       */
      public abstract String getClassName();
  
      /**
       * Returns true if the named property is defined.
       *
       * @param name the name of the property
       * @param start the object in which the lookup began
       * @return true if and only if the property was found in the object
       */
      public boolean has(String name, Scriptable start)
      {
          return null != getSlot(name, 0, SLOT_QUERY);
      }
  
      /**
       * Returns true if the property index is defined.
       *
       * @param index the numeric index for the property
       * @param start the object in which the lookup began
       * @return true if and only if the property was found in the object
       */
      public boolean has(int index, Scriptable start)
      {
          return null != getSlot(null, index, SLOT_QUERY);
      }
  
      /**
       * Returns the value of the named property or NOT_FOUND.
       *
       * If the property was created using defineProperty, the
       * appropriate getter method is called.
       *
       * @param name the name of the property
       * @param start the object in which the lookup began
       * @return the value of the property (may be null), or NOT_FOUND
       */
      public Object get(String name, Scriptable start)
      {
          Slot slot = getSlot(name, 0, SLOT_QUERY);
          if (slot == null) {
              return Scriptable.NOT_FOUND;
          }
          return slot.getValue(start);
      }
  
      /**
       * Returns the value of the indexed property or NOT_FOUND.
       *
       * @param index the numeric index for the property
       * @param start the object in which the lookup began
       * @return the value of the property (may be null), or NOT_FOUND
       */
      public Object get(int index, Scriptable start)
      {
          Slot slot = getSlot(null, index, SLOT_QUERY);
          if (slot == null) {
              return Scriptable.NOT_FOUND;
          }
          return slot.getValue(start);
      }
  
      /**
       * Sets the value of the named property, creating it if need be.
       *
       * If the property was created using defineProperty, the
       * appropriate setter method is called. <p>
       *
       * If the property's attributes include READONLY, no action is
       * taken.
       * This method will actually set the property in the start
       * object.
       *
       * @param name the name of the property
       * @param start the object whose property is being set
       * @param value value to set the property to
       */
      public void put(String name, Scriptable start, Object value)
      {
          if (putImpl(name, 0, start, value))
              return;
  
          if (start == this) throw Kit.codeBug();
          start.put(name, start, value);
      }
  
      /**
       * Sets the value of the indexed property, creating it if need be.
       *
       * @param index the numeric index for the property
       * @param start the object whose property is being set
       * @param value value to set the property to
       */
      public void put(int index, Scriptable start, Object value)
      {
          if (putImpl(null, index, start, value))
              return;
  
          if (start == this) throw Kit.codeBug();
          start.put(index, start, value);
      }
  
      /**
       * Removes a named property from the object.
       *
       * If the property is not found, or it has the PERMANENT attribute,
       * no action is taken.
       *
       * @param name the name of the property
       */
      public void delete(String name)
      {
          checkNotSealed(name, 0);
          removeSlot(name, 0);
      }
  
      /**
       * Removes the indexed property from the object.
       *
       * If the property is not found, or it has the PERMANENT attribute,
       * no action is taken.
       *
       * @param index the numeric index for the property
       */
      public void delete(int index)
      {
          checkNotSealed(null, index);
          removeSlot(null, index);
      }
  
      /**
       * Sets the value of the named const property, creating it if need be.
       *
       * If the property was created using defineProperty, the
       * appropriate setter method is called. <p>
       *
       * If the property's attributes include READONLY, no action is
       * taken.
       * This method will actually set the property in the start
       * object.
       *
       * @param name the name of the property
       * @param start the object whose property is being set
       * @param value value to set the property to
       */
      public void putConst(String name, Scriptable start, Object value)
      {
          if (putConstImpl(name, 0, start, value, READONLY))
              return;
  
          if (start == this) throw Kit.codeBug();
          if (start instanceof ConstProperties)
              ((ConstProperties)start).putConst(name, start, value);
          else
              start.put(name, start, value);
      }
  
      public void defineConst(String name, Scriptable start)
      {
          if (putConstImpl(name, 0, start, Undefined.instance, UNINITIALIZED_CONST))
              return;
  
          if (start == this) throw Kit.codeBug();
          if (start instanceof ConstProperties)
              ((ConstProperties)start).defineConst(name, start);
      }
      /**
       * Returns true if the named property is defined as a const on this object.
       * @param name
       * @return true if the named property is defined as a const, false
       * otherwise.
       */
      public boolean isConst(String name)
      {
          Slot slot = getSlot(name, 0, SLOT_QUERY);
          if (slot == null) {
              return false;
          }
          return (slot.getAttributes() & (PERMANENT|READONLY)) ==
                                         (PERMANENT|READONLY);
  
      }
      /**
       * @deprecated Use {@link #getAttributes(String name)}. The engine always
       * ignored the start argument.
       */
      public final int getAttributes(String name, Scriptable start)
      {
          return getAttributes(name);
      }
  
      /**
       * @deprecated Use {@link #getAttributes(int index)}. The engine always
       * ignored the start argument.
       */
      public final int getAttributes(int index, Scriptable start)
      {
          return getAttributes(index);
      }
  
      /**
       * @deprecated Use {@link #setAttributes(String name, int attributes)}.
       * The engine always ignored the start argument.
       */
      public final void setAttributes(String name, Scriptable start,
                                      int attributes)
      {
          setAttributes(name, attributes);
      }
  
      /**
       * @deprecated Use {@link #setAttributes(int index, int attributes)}.
       * The engine always ignored the start argument.
       */
      public void setAttributes(int index, Scriptable start,
                                int attributes)
      {
          setAttributes(index, attributes);
      }
  
      /**
       * Get the attributes of a named property.
       *
       * The property is specified by <code>name</code>
       * as defined for <code>has</code>.<p>
       *
       * @param name the identifier for the property
       * @return the bitset of attributes
       * @exception EvaluatorException if the named property is not found
       * @see org.mozilla.javascript.ScriptableObject#has(String, Scriptable)
       * @see org.mozilla.javascript.ScriptableObject#READONLY
       * @see org.mozilla.javascript.ScriptableObject#DONTENUM
       * @see org.mozilla.javascript.ScriptableObject#PERMANENT
       * @see org.mozilla.javascript.ScriptableObject#EMPTY
       */
      public int getAttributes(String name)
      {
          return findAttributeSlot(name, 0, SLOT_QUERY).getAttributes();
      }
  
      /**
       * Get the attributes of an indexed property.
       *
       * @param index the numeric index for the property
       * @exception EvaluatorException if the named property is not found
       *            is not found
       * @return the bitset of attributes
       * @see org.mozilla.javascript.ScriptableObject#has(String, Scriptable)
       * @see org.mozilla.javascript.ScriptableObject#READONLY
       * @see org.mozilla.javascript.ScriptableObject#DONTENUM
       * @see org.mozilla.javascript.ScriptableObject#PERMANENT
       * @see org.mozilla.javascript.ScriptableObject#EMPTY
       */
      public int getAttributes(int index)
      {
          return findAttributeSlot(null, index, SLOT_QUERY).getAttributes();
      }
  
      /**
       * Set the attributes of a named property.
       *
       * The property is specified by <code>name</code>
       * as defined for <code>has</code>.<p>
       *
       * The possible attributes are READONLY, DONTENUM,
       * and PERMANENT. Combinations of attributes
       * are expressed by the bitwise OR of attributes.
       * EMPTY is the state of no attributes set. Any unused
       * bits are reserved for future use.
       *
       * @param name the name of the property
       * @param attributes the bitset of attributes
       * @exception EvaluatorException if the named property is not found
       * @see org.mozilla.javascript.Scriptable#has(String, Scriptable)
       * @see org.mozilla.javascript.ScriptableObject#READONLY
       * @see org.mozilla.javascript.ScriptableObject#DONTENUM
       * @see org.mozilla.javascript.ScriptableObject#PERMANENT
       * @see org.mozilla.javascript.ScriptableObject#EMPTY
       */
      public void setAttributes(String name, int attributes)
      {
          checkNotSealed(name, 0);
          findAttributeSlot(name, 0, SLOT_MODIFY).setAttributes(attributes);
      }
  
      /**
       * Set the attributes of an indexed property.
       *
       * @param index the numeric index for the property
       * @param attributes the bitset of attributes
       * @exception EvaluatorException if the named property is not found
       * @see org.mozilla.javascript.Scriptable#has(String, Scriptable)
       * @see org.mozilla.javascript.ScriptableObject#READONLY
       * @see org.mozilla.javascript.ScriptableObject#DONTENUM
       * @see org.mozilla.javascript.ScriptableObject#PERMANENT
       * @see org.mozilla.javascript.ScriptableObject#EMPTY
       */
      public void setAttributes(int index, int attributes)
      {
          checkNotSealed(null, index);
          findAttributeSlot(null, index, SLOT_MODIFY).setAttributes(attributes);
      }
  
      /**
       * XXX: write docs.
       */
      public void setGetterOrSetter(String name, int index,
                                    Callable getterOrSetter, boolean isSetter)
      {
          setGetterOrSetter(name, index, getterOrSetter, isSetter, false);
      }
  
      private void setGetterOrSetter(String name, int index, Callable getterOrSetter,
                                     boolean isSetter, boolean force)
      {
          if (name != null && index != 0)
              throw new IllegalArgumentException(name);
  
          if (!force) {
              checkNotSealed(name, index);
          }
  
          final GetterSlot gslot;
          if (isExtensible()) {
              gslot = (GetterSlot)getSlot(name, index, SLOT_MODIFY_GETTER_SETTER);
          } else {
              Slot slot = unwrapSlot(getSlot(name, index, SLOT_QUERY));
              if (!(slot instanceof GetterSlot))
                  return;
              gslot = (GetterSlot) slot;
          }
  
          if (!force) {
              int attributes = gslot.getAttributes();
              if ((attributes & READONLY) != 0) {
                  throw Context.reportRuntimeError1("msg.modify.readonly", name);
              }
          }
          if (isSetter) {
              gslot.setter = getterOrSetter;
          } else {
              gslot.getter = getterOrSetter;
          }
          gslot.value = Undefined.instance;
      }
  
      /**
       * Get the getter or setter for a given property. Used by __lookupGetter__
       * and __lookupSetter__.
       *
       * @param name Name of the object. If nonnull, index must be 0.
       * @param index Index of the object. If nonzero, name must be null.
       * @param isSetter If true, return the setter, otherwise return the getter.
       * @exception IllegalArgumentException if both name and index are nonnull
       *            and nonzero respectively.
       * @return Null if the property does not exist. Otherwise returns either
       *         the getter or the setter for the property, depending on
       *         the value of isSetter (may be undefined if unset).
       */
      public Object getGetterOrSetter(String name, int index, boolean isSetter)
      {
          if (name != null && index != 0)
              throw new IllegalArgumentException(name);
          Slot slot = unwrapSlot(getSlot(name, index, SLOT_QUERY));
          if (slot == null)
              return null;
          if (slot instanceof GetterSlot) {
              GetterSlot gslot = (GetterSlot)slot;
              Object result = isSetter ? gslot.setter : gslot.getter;
              return result != null ? result : Undefined.instance;
          } else
              return Undefined.instance;
      }
  
      /**
       * Returns whether a property is a getter or a setter
       * @param name property name
       * @param index property index
       * @param setter true to check for a setter, false for a getter
       * @return whether the property is a getter or a setter
       */
      protected boolean isGetterOrSetter(String name, int index, boolean setter) {
          Slot slot = unwrapSlot(getSlot(name, index, SLOT_QUERY));
          if (slot instanceof GetterSlot) {
              if (setter && ((GetterSlot)slot).setter != null) return true;
              if (!setter && ((GetterSlot)slot).getter != null) return true;
          }
          return false;
      }
  
      void addLazilyInitializedValue(String name, int index,
                                     LazilyLoadedCtor init, int attributes)
      {
          if (name != null && index != 0)
              throw new IllegalArgumentException(name);
          checkNotSealed(name, index);
          GetterSlot gslot = (GetterSlot)getSlot(name, index,
                                                 SLOT_MODIFY_GETTER_SETTER);
          gslot.setAttributes(attributes);
          gslot.getter = null;
          gslot.setter = null;
          gslot.value = init;
      }
  
      /**
       * Returns the prototype of the object.
       */
      public Scriptable getPrototype()
      {
          return prototypeObject;
      }
  
      /**
       * Sets the prototype of the object.
       */
      public void setPrototype(Scriptable m)
      {
          prototypeObject = m;
      }
  
      /**
       * Returns the parent (enclosing) scope of the object.
       */
      public Scriptable getParentScope()
      {
          return parentScopeObject;
      }
  
      /**
       * Sets the parent (enclosing) scope of the object.
       */
      public void setParentScope(Scriptable m)
      {
          parentScopeObject = m;
      }
  
      /**
       * Returns an array of ids for the properties of the object.
       *
       * <p>Any properties with the attribute DONTENUM are not listed. <p>
       *
       * @return an array of java.lang.Objects with an entry for every
       * listed property. Properties accessed via an integer index will
       * have a corresponding
       * Integer entry in the returned array. Properties accessed by
       * a String will have a String entry in the returned array.
       */
      public Object[] getIds() {
          return getIds(false);
      }
  
      /**
       * Returns an array of ids for the properties of the object.
       *
       * <p>All properties, even those with attribute DONTENUM, are listed. <p>
       *
       * @return an array of java.lang.Objects with an entry for every
       * listed property. Properties accessed via an integer index will
       * have a corresponding
       * Integer entry in the returned array. Properties accessed by
       * a String will have a String entry in the returned array.
       */
      public Object[] getAllIds() {
          return getIds(true);
      }
  
      /**
       * Implements the [[DefaultValue]] internal method.
       *
       * <p>Note that the toPrimitive conversion is a no-op for
       * every type other than Object, for which [[DefaultValue]]
       * is called. See ECMA 9.1.<p>
       *
       * A <code>hint</code> of null means "no hint".
       *
       * @param typeHint the type hint
       * @return the default value for the object
       *
       * See ECMA 8.6.2.6.
       */
      public Object getDefaultValue(Class<?> typeHint)
      {
          return getDefaultValue(this, typeHint);
      }
  
      public static Object getDefaultValue(Scriptable object, Class<?> typeHint)
      {
          Context cx = null;
          for (int i=0; i < 2; i++) {
              boolean tryToString;
              if (typeHint == ScriptRuntime.StringClass) {
                  tryToString = (i == 0);
              } else {
                  tryToString = (i == 1);
              }
  
              String methodName;
              Object[] args;
              if (tryToString) {
                  methodName = "toString";
                  args = ScriptRuntime.emptyArgs;
              } else {
                  methodName = "valueOf";
                  args = new Object[1];
                  String hint;
                  if (typeHint == null) {
                      hint = "undefined";
                  } else if (typeHint == ScriptRuntime.StringClass) {
                      hint = "string";
                  } else if (typeHint == ScriptRuntime.ScriptableClass) {
                      hint = "object";
                  } else if (typeHint == ScriptRuntime.FunctionClass) {
                      hint = "function";
                  } else if (typeHint == ScriptRuntime.BooleanClass
                             || typeHint == Boolean.TYPE)
                  {
                      hint = "boolean";
                  } else if (typeHint == ScriptRuntime.NumberClass ||
                           typeHint == ScriptRuntime.ByteClass ||
                           typeHint == Byte.TYPE ||
                           typeHint == ScriptRuntime.ShortClass ||
                           typeHint == Short.TYPE ||
                           typeHint == ScriptRuntime.IntegerClass ||
  