root/core/ArrayObject.cpp

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

DEFINITIONS

This source file includes following definitions.
  1. m_denseArr
  2. getDense
  3. checkForSparseToDenseConversion
  4. setAtomProperty
  5. _setUintProperty
  6. getAtomProperty
  7. _getUintProperty
  8. _getIntProperty
  9. _setIntProperty
  10. deleteAtomProperty
  11. delUintProperty
  12. getAtomPropertyIsEnumerable
  13. hasAtomProperty
  14. hasUintProperty
  15. nextName
  16. nextValue
  17. nextNameIndex
  18. format
  19. get_length
  20. set_length
  21. getLength
  22. setLength
  23. AS3_pop
  24. AS3_push
  25. AS3_unshift
  26. size

/* ***** 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"

namespace avmplus
{
        ArrayObject::ArrayObject(VTable *vtable, ScriptObject* proto, uint32 capacity)
                : ScriptObject(vtable, proto, 0),
                m_denseArr(capacity)
        {
                SAMPLE_FRAME("Array", core());
                AvmAssert(traits()->getSizeOfInstance() >= sizeof(ArrayObject));
                m_length = 0;
                m_lowHTentry = NO_LOW_HTENTRY;
        }

#if 0 // Test code to determine if our array is a pure dense array
        bool ArrayObject::getDense()
        {
                // If are dense part equals are length and we have no 
                // atoms in our HT, we are a pure dense array.  We can't
                // call getTable()->GetSize() since we might have deleted
                // atoms in our HT and size would be non-zero.
                return (isSimpleDense() && !ScriptObject::nextNameIndex(0));
        }
#endif

        // This routine checks to see if our dense portion is directly next
        // to any entries in our HT.  If so, the HT entries are deleted and added
        // to the dense portion.  If the HT is completely emptied, it is cleared.
        void ArrayObject::checkForSparseToDenseConversion()
        {
                // check for lowHTentry being consumed
                if (m_lowHTentry == NO_LOW_HTENTRY)
                        return;

                if (getDenseLength() != m_lowHTentry)
                        return;

                while (getDenseLength() == m_lowHTentry)
                {
                        AvmAssert (ScriptObject::hasUintProperty (m_lowHTentry));

                        // Move prop from HT to dense Array. No need to update m_length
                        Atom lowHT = ScriptObject::getUintProperty (m_lowHTentry);
                        this->m_denseArr.push (lowHT);

                        // Delete prop from HT
                        ScriptObject::delUintProperty (m_lowHTentry);

                        // If our low entry happened to match our length, we're out of HT entries
                        // and we can just quit.
                        if ((m_lowHTentry + 1) == m_length)
                        {
                                m_lowHTentry = NO_LOW_HTENTRY;                          
                        }
                        else
                        {
                                // Find the next integer HT prop and update m_lowHTentry
                                // This is tricky.  Our HT section could be huge but very sparse
                                // Do we want to linearly walk from index+1 to m_length or do
                                // we want to walk the entire HT looking for a low integer value?

                                if (ScriptObject::hasUintProperty (m_lowHTentry + 1))
                                {
                                        m_lowHTentry++;
                                }
                                else
                                {
                                        // assume we don't find an entry
                                        m_lowHTentry = NO_LOW_HTENTRY;
                                        int index = ScriptObject::nextNameIndex(0);
                                        while (index)
                                        {
                                                Atom name = ScriptObject::nextName (index);
                                                uint32 nameIndex; 
                                                if (AvmCore::getIndexFromAtom(name, &nameIndex))
                                                {
                                                        if ((m_lowHTentry == NO_LOW_HTENTRY) || (nameIndex < m_lowHTentry))
                                                        {
                                                                m_lowHTentry = nameIndex;
                                                        }
                                                }

                                                index = ScriptObject::nextNameIndex(index);
                                        }
                                }
                        }
                }

                // We're done moving our sparse entries over to our dense part of our array.
                // This may have left a large HT that is now completely empty.  If ScriptObject::nextNameIndex(0)
                // returns 0, we know we have no atoms in our HT and we can clear it.
                if (ScriptObject::nextNameIndex (0) == 0)
                        getTable()->reset();
        }

        void ArrayObject::setAtomProperty(Atom name, Atom value)
        {
                if (traits()->needsHashtable())
                {
                        AvmCore *core = this->core();
                        // Update the array length.
                        uint32 index;
                        if (AvmCore::getIndexFromAtom(name, &index))
                        {
                                return _setUintProperty (index, value);
                        }
                        
                        if (name == core->klength->atom())
                                return setLength(AvmCore::toUInt32(value));
                }

                ScriptObject::setAtomProperty(name, value);
        }
        
        void ArrayObject::_setUintProperty(uint32 index, Atom value)
        {
                if (traits()->needsHashtable())
                {
                        if (hasDense())
                        {
                                if (index == getDenseLength())
                                {
                                        this->m_denseArr.push (value);
                                        if (m_length < getDenseLength())
                                                m_length = getDenseLength();

                                        checkForSparseToDenseConversion ();
                                        return;
                                }
                                else if (index < getDenseLength())
                                {
                                        this->m_denseArr.setAt (index, value);
                                        return;
                                }
                                else 
                                {
                                        // fall through and put the new property into our HT
                                }
                        }
                        // If we're NOT dense yet and setting first element, we can create a dense array
                        else if (index == 0)
                        {                       
                                m_denseArr.push (value);
                                if (!m_length)
                                        m_length = 1;
                                else
                                        checkForSparseToDenseConversion ();
                                return;
                        }

                        if (m_length <= index) {
                                m_length = index+1;
                        }

                        if ((m_lowHTentry == NO_LOW_HTENTRY) || (index < m_lowHTentry))
                                m_lowHTentry = index;
                }
                // end if (dynamic)

                // If our index value is going to overflow our int atom storage and be
                // converted to a string, do that here instead of calling the
                // SciptObject::setUintProperty which will call ArrayObject::setAtomProperty
                // which will call back into this routine in an infinite loop.
                if (index & ScriptObject::MAX_INTEGER_MASK)
                        ScriptObject::setAtomProperty(core()->internUint32(index)->atom(), value);
                else
                        ScriptObject::setUintProperty(index, value);
        }

        Atom ArrayObject::getAtomProperty(Atom name) const
        {
                if (traits()->needsHashtable())
                {
                        AvmCore *core = this->core();
                        if (hasDense())
                        {
                                uint32 index;
                                if (AvmCore::getIndexFromAtom(name, &index))
                                {
                                        // if we get here, we have a valid integer index.
                                        if ((index < getDenseLength()))
                                                return m_denseArr.getAtFast(index);
                                }
                        }

                        if (name == core->klength->atom())
                                return core->intToAtom (getLength());
                }

                return ScriptObject::getAtomProperty(name);
        }

        Atom ArrayObject::_getUintProperty(uint32 index) const
        {
                if (traits()->needsHashtable())
                {
                        if (hasDense())
                        {
                                if ((index < getDenseLength()))
                                        return m_denseArr.getAtFast(index);
                        }
                }

                return ScriptObject::getUintProperty (index);
        }

        Atom ArrayObject::_getIntProperty(int index) const
        {
                if (index >= 0) 
                        return _getUintProperty(index);
                else // integer is negative - we must intern it
                        return getStringProperty(core()->internInt(index));
        }

        void ArrayObject::_setIntProperty(int index, Atom value)
        {
                if (index >= 0) 
                        _setUintProperty(index, value);
                else // integer is negative - we must intern it
                        setStringProperty(core()->internInt(index), value);
        }

        // This does NOT affect the length of the array
        bool ArrayObject::deleteAtomProperty(Atom name)
        {
                if (traits()->needsHashtable())
                {
                        if (hasDense())
                        {
                                uint32 index;
                                if (AvmCore::getIndexFromAtom(name, &index) && index < getDenseLength())
                                {
                                        return delUintProperty(index);
                                }
                        }
                }

                return ScriptObject::deleteAtomProperty(name);
        }

        bool ArrayObject::delUintProperty(uint32 index)
        {
                // if we get here, we have a valid integer index.
                if (traits()->needsHashtable())
                {
                        if ((index < getDenseLength()))
                        {
                                if (index == (getDenseLength() - 1))
                                {
                                        m_denseArr.pop();
                                }
                                // We're deleting an element in the middle of our array.  The lower
                                // part can be left in the dense array but the upper part needs to
                                // get moved to the HT.
                                else
                                {
                                        for (uint32 i = index + 1; i < getDenseLength(); i++)
                                        {
                                                ScriptObject::setUintProperty (i, m_denseArr.getAtFast(i));
                                        }
                                        m_denseArr.splice (index, 0, (getDenseLength() - index), 0);
                                }

                                return true;
                        }
                }
                return ScriptObject::delUintProperty(index);
        }

        bool ArrayObject::getAtomPropertyIsEnumerable(Atom name) const
        {
                if (traits()->needsHashtable())
                {
                        if (hasDense())
                        {
                                uint32 index;
                                if (AvmCore::getIndexFromAtom(name, &index))
                                {
                                        // {DontEnum} is not supported on the dense portion
                                        // of an array.  Those properties are always enumerable.
                                        if (index < getDenseLength())
                                                return true;
                                }
                        }
                }

                return ScriptObject::getAtomPropertyIsEnumerable(name);
        }
        
        bool ArrayObject::hasAtomProperty(Atom name) const
        {
                if (traits()->needsHashtable())
                {
                        if (hasDense())
                        {
                                uint32 index;
                                if (AvmCore::getIndexFromAtom(name, &index))
                                {
                                        if (index < getDenseLength())
                                                return true;
                                }
                        }
                }

                return ScriptObject::hasAtomProperty(name);
        }

        bool ArrayObject::hasUintProperty(uint32 index) const
        {
                if (traits()->needsHashtable())
                {
                        if (hasDense())
                        {
                                if (index < getDenseLength())
                                        return true;
                        }
                }

                return ScriptObject::hasUintProperty (index);
        }

        // Iterator support - for in, for each
        Atom ArrayObject::nextName(int index)
        {
                AvmAssert(index > 0);

                int denseLength = (int)getDenseLength();
                if (index <= denseLength)
                {
                        AvmCore *core = this->core();
                        return core->intToAtom(index-1);
                }
                else
                {
                        return ScriptObject::nextName (index - denseLength);
                }
        }

        Atom ArrayObject::nextValue(int index)
        {
                AvmAssert(index > 0);

                int denseLength = (int) getDenseLength();
                if (index <= denseLength)
                {
                        return m_denseArr.getAtFast (index-1);
                }
                else
                {
                        return ScriptObject::nextValue (index - denseLength);
                }
        }

        int ArrayObject::nextNameIndex(int index)
        {
                int denseLength = (int) getDenseLength();
                if (index < denseLength)
                {
                        return index + 1;
                }
                else
                {
                        index = ScriptObject::nextNameIndex (index - denseLength);
                        if (!index)
                                return index;
                        return denseLength + index;
                }
        }

#ifdef AVMPLUS_VERBOSE
        Stringp ArrayObject::format(AvmCore* core) const
        {
                Stringp prefix = core->newConstantStringLatin1("[]@");
                return core->concatStrings(prefix, core->formatAtomPtr(atom()));
        }
#endif

        // Non-virtual members for ActionScript method implementation.
        // Always calls thru to the virtual method to allow subclasses to override in C++.
        uint32 ArrayObject::get_length() const 
        { 
                return getLength(); 
        }
        
        void ArrayObject::set_length(uint32 newLength) 
        {
                setLength(newLength); 
        }

        /*virtual*/ uint32 ArrayObject::getLength() const 
        {
                return m_length;
        }
        
        /*virtual*/ void ArrayObject::setLength(uint32 newLength)
        {
                if (traits()->needsHashtable())
                {
                        // Delete all items between size and newLength
                        uint32 oldLength = getLength();
                        if (newLength < oldLength) 
                        {
                                uint32 deleteStart = newLength;
                                uint32 denseLength = getDenseLength();
                                if (newLength < denseLength)
                                {
                                        this->m_denseArr.splice (deleteStart, 0, (denseLength - deleteStart), 0);
                                        deleteStart = denseLength;
                                }

                                for (uint32 i = deleteStart; i < oldLength; i++) {
                                        delUintProperty(i);
                                }
                        }
                        m_length = newLength;
                }
                // else, if !dynamic ignore set.
        }

        // public native function pop(...rest):Object
        Atom ArrayObject::AS3_pop()
        {
                if (isSimpleDense())
                {
                        if (!m_length)
                                return undefinedAtom;

                        m_length--;
                        return m_denseArr.pop ();
                }

                if (getLength() != 0)
                {
                        Atom outAtom = _getUintProperty(getLength()-1);
                        setLength(getLength()-1);
                        return outAtom;
                } 
                else
                {
                        return undefinedAtom;
                }
        }

        uint32 ArrayObject::AS3_push(Atom* argv, int argc)
        {
                if (isSimpleDense())
                {
                        m_denseArr.push (argv, argc);
                        m_length += argc;
                }
                else
                {
                        for (int i=0; i < argc; i++) {
                                _setUintProperty(getLength(), argv[i]);
                        }
                }
                return m_length;
        }

        uint32 ArrayObject::AS3_unshift(Atom* argv, int argc)
        {
                if (argc != 0) 
                {
                        if (isSimpleDense())
                        {
                                m_denseArr.unshift (argv, argc);
                                m_length += argc;
                        }
                        else
                        {
                                uint32 i;
                                // First, move all the elements up
                                uint32 len = getLength();
                                for (i=len; i > 0; ) {  // note: i is unsigned, can't check if --i >=0.
                                        i--;
                                        _setUintProperty(i+argc, _getUintProperty(i));
                                }
                                
                                for (i=0; i < ((uint32)argc); i++) {
                                        _setUintProperty(i, argv[i]);
                                }

                                setLength(len+argc);
                        }
                }

                return getLength();
        }

#ifdef DEBUGGER
        uint64 ArrayObject::size() const
        {
                uint64 s = ScriptObject::size();
                if (isSimpleDense())
                {
                        s += getLength()*sizeof(Atom);
                }
                return s;
        }
#endif
}

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