зеркало из https://github.com/mozilla/gecko-dev.git
516 строки
12 KiB
C++
516 строки
12 KiB
C++
/* ***** 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 ] Adobe Systems Incorporated. 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 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)
|
|
{
|
|
AvmAssert(traits()->sizeofInstance >= 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;
|
|
|
|
AvmCore *core = this->core();
|
|
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 (core->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 (core->getIndexFromAtom (name, &index))
|
|
{
|
|
return _setUintProperty (index, value);
|
|
}
|
|
|
|
if (name == core->klength->atom())
|
|
return setLength(core->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 (core->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 (core()->getIndexFromAtom (name, &index))
|
|
{
|
|
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 (core()->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 (core()->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->newString("[]@");
|
|
return core->concatStrings(prefix, core->formatAtomPtr(atom()));
|
|
}
|
|
#endif
|
|
|
|
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::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::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::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
|
|
uint32 ArrayObject::size() const
|
|
{
|
|
if(isSimpleDense())
|
|
{
|
|
return ScriptObject::size() + getLength()*sizeof(Atom);
|
|
}
|
|
return ScriptObject::size();
|
|
}
|
|
#endif
|
|
}
|