root/core/ScriptObject.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. delegate
  2. delegate
  3. initHashtable
  4. getTable
  5. getAtomProperty
  6. getAtomPropertyFromProtoChain
  7. hasMultinameProperty
  8. hasAtomProperty
  9. setAtomProperty
  10. setMultinameProperty
  11. getAtomPropertyIsEnumerable
  12. setAtomPropertyIsEnumerable
  13. deleteAtomProperty
  14. deleteMultinameProperty
  15. getUintProperty
  16. setUintProperty
  17. delUintProperty
  18. hasUintProperty
  19. getMultinameProperty
  20. callProperty
  21. constructProperty
  22. getDescendants
  23. isGlobalObject
  24. format
  25. defaultValue
  26. toString
  27. call
  28. construct
  29. applyTypeArgs
  30. getSlotAtom
  31. getSlotObject
  32. coerceAndSetSlotAtom
  33. nextName
  34. nextValue
  35. nextNameIndex
  36. createInstance
  37. size
  38. implToString

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (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.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine.].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 2004-2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */

#include "avmplus.h"
//#define DOPROF
//#include "../vprof/vprof.h"

namespace avmplus
{
        ScriptObject::ScriptObject(VTable* _vtable, ScriptObject* _delegate) :
#ifdef DEBUGGER 
                AvmPlusScriptableObject(sotObject(_vtable)), 
#endif // DEBUGGER
                vtable(_vtable),
                // note that it's substantially more efficient to initialize this in the ctor
                // list vs. a later explicit call to setDelegate, as we don't have to check for marking
                // nor decrement an existing value...
                delegate(_delegate)
        {
                AvmAssert(vtable->traits->isResolved());

                // Ensure that our object is large enough to hold its extra traits data.
                AvmAssert(MMgc::GC::Size(this) >= vtable->traits->getTotalSize());
        }

        ScriptObject::ScriptObject(VTable* _vtable, ScriptObject* _delegate, int capacity) :
#ifdef DEBUGGER 
                AvmPlusScriptableObject(sotObject(_vtable)), 
#endif // DEBUGGER
                vtable(_vtable),
                // note that it's substantially more efficient to initialize this in the ctor
                // list vs. a later explicit call to setDelegate, as we don't have to check for marking
                // nor decrement an existing value...
                delegate(_delegate)
        {
                AvmAssert(vtable->traits->isResolved());

                // Ensure that our object is large enough to hold its extra traits data.
                AvmAssert(MMgc::GC::Size(this) >= vtable->traits->getTotalSize());

                //if capacity not specified then initialize the hashtable lazily
                if (vtable->traits->needsHashtable() && capacity)
                {
                        initHashtable(capacity);
                }
        }

        ScriptObject::~ScriptObject()
        {
                //setDelegate(NULL); -- no longer necessary
                vtable->traits->destroyInstance(this);
        }
        
        void ScriptObject::initHashtable(int capacity /*=InlineHashtable::kDefaultCapacity*/)
        {
                AvmAssert(vtable->traits->isDictionary == 0); //should not be called DictionaryObject uses HeapHashtable
                
                MMGC_MEM_TYPE(this);
                union {
                        uint8_t* p;
                        InlineHashtable* iht;
                };
                p = (uint8_t*)this + vtable->traits->getHashtableOffset();
                iht->initialize(this->gc(), capacity);
                iht->setDontEnumSupport();
        }
        
        InlineHashtable* ScriptObject::getTable() const
        {
                AvmAssert(vtable->traits->getHashtableOffset() != 0);
                union {
                        uint8_t* p;
                        InlineHashtable* iht;
                        HeapHashtable** hht;
                };
                p = (uint8_t*)this + vtable->traits->getHashtableOffset();
                if(!vtable->traits->isDictionary)
                {
                        if (iht->getCapacity() == 0)
                                const_cast<ScriptObject*>(this)->initHashtable(); 
                        return iht;
                }
                else
                {
                        //DictionaryObjects store pointer to HeapHashtable at
                        //the hashtable offset
                        return (*hht)->get_ht();
                }
        }
        
    /**
     * traverse the delegate chain looking for a value.
     * [ed] it's okay to look only at the HT's in the delegate chain because
     * delegate values may only be instances of Object.  They cannot be objects
     * with slots.  We don't need to look at traits at each step.
     * todo - enforce this rule
     * @param name
     * @return
     */
        Atom ScriptObject::getAtomProperty(Atom name) const
        {
                if (!traits()->needsHashtable())
                {
                        return getAtomPropertyFromProtoChain(name, delegate, traits());
                }
                else
                {
                        Stringp s = core()->atomToString(name);
                        AvmAssert(s->isInterned());
                        Atom ival = s->getIntAtom();
                        if (ival)
                        {
                                name = ival;
                        }

                        // dynamic lookup on this object
                        const ScriptObject *o = this;
                        do
                        {
                                InlineHashtable *table = o->getTable();
                                const Atom* atoms = table->getAtoms();
                                int i = table->find(name, atoms, table->getCapacity());
                                if (atoms[i] != InlineHashtable::EMPTY)
                                        return atoms[i+1];
                        }
                        while ((o = o->delegate) != NULL);
                        return undefinedAtom;
                }                       
        }
        
        Atom ScriptObject::getAtomPropertyFromProtoChain(Atom name, ScriptObject* o, Traits *origObjTraits) const
        {
        // todo will delegate always be non-null here?
                if (o != NULL)
                {
                        Atom searchname = name;
                        Stringp s = core()->atomToString(name);
                        AvmAssert(s->isInterned());
                        Atom ival = s->getIntAtom();
                        if (ival)
                        {
                                searchname = ival;
                        }
                        do
                        {
                                const Atom* atoms = o->getTable()->getAtoms();
                                int i = o->getTable()->find(searchname, atoms, o->getTable()->getCapacity());
                                if (atoms[i] != InlineHashtable::EMPTY)
                                        return atoms[i+1];
                        }
                        while ((o = o->delegate) != NULL);
                }
                // NOTE use default public since name is not used
                Multiname multiname(core()->getAnyPublicNamespace(), AvmCore::atomToString(name));
                toplevel()->throwReferenceError(kReadSealedError, &multiname, origObjTraits);
                // unreached
                return undefinedAtom;
        }

        bool ScriptObject::hasMultinameProperty(const Multiname* multiname) const
        {
                if (traits()->needsHashtable() && multiname->isValidDynamicName())
                        {
                                return hasAtomProperty(multiname->getName()->atom());
                        }
                        else
                        {
                        // ISSUE should this walk the proto chain?
                        return false;
                }
        }

        bool ScriptObject::hasAtomProperty(Atom name) const
        {
                if (traits()->needsHashtable())
                {
                        Stringp s = core()->atomToString(name);
                        AvmAssert(s->isInterned());
                        Atom ival = s->getIntAtom();
                        if (ival)
                        {
                                name = ival;
                        }

                        return getTable()->contains(name);
                }
                else
                {
                        // ISSUE should this walk the proto chain?
                        return false;
                }
        }

    void ScriptObject::setAtomProperty(Atom name, Atom value)
    {
                if (traits()->needsHashtable())
                {
                        Stringp s = core()->atomToString(name);
                        AvmAssert(s->isInterned());
                        Atom ival = s->getIntAtom();
                        if (ival)
                        {
                                name = ival;
                        }

                        MMGC_MEM_TYPE(this);
                        getTable()->add (name, value);
                        MMGC_MEM_TYPE(NULL);
                }
                else
                {
                        // NOTE use default public since name is not used
                        Multiname multiname(core()->getAnyPublicNamespace(), AvmCore::atomToString(name));

                        // cannot create properties on a sealed object.
                        toplevel()->throwReferenceError(kWriteSealedError, &multiname, traits());
                }
    }

        void ScriptObject::setMultinameProperty(const Multiname* name, Atom value)
        {
                if (traits()->needsHashtable() && name->isValidDynamicName())
                {
                        setStringProperty(name->getName(), value);
                }
                else
                {
                        // cannot create properties on a sealed object.
                        toplevel()->throwReferenceError(kWriteSealedError, name, traits());
                }
        }

        bool ScriptObject::getAtomPropertyIsEnumerable(Atom name) const
        {
                if (traits()->needsHashtable())
                {
                        Stringp s = core()->atomToString(name);
                        AvmAssert(s->isInterned());
                        Atom ival = s->getIntAtom();
                        if (ival)
                        {
                                name = ival;
                        }

                        return getTable()->getAtomPropertyIsEnumerable(name);
                }
                else
                {
                        // ISSUE should this walk the proto chain?
                        return false;
                }
        }

        void ScriptObject::setAtomPropertyIsEnumerable(Atom name, bool enumerable)
        {
                if (traits()->needsHashtable())
                {
                        Stringp s = core()->atomToString(name);
                        AvmAssert(s->isInterned());
                        Atom ival = s->getIntAtom();
                        if (ival)
                        {
                                name = ival;
                        }

                        getTable()->setAtomPropertyIsEnumerable(name, enumerable);
                }
                else
                {
                        // cannot create properties on a sealed object. just use any public
                        Multiname multiname(core()->getAnyPublicNamespace(), AvmCore::atomToString(name));
                        toplevel()->throwReferenceError(kWriteSealedError, &multiname, traits());
                }
        }
        
        bool ScriptObject::deleteAtomProperty(Atom name)
        {
                if (traits()->needsHashtable())
                {
                        Stringp s = core()->atomToString(name);
                        AvmAssert(s->isInterned());
                        Atom ival = s->getIntAtom();
                        if (ival)
                        {
                                name = ival;
                        }

                        getTable()->remove(name);
                        return true;
                }
                else
                {
                        return false;
                }
        }
        
        bool ScriptObject::deleteMultinameProperty(const Multiname* name)
        {
                if (traits()->needsHashtable() && name->isValidDynamicName())
                {
                        return deleteStringProperty(name->getName());
                }
                else
                {
                        return false;
                }
        }

        Atom ScriptObject::getUintProperty(uint32 i) const
        {
                AvmCore* core = this->core();

                if (!(i&MAX_INTEGER_MASK))
                {
                        if (!traits()->needsHashtable())
                        {
                                Atom name = core->internUint32(i)->atom();
                                return getAtomPropertyFromProtoChain(name, delegate, traits());
                        }
                        else
                        {
                                // dynamic lookup on this object
                                Atom name = core->uintToAtom (i);
                                const ScriptObject *o = this;
                                do
                                {
                                        InlineHashtable *table = o->getTable();
                                        const Atom* atoms = table->getAtoms();
                                        int i = table->find(name, atoms, table->getCapacity());
                                        if (atoms[i] != InlineHashtable::EMPTY)
                                                return atoms[i+1];
                                }
                                while ((o = o->delegate) != NULL);
                                return undefinedAtom;
                        }                       
                }
                else
                {
                        return getAtomProperty(core->internUint32(i)->atom());
                }               
        }

        void ScriptObject::setUintProperty(uint32 i, Atom value)
        {
                AvmCore* core = this->core();
                if (!(i&MAX_INTEGER_MASK)) 
                {
                        Atom name = core->uintToAtom (i);
                        if (traits()->needsHashtable())
                        {
                                MMGC_MEM_TYPE(this);
                                getTable()->add(name, value);
                                MMGC_MEM_TYPE(NULL);
                        }
                        else
                        {
                                // NOTE use default public since name is not used
                                Multiname multiname(core->getAnyPublicNamespace(), core->string(name));

                                // cannot create properties on a sealed object.
                                toplevel()->throwReferenceError(kWriteSealedError, &multiname, traits());
                        }
                }
                else
                {
                        setAtomProperty(core->internUint32(i)->atom(), value);
                }
        }

        bool ScriptObject::delUintProperty(uint32 i)
        {
                AvmCore* core = this->core();
                if (!(i&MAX_INTEGER_MASK)) 
                {
                        Atom name = core->uintToAtom (i);
                        if (traits()->needsHashtable())
                        {
                                getTable()->remove(name);
                                return true;
                        }
                        else
                        {
                                return false;
                        }
                }
                else
                {
                        return deleteAtomProperty(core->internUint32(i)->atom());
                }
        }

        bool ScriptObject::hasUintProperty(uint32 i) const
        {
                AvmCore* core = this->core();
                if (!(i&MAX_INTEGER_MASK)) 
                {
                        Atom name = core->uintToAtom (i);
                        if (traits()->needsHashtable())
                        {
                                return getTable()->contains(name);
                        }
                        else
                        {
                                // ISSUE should this walk the proto chain?
                                return false;
                        }
                }
                else
                {
                        return hasAtomProperty(core->internUint32(i)->atom());
                }
        }

        Atom ScriptObject::getMultinameProperty(const Multiname* multiname) const
        {
                if (multiname->isValidDynamicName())
                {
                        return getStringProperty(multiname->getName());
                }
                else
                {
                        Toplevel* toplevel = this->toplevel();

                        if (multiname->isNsset())
                                toplevel->throwReferenceError(kReadSealedErrorNs, multiname, traits());
                        else
                                toplevel->throwReferenceError(kReadSealedError, multiname, traits());
                        return undefinedAtom;
                }
        }

        // this = argv[0] (ignored)
        // arg1 = argv[1]
        // argN = argv[argc]
        Atom ScriptObject::callProperty(const Multiname* multiname, int argc, Atom* argv)
        {
                Toplevel* toplevel = this->toplevel();
                Atom method = getMultinameProperty(multiname);
                if (!AvmCore::isObject(method))
                        toplevel->throwTypeError(kCallOfNonFunctionError, core()->toErrorString(multiname));
                argv[0] = atom(); // replace receiver
                return toplevel->op_call(method, argc, argv);
        }

        Atom ScriptObject::constructProperty(const Multiname* multiname, int argc, Atom* argv)
        {
                Atom ctor = getMultinameProperty(multiname);
                argv[0] = atom(); // replace receiver
                return toplevel()->op_construct(ctor, argc, argv);
        }
        
        Atom ScriptObject::getDescendants(const Multiname* /*name*/) const
        {
                toplevel()->throwTypeError(kDescendentsError, core()->toErrorString(traits()));
                return undefinedAtom;// not reached
        }

    bool ScriptObject::isGlobalObject() const
    {
        AvmAssert(vtable != 0);
        AvmAssert(vtable->init != 0);
                MethodEnv* init = vtable->init;
                if (!init->isScriptEnv())
                        return false;
        const ScriptEnv* const scriptInitForVTable = static_cast<const ScriptEnv*>(init);
        return scriptInitForVTable->global == this;
    }

#ifdef AVMPLUS_VERBOSE
        Stringp ScriptObject::format(AvmCore* core) const
        {
                if (traits()->name() != NULL) {
                        return core->concatStrings(traits()->format(core),
                                                                           core->concatStrings(core->newConstantStringLatin1("@"),
                                                                                                                   core->formatAtomPtr(atom())));
                } else {
                        return core->concatStrings(core->newConstantStringLatin1("{}@"),
                                                                           core->formatAtomPtr(atom()));
                }
        }
#endif

        Atom ScriptObject::defaultValue()
        {
                AvmCore *core = this->core();
                Toplevel* toplevel = this->toplevel();

                Atom atomv_out[1];

                // call this.valueOf()
                // NOTE use callers versioned public to get correct valueOf
                Multiname tempname(core->findPublicNamespace(), core->kvalueOf);
                atomv_out[0] = atom();
                Atom result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable);

                // if result is primitive, return it
                if (atomKind(result) != kObjectType)
                        return result;

                // otherwise call this.toString()
                tempname.setName(core->ktoString);
                atomv_out[0] = atom();
                result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable);

                // if result is primitive, return it
                if (atomKind(result) != kObjectType)
                        return result;

                // could not convert to primitive.
                toplevel->throwTypeError(kConvertToPrimitiveError, core->toErrorString(traits()));
                return undefinedAtom;
        }

        // Execute the ToString algorithm as described in ECMA-262 Section 9.8.
        // This is ToString(ToPrimitive(input argument, hint String))
        // ToPrimitive(input argument, hint String) calls [[DefaultValue]]
        // described in ECMA-262 8.6.2.6.  The [[DefaultValue]] algorithm
        // with hint String is inlined here.
        Atom ScriptObject::toString()
        {
                AvmCore *core = this->core();
                Toplevel* toplevel = this->toplevel();

                Atom atomv_out[1];

                // call this.toString()
                // NOTE use callers versioned public to get correct toString
                Multiname tempname(core->findPublicNamespace(), core->ktoString);
                atomv_out[0] = atom();
                Atom result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable);

                // if result is primitive, return its ToString
                if (atomKind(result) != kObjectType)
                        return core->string(result)->atom();

                // otherwise call this.valueOf()
                tempname.setName(core->kvalueOf);
                atomv_out[0] = atom();
                result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable);

                // if result is primitive, return it
                if (atomKind(result) != kObjectType)
                        return core->string(result)->atom();

                // could not convert to primitive.
                toplevel->throwTypeError(kConvertToPrimitiveError, core->toErrorString(traits()));
                return undefinedAtom;
        }

        // this = argv[0] (ignored)
        // arg1 = argv[1]
        // argN = argv[argc]
        Atom ScriptObject::call(int /*argc*/, Atom* /*argv*/)
        {
                // TypeError in ECMA to execute a non-function
                // NOTE use default public since name is not used
                Multiname name(core()->getAnyPublicNamespace(), core()->internConstantStringLatin1("value"));
                toplevel()->throwTypeError(kCallOfNonFunctionError, core()->toErrorString(&name));
                return undefinedAtom;
        }

        // this = argv[0] (ignored)
        // arg1 = argv[1]
        // argN = argv[argc]
        Atom ScriptObject::construct(int /*argc*/, Atom* /*argv*/)
        {
                // TypeError in ECMA to execute a non-function
                toplevel()->throwTypeError(kConstructOfNonFunctionError);
                return undefinedAtom;
        }

        Atom ScriptObject::applyTypeArgs(int /*argc*/, Atom* /*argv*/)
        {
                toplevel()->throwTypeError(kTypeAppOfNonParamType);
                return undefinedAtom;
        }
        
        Atom ScriptObject::getSlotAtom(uint32_t slot)
        {
                Traits* traits = this->traits();
                const TraitsBindingsp td = traits->getTraitsBindings();
                // repeated if-else is actually more performant than a switch statement in this case.
                // SST_atom is most common case, put it first
                void* p;
                const SlotStorageType sst = td->calcSlotAddrAndSST(slot, (void*)this, p);
                if (sst == SST_atom)
                {
                        return *((const Atom*)p);
                }
                else if (sst == SST_double)
                {
                        return traits->core->doubleToAtom(*((const double*)p));                         
                }
                else if (sst == SST_int32)
                {
                        return traits->core->intToAtom(*((const int32_t*)p));
                }
                else if (sst == SST_uint32)
                {
                        return traits->core->uintToAtom(*((const int32_t*)p));
                }
                else if (sst == SST_bool32)
                {
                        return (*((const int32_t*)p)<<3)|kBooleanType;
                }
                else if (sst == SST_string)
                {
                        return (*((const Stringp*)p))->atom(); // may be null|kStringType, that's ok
                }
                else if (sst == SST_namespace)
                {
                        return (*((const Namespacep*)p))->atom(); // may be null|kNamespaceType, no problemo
                }
                else // if (sst == SST_scriptobject)
                {
                        AvmAssert(sst == SST_scriptobject);
                        return (*((const ScriptObject**)p))->atom(); // may be null|kObjectType, copacetic
                }
        }

        ScriptObject* ScriptObject::getSlotObject(uint32_t slot)
        {
                Traits* traits = this->traits();
                const TraitsBindingsp td = traits->getTraitsBindings();
                void* p;
                const SlotStorageType sst = td->calcSlotAddrAndSST(slot, (void*)this, p);

                // based on profiling of Flex apps, it's *much* more common for the slot in this case
                // to have a type (vs "atom"), so check for that first...
                if (sst == SST_scriptobject)
                {
                        return *((ScriptObject**)p);
                }
                else if (sst == SST_atom)
                {
                        Atom const a = *((const Atom*)p);

                        // don't call AvmCore::isObject(); it checks for null, which we don't care about here
                        if (atomKind(a) == kObjectType)
                                return (ScriptObject*)atomPtr(a);
                        
                        // else fall thru and return null
                } 

                return NULL;
        }

        // note: coerceAndSetSlotAtom now includes a simplified and streamlined version
        // of Toplevel::coerce. If you modify that code, you might need to modify this code.
        void ScriptObject::coerceAndSetSlotAtom(uint32_t slot, Atom value)
        {
                Traits* traits = this->traits();
                const TraitsBindingsp td = traits->getTraitsBindings();
                void* p;
                const SlotStorageType sst = td->calcSlotAddrAndSST(slot, (void*)this, p);
                // repeated if-else is actually more performant than a switch statement in this case.
                // SST_atom is most common case, put it first
                if (sst == SST_atom)
                {
                        // no call to coerce() needed, since anything will fit here... with one exception:
                        // BUILTIN_object needs to convert undefined->null (though BUILTIN_any does not).
                        // it's cheaper to do that here than call out to coerce().
                        AvmAssert(td->getSlotTraits(slot) == NULL || td->getSlotTraits(slot)->builtinType == BUILTIN_object);
                        if (value == undefinedAtom && td->getSlotTraits(slot) != NULL)
                                value = nullObjectAtom;
                        WBATOM(traits->core->GetGC(), this, (Atom*)p, value);
                }
                else if (sst == SST_double)
                {
                        *((double*)p) = AvmCore::number(value);
                }
                else if (sst == SST_int32)
                {
                        *((int32_t*)p) = AvmCore::integer(value);
                }
                else if (sst == SST_uint32)
                {
                        *((uint32_t*)p) = AvmCore::toUInt32(value);
                }
                else if (sst == SST_bool32)
                {
                        *((int32_t*)p) = AvmCore::boolean(value);
                }
                else 
                {
                        // null/undefined -> NULL for all of these
                        if (AvmCore::isNullOrUndefined(value))
                        {
                                value = (Atom)0; // don't bother setting tag bits 
                        }
                        else if (sst == SST_string)
                        {
                                value = (Atom)traits->core->string(value); // don't bother setting tag bits 
                        }
                        else if (sst == SST_namespace)
                        {
                                // Namespace is final, so we don't have to do the hard work
                                if (atomKind(value) != kNamespaceType)
                                        goto failure;
                        }
                        else // if (sst == SST_scriptobject)
                        {
                                AvmAssert(sst == SST_scriptobject);
                                if (atomKind(value) != kObjectType || !AvmCore::atomToScriptObject(value)->traits()->subtypeof(td->getSlotTraits(slot)))
                                        goto failure;
                        }
                        WBRC(traits->core->GetGC(), this, p, atomPtr(value));
                }
                return;

        failure:
                toplevel()->throwTypeError(kCheckTypeFailedError, traits->core->atomToErrorString(value), traits->core->toErrorString(td->getSlotTraits(slot)));
                return;
        }

        Atom ScriptObject::nextName(int index)
        {
                AvmAssert(traits()->needsHashtable());
                AvmAssert(index > 0);

                InlineHashtable *ht = getTable();
                if (uint32_t(index)-1 >= ht->getCapacity()/2)
                        return nullStringAtom;
                const Atom* atoms = ht->getAtoms();
                Atom m = ht->removeDontEnumMask(atoms[(index-1)<<1]);
                if (AvmCore::isNullOrUndefined(m))
                        return nullStringAtom;
                return m;
        }

        Atom ScriptObject::nextValue(int index)
        {
                AvmAssert(traits()->needsHashtable());
                AvmAssert(index > 0);

                InlineHashtable *ht = getTable();
                if (uint32_t(index)-1 >= ht->getCapacity()/2)
                        return undefinedAtom;
                const Atom* atoms = ht->getAtoms();
                Atom m = ht->removeDontEnumMask(atoms[(index-1)<<1]);
                if (AvmCore::isNullOrUndefined(m))
                        return nullStringAtom;
                return atoms[((index-1)<<1)+1];
        }

        int ScriptObject::nextNameIndex(int index)
        {
                AvmAssert(index >= 0);

                if (!traits()->needsHashtable())
                        return 0;

                // todo clean this up.
                if (index != 0) {
                        index = index<<1;
                }
                // Advance to first non-empty slot.
                InlineHashtable* table = getTable();
                const Atom* atoms = table->getAtoms();
                int numAtoms = table->getCapacity();
                while (index < numAtoms) {
                        Atom m = atoms[index];
                        if (table->enumerable(m))
                                return (index>>1)+1;
                        index += 2;
                }
                return 0;
        }

        /**
         * the default implementation calls the delegate (should be the base class objects) 
         * so that derived classes delegate object allocation to the base class.  If you 
         * are overriding createInstance and your base class is Object, unconditionally allocate
         * the instance object using the given vtable and prototype.
         */
        ScriptObject* ScriptObject::createInstance(VTable* ivtable, ScriptObject* prototype)
        {
                if (ivtable->base)
                {
                        ScopeChain* scope = vtable->init->scope();
                        if (scope->getSize())
                        {
                                Atom baseAtom = scope->getScope(scope->getSize()-1);
                                if (!AvmCore::isObject(baseAtom))
                                        toplevel()->throwVerifyError(kCorruptABCError);

                                ScriptObject *base = AvmCore::atomToScriptObject(baseAtom);
                                // make sure scope object is base type's class object
                                AvmAssert(base->traits()->itraits == this->traits()->itraits->base);
                                return base->createInstance(ivtable, prototype);
                        }
                }

                return core()->newObject(ivtable, prototype);
        }

        
#ifdef DEBUGGER
        uint64 ScriptObject::size() const
        {
                uint64 size = traits()->getTotalSize();
                if(traits()->needsHashtable())
                {
                        size += getTable()->size();
                }
                size -= sizeof(AvmPlusScriptableObject);
                return size;
        }

        
#endif

        Stringp ScriptObject::implToString() const
        {
                AvmCore* core = this->core();
                Traits* t = this->traits();
                Stringp s = core->concatStrings(core->newConstantStringLatin1("[object "), t->name());
                return core->concatStrings(s, core->newConstantStringLatin1("]"));
        }
}

/* [<][>][^][v][top][bottom][index][help] */