840 lines
20 KiB
C++
840 lines
20 KiB
C++
/* ***** 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) 1993-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"
|
|
|
|
namespace avmplus
|
|
{
|
|
#ifdef DEBUGGER
|
|
class SlotIterator : public ScriptObject
|
|
{
|
|
public:
|
|
SlotIterator(ScriptObject *obj, VTable *vtable)
|
|
: obj(obj),
|
|
ScriptObject(vtable, vtable->toplevel->objectClass->prototype) {}
|
|
|
|
int nextNameIndex(int index)
|
|
{
|
|
if(index == 0)
|
|
currTraits = obj->traits();
|
|
|
|
while (currTraits != NULL)
|
|
{
|
|
while ((index = currTraits->next(index)) != 0)
|
|
{
|
|
return index;
|
|
}
|
|
|
|
currTraits = currTraits->base;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
Atom nextValue(int index)
|
|
{
|
|
Multiname mn(currTraits->nsAt(index), currTraits->keyAt(index), true);
|
|
QNameObject *qname = new (gc(), toplevel()->qnameClass()->ivtable()->getExtraSize())
|
|
QNameObject(toplevel()->qnameClass(), mn);
|
|
|
|
return qname->atom();
|
|
}
|
|
|
|
private:
|
|
DRCWB(ScriptObject *) obj;
|
|
DWB(Traits *) currTraits;
|
|
};
|
|
#endif
|
|
|
|
ScriptObject::ScriptObject(VTable *vtable,
|
|
ScriptObject *delegate,
|
|
int capacity /*=Hashtable::kDefaultCapacity*/)
|
|
:
|
|
#ifdef DEBUGGER
|
|
AvmPlusScriptableObject((Atom)vtable),
|
|
#endif // DEBUGGER
|
|
vtable(vtable)
|
|
{
|
|
// initialize slots in this object to initial values from traits.
|
|
Traits* traits = vtable->traits;
|
|
AvmAssert(traits->linked);
|
|
|
|
// Ensure that our object is large enough to hold its extra traits data.
|
|
AvmAssert(MMgc::GC::Size(this) >= traits->getTotalSize());
|
|
|
|
setDelegate(delegate);
|
|
|
|
if (traits->needsHashtable)
|
|
{
|
|
MMGC_MEM_TYPE(this);
|
|
getTable()->initialize(this->gc(), capacity);
|
|
getTable()->setDontEnumSupport(true);
|
|
}
|
|
}
|
|
|
|
ScriptObject::~ScriptObject()
|
|
{
|
|
#ifdef MMGC_DRC
|
|
setDelegate(NULL);
|
|
vtable->traits->destroyInstance(this);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* traverse the delegate chain looking for a value.
|
|
* [ed] it's okay to look only at the HT's in the delegate chain because
|
|
* delegate values may only be instances of Object. They cannot be objects
|
|
* with slots. We don't need to look at traits at each step.
|
|
* todo - enforce this rule
|
|
* @param name
|
|
* @return
|
|
*/
|
|
Atom ScriptObject::getAtomProperty(Atom name) const
|
|
{
|
|
if (!traits()->needsHashtable)
|
|
{
|
|
return getAtomPropertyFromProtoChain(name, delegate, traits());
|
|
}
|
|
else
|
|
{
|
|
Stringp s = core()->atomToString(name);
|
|
AvmAssert(s->isInterned());
|
|
Atom ival = s->getIntAtom();
|
|
if (ival)
|
|
{
|
|
name = ival;
|
|
}
|
|
|
|
// dynamic lookup on this object
|
|
const ScriptObject *o = this;
|
|
do
|
|
{
|
|
Hashtable *table = o->getTable();
|
|
const Atom *atoms = table->getAtoms();
|
|
int i = table->find(name, atoms, table->getNumAtoms());
|
|
if (atoms[i] != Hashtable::EMPTY)
|
|
return atoms[i+1];
|
|
}
|
|
while ((o = o->delegate) != NULL);
|
|
return undefinedAtom;
|
|
}
|
|
}
|
|
|
|
Atom ScriptObject::getAtomPropertyFromProtoChain(Atom name, ScriptObject* o, Traits *origObjTraits) const
|
|
{
|
|
// todo will delegate always be non-null here?
|
|
if (o != NULL)
|
|
{
|
|
Atom searchname = name;
|
|
Stringp s = core()->atomToString(name);
|
|
AvmAssert(s->isInterned());
|
|
Atom ival = s->getIntAtom();
|
|
if (ival)
|
|
{
|
|
searchname = ival;
|
|
}
|
|
do
|
|
{
|
|
Atom *atoms = o->getTable()->getAtoms();
|
|
int i = o->getTable()->find(searchname, atoms, o->getTable()->getNumAtoms());
|
|
if (atoms[i] != Hashtable::EMPTY)
|
|
return atoms[i+1];
|
|
}
|
|
while ((o = o->delegate) != NULL);
|
|
}
|
|
Multiname multiname(core()->publicNamespace,AvmCore::atomToString(name));
|
|
toplevel()->throwReferenceError(kReadSealedError, &multiname, origObjTraits);
|
|
// unreached
|
|
return undefinedAtom;
|
|
}
|
|
|
|
bool ScriptObject::hasMultinameProperty(Multiname* multiname) const
|
|
{
|
|
if (traits()->needsHashtable)
|
|
{
|
|
if (isValidDynamicName(multiname))
|
|
{
|
|
return hasAtomProperty(multiname->getName()->atom());
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// ISSUE should this walk the proto chain?
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool ScriptObject::hasAtomProperty(Atom name) const
|
|
{
|
|
if (traits()->needsHashtable)
|
|
{
|
|
Stringp s = core()->atomToString(name);
|
|
AvmAssert(s->isInterned());
|
|
Atom ival = s->getIntAtom();
|
|
if (ival)
|
|
{
|
|
name = ival;
|
|
}
|
|
|
|
return getTable()->contains(name);
|
|
}
|
|
else
|
|
{
|
|
// ISSUE should this walk the proto chain?
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void ScriptObject::setAtomProperty(Atom name, Atom value)
|
|
{
|
|
if (traits()->needsHashtable)
|
|
{
|
|
Stringp s = core()->atomToString(name);
|
|
AvmAssert(s->isInterned());
|
|
Atom ival = s->getIntAtom();
|
|
if (ival)
|
|
{
|
|
name = ival;
|
|
}
|
|
|
|
MMGC_MEM_TYPE(this);
|
|
getTable()->add (name, value);
|
|
MMGC_MEM_TYPE(NULL);
|
|
}
|
|
else
|
|
{
|
|
Multiname multiname(core()->publicNamespace, AvmCore::atomToString(name));
|
|
// cannot create properties on a sealed object.
|
|
toplevel()->throwReferenceError(kWriteSealedError, &multiname, traits());
|
|
}
|
|
}
|
|
|
|
void ScriptObject::setMultinameProperty(Multiname* name, Atom value)
|
|
{
|
|
if (traits()->needsHashtable && isValidDynamicName(name))
|
|
{
|
|
setStringProperty(name->getName(), value);
|
|
}
|
|
else
|
|
{
|
|
// cannot create properties on a sealed object.
|
|
toplevel()->throwReferenceError(kWriteSealedError, name, traits());
|
|
}
|
|
}
|
|
|
|
bool ScriptObject::getAtomPropertyIsEnumerable(Atom name) const
|
|
{
|
|
if (traits()->needsHashtable)
|
|
{
|
|
Stringp s = core()->atomToString(name);
|
|
AvmAssert(s->isInterned());
|
|
Atom ival = s->getIntAtom();
|
|
if (ival)
|
|
{
|
|
name = ival;
|
|
}
|
|
|
|
return getTable()->getAtomPropertyIsEnumerable(name);
|
|
}
|
|
else
|
|
{
|
|
// ISSUE should this walk the proto chain?
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void ScriptObject::setAtomPropertyIsEnumerable(Atom name, bool enumerable)
|
|
{
|
|
if (traits()->needsHashtable)
|
|
{
|
|
Stringp s = core()->atomToString(name);
|
|
AvmAssert(s->isInterned());
|
|
Atom ival = s->getIntAtom();
|
|
if (ival)
|
|
{
|
|
name = ival;
|
|
}
|
|
|
|
getTable()->setAtomPropertyIsEnumerable(name, enumerable);
|
|
}
|
|
else
|
|
{
|
|
// cannot create properties on a sealed object.
|
|
Multiname multiname(core()->publicNamespace, AvmCore::atomToString(name));
|
|
toplevel()->throwReferenceError(kWriteSealedError, &multiname, traits());
|
|
}
|
|
}
|
|
|
|
bool ScriptObject::deleteAtomProperty(Atom name)
|
|
{
|
|
if (traits()->needsHashtable)
|
|
{
|
|
Stringp s = core()->atomToString(name);
|
|
AvmAssert(s->isInterned());
|
|
Atom ival = s->getIntAtom();
|
|
if (ival)
|
|
{
|
|
name = ival;
|
|
}
|
|
|
|
getTable()->remove(name);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
bool ScriptObject::deleteMultinameProperty(Multiname* name)
|
|
{
|
|
if (traits()->needsHashtable && isValidDynamicName(name))
|
|
{
|
|
return deleteStringProperty(name->getName());
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Atom ScriptObject::getUintProperty(uint32 i) const
|
|
{
|
|
AvmCore* core = this->core();
|
|
|
|
if (!(i&MAX_INTEGER_MASK))
|
|
{
|
|
if (!traits()->needsHashtable)
|
|
{
|
|
Atom name = core->internUint32(i)->atom();
|
|
return getAtomPropertyFromProtoChain(name, delegate, traits());
|
|
}
|
|
else
|
|
{
|
|
// dynamic lookup on this object
|
|
Atom name = core->uintToAtom (i);
|
|
const ScriptObject *o = this;
|
|
do
|
|
{
|
|
Hashtable *table = o->getTable();
|
|
const Atom *atoms = table->getAtoms();
|
|
int i = table->find(name, atoms, table->getNumAtoms());
|
|
if (atoms[i] != Hashtable::EMPTY)
|
|
return atoms[i+1];
|
|
}
|
|
while ((o = o->delegate) != NULL);
|
|
return undefinedAtom;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return getAtomProperty(core->internUint32(i)->atom());
|
|
}
|
|
}
|
|
|
|
void ScriptObject::setUintProperty(uint32 i, Atom value)
|
|
{
|
|
AvmCore* core = this->core();
|
|
if (!(i&MAX_INTEGER_MASK))
|
|
{
|
|
Atom name = core->uintToAtom (i);
|
|
if (traits()->needsHashtable)
|
|
{
|
|
MMGC_MEM_TYPE(this);
|
|
getTable()->add(name, value);
|
|
MMGC_MEM_TYPE(NULL);
|
|
}
|
|
else
|
|
{
|
|
Multiname multiname(core->publicNamespace, core->internUint32(i));
|
|
// cannot create properties on a sealed object.
|
|
toplevel()->throwReferenceError(kWriteSealedError, &multiname, traits());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
setAtomProperty(core->internUint32(i)->atom(), value);
|
|
}
|
|
}
|
|
|
|
bool ScriptObject::delUintProperty(uint32 i)
|
|
{
|
|
AvmCore* core = this->core();
|
|
if (!(i&MAX_INTEGER_MASK))
|
|
{
|
|
Atom name = core->uintToAtom (i);
|
|
if (traits()->needsHashtable)
|
|
{
|
|
getTable()->remove(name);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return deleteAtomProperty(core->internUint32(i)->atom());
|
|
}
|
|
}
|
|
|
|
bool ScriptObject::hasUintProperty(uint32 i) const
|
|
{
|
|
AvmCore* core = this->core();
|
|
if (!(i&MAX_INTEGER_MASK))
|
|
{
|
|
Atom name = core->uintToAtom (i);
|
|
if (traits()->needsHashtable)
|
|
{
|
|
return getTable()->contains(name);
|
|
}
|
|
else
|
|
{
|
|
// ISSUE should this walk the proto chain?
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return hasAtomProperty(core->internUint32(i)->atom());
|
|
}
|
|
}
|
|
|
|
Atom ScriptObject::getMultinameProperty(Multiname* multiname) const
|
|
{
|
|
if (isValidDynamicName(multiname))
|
|
{
|
|
return getStringProperty(multiname->getName());
|
|
}
|
|
else
|
|
{
|
|
Toplevel* toplevel = this->toplevel();
|
|
|
|
if (multiname->isNsset())
|
|
toplevel->throwReferenceError(kReadSealedErrorNs, multiname, traits());
|
|
else
|
|
toplevel->throwReferenceError(kReadSealedError, multiname, traits());
|
|
return undefinedAtom;
|
|
}
|
|
}
|
|
|
|
// this = argv[0] (ignored)
|
|
// arg1 = argv[1]
|
|
// argN = argv[argc]
|
|
Atom ScriptObject::callProperty(Multiname* multiname, int argc, Atom* argv)
|
|
{
|
|
Toplevel* toplevel = this->toplevel();
|
|
Atom method = getMultinameProperty(multiname);
|
|
if (!AvmCore::isObject(method))
|
|
toplevel->throwTypeError(kCallOfNonFunctionError, core()->toErrorString(multiname));
|
|
argv[0] = atom(); // replace receiver
|
|
return toplevel->op_call(method, argc, argv);
|
|
}
|
|
|
|
Atom ScriptObject::constructProperty(Multiname* multiname, int argc, Atom* argv)
|
|
{
|
|
Atom ctor = getMultinameProperty(multiname);
|
|
argv[0] = atom(); // replace receiver
|
|
return toplevel()->op_construct(ctor, argc, argv);
|
|
}
|
|
|
|
Atom ScriptObject::getDescendants(Multiname* /*name*/) const
|
|
{
|
|
toplevel()->throwTypeError(kDescendentsError, core()->toErrorString(traits()));
|
|
return undefinedAtom;// not reached
|
|
}
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
Stringp ScriptObject::format(AvmCore* core) const
|
|
{
|
|
if (traits()->name != NULL) {
|
|
return core->concatStrings(traits()->format(core),
|
|
core->concatStrings(core->newString("@"),
|
|
core->formatAtomPtr(atom())));
|
|
} else {
|
|
return core->concatStrings(core->newString("{}@"),
|
|
core->formatAtomPtr(atom()));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Atom ScriptObject::defaultValue()
|
|
{
|
|
AvmCore *core = this->core();
|
|
Toplevel* toplevel = this->toplevel();
|
|
|
|
Atom atomv_out[1];
|
|
|
|
// call this.valueOf()
|
|
Multiname tempname(core->publicNamespace, core->kvalueOf);
|
|
atomv_out[0] = atom();
|
|
Atom result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable);
|
|
|
|
// if result is primitive, return it
|
|
if ((result&7) != kObjectType)
|
|
return result;
|
|
|
|
// otherwise call this.toString()
|
|
tempname.setName(core->ktoString);
|
|
atomv_out[0] = atom();
|
|
result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable);
|
|
|
|
// if result is primitive, return it
|
|
if ((result&7) != kObjectType)
|
|
return result;
|
|
|
|
// could not convert to primitive.
|
|
toplevel->throwTypeError(kConvertToPrimitiveError, core->toErrorString(traits()));
|
|
return undefinedAtom;
|
|
}
|
|
|
|
// Execute the ToString algorithm as described in ECMA-262 Section 9.8.
|
|
// This is ToString(ToPrimitive(input argument, hint String))
|
|
// ToPrimitive(input argument, hint String) calls [[DefaultValue]]
|
|
// described in ECMA-262 8.6.2.6. The [[DefaultValue]] algorithm
|
|
// with hint String is inlined here.
|
|
Atom ScriptObject::toString()
|
|
{
|
|
AvmCore *core = this->core();
|
|
Toplevel* toplevel = this->toplevel();
|
|
|
|
Atom atomv_out[1];
|
|
|
|
// call this.toString()
|
|
Multiname tempname(core->publicNamespace, core->ktoString);
|
|
atomv_out[0] = atom();
|
|
Atom result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable);
|
|
|
|
// if result is primitive, return its ToString
|
|
if ((result&7) != kObjectType)
|
|
return core->string(result)->atom();
|
|
|
|
// otherwise call this.valueOf()
|
|
tempname.setName(core->kvalueOf);
|
|
atomv_out[0] = atom();
|
|
result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable);
|
|
|
|
// if result is primitive, return it
|
|
if ((result&7) != kObjectType)
|
|
return core->string(result)->atom();
|
|
|
|
// could not convert to primitive.
|
|
toplevel->throwTypeError(kConvertToPrimitiveError, core->toErrorString(traits()));
|
|
return undefinedAtom;
|
|
}
|
|
|
|
// this = argv[0] (ignored)
|
|
// arg1 = argv[1]
|
|
// argN = argv[argc]
|
|
Atom ScriptObject::call(int /*argc*/, Atom* /*argv*/)
|
|
{
|
|
// TypeError in ECMA to execute a non-function
|
|
Multiname name(core()->publicNamespace, core()->constantString("value"));
|
|
toplevel()->throwTypeError(kCallOfNonFunctionError, core()->toErrorString(&name));
|
|
return undefinedAtom;
|
|
}
|
|
|
|
// this = argv[0] (ignored)
|
|
// arg1 = argv[1]
|
|
// argN = argv[argc]
|
|
Atom ScriptObject::construct(int /*argc*/, Atom* /*argv*/)
|
|
{
|
|
// TypeError in ECMA to execute a non-function
|
|
toplevel()->throwTypeError(kConstructOfNonFunctionError);
|
|
return undefinedAtom;
|
|
}
|
|
|
|
Atom ScriptObject::getSlotAtom(int slot)
|
|
{
|
|
Traits* traits = this->traits();
|
|
AvmCore* core = traits->core;
|
|
Traits* t = traits->getSlotTraits(slot);
|
|
AvmAssert(t != VOID_TYPE);
|
|
void* p = ((char*)this) + traits->getOffsets()[slot];
|
|
if (!t || t == OBJECT_TYPE)
|
|
{
|
|
return *((Atom*)p);
|
|
}
|
|
else if (t == NUMBER_TYPE)
|
|
{
|
|
return core->doubleToAtom(*((double*)p));
|
|
}
|
|
else if (t == INT_TYPE)
|
|
{
|
|
return core->intToAtom(*((int*)p));
|
|
}
|
|
else if (t == UINT_TYPE)
|
|
{
|
|
return core->uintToAtom(*((uint32*)p));
|
|
}
|
|
else if (t == BOOLEAN_TYPE)
|
|
{
|
|
return (*((int*)p)<<3)|kBooleanType;
|
|
}
|
|
else if (t == STRING_TYPE)
|
|
{
|
|
Stringp s = *((Stringp*)p);
|
|
return s->atom(); // may be null|kStringType, that's ok
|
|
}
|
|
else if (t == NAMESPACE_TYPE)
|
|
{
|
|
Namespace* ns = *((Namespace**)p);
|
|
return ns->atom(); // may be null|kNamespaceType, no problemo
|
|
}
|
|
else
|
|
{
|
|
ScriptObject* o = *((ScriptObject**)p);
|
|
return o->atom(); // may be null|kObjectType, copasthetic
|
|
}
|
|
}
|
|
|
|
void ScriptObject::setSlotAtom(int slot, Atom value)
|
|
{
|
|
Traits* traits = this->traits();
|
|
AvmCore* core = traits->core;
|
|
Traits* t = traits->getSlotTraits(slot);
|
|
AvmAssert(t != VOID_TYPE);
|
|
Atom *p = (Atom*)(((char*)this) + traits->getOffsets()[slot]);
|
|
if (!t || t == OBJECT_TYPE)
|
|
{
|
|
//*((Atom*)p) = value;
|
|
WBATOM(core->GetGC(), this, p, value);
|
|
}
|
|
else if (t == NUMBER_TYPE)
|
|
{
|
|
*((double*)p) = AvmCore::number_d(value);
|
|
}
|
|
else if (t == INT_TYPE)
|
|
{
|
|
*((int*)p) = AvmCore::integer_i(value);
|
|
}
|
|
else if (t == UINT_TYPE)
|
|
{
|
|
*((uint32*)p) = AvmCore::integer_u(value);
|
|
}
|
|
else if (t == BOOLEAN_TYPE)
|
|
{
|
|
*((int*)p) = urshift(value,3);
|
|
}
|
|
else
|
|
{
|
|
//*((int32*)p) = value & ~7;
|
|
WBRC(core->GetGC(), this, p, value & ~7);
|
|
}
|
|
}
|
|
|
|
Atom ScriptObject::nextName(int index)
|
|
{
|
|
AvmAssert(traits()->needsHashtable);
|
|
AvmAssert(index > 0);
|
|
|
|
Hashtable *ht = getTable();
|
|
if (index-1 >= ht->getNumAtoms()/2)
|
|
return nullStringAtom;
|
|
Atom m = ht->getAtoms()[(index-1)<<1] & ~ht->dontEnumMask();
|
|
if (AvmCore::isNullOrUndefined(m))
|
|
return nullStringAtom;
|
|
return m;
|
|
}
|
|
|
|
Atom ScriptObject::nextValue(int index)
|
|
{
|
|
AvmAssert(traits()->needsHashtable);
|
|
AvmAssert(index > 0);
|
|
|
|
Hashtable *ht = getTable();
|
|
if (index-1 >= ht->getNumAtoms()/2)
|
|
return undefinedAtom;
|
|
Atom m = ht->getAtoms()[(index-1)<<1] & ~ht->dontEnumMask();
|
|
if (AvmCore::isNullOrUndefined(m))
|
|
return nullStringAtom;
|
|
return getTable()->getAtoms()[((index-1)<<1)+1];
|
|
}
|
|
|
|
int ScriptObject::nextNameIndex(int index)
|
|
{
|
|
AvmAssert(index >= 0);
|
|
|
|
if (!traits()->needsHashtable)
|
|
return 0;
|
|
|
|
// todo clean this up.
|
|
if (index != 0) {
|
|
index = index<<1;
|
|
}
|
|
// Advance to first non-empty slot.
|
|
Hashtable* table = getTable();
|
|
int numAtoms = table->getNumAtoms();
|
|
int dontEnumMask = table->dontEnumMask();
|
|
while (index < numAtoms) {
|
|
Atom m = table->getAtoms()[index];
|
|
if (m != 0 && m != undefinedAtom && (m & dontEnumMask) != Hashtable::kDontEnumBit)
|
|
return (index>>1)+1;
|
|
index += 2;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* the default implementation calls the delegate (should be the base class objects)
|
|
* so that derived classes delegate object allocation to the base class. If you
|
|
* are overriding createInstance and your base class is Object, unconditionally allocate
|
|
* the instance object using the given vtable and prototype.
|
|
*/
|
|
ScriptObject* ScriptObject::createInstance(VTable* ivtable, ScriptObject* prototype)
|
|
{
|
|
if (ivtable->base)
|
|
{
|
|
ScopeChain *scope = vtable->scope;
|
|
if (scope->getSize())
|
|
{
|
|
Atom baseAtom = scope->getScope(scope->getSize()-1);
|
|
if (!AvmCore::isObject(baseAtom))
|
|
toplevel()->throwVerifyError(kCorruptABCError);
|
|
|
|
ScriptObject *base = AvmCore::atomToScriptObject(baseAtom);
|
|
// make sure scope object is base type's class object
|
|
AvmAssert(base->traits()->itraits == this->traits()->itraits->base);
|
|
return base->createInstance(ivtable, prototype);
|
|
}
|
|
}
|
|
|
|
return core()->newObject(ivtable, prototype);
|
|
}
|
|
|
|
|
|
/**
|
|
* Function.prototype.call()
|
|
*/
|
|
Atom ScriptObject::function_call(Atom thisArg,
|
|
Atom *argv,
|
|
int argc)
|
|
{
|
|
if (argc > 0)
|
|
{
|
|
// argc = # of AS3 arguments
|
|
AvmAssert(argc >= 0);
|
|
Atom *newargs = (Atom *) alloca(sizeof(Atom)*(argc+1));
|
|
newargs[0] = thisArg;
|
|
memcpy(&newargs[1], argv, argc*sizeof(Atom));
|
|
return call(argc, newargs);
|
|
}
|
|
else
|
|
{
|
|
// AS3 caller didn't supply any args
|
|
Atom newargs[1] = { thisArg };
|
|
return call(0, newargs);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Function.prototype.apply()
|
|
*/
|
|
Atom ScriptObject::function_apply(Atom thisArg, Atom argArray)
|
|
{
|
|
// when argArray == undefined or null, same as not being there at all
|
|
// see Function/e15_3_4_3_1.as
|
|
|
|
if (!AvmCore::isNullOrUndefined(argArray))
|
|
{
|
|
AvmCore* core = this->core();
|
|
|
|
if (!core->istype(argArray, ARRAY_TYPE))
|
|
toplevel()->throwTypeError(kApplyError);
|
|
|
|
ArrayObject *a = (ArrayObject*)AvmCore::atomToScriptObject(argArray);
|
|
|
|
int count = a->getLength();
|
|
Atom* newargs = (Atom*) alloca(sizeof(Atom)*(1+count));
|
|
newargs[0] = thisArg;
|
|
for (int i=0; i<count; i++) {
|
|
newargs[i+1] = a->getUintProperty(i);
|
|
}
|
|
return call(count, newargs);
|
|
}
|
|
else
|
|
{
|
|
Atom newargs[1] = { thisArg };
|
|
return call(0, newargs);
|
|
}
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
ScriptObject* ScriptObject::getSlotIterator()
|
|
{
|
|
VTable *vt = toplevel()->objectClass->ivtable();
|
|
return new (gc(), vt->getExtraSize()) SlotIterator(this, vt);
|
|
}
|
|
|
|
Stringp ScriptObject::getTypeName() const
|
|
{
|
|
Stringp result;
|
|
Namespace* ns = traits()->ns;
|
|
Stringp name = traits()->name;
|
|
|
|
if ((ns != NULL) && (name != NULL))
|
|
{
|
|
result = Multiname::format(core(), ns, name);
|
|
}
|
|
else
|
|
{
|
|
result = name;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
uint32 ScriptObject::size() const
|
|
{
|
|
uint32 size = traits()->getTotalSize();
|
|
if(traits()->needsHashtable)
|
|
{
|
|
Hashtable *ht = getTable();
|
|
size += ht->getSize() * 2 * sizeof(Atom);
|
|
}
|
|
size -= sizeof(AvmPlusScriptableObject);
|
|
return size;
|
|
}
|
|
#endif
|
|
}
|