root/core/VectorClass.cpp

/* [<][>][^][v][top][bottom][index][help] */
/* ***** 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"
#include "BuiltinNatives.h"

using namespace MMgc;

namespace avmplus
{
        bool VectorBaseObject::hasAtomProperty(Atom name) const
        {
                uint32 index;
                bool isNumber=false;
                if (getVectorIndex(name, index, isNumber))
                {
                        return index < m_length;
                }
                else
                {
                        if(isNumber)
                        {
                                return false;
                        }
                        return ScriptObject::hasAtomProperty(name);
                }
        }
        
        // helper method
        // sets index to the uint32 value of name, if it can be converted
        // isNumber is set to true if name was a number (whether it was a uint32 value or not)
        bool VectorBaseObject::getVectorIndex(Atom name, uint32& index, bool& isNumber) const
        {
                AvmCore* core = this->core();
                isNumber = false;
                if (AvmCore::getIndexFromAtom(name, &index))
                {
                        isNumber = true;
                        return true;
                }
                else
                {
                        if( AvmCore::isString(name) )
                        {
                                Stringp s = core->string(name);
                                const wchar c = s->charAt(0);
                                // Does it look like a number?
                                if( s->length() > 0 && c >= '0' && c <= '9' )
                                {
                                        double index_d = s->toNumber();
                                        if( !MathUtils::isNaN(index_d) )
                                        {
                                                isNumber = true;

                                                // name is a string that looks like a number
                                                int i = MathUtils::real2int(index_d);
                                                if ((double)i == index_d)
                                                {
                                                        // It's an indexed property name
                                                        index = i;
                                                        return true;
                                                }
                                                else
                                                {
                                                        return false;
                                                }
                                        }
                                }
                        }
                }
                return false;
        }

        void VectorBaseObject::setAtomProperty(Atom name, Atom value)
        {
                uint32 index;
                bool isNumber=false;
                if (getVectorIndex(name, index, isNumber))
                {
                        setUintProperty(index, value);
                }
                else
                {
                        // NOTE use default public for message gen
                        Multiname mn(core()->getAnyPublicNamespace(), core()->string(name));

                        // Vector is sorta sealed, can only write to "indexed" properties
                        toplevel()->throwReferenceError(kWriteSealedError, &mn, traits());
                }
        }
        
        Atom VectorBaseObject::getAtomProperty(Atom name) const
        {
                uint32 index;
                bool isNumber=false;
                AvmCore* core = this->core();
                if (getVectorIndex(name, index, isNumber))
                {
                        return getUintProperty(index);
                }
                else
                {
                        if(isNumber)
                        {
                                // Not a valid indexed name - has a decimal part
                                // NOTE use default public for message gen
                                Multiname mn(core->findPublicNamespace(), core->string(name));
                                toplevel()->throwReferenceError(kReadSealedError, &mn, traits());
                        }
                        // Check the prototype chain - that will throw if there is no match
                        return getAtomPropertyFromProtoChain(name, getDelegate(), traits());
                }
        }

        uint32 VectorBaseObject::get_length()
        {
                return m_length;
        }
        
        void VectorBaseObject::set_length(uint32 newLength)
        {
                if( m_fixed )
                        toplevel()->throwRangeError(kVectorFixedError); 
                if (newLength > m_capacity)
                {
                        grow(newLength, true);
                }
                m_length = newLength;
        }

        bool VectorBaseObject::get_fixed()
        {
                return m_fixed;
        }

        void VectorBaseObject::set_fixed(bool fixed)
        {
                m_fixed = fixed;
        }

        // Iterator support - for in, for each
        Atom VectorBaseObject::nextName(int index)
        {
                AvmAssert(index > 0);
                if (((uint32)index) <= m_length)
                {
                        AvmCore *core = this->core();
                        return core->intToAtom(index-1);
                }
                else
                {
                        return nullStringAtom;
                }
        }
        Atom VectorBaseObject::nextValue(int index)
        {
                AvmAssert(index > 0);
                if (((uint32)index) <= m_length)
                {
                        return getUintProperty(index-1);
                }
                else
                {
                        return undefinedAtom;
                }
        }
        int VectorBaseObject::nextNameIndex(int index)
        {
                if (((uint32)index) < m_length)
                {
                        return index + 1;
                }
                else
                {
                        return 0;
                }
        }

        Atom VectorBaseObject::map (ScriptObject *callback, Atom thisObject)
        {
                AvmCore* core = this->core();
                VectorBaseObject *r = newVector(m_length);

                if (!callback)
                        return r->atom();

                ScriptObject *d = this;
                uint32 len = m_length;

                for (uint32 i = 0; i < len; i++)
                {
                        // If thisObject is null, the call function will substitute the global object 
                        // args are modified in place by callee
                        Atom args[4] = {
                                thisObject,
                                d->getUintProperty(i), // element
                                core->uintToAtom(i), // index
                                this->atom()
                        };
                        Atom result = callback->call(3, args);
                        r->setUintProperty (i, result);
                }

                return r->atom();
        }

        Atom VectorBaseObject::filter(ScriptObject *callback, Atom thisObject)
        {
                AvmCore* core = this->core();
                VectorBaseObject *r = newVector();

                if (!callback)
                        return r->atom();

                ScriptObject *d = this;
                uint32 len = m_length;

                for (uint32 i = 0, k = 0; i < len; i++)
                {
                        // If thisObject is null, the call function will substitute the global object 
                        // args are modified in place by callee
                        Atom element = d->getUintProperty(i);
                        Atom args[4] = {
                                thisObject,
                                element,
                                core->uintToAtom(i), // index
                                this->atom()
                        };
                        Atom result = callback->call(3, args);
                        if (result == trueAtom)
                                r->setUintProperty(k++, element);
                }

                return r->atom();
        }

        uint32 VectorBaseObject::AS3_push(Atom *argv, int argc)
        {
                if( m_fixed )
                        toplevel()->throwRangeError(kVectorFixedError);
                grow(m_length + argc);
                for (int i=0; i < argc; i++) {
                        setUintProperty(m_length, argv[i]);
                }
                return m_length;
        }

        //
        // IntVectorClass
        //

        IntVectorClass::IntVectorClass(VTable *vtable)
                : ClassClosure(vtable)
    {
                toplevel()->intVectorClass = this;
        prototype = toplevel()->objectClass->construct();
        }

        ScriptObject* IntVectorClass::createInstance(VTable *ivtable,
                                                                                                 ScriptObject *prototype)
    {
        return new (core()->GetGC(), ivtable->getExtraSize()) IntVectorObject(ivtable, prototype);
    }

        Atom IntVectorClass::call(int argc, Atom* argv) 
        {
                if (argc != 1)
                {
                        toplevel()->throwArgumentError(kCoerceArgumentCountError, toplevel()->core()->toErrorString(argc));
                }
                if( AvmCore::istype(argv[1], ivtable()->traits ) )
                        return argv[1];

                IntVectorObject* v = (IntVectorObject*)createInstance(ivtable(), prototype);

                v->initWithObj(argv[1]);

                return v->atom();
        }

        IntVectorObject* IntVectorClass::newVector(uint32 length)
        {
                VTable* ivtable = this->ivtable();
                IntVectorObject *v = new (core()->GetGC(), ivtable->getExtraSize()) 
                        IntVectorObject(ivtable, prototype);
                v->set_length(length);
                return v;
        }


        VectorBaseObject* IntVectorObject::newVector(uint32 length)
        {
                return toplevel()->intVectorClass->newVector(length);
        }

        //
        // UIntVectorClass
        //

        UIntVectorClass::UIntVectorClass(VTable *vtable)
                : ClassClosure(vtable)
    {
                toplevel()->uintVectorClass = this;
        prototype = toplevel()->objectClass->construct();
        }

        ScriptObject* UIntVectorClass::createInstance(VTable *ivtable,
                                                                                                 ScriptObject *prototype)
    {
        return new (core()->GetGC(), ivtable->getExtraSize()) UIntVectorObject(ivtable, prototype);
    }

        Atom UIntVectorClass::call(int argc, Atom* argv) 
        {
                if (argc != 1)
                {
                        toplevel()->throwArgumentError(kCoerceArgumentCountError, toplevel()->core()->toErrorString(argc));
                }

                if( AvmCore::istype(argv[1], ivtable()->traits ) )
                        return argv[1];

                UIntVectorObject* v = (UIntVectorObject*)createInstance(ivtable(), prototype);

                v->initWithObj(argv[1]);

                return v->atom();
        }

        UIntVectorObject* UIntVectorClass::newVector(uint32 length)
        {
                VTable* ivtable = this->ivtable();
                UIntVectorObject *v = new (core()->GetGC(), ivtable->getExtraSize()) 
                        UIntVectorObject(ivtable, prototype);
                v->set_length(length);
                return v;
        }

        VectorBaseObject* UIntVectorObject::newVector(uint32 length)
        {
                return toplevel()->uintVectorClass->newVector(length);
        }

        //
        // DoubleVectorClass
        //

        DoubleVectorClass::DoubleVectorClass(VTable *vtable)
                : ClassClosure(vtable)
        {
                toplevel()->doubleVectorClass = this;
        prototype = toplevel()->objectClass->construct();
        }

        ScriptObject* DoubleVectorClass::createInstance(VTable *ivtable,
                                                                                                   ScriptObject *prototype)
    {
        return new (core()->GetGC(), ivtable->getExtraSize()) DoubleVectorObject(ivtable, prototype);
    }

        Atom DoubleVectorClass::call(int argc, Atom* argv) 
        {
                if (argc != 1)
                {
                        toplevel()->throwArgumentError(kCoerceArgumentCountError, toplevel()->core()->toErrorString(argc));
                }

                if( AvmCore::istype(argv[1], ivtable()->traits ) )
                        return argv[1];

                DoubleVectorObject* v = (DoubleVectorObject*)createInstance(ivtable(), prototype);

                v->initWithObj(argv[1]);

                return v->atom();
        }

        DoubleVectorObject* DoubleVectorClass::newVector(uint32 length)
        {
                VTable* ivtable = this->ivtable();
                DoubleVectorObject *v = new (core()->GetGC(), ivtable->getExtraSize()) 
                        DoubleVectorObject(ivtable, prototype);
                v->set_length(length);
                return v;
        }

        VectorBaseObject* DoubleVectorObject::newVector(uint32 length)
        {
                return toplevel()->doubleVectorClass->newVector(length);
        }

        //
        // VectorClass
        //

        VectorClass::VectorClass(VTable *vtable)
        : ClassClosure(vtable)
        {
                toplevel()->vectorClass = this;
                prototype = toplevel()->objectClass->construct();
                instantiated_types = new (core()->GetGC(), 0) HeapHashtable(core()->GetGC());
        }

        /*static*/ Stringp VectorClass::makeVectorClassName(AvmCore* core, Traits* t)
        {
                Stringp s = core->newConstantStringLatin1("Vector.<");
                s = s->append(t->formatClassName());
                s = s->append(core->newConstantStringLatin1(">"));
                // all callers want it interned, so let's do it here
                return core->internString(s);
        }

        Atom VectorClass::applyTypeArgs(int argc, Atom* argv)
        {
                //Vector only takes 1 type argument
                AvmAssert(argc==1);
                if (argc != 1)
                {
                        toplevel()->typeErrorClass()->throwError(kWrongTypeArgCountError, traits()->formatClassName(), core()->toErrorString(1), core()->toErrorString(argc));
                }
                Atom type = argv[0];
                AvmCore* core = this->core();

                if (ISNULL(type))
                        return toplevel()->objectVectorClass->atom();

                if (atomKind(type) != kObjectType)
                        toplevel()->throwVerifyError(kCorruptABCError);

                ScriptObject* so = AvmCore::atomToScriptObject(type);

                if (so == toplevel()->intClass)
                        return toplevel()->intVectorClass->atom();
                else if (so == toplevel()->numberClass)
                        return toplevel()->doubleVectorClass->atom();
                else if (so == toplevel()->uintClass)
                        return toplevel()->uintVectorClass->atom();

                Traits* param_traits = so->vtable->ivtable->traits;

                if (!instantiated_types->contains(type))
                {
                        Stringp fullname = VectorClass::makeVectorClassName(core, param_traits);

                        VTable* vtab = this->vtable->newParameterizedVTable(param_traits, fullname);

                        ObjectVectorClass* new_type = new (vtab->gc(), vtab->getExtraSize()) ObjectVectorClass(vtab);
                        new_type->index_type = (ClassClosure*)AvmCore::atomToScriptObject(type);
                        new_type->setDelegate(toplevel()->classClass->prototype);

                        // Is this right?  Should each instantiation get its own prototype?
                        new_type->prototype = toplevel()->objectVectorClass->prototype;
                        instantiated_types->add(type, new_type->atom());
                }
                return (Atom)instantiated_types->get(type);
        }

        Atom ObjectVectorClass::call(int argc, Atom* argv) 
        {
                if (argc != 1)
                {
                        toplevel()->throwArgumentError(kCoerceArgumentCountError, toplevel()->core()->toErrorString(argc));
                }

                if( AvmCore::istype(argv[1], ivtable()->traits ) )
                        return argv[1];

                ObjectVectorObject* v = (ObjectVectorObject*)createInstance(ivtable(), prototype);

                v->initWithObj(argv[1]);

                return v->atom();
        }
    
    ScriptObject* VectorClass::createInstance(VTable * /*ivtable*/, ScriptObject * /*prototype*/)
    {
        toplevel()->throwTypeError(kConstructOfNonFunctionError);
        return 0;
    }    

        ObjectVectorObject* VectorClass::newVector(ClassClosure* type, uint32 length)
        {
                Atom args[1] = {type->atom()};

                ObjectVectorClass* vecclass = (ObjectVectorClass*)AvmCore::atomToScriptObject(applyTypeArgs(1, args));
                return vecclass->newVector(length);
        }

        //
        // ObjectVectorClass
        //

        ObjectVectorClass::ObjectVectorClass(VTable *vtable)
                : ClassClosure(vtable)
    {
                if( !toplevel()->objectVectorClass )
                        toplevel()->objectVectorClass = this;
        prototype = toplevel()->objectClass->construct();
        }

        ScriptObject* ObjectVectorClass::createInstance(VTable *ivtable,
                                                                                                   ScriptObject *prototype)
    {
                ObjectVectorObject* v = new (core()->GetGC(), ivtable->getExtraSize()) ObjectVectorObject(ivtable, prototype);
                v->set_type(index_type->atom());
        return v;
    }

        ObjectVectorObject* ObjectVectorClass::newVector(uint32 length)
        {
                VTable* ivtable = this->ivtable();
                ObjectVectorObject *v = new (core()->GetGC(), ivtable->getExtraSize()) 
                        ObjectVectorObject(ivtable, prototype);
                v->set_type(this->index_type->atom());
                v->set_length(length);
                return v;
        }

        Atom ObjectVectorObject::getUintProperty(uint32 index) const
        {
                return _getUintProperty(index);
        }
        Atom ObjectVectorObject::_getUintProperty(uint32 index) const
        {
                if (m_length <= index)
                {
                        toplevel()->throwRangeError(kOutOfRangeError, core()->uintToString(index), core()->uintToString(m_length));
                }
                else
                {
                        return m_array[index];
                }
                return 0;
        }
        
        void ObjectVectorObject::setUintProperty(uint32 index, Atom value)
        {
                return _setUintProperty(index, value);
        }
        void ObjectVectorObject::_setUintProperty(uint32 index, Atom value)
        {
                if (m_length <= index)
                {
                        if( index > m_length || m_fixed )
                                toplevel()->throwRangeError(kOutOfRangeError, core()->uintToString(index), core()->uintToString(m_length));
                        grow(index+1);
                        m_length = index+1;
                }
                WBATOM( MMgc::GC::GetGC(m_array), m_array, m_array + index, (t ? toplevel()->coerce(value, t->traits()->itraits) : value));
        }                       

        Atom ObjectVectorObject::_getIntProperty(int index) const
        {
                if (index >= 0) 
                {
                        if (m_length <= (uint32)index)
                        {
                                toplevel()->throwRangeError(kOutOfRangeError, core()->uintToString(index), core()->uintToString(m_length));
                        }
                        else
                        {
                                return m_array[index];
                        }
                }
                else 
                        toplevel()->throwRangeError(kOutOfRangeError, core()->intToString(index), core()->uintToString(m_length));
                return 0;
        }

        void ObjectVectorObject::_setIntProperty(int index, Atom value)
        {
                if (index >= 0) 
                        _setUintProperty(index, value);
                else 
                        toplevel()->throwRangeError(kOutOfRangeError, core()->intToString(index), core()->uintToString(m_length));
        }

        // using VMPI_memset to clear an atom range to zero is not really right and will generate assertions in Box
        // code. Let's set it to what we really want, a nullObjectAtom.
        static void nullAtomRange(Atom* a, size_t count)
        {
                while (count--)
                        *a++ = nullObjectAtom;
        }

        void ObjectVectorObject::set_length(uint32 newLength)
        {
                if (newLength > m_length)
                {
                        if( m_fixed )
                                toplevel()->throwRangeError(kVectorFixedError);

                        grow(newLength, true);
                }
                else if( newLength < m_length)
                {
                        if( m_fixed )
                                toplevel()->throwRangeError(kVectorFixedError);

                        nullAtomRange(m_array+newLength, (m_length-newLength));
                        //_spliceHelper (newLength, 0, (m_length - newLength), 0, 0);
                }
                m_length = newLength;
        }

        void ObjectVectorObject::set_type(Atom a)
        {
                this->t = (ClassClosure*)AvmCore::atomToScriptObject(a);
        }

        Atom ObjectVectorObject::get_type()
        {
                return t->atom();
        }

        void ObjectVectorObject::grow(uint32 newCapacity, bool exact)
        {
                if (newCapacity > m_capacity)
                {
                        if(!exact)
                                newCapacity = newCapacity + (newCapacity >>2);
                        //newCapacity = ((newCapacity+kGrowthIncr)/kGrowthIncr)*kGrowthIncr;
                        GC* gc = GC::GetGC(this);
                        Atom* newArray = (Atom*) gc->Calloc(newCapacity, sizeof(Atom), GC::kContainsPointers);
                        nullAtomRange(newArray, newCapacity);
                        Atom* oldAtoms = m_array;
                        if (!newArray)
                        {
                                toplevel()->throwError(kOutOfMemoryError);
                        }
                        if (m_array)
                        {
                                VMPI_memcpy(newArray, m_array, m_length * sizeof(Atom));
                                nullAtomRange(oldAtoms, m_length);
                                gc->Free(oldAtoms);
                        }
                        m_array = newArray;
                        m_capacity = newCapacity;
                }
        }

        VectorBaseObject* ObjectVectorObject::newVector(uint32 length)
        {
        Atom args[1] = {t->atom()};
        
                ObjectVectorClass* vecclass = (ObjectVectorClass*)AvmCore::atomToScriptObject(toplevel()->vectorClass->applyTypeArgs(1, args));
                return vecclass->newVector(length);
        }

        void ObjectVectorObject::_spliceHelper(uint32 insertPoint, uint32 insertCount, uint32 deleteCount, Atom args, int offset)
        {
                long l_shiftAmount = (long)insertCount - (long) deleteCount; // long because result could be negative

                grow(m_length + l_shiftAmount);

                Atom *arr = m_array;

                ScriptObject* so_args = atomKind(args)==kObjectType ?  AvmCore::atomToScriptObject(args) : 0;
                ObjectVectorObject* vec_args = isVector(args);

                if (l_shiftAmount < 0) 
                {
                        int numberBeingDeleted = -l_shiftAmount;

                        // shift elements down
                        int toMove = m_length - insertPoint - deleteCount;
                        VMPI_memmove(arr + insertPoint + insertCount, arr + insertPoint + deleteCount, toMove * sizeof(Atom));

                        nullAtomRange(arr + m_length - numberBeingDeleted, numberBeingDeleted);
                }
                else if (l_shiftAmount > 0)
                {
                        VMPI_memmove(arr + insertPoint + l_shiftAmount, arr + insertPoint, (m_length - insertPoint) * sizeof(Atom));
                        //clear for gc purposes
                        nullAtomRange(arr + insertPoint, l_shiftAmount);
                }

                set_length(m_length + l_shiftAmount);

                // Add the items to insert
                if (insertCount)
                {
                        if( vec_args && (offset+insertCount <= vec_args->m_length) )
                        {
                                Atom* a = vec_args->m_array;
                                for (uint32 i=0; i<insertCount; i++)
                                {
                                        _setUintProperty(insertPoint+i, a[i+offset]);
                                }
                        }
                        else if( so_args )
                        {
                                for (uint32 i=0; i<insertCount; i++)
                                {
                                        //setUintProperty(insertPoint+i, so_args->getUintProperty(i+offset));
                                        _setUintProperty(insertPoint+i, so_args->getUintProperty(i+offset));
                                }
                        }
                }

                return;
        }

        Atom ObjectVectorObject::AS3_pop()
        {
                if(m_fixed)
                        toplevel()->throwRangeError(kVectorFixedError);
                if(m_length)
                {
                        uint32 l = m_length-1;
                        Atom r = m_array[l];
                        set_length(m_length-1);
                        return r;
                }
                return undefinedAtom;
        }

        uint32 ObjectVectorObject::AS3_unshift(Atom* argv, int argc)
        {
                // shift elements up by argc
                // inserts args into initial spots

                if( argc > 0 )
                {
                        if( m_fixed )
                                toplevel()->throwRangeError(kVectorFixedError);
                        grow (m_length + argc);
                        Atom *arr = m_array;
                        VMPI_memmove(arr + argc, arr, m_length * sizeof(Atom));
                        // clear moved element for RC purposes
                        nullAtomRange(arr, argc);
                        m_length += argc;
                        for(int i=0; i<argc; i++) {
                                _setUintProperty(i, argv[i]);
                        }
                }
                return m_length;
        }

}

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