/* [<][>][^][v][top][bottom][index][help] */
/* ***** 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 ***** */
#ifndef __avmplus_Traits__
#define __avmplus_Traits__
namespace avmplus
{
// Note: we rely on being able to store this in 3 bits.
enum TraitsPosType
{
TRAITSTYPE_INSTANCE = 0, // instance info in abc (non-interface)
TRAITSTYPE_CLASS = 1, // class info in abc
TRAITSTYPE_SCRIPT = 2, // script info in abc
TRAITSTYPE_CATCH = 3, // synthetic catch type
TRAITSTYPE_ACTIVATION = 4, // activation info from abc
TRAITSTYPE_NVA = 5, // null/void/any -- traitsPos will always be null
TRAITSTYPE_RT = 6, // Traits defined at runtime, e.g. instantiated parameterized types
TRAITSTYPE_INTERFACE = 7 // instance info in abc (interface)
};
const uint32_t NOT_DERIVED_OR_XML_MASK =
// commented out, why?
// (1<<BUILTIN_array) |
// (1<<BUILTIN_date) |
// (1<<BUILTIN_regexp) |
(1<<BUILTIN_boolean) |
(1<<BUILTIN_class) |
(1<<BUILTIN_function) |
(1<<BUILTIN_int) |
(1<<BUILTIN_methodClosure) |
(1<<BUILTIN_namespace) |
(1<<BUILTIN_null) |
(1<<BUILTIN_number) |
(1<<BUILTIN_object) |
(1<<BUILTIN_qName) |
(1<<BUILTIN_string) |
(1<<BUILTIN_uint) |
(1<<BUILTIN_void) |
(1<<BUILTIN_xmlList) |
(1<<BUILTIN_xml);
const uint32_t MACHINE_TYPE_MASK = (1<<BUILTIN_object) | (1<<BUILTIN_void) | (1<<BUILTIN_int) | (1<<BUILTIN_uint) | (1<<BUILTIN_boolean) | (1<<BUILTIN_number);
const uint32_t NUMERIC_TYPE_MASK = (1<<BUILTIN_int) | (1<<BUILTIN_uint) | (1<<BUILTIN_number);
const uint32_t XML_TYPE_MASK = (1<<BUILTIN_xml) | (1<<BUILTIN_xmlList);
#ifdef VMCFG_AOT
const uint32_t SSTOBJECT_TYPE_MASK = ~ ((1<<BUILTIN_int) | (1<<BUILTIN_uint) | (1<<BUILTIN_number) | (1<<BUILTIN_boolean) | (1<<BUILTIN_any) | (1<<BUILTIN_object) | (1<<BUILTIN_string) | (1<<BUILTIN_namespace));
const uint32_t SSTATOM_TYPE_MASK = (1<<BUILTIN_object) | (1<<BUILTIN_any);
#endif
typedef const uint8_t* TraitsPosPtr;
// Note: we rely on this being <= 8 entries.
enum SlotStorageType
{
// we rely on these 4 being first, so we can do <= SST_scriptobject in isAtomOrRCObjectSlot.
// SST_atom is most frequently encountered, so value of zero is best
SST_atom,
SST_string,
SST_namespace,
SST_scriptobject,
SST_int32,
SST_uint32,
SST_bool32,
SST_double
};
bool isAtomOrRCObjectSlot(SlotStorageType sst);
class TraitsBindings : public QCachedItem
{
friend class Traits;
friend class StTraitsBindingsIterator;
private:
struct SlotInfo
{
Traitsp type;
uint32_t offsetAndSST; // lower 3 bits: SlotStorageType, upper 29 bits: offset div 4
#ifdef AVMPLUS_64BIT
uint32_t padding; // alignment to power-of-two boundary is a Good Thing here
#endif
SlotStorageType sst() const; // get just the storage type of this slot
uint32_t offset() const; // get just the offset of this slot
};
struct BindingMethodInfo
{
MethodInfo* f;
};
private:
TraitsBindings(Traits* _owner,
TraitsBindingsp _base,
MultinameHashtable* _bindings,
uint32_t _slotCount,
uint32_t _methodCount);
public:
static const uint32_t MAX_SLOT_OFFSET = (1U << 31) - 1;
public:
static TraitsBindings* alloc(MMgc::GC* gc, Traits* _owner, TraitsBindingsp _base, MultinameHashtable* _bindings, uint32_t slotCount, uint32_t methodCount);
void buildSlotDestroyInfo(MMgc::GC* gc, FixedBitSet& slotDestroyInfo, uint32_t slotAreaCount, uint32_t sizeOfSlotArea) const;
Traitsp getSlotTraits(uint32_t i) const;
uint32_t getSlotOffset(uint32_t i) const;
// if you need SST you probably want offset too
SlotStorageType calcSlotAddrAndSST(uint32_t i, void* pin, void*& pout) const;
MethodInfo* getMethod(uint32_t i) const;
Binding findBinding(Stringp key) const;
Binding findBinding(Stringp name, Namespacep ns) const;
Binding findBinding(Stringp name, NamespaceSetp nsset) const;
Binding findBindingAndDeclarer(const Multiname& mn, Traitsp& declarer) const;
// note: if you are just doing a single iteration thru a single TraitsBindings,
// it's more efficient (and easier) to use StTraitsBindingsIterator instead.
int32_t next(int32_t index) const;
Stringp keyAt(int32_t index) const;
Namespacep nsAt(int32_t index) const;
Binding valueAt(int32_t index) const;
private:
SlotInfo* getSlots();
const SlotInfo* getSlots() const;
BindingMethodInfo* getMethods();
const BindingMethodInfo* getMethods() const;
void setSlotInfo(uint32_t i, Traits* t, SlotStorageType sst, uint32_t offset);
void setMethodInfo(uint32_t i, MethodInfo* f);
bool checkOverride(AvmCore* core, MethodInfo* virt, MethodInfo* over) const;
bool checkLegalInterfaces(AvmCore* core) const;
void fixOneInterfaceBindings(Traitsp ifc);
// ------------------------ DATA SECTION BEGIN
public: const Traitsp owner;
public: const TraitsBindingsp base;
private: MultinameHashtable* const m_bindings;
private: Traits** m_interfaces;
public: const uint32_t slotCount; // including slots in our base classes
public: const uint32_t methodCount; // including methods in our base classes
private: uint32_t m_slotSize; // size of slot area in bytes, including base classes
// plus extra at end
// ------------------------ DATA SECTION END
};
// NOTE: caller must check for null key, eg,
//
// StTraitsBindingsIterator iter(mnht);
// while (iter.next()) {
// if (!iter.key()) continue;
// .. rest of loop ..
// }
//
class StTraitsBindingsIterator : public StMNHTIterator
{
private:
TraitsBindingsp const volatile _tb; // kept just to ensure it doesn't get collected -- must be volatile!
public:
StTraitsBindingsIterator(TraitsBindingsp tb);
};
class TraitsMetadata : public QCachedItem
{
friend class Traits;
public:
typedef const uint8_t* MetadataPtr;
private:
TraitsMetadata(TraitsMetadatap _base, PoolObject* _residingPool, MetadataPtr _metadata_pos, uint32_t _slotCount, uint32_t _methodCount);
public:
MetadataPtr getMetadataPos(PoolObject*& residingPool) const;
MetadataPtr getSlotMetadataPos(uint32_t i, PoolObject*& residingPool) const;
MetadataPtr getMethodMetadataPos(uint32_t i, PoolObject*& residingPool) const;
// ------------------------ DATA SECTION BEGIN
public: const TraitsMetadatap base;
private: PoolObject* const residingPool;
public: const uint32_t slotCount; // including slots in our base classes
public: const uint32_t methodCount; // including methods in our base classes
private: MetadataPtr metadataPos;
private: MetadataPtr* slotMetadataPos;
private: MetadataPtr* methodMetadataPos;
// ------------------------ DATA SECTION END
};
typedef ClassClosure* (*CreateClassClosureProc)(VTable*);
/**
* Traits objects describe the layout of objects. Traits are
* used to describe a variety of things in the VM, such as
* classes, global objects, and activation objects.
*
* A Traits object contains a hash table with the names of
* all properties and methods. Each name maps to a binding.
*
* There are several types of binding: binding to a slot,
* binding to an accessor, binding to a virtual function,
* binding to a final function.
*/
class Traits : public MMgc::GCObject
{
friend class TraitsBindings; // for m_sizeofInstance
friend class InterfaceIterator;
#if defined FEATURE_NANOJIT
friend class CodegenLIR;
#endif
public:
uint16_t getSizeOfInstance() const;
uint32_t getTotalSize() const;
uint32_t getHashtableOffset() const;
// in bytes. includes size for all base classes too.
uint32_t getExtraSize() const;
// sadly, it's still more efficient to stash this in Traits itself, as it's nontrivial to recover when
// we rebuild the TraitMethodInfo.
void setMetadataPos(const byte* pos);
private:
// in bytes. size of the slots not included in the size of instance. Includes size of slots for bases classes that are not native.
uint32_t getSlotAreaSize() const;
void computeSlotAreaCountAndSize(TraitsBindings* tb, uint32_t& slotCount, uint32_t& size) const;
uint32_t computeSlotAreaStart(uint32_t nPointerSlots, uint32_t n32BitNonPointerSlots, uint32_t n64BitNonPointerSlots) const;
void buildBindings(TraitsBindingsp basetb,
MultinameHashtable* bindings,
uint32_t& slotCount,
uint32_t& methodCount,
uint32_t& n32BitNonPointerSlots,
uint32_t& n64BitNonPointerSlots,
const Toplevel* toplevel) const;
uint32_t finishSlotsAndMethods(TraitsBindingsp basetb,
TraitsBindings* tb,
const Toplevel* toplevel,
AbcGen* abcGen,
uint32_t n32BitNonPointerSlots,
uint32_t n64BitNonPointerSlots) const;
TraitsBindings* _buildTraitsBindings(const Toplevel* toplevel, AbcGen* abcGen);
TraitsMetadata* _buildTraitsMetadata();
TraitsPosPtr traitsPosStart() const;
TraitsPosPtr skipToInstanceInitPos(TraitsPosPtr pos) const;
Binding getOverride(TraitsBindingsp basetb, Namespacep ns, Stringp name, int tag, const Toplevel *toplevel) const;
void addVersionedBindings(MultinameHashtable* bindings,
Stringp name,
NamespaceSetp compat_nss,
Binding binding) const;
private:
/**
* Constructor for traits. base != NULL means inheritance.
*/
Traits(PoolObject* pool,
Traits* base,
uint16_t sizeofInstance,
uint16_t offsetofSlots,
TraitsPosPtr traitsPos,
TraitsPosType posType);
Traits* _newParameterizedTraits(Stringp name, Namespacep ns, Traits* _base);
public:
/**
* traits with base traits (inheritance)
*/
static Traits* newTraits(PoolObject* pool,
Traits *base,
uint16_t sizeofInstance,
uint16_t offsetofSlots,
TraitsPosPtr traitsPos,
TraitsPosType posType);
static Traits* newCatchTraits(const Toplevel* toplevel, PoolObject* pool, TraitsPosPtr traitsPos, Stringp name, Namespacep ns);
Traits* newParameterizedITraits(Stringp name, Namespacep ns);
Traits* newParameterizedCTraits(Stringp name, Namespacep ns);
public:
bool allowEarlyBinding() const;
private:
TraitsBindings* FASTCALL _getTraitsBindings();
TraitsMetadata* FASTCALL _getTraitsMetadata();
public:
TraitsBindingsp getTraitsBindings();
TraitsMetadatap getTraitsMetadata();
bool containsInterface(Traitsp t); // legacy name
bool subtypeof(Traitsp t);
static Traits** allocSupertypeList(MMgc:: GC*, uint32_t size);
private:
void build_primary_supertypes();
void build_secondary_supertypes();
bool FASTCALL secondary_subtypeof(Traitsp t); // slow path called by subtypeof()
bool isPrimary() const;
uint32_t countNewInterfaces(List<Traitsp, LIST_GCObjects>& seen);
void resolveSignaturesSelf(const Toplevel* toplevel);
public:
void genDefaultValue(uint32_t value_index, uint32_t slot_id, const Toplevel* toplevel, Traits* slotType, CPoolKind kind, AbcGen& gen) const;
void genInitBody(const Toplevel* toplevel, AbcGen& gen);
void resolveSignatures(const Toplevel* toplevel);
#ifdef VMCFG_AOT
void initActivationTraits(Toplevel *toplevel);
#endif
// convenient wrapper to check for null (returns "BUILTIN_any")
static BuiltinType getBuiltinType(const Traitsp t);
bool notDerivedObjectOrXML() const;
bool isMachineType() const;
bool isNumeric() const;
bool isXMLType() const;
bool isInterface() const; // true if this is an interface type
bool isInstanceType() const; // interface type or class instance type
#ifdef VMCFG_AOT
// returns true, if the slot storage type for this traits is ScriptObject*.
inline bool isSSTObject() const { return ((1<<builtinType) & SSTOBJECT_TYPE_MASK) != 0; }
inline static bool isSSTAtom(Traits* t) { return ((1<<getBuiltinType(t)) & SSTATOM_TYPE_MASK) != 0; }
#endif
TraitsPosType posType() const;
bool isResolved() const;
bool isActivationTraits() const;
bool needsHashtable() const;
void set_needsHashtable(bool v);
static bool isMachineCompatible(const Traits* a, const Traits* b);
void setCreateClassClosureProc(CreateClassClosureProc p);
CreateClassClosureProc getCreateClassClosureProc() const;
Stringp formatClassName();
void destroyInstance(ScriptObject *obj) const;
public:
#if VMCFG_METHOD_NAMES
Stringp format(AvmCore* core, bool includeAllNamespaces = false) const;
#endif
// call init_declaringScope for each method that we own. this should be
// called exactly once per Traits, *after* the Traits has been resolved.
void init_declaringScopes(const ScopeTypeChain* stc);
Namespacep ns() const;
Stringp name() const;
void set_names(Namespacep p_ns, Stringp p_name);
// this returns true iff we implement an interface that is not implemented by our parent.
// essential for efficient building of IMT thunks.
bool implementsNewInterfaces() const;
// Data Structures for fast subtype checking (edwsmith 11/17/09):
// subtypeof(S,T) is frequently used by the (often implicit) "is" and "coerce"
// operators and therefore is critical to optimize.
//
// We have the following data structure in each Traits:
// * primary_supertypes: a fixed-sized array of the first several base classes
// * MAX_PRIMARY_SUPERTYPE: a tunable policy constant, currently 8 from testing
// * secondary_supertypes: a possibly shared null terminated array of any
// remaining base classes, plus all interfaces implemented by this Traits
// * supertype_cache: a one-entry positive-hit cache
// * supertype_neg_cache: a one-entry negative-hit cache
// * supertype_offset: if this traits is a shallowly-inherited class, holds
// the byte offset of this traits in any subclass's primary_supertypes array.
// otherwise, holds the byte offset of supertype_cache.
//
// secondary_supertypes is shared as follows:
// * if a class is primary with no interfaces, we share an empty-list singleton
// * if a class is primary and doesn't add interfaces, list is shared with base class.
// * if a class is a leaf class and doesn't add interfaces, we share the list between
// the non-leaf base class and each other non-interface-adding leaf subclass
//
// supertype_offset cleverly encodes whether the traits is "primary" and expected
// to be in subclass's primary_supertypes list, or "secondary" and expected to be
// in the secondary_supertypes list. When it points to the cache, this type is
// a secondary type. Using this encoding allows step 1 (below) to be fast.
//
// primary_supertypes and secondary_supertypes are initialized in the Traits
// constructor. primary_supertypes is never modified. secondary_supertypes can
// be replaced once when a base class's list is shared by one or more leaf-subclasses.
// (see build_primary_supertypes() and build_secondary_supertype()).
//
// In a large app with thousands of classes and hundreds of interfaces,
// The combined memory of the shared secondary_supertypes lists is often
// <1% of the overall traits memory. The "big knob" affecting memory vs performance
// is the MAX_PRIMARY_SUPERTYPE policy constant.
//
// the operation subtypeof(S,T) that tests whether S is a subtype of T is:
// 1. if S[T.offset] == T, return true
// 2. if T.offset != offsetof(supertype_cache), return false
// 3. if S.supertype_neg_cache == T, return false
// 4. if S == T, return true
// 5. if T in S.secondary_supertypes, set S.supertype_cache == T and return true
// 6. else, set S.supertype_neg_cache == T and return false.
//
// These tests are ordered by frequency based on empirical testing.
//
// step 1 combines the ordinary base-class test when T is primary with
// the cache-hit test when T is secondary. Step 2 exits fast when T is primary.
//
// when T is a known primary class, steps 1 & 2 collapse into a simple
// load and compare.
//
// when T is a known interface or deeply inherited class, step 1 & 3 (both cache tests)
// become two loads+compares and step 2 can be skipped.
//
// Step 4 allows us to omit "self" from secondary_supertypes, which allows more
// sharing of that list. Step 4 can be skipped when T is a known interface.
// When T is final, only step 4 is needed; this is not common in practice but
// worth noting for jit-compilation.
//
// supertype_neg_cache is a hidden pointer because when S is *not* a subtype of
// T, then S and T are unrelated and we do not want the edge S->T to pin T in memory.
// However, in subtype_cache, and primary/secondary_supertypes[], entries *are* all
// supertypes of S and therefore the extra references do not uniquely pin T in memory.
//
// primary_supertypes[] does not have a write barrier because it is initialized in
// the constructor before other memory is allocated, like base.
//
// more ideas to optimize:
//
// The final search in step 5 is a linear null terminated search and is used
// well under 1% of the time in a range of apps tested. If necessary it could
// be further optimized by a) moving found items to the front; b) padding to
// the next power of two and using a quadradic probe; c) comparing several items
// at once using SIMD instructions (essentially a fast strchr operation on pointers)
//
// we could save one load per subtypeof() operation by moving all these
// structures to VTable. Emperically, there are about 1.2 VTables per Traits,
// so we'd expect to see a slight increase in overhead to save that load-per-test.
//
// to save memory or better handle deep class heirarchies we can tune MAX_PRIMARY_SUPERTYPE.
// the value 8 was picked after emperical testing on a range of Flash and Flex applications.
//
// The "base" field is redundant; if accessing base is not frequent we can
// encapsulate access and get it from either primary_supertypes or seconary_supertypes.
// (this can be O(1) for secondary_supertypes if base is always the 1st or 2nd entry).
// we can even walk up the class tree using secondary_supertypes and primary_supertypes
// in sequence. (best to encapsulate this in an iterator class).
// size of the fixed-sized primary supertype list
private: static const int MAX_PRIMARY_SUPERTYPE = 8;
// ------------------------ DATA SECTION BEGIN
public: AvmCore* const core; // @todo remove, can get from pool->core
public: Traits* const base; // Pointer to the base traits; that is, the traits of the base class
private: DWB(Traits*) m_supertype_cache; // 1-entry cache for subtypeof=true
private: MMgc::GCHiddenPointer<Traits*>
m_supertype_neg_cache; // 1-entry cache for subtypeof=false (hidden to avoid pinning)
private: Traits* m_primary_supertypes[MAX_PRIMARY_SUPERTYPE]; // contains the first several base classes, written with explicit WB's
private: DWB(Traits**) m_secondary_supertypes;
public: PoolObject* const pool; // The constant pool owning this definition. never null.
public: Traits* itraits; // if this type is a factory, itraits is non-null and points to the type created by this factory.
private: DRCWB(Namespacep) _ns; // The namespace of the class described by this traits object
private: DRCWB(Stringp) _name; // The name of the class described by this traits object
public: DRCWB(Namespacep) protectedNamespace; // protected namespace, if any
public: DWB(MethodInfo*) init; // not a call/init union b/c smart pointers and union's don't mix
private: CreateClassClosureProc m_createClassClosure;
private: const TraitsPosPtr m_traitsPos; // ptr into our ABC definition, depending on m_posType
private: const byte* metadata_pos;
private: FixedBitSet m_slotDestroyInfo;
private: DWB(MMgc::GCWeakRef*) m_tbref; // our TraitsBindings
private: DWB(MMgc::GCWeakRef*) m_tmref; // our TraitsMetadata
#ifdef VMCFG_CACHE_GQCN
private: DRCWB(Stringp) _fullname; // value returned by formatClassName
#endif
private: uint16_t m_sizeofInstance; // sizeof implementation class, e.g. ScriptObject, etc. < 64k. Not counting extra room for slots.
private: uint16_t m_offsetofSlots; // offset of first slot.
private: uint32_t m_hashTableOffset; // offset to our hashtable (or 0 if none)
private: uint32_t m_totalSize; // total size, including sizeofInstance + slots + hashtable
public: uint8_t builtinType; // BuiltinType enumeration -- only need 5 bits but stored in byte for faster access
private: const uint8_t m_posType; // TraitsPosType enumeration -- only need 3 bits but stored in byte for faster access
private: uint8_t m_bindingCapLog2; // if nonzero, log2 of the cap needed for bindings
private: uint8_t m_supertype_offset; // if this traits is primary, == offset in primary_supertypes array; otherwise == offset of supertype_cache
// 7 bits follow
private: uint32_t m_needsHashtable:1; // If true, the class needs a hash table. Typically true for dynamic classes, but will be false for XML
private: uint32_t linked:1; // set once signature types have been resolved */
public: uint32_t final:1; // set when the class cannot be extended */
public: uint32_t commonBase:1; // used for Verify::findCommonBase */
public: uint32_t isDictionary:1; // how we implement dictionary or strict style lookups
// If hasCustomConstruct is false, the JIT will early bind to the AS defined constructor.
public: uint32_t hasCustomConstruct:1; // does this type use the default ClassClosure::construct method or not?
// If the traits are for a type that implements its own construct method, m_immplementsNewInterfaces must be set to true.
private: uint32_t m_implementsNewInterfaces:1; // does this type implement interfaces not implemented by its base?
// ------------------------ DATA SECTION END
};
// iterates over each interface supertype of the given traits.
class InterfaceIterator
{
Traits** st;
public:
InterfaceIterator(Traits*);
InterfaceIterator(const TraitsBindings*);
bool hasNext(); // returns true if there are more interfaces
Traits* next(); // get the next one (moves iterator forward)
};
}
#endif /* __avmplus_Traits__ */