/* -*- 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/. */ #include "avmplus.h" #include "VectorClass-impl.h" namespace avmplus { // Force explicit instantiations for various non-inlined methods; // some compilers don't need this, but some do. template class TypedVectorClass; template class TypedVectorClass; template class TypedVectorClass; #ifdef VMCFG_FLOAT template class TypedVectorClass; template class TypedVectorClass; #endif template class TypedVectorClass; template class TypedVectorObject< DataList >; template class TypedVectorObject< DataList >; template class TypedVectorObject< DataList >; #ifdef VMCFG_FLOAT template class TypedVectorObject< DataList >; template class TypedVectorObject< DataList >; #endif template class TypedVectorObject< AtomList >; } namespace avmplus { // ---------------------------- // helper method // sets index to the uint32_t value of name, if it can be converted to a vector index // isNumber is set to true if name was a number (whether it was a uint32_t value or not) VectorBaseObject::VectorIndexStatus VectorBaseObject::getVectorIndex(Atom name, uint32_t& index) const { // This is implicitly asserted by the call to AvmCore::getIndexFromAtom() below. // Make it explicit here. AvmAssert(atomIsIntptr(name) || AvmCore::isString(name)); if (AvmCore::getIndexFromAtom(name, &index)) { return kValidNumber; } else if (AvmCore::isString(name)) { Stringp s = core()->string(name); wchar c = s->charAt(0); // Does it look like a number? if (s->length() > 0 && ((c >= '0' && c <= '9') || c == '-')) { double const index_d = s->toNumber(); if (!MathUtils::isNaN(index_d)) { // Name is a string that looks like a number. // Convert using int, not uint, as it's much faster. int32_t const index_i = int32_t(index_d); if (double(index_i) == index_d) { index = uint32_t(index_i); return (index_i >= 0 ? kValidNumber : kInvalidNumber); } #if 0 // If number cannot be converted to a int, try to convert // to a uint. This is slower, but we are going to throw // an exception anyway at this point: Either the number // is non-integral, or it is larger than we can allocate // for a (dense) vector. uint32_t const index_u = uint32_t(index_d); if (double(index_u) == index_d) { index = index_u; return kValidNumber; } #else // Number is non-integral, negative, or too large for an int. // Note that some larger values that fit in a uint are valid // index values, and we would expect that kValidNumber would be // returned. In Tamarin, however, we cannot allocate a vector // sufficiently large to make such an index legal, so such an // index will always yield a ReferenceError whenever it is used // to subscript a vector, just as for kInvalidNumber. Furthermore, // a similar quirk has been long-standing, and compatibility with // SWF10 relies upon it. return kInvalidNumber; #endif } } } return kNotNumber; } // Return true if name is a negative number or a string denoting the same. // Assumes that getVectorIndex() has been previously invoked with argument 'name', // and returned with the 'isNumber' flag true. bool VectorBaseObject::isNegativeVectorIndexAtom(Atom name) { AvmAssert(atomIsIntptr(name) || (AvmCore::isString(name) && AvmCore::atomToString(name)->length() > 0)); return (atomIsIntptr(name) ? atomGetIntptr(name) < 0 : AvmCore::atomToString(name)->charAt(0) == '-'); } // Helper for _getDoubleProperty and _getNativedoubleProperty // Called when vector index of type double is non-integral or negative, or too large to fit in an int32. void VectorBaseObject::throwGetDoubleException(double d, uint32_t length) const { // For SWF11 and later, throw RangeError. // For SWF10 and earlier, throw ReferenceError if the value is not legal ECMAscript Array index, // which must be in the range 0..2^32-2. Otherwise, throw RangeError. if ((double(uint32_t(d)) == d && uint32_t(d) != 0xffffffff) || core()->currentBugCompatibility()->bugzilla456852b) { toplevel()->throwRangeError(kOutOfRangeError, core()->doubleToString(d), core()->uintToString(length)); } else { // NOTE: Backward compatibility for SWF10 and earlier requires that we search the // prototype chain. Unfortunately, that might result in a value of type other than // the declared element type of the vector, as assumed here, so we punt and just throw. Multiname mn(core()->findPublicNamespace(), core()->internDouble(d)); toplevel()->throwReferenceError(kReadSealedError, &mn, traits()); } } // Helper for _setDoubleProperty and _setNativeDoubleProperty. // Called when vector index of type double is non-integral or negative, or too large to fit in an int32. void VectorBaseObject::throwSetDoubleException(double d, uint32_t length) const { // For SWF11 and later, throw RangeError. // For SWF10 and earlier, throw ReferenceError if the value is not legal ECMAscript Array index, // or one greater than the largest legal index, i.e., in the range 0..2^32-1. Othewrwise, throw RangeError. if (double(uint32_t(d)) == d || core()->currentBugCompatibility()->bugzilla456852b) { toplevel()->throwRangeError(kOutOfRangeError, core()->doubleToString(d), core()->uintToString(length)); } else { Multiname mn(core()->findPublicNamespace(), core()->internDouble(d)); toplevel()->throwReferenceError(kWriteSealedError, &mn, traits()); } } // ---------------------------- VectorClass::VectorClass(VTable *vtable) : ClassClosure(vtable) { setPrototypePtr(toplevel()->objectClass->construct()); } /*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); } ClassClosure* VectorClass::getTypedVectorClass(ClassClosure* typeClass) { ClassClosure* result = NULL; Toplevel* toplevel = this->toplevel(); if (typeClass == NULL) { result = toplevel->objectVectorClass(); } else if (typeClass == toplevel->intClass()) { result = toplevel->intVectorClass(); } else if (typeClass == toplevel->uintClass()) { result = toplevel->uintVectorClass(); } else if (typeClass == toplevel->numberClass()) { result = toplevel->doubleVectorClass(); } #ifdef VMCFG_FLOAT else if (typeClass == toplevel->floatClass()) { result = toplevel->floatVectorClass(); } else if (typeClass == toplevel->float4Class()) { result = toplevel->float4VectorClass(); } #endif // VMCFG_FLOAT else { // if we have an object, we must have an itraits (otherwise the typearg is not a Class) Traits* typeTraits = typeClass->vtable->traits->itraits; if (!typeTraits) toplevel->throwVerifyError(kCorruptABCError); Domain* typeDomain = typeClass->traits()->pool->domain; if ((result = typeDomain->getParameterizedType(typeClass)) == NULL) { Stringp fullname = VectorClass::makeVectorClassName(core(), typeTraits); VTable* vt = this->vtable->newParameterizedVTable(typeTraits, fullname); vt->ivtable->createInstanceProc = ClassClosure::impossibleCreateInstanceProc; ObjectVectorClass* parameterizedVector = ObjectVectorClass::create(vt->gc(), vt); parameterizedVector->m_typeTraits = typeClass ? (Traits*)(typeClass->traits()->itraits) : NULL; parameterizedVector->setDelegate(toplevel->classClass()->prototypePtr()); // Is this right? Should each instantiation get its own prototype? parameterizedVector->setPrototypePtr(toplevel->objectVectorClass()->prototypePtr()); typeDomain->addParameterizedType(typeClass, parameterizedVector); result = parameterizedVector; } } AvmAssert(result != NULL); return result; } Atom VectorClass::applyTypeArgs(int argc, Atom* argv) { Toplevel* toplevel = this->toplevel(); if (argc != 1) { // Vector only takes 1 type argument toplevel->typeErrorClass()->throwError(kWrongTypeArgCountError, traits()->formatClassName(), core()->toErrorString(1), core()->toErrorString(argc)); } Atom const typeAtom = argv[0]; ClassClosure* typeClass = NULL; if (!ISNULL(typeAtom)) { if (atomKind(typeAtom) != kObjectType) toplevel->throwVerifyError(kCorruptABCError); typeClass = (ClassClosure*)AvmCore::atomToScriptObject(typeAtom); if (!typeClass->vtable->traits->itraits) toplevel->throwVerifyError(kCorruptABCError); } return getTypedVectorClass(typeClass)->atom(); } Atom VectorClass::construct(int /*argc*/, Atom* /*argv*/) { toplevel()->throwTypeError(kConstructOfNonFunctionError); return undefinedAtom; } // FIXME: this could return a non-ObjectVectorObject, so we should really // return VectorBaseObject instead. ObjectVectorObject* VectorClass::newVector(ClassClosure* type, uint32_t length) { ClassClosure* vc = getTypedVectorClass(type); Atom args[2] = { nullObjectAtom, core()->uintToAtom(length) }; return (ObjectVectorObject*)AvmCore::atomToScriptObject(vc->construct(1, args)); } // ---------------------------- IntVectorClass::IntVectorClass(VTable* vtable) : TypedVectorClass(vtable) { // This is an ugly hack: Vector<> doesn't work properly with the ClassManifest setup, // because the name it returns for itself isn't the name listed for finddef. // To work around this (and maintain legacy internal behavior), we pre-emptively // enter the Vector classes into the table upon first creation. (Arguably the name // lookup issue should be fixed.) toplevel()->builtinClasses()->fillInClass(avmplus::NativeID::abcclass___AS3___vec_Vector_int, this); this->m_typeTraits = toplevel()->intClass()->traits()->itraits; } Atom IntVectorClass::construct(int argc, Atom* argv) { return constructImpl(argc, argv); } // ---------------------------- UIntVectorClass::UIntVectorClass(VTable* vtable) : TypedVectorClass(vtable) { // This is an ugly hack: Vector<> doesn't work properly with the ClassManifest setup, // because the name it returns for itself isn't the name listed for finddef. // To work around this (and maintain legacy internal behavior), we pre-emptively // enter the Vector classes into the table upon first creation. (Arguably the name // lookup issue should be fixed.) toplevel()->builtinClasses()->fillInClass(avmplus::NativeID::abcclass___AS3___vec_Vector_uint, this); this->m_typeTraits = toplevel()->uintClass()->traits()->itraits; } Atom UIntVectorClass::construct(int argc, Atom* argv) { return constructImpl(argc, argv); } // ---------------------------- DoubleVectorClass::DoubleVectorClass(VTable* vtable) : TypedVectorClass(vtable) { // This is an ugly hack: Vector<> doesn't work properly with the ClassManifest setup, // because the name it returns for itself isn't the name listed for finddef. // To work around this (and maintain legacy internal behavior), we pre-emptively // enter the Vector classes into the table upon first creation. (Arguably the name // lookup issue should be fixed.) toplevel()->builtinClasses()->fillInClass(avmplus::NativeID::abcclass___AS3___vec_Vector_double, this); this->m_typeTraits = toplevel()->numberClass()->traits()->itraits; } Atom DoubleVectorClass::construct(int argc, Atom* argv) { return constructImpl(argc, argv); } // ---------------------------- #ifdef VMCFG_FLOAT FloatVectorClass::FloatVectorClass(VTable* vtable) : TypedVectorClass(vtable) { toplevel()->builtinClasses()->fillInClass(avmplus::NativeID::abcclass___AS3___vec_Vector_float, this); this->m_typeTraits = toplevel()->floatClass()->traits()->itraits; } Atom FloatVectorClass::construct(int argc, Atom* argv) { return constructImpl(argc, argv); } Float4VectorClass::Float4VectorClass(VTable* vtable) : TypedVectorClass(vtable) { toplevel()->builtinClasses()->fillInClass(avmplus::NativeID::abcclass___AS3___vec_Vector_float4, this); this->m_typeTraits = toplevel()->float4Class()->traits()->itraits; } Atom Float4VectorClass::construct(int argc, Atom* argv) { return constructImpl(argc, argv); } #endif // ---------------------------- ObjectVectorClass::ObjectVectorClass(VTable* vtable) : TypedVectorClass(vtable) { // This is an ugly hack: Vector<> doesn't work properly with the ClassManifest setup, // because the name it returns for itself isn't the name listed for finddef. // To work around this (and maintain legacy internal behavior), we pre-emptively // enter the Vector classes into the table upon first creation. (Arguably the name // lookup issue should be fixed.) toplevel()->builtinClasses()->fillInClass(avmplus::NativeID::abcclass___AS3___vec_Vector_object, this); this->m_typeTraits = toplevel()->objectClass->traits()->itraits; } Atom ObjectVectorClass::construct(int argc, Atom* argv) { return constructImpl(argc, argv); } // ---------------------------- IntVectorObject::IntVectorObject(VTable* ivtable, ScriptObject* delegate) : TypedVectorObject< DataList >(ivtable, delegate) { } IntVectorObject* IntVectorObject::newThisType() { return (IntVectorObject*)_newVector(); } // ---------------------------- UIntVectorObject::UIntVectorObject(VTable* ivtable, ScriptObject* delegate) : TypedVectorObject< DataList >(ivtable, delegate) { } UIntVectorObject* UIntVectorObject::newThisType() { return (UIntVectorObject*)_newVector(); } // ---------------------------- DoubleVectorObject::DoubleVectorObject(VTable* ivtable, ScriptObject* delegate) : TypedVectorObject< DataList >(ivtable, delegate) { } DoubleVectorObject* DoubleVectorObject::newThisType() { return (DoubleVectorObject*)_newVector(); } // ---------------------------- #ifdef VMCFG_FLOAT FloatVectorObject::FloatVectorObject(VTable* ivtable, ScriptObject* delegate) : TypedVectorObject< DataList >(ivtable, delegate) { } FloatVectorObject* FloatVectorObject::newThisType() { return (FloatVectorObject*)_newVector(); } Float4VectorObject::Float4VectorObject(VTable* ivtable, ScriptObject* delegate) : TypedVectorObject< DataList >(ivtable, delegate) { } Float4VectorObject* Float4VectorObject::newThisType() { return (Float4VectorObject*)_newVector(); } #endif // VMCFG_FLOAT // ---------------------------- ObjectVectorObject::ObjectVectorObject(VTable* ivtable, ScriptObject* delegate) : TypedVectorObject< AtomList >(ivtable, delegate) { } ObjectVectorObject* ObjectVectorObject::newThisType() { return (ObjectVectorObject*)_newVector(); } }