/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- alloc
- findBinding
- findBinding
- findBinding
- findBindingAndDeclarer
- buildSlotDestroyInfo
- checkOverride
- isCompatibleOverrideKind
- checkLegalInterfaces
- fixOneInterfaceBindings
- addVersionedBindings
- getSlotMetadataPos
- getMethodMetadataPos
- hasCustomConstruct
- newTraits
- newCatchTraits
- _newParameterizedTraits
- traitsPosStart
- skipToInstanceInitPos
- is8ByteSlot
- pad8
- bt2sst
- isPointerSlot
- m_earlySlotBinding
- calc_id
- slotCount
- readNameEntry
- allowEarlyBinding
- buildBindings
- computeSlotOffset
- computeSlotAreaCountAndSize
- computeSlotAreaStart
- finishSlotsAndMethods
- skipToInterfaceCount
- countNewInterfaces
- calcLog2
- _buildTraitsBindings
- _buildTraitsMetadata
- init_declaringScopes
- insertSupertype
- resolveSignatures
- resolveSignaturesSelf
- hookUpActivationTraitsInitMethodForTraitsMethod
- initActivationTraits
- isMachineCompatible
- format
- genDefaultValue
- genInitBody
- destroyInstance
- formatClassName
- getOverride
- _getTraitsBindings
- _getTraitsMetadata
- countSupertypes
- build_primary_supertypes
- build_secondary_supertypes
- secondary_subtypeof
- allocSupertypeList
/* ***** 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
{
using namespace MMgc;
// -------------------------------------------------------------------
// -------------------------------------------------------------------
// -------------------------------------------------------------------
// -------------------------------------------------------------------
/*static*/ TraitsBindings* TraitsBindings::alloc(GC* gc,
Traits* _owner,
TraitsBindingsp _base,
MultinameHashtable* _bindings,
uint32_t slotCount,
uint32_t methodCount)
{
const uint32_t extra = slotCount * sizeof(SlotInfo) +
methodCount * sizeof(MethodInfo);
TraitsBindings* tb = new (gc, extra) TraitsBindings(_owner, _base, _bindings, slotCount, methodCount);
if (_base)
{
if (_base->slotCount)
{
const SlotInfo* src = &_base->getSlots()[0];
SlotInfo* dst = &tb->getSlots()[0];
VMPI_memcpy(dst, src, _base->slotCount * sizeof(SlotInfo));
AvmAssert(((_owner->isMachineType()) || (tb->owner->m_sizeofInstance >= _base->owner->m_sizeofInstance)));
}
if (_base->methodCount)
VMPI_memcpy(&tb->getMethods()[0], &_base->getMethods()[0], _base->methodCount * sizeof(MethodInfo));
}
return tb;
}
Binding TraitsBindings::findBinding(Stringp name) const
{
for (TraitsBindingsp self = this; self; self = self->base)
{
const Binding b = self->m_bindings->getName(name);
if (b != BIND_NONE)
return b;
}
return BIND_NONE;
}
Binding TraitsBindings::findBinding(Stringp name, Namespacep ns) const
{
for (TraitsBindingsp self = this; self; self = self->base)
{
const Binding b = self->m_bindings->get(name, ns);
if (b != BIND_NONE)
return b;
}
return BIND_NONE;
}
Binding TraitsBindings::findBinding(Stringp name, NamespaceSetp nsset) const
{
for (TraitsBindingsp self = this; self; self = self->base)
{
const Binding b = self->m_bindings->get(name, nsset);
if (b != BIND_NONE)
return b;
}
return BIND_NONE;
}
Binding TraitsBindings::findBindingAndDeclarer(const Multiname& mn, Traitsp& declarer) const
{
if (mn.isBinding())
{
for (TraitsBindingsp self = this; self; self = self->base)
{
Namespacep foundns = NULL;
Binding const b = self->m_bindings->getMulti(mn, foundns);
if (b != BIND_NONE)
{
declarer = self->owner;
// if the member is 'protected' then we have to do extra work,
// as we may have found it in a descendant's protected namespace --
// we have to bounce up the inheritance chain and check our parent's
// protected namespace.
while (foundns == declarer->protectedNamespace)
{
Traitsp declParent = declarer->base;
if (!declParent || declParent->protectedNamespace == NULL)
break;
Binding const bp = declParent->getTraitsBindings()->findBinding(mn.getName(), declParent->protectedNamespace);
if (bp != b)
break;
// self->owner->core->console<<"bounce "<<declarer<<" to "<<declParent<<"\n";
declarer = declParent;
foundns = declParent->protectedNamespace;
}
// self->owner->core->console<<"final declarer is "<<declarer<<"\n";
return b;
}
}
}
declarer = NULL;
return BIND_NONE;
}
void TraitsBindings::buildSlotDestroyInfo(MMgc::GC* gc, FixedBitSet& slotDestroyInfo, uint32_t slotAreaCount, uint32_t slotAreaSize) const
{
MMGC_STATIC_ASSERT(kUnusedAtomTag == 0 && kObjectType == 1 && kStringType == 2 && kNamespaceType == 3);
AvmAssert(slotAreaCount <= slotCount);
// not the same as slotCount since a slot of type double
// takes two bits (in 32-bit builds). note that the bits are
// always 4-byte chunks even in 64-bit builds!
const uint32_t bitsNeeded = slotAreaSize / sizeof(uint32_t); // not sizeof(Atom)!
AvmAssert(bitsNeeded * sizeof(uint32_t) == slotAreaSize); // should be even multiple!
// allocate one extra bit and use it for "all-zero"
slotDestroyInfo.resize(gc, bitsNeeded+1);
if (slotAreaSize > 0)
{
const uint32_t sizeofInstance = this->owner->getSizeOfInstance();
const TraitsBindings::SlotInfo* tbs = getSlots() + (slotCount - slotAreaCount);
const TraitsBindings::SlotInfo* tbs_end = tbs + slotAreaCount;
for ( ; tbs < tbs_end; ++tbs)
{
// offset is pointed off the end of our object
if (isAtomOrRCObjectSlot(tbs->sst()))
{
//owner->core->console<<"SDI "<<owner<<" "<<sizeofInstance<<" "<<tbs->type<<" "<<tbs->offset()<<"\n";
AvmAssert(tbs->offset() >= sizeofInstance);
const uint32_t off = tbs->offset() - sizeofInstance;
AvmAssert((off % 4) == 0);
// if slot is "big" then this is the bit of the first 4 bytes. that's fine.
slotDestroyInfo.set((off>>2)+1); // +1 to leave room for bit 0
slotDestroyInfo.set(0); // bit 0 is "anyset" flag
}
// otherwise leave the bit zero
}
}
else
{
AvmAssert(slotAreaCount == 0);
AvmAssert(bitsNeeded == 0);
}
// if nothing set, blow away what we built and realloc as single clear bit -- smaller and faster
if (!slotDestroyInfo.test(0))
{
slotDestroyInfo.resize(gc, 1);
AvmAssert(!slotDestroyInfo.test(0));
}
}
bool TraitsBindings::checkOverride(AvmCore* core, MethodInfo* virt, MethodInfo* over) const
{
if (over == virt)
return true;
MethodSignaturep overms = over->getMethodSignature();
MethodSignaturep virtms = virt->getMethodSignature();
Traits* overTraits = overms->returnTraits();
Traits* virtTraits = virtms->returnTraits();
if (overTraits != virtTraits)
{
#ifdef AVMPLUS_VERBOSE
core->console << "\n";
core->console << "return types dont match\n";
core->console << " virt " << virtTraits << " " << virt << "\n";
core->console << " over " << overTraits << " " << over << "\n";
#endif
return false;
}
if (overms->param_count() != virtms->param_count() ||
overms->optional_count() != virtms->optional_count())
{
#ifdef AVMPLUS_VERBOSE
core->console << "\n";
core->console << "param count mismatch\n";
core->console << " virt params=" << virtms->param_count() << " optional=" << virtms->optional_count() << " " << virt << "\n";
core->console << " over params=" << overms->param_count() << " optional=" << overms->optional_count() << " " << virt << "\n";
#endif
return false;
}
// allow subclass param 0 to implement or extend base param 0
virtTraits = virtms->paramTraits(0);
if (!owner->subtypeof(virtTraits) || !Traits::isMachineCompatible(this->owner, virtTraits))
{
if (!this->owner->isMachineType() && virtTraits == core->traits.object_itraits)
{
over->setUnboxThis();
}
else
{
#ifdef AVMPLUS_VERBOSE
core->console << "\n";
core->console << "param 0 incompatible\n";
core->console << " virt " << virtTraits << " " << virt << "\n";
core->console << " over " << this->owner << " " << over << "\n";
#endif
return false;
}
}
for (int k=1, p=overms->param_count(); k <= p; k++)
{
overTraits = overms->paramTraits(k);
virtTraits = virtms->paramTraits(k);
if (overTraits != virtTraits)
{
#ifdef AVMPLUS_VERBOSE
core->console << "\n";
core->console << "param " << k << " incompatible\n";
core->console << " virt " << virtTraits << " " << virt << "\n";
core->console << " over " << overTraits << " " << over << "\n";
#endif
return false;
}
}
if (virt->unboxThis())
{
// the UNBOX_THIS flag is sticky, all the way down the inheritance tree
over->setUnboxThis();
}
return true;
}
static bool isCompatibleOverrideKind(BindingKind baseKind, BindingKind overKind)
{
static const uint8_t kCompatibleBindingKinds[8] =
{
0, // BKIND_NONE
(1<<BKIND_METHOD), // BKIND_METHOD
0, // BKIND_VAR
0, // BKIND_CONST
0, // unused
(1<<BKIND_GET) | (1<<BKIND_SET) | (1<<BKIND_GETSET), // BKIND_GET
(1<<BKIND_GET) | (1<<BKIND_SET) | (1<<BKIND_GETSET), // BKIND_SET
(1<<BKIND_GET) | (1<<BKIND_SET) | (1<<BKIND_GETSET) // BKIND_GETSET
};
return (kCompatibleBindingKinds[baseKind] & (1<<overKind)) != 0;
}
bool TraitsBindings::checkLegalInterfaces(AvmCore* core) const
{
// make sure every interface method is implemented
for (InterfaceIterator ifc_iter(this); ifc_iter.hasNext();)
{
Traits* ifc = ifc_iter.next();
// don't need to bother checking interfaces in our parent.
if (this->base && this->base->owner->subtypeof(ifc))
continue;
TraitsBindingsp ifcd = ifc->getTraitsBindings();
StTraitsBindingsIterator iter(ifcd);
while (iter.next())
{
Stringp name = iter.key();
if (!name) continue;
Namespacep ns = iter.ns();
Binding iBinding = iter.value();
const BindingKind iBindingKind = AvmCore::bindingKind(iBinding);
Binding cBinding = this->findBinding(name, ns);
if (!isCompatibleOverrideKind(iBindingKind, AvmCore::bindingKind(cBinding)))
{
// Try again with public namespace that matches the version of the current traits
const Binding pBinding = this->findBinding(name, core->getPublicNamespace(owner->pool));
if (isCompatibleOverrideKind(iBindingKind, AvmCore::bindingKind(pBinding)))
cBinding = pBinding;
}
if (iBinding == cBinding)
continue;
if (!isCompatibleOverrideKind(iBindingKind, AvmCore::bindingKind(cBinding)))
return false;
switch (iBindingKind)
{
default:
{
AvmAssert(0); // interfaces shouldn't have anything but methods, getters, and setters
return false;
}
case BKIND_METHOD:
{
MethodInfo* virt = ifcd->getMethod(AvmCore::bindingToMethodId(iBinding));
MethodInfo* over = this->getMethod(AvmCore::bindingToMethodId(cBinding));
if (!checkOverride(core, virt, over))
return false;
break;
}
case BKIND_GET:
case BKIND_SET:
case BKIND_GETSET:
{
// check getter & setter overrides
if (AvmCore::hasGetterBinding(iBinding))
{
if (!AvmCore::hasGetterBinding(cBinding))
return false;
MethodInfo* virt = ifcd->getMethod(AvmCore::bindingToGetterId(iBinding));
MethodInfo* over = this->getMethod(AvmCore::bindingToGetterId(cBinding));
if (!checkOverride(core, virt, over))
return false;
}
if (AvmCore::hasSetterBinding(iBinding))
{
if (!AvmCore::hasSetterBinding(cBinding))
return false;
MethodInfo* virt = ifcd->getMethod(AvmCore::bindingToSetterId(iBinding));
MethodInfo* over = this->getMethod(AvmCore::bindingToSetterId(cBinding));
if (!checkOverride(core, virt, over))
return false;
}
}
break;
} // switch
} // for j
} // for tbi
return true;
}
void TraitsBindings::fixOneInterfaceBindings(Traitsp ifc)
{
AvmAssert(!owner->isInterface());
AvmAssert(ifc->linked);
TraitsBindingsp ifcd = ifc->getTraitsBindings();
StTraitsBindingsIterator iter(ifcd);
while (iter.next())
{
Stringp name = iter.key();
if (!name) continue;
Namespacep ns = iter.ns();
Binding iBinding = iter.value();
const BindingKind iBindingKind = AvmCore::bindingKind(iBinding);
const Binding cBinding = this->findBinding(name, ns);
if (!isCompatibleOverrideKind(iBindingKind, AvmCore::bindingKind(cBinding)))
{
// Try again with public namespace that matches the version of the current traits
const Binding pBinding = this->findBinding(name, ifc->core->getPublicNamespace(owner->pool));
if (isCompatibleOverrideKind(iBindingKind, AvmCore::bindingKind(pBinding)))
{
this->m_bindings->add(name, ns, pBinding);
}
}
}
}
void Traits::addVersionedBindings(MultinameHashtable* bindings,
Stringp name,
NamespaceSetp nss,
Binding binding) const
{
int32_t apis = 0;
for (NamespaceSetIterator iter(nss); iter.hasNext();)
{
Namespacep ns = iter.next();
apis |= ApiUtils::getCompatibleAPIs(core, ns->getAPI());
}
Namespacep ns = ApiUtils::getVersionedNamespace(core, nss->nsAt(0), apis);
bindings->add(name, ns, binding);
}
// -------------------------------------------------------------------
// -------------------------------------------------------------------
TraitsMetadata::MetadataPtr TraitsMetadata::getSlotMetadataPos(uint32_t i, PoolObject*& residingPool) const
{
AvmAssert(i < slotCount);
residingPool = NULL;
for (TraitsMetadatap self = this; self && (i < self->slotCount); self = self->base)
{
MetadataPtr pos = self->slotMetadataPos[i];
if (pos)
{
residingPool = self->residingPool;
return pos;
}
}
return NULL;
}
TraitsMetadata::MetadataPtr TraitsMetadata::getMethodMetadataPos(uint32_t i, PoolObject*& residingPool) const
{
AvmAssert(i < methodCount);
residingPool = NULL;
for (TraitsMetadatap self = this; self && (i < self->methodCount); self = self->base)
{
MetadataPtr pos = self->methodMetadataPos[i];
if (pos)
{
residingPool = self->residingPool;
return pos;
}
}
return NULL;
}
// -------------------------------------------------------------------
// -------------------------------------------------------------------
Traits::Traits(PoolObject* _pool,
Traits* _base,
uint16_t _sizeofInstance,
uint16_t _offsetofSlots,
TraitsPosPtr traitsPos,
TraitsPosType posType) :
core(_pool->core),
base(_base),
pool(_pool),
m_traitsPos(traitsPos),
m_tbref(_pool->core->GetGC()->emptyWeakRef),
m_tmref(_pool->core->GetGC()->emptyWeakRef),
m_sizeofInstance(_sizeofInstance),
m_offsetofSlots(_offsetofSlots),
builtinType(BUILTIN_none),
m_posType(uint8_t(posType)),
// assume everything in builtin pools have custom construct unless stated otherwise
hasCustomConstruct(_pool->isBuiltin)
{
AvmAssert(m_tbref->get() == NULL);
AvmAssert(m_tmref->get() == NULL);
AvmAssert(BUILTIN_COUNT <= 32);
AvmAssert(m_slotDestroyInfo.allocatedSize() == 0);
#ifdef _DEBUG
switch (posType)
{
case TRAITSTYPE_NVA:
case TRAITSTYPE_RT:
AvmAssert(m_traitsPos == 0);
break;
default:
AvmAssert(m_traitsPos != 0);
break;
}
#endif
build_primary_supertypes();
build_secondary_supertypes();
}
/*static*/ Traits* Traits::newTraits(PoolObject* pool,
Traits *base,
uint16_t objectSize,
uint16_t offsetOfSlots,
TraitsPosPtr traitsPos,
TraitsPosType posType)
{
AvmAssert(posType != TRAITSTYPE_CATCH);
AvmAssert(pool != NULL);
Traits* traits = new (pool->core->GetGC()) Traits(pool, base, objectSize, offsetOfSlots, traitsPos, posType);
return traits;
}
/*static*/ Traits* Traits::newCatchTraits(const Toplevel* toplevel, PoolObject* pool, TraitsPosPtr traitsPos, Stringp name, Namespacep ns)
{
AvmAssert(pool != NULL);
Traits* traits = new (pool->core->GetGC()) Traits(pool, NULL, sizeof(ScriptObject), sizeof(ScriptObject), traitsPos, TRAITSTYPE_CATCH);
traits->final = true;
traits->set_names(ns, name);
traits->resolveSignatures(toplevel);
return traits;
}
Traits* Traits::_newParameterizedTraits(Stringp name, Namespacep ns, Traits* _base)
{
Traits* newtraits = Traits::newTraits(this->pool, _base, this->getSizeOfInstance(), this->m_offsetofSlots, NULL, TRAITSTYPE_RT);
newtraits->m_needsHashtable = this->m_needsHashtable;
newtraits->set_names(ns, name);
return newtraits;
}
TraitsPosPtr Traits::traitsPosStart() const
{
TraitsPosPtr pos = m_traitsPos;
switch (posType())
{
case TRAITSTYPE_INTERFACE:
case TRAITSTYPE_INSTANCE:
pos = skipToInstanceInitPos(pos);
// fall thru, no break
case TRAITSTYPE_CLASS:
case TRAITSTYPE_SCRIPT:
AvmCore::skipU30(pos, 1); // skip in init_index
break;
case TRAITSTYPE_ACTIVATION:
// nothing to do
break;
case TRAITSTYPE_CATCH:
case TRAITSTYPE_NVA:
case TRAITSTYPE_RT:
pos = NULL;
break;
}
return pos;
}
TraitsPosPtr Traits::skipToInstanceInitPos(TraitsPosPtr pos) const
{
AvmAssert(isInstanceType() && pos != NULL);
AvmCore::skipU30(pos, 2); // skip the QName & base traits
const uint8_t theflags = *pos++;
const bool hasProtected = (theflags & 8) != 0;
if (hasProtected)
{
AvmCore::skipU30(pos);
}
const uint32_t interfaceCount = AvmCore::readU30(pos);
AvmCore::skipU30(pos, interfaceCount);
return pos;
}
static bool is8ByteSlot(Traits* slotTE)
{
#ifdef AVMPLUS_64BIT
const uint32_t BIG_TYPE_MASK = ~((1U<<BUILTIN_int) | (1U<<BUILTIN_uint) | (1U<<BUILTIN_boolean));
#else
const uint32_t BIG_TYPE_MASK = (1U<<BUILTIN_number);
#endif
return ((1 << Traits::getBuiltinType(slotTE)) & BIG_TYPE_MASK) != 0;
}
// Sun compilers don't allow static and REALLY_INLINE
/*static*/ REALLY_INLINE int32_t pad8(int32_t nextSlotOffset)
{
// 8-aligned, 8-byte field
if (nextSlotOffset & 7)
{
AvmAssert((nextSlotOffset % 4) == 0); // should always be a multiple of 4
nextSlotOffset += 4;
}
int32_t slotOffset = nextSlotOffset;
nextSlotOffset += 8;
return slotOffset;
}
static SlotStorageType bt2sst(BuiltinType bt)
{
AvmAssert(bt != BUILTIN_void);
switch (bt)
{
case BUILTIN_int: return SST_int32;
case BUILTIN_uint: return SST_uint32;
case BUILTIN_number: return SST_double;
case BUILTIN_boolean: return SST_bool32;
case BUILTIN_any: return SST_atom;
case BUILTIN_object: return SST_atom;
case BUILTIN_string: return SST_string;
case BUILTIN_namespace: return SST_namespace;
default: return SST_scriptobject;
}
}
// Sun compilers don't allow static and REALLY_INLINE
/*static*/ REALLY_INLINE bool isPointerSlot(Traits* slotTE)
{
BuiltinType bt = Traits::getBuiltinType(slotTE);
AvmAssert(bt != BUILTIN_void);
MMGC_STATIC_ASSERT(BUILTIN_COUNT < (sizeof(int) * 8));
int const IS_POINTER = ~((1<<BUILTIN_int)|(1<<BUILTIN_uint)|(1<<BUILTIN_number)|(1<<BUILTIN_boolean));
return ((1 << bt) & IS_POINTER) != 0;
}
// the logic for assigning slot id's is used in several places, so it's now collapsed here
// rather than redundantly sprinkled thru several bits of code.
class SlotIdCalcer
{
private:
uint32_t m_slotCount;
bool m_earlySlotBinding;
public:
SlotIdCalcer(uint32_t _baseSlotCount, bool _earlySlotBinding) :
m_slotCount(_baseSlotCount),
m_earlySlotBinding(_earlySlotBinding)
{
}
uint32_t calc_id(uint32_t id)
{
if (!id || !m_earlySlotBinding)
{
id = ++m_slotCount;
}
if (m_slotCount < id)
m_slotCount = id;
return id - 1;
}
uint32_t slotCount() const { return m_slotCount; }
};
struct NameEntry
{
const uint8_t* meta_pos;
uint32_t qni, id, info, value_index;
CPoolKind value_kind;
TraitKind kind;
uint8_t tag;
void readNameEntry(const uint8_t*& pos);
};
void NameEntry::readNameEntry(const uint8_t*& pos)
{
qni = AvmCore::readU30(pos);
tag = *pos++;
kind = (TraitKind) (tag & 0x0f);
value_kind = CONSTANT_unused_0x00;
value_index = 0;
// Read in the trait entry.
switch (kind)
{
case TRAIT_Slot:
case TRAIT_Const:
id = AvmCore::readU30(pos); // slot id
info = AvmCore::readU30(pos); // value type
value_index = AvmCore::readU30(pos); // value index
if (value_index)
value_kind = (CPoolKind)*pos++; // value kind
break;
case TRAIT_Class:
id = AvmCore::readU30(pos); // slot id
info = AvmCore::readU30(pos); // classinfo
break;
case TRAIT_Getter:
case TRAIT_Setter:
case TRAIT_Method:
AvmCore::skipU30(pos); // disp id (never used)
id = AvmCore::readU30(pos); // method index
info = 0;
break;
default:
// unsupported traits type -- can't happen, caught in AbcParser::parseTraits
AvmAssert(0);
break;
}
meta_pos = pos;
if (tag & ATTR_metadata)
{
uint32_t metaCount = AvmCore::readU30(pos);
AvmCore::skipU30(pos, metaCount);
}
}
bool Traits::allowEarlyBinding() const
{
// the compiler can early bind to a type's slots when it's defined
// or when the base class came from another abc file and has zero slots
// this ensures you cant use the early opcodes to access an external type's
// private members.
TraitsBindingsp tb = this->base ? this->base->getTraitsBindings() : NULL;
while (tb != NULL && tb->slotCount > 0)
{
if (tb->owner->pool != this->pool && tb->slotCount > 0)
{
return false;
}
tb = tb->base;
}
return true;
}
void Traits::buildBindings(TraitsBindingsp basetb,
MultinameHashtable* bindings,
uint32_t& slotCount,
uint32_t& methodCount,
uint32_t& n32BitNonPointerSlots,
uint32_t& n64BitNonPointerSlots,
const Toplevel* toplevel) const
{
const uint8_t* pos = traitsPosStart();
const uint32_t baseSlotCount = basetb ? basetb->slotCount : 0;
const uint32_t baseMethodCount = basetb ? basetb->methodCount : 0;
//slotCount = baseSlotCount;
methodCount = baseMethodCount;
SlotIdCalcer sic(baseSlotCount, this->allowEarlyBinding());
NameEntry ne;
const uint32_t nameCount = pos ? AvmCore::readU30(pos) : 0;
for (uint32_t i = 0; i < nameCount; i++)
{
ne.readNameEntry(pos);
Multiname mn;
this->pool->resolveQName(ne.qni, mn, toplevel);
Stringp name = mn.getName();
Namespacep ns;
NamespaceSetp compat_nss;
if (mn.namespaceCount() > 1) {
ns = mn.getNsset()->nsAt(0);
compat_nss = mn.getNsset();
}
else {
ns = mn.getNamespace();
compat_nss = NamespaceSet::create(core->GetGC(), ns);
}
switch (ne.kind)
{
case TRAIT_Slot:
case TRAIT_Const:
case TRAIT_Class:
{
uint32_t slot_id = sic.calc_id(ne.id);
if (toplevel)
{
AvmAssert(!this->linked);
// first time thru, we must do some additional verification checks... these were formerly
// done in AbcParser::parseTraits but require the base class to be resolved first, so we
// now defer it to here.
// illegal raw slot id.
if (ne.id > nameCount)
toplevel->throwVerifyError(kCorruptABCError);
// slots are final.
if (basetb && slot_id < basetb->slotCount)
toplevel->throwVerifyError(kIllegalOverrideError, core->toErrorString(mn), core->toErrorString(base));
// a slot cannot override anything else.
if (bindings->get(name, ns) != BIND_NONE)
toplevel->throwVerifyError(kCorruptABCError);
// In theory we should reject duplicate slots here;
// in practice we don't, as it causes problems with some existing content
//if (basetb->findBinding(name, ns) != BIND_NONE)
// toplevel->throwVerifyError(kIllegalOverrideError, toplevel->core()->toErrorString(qn), toplevel->core()->toErrorString(this));
// Interfaces cannot have slots.
if (this->isInterface())
toplevel->throwVerifyError(kIllegalSlotError, core->toErrorString(this));
}
AvmAssert(!(ne.id > nameCount)); // unhandled verify error
AvmAssert(!(basetb && slot_id < basetb->slotCount)); // unhandled verify error
AvmAssert(!(bindings->get(name, ns) != BIND_NONE)); // unhandled verify error
addVersionedBindings(bindings, name, compat_nss, AvmCore::makeSlotBinding(slot_id, ne.kind==TRAIT_Slot ? BKIND_VAR : BKIND_CONST));
Traitsp slotType = (ne.kind == TRAIT_Class) ?
pool->getClassTraits(ne.info) :
pool->resolveTypeName(ne.info, toplevel);
if (!isPointerSlot(slotType))
{
if (is8ByteSlot(slotType))
++n64BitNonPointerSlots;
else
++n32BitNonPointerSlots;
}
break;
}
case TRAIT_Method:
{
Binding baseBinding = this->getOverride(basetb, ns, name, ne.tag, toplevel);
if (baseBinding == BIND_NONE)
{
addVersionedBindings(bindings, name, compat_nss, AvmCore::makeMGSBinding(methodCount, BKIND_METHOD));
// accessors require 2 vtable slots, methods only need 1.
methodCount += 1;
}
else if (AvmCore::isMethodBinding(baseBinding))
{
// something got overridden, need new name entry for this subclass
// but keep the existing disp_id
addVersionedBindings(bindings, name, compat_nss, baseBinding);
}
else
{
if (toplevel)
toplevel->throwVerifyError(kCorruptABCError);
AvmAssert(!"unhandled verify error");
}
break;
}
case TRAIT_Getter:
case TRAIT_Setter:
{
// if nothing already is defined in this class, use base class in case getter/setter has already been defined.
Binding baseBinding = bindings->get(name, ns);
if (baseBinding == BIND_NONE)
baseBinding = this->getOverride(basetb, ns, name, ne.tag, toplevel);
const BindingKind us = (ne.kind == TRAIT_Getter) ? BKIND_GET : BKIND_SET;
const BindingKind them = (ne.kind == TRAIT_Getter) ? BKIND_SET : BKIND_GET;
if (baseBinding == BIND_NONE)
{
addVersionedBindings(bindings, name, compat_nss, AvmCore::makeMGSBinding(methodCount, us));
// accessors require 2 vtable slots, methods only need 1.
methodCount += 2;
}
else if (AvmCore::isAccessorBinding(baseBinding))
{
// something maybe got overridden, need new name entry for this subclass
// but keep the existing disp_id
// both get & set bindings use the get id. set_id = get_id + 1.
if (AvmCore::bindingKind(baseBinding) == them)
{
baseBinding = AvmCore::makeGetSetBinding(baseBinding);
}
addVersionedBindings(bindings, name, compat_nss, baseBinding);
}
else
{
if (toplevel)
toplevel->throwVerifyError(kCorruptABCError);
AvmAssert(!"unhandled verify error");
}
break;
}
default:
// unsupported traits type -- can't happen, caught in AbcParser::parseTraits
AvmAssert(0);
break;
}
} // for i
slotCount = sic.slotCount();
}
namespace
{
// Don't mess with the order of the members of the
// following struct.
//
// This struct is never actually instantiated, it is
// only used to determine the default padding that C++
// compiler will insert between 32 bit member variables and
// 64 bit member variables.
struct GlueClassTest_Slots
{
int32_t m_intSlot;
double m_numberSlot;
int32_t m_otherIntSlot;
void* m_ptrSlot;
};
static const bool align8ByteSlots = (offsetof(GlueClassTest_Slots, m_numberSlot) == 8);
static const bool alignPointersTo8Bytes = (offsetof(GlueClassTest_Slots, m_ptrSlot) == 24);
static const bool is64Bit = sizeof(void*) == 8;
}
// Sun compilers don't allow static and REALLY_INLINE
/*static*/ REALLY_INLINE int32_t computeSlotOffset(Traits* slotType, int32_t& next32BitSlotOffset, int32_t& nextPointerSlotOffset, int32_t& next64BitSlotOffset)
{
if (isPointerSlot(slotType))
{
int32_t const result = nextPointerSlotOffset;
nextPointerSlotOffset += sizeof(void*);
return result;
}
else if (is8ByteSlot(slotType))
{
int32_t const result = next64BitSlotOffset;
next64BitSlotOffset += 8;
return result;
}
else
{
int32_t const result = next32BitSlotOffset;
next32BitSlotOffset += 4;
return result;
}
}
void Traits::computeSlotAreaCountAndSize(TraitsBindings* tb, uint32_t& slotCount, uint32_t& size) const
{
const TraitsBindings* prevBindings = tb;
const TraitsBindings* currBindings = tb->base;
uint32_t thisSize = getSizeOfInstance();
while ((currBindings != NULL) && (currBindings->owner->getSizeOfInstance() == thisSize))
{
const TraitsBindings* baseBindings = currBindings->base;
AvmAssert((baseBindings == 0) || (baseBindings->owner->getSizeOfInstance() <= thisSize)); (void)baseBindings;
prevBindings = currBindings;
currBindings = currBindings->base;
}
// currBindings is first ancestor class that is native with at least one slot or native member variable
// or null if there is no native ancenstor class
if (currBindings == NULL)
{
// We could not find a ancestor class that is native.
slotCount = tb->slotCount;
size = tb->m_slotSize;
}
else if (currBindings == tb->base)
{
AvmAssert(tb->base != NULL);
AvmAssert((tb->base->slotCount == 0) || (tb->base->owner->getSizeOfInstance() <= thisSize));
// "this" is a native class.
slotCount = 0;
size = 0;
}
else
{
AvmAssert(tb->base != NULL);
AvmAssert(currBindings != NULL);
AvmAssert(currBindings->owner->getSizeOfInstance() <= thisSize);
AvmAssert(tb->slotCount >= prevBindings->slotCount);
AvmAssert(tb->m_slotSize >= prevBindings->m_slotSize);
slotCount = tb->slotCount - prevBindings->slotCount;
size = tb->m_slotSize - prevBindings->m_slotSize;
}
}
inline uint32_t Traits::computeSlotAreaStart(uint32_t nPointerSlots, uint32_t n32BitNonPointerSlots, uint32_t n64BitNonPointerSlots) const
{
// Actual size of slots can be larger because of padding inserted at beginning of slot area and between 4 byte slots and 8 byte slots.
uint32_t minSizeOfSlots = (nPointerSlots * sizeof(void*)) + (n32BitNonPointerSlots * 4) + (n64BitNonPointerSlots * 8);
uint32_t result = 0;
if (base && minSizeOfSlots)
{
if (getSizeOfInstance() == base->getSizeOfInstance())
{
// we are not a native subclass or if we are, we don't have any slots.
// Our slots start right after our base class slots.
// The end of our base class slots is the same offset at which
// our base class put its hash table.
if (base->m_hashTableOffset)
result = base->m_hashTableOffset;
else
result = base->getTotalSize();
}
else
{
// We are a native subclass
// We should be bigger than our base class
// Our slots are at the end of our instance class.
AvmAssert(base->getSizeOfInstance() < getSizeOfInstance());
AvmAssert((getSizeOfInstance() - base->getSizeOfInstance()) >= static_cast<intptr_t>(minSizeOfSlots));
result = m_offsetofSlots;
// result is an offset to a C++ class instance embedded in a glue class instance
// If the C++ class instance containing the slot values contains 8 byte types that
// are supposed to be 8 byte aligned, then result should also be 8 byte aligned.
//
// If either of these asserts fail, the slot offset calculation code in finishSlotsAndMethods
// will most likely produce incorrect slot offsets for 8 byte slots.
AvmAssert(((result % 8) == 0) || (!align8ByteSlots) || (n64BitNonPointerSlots == 0));
AvmAssert(((result % 8) == 0) || (!alignPointersTo8Bytes) || (nPointerSlots == 0));
}
}
else
{
AvmAssert((getSizeOfInstance() == sizeof(ScriptObject)) || (minSizeOfSlots == 0));
result = getSizeOfInstance();
}
return result;
}
uint32_t Traits::finishSlotsAndMethods(TraitsBindingsp basetb,
TraitsBindings* tb,
const Toplevel* toplevel,
AbcGen* abcGen,
uint32_t n32BitNonPointerSlots,
uint32_t n64BitNonPointerSlots) const
{
const uint8_t* pos = traitsPosStart();
SlotIdCalcer sic(basetb ? basetb->slotCount : 0, this->allowEarlyBinding());
uint32_t nBaseSlots = tb->base ? tb->base->slotCount : 0;
uint32_t nPointerSlots = tb->slotCount - (n32BitNonPointerSlots + n64BitNonPointerSlots + nBaseSlots);
int32_t slotAreaStart = computeSlotAreaStart(nPointerSlots, n32BitNonPointerSlots, n64BitNonPointerSlots);
int32_t next32BitSlotOffset = slotAreaStart;
int32_t endOf32BitSlots = next32BitSlotOffset + (n32BitNonPointerSlots * 4);
int32_t nextPointerSlotOffset = alignPointersTo8Bytes && (nPointerSlots != 0) ? pad8(endOf32BitSlots) : endOf32BitSlots;
int32_t endOfPointerSlots = nextPointerSlotOffset + (nPointerSlots * sizeof(void*));
int32_t next64BitSlotOffset = align8ByteSlots && (n64BitNonPointerSlots != 0) ? pad8(endOfPointerSlots) : endOfPointerSlots;
int32_t endOf64BitSlots = next64BitSlotOffset + (n64BitNonPointerSlots * 8);
NameEntry ne;
const uint32_t nameCount = pos ? AvmCore::readU30(pos) : 0;
for (uint32_t i = 0; i < nameCount; i++)
{
AvmAssert(next32BitSlotOffset <= endOf32BitSlots);
AvmAssert(nextPointerSlotOffset <= endOfPointerSlots);
AvmAssert(next64BitSlotOffset <= endOf64BitSlots); (void)endOf64BitSlots;
ne.readNameEntry(pos);
Multiname mn;
this->pool->resolveQName(ne.qni, mn, toplevel);
Namespacep ns = mn.getNamespace();
Stringp name = mn.getName();
// NOTE only one versioned namespace from the set needed here
switch (ne.kind)
{
case TRAIT_Slot:
case TRAIT_Const:
case TRAIT_Class:
{
AvmAssert(endOf64BitSlots > slotAreaStart);
uint32_t slotid = sic.calc_id(ne.id);
// note, for TRAIT_Class, AbcParser::parseTraits has already verified that pool->cinits[ne.info] is not null
Traitsp slotType = (ne.kind == TRAIT_Class) ?
pool->getClassTraits(ne.info) :
pool->resolveTypeName(ne.info, toplevel);
uint32_t slotOffset = computeSlotOffset(slotType, next32BitSlotOffset, nextPointerSlotOffset, next64BitSlotOffset);
AvmAssert(slotOffset >= sizeof(ScriptObject));
tb->setSlotInfo(slotid, slotType, bt2sst(getBuiltinType(slotType)), slotOffset);
if (abcGen)
genDefaultValue(ne.value_index, slotid, toplevel, slotType, ne.value_kind, *abcGen);
break;
}
case TRAIT_Getter:
case TRAIT_Setter:
case TRAIT_Method:
{
const Binding b = tb->m_bindings->get(name, ns);
AvmAssert(b != BIND_NONE);
const uint32 disp_id = uint32(uintptr_t(b) >> 3) + (ne.kind == TRAIT_Setter);
MethodInfo* f = this->pool->getMethodInfo(ne.id);
//AvmAssert(f->declaringTraits() == this);
tb->setMethodInfo(disp_id, f);
break;
}
default:
// unsupported traits type -- can't happen, caught in AbcParser::parseTraits
AvmAssert(0);
break;
}
} // for i
// check for sparse slot table -- anything not specified will default to * (but we must allocate space for it)
for (uint32_t i = 0; i < tb->slotCount; i++)
{
if (tb->getSlotOffset(i) > 0)
continue;
#ifdef AVMPLUS_VERBOSE
if (pool->isVerbose(VB_traits))
{
core->console << "WARNING: slot " << i+1 << " on " << this << " not defined by compiler. Using *\n";
}
#endif
const Traitsp slotType = NULL;
const uint32_t slotOffset = nextPointerSlotOffset;
nextPointerSlotOffset += sizeof(void*);
AvmAssert(slotOffset >= sizeof(ScriptObject));
tb->setSlotInfo(i, slotType, SST_atom, slotOffset);
}
AvmAssert(next32BitSlotOffset == endOf32BitSlots);
AvmAssert(nextPointerSlotOffset == endOfPointerSlots);
AvmAssert(next64BitSlotOffset == endOf64BitSlots);
AvmAssert(endOf64BitSlots >= slotAreaStart);
return endOf64BitSlots - slotAreaStart;
}
static const uint8_t* skipToInterfaceCount(const uint8_t* pos)
{
AvmAssert(pos != NULL);
AvmCore::skipU30(pos, 2); // skip the QName + basetraits
const uint8_t theflags = *pos++;
if (theflags & 8)
AvmCore::skipU30(pos); // skip protected namespace
return pos;
}
// Apps often have many interfaces redundantly listed, so first time thru,
// eliminate redundant ones. We do this by only adding new interfaces to
// the "seen" list and only traversing new super-interfaces. An interface
// is NEW if our base class does not implement it.
uint32_t Traits::countNewInterfaces(List<Traitsp, LIST_GCObjects>& seen)
{
// each Traits* added to this list is rooted via Domain and PoolObject,
// so it is okay for this Stack to be opaque to the GC.
Stack<Traits*> pending;
pending.add(this);
while (!pending.isEmpty()) {
Traits* t = pending.pop();
const uint8_t* pos = skipToInterfaceCount(t->m_traitsPos);
const uint32_t interfaceCount = AvmCore::readU30(pos);
for (uint32_t j = 0; j < interfaceCount; j++) {
// we know all interfaces have been resolved already, it is done
// before traits construction in AbcParser::parseInstanceInfos().
Traitsp intf = t->pool->resolveTypeName(pos, NULL);
AvmAssert(intf && intf->isInterface() && intf->base == NULL);
// an interface can "extend" multiple other interfaces, so we must recurse here.
if ((!base || !base->subtypeof(intf)) && seen.indexOf(intf) < 0) {
seen.add(intf);
pending.add(intf);
}
}
}
return seen.size();
}
static uint8_t calcLog2(uint32_t cap)
{
uint8_t capLog = 1; // start with at least 2 entries
while ((1U<<capLog) < cap)
{
++capLog;
}
AvmAssert((1U<<capLog) >= cap);
return capLog;
}
TraitsBindings* Traits::_buildTraitsBindings(const Toplevel* toplevel, AbcGen* abcGen)
{
// no, this can be called before the resolved bit is set
//AvmAssert(this->linked);
#ifdef AVMPLUS_VERBOSE
if (pool->isVerbose(VB_traits))
{
core->console << "Generate TraitsBindings for "<<this<<"\n";
}
#endif
TraitsBindings* thisData = NULL;
// if we know the cap we need, go there right away, otherwise start at small power of 2
// this saves tremendously on subsequent builds of this set of bindings since we don't have to
// waste time growing the MNHT as we build it
const int32_t bindingCap = m_bindingCapLog2 ? (1 << m_bindingCapLog2) : 2;
MMgc::GC* gc = core->GetGC();
MultinameHashtable* bindings = new (gc) MultinameHashtable(bindingCap);
AvmAssert(bindings->numQuads == bindingCap);
if (this->posType() == TRAITSTYPE_CATCH)
{
const uint8_t* pos = m_traitsPos;
Traits* t = this->pool->resolveTypeName(pos, toplevel);
// this assumes we save name/ns in all builds, not just verbose
NamespaceSetp nss = NamespaceSet::create(core->GetGC(), this->ns());
NamespaceSetp compat_nss = nss;
addVersionedBindings(bindings, this->name(), compat_nss, AvmCore::makeSlotBinding(0, BKIND_VAR));
// bindings just need room for one slot binding
thisData = TraitsBindings::alloc(gc, this, /*base*/NULL, bindings, /*slotCount*/1, /*methodCount*/0);
thisData->setSlotInfo(0, t, bt2sst(getBuiltinType(t)), this->m_sizeofInstance);
thisData->m_slotSize = is8ByteSlot(t) ? 8 : 4;
}
else
{
TraitsBindingsp basetb = this->base ? this->base->getTraitsBindings() : NULL;
// Copy protected traits from base class into new protected namespace
if (basetb && base->protectedNamespace && this->protectedNamespace)
{
StTraitsBindingsIterator iter(basetb);
while (iter.next())
{
if (!iter.key()) continue;
if (iter.ns() == base->protectedNamespace)
{
bindings->add(iter.key(), this->protectedNamespace, iter.value());
}
}
}
uint32_t slotCount = 0;
uint32_t methodCount = 0;
uint32_t n32BitNonPointerSlots = 0;
uint32_t n64BitNonPointerSlots = 0;
buildBindings(basetb, bindings, slotCount, methodCount, n32BitNonPointerSlots, n64BitNonPointerSlots, toplevel);
thisData = TraitsBindings::alloc(gc, this, basetb, bindings, slotCount, methodCount);
thisData->m_slotSize = finishSlotsAndMethods(basetb, thisData, toplevel, abcGen, n32BitNonPointerSlots, n64BitNonPointerSlots);
if (basetb) {
thisData->m_slotSize += basetb->m_slotSize;
}
if (!isInterface() && m_implementsNewInterfaces) {
// fix up interface bindings
for (InterfaceIterator it(this); it.hasNext(); ) {
Traits* t = it.next();
if (!base || !base->subtypeof(t)) {
// new interface not implemented in base.
thisData->fixOneInterfaceBindings(t);
}
}
}
}
// hashtable (if we have one) must start on pointer-sized boundary...
// much easier to always round slotsize up unconditionally rather than
// only for cases with hashtable, so that's what we'll do. (MMgc currently
// allocate in 8-byte increments anyway, so we're not really losing any space.)
thisData->m_slotSize = (thisData->m_slotSize + (sizeof(uintptr_t)-1)) & ~(sizeof(uintptr_t)-1);
// remember the cap we need
if (m_bindingCapLog2 == 0)
m_bindingCapLog2 = calcLog2(thisData->m_bindings->numQuads); // remember capacity, not count
AvmAssert(m_bindingCapLog2 > 0);
#ifdef AVMPLUS_VERBOSE
if (pool->isVerbose(VB_traits))
{
core->console << this << " bindings\n";
StTraitsBindingsIterator iter(thisData);
while (iter.next())
{
core->console << iter.key() << ":" << (uint32_t)(uintptr_t)(iter.value()) << "\n";
}
core->console << this << " end bindings \n";
}
#endif
AvmAssert(m_tbref->get() == NULL);
m_tbref = thisData->GetWeakRef();
core->tbCache()->add(thisData);
return thisData;
}
TraitsMetadata* Traits::_buildTraitsMetadata()
{
AvmAssert(this->linked);
#ifdef AVMPLUS_VERBOSE
if (pool->isVerbose(VB_traits))
{
core->console << "Generate TraitsMetadata for "<<this<<"\n";
}
#endif
TraitsBindingsp td = this->getTraitsBindings();
MMgc::GC* gc = core->GetGC();
TraitsMetadatap basetm = this->base ? this->base->getTraitsMetadata() : NULL;
const uint32_t extra = td->slotCount * sizeof(TraitsMetadata::MetadataPtr) + td->methodCount * sizeof(TraitsMetadata::MetadataPtr);
TraitsMetadata* tm = new (gc, extra) TraitsMetadata(basetm, this->pool, this->metadata_pos, td->slotCount, td->methodCount);
tm->slotMetadataPos = (TraitsMetadata::MetadataPtr*)(tm + 1);
tm->methodMetadataPos = (TraitsMetadata::MetadataPtr*)(tm->slotMetadataPos + tm->slotCount);
const uint8_t* pos = traitsPosStart();
const uint32_t nameCount = pos ? AvmCore::readU30(pos) : 0;
SlotIdCalcer sic(td->base ? td->base->slotCount : 0, this->allowEarlyBinding());
NameEntry ne;
for (uint32_t i = 0; i < nameCount; i++)
{
ne.readNameEntry(pos);
switch (ne.kind)
{
case TRAIT_Class:
AvmAssert(0);
// classes shouldn't have metadata, but just fall thru just in case
case TRAIT_Slot:
case TRAIT_Const:
{
const uint32_t slot_id = sic.calc_id(ne.id);
if (ne.tag & ATTR_metadata)
tm->slotMetadataPos[slot_id] = ne.meta_pos;
break;
}
case TRAIT_Getter:
case TRAIT_Setter:
case TRAIT_Method:
{
if (ne.tag & ATTR_metadata)
{
Multiname qn;
// passing NULL for toplevel here, since it's only used if a verification error occurs --
// but if there was one, we would have encountered it during AbcParser::parseTraits.
this->pool->resolveQName(ne.qni, qn, /*toplevel*/NULL);
const Binding b = td->findBinding(qn.getName(), qn.getNamespace());
AvmAssert(b != BIND_NONE);
const uint32 disp_id = uint32(uintptr_t(b) >> 3) + (ne.kind == TRAIT_Setter);
tm->methodMetadataPos[disp_id] = ne.meta_pos;
}
break;
}
default:
// unsupported traits type -- can't happen, caught in AbcParser::parseTraits
AvmAssert(0);
break;
}
} // for i
AvmAssert(m_tmref->get() == NULL);
m_tmref = tm->GetWeakRef();
core->tmCache()->add(tm);
return tm;
}
void Traits::init_declaringScopes(const ScopeTypeChain* stc)
{
AvmAssert(linked);
if (!linked)
return;
if (this->init)
this->init->init_declaringScope(stc);
{
TraitsBindingsp tb = this->getTraitsBindings();
const TraitsBindings::BindingMethodInfo* tbm = tb->getMethods();
const TraitsBindings::BindingMethodInfo* tbm_end = tbm + tb->methodCount;
for ( ; tbm < tbm_end; ++tbm)
{
if (tbm->f == NULL)
continue;
if (tbm->f->declaringTraits() == this)
{
tbm->f->init_declaringScope(stc);
}
}
}
}
/**
* add t to pending[] ahead of any existing entries that are
* subtypes or implementers of t, so that when we iterate through
* pending[] we will traverse the inheritance DAG top-down.
*/
void insertSupertype(Traits* t, List<Traits*> &pending)
{
uint32_t i = 0;
for (uint32_t n = pending.size(); i < n; i++) {
if (pending[i]->subtypeof(t)) {
pending.insert(i, t);
return;
}
}
pending.add(t);
}
/**
* resolve this traits signatures by visiting all supertypes in
* top-down toplogical order, then resolving our own signatures. The top-down
* order ensures that we resolve base classes and interfaces first, while
* avoiding recursion.
*/
void Traits::resolveSignatures(const Toplevel* toplevel)
{
// toplevel actually can be null, when resolving the builtin classes...
// but they should never cause verification errors in functioning builds
if (linked)
return;
List<Traits*> pending(core->gc);
// copy primary supertypes into pending.
// primary_supertypes[] is already in top-down order.
for (int32_t i = 0; i < MAX_PRIMARY_SUPERTYPE; i++) {
Traits* t = m_primary_supertypes[i];
if (t == NULL || t == this)
break;
if (!t->linked)
pending.add(t);
}
// copy other base types, and interfaces, into pending[] by
// and maintaining the partial ordering that each type's bases and
// interfaces are visited before that type itself is visited.
for (Traits** st = m_secondary_supertypes; *st != NULL; st++) {
Traits* t = *st;
if (t != this && !t->linked)
insertSupertype(t, pending);
}
for (uint32_t i = 0, n = pending.size(); i < n; i++) {
AvmAssert(!pending[i]->linked);
pending[i]->resolveSignaturesSelf(toplevel);
}
this->resolveSignaturesSelf(toplevel);
}
/**
* This must be called before any method is verified or any
* instances are created. It is not done eagerly in AbcParser
* because doing so would prevent circular type references between
* slots of cooperating classes.
*
* Resolve the type and position/width of each slot.
*/
void Traits::resolveSignaturesSelf(const Toplevel* toplevel)
{
#ifdef DEBUG
AvmAssert(!linked);
// make sure our supertypes are resolved. (must be done before calling _buildTraitsBindings)
for (Traits* t = this->base; t != NULL; t = t->base)
AvmAssert(t->linked);
for (Traits** st = this->m_secondary_supertypes; *st != NULL; st++)
AvmAssert(*st == this || (*st)->linked);
#endif
MMgc::GC* gc = core->GetGC();
AbcGen gen(gc);
AbcGen *pgen;
#if defined(VMCFG_AOT)
pgen = (!(this->init)) || (!(this->init->isCompiledMethod())) ? &gen : 0;
#else
pgen = &gen;
#endif
TraitsBindings* tb = _buildTraitsBindings(toplevel, pgen);
this->genInitBody(toplevel, gen);
// leave m_tmref as empty, we don't need it yet
uint32_t slotAreaSize = 0;
uint32_t slotAreaCount = 0;
switch (posType())
{
case TRAITSTYPE_INTERFACE:
case TRAITSTYPE_INSTANCE:
case TRAITSTYPE_CLASS:
case TRAITSTYPE_SCRIPT:
case TRAITSTYPE_ACTIVATION:
case TRAITSTYPE_CATCH:
computeSlotAreaCountAndSize(tb, slotAreaCount, slotAreaSize);
m_totalSize = getSizeOfInstance() + slotAreaSize;
break;
case TRAITSTYPE_NVA:
case TRAITSTYPE_RT:
m_totalSize = getSizeOfInstance();
break;
}
AvmAssert(m_totalSize >= m_sizeofInstance);
if (m_needsHashtable || (base && base->base && base->m_hashTableOffset && !isXMLType()))
{
// round up total size to multiple of pointer size.
m_totalSize = ((m_totalSize+(sizeof(uintptr_t)-1))&~(sizeof(uintptr_t)-1));
m_hashTableOffset = m_totalSize;
m_totalSize += sizeof(InlineHashtable);
AvmAssert(builtinType == BUILTIN_boolean ? true : (m_hashTableOffset & 3) == 0);
AvmAssert((m_hashTableOffset & (sizeof(uintptr_t)-1)) == 0);
AvmAssert((m_totalSize & (sizeof(uintptr_t)-1)) == 0);
}
// make sure all the methods have resolved types
{
const TraitsBindings::BindingMethodInfo* tbm = tb->getMethods();
const TraitsBindings::BindingMethodInfo* tbm_end = tbm + tb->methodCount;
for ( ; tbm < tbm_end; ++tbm)
{
// don't assert: could be null if only one of a get/set pair is implemented
//AvmAssert(tbm->f != NULL);
if (tbm->f != NULL)
{
tbm->f->resolveSignature(toplevel);
}
}
}
if (this->init != NULL)
this->init->resolveSignature(toplevel);
bool legal = true;
TraitsBindingsp tbbase = tb->base; // might be null
if (tbbase && tbbase->methodCount > 0)
{
// check concrete overrides
const TraitsBindings::BindingMethodInfo* basetbm = tbbase->getMethods();
const TraitsBindings::BindingMethodInfo* basetbm_end = basetbm + tbbase->methodCount;
const TraitsBindings::BindingMethodInfo* tbm = tb->getMethods();
for ( ; basetbm < basetbm_end; ++basetbm, ++tbm)
{
if (basetbm->f != NULL && basetbm->f != tbm->f)
legal &= tb->checkOverride(core, basetbm->f, tbm->f);
}
}
if (legal && !this->isInterface())
{
legal &= tb->checkLegalInterfaces(core);
}
if (!legal)
{
AvmAssert(!linked);
Multiname qname(ns(), name());
if (toplevel)
toplevel->throwVerifyError(kIllegalOverrideError, core->toErrorString(&qname), core->toErrorString(this));
AvmAssert(!"unhandled verify error");
}
tb->buildSlotDestroyInfo(gc, m_slotDestroyInfo, slotAreaCount, slotAreaSize);
linked = true;
}
#ifdef VMCFG_AOT
static inline void hookUpActivationTraitsInitMethodForTraitsMethod(AvmCore* core, Toplevel *toplevel, MethodInfo* m)
{
AvmAssert(m->needActivation());
const AOTInfo* aotInfo = m->pool()->aotInfo;
Traits* activationTraits = m->activationTraits();
AvmAssert(activationTraits != NULL);
AvmAssert(aotInfo->activationTraits != NULL);
AvmAssert(m->method_id() < aotInfo->nActivationTraits);
AvmAssert(aotInfo->activationTraits[m->method_id()] == activationTraits);
AvmAssert(aotInfo->activationTraitsInitFunctions != NULL);
// See comment in initActivationTraits about why this can be called more than once per Traits
if (activationTraits->init == NULL) {
if (aotInfo->activationTraitsInitFunctions[m->method_id()]) {
NativeMethodInfo compiledMethodInfo;
compiledMethodInfo.thunker = aotThunker;
compiledMethodInfo.handler.function = aotInfo->activationTraitsInitFunctions[m->method_id()];
activationTraits->init = new (core->gc) MethodInfo(MethodInfo::kInitMethodStub, activationTraits, &compiledMethodInfo);
}
m->resolveActivation(toplevel);
}
}
void Traits::initActivationTraits(Toplevel *toplevel)
{
// Note: this can be called multiple times per Traits from initScript, which must call this in case it's needed
// but is itself called once per Toplevel
const uint8_t* pos = traitsPosStart();
if (this->init->needActivation()) {
MethodInfo* m = this->init;
hookUpActivationTraitsInitMethodForTraitsMethod(core, toplevel, this->init);
}
NameEntry ne;
const uint32_t nameCount = pos ? AvmCore::readU30(pos) : 0;
for (uint32_t i = 0; i < nameCount; i++)
{
ne.readNameEntry(pos);
switch (ne.kind)
{
case TRAIT_Slot:
case TRAIT_Const:
case TRAIT_Class:
break;
case TRAIT_Method:
case TRAIT_Getter:
case TRAIT_Setter:
{
MethodInfo* m = pool->getMethodInfo(ne.id);
if (m->needActivation()) {
hookUpActivationTraitsInitMethodForTraitsMethod(core, toplevel, m);
}
break;
}
default:
// unsupported traits type -- can't happen, caught in AbcParser::parseTraits
AvmAssert(0);
break;
}
} // for i
}
#endif
// static
bool Traits::isMachineCompatible(const Traits* a, const Traits* b)
{
return (a == b) ||
// *, Object, and Void are each represented as Atom
((!a || a->builtinType == BUILTIN_object || a->builtinType == BUILTIN_void) &&
(!b || b->builtinType == BUILTIN_object || b->builtinType == BUILTIN_void)) ||
// all other non-pointer types have unique representations
(a && b && !a->isMachineType() && !b->isMachineType());
}
#if VMCFG_METHOD_NAMES
Stringp Traits::format(AvmCore* core, bool includeAllNamespaces) const
{
if (name() != NULL)
return Multiname::format(core, ns(), name(), false, !includeAllNamespaces);
return core->concatStrings(core->newConstantStringLatin1("Traits@"),
core->formatAtomPtr((uintptr)this));
}
#endif
void Traits::genDefaultValue(uint32_t value_index, uint32_t slot_id, const Toplevel* toplevel, Traitsp slotType, CPoolKind kind, AbcGen& gen) const
{
// toplevel actually can be null, when resolving the builtin classes...
// but they should never cause verification errors in functioning builds
//AvmAssert(toplevel != NULL);
Atom value = pool->getLegalDefaultValue(toplevel, value_index, kind, slotType);
switch (Traits::getBuiltinType(slotType))
{
case BUILTIN_any:
case BUILTIN_object:
{
if (value == 0)
return;
break;
}
case BUILTIN_number:
{
if (AvmCore::number_d(value) == 0)
return;
break;
}
case BUILTIN_boolean:
{
AvmAssert((uintptr_t(falseAtom)>>3) == 0);
if (value == falseAtom)
return;
AvmAssert(value == trueAtom);
break;
}
case BUILTIN_uint:
case BUILTIN_int:
{
if (value == (zeroIntAtom))
return;
break;
}
case BUILTIN_namespace:
case BUILTIN_string:
default:
{
if (AvmCore::isNull(value))
return;
break;
}
}
gen.getlocalN(0);
if (value == undefinedAtom)
gen.pushundefined();
else if (AvmCore::isNull(value))
gen.pushnull();
else if (value == core->kNaN)
gen.pushnan();
else if (value == trueAtom)
gen.pushtrue();
else
gen.pushconstant(kind, value_index);
gen.setslot(slot_id);
}
void Traits::genInitBody(const Toplevel* toplevel, AbcGen& gen)
{
// if initialization code gen is required, create a new method body and write it to traits->init->body_pos
if (gen.size() == 0)
return;
MMgc::GC* gc = core->GetGC();
AbcGen newMethodBody(gc, uint32_t(16 + gen.size())); // @todo 16 is a magic value that was here before I touched the code -- I don't know the significance
// insert body preamble
if (this->init)
{
const uint8_t* pos = this->init->abc_body_pos();
if (!pos)
toplevel->throwVerifyError(kCorruptABCError);
uint32_t maxStack = AvmCore::readU30(pos);
// the code we're generating needs at least 2
maxStack = maxStack > 1 ? maxStack : 2;
newMethodBody.writeInt(maxStack); // max_stack
newMethodBody.writeInt(AvmCore::readU30(pos)); //local_count
newMethodBody.writeInt(AvmCore::readU30(pos)); //init_scope_depth
newMethodBody.writeInt(AvmCore::readU30(pos)); //max_scope_depth
// skip real code length
uint32_t code_length = AvmCore::readU30(pos);
// if first instruction is OP_constructsuper keep it as first instruction
if (*pos == OP_constructsuper)
{
gen.getBytes().insert(0, OP_constructsuper);
// don't invoke it again later
pos++;
code_length--;
}
gen.abs_jump(pos, code_length);
// this handles an obscure case: we have already resolved the signature for this
// and have a MethodSignature cached, but we just (potentially) increased the value of
// max_stack above. This updates the cached value of max_stack (iff we have
// a MethodSignature cached for this->init)
this->init->update_max_stack(maxStack);
}
else
{
// make one
this->init = new (gc) MethodInfo(MethodInfo::kInitMethodStub, this);
newMethodBody.writeInt(2); // max_stack
newMethodBody.writeInt(1); //local_count
newMethodBody.writeInt(1); //init_scope_depth
newMethodBody.writeInt(1); //max_scope_depth
gen.returnvoid();
}
newMethodBody.writeInt((uint32_t)gen.size()); // code length
newMethodBody.writeBytes(gen.getBytes());
// no exceptions, when we jump to the real code, we'll read the exceptions for that code
newMethodBody.writeInt(0);
// the verifier and interpreter don't read the activation traits so stop here
uint8_t* newBytes = (uint8_t*) gc->Alloc(newMethodBody.size());
VMPI_memcpy(newBytes, newMethodBody.getBytes().getData(), newMethodBody.size());
this->init->set_abc_body_pos_wb(gc, newBytes);
}
void Traits::destroyInstance(ScriptObject* obj) const
{
AvmAssert(linked);
InlineHashtable* ht = m_hashTableOffset ? obj->getTableNoInit() : NULL;
// start by clearing native space to zero (except baseclasses)
union {
char* p_8;
uint32_t* p;
};
p_8 = (char*)obj + sizeof(AvmPlusScriptableObject);
AvmAssert((uintptr_t(p) % sizeof(uint32_t)) == 0);
if (!m_slotDestroyInfo.test(0))
{
AvmAssert(m_slotDestroyInfo.cap() == 1);
AvmAssert(m_totalSize >= (sizeof(AvmPlusScriptableObject) + (ht ? sizeof(InlineHashtable) : 0)));
uint32_t sizeToZero = m_totalSize - (sizeof(AvmPlusScriptableObject) + (ht ? sizeof(InlineHashtable) : 0));
AvmAssert((sizeToZero % sizeof(uint32_t)) == 0); // we assume all sizes are multiples of sizeof(uint32_t)
// no RCObjects, so just zero it all... my, that was easy
VMPI_memset(p, 0, sizeToZero);
}
else
{
uint32_t sizeToZero = m_sizeofInstance - uint32_t(sizeof(AvmPlusScriptableObject));
AvmAssert((sizeToZero % sizeof(uint32_t)) == 0); // we assume all sizes are multiples of sizeof(uint32_t)
VMPI_memset(p, 0, sizeToZero);
p += (sizeToZero / sizeof(uint32_t));
AvmAssert(m_slotDestroyInfo.cap() >= 1);
AvmAssert((uintptr_t(p) % sizeof(uint32_t)) == 0);
const uint32_t slotAreaSize = getSlotAreaSize();
const uint32_t bitsUsed = slotAreaSize / sizeof(uint32_t); // not sizeof(Atom)!
for (uint32_t bit = 1; bit <= bitsUsed; bit++)
{
if (m_slotDestroyInfo.test(bit))
{
#ifdef AVMPLUS_64BIT
AvmAssert((uintptr_t(p) & 7) == 0); // we had better be on an 8-byte boundary...
#endif
Atom a = *(const Atom*)p;
RCObject* rc = NULL;
if (atomKind(a) <= kNamespaceType)
{
rc = (RCObject*)atomPtr(a);
if (rc)
{
AvmAssert(GC::GetGC(obj)->IsRCObject(rc));
rc->DecrementRef();
}
}
}
*p++ = 0;
}
}
// finally, zap the hashtable (if any)
if(ht)
{
ht->destroy();
}
//for DictionaryObject also zero out the
//hashtable pointer stored at the offset address;
if(isDictionary)
{
union {
char* p_8;
uintptr_t* ptr;
};
p_8 = (char*)obj + m_hashTableOffset;
*ptr = 0;
}
}
Stringp Traits::formatClassName()
{
#ifdef VMCFG_CACHE_GQCN
if (_fullname != NULL)
return _fullname;
#endif
Multiname qname(ns(), name());
qname.setQName();
StringBuffer buffer(core);
buffer << qname;
int length = buffer.length();
if (length && buffer.c_str()[length-1] == '$')
{
length--;
}
#ifndef VMCFG_CACHE_GQCN
Stringp _fullname;
#endif
_fullname = core->newStringUTF8(buffer.c_str(), length);
return _fullname;
}
Binding Traits::getOverride(TraitsBindingsp basetb, Namespacep ns, Stringp name, int tag, const Toplevel* toplevel) const
{
Binding baseBinding = BIND_NONE;
if (base)
{
const Namespacep lookupNS = (protectedNamespace == ns && base->protectedNamespace) ? (Namespacep)base->protectedNamespace : ns;
AvmAssert(basetb != NULL);
baseBinding = basetb->findBinding(name, lookupNS);
}
const BindingKind baseBindingKind = AvmCore::bindingKind(baseBinding);
const TraitKind kind = TraitKind(tag & 0x0f);
// some extra-picky compilers will complain about the values being out of
// range for the comparison (if we use "kind") even with explicit int casting.
// so recycle the expression for the assert.
AvmAssert((tag & 0x0f) >= 0 && (tag & 0x0f) < TRAIT_COUNT);
static const uint8_t kDesiredKind[TRAIT_COUNT] =
{
BKIND_NONE, // TRAIT_Slot
BKIND_METHOD, // TRAIT_Method
BKIND_GET, // TRAIT_Getter
BKIND_SET, // TRAIT_Setter
BKIND_NONE, // TRAIT_Class
BKIND_NONE, // TRAIT_Function
BKIND_NONE // TRAIT_Const
};
const BindingKind desiredKind = BindingKind(kDesiredKind[kind]);
const uint8_t dkMask = uint8_t(1 << desiredKind);
// given baseBindingKind, what are legal desiredKinds?
static const uint8_t kLegalBaseKinds[8] =
{
(1<<BKIND_METHOD) | (1<<BKIND_GET) | (1<<BKIND_SET), // BKIND_NONE
(1<<BKIND_METHOD), // BKIND_METHOD
0, // BKIND_VAR
0, // BKIND_CONST
0, // unused
(1<<BKIND_GET) | (1<<BKIND_SET), // BKIND_GET
(1<<BKIND_GET) | (1<<BKIND_SET), // BKIND_SET
(1<<BKIND_GET) | (1<<BKIND_SET) // BKIND_GETSET
};
if ((kLegalBaseKinds[baseBindingKind] & dkMask) == 0)
goto failure;
// given baseBindingKind, which desiredKinds *require* override?
static const uint8_t kOverrideRequired[8] =
{
0, // BKIND_NONE
(1<<BKIND_METHOD), // BKIND_METHOD
0, // BKIND_VAR
0, // BKIND_CONST
0, // unused
(1<<BKIND_GET), // BKIND_GET
(1<<BKIND_SET), // BKIND_SET
(1<<BKIND_GET) | (1<<BKIND_SET) // BKIND_GETSET
};
if (((kOverrideRequired[baseBindingKind] & dkMask) ? ATTR_override : 0) != (tag & ATTR_override))
goto failure;
return baseBinding;
failure:
#ifdef AVMPLUS_VERBOSE
if (pool->isVerbose(VB_traits))
core->console << "illegal override in "<< this << ": " << Multiname(ns,name) <<"\n";
#endif
if (toplevel)
toplevel->throwVerifyError(kIllegalOverrideError, toplevel->core()->toErrorString(Multiname(ns,name)), toplevel->core()->toErrorString(this));
AvmAssert(!"unhandled verify error");
return BIND_NONE;
}
TraitsBindings* FASTCALL Traits::_getTraitsBindings()
{
AvmAssert(this->linked);
// note: TraitsBindings are always built the first time in resolveSignature; this is only
// executed for subsequent re-buildings. Thus we pass NULL for toplevel (it's only used
// for verification errors, but those will have been caught prior to this) and for
// abcGen (since it only needs to be done once).
TraitsBindings* tb = _buildTraitsBindings(/*toplevel*/NULL, /*abcGen*/NULL);
return tb;
}
TraitsMetadata* FASTCALL Traits::_getTraitsMetadata()
{
AvmAssert(this->linked);
TraitsMetadata* tm = _buildTraitsMetadata();
return tm;
}
// Count supertypes in the given list, like strlen().
static uint32_t countSupertypes(Traits** list)
{
uint32_t n = 0;
for (Traits** t = list; *t != NULL; t++)
n++;
return n;
}
// Initialize the m_primary_supertypes array by copying down the entries
// from our base class (if it exists), and adding this traits if there is room.
void Traits::build_primary_supertypes()
{
MMGC_STATIC_ASSERT(offsetof(Traits, m_primary_supertypes) + sizeof(m_primary_supertypes) < 256);
MMGC_STATIC_ASSERT(offsetof(Traits, m_supertype_cache) < 256);
// compute m_supertype_offset and fill in m_primary_supertypes
if (!base) {
// class roots and interfaces
m_supertype_offset = isInterface() ? offsetof(Traits, m_supertype_cache) : offsetof(Traits, m_primary_supertypes);
WB(core->gc, this, &m_primary_supertypes[0], this);
} else {
// single inherited classes
AvmAssert(!isInterface());
for (int i=0; i < MAX_PRIMARY_SUPERTYPE; i++)
WB(core->gc, this, &m_primary_supertypes[i], base->m_primary_supertypes[i]);
size_t off = base->m_supertype_offset;
if (off != offsetof(Traits, m_supertype_cache) &&
(off += sizeof(Traits*)) - offsetof(Traits, m_primary_supertypes) < sizeof(m_primary_supertypes)) {
AvmAssert(off == (uint8_t) off);
m_supertype_offset = uint8_t(off);
WB(core->gc, this, (Traits*)(uintptr_t(this)+off), this);
} else {
// Inheritance is too deep to add this traits to m_primary_supertypes.
// Make this traits "secondary", set m_supertype_offset to the cache.
m_supertype_offset = offsetof(Traits, m_supertype_cache);
}
}
}
// Initialize the m_secondary_supertypes array as follows:
// * if this traits adds a new interface, create a new list with all
// the entries from the base class, plus the base class itself,
// plus the new interfaces
// * if we do not add new interfaces and there's no base class,
// use the empty list.
// * if there is a base class, and the base class is primary, just
// copy base->m_secondary_supertypes (containing any base interfaces)
// * otherwise create a new secondary_supertypes list with everything
// from the base class, plus the base class itself. Install this list
// back in the base class so it can be shared by other leaf classes.
void Traits::build_secondary_supertypes()
{
MMgc::GC* gc = core->GetGC();
List<Traitsp, LIST_GCObjects> seen(gc);
uint32_t count;
if (!isInstanceType() || (count = countNewInterfaces(seen)) == 0) {
// no new interfaces, attempt to share the base type's secondary list
if (!base) {
this->m_secondary_supertypes = core->_emptySupertypeList;
} else {
Traits** base_list = base->m_secondary_supertypes;
// If we require base in our secondary_supertypes list, so will other
// sibling leaf types. Try to share the seconary_supertypes list by
// inserting base at position 0.
if (base->isPrimary() || base_list[0] == base) {
// just copy the base list.
this->m_secondary_supertypes = base_list;
} else {
// must prepend base to base_list, save the copy on this type and base.
count = countSupertypes(base_list);
Traits** list = allocSupertypeList(gc, count + 1);
WB(gc, list, list, base);
for (uint32_t i=0; i < count; i++)
WB(gc, list, list+i+1, base_list[i]);
base->m_secondary_supertypes = list;
this->m_secondary_supertypes = list;
}
}
} else {
// this type implements new interfaces so we need a new list
this->m_implementsNewInterfaces = true;
if (base && !base->isPrimary() && base->m_secondary_supertypes[0] != base) {
seen.add(base);
count++;
}
Traits** list;
uint32_t baseCount = base ? countSupertypes(base->m_secondary_supertypes) : 0;
if (baseCount > 0) {
uint32_t total = count + baseCount;
list = allocSupertypeList(gc, total);
for (Traits **d = list, **s = base->m_secondary_supertypes; *s != NULL; s++, d++)
WB(gc, list, d, *s);
} else {
list = allocSupertypeList(gc, count);
}
this->m_secondary_supertypes = list;
for (uint32_t i=0; i < count; i++) {
WB(gc, list, list+baseCount+i, seen[i]);
}
}
#ifdef DEBUG
// sanity check to make sure we don't have any duplicate supertypes.
List<Traitsp, LIST_GCObjects> supertypes(gc);
for (int i = 0; i < MAX_PRIMARY_SUPERTYPE; i++) {
Traits* t = m_primary_supertypes[i];
if (t != NULL) {
if (supertypes.indexOf(t) != -1) {
core->console << "t " << this << " dup primary " << t << "\n";
AvmAssert(false);
}
supertypes.add(t);
}
}
for (Traits** st = m_secondary_supertypes; *st != NULL; st++) {
Traits* t = *st;
if (supertypes.indexOf(t) != -1) {
core->console << "t " << this << " dup secondary " << t << "\n";
AvmAssert(false);
}
supertypes.add(t);
}
#endif
}
// search interfaces and bases that didn't fit in m_primary_supertypes,
// and cache positive/negative results
bool Traits::secondary_subtypeof(Traits* t)
{
for (Traits** s = m_secondary_supertypes; *s != NULL; s++) {
if (t == *s) {
m_supertype_cache = t;
return true;
}
}
m_supertype_neg_cache = t;
return false;
}
// create a new supertype list of the given length
Traits** Traits::allocSupertypeList(GC* gc, uint32_t size)
{
return (Traits**) gc->Alloc((size+1) * sizeof(Traits*), MMgc::GC::kZero);
}
}