/* -*- 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_impl__ #define __avmplus_VectorClass_impl__ namespace avmplus { // ---------------------------- Atom TypedVectorClassBase::call(int argc, Atom* argv) { Toplevel* toplevel = this->toplevel(); AvmCore* core = toplevel->core(); if (argc != 1) toplevel->throwArgumentError(kCoerceArgumentCountError, core->toErrorString(argc)); Traits* itraits = ivtable()->traits; if (AvmCore::istype(argv[1], itraits)) return argv[1]; if (!AvmCore::isObject(argv[1])) toplevel->throwTypeError(kCheckTypeFailedError, core->atomToErrorString(argv[1]), core->toErrorString(itraits)); ScriptObject* so_args = AvmCore::atomToScriptObject(argv[1]); uint32_t len = so_args->getLengthProperty(); return createAndInitVectorFromObject(so_args, len); } void TypedVectorClassBase::_forEach(Atom thisAtom, ScriptObject* callback, Atom thisObject) { return ArrayClass::generic_forEach(toplevel(), thisAtom, callback, thisObject); } bool TypedVectorClassBase::_every(Atom thisAtom, ScriptObject* callback, Atom thisObject) { return ArrayClass::generic_every(toplevel(), thisAtom, callback, thisObject); } bool TypedVectorClassBase::_some(Atom thisAtom, ScriptObject* callback, Atom thisObject) { return ArrayClass::generic_some(toplevel(), thisAtom, callback, thisObject); } Atom TypedVectorClassBase::_sort(Atom thisAtom, ArrayObject* args) { return ArrayClass::generic_sort(toplevel(), thisAtom, args); } // ---------------------------- template TypedVectorClass::TypedVectorClass(VTable* vtable) : TypedVectorClassBase(vtable) { setPrototypePtr(toplevel()->objectClass->construct()); } template OBJ* TypedVectorClass::newVector(uint32_t length, bool fixed) { OBJ* v = (OBJ*)OBJ::create(gc(), ivtable(), prototypePtr()); v->m_vecClass = this; if (length > 0) v->set_length(length); v->m_fixed = fixed; return v; } template Atom TypedVectorClass::createAndInitVectorFromObject(ScriptObject* so_args, uint32_t len) { OBJ* v = newVector(); v->_spliceHelper_so(0, len, 0, so_args, 0); return v->atom(); } template Atom TypedVectorClass::constructImpl(int argc, Atom* argv) { uint32_t len = 0; bool fixed = false; if (argc > 0) { len = AvmCore::toUInt32(argv[1]); if (argc > 1) { fixed = AvmCore::boolean(argv[2]) != 0; if (argc > 2) { vtable->init->argcError(argc); } } } OBJ* v = newVector(len, fixed); return v->atom(); } // ---------------------------- VectorBaseObject::VectorBaseObject(VTable* ivtable, ScriptObject* delegate) : ScriptObject(ivtable, delegate) , m_vecClass(NULL) , m_fixed(false) { } VectorBaseObject* VectorBaseObject::_newVector() { AvmAssert(m_vecClass != NULL); Atom args[1] = { nullObjectAtom }; return (VectorBaseObject*)AvmCore::atomToScriptObject(m_vecClass->construct(0, args)); } void FASTCALL VectorBaseObject::throwFixedError() const { toplevel()->throwRangeError(kVectorFixedError); } Atom VectorBaseObject::_mapImpl(ScriptObject* callback, Atom thisObject, VectorBaseObject* r, uint32_t len) { AvmCore* core = this->core(); if (callback) { ScriptObject* d = this; for (uint32_t 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::_filterImpl(ScriptObject* callback, Atom thisObject, VectorBaseObject* r, uint32_t len) { AvmCore* core = this->core(); if (callback) { ScriptObject* d = this; for (uint32_t 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(); } // ---------------------------- template TypedVectorObject::TypedVectorObject(VTable* ivtable, ScriptObject* delegate) : VectorBaseObject(ivtable, delegate) #ifdef DEBUGGER , m_created(false) , m_flushBeforeConstructionComplete(false) #endif , m_list(ivtable->gc(), 0) { #ifdef DEBUGGER m_created = true; if (m_flushBeforeConstructionComplete) { registerDependentObjects(getSampler()); getSampler()->recordObjectReallocation(this); } #endif } template bool TypedVectorObject::hasAtomProperty(Atom name) const { uint32_t index; VectorIndexStatus status = getVectorIndex(name, index); if (status == kValidNumber) { return index < m_list.length(); } else if (status == kInvalidNumber) { if (core()->currentBugCompatibility()->bugzilla456852b) return false; else if (!isNegativeVectorIndexAtom(name)) // Prior to SWF11, isNumber was incorrectly set false for negative numbers, // so we fall through and allow searching the prototype chain for these. // See getAtomProperty() below. return false; } // status == kNotNumber, or index is negative and compatibility is earlier than SWF11. return ScriptObject::hasAtomProperty(name); } template void TypedVectorObject::setAtomProperty(Atom name, Atom value) { uint32_t index; VectorIndexStatus status = getVectorIndex(name, index); if (status == kValidNumber) { setUintProperty(index, value); } else if (status == kInvalidNumber && core()->currentBugCompatibility()->bugzilla456852b) { throwRangeError_a(name); } else { toplevel()->throwReferenceError(kWriteSealedError, core()->string(name), traits()); } } template Atom TypedVectorObject::getAtomProperty(Atom name) const { uint32_t index; VectorIndexStatus status = getVectorIndex(name, index); if (status == kValidNumber) { return getUintProperty(index); } else if (status == kInvalidNumber) { if (core()->currentBugCompatibility()->bugzilla456852b) { throwRangeError_a(name); return undefinedAtom; // not reached } else if (!isNegativeVectorIndexAtom(name)) { // Prior to SWF11, isNumber was incorrectly set false for negative numbers. toplevel()->throwReferenceError(kReadSealedError, core()->string(name), traits()); return undefinedAtom; // not reached } } // status == kNotNumber, or index is negative and compatibility is earlier than SWF11. // Check the prototype chain - that will throw if there is no match. return getAtomPropertyFromProtoChain(name, getDelegate(), traits()); } template /*virtual*/ uint32_t TypedVectorObject::getLength() const { return get_length(); } template /*virtual*/ void TypedVectorObject::setLength(uint32_t length) { set_length(length); } template void TypedVectorObject::set_length(uint32_t newLength) { checkFixed(); // this is what the old code did, so we'll do it too: set the capacity explicitly if we grow if (newLength > m_list.capacity()) { m_list.set_capacity(newLength); } m_list.set_length(newLength); } template Atom TypedVectorObject::nextName(int index) { AvmAssert(index > 0); if (uint32_t(index) <= m_list.length()) { return core()->intToAtom(index-1); } else { return nullStringAtom; } } template Atom TypedVectorObject::nextValue(int index) { AvmAssert(index > 0); if (uint32_t(index) <= m_list.length()) { return getUintProperty(index-1); } else { return undefinedAtom; } } template int TypedVectorObject::nextNameIndex(int index) { if (uint32_t(index) < m_list.length()) { return index + 1; } else { return 0; } } template Atom TypedVectorObject::_map(ScriptObject* callback, Atom thisObject) { TypedVectorObject* r = (TypedVectorObject*)_newVector(); uint32_t const len = m_list.length(); r->set_length(len); return _mapImpl(callback, thisObject, r, len); } template Atom TypedVectorObject::_filter(ScriptObject* callback, Atom thisObject) { TypedVectorObject* r = (TypedVectorObject*)_newVector(); uint32_t const len = m_list.length(); return _filterImpl(callback, thisObject, r, len); } template void TypedVectorObject::_spliceHelper_so(uint32_t insertPoint, uint32_t insertCount, uint32_t deleteCount, ScriptObject* so_args, uint32_t offset) { AvmAssert(!AvmCore::istype(so_args->atom(), vtable->traits)); // NB: so_args could be any kind of object, but in practice, // it's almost always an ArrayObject; it might be profitable // to sniff for that case and optimize appropriately. // passing NULL for args inserts the proper space, but leaves the inserted entries empty m_list.splice(insertPoint, insertCount, deleteCount, NULL); for (uint32_t i = 0; i < insertCount; i++) { typename TLIST::OPAQUE_TYPE value; atomToValue(so_args->getUintProperty(i+offset), value); m_list.set(insertPoint+i, (typename TLIST::TYPE)value); } } template void TypedVectorObject::_spliceHelper(uint32_t insertPoint, uint32_t insertCount, uint32_t deleteCount, Atom args, uint32_t offset) { if (AvmCore::istype(args, vtable->traits)) { TypedVectorObject* vec_args = (TypedVectorObject*)AvmCore::atomToScriptObject(args); m_list.splice(insertPoint, insertCount, deleteCount, vec_args->m_list, offset); } else if (AvmCore::isObject(args)) { ScriptObject* so_args = AvmCore::atomToScriptObject(args); _spliceHelper_so(insertPoint, insertCount, deleteCount, so_args, offset); } else { AvmAssert(insertCount == 0); m_list.splice(insertPoint, 0, deleteCount, NULL); } } template void TypedVectorObject::_splice(uint32_t insertPoint, uint32_t insertCount, uint32_t deleteCount, const Atom* argv) { // passing NULL for args inserts the proper space, but leaves the inserted entries empty m_list.splice(insertPoint, insertCount, deleteCount, NULL); for (uint32_t i = 0; i < insertCount; i++) { typename TLIST::OPAQUE_TYPE value; atomToValue(argv[i], value); m_list.set(insertPoint+i, (typename TLIST::TYPE)value); } } template uint32_t TypedVectorObject::AS3_push(Atom* argv, int argc) { checkFixed(); _splice(m_list.length(), argc, 0, argv); return m_list.length(); } template typename TLIST::TYPE TypedVectorObject::AS3_pop() { checkFixed(); return m_list.isEmpty() ? (typename TLIST::TYPE)TypedVectorConstants::undefinedValue() : m_list.removeLast(); } #ifdef VMCFG_FLOAT template void TypedVectorObject::AS3_pop(typename TLIST::TYPE* retval) { checkFixed(); *retval = m_list.isEmpty() ? (typename TLIST::TYPE)TypedVectorConstants::undefinedValue() : m_list.removeLast(); } #endif template typename TLIST::TYPE TypedVectorObject::AS3_shift() { checkFixed(); return m_list.isEmpty() ? (typename TLIST::TYPE)TypedVectorConstants::undefinedValue() : m_list.removeFirst(); } #ifdef VMCFG_FLOAT template void TypedVectorObject::AS3_shift(typename TLIST::TYPE* retval) { checkFixed(); *retval = m_list.isEmpty() ? (typename TLIST::TYPE)TypedVectorConstants::undefinedValue() : m_list.removeFirst(); } #endif template uint32_t TypedVectorObject::AS3_unshift(Atom* argv, int argc) { checkFixed(); _splice(0, argc, 0, argv); return m_list.length(); } template REALLY_INLINE typename TLIST::TYPE TypedVectorObject::_getNativeIntProperty(int32_t index_i) const { AvmAssertMsg(!IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _getNativeIntProperty helper used for Vector."); uint32_t const index = checkReadIndex_i(index_i); return m_list.get(index); } template void TypedVectorObject::_setNativeIntProperty(int32_t index_i, typename TLIST::TYPE value) { AvmAssertMsg(!IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _setNativeIntProperty helper used for Vector."); uint32_t const index = checkWriteIndex_i(index_i); m_list.set(index, value); } #ifdef VMCFG_AOT template NO_INLINE void TypedVectorObject::_setNativeIntPropertySlow(int32_t index_i, typename TLIST::TYPE value) { uint32_t const length = m_list.length(); AvmAssert(uint32_t(index_i) >= length); uint32_t const limit = length + 1 - uint32_t(m_fixed); if (uint32_t(index_i) >= limit) throwRangeError_i(index_i); AvmAssertMsg(!IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _setNativeIntProperty helper used for Vector."); m_list.set((uint32_t)index_i, value); } #endif template typename TLIST::TYPE TypedVectorObject::_getNativeUintProperty(uint32_t index) const { AvmAssertMsg(!IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _getNativeUIntProperty helper used for Vector."); checkReadIndex_u(index); return m_list.get(index); } template void TypedVectorObject::_setNativeUintProperty(uint32_t index, typename TLIST::TYPE value) { AvmAssertMsg(!IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _setNativeUIntProperty helper used for Vector."); checkWriteIndex_u(index); m_list.set(index, value); } template typename TLIST::TYPE TypedVectorObject::_getNativeDoubleProperty(double index_d) const { AvmAssertMsg(!IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _getNativeDoubleProperty helper used for Vector."); uint32_t const index = checkReadIndex_d(index_d); return m_list.get(index); } template void TypedVectorObject::_setNativeDoubleProperty(double index_d, typename TLIST::TYPE value) { AvmAssertMsg(!IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _setNativeDoubleProperty helper used for Vector."); uint32_t const index = checkWriteIndex_d(index_d); m_list.set(index, value); } #ifdef VMCFG_FLOAT template void TypedVectorObject::_getFloat4IntProperty(typename TLIST::TYPE* result, int32_t index_i) const { AvmAssertMsg(IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _getNativeIntProperty helper used for Vector.<*>"); uint32_t const index = checkReadIndex_i(index_i); if(result) *result = m_list.get(index); return; } template void TypedVectorObject::_setFloat4IntProperty(int32_t index_i, const typename TLIST::TYPE& value) { AvmAssertMsg(IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _setNativeIntProperty helper used for Vector.<*>"); uint32_t const index = checkWriteIndex_i(index_i); m_list.set(index, value); } template void TypedVectorObject::_getFloat4UintProperty(typename TLIST::TYPE* result, uint32_t index) const { AvmAssertMsg(IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _getNativeUIntProperty helper used for Vector.<*>"); checkReadIndex_u(index); if(result) *result = m_list.get(index); return; } template void TypedVectorObject::_setFloat4UintProperty(uint32_t index, const typename TLIST::TYPE& value) { AvmAssertMsg(IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _setNativeUIntProperty helper used for Vector.<*>"); checkWriteIndex_u(index); m_list.set(index, value); } template void TypedVectorObject::_getFloat4DoubleProperty(typename TLIST::TYPE* result, double index_d) const { AvmAssertMsg(IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _getNativeDoubleProperty helper used for Vector.<*>"); uint32_t const index = checkReadIndex_d(index_d); if(result) *result = m_list.get(index); return; } template void TypedVectorObject::_setFloat4DoubleProperty(double index_d, const typename TLIST::TYPE& value) { AvmAssertMsg(IS_FLOAT4_TYPE(typename TLIST::TYPE), "wrong _setNativeDoubleProperty helper used for Vector.<*>"); uint32_t const index = checkWriteIndex_d(index_d); m_list.set(index, value); // needed to compile for most types; hopefully the useless call will be optimized-away. } #endif template bool TypedVectorObject::_hasUintProperty(uint32_t index) const { return (index < m_list.length()); } template Atom TypedVectorObject::_getUintProperty(uint32_t index) const { checkReadIndex_u(index); return valueToAtom((typename TLIST::OPAQUE_TYPE)m_list.get(index)); } template void TypedVectorObject::_setUintProperty(uint32_t index, Atom value) { typename TLIST::OPAQUE_TYPE tmp; atomToValue(value, tmp); checkWriteIndex_u(index); m_list.set(index, (typename TLIST::TYPE)tmp); } template void TypedVectorObject::_setKnownUintProperty(uint32_t index, Atom value) { typename TLIST::OPAQUE_TYPE tmp; atomToValueKnown(value, tmp); checkWriteIndex_u(index); m_list.set(index, (typename TLIST::TYPE)tmp); } template Atom TypedVectorObject::_getIntProperty(int32_t index_i) const { uint32_t const index = checkReadIndex_i(index_i); return valueToAtom((typename TLIST::OPAQUE_TYPE)m_list.get(index)); } template void TypedVectorObject::_setIntProperty(int32_t index_i, Atom value) { typename TLIST::OPAQUE_TYPE tmp; atomToValue(value, tmp); uint32_t index = checkWriteIndex_i(index_i); m_list.set(index, (typename TLIST::TYPE)tmp); } template void TypedVectorObject::_setKnownIntProperty(int32_t index_i, Atom value) { typename TLIST::OPAQUE_TYPE tmp; atomToValueKnown(value, tmp); uint32_t index = checkWriteIndex_i(index_i); m_list.set(index, (typename TLIST::TYPE)tmp); } template Atom TypedVectorObject::_getDoubleProperty(double index_d) const { uint32_t const index = checkReadIndex_d(index_d); return valueToAtom((typename TLIST::OPAQUE_TYPE)m_list.get(index)); } template void TypedVectorObject::_setDoubleProperty(double index_d, Atom value) { typename TLIST::OPAQUE_TYPE tmp; atomToValue(value, tmp); uint32_t index = checkWriteIndex_d(index_d); m_list.set(index, (typename TLIST::TYPE)tmp); } template void TypedVectorObject::_setKnownDoubleProperty(double index_d, Atom value) { typename TLIST::OPAQUE_TYPE tmp; atomToValueKnown(value, tmp); uint32_t index = checkWriteIndex_d(index_d); m_list.set(index, (typename TLIST::TYPE)tmp); } template void FASTCALL TypedVectorObject::throwRangeError_u(uint32_t index) const { toplevel()->throwRangeError(kOutOfRangeError, core()->uintToString(index), core()->uintToString(m_list.length())); } template void FASTCALL TypedVectorObject::throwRangeError_i(int32_t index) const { toplevel()->throwRangeError(kOutOfRangeError, core()->intToString(index), core()->uintToString(m_list.length())); } template void FASTCALL TypedVectorObject::throwRangeError_a(Atom index) const { toplevel()->throwRangeError(kOutOfRangeError, core()->string(index), core()->uintToString(m_list.length())); } // ---------------------------- } #endif /* __avmplus_VectorClass_impl__ */