/* [<][>][^][v][top][bottom][index][help] */
DEFINITIONS
This source file includes following definitions.
- finalize
- finalize_mbody
- read
- prepare
- check_multiname_name
- check_multiname_namespace
- check_multiname_namespaceset
- setMultinameNames
- setNamespaceURI
- locateClass
- locateClass
- read_version
- read_integer_constants
- read_unsigned_integer_constants
- read_double_constants
- read_string_constants
- read_namespaces
- read_namespace_sets
- read_multinames
- pool_value
- read_method_infos
- skip_metadata
- read_instances
- read_classes
- read_scripts
- read_method_bodies
- read
- 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 = ∈
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