Files
avmplus/core/VectorClass.cpp
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

444 lines
17 KiB
C++

/* -*- 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<IntVectorObject>;
template class TypedVectorClass<UIntVectorObject>;
template class TypedVectorClass<DoubleVectorObject>;
#ifdef VMCFG_FLOAT
template class TypedVectorClass<FloatVectorObject>;
template class TypedVectorClass<Float4VectorObject>;
#endif
template class TypedVectorClass<ObjectVectorObject>;
template class TypedVectorObject< DataList<int32_t> >;
template class TypedVectorObject< DataList<uint32_t> >;
template class TypedVectorObject< DataList<double> >;
#ifdef VMCFG_FLOAT
template class TypedVectorObject< DataList<float> >;
template class TypedVectorObject< DataList<float4_t, 16> >;
#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<IntVectorObject>(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<UIntVectorObject>(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<DoubleVectorObject>(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<FloatVectorObject>(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<Float4VectorObject>(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<ObjectVectorObject>(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<int32_t> >(ivtable, delegate)
{
}
IntVectorObject* IntVectorObject::newThisType()
{
return (IntVectorObject*)_newVector();
}
// ----------------------------
UIntVectorObject::UIntVectorObject(VTable* ivtable, ScriptObject* delegate)
: TypedVectorObject< DataList<uint32_t> >(ivtable, delegate)
{
}
UIntVectorObject* UIntVectorObject::newThisType()
{
return (UIntVectorObject*)_newVector();
}
// ----------------------------
DoubleVectorObject::DoubleVectorObject(VTable* ivtable, ScriptObject* delegate)
: TypedVectorObject< DataList<double> >(ivtable, delegate)
{
}
DoubleVectorObject* DoubleVectorObject::newThisType()
{
return (DoubleVectorObject*)_newVector();
}
// ----------------------------
#ifdef VMCFG_FLOAT
FloatVectorObject::FloatVectorObject(VTable* ivtable, ScriptObject* delegate)
: TypedVectorObject< DataList<float> >(ivtable, delegate)
{
}
FloatVectorObject* FloatVectorObject::newThisType()
{
return (FloatVectorObject*)_newVector();
}
Float4VectorObject::Float4VectorObject(VTable* ivtable, ScriptObject* delegate)
: TypedVectorObject< DataList<float4_t, 16> >(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();
}
}