Files
avmplus/core/ScriptObject.cpp
T
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

941 lines
30 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"
//#define DOPROF
//#include "../vprof/vprof.h"
namespace avmplus
{
ScriptObject::ScriptObject(VTable* _vtable, ScriptObject* _delegate) :
#ifdef DEBUGGER
AvmPlusScriptableObject(sotObject(_vtable)),
#endif // DEBUGGER
vtable(_vtable),
// note that it's substantially more efficient to initialize this in the ctor
// list vs. a later explicit call to setDelegate, as we don't have to check for marking
// nor decrement an existing value...
delegate(_delegate)
{
AvmAssert(vtable->traits->isResolved());
// Ensure that our object is large enough to hold its extra traits data.
AvmAssert(MMgc::GC::Size(this) >= vtable->traits->getTotalSize());
}
ScriptObject::ScriptObject(VTable* _vtable, ScriptObject* _delegate, int capacity) :
#ifdef DEBUGGER
AvmPlusScriptableObject(sotObject(_vtable)),
#endif // DEBUGGER
vtable(_vtable),
// note that it's substantially more efficient to initialize this in the ctor
// list vs. a later explicit call to setDelegate, as we don't have to check for marking
// nor decrement an existing value...
delegate(_delegate)
{
AvmAssert(vtable->traits->isResolved());
// Ensure that our object is large enough to hold its extra traits data.
AvmAssert(MMgc::GC::Size(this) >= vtable->traits->getTotalSize());
//if capacity not specified then initialize the hashtable lazily
if (vtable->traits->needsHashtable() && capacity)
{
initHashtable(capacity);
}
}
// Not actually entirely trivial, as it decrements reference counts
// manually, not just by GCMember smart pointer destructors.
#ifdef DRC_TRIVIAL_DESTRUCTOR
ScriptObject::~ScriptObject()
{
//setDelegate(NULL); -- no longer necessary
vtable->traits->destroyInstance(this);
}
#endif
/* The storage for a ScriptObject or a subclass SO of ScriptObject is
* laid out as follows.
*
* - first the bits of the C++ class; if there are pointers here
* then they must be traced explicitly by the appropriate class's
* gcTrace method
* - then the bits for the ActionScript slots
* - optionally an InlineHashtable for dynamic properties
*
* The in-line slots are native and their representation is described
* by the Traits object (vtable->traits). They are not named, but named
* lookup is possible by going to the Traits object, which contains
* a name->slot map.
*
* The InlineHashtable always stores Atoms.
*/
bool ScriptObject::gcTrace(MMgc::GC* gc, size_t cursor)
{
(void)cursor;
gc->TraceLocation(&vtable);
gc->TraceLocation(&delegate);
traits()->traceSlots(gc, this);
if (traits()->needsHashtable())
{
// Avoid initializing the hash table here
InlineHashtable* iht = getTableNoInit();
// The iht pointer can be NULL for Dictionary objects that
// haven't had init called yet.
if(iht)
iht->gcTrace(gc);
}
return false;
}
void ScriptObject::initHashtable(int capacity /*=InlineHashtable::kDefaultCapacity*/)
{
AvmAssert(vtable->traits->isDictionary() == 0); //should not be called DictionaryObject uses HeapHashtable
MMGC_MEM_TYPE(this);
union {
uint8_t* p;
InlineHashtable* iht;
};
p = (uint8_t*)this + vtable->traits->getHashtableOffset();
iht->initializeWithDontEnumSupport(this->gc(), capacity);
#ifdef DEBUGGER
if (getSampler())
{
getSampler()->addDependentObject(this, iht);
}
#endif
}
InlineHashtable* ScriptObject::getTable() const
{
AvmAssert(vtable->traits->getHashtableOffset() != 0);
union {
uint8_t* p;
InlineHashtable* iht;
HeapHashtable** hht;
};
p = (uint8_t*)this + vtable->traits->getHashtableOffset();
if(!vtable->traits->isDictionary())
{
if (iht->needsInitialize())
const_cast<ScriptObject*>(this)->initHashtable();
return iht;
}
else
{
//DictionaryObjects store pointer to HeapHashtable at
//the hashtable offset
if (*hht == NULL)
{
// ugh... if we are here, it means that a Dictionary subclass didn't
// call the Dictionary ctor via super(), so our HeapHashtable was never created.
// This is a horrible band-aid: lazily create it here. (Since the
// ctor was never called, we'll just assume weakKeys = false.) This is
// a horrible encapsulation violation, but a proper fix requires a fairly
// sweeping rethinking of ScriptObject, so this will have to do short-term.
MMgc::GC* gc = this->gc();
// if we are lazy-initing here, it means our ctor was never called:
// so, just assume weakKeys = false.
HeapHashtable* ht = HeapHashtable::create(gc);
WB(gc, this, hht, ht);
#ifdef DEBUGGER
if (getSampler() && ht)
{
getSampler()->addDependentObject(this, ht->get_ht());
}
#endif
}
InlineHashtable *ihp = (*hht)->get_ht();
AvmAssertMsg(ihp != NULL, "Illegal to call getTable before Dictionary init is called");
return ihp;
}
}
bool ScriptObject::isOwnAtomPropertyHere(Atom name, Atom *recv) const
{
AvmAssert(this->vtable->traits->getHashtableOffset() != 0);
Atom const value = this->getTable()->getNonEmpty(name);
if (!InlineHashtable::isEmpty(value))
{
*recv = value;
return true;
}
else
{
return false;
}
}
/**
* 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
{
// ensure prototype is dynamic
if (!o->vtable->traits->getHashtableOffset())
continue;
Atom value;
if (o->isOwnAtomPropertyHere(name, &value))
return value;
}
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
{
// ensure prototype is dynamic
if (!o->vtable->traits->getHashtableOffset())
continue;
Atom value;
if (o->isOwnAtomPropertyHere(searchname, &value))
return value;
}
while ((o = o->delegate) != NULL);
}
// NOTE use default public since name is not used
Multiname multiname(core()->getAnyPublicNamespace(), AvmCore::atomToString(name));
toplevel()->throwReferenceError(kReadSealedError, &multiname, origObjTraits);
// unreached
return undefinedAtom;
}
bool ScriptObject::hasMultinameProperty(const Multiname* multiname) const
{
if (traits()->needsHashtable() && multiname->isValidDynamicName())
{
return hasAtomProperty(multiname->getName()->atom());
}
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::throwWriteSealedError(const Multiname& name)
{
toplevel()->throwReferenceError(kWriteSealedError, name, traits());
}
void ScriptObject::throwWriteSealedError(Atom name)
{
AvmCore* core = this->core();
throwWriteSealedError(Multiname(core->getAnyPublicNamespace(), core->intern(name)));
}
void ScriptObject::throwCantInstantiateError()
{
Multiname qname(traits()->ns(), traits()->name());
toplevel()->argumentErrorClass()->throwError(kCantInstantiateError, core()->toErrorString(&qname));
}
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
{
throwWriteSealedError(name);
}
}
void ScriptObject::setMultinameProperty(const Multiname* name, Atom value)
{
if (traits()->needsHashtable() && name->isValidDynamicName())
{
setStringProperty(name->getName(), value);
}
else
{
throwWriteSealedError(*name);
}
}
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
{
throwWriteSealedError(name);
}
}
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(const Multiname* name)
{
if (traits()->needsHashtable() && name->isValidDynamicName())
{
return deleteStringProperty(name->getName());
}
else
{
return false;
}
}
Atom ScriptObject::getUintProperty(uint32_t i) const
{
// N.B.: a key present in ScriptObject must be interned string;
// thus uninterned implies absent (cf. bugzilla 556023).
AvmCore* core = this->core();
if (!(i&MAX_INTEGER_MASK))
{
if (!traits()->needsHashtable())
{
Stringp interned;
bool present = core->isInternedUint(i, &interned);
if (present)
{
Atom name = interned->atom();
return getAtomPropertyFromProtoChain(name, delegate,
traits());
}
else
{
return undefinedAtom;
}
}
else
{
// dynamic lookup on this object
Atom name = core->uintToAtom (i);
const ScriptObject *o = this;
do
{
// ensure prototype is dynamic
if (!o->vtable->traits->getHashtableOffset())
continue;
Atom value;
if (o->isOwnAtomPropertyHere(name, &value))
return value;
}
while ((o = o->delegate) != NULL);
return undefinedAtom;
}
}
else
{
Stringp interned;
bool present;
present = core->isInternedUint(i, &interned);
if (present)
{
return getAtomProperty(interned->atom());
}
else
{
return undefinedAtom;
}
}
}
void ScriptObject::setUintProperty(uint32_t 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
{
throwWriteSealedError(core->internUint32(i)->atom());
}
}
else
{
setAtomProperty(core->internUint32(i)->atom(), value);
}
}
bool ScriptObject::delUintProperty(uint32_t 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_t 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(const Multiname* multiname) const
{
if (multiname->isValidDynamicName())
{
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(const Multiname* multiname, int argc, Atom* argv)
{
Atom method = getMultinameProperty(multiname);
if (!AvmCore::isObject(method))
toplevel()->throwTypeError(kCallOfNonFunctionError, core()->toErrorString(multiname));
argv[0] = atom(); // replace receiver
return avmplus::op_call(toplevel(), method, argc, argv);
}
Atom ScriptObject::getDescendants(const Multiname* /*name*/) const
{
toplevel()->throwTypeError(kDescendentsError, core()->toErrorString(traits()));
return undefinedAtom;// not reached
}
bool ScriptObject::isGlobalObject() const
{
return traits()->posType() == TRAITSTYPE_SCRIPT;
}
#ifdef AVMPLUS_VERBOSE
PrintWriter& ScriptObject::print(PrintWriter& prw) const
{
(traits()->name() != NULL) ? prw << traits()
: prw << "{}";
return prw << "@" << asAtomHex(atom());
}
#endif
Atom ScriptObject::defaultValue()
{
AvmCore *core = this->core();
Toplevel* toplevel = this->toplevel();
Atom atomv_out[1];
// call this.valueOf()
// NOTE use callers versioned public to get correct valueOf
Multiname tempname(core->findPublicNamespace(), core->kvalueOf);
atomv_out[0] = atom();
Atom result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable);
// if result is primitive, return it
if (atomKind(result) != 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 (atomKind(result) != 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.
Stringp ScriptObject::toString()
{
AvmCore *core = this->core();
Toplevel* toplevel = this->toplevel();
Atom atomv_out[1];
// call this.toString()
// NOTE use callers versioned public to get correct toString
Multiname tempname(core->findPublicNamespace(), core->ktoString);
atomv_out[0] = atom();
Atom result = toplevel->callproperty(atom(), &tempname, 0, atomv_out, vtable);
// if result is primitive, return its ToString
if (atomKind(result) != kObjectType)
return core->string(result);
// 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 (atomKind(result) != kObjectType)
return core->string(result);
// could not convert to primitive.
toplevel->throwTypeError(kConvertToPrimitiveError, core->toErrorString(traits()));
return NULL; // unreachable
}
// 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
// NOTE use default public since name is not used
Multiname name(core()->getAnyPublicNamespace(), core()->kvalue);
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::applyTypeArgs(int /*argc*/, Atom* /*argv*/)
{
toplevel()->throwTypeError(kTypeAppOfNonParamType);
return undefinedAtom;
}
Atom ScriptObject::getSlotAtom(uint32_t slot, AvmCore *core)
{
Traits* traits = this->traits();
const TraitsBindingsp td = traits->getTraitsBindings();
// repeated if-else is actually more performant than a switch statement in this case.
// SST_atom is most common case, put it first
void* p;
const SlotStorageType sst = td->calcSlotAddrAndSST(slot, (void*)this, p);
if (sst == SST_atom)
{
return *((const Atom*)p);
}
else if (sst == SST_double)
{
return core->doubleToAtom(*((const double*)p));
}
#ifdef VMCFG_FLOAT
else if (sst == SST_float)
{
return traits->core->floatToAtom(*((const float*)p));
}
else if (sst == SST_float4)
{
return traits->core->float4ToAtom(AvmThunkUnbox_FLOAT4(float4_t, *((Atom*)p)) );
}
#endif // VMCFG_FLOAT
else if (sst == SST_int32)
{
return core->intToAtom(*((const int32_t*)p));
}
else if (sst == SST_uint32)
{
return core->uintToAtom(*((const int32_t*)p));
}
else if (sst == SST_bool32)
{
return (*((const int32_t*)p)<<3)|kBooleanType;
}
else if (sst == SST_string)
{
return (*((const Stringp*)p))->atom(); // may be null|kStringType, that's ok
}
else if (sst == SST_namespace)
{
return (*((const Namespacep*)p))->atom(); // may be null|kNamespaceType, no problemo
}
else // if (sst == SST_scriptobject)
{
AvmAssert(sst == SST_scriptobject);
return (*((const ScriptObject**)p))->atom(); // may be null|kObjectType, copacetic
}
}
Atom ScriptObject::getSlotAtom(uint32_t slot)
{
return getSlotAtom(slot, this->traits()->core);
}
ScriptObject* ScriptObject::getSlotObject(uint32_t slot)
{
Traits* traits = this->traits();
const TraitsBindingsp td = traits->getTraitsBindings();
void* p;
const SlotStorageType sst = td->calcSlotAddrAndSST(slot, (void*)this, p);
// based on profiling of Flex apps, it's *much* more common for the slot in this case
// to have a type (vs "atom"), so check for that first...
if (sst == SST_scriptobject)
{
return *((ScriptObject**)p);
}
else if (sst == SST_atom)
{
Atom const a = *((const Atom*)p);
// don't call AvmCore::isObject(); it checks for null, which we don't care about here
if (atomKind(a) == kObjectType)
return (ScriptObject*)atomPtr(a);
// else fall thru and return null
}
return NULL;
}
// note: coerceAndSetSlotAtom now includes a simplified and streamlined version
// of Toplevel::coerce. If you modify that code, you might need to modify this code.
void ScriptObject::coerceAndSetSlotAtom(uint32_t slot, Atom value)
{
Traits* traits = this->traits();
const TraitsBindingsp td = traits->getTraitsBindings();
void* p;
const SlotStorageType sst = td->calcSlotAddrAndSST(slot, (void*)this, p);
// repeated if-else is actually more performant than a switch statement in this case.
// SST_atom is most common case, put it first
if (sst == SST_atom)
{
// no call to coerce() needed, since anything will fit here... with one exception:
// BUILTIN_object needs to convert undefined->null (though BUILTIN_any does not).
// it's cheaper to do that here than call out to coerce().
AvmAssert(td->getSlotTraits(slot) == NULL || td->getSlotTraits(slot)->builtinType == BUILTIN_object);
if (value == undefinedAtom && td->getSlotTraits(slot) != NULL)
value = nullObjectAtom;
WBATOM(traits->core->GetGC(), this, (Atom*)p, value);
}
else if (sst == SST_double)
{
*((double*)p) = AvmCore::number(value);
}
#ifdef VMCFG_FLOAT
else if (sst == SST_float)
{
*((float*)p) = AvmCore::singlePrecisionFloat(value);
}
else if (sst == SST_float4)
{
float4_t* ptr = reinterpret_cast<float4_t*>(p);
AvmCore::float4(ptr, value);
}
#endif // VMCFG_FLOAT
else if (sst == SST_int32)
{
*((int32_t*)p) = AvmCore::integer(value);
}
else if (sst == SST_uint32)
{
*((uint32_t*)p) = AvmCore::toUInt32(value);
}
else if (sst == SST_bool32)
{
*((int32_t*)p) = AvmCore::boolean(value);
}
else
{
// null/undefined -> NULL for all of these
if (AvmCore::isNullOrUndefined(value))
{
value = (Atom)0; // don't bother setting tag bits
}
else if (sst == SST_string)
{
value = (Atom)traits->core->string(value); // don't bother setting tag bits
}
else if (sst == SST_namespace)
{
// Namespace is final, so we don't have to do the hard work
if (atomKind(value) != kNamespaceType)
goto failure;
}
else // if (sst == SST_scriptobject)
{
AvmAssert(sst == SST_scriptobject);
if (atomKind(value) != kObjectType || !AvmCore::atomToScriptObject(value)->traits()->subtypeof(td->getSlotTraits(slot)))
goto failure;
}
WBRC(traits->core->GetGC(), this, p, atomPtr(value));
}
return;
failure:
toplevel()->throwTypeError(kCheckTypeFailedError, traits->core->atomToErrorString(value), traits->core->toErrorString(td->getSlotTraits(slot)));
return;
}
Atom ScriptObject::nextName(int index)
{
if (!traits()->needsHashtable())
return nullStringAtom;
AvmAssert(index > 0);
InlineHashtable* ht = getTable();
Atom m = ht->keyAt(index);
return AvmCore::isNullOrUndefined(m) ? nullStringAtom : m;
}
Atom ScriptObject::nextValue(int index)
{
if (!traits()->needsHashtable())
return undefinedAtom;
AvmAssert(index > 0);
InlineHashtable* ht = getTable();
Atom m = ht->keyAt(index);
if (AvmCore::isNullOrUndefined(m))
return nullStringAtom;
return ht->valueAt(index);
}
int ScriptObject::nextNameIndex(int index)
{
AvmAssert(index >= 0);
if (!traits()->needsHashtable())
return 0;
return getTable()->next(index);
}
#ifdef DEBUGGER
uint64_t ScriptObject::bytesUsed() const
{
uint64_t bytesUsed = traits()->getTotalSize();
if(traits()->needsHashtable())
{
if (traits()->isDictionary())
{
union {
uint8_t* p;
HeapHashtable** hht;
};
p = (uint8_t*)this + traits()->getHashtableOffset();
bytesUsed += (*hht)->bytesUsed();
}
else
{
bytesUsed += getTable()->bytesUsed();
}
}
return bytesUsed;
}
void ScriptObject::registerDependentObjects(IMemorySampler *memSampler)
{
if (traits()->needsHashtable())
{
InlineHashtable* ht = getTableNoInit();
if (ht)
{
memSampler->addDependentObject(this, ht);
}
}
}
#endif
Stringp ScriptObject::implToString() const
{
AvmCore* core = this->core();
Traits* t = this->traits();
Stringp s = core->concatStrings(core->newConstantStringLatin1("[object "), t->name());
return core->concatStrings(s, core->newConstantStringLatin1("]"));
}
uint32_t ScriptObject::getLengthProperty()
{
Toplevel* toplevel = this->toplevel();
AvmCore* core = toplevel->core();
Multiname mname(core->getAnyPublicNamespace(), core->klength);
Atom lenAtm = toplevel->getproperty(this->atom(), &mname, this->vtable);
return AvmCore::toUInt32(lenAtm);
}
void ScriptObject::setLengthProperty(uint32_t newLen)
{
Toplevel* toplevel = this->toplevel();
AvmCore* core = toplevel->core();
Multiname mname(core->getAnyPublicNamespace(), core->klength);
Atom lenAtm = core->uintToAtom(newLen);
toplevel->setproperty(this->atom(), &mname, lenAtm, this->vtable);
}
}