root/libcore/abc/AbcBlock.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. finalize
  2. finalize_mbody
  3. read
  4. prepare
  5. check_multiname_name
  6. check_multiname_namespace
  7. check_multiname_namespaceset
  8. setMultinameNames
  9. setNamespaceURI
  10. locateClass
  11. locateClass
  12. read_version
  13. read_integer_constants
  14. read_unsigned_integer_constants
  15. read_double_constants
  16. read_string_constants
  17. read_namespaces
  18. read_namespace_sets
  19. read_multinames
  20. pool_value
  21. read_method_infos
  22. skip_metadata
  23. read_instances
  24. read_classes
  25. read_scripts
  26. read_method_bodies
  27. read
  28. update_global_name

// 
//   Copyright (C) 2007, 2008, 2009, 2010, 2011 Free Software Foundation, Inc.
// 
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful, 
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

// The AS3 abc block format reader.
//

#include "gnashconfig.h"
#include "AbcBlock.h"
#include "SWFStream.h" // for use
#include "VM.h"
#include "log.h"
#include "ClassHierarchy.h"
#include "Class.h"
#include "namedStrings.h"
#include "CodeStream.h"
#include "action_buffer.h"
#include "Machine.h"
#include "Global_as.h"

#include <boost/bind.hpp>

namespace gnash {

namespace abc {

bool
Trait::finalize(AbcBlock *block, abc::Class* script, bool do_static)
{
        log_abc("Finalize class %s (%s), trait kind: %s", 
            block->_stringTable->value(script->getName()), script, _kind);

        switch (_kind)
        {
        case KIND_SLOT:
        case KIND_CONST:
        {
            // Validate the type.
            abc::Class* type;
            if (_typeIndex) {
                log_abc("Trait type: %s", 
                    block->_stringPool[
                        block->_multinamePool[_typeIndex].getABCName()]);
                type = block->locateClass(block->_multinamePool[_typeIndex]);
            }
            else {
                type = block->mTheObject;
            }

            if (!type) {
                log_error(_("ABC: Finalizing trait yielded bad type for slot."));
                return false;
            }

            // The name has been validated in read.
            // TODO: Find a better way to initialize trait values.
            if (!_hasValue) {
                as_object* null = 0;
                _value = null; 
            }

            log_abc("Adding property=%s with value=%s slot=%u",
                    block->_stringPool[_name], _value, _slotID);

            script->addValue(_globalName, _namespace, _slotID, type, 
                _value, _kind == KIND_CONST, do_static);
            break;
        }

        case KIND_METHOD:
            script->addMethod(_globalName, _namespace, _method, false);
            break;
        
        case KIND_GETTER:
            script->addGetter(_name, _namespace, _method, do_static);
            break;

        case KIND_SETTER:
            script->addSetter(_name, _namespace, _method, do_static);
            break;
        
        case KIND_CLASS:
            log_abc("Adding class %s, value %s, slot=%u",
                    block->_stringPool[_name], _value, _slotID);
            script->addMemberScript(_globalName, _namespace, _slotID, 
                block->_classes[_classInfoIndex], do_static);
            break;

        case KIND_FUNCTION:
            script->addSlotFunction(_name, _namespace, _slotID, _method,
                    do_static);
            break;

        default:
            return false;
        } 
        return true;
}

bool
Trait::finalize_mbody(AbcBlock *block, Method *pMethod)
{
        log_abc("Finalizing method trait: kind %s", _kind);
        switch (_kind)
        {
        case KIND_SLOT:
        case KIND_CONST:
        {
            // Validate the type.
            abc::Class *type;
            if (_typeIndex) {
                type = block->locateClass(block->_multinamePool[_typeIndex]);
            }
            else {
                type = block->mTheObject;
            }

            if (!type) {
                log_error(_("ABC: Finalizing trait yielded bad type for slot."));
                return false;
            }

            // The name has been validated in read.
            // TODO: Find a better way to initialize trait values.
            if (!_hasValue) {
                _value = as_value((as_object*)0); // NULL value, right ?
            }
            log_abc("Adding property=%s with value=%s slot=%u",
                    block->_stringPool[_name], _value.toDebugString(), _slotID);
            pMethod->addValue(_globalName, _namespace, _slotID, type, 
                _value, _kind == KIND_CONST);
            break;
        }
        case KIND_METHOD:
        {
            pMethod->addMethod(_name, _namespace, _method);
            break;
        }
        case KIND_GETTER:
        {
            pMethod->addGetter(_name, _namespace, _method);
            break;
        }
        case KIND_SETTER:
        {
            pMethod->addSetter(_name, _namespace, _method);
            break;
        }
        case KIND_CLASS:
        {
            pMethod->addMemberScript(_name, _namespace, _slotID, 
                block->_classes[_classInfoIndex]);
            break;
        }
        case KIND_FUNCTION:
        {
            pMethod->addSlotFunction(_name, _namespace, _slotID, _method);
            break;
        }
        default:
            // Not here -- validated already in read.
            return false;
        } 
        return true;
}

/// Read an AS3 'trait'
bool
Trait::read(SWFStream* in, AbcBlock *block)
{
        boost::uint32_t name = in->read_V32();
        if (name >= block->_multinamePool.size())
        {
                log_error(_("ABC: Bad name for trait."));
                return false;
        }
        if (!block->_multinamePool[name].isQName())
        {
                log_error(_("ABC: Trait name must be fully qualified."));
                return false;
        }
        MultiName multiname = block->_multinamePool[name];
        _name = block->_multinamePool[name].getABCName();
        _globalName = block->_multinamePool[name].getGlobalName();
        _namespace = block->_multinamePool[name].getNamespace();

        boost::uint8_t kind = in->read_u8();
        _kind = static_cast<Kind>(kind & 0x0F);

        log_abc("Trait name: %s, Trait kind: %s",
            block->_stringPool[multiname.getABCName()], _kind);

    switch (_kind)
        {
        case KIND_SLOT:
        case KIND_CONST:
        {
            _slotID = in->read_V32();
            _typeIndex = in->read_V32();
            boost::uint32_t vindex = in->read_V32();
            log_abc("Slot ID=%u Type=%s Pool index=%u", _slotID,
                    block->_stringPool[
                    block->_multinamePool[_typeIndex].getABCName()], vindex);
            
            if (vindex) {
                const AbcBlock::PoolConstant c =
                    static_cast<AbcBlock::PoolConstant>(in->read_u8());

                if (!block->pool_value(vindex, c, _value))
                    return false; // Message done by pool_value
                _hasValue = true;
            }
            else _hasValue = false;
            break;
        }
        case KIND_METHOD:
        case KIND_GETTER:
        case KIND_SETTER:
        {
            // Ignore the 'disp_id'
            in->skip_V32();

            boost::uint32_t offset = in->read_V32();
            log_abc("Method index=%u", offset);
            if (offset >= block->_methods.size()) {
                log_error(_("Bad method id in trait."));
                return false;
            }
            _method = block->_methods[offset];
            break;
        }
        case KIND_CLASS:
        {
            _slotID = in->read_V32();
            _classInfoIndex = in->read_V32();
            log_abc("Slot id: %u Class index: %u Class Name: %s", _slotID, 
                _classInfoIndex, 
                block->_stringTable->value(
                    block->_classes[_classInfoIndex]->getName()));

            if (_classInfoIndex >= block->_classes.size()) {
                log_error(_("Bad Class id in trait."));
                return false;
            }
            break;
        }
        case KIND_FUNCTION:
        {
            _slotID = in->read_V32();
            boost::uint32_t offset = in->read_V32();
            if (offset >= block->_methods.size()) {
                log_error(_("Bad method id in trait."));
                return false;
            }
            _method = block->_methods[offset];
            break;
        }
        default:
        {
            log_error(_("ABC: Unknown type of trait."));
        }
        }

        // Ignore the metadata, but it must be read to know how to ignore it.
        if ((kind >> 4) & 0x04) {
                boost::uint32_t mcount = in->read_V32();
                for (size_t i = 0; i < mcount; ++i) {
                        in->skip_V32();
                }
        }
        return true;
}

std::ostream&
operator<<(std::ostream& o, const Trait::Kind k)
{
    switch (k) {
        case abc::Trait::KIND_SLOT:
            return o << "slot";
        case abc::Trait::KIND_CONST:
            return o << "const";
        case abc::Trait::KIND_METHOD:
            return o << "method";
        case abc::Trait::KIND_GETTER:
            return o << "getter";
        case abc::Trait::KIND_SETTER:
            return o << "setter";
        case abc::Trait::KIND_CLASS:
            return o << "class";
        case abc::Trait::KIND_FUNCTION:
            return o << "function";
        default:
            return o << "Unknown kind " << static_cast<int>(k);
    }
}

AbcBlock::AbcBlock()
    :
    _stringTable(&VM::get().getStringTable())
{
        mCH = &VM::get().getMachine()->global()->classHierarchy();
        // TODO: Make this the real 'Object' prototype.
        mCH->getGlobalNs()->stubPrototype(*mCH, NSV::CLASS_OBJECT);
        mTheObject = mCH->getGlobalNs()->getScript(NSV::CLASS_OBJECT);
}

void
AbcBlock::prepare(Machine* mach)
{

    std::for_each(_classes.begin(), _classes.end(),
            std::mem_fun(&abc::Class::initPrototype));
    // The last (entry) script has Global as its prototype.
    // This can be deduced because the global classes are initialized with a
    // slot on script 0 (entry script). OpNewClass then attempts to set the
    // corresponding slot once the class has been constructed. At this point,
    // global should verifiably be on the stack, so the slots are expected
    // to be set on the global object.
    // It seems likely, though testing it is not straightforward, that all
    // scripts have Global as a target object (prototype), so for now we
    // will do that.
    std::for_each(_scripts.begin(), _scripts.end(),
            boost::bind(&abc::Class::setPrototype, _1, mach->global()));
 
    std::for_each(_methods.begin(), _methods.end(),
            boost::bind(&Method::initPrototype, _1, mach));
    
    // TODO: Remove this, initialize traits only when needed; possibly 
    // consruct them on parsing without the need for a finalize method.
    std::for_each(_methods.begin(), _methods.end(),
            boost::bind(&abc::Method::initTraits, _1, *this));
    std::for_each(_classes.begin(), _classes.end(),
            boost::bind(&abc::Class::initTraits, _1, *this));
    std::for_each(_scripts.begin(), _scripts.end(),
            boost::bind(&abc::Class::initTraits, _1, *this));

}

void
AbcBlock::check_multiname_name(boost::uint32_t name)
{

        if (name >= _stringPool.size()) {
                throw ParserException("ABC: Out of bounds string for Multiname.");
        }
}

void
AbcBlock::check_multiname_namespace(boost::uint32_t ns)
{
        if (ns >= _namespacePool.size()) {
                throw ParserException("ABC: Out of bounds namespace for Multiname.");
        }
}

void
AbcBlock::check_multiname_namespaceset(boost::uint32_t nsset)
{
        if (!nsset)
    {
                throw ParserException("ABC: 0 selection for namespace set is invalid.");
        }
        if (nsset >= _namespaceSetPool.size())
    {
                throw ParserException("ABC: Out of bounds namespace set for Multiname.");
        }
}

void
AbcBlock::setMultinameNames(MultiName *n, abc::URI ABCName)
{
        
        n->setABCName(ABCName);
        std::string name = _stringPool[ABCName];
        string_table::key global_key = _stringTable->find(name, true);
        log_abc("Global key %u", global_key);
        n->setGlobalName(global_key);
        log_abc("Multiname: %s ABCName set to %u, global name set to %u",
            name, n->getABCName(), n->getGlobalName());
}

void
AbcBlock::setNamespaceURI(Namespace *ns, abc::URI ABCName)
{
        std::string name = _stringPool[ABCName];
        string_table::key global_key = _stringTable->find(name);
        ns->setURI(global_key);
        log_abc("Namespace: %s AbcURI=%u URI=%u.", name, ABCName, global_key);
}

abc::Class*
AbcBlock::locateClass(const std::string& className)
{

    const std::string::size_type pos = className.rfind(".");
    
    MultiName a;

    const std::string& nsstr = (pos != std::string::npos) ? 
        className.substr(0, pos) : "";

    const std::string& clstr = (pos != std::string::npos) ? 
        className.substr(pos + 1) : className;
    
    a.setGlobalName(_stringTable->find(clstr));

    for (std::vector<Namespace*>::iterator i = _namespacePool.begin();
            i != _namespacePool.end(); ++i) {
        
        const size_t key = (*i)->getURI();

        if (key == _stringTable->find(nsstr)) {
            a.setNamespace(*i);
            break;
        }
    }
    
    return locateClass(a);

}

abc::Class*
AbcBlock::locateClass(MultiName& m)
{
        abc::Class* found = 0;

        if (m.getNamespace())
        {
                found = m.getNamespace()->getScript(m.getGlobalName());
                if (found) return found;
        }
        if (m.namespaceSet() && !m.namespaceSet()->empty())
        {
                std::vector<Namespace*>::const_iterator i;
                for (i = m.namespaceSet()->begin(); i != m.namespaceSet()->end(); ++i) {

                        found = (*i)->getScript(m.getGlobalName());
                        if (found) return found;
                }
        }

    log_abc("Could not locate class in ABC block resources!");

    return 0;

}

/// Read the ActionBlock version number.
bool
AbcBlock::read_version()
{
        // Minor version, major version.
        mVersion = (_stream->read_u16()) | (_stream->read_u16() << 16);
        log_debug(_("Abc Version: %d.%d"), (mVersion & 0xFFFF0000) >> 16, 
                (mVersion & 0x0000FFFF));
        return true;
}

/// Read the pool of integer constants.
bool
AbcBlock::read_integer_constants()
{
        // count overestimates by 1.
        boost::uint32_t count = _stream->read_V32();
        _integerPool.resize(count);
        if (count)
                _integerPool[0] = 0;
        for (unsigned int i = 1; i < count; ++i)
        {
                _integerPool[i] = static_cast<boost::int32_t> (_stream->read_V32());
        }
        return true;
}

/// Read the pool of unsigned integer constants.
bool
AbcBlock::read_unsigned_integer_constants()
{
        // count overestimates by 1.
        boost::uint32_t count = _stream->read_V32();
        _uIntegerPool.resize(count);
        if (count)
                _uIntegerPool[0] = 0;
        for (unsigned int i = 1; i < count; ++i)
        {
                _uIntegerPool[i] = _stream->read_V32();
        }
        return true;
}

/// Read the pool of 64-bit double constants.
bool
AbcBlock::read_double_constants()
{
        boost::uint32_t count = _stream->read_V32();
        _doublePool.resize(count);
        if (count) _doublePool[0] = 0.0;

        for (size_t i = 1; i < count; ++i)
        {
                _doublePool[i] = _stream->read_d64();
                log_abc("Double %u=%lf", i, _doublePool[i]);
        }
        return true;
}

/// Read the pool of string constants.
bool
AbcBlock::read_string_constants()
{
        log_abc("Begin reading string constants.");
        boost::uint32_t count = _stream->read_V32();
        log_abc("There are %u string constants.", count);
        _stringPool.resize(count);

    if (count) {
                _stringPool[0] = "";
        }

    for (size_t i = 1; i < count; ++i) {
                boost::uint32_t length = _stream->read_V32();
                _stream->read_string_with_length(length, _stringPool[i]);
                log_abc("Adding string constant to string pool: index=%u %s",
                i, _stringPool[i]);
        }
        return true;
}

/// Read the pool of namespaces
/// Any two namespaces with the same uri here are the same namespace, 
/// excepting private namespaces.
bool
AbcBlock::read_namespaces()
{       
        log_abc("Begin reading namespaces.");
        boost::uint32_t count = _stream->read_V32();
        log_abc("There are %u namespaces.", count);
        _namespacePool.resize(count);
        if (count) {
                _namespacePool[0] = mCH->getGlobalNs();
        }

        for (size_t i = 1; i < count; ++i)
        {
                NamespaceConstant kind =
            static_cast<NamespaceConstant>(_stream->read_u8());

                boost::uint32_t nameIndex = _stream->read_V32();
                log_abc("Namespace %u: %s, index %u, name %s", i, kind,
                nameIndex, _stringPool[nameIndex]);

                if (nameIndex >= _stringPool.size()) {
                        log_error(_("ABC: Bad string given for namespace."));
                        return false;
                }
                
                if (kind == PRIVATE_NS)
                {
                        _namespacePool[i] = mCH->anonNamespace(nameIndex);
                        _namespacePool[i]->setPrivate();
                }
                else
                {
            string_table::key gn = _stringTable->find(_stringPool[nameIndex]);
                        Namespace *n = mCH->findNamespace(gn);
                        if (!n) n = mCH->addNamespace(gn);
                        _namespacePool[i] = n;
                }
                if (kind == PROTECTED_NS) _namespacePool[i]->setProtected();
                if (kind == PACKAGE_NS) _namespacePool[i]->setPackage();
                setNamespaceURI(_namespacePool[i], nameIndex);
        }
        return true;
}

/// Read the set of sets of namespaces.
bool
AbcBlock::read_namespace_sets()
{
        boost::uint32_t count = _stream->read_V32();
        _namespaceSetPool.resize(count);
        if (count)
        {
                _namespaceSetPool[0].resize(0);
        }
        for (unsigned int i = 1; i < count; ++i)
        {
                boost::uint32_t icount = _stream->read_V32();
                _namespaceSetPool[i].resize(icount);
                for (unsigned int j = 0; j < icount; ++j)
                {
                        boost::uint32_t selection = _stream->read_V32();
                        if (!selection || selection >= _namespacePool.size())
                        {
                                log_error(_("ABC: Bad namespace for namespace set."));
                                return false;
                        }
                        _namespaceSetPool[i][j] = _namespacePool[selection];
                }
        }
        return true;
}

/// Read the multinames.
bool
AbcBlock::read_multinames()
{
        boost::uint32_t count = _stream->read_V32();
        log_abc("There are %u multinames.", count);
        _multinamePool.resize(count);
        if (count)
        {
//              _multinamePool[0].setABCName(0);
                setMultinameNames(&_multinamePool[0], 0);
                _multinamePool[0].setNamespace(mCH->getGlobalNs());
        }
        for (unsigned int i = 1; i < count; ++i)
        {
        MultiName::Kind kind = static_cast<MultiName::Kind>(_stream->read_u8());
                boost::uint32_t ns = 0;
                boost::uint32_t name = 0;
                boost::uint32_t nsset = 0;

                log_abc("Multiname %u has kind %s", i, static_cast<int>(kind));

                // Read, but don't upper validate until after the switch.
                switch (kind)
                {
            case MultiName::KIND_Qname:
            case MultiName::KIND_QnameA:
                ns = _stream->read_V32();
                check_multiname_namespace(ns);
                name = _stream->read_V32();
                check_multiname_name(name);
                log_abc("\tnamespace_index=%u name_index=%u name=%s",
                        ns, name, _stringPool[name]);
                break;
            
            case MultiName::KIND_RTQname:
            case MultiName::KIND_RTQnameA:
                name = _stream->read_V32();
                check_multiname_name(name);
                break;
            
            case MultiName::KIND_RTQnameL:
            case MultiName::KIND_RTQnameLA:
                break;
            
            case MultiName::KIND_Multiname:
            case MultiName::KIND_MultinameA:
                name = _stream->read_V32();
                check_multiname_name(name);
                nsset = _stream->read_V32();
                check_multiname_namespaceset(nsset);
                break;
            
            case MultiName::KIND_MultinameL:
            case MultiName::KIND_MultinameLA:
                nsset = _stream->read_V32();
                check_multiname_namespaceset(nsset);
                break;
            
            default:
                // Unknown type.
                log_error(_("Action Block: Unknown multiname type (%d)."),
                        kind);
                return false;
        } 

                _multinamePool[i].setFlags(kind);
                setMultinameNames(&_multinamePool[i], name);
                log_abc("Done setting multinames: abc=%u global=%u",
               _multinamePool[i].getABCName(),
               _multinamePool[i].getGlobalName());

        _multinamePool[i].setNamespace(_namespacePool[ns]);

                if (nsset) {
                        _multinamePool[i].namespaceSet(&_namespaceSetPool[nsset]);
        }
        } // End of main loop.
        return true;
}

bool
AbcBlock::pool_value(boost::uint32_t index, PoolConstant type, as_value &v)
{
        if (!index)
                return true;

        log_abc("Pool value: index is %u type is 0x%X", index, type | 0x0);
        switch (type)
        {
        case POOL_STRING: 
        {
                if (index >= _stringPool.size())
                {
                        log_error(_("Action Block: Bad index in optional argument."));
                        return false;
                }
                v.set_string(_stringPool[index]);
                break;
        }
        case POOL_INTEGER: 
        {
                if (index >= _integerPool.size())
            {
                        log_error(_("Action Block: Bad index in optional argument."));
                        return false;
                }
                v.set_double(_integerPool[index]);
                break;
        }
        case POOL_UINTEGER:
        {
                if (index >= _uIntegerPool.size())
                {
                        log_error(_("Action Block: Bad index in optional argument."));
                        return false;
                }
                v.set_double(_uIntegerPool[index]);
                break;
        }
        case POOL_DOUBLE: 
        {
                if (index >= _doublePool.size())
                {
                        log_error(_("Action Block: Bad index in optional argument."));
                        return false;
                }
                v.set_double(static_cast<double>(_doublePool[index]));
                break;
        }
        case POOL_NAMESPACE: // Namespace
        {
                if (index >= _namespacePool.size())
                {
                        log_error(_("ABC: Bad index in optional argument, namespaces."));
                        return false;
                }
                break;
        }
        case POOL_FALSE: // False value
        {
                v.set_bool(false);
                break;
        }
        case POOL_TRUE: // True value
        {
                v.set_bool(true);
                break;
        }
        case POOL_NULL: // NULL value
        {
                v.set_null();
                break;
        }
        default: // All others are bogus.
        {
                log_error(_("ABC: Bad default value type (%X), but continuing."), type);
                return true;
                break;
        }
        } // end of switch
        return true;
}

/// Read the method infos.
bool
AbcBlock::read_method_infos()
{
        log_abc("Begin read_method_infos.");

        boost::uint32_t count = _stream->read_V32();
    log_abc("Method count: %u", count);

        _methods.resize(count);
        for (unsigned int i = 0; i < count; ++i)
        {
                log_abc(" Reading method %u", i);
                Method *pMethod = mCH->newMethod();
                pMethod->setMethodID(i);
                _methods[i] = pMethod;
                boost::uint32_t param_count = _stream->read_V32();
                boost::uint32_t return_type = _stream->read_V32();

        const std::string& rt = return_type ? 
            _stringPool[_multinamePool[return_type].getABCName()] :
            "*";

                log_abc("  Param count: %u, return type: %s", param_count, 
                rt, return_type);

                pMethod->setMinArgumentCount(param_count);
                pMethod->setMaxArgumentCount(param_count);

                if (return_type >= _multinamePool.size()) {
                        log_error(_("ABC: Bad return type for method info."));
                        return false;
                }

        if (!return_type) {
            pMethod->setReturnType(0);
        }
        else {
            // TODO: this can be 'void', which clearly isn't a class, so this
            // seems bogus. As setReturnType is a no-op, we should log it
            // and ignore it.
            abc::Class* rtScript = locateClass(_multinamePool[return_type]);
            if (!rtScript) {
                log_abc(_("ABC: Unknown return type."));
            }

            pMethod->setReturnType(rtScript);
        }
                for (size_t j = 0; j < param_count; ++j) {
                        log_abc("  Reading parameter %u", j);
                        // The parameter type.
                        boost::uint32_t ptype = _stream->read_V32();
            
            const std::string& pt = return_type ? 
                _stringPool[_multinamePool[ptype].getABCName()] :
                "*";
                        
            log_abc("   Parameter type(index): %s(%u)", pt, ptype);

                        if (ptype >= _multinamePool.size()) {
                                log_error(_("ABC: Bad parameter type in method."));
                                return false;
                        }
                        
            // A value of 0 is legitimate, meaning 'any (*)'. 
            if (ptype) {
                abc::Class* param_type = locateClass(_multinamePool[ptype]);

                if (!param_type) {
                    log_abc((_("ABC: Unknown parameter type.")));
                }
                
                // This currently also pushes 0, meaning 'any'; perhaps it
                // should throw a VerifyError if the class is not known.
                pMethod->pushArgument(param_type);
            }
            else {
                pMethod->pushArgument(0);
            }
                }

                boost::uint32_t method_name = _stream->read_V32();
                log_abc(  "Method name=%s %d", _stringPool[method_name], method_name);
                boost::uint8_t flags = _stream->read_u8();
                log_abc("  Flags: %X", flags | 0x0);
//              log_abc("Check if flags and optional args.");
                // If there are default parameters, read them now.
                // Runtime will do validation of whether or not these can actually
                // be assigned to the corresponding parameters.
                if (flags & METHOD_OPTIONAL_ARGS) {
                        
            boost::uint32_t ocount = _stream->read_V32();
                        log_abc("  Optional args: %u", ocount);
                        pMethod->setMinArgumentCount(pMethod->maxArgumentCount() - ocount);
                        
            for (unsigned int j = 0; j < ocount; ++j) {
                                log_abc("  Reading optional arg: %u", j);
                                boost::uint32_t index = _stream->read_V32();
                                
                PoolConstant kindof =
                    static_cast<PoolConstant>(_stream->read_u8());

                                log_abc("   Index: %u Kindof: %u", index, kindof);
                                as_value v;
                                if (!pool_value(index, kindof, v)) {
                                        return false; 
                }
                                pMethod->pushOptional(v);
                        }
                        log_abc("Done handling optional args.");
                }

        if (flags & METHOD_ACTIVATION) {
            log_abc("Method needs activation");
            pMethod->setNeedsActivation();
        }

                // If there are names present for the parameters, skip them.
                if (flags & METHOD_ARG_NAMES) {
                        for (size_t j = 0; j < param_count; ++j) {
                                _stream->skip_V32();
                        }
                }
        } // End of method loop.
        return true;
}

/// Skip the metadata, which is useless to us.
bool
AbcBlock::skip_metadata()
{
        boost::uint32_t count = _stream->read_V32();
        for (unsigned int i = 0; i < count; ++i)
        {
                _stream->skip_V32(); // A name index.
                boost::uint32_t icount = _stream->read_V32();
                for (unsigned int j = 0; j < icount; ++j)
                {
                        // key/values may not be stored together, but this still works.
                        _stream->skip_V32();
                        _stream->skip_V32();
                }
        }
        return true;
}

/// Load the instances from the block.
bool
AbcBlock::read_instances()
{
        boost::uint32_t count = _stream->read_V32();
        log_abc("There are %u instances.", count);
        _classes.resize(count);
        for (size_t i = 0; i < count; ++i) {
                //Read multiname index.
                boost::uint32_t index = _stream->read_V32();
                // 0 is allowed as a name, typically for the last entry.
                if (index >= _multinamePool.size()) {
                        log_error(_("ABC: Out of bounds instance name."));
                        return false;
                }
        
        MultiName& multiname = _multinamePool[index];
                
        // This must be a QName.
                if (!multiname.isQName()) {
                        log_error(_("ABC: QName required for instance."));
                        return false;
                }
                
        if (!multiname.getNamespace()) {
                        log_error(_("ABC: No namespace to use for storing class."));
                        return false;
                }

        abc::Class* cl = locateClass(multiname);
                
        if (!cl) {

            const string_table::key className = multiname.getGlobalName();

                        cl = mCH->newClass();
            cl->setName(className);

                        if (!multiname.getNamespace()->addScript(className, cl)) {

                                log_error(_("Duplicate class registration."));
                                return false;
                        }
            log_debug("Adding class %s (%s) to namespace %s",
                    _stringTable->value(multiname.getGlobalName()),
                    _stringPool[multiname.getABCName()],
                    _stringTable->value(multiname.getNamespace()->getURI()));
            log_debug("Namespace now:");
            multiname.getNamespace()->dump(*_stringTable);

                }
                cl->setDeclared();
                _classes[i] = cl;
                boost::uint32_t super_index = _stream->read_V32();

                if (super_index && super_index >= _multinamePool.size()) {
                        log_error(_("ABC: Bad super type."));
                        return false;
                }

                if (!super_index) {
                        cl->setSuper(mTheObject);
                }
                else {
                        abc::Class *pSuper = locateClass(_multinamePool[super_index]);
                        if (!pSuper)
                        {
                                log_error(_("ABC: Super type not found (%s)"), 
                                        _stringPool[_multinamePool[super_index].getABCName()]);
                            return false;
            }

                        if (pSuper->isFinal())
                        {
                                log_error(_("ABC: Can't extend a class which is final."));
                                return false;
                        }

                        if (pSuper->isInterface())
                        {
                                log_error(_("ABC: Can't extend an interface type."));
                                return false;
                        }

                        if (pSuper == cl)
                        {
                                log_error(_("ABC: Class cannot be its own supertype."));
                                return false;
                        }
                        cl->setSuper(pSuper);
                        pSuper->setInherited();
                }

                boost::uint8_t flags = _stream->read_u8();
                log_abc("Instance %u(%s) multiname index=%u name=%s super index=%u "
                "flags=%X", i, cl, index, 
                _stringPool[_multinamePool[index].getABCName()],
                super_index, flags | 0x0);

                if (flags & INSTANCE_SEALED) cl->setSealed();
                if (flags & INSTANCE_FINAL) cl->setFinal();
                if (flags & INSTANCE_INTERFACE) cl->setInterface();
                if ((flags & 7) == INSTANCE_DYNAMIC) cl->setDynamic();

                if (flags & INSTANCE_PROTECTED_NS) {
                        boost::uint32_t ns_index = _stream->read_V32();
                        if (ns_index >= _namespacePool.size()) {
                                log_error(_("ABC: Bad namespace for protected."));
                                return false;
                        }
                        // Set the protected namespace's parent, if it exists.
                        if (cl->getSuper()->hasProtectedNs())
                                _namespacePool[ns_index]->setParent(
                        cl->getSuper()->getProtectedNs());
                        cl->setProtectedNs(_namespacePool[ns_index]);
                }

                // This is the list of interfaces which the instances has agreed to
                // implement. They must be interfaces, and they must exist.
                boost::uint32_t intcount = _stream->read_V32();
                log_abc("This instance has %u interfaces.", intcount);
                for (size_t j = 0; j < intcount; ++j) {
                        boost::uint32_t i_index = _stream->read_V32();
                        log_abc("Interface %u has multiname index=%u", i, i_index);
                        // 0 is allowed as an interface, typically for the last one.
                        if (i_index >= _multinamePool.size()) {
                                log_error(_("ABC: Bad name for interface."));
                                return false;
                        }
                        abc::Class *pInterface = locateClass(_multinamePool[i_index]);
                        // These may be undefined still, so don't check interface just yet.
                        if (0) //!pInterface || !pInterface->isInterface())
                        {
                                log_error(_("ABC: Can't implement a non-interface type."));
                                return false;
                        }
                        cl->pushInterface(pInterface);
                }

                // The next thing should be the constructor.
                // TODO: What does this mean exactly? How does it differ from the one in
                // the class info block?
                boost::uint32_t offset = _stream->read_V32();
                log_abc("Moffset: %u", offset);
                if (offset >= _methods.size()) {
                        log_error(_("ABC: Out of bounds method for initializer."));
                        return false;
                }
                // Don't validate for previous owner.
                cl->setConstructor(_methods[offset]);

                // Next come the 'traits' of the instance. (The members.)
                boost::uint32_t tcount = _stream->read_V32();
                log_abc("Trait count: %u", tcount);
                for (unsigned int j = 0; j < tcount; ++j)
                {
                        Trait t;
                        t.set_target(cl, false);
                        if (!t.read(_stream, this)) return false;
            cl->addInstanceTrait(t);
                }
        } 
        return true;
}

/// Read the class data
bool
AbcBlock::read_classes()
{
        // Count was found in read_instances().
        log_abc("Begin reading classes.");
        boost::uint32_t count = _classes.size();
        log_abc("There are %u classes.", count);
        
    for (size_t i = 0; i < count; ++i) {
                abc::Class* cl = _classes[i];
                boost::uint32_t offset = _stream->read_V32();
                log_abc("Class %u(%s) static constructor index=%u", i, cl, offset);

        if (offset >= _methods.size()) {
                        log_error(_("ABC: Out of bound static constructor for class."));
                        return false;
                }

                // Don't validate for previous owner.
                cl->setStaticConstructor(_methods[offset]);

                boost::uint32_t tcount = _stream->read_V32();
                log_abc("This class has %u traits.", tcount);
                for (size_t j = 0; j < tcount; ++j) {
            Trait t;
                        t.set_target(cl, true);
                        if (!(t.read(_stream, this))) return false;
            cl->addStaticTrait(t);
                }
        } 
        return true;
}

/// Read the scripts (global functions)
/// The final script is the entry point for the block.
bool
AbcBlock::read_scripts()
{
        log_abc("Begin reading scripts.");

        const boost::uint32_t scriptcount = _stream->read_V32();
        log_abc("There are %u scripts.", scriptcount);

        _scripts.resize(scriptcount);
        for (size_t i = 0; i < scriptcount; ++i) {

                abc::Class* script = mCH->newClass();
                _scripts[i] = script;

                boost::uint32_t offset = _stream->read_V32();
                log_abc("Reading script %u(%s) initializer method index=%u", i,
                script, offset);
                if (offset >= _methods.size()) {
                        log_error(_("ABC: Out of bounds method for script."));
                        return false;
                }

                script->setConstructor(_methods[offset]);

                const boost::uint32_t tcount = _stream->read_V32();
                for (size_t j = 0; j < tcount; ++j) {
                        
            Trait t;
                        t.set_target(script, false);
                        if (!(t.read(_stream, this))) {
                                return false;
            }
                        log_abc("Trait: %u name: %s(%u) kind: %s value: %s ", j, 
                    _stringPool[t._name], t._name, t._kind, t._value);

            // TODO: this should not use Class!
                        script->addStaticTrait(t);
                }
        } 
        return true;
}

/// Read the method bodies and attach them to the methods.
bool
AbcBlock::read_method_bodies()
{
        boost::uint32_t count = _stream->read_V32();
        log_abc("There are %u method bodies.", count);
        for (unsigned int i = 0; i < count; ++i)
        {
                boost::uint32_t offset = _stream->read_V32();
                log_abc("Method body %u method offset=%u", i, offset);

                if (offset >= _methods.size()) {
                        log_error(_("ABC: Out of bounds for method body."));
                        return false;
                }

        Method& method = *_methods[offset];

                if (method.getBody()) {
                        log_error(_("ABC: Only one body per method."));
                        return false;
                }

                // Maximum stack size.
        method.setMaxStack(_stream->read_V32());
                
        // Maximum register size.
                method.setMaxRegisters(_stream->read_V32());
                
        // Scope depth.
                method.setScopeDepth(_stream->read_V32());
                
        // Max scope depth.
                method.setMaxScope(_stream->read_V32());
                
        // Code length
                boost::uint32_t clength = _stream->read_V32();
                method.setBodyLength(clength);

                // The code.
                //TODO: Clean this up.
                std::string body;
                _stream->read_string_with_length(clength, body);

                method.setBody(new CodeStream(body));
                
        // Exception count and exceptions
        
        // Note: catch type and variable name are documented to be
        // indices in the string pool, but they are in fact indices
        // in the multiname pool.
                const boost::uint32_t ecount = _stream->read_V32();
                for (unsigned int j = 0; j < ecount; ++j) {
                        asException *ex = mCH->newException();

                        // Where the try block begins and ends.
                        ex->setStart(_stream->read_V32());
                        ex->setEnd(_stream->read_V32());

                        // Where to go when the exception is activated.
                        ex->setCatch(_stream->read_V32());

                        // What types should be caught.
                        boost::uint32_t catch_type = _stream->read_V32();
                        if (catch_type >= _multinamePool.size()) {
                                log_error(_("ABC: Out of bound type for exception."));
                                return false;
                        }
                        if (!catch_type) {
                                ex->catchAny();
                        }
                        else {
                                abc::Class *type = locateClass(_multinamePool[catch_type]);
                                if (!type) {

                                        log_error(_("ABC: Unknown type of object to catch. (%s)"), 
                                                _stringTable->value(
                            _multinamePool[catch_type].getABCName()));

                    // return false;
                                        // Fake it, for now:
                                        ex->catchAny();
                                }
                                else {
                                        ex->setCatchType(type);
                                }
                        }

                        // A variable name for the catch type.
                        // In version 46.15, no names.
                        if (mVersion != ((46 << 16) | 15)) {
                                boost::uint32_t cvn = _stream->read_V32();
                                if (cvn >= _multinamePool.size()) {
                                        log_error(_("ABC: Out of bound name for caught "
                                "exception."));
                                        return false;
                                }
                                ex->setName(_multinamePool[cvn].getABCName());
                                ex->setNamespace(_multinamePool[cvn].getNamespace());
                        }
                } 

        // Traits
                boost::uint32_t tcount = _stream->read_V32();
                for (unsigned int j = 0; j < tcount; ++j)
                {
                        Trait t;
                        t.set_target(_methods[offset]);
                        
            if (!t.read(_stream, this)) {
                                return false;
            }

                        log_abc("Activation trait: %u name: %s, kind: %s, value: %s ", j, 
                    _stringPool[t._name], t._kind, t._value);
            _methods[offset]->addTrait(t);
                }
        } 
        return true;
}

// Load up all of the data.
bool
AbcBlock::read(SWFStream& in)
{
    // This isn't very nice:
        _stream = &in;

        if (!read_version()) return false;
        if (!read_integer_constants()) return false;
        if (!read_unsigned_integer_constants()) return false;
        log_abc("Done reading unsigned integer constants.");
        
    if (!read_double_constants()) return false;
        log_abc("Done reading double constants.");
        
    if (!read_string_constants()) return false;
        log_abc("Done reading string constants.");
        
    if (!read_namespaces()) return false;
        log_abc("Done reading namespaces.");
        
    if (!read_namespace_sets()) return false;
        log_abc("Done reading namespace sets.");
        
    if (!read_multinames()) return false;
        log_abc("Done reading multinames.");
        
    if (!read_method_infos()) return false;
        log_abc("Done reading method infos.");
        
    if (!skip_metadata()) return false;
        log_abc("Done reading metadata.");
        
    if (!read_instances()) return false;
        log_abc("Done reading instances.");
        
    if (!read_classes()) return false;
        log_abc("Done reading classes.");
        
    if (!read_scripts()) return false;
        log_abc("Done reading scripts.");
        
    if (!read_method_bodies()) return false;
        log_abc("Done reading stuff.");

        for (size_t i=0; i < _methods.size(); ++i) {
                log_abc("Method %d body:", i);
                IF_VERBOSE_PARSE(_methods[i]->print_body());
        }
        return true;
}


void
AbcBlock::update_global_name(unsigned int multiname_index)
{
        
        MultiName* multiname = &_multinamePool[multiname_index];
        string_table::key new_key = 
        _stringTable->find(_stringPool[multiname->getABCName()], false);
        multiname->setGlobalName(new_key);      
}

std::ostream&
operator<<(std::ostream& o, AbcBlock::NamespaceConstant c)
{
    switch (c)
    {
        case AbcBlock::PRIVATE_NS:
            return o << "private namespace";
        case AbcBlock::CONSTANT_NS:
            return o << "constant namespace";
        case AbcBlock::PACKAGE_NS:
            return o << "package namespace";
        case AbcBlock::PACKAGE_INTERNAL_NS:
            return o << "package internal namespace";
        case AbcBlock::PROTECTED_NS:
            return o << "protected namespace";
        case AbcBlock::EXPLICIT_NS:
            return o << "explicit namespace";
        case AbcBlock::STATIC_PROTECTED_NS:
            return o << "static protected namespace";
        default:
            return o << "invalid namespace constant";
    }
}

std::ostream&
operator<<(std::ostream& o, AbcBlock::MethodConstant c)
{
    switch (c)
    {
        case AbcBlock::METHOD_ARGS:
            return o << "method arg";
        case AbcBlock::METHOD_ACTIVATION:
            return o << "method activation";
        case AbcBlock::METHOD_MORE:
            return o << "method more";
        case AbcBlock::METHOD_OPTIONAL_ARGS:
            return o << "method optional args";
        case AbcBlock::METHOD_IGNORE:
            return o << "method ignore";
        case AbcBlock::METHOD_NATIVE:
            return o << "method native";
        case AbcBlock::METHOD_DEFAULT_NS:
            return o << "default namespace";
        case AbcBlock::METHOD_ARG_NAMES:
            return o << "method arg names";
        default:
            return o << "invalid method constant";
    }
}

std::ostream&
operator<<(std::ostream& o, AbcBlock::InstanceConstant c)
{
    switch (c)
    {
        case AbcBlock::INSTANCE_SEALED:
            return o << "instance sealed";
        case AbcBlock::INSTANCE_FINAL:
            return o << "instance final";
        case AbcBlock::INSTANCE_INTERFACE:
            return o << "instance interface";
        case AbcBlock::INSTANCE_DYNAMIC:
            return o << "instance dynamic";
        case AbcBlock::INSTANCE_PROTECTED_NS:
            return o << "instance protected namespace";
        default:
            return o << "invalid instance constant";
    }
}

std::ostream&
operator<<(std::ostream& o, AbcBlock::PoolConstant c)
{
    switch (c)
    {
        case AbcBlock::POOL_STRING:
            return o << "pool string";
        case AbcBlock::POOL_INTEGER:
            return o << "pool integer";
        case AbcBlock::POOL_UINTEGER:
            return o << "pool uinteger";
        case AbcBlock::POOL_DOUBLE:
            return o << "pool double";
        case AbcBlock::POOL_NAMESPACE:
            return o << "pool namespace";
        case AbcBlock::POOL_FALSE:
            return o << "pool false";
        case AbcBlock::POOL_TRUE:
            return o << "pool true";
        case AbcBlock::POOL_NULL:
            return o << "pool null";
        default:
            return o << "invalid pool constant";

    }
}

} // namespace abc
} // namespace gnash


/* [<][>][^][v][top][bottom][index][help] */