/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#ifndef __avmplus_VectorClass__
#define __avmplus_VectorClass__

/* Documentation on the structure of the Vector code in Tamarin (2011-10-20).
 *
 *
 * Five components: AS3, ABC, ASC, verifier, runtime.  These interact in various ways and a
 * linear narrative is not possible.
 *
 *
 * On the AS3 side we have this (core/Vector.as, core/VectorImpl.as):
 *
 *   package __AS3__.vec 
 *   {
 *     class Vector ...
 *     class Vector$int ...
 *     class Vector$uint ...
 *     class Vector$double ...
 *     class Vector$object ...
 *   }
 *
 * The namespace "__AS3__.vec" is an artifact of a time when we did not have good API
 * versioning and could not introduce new top-level names without the risk of breaking
 * existing code.  Today we would probably have made "Vector" public & versioned.
 *
 * "Vector" is public in its package but cannot be instantiated (a run-time error is thrown)
 * and has no members.  It is possible to capture its value and use it as part of a type 
 * application, eg, "var v=Vector; new v.<int>" will create a Vector.<int>.  It's also 
 * referenced in code using the APPLYTYPE instruction and CONSTANT_TypeName multiname kind (below).
 *
 * Vector$int, Vector$uint, Vector$double, and Vector$object are not public in their package.
 * They represent Vector.<int>, Vector.<uint>, Vector.<Number>, and Vector.<*> respectively;
 * the first three special cases are provided in order to specialize the representation
 * and the last is a catch-all.
 *
 * The Vector$int etc bindings are artifacts of an older design and are kept around for the
 * sake of backward compatibility with existing bytecode, which referenced those names
 * directly (see ASC notes below).
 *
 * The object resulting from the expression "Vector.<int>" is the same object as the Vector$int
 * object in the __AS3__.vec package, ditto for the other three base types.  Note carefully
 * that Vector$object maps to Vector.<*> and that Vector.<Object> is different.  (Vector$object
 * should really have been called Vector$any.)
 *
 * For other base types than int, uint, Number, and * the VM synthesizes new Vector classes at
 * run-time.  The new classes are effectively clones of Vector$object, but their class objects
 * are distinct from the object held in Vector$object.
 *
 *
 * ASC:
 *
 * If AS3 code references the name "Vector" and Vector is not bound lexically in the compilation
 * unit then ASC will generate code that opens the namespace "__AS3__.vec" so that the "Vector"
 * binding becomes available.
 *
 * The current ASC - unlike older ASC - does not reference the Vector$int etc names directly,
 * instead it emits the APPLYTYPE instruction and the CONSTANT_TypeName multiname kind to
 * reference parameterized types in expression and type contexts, respectively.
 *
 * 
 * ABC and verification:
 *
 * The ABC instruction APPLYTYPE creates a class object for a type application T0.<T1,...>.
 * It's a general instruction.  APPLYTYPE is effectively implemented as an a call to the 
 * applyTypeArgs() virtual on avmplus::ScriptObject.
 *
 * APPLYTYPE does not have any interesting verification constraints, all the checking is
 * done at runtime (see below); basically the factory must be Vector and there must be one
 * type argument.
 *
 * The ABC multiname kind CONSTANT_TypeName references an instantiated type and comprises a
 * base type (a factory), an argument count, and a number of arguments, all of which must be
 * multinames.
 *
 * The AbcParser checks that there is exactly one type parameter for CONSTANT_TypeName, this
 * simplifies the representation of Multiname as it only has to support the special case.
 * This constraint can be lifted later without invalidating existing code or needing to introduce
 * a new instruction for the general case.
 *
 * CONSTANT_TypeName names will eventually end up being passed through PoolObject::resolveParameterizedType,
 * which will fail (return NULL) if the base type is not Vector.  That will in turn lead to a
 * verification error, at least in type contexts (called from resolveTypeName).
 *
 * In sum, only Vector.<T> for exactly one T is supported at this time through a combination
 * of checks.
 *
 * 
 * Runtime.
 * 
 * APPLYTYPE ends up in VectorClass::applyTypeArgs(), which checks that there's one parameter 
 * and that it is NULL (representing *) or a normal class object and fails otherwise with
 * a VerifyError (that is a weird error but it's a fact of life at this point).  APPLYTYPE
 * then delegates to VectorClass::getTypedVectorClass().
 *
 * When Vector is instantiated in VectorClass::getTypedVectorClass() and a new Vector type must
 * be created then the new class is stored in a table in the Domain of the factory, the key
 * to the table is the string "Vector.<T>" where "T" is the full formatted name of the
 * parameter class.  The table is used by getTypedVectorClass() to ensure that only one type is
 * instantiated per class parameter.  getTypedVectorClass() has special cases for base types
 * int, uint, Number, and *: in those cases the built-in classes Vector$int, etc are returned.
 *
 * Issues with getTypedVectorClass: The string-based mechanism feels a little clunky, and it's not
 * obvious that a table in Domain is correct, it should probably be in DomainEnv.
 *
 * CONSTANT_TypeName triggers invocations of PoolObject::resolveParameterizedType(), which must
 * create Traits objects corresponding to the parameterized types.  These are stored with the
 * "loaded" traits in the Domain, again the key is the string "Vector.<T>" where "T" is the full
 * formatted name of the parameter class, and again there are special cases for instantiations
 * corresponding to the built-in Vector instantiations Vector$int etc.
 *
 *
 * On the C++ side we have these two hierarchies:
 *
 *   ClassClosure
 *     VectorClass
 *       TypedVectorClassBase
 *         TypedVectorClass<T>
 *           IntVectorClass = TypedVectorClass<IntVectorObject>
 *           UIntVectorClass = TypedVectorClass<UintVectorObject>
 *           DoubleVectorClass = TypedVectorClass<DoubleVectorObject>
 *           ObjectVectorClass = TypedVectorClass<ObjectVectorObject>
 *
 *    ScriptObject
 *      VectorBaseObject
 *        TypedVectorObject<T>
 *          IntVectorObject = TypedVectorObject< DataList<int32_t> >
 *          UIntVectorObject = TypedVectorObject< DataList<uint32_t> >
 *          DoubleVectorObject = TypedVectorObject< DataList<double> >
 *          ObjectVectorObject = TypedVectorObject< AtomList >
 *
 * IntVectorClass corresponds to Vector$int, etc, and IntVectorObject corresponds to instances of
 * Vector$int, ect, with the wrinkle that ObjectVectorClass and ObjectVectorObject are also
 * used internally to represent classes and instances for dynamically instantiated classes,
 * ie Vector.<T> for T not int, uint, Number, or *.
 *
 * The Toplevel's builtin class manifest has getters, get_VectorClass(), get_Vector_intClass(), etc,
 * to return the unique instances representing Vector, Vector$int, etc.  The traits for those
 * classes are stored on the AvmCore: traits.vectorint_itraits, etc.
 *
 *
 * There are several workhorse methods:
 *
 *  VectorClass::getTypedVectorClass      ->  ClassClosure*
 *    Resolves to the vector Class object given the Class object for the parameter type.
 *    Calls newParameterizedVTable.
 *
 *  VTable::newParameterizedVTable        ->  VTable*
 *    Orchestrates building new VTable, ITraits, and CTraits for a new Vector type, reusing
 *    any existing ITraits and CTraits.  Calls newParameterizedCTraits and resolveParameterizedType.
 *
 *  PoolObject::resolveParameterizedType  ->  Traits*
 *    Resolves to the instance traits, ie, "Vector.<T>", used to handle CONSTANT_TypeName.
 *    (Can return NULL if the factory is not Vector.)  Calls newParameterizedITraits.
 *
 *  Traits::newParameterizedITraits       ->  Traits*
 *    Creates a Traits representing a Vector.<T> instance
 *
 *  Traits::newParameterizedCTraits       ->  Traits*
 *    Creates a Traits representing the Vector.<T> class
 *
 * When the traits for a new Vector type has been created it is added to the set of loaded traits
 * in the domain under the name "Vector.<T>$".
 *
 *
 * Consider APPLYTYPE VectorClass, T:
 *
 *   - VectorClass::applyTypeArgs does some error checking and tail-calls VectorClass::getTypedVectorClass
 *   - VectorClass::getTypedVectorClass looks for a cached class object in the Domain
 *   - If found return it
 *   - If not found:
 *     - newParameterizedVTable is called with fullname="Vector.<T>"
 *       - Call DomainMgr::findTraitsInPoolByNameAndNS to look for a loaded trait for "Vector.<T>$"
 *         - If not found then create ctraits and itraits
 *           - That will register the itraits (in resolveParameterizedType) but not the ctraits
 *              [[[ The CTraits are never registered.  The lookup is redundant, I think,
 *                  for that reason.  That's OK, because the call to newParameterizedVTable
 *                  is guarded by the lookup in getTypedVectorClass, so we make one lookup
 *                  that fails but that's all.  I've left a comment in the code.  ]]]
 *       - Create and initialize a new vtable based on the ctraits/itraits
 *       - return the vtable
 *     - a new instance of ObjectVectorClass is created and initialized
 *     - the new instance is registered with the Domain
 *     - return the new instance
 *
 *
 * Consider CONSTANT_TypeName VectorClass, T:
 *
 *   - PoolObject::resolveParameterizedType is called to resolve the type and return an itraits
 *     - Call DomainMgr::findTraitsInPoolByNameAndNS to look for a loaded itrait for "Vector.<T>"
 *       - If not found then create it and register it
 *       - return it
 *
 * From all of that it follows that CONSTANT_TypeName and APPLYTYPE communicate about itraits via the
 * domain manager, and itraits are stored as loaded traits in the domain.  
 *
 *
 * Questions to resolve in the future (not all of these are hard, necessarily):
 *
 * Q: When does a traits represent a parameterized class, or an instance of a parameterized class?
 *    Ie, how do we recognize it in C++ code?
 *
 * Q: Given some traits representing a parameterized class, how can we find its class object?
 *    (Right now getTypedVectorClass keys on the class object of the parameter class.)
 *
 * Q: Given an instantiation of Vector with T (a class object), how do we find T (class object)?
 */

namespace avmplus
{
    // ----------------------------

    class GC_AS3_EXACT(VectorClass, ClassClosure)
    {
    protected:
        VectorClass(VTable* vtable);
    public:
        REALLY_INLINE static VectorClass* create(MMgc::GC* gc, VTable* cvtable)
        {
            return new (gc, MMgc::kExact, cvtable->getExtraSize()) VectorClass(cvtable);
        }

        virtual Atom applyTypeArgs(int argc, Atom* argv);

        ClassClosure* getTypedVectorClass(ClassClosure* typeClass);

        static Stringp makeVectorClassName(AvmCore* core, Traits* t);

        // make a Vector of subtype Vector<typeClass>,
        // eg if typeClass == StringClass then return a new Vector<String>
        ObjectVectorObject* newVector(ClassClosure* typeClass, uint32_t length = 0);

    private:
        GC_NO_DATA(VectorClass)

        DECLARE_SLOTS_VectorClass;
    };

    // ----------------------------

    class GC_CPP_EXACT(TypedVectorClassBase, ClassClosure)
    {
    protected:
        explicit TypedVectorClassBase(VTable* vtable);

    public:
        Traits* getTypeTraits() const;

        // ClassClosure overrides
        virtual Atom call(int argc, Atom* argv);

        // AS3 native function implementations
        void _forEach(Atom thisAtom, ScriptObject* callback, Atom thisObject);
        bool _every(Atom thisAtom, ScriptObject* callback, Atom thisObject);
        bool _some(Atom thisAtom, ScriptObject* callback, Atom thisObject);
        Atom _sort(Atom thisAtom, ArrayObject* args);

    protected:
        virtual Atom createAndInitVectorFromObject(ScriptObject* so, uint32_t len) = 0;

    protected:
        GC_DATA_BEGIN(TypedVectorClassBase)

        GCMember<Traits> GC_POINTER(m_typeTraits);

        GC_DATA_END(TypedVectorClassBase)
    };

    // ----------------------------

    template<class OBJ>
    class TypedVectorClass : public TypedVectorClassBase
    {
        friend class VectorClass;

    protected:
        explicit TypedVectorClass(VTable* vtable);

    public:
        OBJ* newVector(uint32_t length = 0, bool fixed = false);

        virtual bool gcTrace(MMgc::GC* gc, size_t cursor)
        {
            return TypedVectorClassBase::gcTrace(gc, cursor);
        }

    protected:
        virtual Atom createAndInitVectorFromObject(ScriptObject* so, uint32_t len);
        Atom constructImpl(int argc, Atom* argv);
    };


    // ----------------------------

    class GC_AS3_EXACT(IntVectorClass, TypedVectorClass<IntVectorObject>)
    {
    private:
        explicit IntVectorClass(VTable* vtable);
    public:
        REALLY_INLINE static IntVectorClass* create(MMgc::GC* gc, VTable* cvtable)
        {
            return new (gc, MMgc::kExact, cvtable->getExtraSize()) IntVectorClass(cvtable);
        }

        GC_NO_DATA(IntVectorClass)

        DECLARE_SLOTS_IntVectorClass;
    };

    // ----------------------------

    class GC_AS3_EXACT(UIntVectorClass, TypedVectorClass<UIntVectorObject>)
    {
    private:
        explicit UIntVectorClass(VTable* vtable);
    public:
        REALLY_INLINE static UIntVectorClass* create(MMgc::GC* gc, VTable* cvtable)
        {
            return new (gc, MMgc::kExact, cvtable->getExtraSize()) UIntVectorClass(cvtable);
        }

        GC_NO_DATA(UIntVectorClass)

        DECLARE_SLOTS_UIntVectorClass;
    };

    // ----------------------------

    class GC_AS3_EXACT(DoubleVectorClass, TypedVectorClass<DoubleVectorObject>)
    {
    private:
        explicit DoubleVectorClass(VTable* vtable);
    public:
        REALLY_INLINE static DoubleVectorClass* create(MMgc::GC* gc, VTable* cvtable)
        {
            return new (gc, MMgc::kExact, cvtable->getExtraSize()) DoubleVectorClass(cvtable);
        }

        GC_NO_DATA(DoubleVectorClass)

        DECLARE_SLOTS_DoubleVectorClass;
    };

    // ----------------------------

#ifdef VMCFG_FLOAT
    class GC_AS3_EXACT(FloatVectorClass, TypedVectorClass<FloatVectorObject>)
    {
    private:
        explicit FloatVectorClass(VTable* vtable);
    public:
        REALLY_INLINE static FloatVectorClass* create(MMgc::GC* gc, VTable* cvtable)
        {
            return new (gc, MMgc::kExact, cvtable->getExtraSize()) FloatVectorClass(cvtable);
        }

        GC_NO_DATA(FloatVectorClass)

        DECLARE_SLOTS_FloatVectorClass;
    };

    class GC_AS3_EXACT(Float4VectorClass, TypedVectorClass<Float4VectorObject>)
    {
    private:
        explicit Float4VectorClass(VTable* vtable);
    public:
        REALLY_INLINE static Float4VectorClass* create(MMgc::GC* gc, VTable* cvtable)
        {
            return new (gc, MMgc::kExact, cvtable->getExtraSize()) Float4VectorClass(cvtable);
        }
        
        GC_NO_DATA(Float4VectorClass)
        
        DECLARE_SLOTS_Float4VectorClass;
    };
#endif

    // ----------------------------

    class GC_AS3_EXACT(ObjectVectorClass, TypedVectorClass<ObjectVectorObject>)
    {
    private:
        explicit ObjectVectorClass(VTable* vtable);
    public:
        REALLY_INLINE static ObjectVectorClass* create(MMgc::GC* gc, VTable* cvtable)
        {
            return new (gc, MMgc::kExact, cvtable->getExtraSize()) ObjectVectorClass(cvtable);
        }

        GC_NO_DATA(ObjectVectorClass)

        DECLARE_SLOTS_ObjectVectorClass;
    };

    // ----------------------------

    struct VectorIndex
    {
        explicit VectorIndex(AvmCore* core, Atom name);

        uint32_t index;
        enum { kNotNumber, kInvalidNumber, kValidNumber } status;
    };

    // ----------------------------

    template<class T>
    struct TypedVectorConstants
    {
        static T nullValue();
        static T undefinedValue();
    };

    // ----------------------------

#ifdef VMCFG_FLOAT
    template<typename T>
    struct typeIsFloat4
    {
        const static bool value = false;
    };

    template<>
    struct typeIsFloat4<float4_t>
    {
        const static bool value = true;
    };
#define IS_FLOAT4_TYPE(T) typeIsFloat4<T>::value
#else
#define IS_FLOAT4_TYPE(T) false
#endif 

    // ----------------------------

    class GC_CPP_EXACT(VectorBaseObject, ScriptObject)
    {
    protected:
        explicit VectorBaseObject(VTable* ivtable, ScriptObject* delegate);

    public:
        // AS3 native getter/setter implementations
        bool get_fixed() const;
        void set_fixed(bool fixed);

        // used by Flash code for AMF3 -- note that this is
        // the type that the Vector contains, not the Vector itself
        // (ie, "int" rather than "Vector<int>")
        Traits* getTypeTraits() const;

        // AIR needs to be able to get/set the length of an arbitrary
        // Vector without knowing its subclass; these simply forward into the
        // appropriate calls of the well-typed subclass.
        virtual uint32_t getLength() const = 0;
        virtual void setLength(uint32_t length) = 0;

#ifdef VMCFG_VECTOR_SMASH_PROTECTION
		// For diagnostic purposes only!
		virtual void setLengthUnchecked(uint32_t length) = 0;
#endif

    protected:
        
        Atom _mapImpl(ScriptObject* callback, Atom thisObject, VectorBaseObject* r, uint32_t len);
        Atom _filterImpl(ScriptObject* callback, Atom thisObject, VectorBaseObject* r, uint32_t len);

        VectorBaseObject* _newVector();

        void checkFixed() const;
        void FASTCALL throwFixedError() const;

        void atomToValue(Atom atom, int32_t& value);
        void atomToValueKnown(Atom atom, int32_t& value);
        Atom valueToAtom(const int32_t& value) const;

        void atomToValue(Atom atom, uint32_t& value);
        void atomToValueKnown(Atom atom, uint32_t& value);
        Atom valueToAtom(const uint32_t& value) const;

        void atomToValue(Atom atom, double& value);
        void atomToValueKnown(Atom atom, double& value);
        Atom valueToAtom(const double& value) const;

#ifdef VMCFG_FLOAT
        void atomToValue(Atom atom, float& value);
        void atomToValueKnown(Atom atom, float& value);
        Atom valueToAtom(const float& value) const;

        void atomToValue(Atom atom, float4_t& value);
        void atomToValueKnown(Atom atom, float4_t& value);
        Atom valueToAtom(const float4_t& value) const;
#endif // VMCFG_FLOAT

        void atomToValue(Atom atom, OpaqueAtom& value);
        void atomToValueKnown(Atom atom, OpaqueAtom& value);
        Atom valueToAtom(const OpaqueAtom& value) const;

        enum VectorIndexStatus { kNotNumber, kInvalidNumber, kValidNumber };
        VectorIndexStatus getVectorIndex(Atom name, uint32_t& index) const;
        static bool isNegativeVectorIndexAtom(Atom name);

        void throwGetDoubleException(double d, uint32_t length) const;
        void throwSetDoubleException(double d, uint32_t length) const;
    
        GC_DATA_BEGIN(VectorBaseObject)

        GCMember<TypedVectorClassBase>    GC_POINTER(m_vecClass);
        bool                            m_fixed;

        GC_DATA_END(VectorBaseObject)
    };

    // ----------------------------

    template<class TLIST>
    class TypedVectorObject : public VectorBaseObject
    {
        friend class CodegenLIR;
        friend class ObjectVectorObject;
        template<class OBJ> friend class TypedVectorClass;
        template<class TLISTVA, uintptr_t align> friend class VectorAccessor;

    public:
        typedef TLIST LIST;

    protected:
        explicit TypedVectorObject(VTable* ivtable, ScriptObject* delegate);

    public:
        // overrides
        virtual uint32_t getLength() const;
        virtual void setLength(uint32_t length);

#ifdef VMCFG_VECTOR_SMASH_PROTECTION
		// For diagnostic purposes only!
		virtual void setLengthUnchecked(uint32_t length);
#endif

        // AS3 native getter/setter implementations
        uint32_t get_length() const;
        void set_length(uint32_t newLength);

        // AS3 native function implementations
        Atom _map(ScriptObject* callback, Atom thisObject);
        Atom _filter(ScriptObject* callback, Atom thisObject);
        void _reverse();
        // First delete deleteCount entries, starting at insertPoint.
        // Then insert insertCount entries starting at insertPoint;
        // the insertCount entries are read from args, starting at offset.
        void _spliceHelper(uint32_t insertPoint, uint32_t insertCount, uint32_t deleteCount, Atom args, uint32_t offset);
		void AS3_insertAt(int32_t index, typename TLIST::TYPE element);
		typename TLIST::TYPE AS3_removeAt(int32_t index);
        uint32_t AS3_push(Atom* argv, int argc);
        typename TLIST::TYPE AS3_pop();
#ifdef VMCFG_FLOAT
        void AS3_pop(typename TLIST::TYPE *);
#endif
        uint32_t AS3_unshift(Atom* argv, int argc);
        typename TLIST::TYPE AS3_shift();
#ifdef VMCFG_FLOAT
        void AS3_shift(typename TLIST::TYPE *);
#endif

        // ScriptObject method overrides
        virtual bool hasAtomProperty(Atom name) const;
        virtual Atom getAtomProperty(Atom name) const;
        virtual void setAtomProperty(Atom name, Atom value);
        virtual bool hasUintProperty(uint32_t index) const;
        virtual Atom getUintProperty(uint32_t index) const;
        virtual void setUintProperty(uint32_t index, Atom value);
        virtual Atom nextName(int index);
        virtual Atom nextValue(int index);
        virtual int nextNameIndex(int index);
        
        // "fast" methods that don't range-check in nondebug builds
        // and are inlined; for internal use by various bits of Flash glue code
        typename TLIST::TYPE getUintPropertyFast(uint32_t index) const;
        void setUintPropertyFast(uint32_t index, typename TLIST::TYPE value);

        // JIT helpers -- not for public use!
        // (declared public only to avoid a painful 'friend' declaration)
        typename TLIST::TYPE _getNativeIntProperty(int32_t index) const;
        void _setNativeIntProperty(int32_t index, typename TLIST::TYPE value);
#ifdef VMCFG_AOT
		void _setNativeIntPropertyFast(int32_t index, typename TLIST::TYPE value);
		void _setNativeIntPropertySlow(int32_t index, typename TLIST::TYPE value);
#endif
        typename TLIST::TYPE _getNativeUintProperty(uint32_t index) const;
        void _setNativeUintProperty(uint32_t index, typename TLIST::TYPE value);
        typename TLIST::TYPE _getNativeDoubleProperty(double index) const;
        void _setNativeDoubleProperty(double index, typename TLIST::TYPE value);
        bool _hasUintProperty(uint32_t index) const;
        Atom _getUintProperty(uint32_t index) const;
        void _setUintProperty(uint32_t index, Atom value);
        Atom _getIntProperty(int32_t index) const;
        void _setIntProperty(int32_t index, Atom value);
        Atom _getDoubleProperty(double index) const;
        void _setDoubleProperty(double index, Atom value);

#ifdef VMCFG_FLOAT
        /* Float4 helpers; these  helpers are only used for float4 values (i.e. when TLIST::TYPE is float4_t). 
           This is asserted in the implementations. */
        void _getFloat4IntProperty(typename TLIST::TYPE* retval, int32_t index) const;
        void _setFloat4IntProperty(int32_t index, const typename TLIST::TYPE& value);
        void _getFloat4UintProperty(typename TLIST::TYPE* retval, uint32_t index) const;
        void _setFloat4UintProperty(uint32_t index, const typename TLIST::TYPE& value);
        void _getFloat4DoubleProperty(typename TLIST::TYPE* retval, double index) const;
        void _setFloat4DoubleProperty(double index, const typename TLIST::TYPE& value);
        /* asserts used in the implementation, to verify that these helpers are used properly */
#endif
        
        // Optimized setters used by various JIT optimizations.  It is possible that
        // these ought to be moved into ObjectVectorObject in the manner of
        // _setKnown*PropertyWithPointer(), they only make sense for ObjectVectorObject.

        void _setKnownUintProperty(uint32_t index, Atom value);             // ObjectType <: VectorBaseType
        void _setKnownIntProperty(int32_t index, Atom value);               // ditto
        void _setKnownDoubleProperty(double index, Atom value);             // ditto

#ifdef DEBUGGER
        virtual uint64_t bytesUsed() const;
        virtual void registerDependentObjects(IMemorySampler* memSampler);
#endif
#ifdef VMCFG_AOT
		bool canTakeFastPath(int32_t index) const;
#endif
    private:

        void FASTCALL throwRangeError_u(uint32_t index) const;
        void FASTCALL throwRangeError_i(int32_t index) const;
        void FASTCALL throwRangeError_a(Atom index) const;

        void     checkReadIndex_u(uint32_t index) const;
        uint32_t checkReadIndex_i(int32_t index) const;
        uint32_t checkReadIndex_d(double index) const;
        void     checkWriteIndex_u(uint32_t index) const;
        uint32_t checkWriteIndex_i(int32_t index) const;
        uint32_t checkWriteIndex_d(double index) const;

        // variant of _spliceHelper with explicit array of Atom.
        // (Not exposed to AS3.)
        // Note:
        //      _splice(0, 1, 0, &foo)           == Array.unshift(foo)
        //      _splice(this.length, 1, 0, &foo) == Array.push(foo)
        void _splice(uint32_t insertPoint, uint32_t insertCount, uint32_t deleteCount, const Atom* argv);
        
        // _spliceHelper for when we already know that we have an object that is *not* of our Vector type.
        void _spliceHelper_so(uint32_t insertPoint, uint32_t insertCount, uint32_t deleteCount, ScriptObject* so_args, uint32_t offset);

    public:
        virtual bool gcTrace(MMgc::GC* gc, size_t cursor)
        {
            (void)cursor;
            VectorBaseObject::gcTrace(gc, 0);
            m_list.gcTrace(gc);
            return false;
        }

    protected:
#ifdef DEBUGGER
        bool                            m_created;
        bool                            m_flushBeforeConstructionComplete;
#endif
        TLIST                           m_list;
    };

    // Some code internal to Flash/AIR needs to directly get/set the contents of Vectors;
    // this class provides an implicit lock/unlock mechanism. We guarantee that
    // the value returned by addr() is valid for reading/writing for the lifespan of
    // the VectorAccessor (but only for entries 0...get_length()-1, of course).
    // length() is identical to VectorObject::get_length() but is provided here for symmetry.
    // This should obviously only be used in cases where performance is critical, or
    // other circumstances requires it (eg, to pass an array of numbers to a GPU
    // without intermediate copying). Note that it is explicitly legal to pass
    // a NULL VectorObject to the ctor (which will cause addr() to also return NULL
    // and length() to return 0). This class must be used only on the stack.
    template<class TLIST, uintptr_t align=0>
    class VectorAccessor
    {
    public:
        explicit VectorAccessor(TypedVectorObject<TLIST>* v);
        typename TLIST::TYPE* addr();
        uint32_t length();

    private:
        TypedVectorObject<TLIST>* m_vector;

    private:
        void* operator new(size_t size);                                        // unimplemented
    };


    // ----------------------------

    class GC_AS3_EXACT(IntVectorObject, TypedVectorObject< DataList<int32_t> >)
    {
    protected:
        explicit IntVectorObject(VTable* ivtable, ScriptObject* delegate);

    public:
        REALLY_INLINE static IntVectorObject* create(MMgc::GC* gc, VTable* ivtable, ScriptObject* delegate)
        {
            return new (gc, MMgc::kExact, ivtable->getExtraSize()) IntVectorObject(ivtable, delegate);
        }

        // AS3 native function implementations
        IntVectorObject* newThisType();

    // ------------------------ DATA SECTION BEGIN
    private:
        GC_NO_DATA(IntVectorObject)

        DECLARE_SLOTS_IntVectorObject;
    // ------------------------ DATA SECTION END
    };
    typedef VectorAccessor< DataList<int32_t> > IntVectorAccessor;

    // ----------------------------

    class GC_AS3_EXACT(UIntVectorObject, TypedVectorObject< DataList<uint32_t> >)
    {
    protected:
        explicit UIntVectorObject(VTable* ivtable, ScriptObject* delegate);

    public:
        REALLY_INLINE static UIntVectorObject* create(MMgc::GC* gc, VTable* ivtable, ScriptObject* delegate)
        {
            return new (gc, MMgc::kExact, ivtable->getExtraSize()) UIntVectorObject(ivtable, delegate);
        }

        // AS3 native function implementations
        UIntVectorObject* newThisType();


    // ------------------------ DATA SECTION BEGIN
    private:
        GC_NO_DATA(UIntVectorObject)

        DECLARE_SLOTS_UIntVectorObject;
    // ------------------------ DATA SECTION END
    };
    typedef VectorAccessor< DataList<uint32_t> > UIntVectorAccessor;

    // ----------------------------

    class GC_AS3_EXACT(DoubleVectorObject, TypedVectorObject< DataList<double> >)
    {
    protected:
        explicit DoubleVectorObject(VTable* ivtable, ScriptObject* delegate);

    public:
        REALLY_INLINE static DoubleVectorObject* create(MMgc::GC* gc, VTable* ivtable, ScriptObject* delegate)
        {
            return new (gc, MMgc::kExact, ivtable->getExtraSize()) DoubleVectorObject(ivtable, delegate);
        }

        // AS3 native function implementations
        DoubleVectorObject* newThisType();

    // ------------------------ DATA SECTION BEGIN
    private:
        GC_NO_DATA(DoubleVectorObject)

        DECLARE_SLOTS_DoubleVectorObject;
    // ------------------------ DATA SECTION END
    };
    typedef VectorAccessor< DataList<double> > DoubleVectorAccessor;

    // ----------------------------

#ifdef VMCFG_FLOAT
    class GC_AS3_EXACT(FloatVectorObject, TypedVectorObject< DataList<float> >)
    {
    protected:
        explicit FloatVectorObject(VTable* ivtable, ScriptObject* delegate);

    public:
        REALLY_INLINE static FloatVectorObject* create(MMgc::GC* gc, VTable* ivtable, ScriptObject* delegate)
        {
            return new (gc, MMgc::kExact, ivtable->getExtraSize()) FloatVectorObject(ivtable, delegate);
        }

        // AS3 native function implementations
        FloatVectorObject* newThisType();

        // ------------------------ DATA SECTION BEGIN
    private:
        GC_NO_DATA(FloatVectorObject)

            DECLARE_SLOTS_FloatVectorObject;
        // ------------------------ DATA SECTION END
    };
    typedef VectorAccessor< DataList<float> > FloatVectorAccessor;

    // Macros and templates don't mix
    typedef TypedVectorObject< DataList<float4_t, 16> > Float4VectorObjectBaseClass;
    
    class GC_AS3_EXACT(Float4VectorObject, Float4VectorObjectBaseClass)
    {
    protected:
        explicit Float4VectorObject(VTable* ivtable, ScriptObject* delegate);
        
    public:
        REALLY_INLINE static Float4VectorObject* create(MMgc::GC* gc, VTable* ivtable, ScriptObject* delegate)
        {
            return new (gc, MMgc::kExact, ivtable->getExtraSize()) Float4VectorObject(ivtable, delegate);
        }
        
        // AS3 native function implementations
        Float4VectorObject* newThisType();
        
        // ------------------------ DATA SECTION BEGIN
    private:
        GC_NO_DATA(Float4VectorObject)
        
        DECLARE_SLOTS_Float4VectorObject;
        // ------------------------ DATA SECTION END
    };
    typedef VectorAccessor< DataList<float4_t, 16>, 16 > Float4VectorAccessor;
#endif

    // ----------------------------

    class GC_AS3_EXACT(ObjectVectorObject, TypedVectorObject< AtomList >)
    {
    protected:
        explicit ObjectVectorObject(VTable* ivtable, ScriptObject* delegate);

    public:
        REALLY_INLINE static ObjectVectorObject* create(MMgc::GC* gc, VTable* ivtable, ScriptObject* delegate)
        {
            return new (gc, MMgc::kExact, ivtable->getExtraSize()) ObjectVectorObject(ivtable, delegate);
        }

        // The JIT calls _setKnown*PropertyWithPointer when the Vector is known to contain only
        // RCObject values and the value to be stored is an RCObject subtype of the element type.
        // Note the call to m_list.setPointer() instead of m_list.set(); the former streamlines
        // the write barrier.
        //
        // These are here and not in TypedVectorObject because they only make sense
        // for ObjectVectorObject.

        void _setKnownUintPropertyWithPointer(uint32_t index, Atom value)
        {
            checkWriteIndex_u(index);
            AtomList::OPAQUE_TYPE tmp;
            atomToValueKnown(value, tmp);
            m_list.setPointer(index, (AtomList::TYPE)tmp);
        }
        
        void _setKnownIntPropertyWithPointer(int32_t index_i, Atom value)
        {
            uint32_t index = checkWriteIndex_i(index_i);
            AtomList::OPAQUE_TYPE tmp;
            atomToValueKnown(value, tmp);
            m_list.setPointer(index, (AtomList::TYPE)tmp);
        }
        
        void _setKnownDoublePropertyWithPointer(double index_d, Atom value)
        {
            uint32_t index = checkWriteIndex_d(index_d);
            AtomList::OPAQUE_TYPE tmp;
            atomToValueKnown(value, tmp);
            m_list.setPointer(index, (AtomList::TYPE)tmp);
        }


        // AS3 native function implementations
        ObjectVectorObject* newThisType();

    // ------------------------ DATA SECTION BEGIN
    private:
        GC_NO_DATA(ObjectVectorObject)

        DECLARE_SLOTS_ObjectVectorObject;
    // ------------------------ DATA SECTION END
    };
    // This is explicitly NOT provided, as
    // (1) there's currently no need for it, and
    // (2) it would require WB hackery on the part of the user, which is error-prone
    //typedef VectorAccessor<Atom> ObjectVectorAccessor;

    // ----------------------------
}

#endif /* __avmplus_VectorClass__ */
