Permalink
Cannot retrieve contributors at this time
/* ***** 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) 2004-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" | |
//#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); | |
} | |
} | |
ScriptObject::~ScriptObject() | |
{ | |
//setDelegate(NULL); -- no longer necessary | |
vtable->traits->destroyInstance(this); | |
} | |
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->initialize(this->gc(), capacity); | |
iht->setDontEnumSupport(); | |
} | |
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->getCapacity() == 0) | |
const_cast<ScriptObject*>(this)->initHashtable(); | |
return iht; | |
} | |
else | |
{ | |
//DictionaryObjects store pointer to HeapHashtable at | |
//the hashtable offset | |
return (*hht)->get_ht(); | |
} | |
} | |
/** | |
* 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 | |
{ | |
InlineHashtable *table = o->getTable(); | |
const Atom* atoms = table->getAtoms(); | |
int i = table->find(name, atoms, table->getCapacity()); | |
if (atoms[i] != InlineHashtable::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 | |
{ | |
const Atom* atoms = o->getTable()->getAtoms(); | |
int i = o->getTable()->find(searchname, atoms, o->getTable()->getCapacity()); | |
if (atoms[i] != InlineHashtable::EMPTY) | |
return atoms[i+1]; | |
} | |
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::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 | |
{ | |
// NOTE use default public since name is not used | |
Multiname multiname(core()->getAnyPublicNamespace(), AvmCore::atomToString(name)); | |
// cannot create properties on a sealed object. | |
toplevel()->throwReferenceError(kWriteSealedError, &multiname, traits()); | |
} | |
} | |
void ScriptObject::setMultinameProperty(const Multiname* name, Atom value) | |
{ | |
if (traits()->needsHashtable() && name->isValidDynamicName()) | |
{ | |
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. just use any public | |
Multiname multiname(core()->getAnyPublicNamespace(), 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(const Multiname* name) | |
{ | |
if (traits()->needsHashtable() && name->isValidDynamicName()) | |
{ | |
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 | |
{ | |
InlineHashtable *table = o->getTable(); | |
const Atom* atoms = table->getAtoms(); | |
int i = table->find(name, atoms, table->getCapacity()); | |
if (atoms[i] != InlineHashtable::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 | |
{ | |
// NOTE use default public since name is not used | |
Multiname multiname(core->getAnyPublicNamespace(), core->string(name)); | |
// 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(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) | |
{ | |
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(const 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(const Multiname* /*name*/) const | |
{ | |
toplevel()->throwTypeError(kDescendentsError, core()->toErrorString(traits())); | |
return undefinedAtom;// not reached | |
} | |
bool ScriptObject::isGlobalObject() const | |
{ | |
AvmAssert(vtable != 0); | |
AvmAssert(vtable->init != 0); | |
MethodEnv* init = vtable->init; | |
if (!init->isScriptEnv()) | |
return false; | |
const ScriptEnv* const scriptInitForVTable = static_cast<const ScriptEnv*>(init); | |
return scriptInitForVTable->global == this; | |
} | |
#ifdef AVMPLUS_VERBOSE | |
Stringp ScriptObject::format(AvmCore* core) const | |
{ | |
if (traits()->name() != NULL) { | |
return core->concatStrings(traits()->format(core), | |
core->concatStrings(core->newConstantStringLatin1("@"), | |
core->formatAtomPtr(atom()))); | |
} else { | |
return core->concatStrings(core->newConstantStringLatin1("{}@"), | |
core->formatAtomPtr(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. | |
Atom 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)->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 (atomKind(result) != 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 | |
// NOTE use default public since name is not used | |
Multiname name(core()->getAnyPublicNamespace(), core()->internConstantStringLatin1("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::applyTypeArgs(int /*argc*/, Atom* /*argv*/) | |
{ | |
toplevel()->throwTypeError(kTypeAppOfNonParamType); | |
return undefinedAtom; | |
} | |
Atom ScriptObject::getSlotAtom(uint32_t slot) | |
{ | |
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 traits->core->doubleToAtom(*((const double*)p)); | |
} | |
else if (sst == SST_int32) | |
{ | |
return traits->core->intToAtom(*((const int32_t*)p)); | |
} | |
else if (sst == SST_uint32) | |
{ | |
return traits->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 | |
} | |
} | |
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); | |
} | |
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) | |
{ | |
AvmAssert(traits()->needsHashtable()); | |
AvmAssert(index > 0); | |
InlineHashtable *ht = getTable(); | |
if (uint32_t(index)-1 >= ht->getCapacity()/2) | |
return nullStringAtom; | |
const Atom* atoms = ht->getAtoms(); | |
Atom m = ht->removeDontEnumMask(atoms[(index-1)<<1]); | |
if (AvmCore::isNullOrUndefined(m)) | |
return nullStringAtom; | |
return m; | |
} | |
Atom ScriptObject::nextValue(int index) | |
{ | |
AvmAssert(traits()->needsHashtable()); | |
AvmAssert(index > 0); | |
InlineHashtable *ht = getTable(); | |
if (uint32_t(index)-1 >= ht->getCapacity()/2) | |
return undefinedAtom; | |
const Atom* atoms = ht->getAtoms(); | |
Atom m = ht->removeDontEnumMask(atoms[(index-1)<<1]); | |
if (AvmCore::isNullOrUndefined(m)) | |
return nullStringAtom; | |
return atoms[((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. | |
InlineHashtable* table = getTable(); | |
const Atom* atoms = table->getAtoms(); | |
int numAtoms = table->getCapacity(); | |
while (index < numAtoms) { | |
Atom m = atoms[index]; | |
if (table->enumerable(m)) | |
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->init->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); | |
} | |
#ifdef DEBUGGER | |
uint64 ScriptObject::size() const | |
{ | |
uint64 size = traits()->getTotalSize(); | |
if(traits()->needsHashtable()) | |
{ | |
size += getTable()->size(); | |
} | |
size -= sizeof(AvmPlusScriptableObject); | |
return size; | |
} | |
#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("]")); | |
} | |
} |