Exemplo n.º 1
0
    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;
    }
Exemplo n.º 2
0
// After loading an ABC and inserting scripts into the verify queue,
// process the work queues until they are empty.
void BaseExecMgr::verifyEarly(Toplevel* toplevel, AbcEnv* abc_env)
{
    GCList<MethodInfo> verifyQueue2(core->GetGC(), kListInitialCapacity);
    int verified;
    do {
        verified = 0;
        while (!verifyTraitsQueue.isEmpty()) {
            Traits* t = verifyTraitsQueue.removeFirst();
            t->resolveSignatures(toplevel);
            TraitsBindingsp td = t->getTraitsBindings();
            enqFunction(t->init);
            for (int i=0, n=td->methodCount; i < n; i++)
                enqFunction(td->getMethod(i));
        }
        while (!verifyFunctionQueue.isEmpty()) {
            MethodInfo* f = verifyFunctionQueue.removeLast();
            if (!isVerified(f)) {
                if (f->declaringTraits()->init != f && f->declaringScope() == NULL) {
                    verifyQueue2.add(f);
                    continue;
                }
                verified++;
                //console << "pre verify " << f << "\n";
                verifyMethod(f, toplevel, abc_env);
                setVerified(f);
                if (config.verifyonly)
                    f->_invoker = verifyOnlyInvoker;
            }
        }
        while (!verifyQueue2.isEmpty())
            verifyFunctionQueue.add(verifyQueue2.removeLast());
    } while (verified > 0);
}
Exemplo n.º 3
0
Arquivo: VTable.cpp Projeto: bsdf/trx
    uint64_t VTable::bytesUsed() const
    {
        uint64_t bytesUsed = sizeof(VTable);

        if(ivtable != NULL)
            bytesUsed += ivtable->bytesUsed();

        const TraitsBindingsp td = traits->getTraitsBindings();
        const uint32_t n = td->methodCount;
        const uint32_t baseMethodCount = base ? td->base->methodCount : 0;
        bytesUsed += td->methodCount*sizeof(MethodInfo*);

        for (uint32_t i=0; i < n; i++)
        {
            MethodInfo* method = td->getMethod(i);

            if (i < baseMethodCount && td->base && method == td->base->getMethod(i))
            {
                continue;
            }
            else if(method != NULL)
            {
                bytesUsed += method->bytesUsed();
            }
        }
        return bytesUsed;
    }
Exemplo n.º 4
0
	ClassClosure* FunctionClass::createEmptyFunction()
	{
		// invoke AS3 private static function emptyCtor, which returns an empty function.
		TraitsBindingsp t = traits()->getTraitsBindings();
		Binding b = t->findBinding(core()->internConstantStringLatin1("emptyCtor"));
		MethodEnv *f = vtable->methods[AvmCore::bindingToMethodId(b)];
		return (ClassClosure*)AvmCore::atomToScriptObject(f->coerceEnter(this->atom()));
	}
Exemplo n.º 5
0
	static void addBindings(MultinameHashtable* bindings, TraitsBindingsp tb, uint32_t flags)
	{
		if (!tb) return;
		if ((flags & TypeDescriber::HIDE_OBJECT) && !tb->base) return;
		addBindings(bindings, tb->base, flags);
		for (int32_t index = 0; (index = tb->next(index)) != 0; )
		{
			bindings->add(tb->keyAt(index), tb->nsAt(index), tb->valueAt(index));
		}
	}
Exemplo n.º 6
0
    ArrayObject *DomainObject::getVariables (Atom a)
    {
        ArrayObject *result = toplevel()->arrayClass->newArray(0);

        TraitsBindingsp traits = getTraits(a)->getTraitsBindings();
        int i = 0;
        while ((i = traits->next(i)) != 0) {
            Namespace *ns = traits->nsAt(i);
            Stringp name = traits->keyAt(i);
            Binding b = traits->valueAt(i);

            if (core()->isVarBinding(b) && ns->getType() == Namespace::NS_Public) {
                Atom nameAtom = core()->internString(name)->atom();
                result->push(&nameAtom, 1);
            }
        }

        return result;
    }
Exemplo n.º 7
0
 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
     }
 }
Exemplo n.º 8
0
    // 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;
    }
Exemplo n.º 9
0
Arquivo: VTable.cpp Projeto: bsdf/trx
    void VTable::resolveSignatures(ScopeChain* scope)
    {
        AvmAssert(scope != NULL);

        if( this->linked )
            return;
        // don't mark as resolved until the end of the function:
        // if traits->resolveSignatures() throws, we end up with the VTable as
        // "resolved" but the Traits not, which makes us crash in unpredictable ways.
        if (!traits->isResolved())
        {
            traits->resolveSignatures(toplevel());
            traits->setDeclaringScopes(scope->scopeTraits());
        }

#if defined(DEBUG) || defined(_DEBUG)
        // have to use local variables for CodeWarrior
        Traits* traitsBase = traits->base;
        Traits* baseTraits = base ? base->traits : 0;
        // make sure the traits of the base vtable matches the base traits
        AvmAssert((base == NULL && traits->base == NULL) || (base != NULL && traitsBase == baseTraits));
#endif // DEBUG

        AvmCore* core = traits->core;
        MMgc::GC* gc = core->GetGC();

        if (traits->init && !this->init)
        {
            this->init = makeMethodEnv(traits->init, scope);
        }

        // populate method table
        const TraitsBindingsp td = traits->getTraitsBindings();
        const TraitsBindingsp btd = td->base;
        for (uint32_t i = 0, n = td->methodCount; i < n; i++)
        {
            MethodInfo* method = td->getMethod(i);

            if (btd && i < btd->methodCount && method == btd->getMethod(i))
            {
                // inherited method
                // this->methods[i] = base->methods[i];
                WB(gc, this, &methods[i], base->methods[i]);
                continue;
            }

            // new definition
            if (method != NULL)
            {
                //this->methods[i] = new (gc) MethodEnv(method, this);
                WB(gc, this, &methods[i], makeMethodEnv(method, scope));
                continue;
            }
            #ifdef AVMPLUS_VERBOSE
            if (traits->pool->isVerbose(VB_traits))
            {
                // why would the compiler assign sparse disp_id's?
                traits->core->console << "WARNING: empty disp_id " << i << " on " << traits << "\n";
            }
            #endif
        }

        // this is done here b/c this property of the traits isn't set until the
        // Dictionary's ClassClosure is called
        if (base && base->traits->isDictionary())
            traits->set_isDictionary();

        traits->core->exec->notifyVTableResolved(this);

        linked = true;
    }
Exemplo n.º 10
0
	ScriptObject* TypeDescriber::describeTraits(Traitsp traits, uint32_t flags)
	{
		if (!(flags & INCLUDE_TRAITS))
			return NULL;
			
		AvmCore* core = m_toplevel->core();
		GC* gc = core->GetGC();
		TraitsBindingsp tb = traits->getTraitsBindings();
		TraitsMetadatap tm = traits->getTraitsMetadata();

		ScriptObject* o = new_object();

		ArrayObject* bases = NULL;
		ArrayObject* metadata = NULL;
		ArrayObject* interfaces = NULL;
		ArrayObject* methods = NULL;
		ArrayObject* accessors = NULL;
		ArrayObject* variables = NULL;
		ScriptObject* constructor = NULL;

		if (flags & INCLUDE_BASES)
		{
			metadata = new_array();
			PoolObject* class_mdpool;
			const uint8_t* class_md = tm->getMetadataPos(class_mdpool);
			if (class_md)
				addDescribeMetadata(metadata, class_mdpool, class_md);
		}
		
		if (flags & INCLUDE_BASES)
		{
			bases = new_array();
			for (Traitsp b = traits->base; b; b = b->base) 
				pushstr(bases, describeClassName(b));
		}
		
		if (flags & INCLUDE_INTERFACES)
		{
			interfaces = new_array();
			// our TraitsBindings only includes our own interfaces, not any we might have inherited, 
			// so walk the tree. there might be redundant interfaces listed in the inheritance, 
			// so use a list to remove dupes
			List<Traitsp> unique(gc);
			for (Traitsp b = traits; b; b = b->base) 
			{
				TraitsBindingsp tbi = b->getTraitsBindings();
				for (uint32_t i = 0; i < tbi->interfaceCapacity; ++i)
				{
					Traitsp ti = tbi->getInterface(i);
					if (ti && ti->isInterface && unique.indexOf(ti) < 0)
					{
						unique.add(ti);
						pushstr(interfaces, describeClassName(ti));
					}
				}
			}
		}

		// constructor
		if (flags & INCLUDE_CONSTRUCTOR)
		{
			AbstractFunction* initMethod = traits->init;
			if (initMethod && initMethod->param_count)
			{
				constructor = describeParams(initMethod);
			}
		}
		
		if (flags & (INCLUDE_ACCESSORS | INCLUDE_METHODS | INCLUDE_VARIABLES))
		{
			// recover slot/method metadata and method-declarer information.
			
			// make a flattened set of bindings so we don't have to check for overrides as we go.
			// This is not terribly efficient, but doesn't need to be.
			MultinameHashtable* mybind = new (gc) MultinameHashtable();
			addBindings(mybind, tb, flags);

			// Don't want interface methods, so post-process and wipe out any
			// bindings that were added
			for (uint32_t i = 0; i < tb->interfaceCapacity; ++i)
			{
				Traitsp ti = tb->getInterface(i);
				if (ti && ti->isInterface)
				{
					TraitsBindingsp tbi = ti->getTraitsBindings();
					for (int32_t index = 0; (index = tbi->next(index)) != 0; )
					{
						Stringp name = tbi->keyAt(index);
						Namespacep ns = tbi->nsAt(index);
						mybind->add(name, ns, BIND_NONE);
					}
				}
			}
			
			// yuck, replicate buggy behavior in FP9/10
			List<Namespacep> nsremoval(gc);
			if (flags & HIDE_NSURI_METHODS)
			{
				for (uint32_t i = 0; i < tb->interfaceCapacity; ++i)
				{
					Traitsp ti = tb->getInterface(i);
					// already did interfaces, don't need to do them again
					if (ti && !ti->isInterface)
					{
						TraitsBindingsp tbi = ti->getTraitsBindings();
						for (int32_t index = 0; (index = tbi->next(index)) != 0; )
						{
							Namespacep ns = tbi->nsAt(index);
							if (ns->getURI()->length() > 0 && nsremoval.indexOf(ns) < 0)
								nsremoval.add(ns);
						}
					}
				}
			}

			for (int32_t index = 0; (index = mybind->next(index)) != 0; )
			{
				Binding binding = mybind->valueAt(index);
				Stringp name = mybind->keyAt(index);
				Namespacep ns = mybind->nsAt(index);
				Stringp nsuri = ns->getURI();
				TraitsMetadata::MetadataPtr md1 = NULL;
				TraitsMetadata::MetadataPtr md2 = NULL;
				PoolObject* md1pool = NULL;
				PoolObject* md2pool = NULL;

				// We only display public members -- exposing private namespaces could compromise security.
				if (ns->getType() != Namespace::NS_Public) 
					continue;
				
				if ((flags & HIDE_NSURI_METHODS) && nsremoval.indexOf(ns) >= 0)
					continue;
			
				ScriptObject* v = new_object();

				const BindingKind bk = AvmCore::bindingKind(binding);
				switch (bk)
				{
					case BKIND_CONST:
					case BKIND_VAR:
					{
						if (!(flags & INCLUDE_VARIABLES))
							continue;

						const uint32_t slotID = AvmCore::bindingToSlotId(binding);
						const KVPair props[] = {
							{ kstrid_access, strAtom(str(bk == BKIND_CONST ? kstrid_readonly : kstrid_readwrite)) },
							{ kstrid_type, strAtom(describeClassName(tb->getSlotTraits(slotID))) },
						};
						setpropmulti(v, props, elem_count(props));
						if (!variables) variables = new_array();
						pushobj(variables, v);
						md1 = tm->getSlotMetadataPos(slotID, md1pool);
						break;
					}

					case BKIND_METHOD:
					{
						if (!(flags & INCLUDE_METHODS))
							continue;

						const uint32_t methodID = AvmCore::bindingToMethodId(binding);
						const AbstractFunction* af = tb->getMethod(methodID);

						Traitsp declaringTraits = af->declaringTraits;

						const KVPair props[] = {
							{ kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) },
							{ kstrid_returnType, strAtom(describeClassName(af->returnTraits())) },
							{ kstrid_parameters, objAtom(describeParams(af)) },
						};
						setpropmulti(v, props, elem_count(props));
						if (!methods) methods = new_array();
						pushobj(methods, v);
						md1 = tm->getMethodMetadataPos(methodID, md1pool);
						break;
					}
					
					case BKIND_GET:
					case BKIND_SET:
					case BKIND_GETSET:
					{
						if (!(flags & INCLUDE_ACCESSORS))
							continue;
							
						const uint32_t methodID = AvmCore::hasGetterBinding(binding) ?
													AvmCore::bindingToGetterId(binding) :
													AvmCore::bindingToSetterId(binding);

						const AbstractFunction* af = tb->getMethod(methodID);

						Traitsp declaringTraits = af->declaringTraits;

						Traitsp accessorType = AvmCore::hasGetterBinding(binding) ?
													af->returnTraits() :
													af->paramTraits(1);

						static const uint8_t bk2str[8] = 
						{
							uint8_t(kstrid_emptyString),	// BKIND_NONE
							uint8_t(kstrid_emptyString),	// BKIND_METHOD
							uint8_t(kstrid_emptyString),	// BKIND_VAR
							uint8_t(kstrid_emptyString),	// BKIND_CONST
							uint8_t(kstrid_emptyString),	// BKIND_ITRAMP
							uint8_t(kstrid_readonly),		// BKIND_GET
							uint8_t(kstrid_writeonly),		// BKIND_SET
							uint8_t(kstrid_readwrite)		// BKIND_GETSET
						};
						const KVPair props[] = {
							{ kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) },
							{ kstrid_access, strAtom(str(StringId(bk2str[bk]))) },
							{ kstrid_type, strAtom(describeClassName(accessorType)) },
						};
						setpropmulti(v, props, elem_count(props));
						if (AvmCore::hasGetterBinding(binding))
							md1 = tm->getMethodMetadataPos(AvmCore::bindingToGetterId(binding), md1pool);
						if (AvmCore::hasSetterBinding(binding))
							md2 = tm->getMethodMetadataPos(AvmCore::bindingToSetterId(binding), md2pool);
						if (!accessors) accessors = new_array();
						pushobj(accessors, v);
						break;
					}
					case BKIND_NONE:
						break;
				}

				ArrayObject* vm = NULL;
				if ((flags & INCLUDE_METADATA) && (md1 || md2))
				{
					vm = new_array();
					addDescribeMetadata(vm, md1pool, md1);
					addDescribeMetadata(vm, md2pool, md2);
				}
				const KVPair props[] = {
					{ kstrid_name, strAtom(name) },
					{ kstrid_uri, strAtom(nsuri->length() == 0 ? NULL : nsuri) },
					{ kstrid_metadata, objAtom(vm) },
				};
				setpropmulti(v, props, elem_count(props));
			}
		}

		const KVPair props[] = {
			{ kstrid_bases, objAtom(bases) },
			{ kstrid_interfaces, objAtom(interfaces) },
			{ kstrid_metadata, objAtom(metadata) },
			{ kstrid_accessors, objAtom(accessors) },
			{ kstrid_methods, objAtom(methods) },
			{ kstrid_variables, objAtom(variables) },
			{ kstrid_constructor, objAtom(constructor) },
		};
		setpropmulti(o, props, elem_count(props));

		return o;
	}
Exemplo n.º 11
0
    ScriptObject* TypeDescriber::describeTraits(Traitsp traits, uint32_t flags, Toplevel* toplevel)
    {
        if (!(flags & INCLUDE_TRAITS))
            return NULL;

        AvmCore* core = m_toplevel->core();
        GC* gc = core->GetGC();
        TraitsBindingsp tb = traits->getTraitsBindings();
        TraitsMetadatap tm = traits->getTraitsMetadata();

        ScriptObject* o = new_object();

        ArrayObject* bases = NULL;
        ArrayObject* metadata = NULL;
        ArrayObject* interfaces = NULL;
        ArrayObject* methods = NULL;
        ArrayObject* accessors = NULL;
        ArrayObject* variables = NULL;
        ScriptObject* constructor = NULL;

        if (flags & INCLUDE_METADATA)
        {
            metadata = new_array();
            PoolObject* class_mdpool;
            const uint8_t* class_md = tm->getMetadataPos(class_mdpool);
            if (class_md)
                addDescribeMetadata(metadata, class_mdpool, class_md);
        }

        if (flags & INCLUDE_BASES)
        {
            bases = new_array();
            for (Traitsp b = traits->base; b; b = b->base)
                pushstr(bases, describeClassName(b));
        }

        if (flags & INCLUDE_INTERFACES)
        {
            interfaces = new_array();
            for (InterfaceIterator iter(traits); iter.hasNext();)
            {
                Traits* ti = iter.next();
                pushstr(interfaces, describeClassName(ti));
            }
        }

        // constructor
        if (flags & INCLUDE_CONSTRUCTOR)
        {
            MethodInfo* initMethod = traits->init;
            if (initMethod)
            {
                initMethod->resolveSignature(toplevel);
                MethodSignaturep ms = initMethod->getMethodSignature();
                if (ms->param_count() > 0)
                {
                    constructor = describeParams(initMethod, ms);
                }
            }
        }

        if (flags & (INCLUDE_ACCESSORS | INCLUDE_METHODS | INCLUDE_VARIABLES))
        {
            // recover slot/method metadata and method-declarer information.

            // make a flattened set of bindings so we don't have to check for overrides as we go.
            // This is not terribly efficient, but doesn't need to be.
            MultinameBindingHashtable* mybind = MultinameBindingHashtable::create(gc);
            addBindings(m_toplevel->core(), mybind, tb, flags);

            // Don't want interface methods, so post-process and wipe out any
            // bindings that were added.
            for (InterfaceIterator ifc_iter(traits); ifc_iter.hasNext();)
            {
                Traitsp ti = ifc_iter.next();
                TraitsBindingsp tbi = ti->getTraitsBindings();
                StTraitsBindingsIterator iter(tbi);
                while (iter.next())
                {
                    if (!iter.key()) continue;
                    mybind->add(iter.key(), iter.ns(), BIND_NONE);
                }
            }

            // yuck, replicate buggy behavior in FP9/10
            RCList<Namespace> nsremoval(gc, kListInitialCapacity);
            if (flags & HIDE_NSURI_METHODS)
            {
                for (TraitsBindingsp tbi = tb->base; tbi; tbi = tbi->base)
                {
                    StTraitsBindingsIterator iter(tbi);
                    while (iter.next())
                    {
                        if (!iter.key()) continue;
                        Namespacep ns = iter.ns();
                        if (ns->getURI()->length() > 0 && nsremoval.indexOf(ns) < 0)
                        {
                            nsremoval.add(ns);
                        }
                    }
                }
            }

            StMNHTBindingIterator iter(mybind);
            while (iter.next())
            {
                if (!iter.key()) continue;
                Stringp name = iter.key();
                Namespacep ns = iter.ns();
                Binding binding = iter.value();
                Stringp nsuri = ns->getURI();
                TraitsMetadata::MetadataPtr md1 = NULL;
                TraitsMetadata::MetadataPtr md2 = NULL;
                PoolObject* md1pool = NULL;
                PoolObject* md2pool = NULL;

                // We only display public members -- exposing private namespaces could compromise security.
                if (ns->getType() != Namespace::NS_Public) {
                    continue;
                }

                if ((flags & HIDE_NSURI_METHODS) && nsremoval.indexOf(ns) >= 0) {
                    continue;
                }
                ScriptObject* v = new_object();

                const BindingKind bk = AvmCore::bindingKind(binding);
                switch (bk)
                {
                    case BKIND_CONST:
                    case BKIND_VAR:
                    {
                        if (!(flags & INCLUDE_VARIABLES))
                            continue;

                        const uint32_t slotID = AvmCore::bindingToSlotId(binding);
                        const KVPair props[] = {
                            { kstrid_access, strAtom(str(bk == BKIND_CONST ? kstrid_readonly : kstrid_readwrite)) },
                            { kstrid_type, strAtom(describeClassName(tb->getSlotTraits(slotID))) },
                        };
                        setpropmulti(v, props, elem_count(props));
                        if (!variables) variables = new_array();
                        pushobj(variables, v);
                        md1 = tm->getSlotMetadataPos(slotID, md1pool);
                        break;
                    }

                    case BKIND_METHOD:
                    {
                        if (!(flags & INCLUDE_METHODS))
                            continue;

                        const uint32_t methodID = AvmCore::bindingToMethodId(binding);
                        MethodInfo* mi = tb->getMethod(methodID);
                        mi->resolveSignature(toplevel);
                        MethodSignaturep ms = mi->getMethodSignature();

                        Traitsp declaringTraits = mi->declaringTraits();

                        const KVPair props[] = {
                            { kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) },
                            { kstrid_returnType, strAtom(describeClassName(ms->returnTraits())) },
                            { kstrid_parameters, objAtom(describeParams(mi, ms)) },
                        };
                        setpropmulti(v, props, elem_count(props));
                        if (!methods) methods = new_array();
                        pushobj(methods, v);
                        md1 = tm->getMethodMetadataPos(methodID, md1pool);
                        break;
                    }

                    case BKIND_GET:
                    case BKIND_SET:
                    case BKIND_GETSET:
                    {
                        if (!(flags & INCLUDE_ACCESSORS))
                            continue;

                        const uint32_t methodID = AvmCore::hasGetterBinding(binding) ?
                                                    AvmCore::bindingToGetterId(binding) :
                                                    AvmCore::bindingToSetterId(binding);

                        MethodInfo* mi = tb->getMethod(methodID);
                        mi->resolveSignature(toplevel);
                        MethodSignaturep ms = mi->getMethodSignature();

                        Traitsp declaringTraits = mi->declaringTraits();

                        // The verifier does not check the signature of a setter
                        // except when it is invoked.  We must be prepared for the
                        // case in which it has no arguments.
                        Traitsp accessorType;

                        if (AvmCore::hasGetterBinding(binding)) {
                            accessorType = ms->returnTraits();
                        } else {
                            // If setter is malformed, just use '*' as a placeholder.
                            accessorType = (ms->param_count() < 1) ? NULL : ms->paramTraits(1);
                        }

                        static const uint8_t bk2str[8] =
                        {
                            uint8_t(kstrid_emptyString),    // BKIND_NONE
                            uint8_t(kstrid_emptyString),    // BKIND_METHOD
                            uint8_t(kstrid_emptyString),    // BKIND_VAR
                            uint8_t(kstrid_emptyString),    // BKIND_CONST
                            uint8_t(kstrid_emptyString),    // unused
                            uint8_t(kstrid_readonly),       // BKIND_GET
                            uint8_t(kstrid_writeonly),      // BKIND_SET
                            uint8_t(kstrid_readwrite)       // BKIND_GETSET
                        };
                        const KVPair props[] = {
                            { kstrid_declaredBy, strAtom(describeClassName(declaringTraits)) },
                            { kstrid_access, strAtom(str(StringId(bk2str[bk]))) },
                            { kstrid_type, strAtom(describeClassName(accessorType)) },
                        };
                        setpropmulti(v, props, elem_count(props));
                        if (AvmCore::hasGetterBinding(binding))
                            md1 = tm->getMethodMetadataPos(AvmCore::bindingToGetterId(binding), md1pool);
                        if (AvmCore::hasSetterBinding(binding))
                            md2 = tm->getMethodMetadataPos(AvmCore::bindingToSetterId(binding), md2pool);
                        if (!accessors) accessors = new_array();
                        pushobj(accessors, v);
                        break;
                    }
                    case BKIND_NONE:
                        break;
                    default:
                        break;
                }

                ArrayObject* vm = NULL;
                if ((flags & INCLUDE_METADATA) && (md1 || md2))
                {
                    vm = new_array();
                    addDescribeMetadata(vm, md1pool, md1);
                    addDescribeMetadata(vm, md2pool, md2);
                }
                const KVPair props[] = {
                    { kstrid_name, strAtom(name) },
                    { kstrid_uri, strAtom(nsuri->length() == 0 ? NULL : nsuri) },
                    { kstrid_metadata, objAtom(vm) },
                };
                setpropmulti(v, props, elem_count(props));
            }
        }

        const KVPair props[] = {
            { kstrid_bases, objAtom(bases) },
            { kstrid_interfaces, objAtom(interfaces) },
            { kstrid_metadata, objAtom(metadata) },
            { kstrid_accessors, objAtom(accessors) },
            { kstrid_methods, objAtom(methods) },
            { kstrid_variables, objAtom(variables) },
            { kstrid_constructor, objAtom(constructor) },
        };
        setpropmulti(o, props, elem_count(props));

        return o;
    }