Files
avmplus/core/AbcParser.cpp
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

2229 lines
83 KiB
C++

/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "avmplus.h"
#ifdef AVMPLUS_VERBOSE
#define verbose_only(...) __VA_ARGS__
#else
#define verbose_only(...)
#endif
namespace avmplus
{
/**
* parse a .abc file completely
* @param code
* @return
*/
PoolObject* AbcParser::decodeAbc(AvmCore* core, ScriptBuffer code,
Toplevel* toplevel,
Domain* domain,
const NativeInitializer* natives,
ApiVersion apiVersion)
{
int version;
int result = canParse(code, &version);
#ifdef AVMPLUS_VERBOSE
if (core->isVerbose(VB_parse))
core->console << "major=" << (version&0xFFFF) << " minor=" << (version>>16) << "\n";
#endif
if (result != 0) {
switch (result) {
case kInvalidMagicError:
toplevel->throwVerifyError(kInvalidMagicError, core->toErrorString(version>>16), core->toErrorString(version&0xFFFF));
case kCorruptABCError:
default:
toplevel->throwVerifyError(kCorruptABCError);
}
}
#ifdef VMCFG_AOT
if(!natives) // no natives -- runtime-loaded script
{
if(version == 0 && code.getSize() == 24) // only ABC "references" -- these are 0.0 version followed by SHA1
{
void *sha1 = (void *)(code.getBuffer() + 4);
for(uint32_t i = 0; i < nAOTInfos; i++)
{
const AOTInfo *aotInfo = &aotInfos[i];
AvmAssert(aotInfo != NULL);
MMGC_STATIC_ASSERT(sizeof(aotInfo->origABCSHA1) == 20);
if(!memcmp(aotInfo->origABCSHA1, sha1, 20))
{
ScriptBuffer code = ScriptBuffer(new (core->GetGC()) ReadOnlyScriptBufferImpl(aotInfo->abcBytes, aotInfo->nABCBytes));
NativeInitializer ninit(core, NULL, aotInfo, 0, 0);
PoolObject *pool = decodeAbc(core, code, toplevel, domain, &ninit, apiVersion);
AvmAssert(pool != NULL);
AvmAssert(!pool->isBuiltin);
AvmAssert(pool->aotInfo == aotInfo);
#ifdef DEBUGGER
// TODO: is there a better pace to do this for both builtins and user scripts?
addAOTDebugInfo(pool);
#endif
return pool;
}
}
}
toplevel->throwVerifyError( kCorruptABCError ); // not a reference or not found
return NULL;
}
#endif
AbcParser parser(core, code, toplevel, domain, natives);
PoolObject *pObject = parser.parse(apiVersion);
if ( !pObject ) {
toplevel->throwVerifyError( kCorruptABCError );
/*NOTREACHED*/
return NULL;
}
// register this pool giving it a unique id based on MethodInfo count
core->assignPoolId(pObject);
return pObject;
}
#if defined(VMCFG_AOT) && defined(DEBUGGER)
void AbcParser::addAOTDebugInfo(PoolObject *pool)
{
AvmCore *core = pool->core;
const AOTInfo *aotInfo = pool->aotInfo;
Debugger *debugger = core->debugger();
if(debugger)
{
const MethodDebugInfo* methodDebugInfos = aotInfo->methodDebugInfos;
if(methodDebugInfos != NULL)
{
// create the structure representing the ABC
AbcFile* abc = AbcFile::create(core->GetGC(), core, 0);
for(int n = 0; n < (int)aotInfo->nABCMethods; n++)
{
MethodInfo *f = pool->getMethodInfo(n);
// create DMIs
const MethodDebugInfo &info = methodDebugInfos[n];
DebuggerMethodInfo* dmi = DebuggerMethodInfo::create(core, info.local_count, -1, info.scope_count);
pool->_method_dmi.set(n, dmi);
// set the file in the MethodInfo
f->setFile(abc);
// get a SourceFile
SourceFile *src = NULL;
if(info.file_name)
{
Stringp fileName = pool->getString(info.file_name);
src = abc->sourceNamed(fileName);
if(!src)
{
src = SourceFile::create(core->GetGC(), fileName);
abc->sourceAdd(src);
}
}
// set register names
for(int i = 0; i < info.local_count; i++)
{
int name = info.local_names[i];
if(name)
f->setRegName(i, pool->getString(name));
}
// add line info
if(src)
for(int i = 0; i < info.line_count; i++)
src->addLine(info.lines[i], f, 0);
}
// hook the ABC in
debugger->pool2abcIndex.add(pool, (const void *)debugger->abcList.length());
debugger->abcList.add(abc);
}
}
}
#endif
int AbcParser::canParse(ScriptBuffer code, int* version)
{
if (version != NULL)
*version = 0;
if (code.getSize() < 4)
return kCorruptABCError;
int v = AvmCore::readU16(&code[0]) | AvmCore::readU16(&code[2])<<16;
if (version != NULL)
*version = v;
switch (v) {
#ifdef VMCFG_AOT
case 0: // just a "reference" to an AOT-ed ABC!
#endif
case (46<<16|16): // Original ABC version number
#ifdef VMCFG_SWF12
case (47<<16|12): // Flash player TBD
return 0;
#endif
#ifdef VMCFG_SWF13
case (47<<16|13): // Flash player TBD
return 0;
#endif
#ifdef VMCFG_SWF14
case (47<<16|14): // Flash player Anza
return 0;
#endif
#ifdef VMCFG_SWF15
case (47<<16|15): // Flash player Brannan
return 0;
#endif
#ifdef VMCFG_SWF16
case (47<<16|16): // Flash player Cyril
return 0;
#endif
#ifdef VMCFG_SWF17
case (47<<16|17): // Flash player Dolores
return 0;
#endif
#ifdef VMCFG_SWF18
case (47<<16|18): // Flash player TBD
return 0;
#endif
#ifdef VMCFG_SWF19
case (47<<16|19): // Flash player TBD
return 0;
#endif
#ifdef VMCFG_SWF20
case (47<<16|20): // Flash player TBD
return 0;
#endif
default:
return kInvalidMagicError;
}
}
AbcParser::AbcParser(AvmCore* core, ScriptBuffer code,
Toplevel* toplevel,
Domain* domain,
const NativeInitializer* natives)
: instances(core->GetGC(), 0),
toplevel(toplevel),
domain(domain),
metaNames(core->GetGC(), 0),
cachedOsrThresholdString(NULL),
cachedOsrThresholdValue(0)
{
this->core = core;
this->code = code;
this->pool = NULL;
this->version = AvmCore::readU16(&code[0]) | AvmCore::readU16(&code[2])<<16;
this->pos = &code[4];
this->natives = natives;
abcStart = &code[0];
abcEnd = &code[(int)code.getSize()];
classCount = 0;
// Flag overrides based on version go here
if (this->version >= (47<<16|12)) {
// Flash Player Wasabi
}
if (this->version >= (47<<16|13)) {
// Flash Player Serrano
}
if (this->version >= (47<<16|14)) {
// Flash Player Anza
}
if (this->version >= (47<<16|15)) {
// Flash Player Brannan
}
if (this->version >= (47<<16|16)) {
// Flash Player Cyril
}
if (this->version >= (47<<16|17)) {
// Flash Player Dolores
}
if (this->version >= (47<<16|18)) {
// Flash Player Ellis
}
if (this->version >= (47<<16|19)) {
// Flash Player TBD
}
if (this->version >= (47<<16|20)) {
// Flash Player TBD
}
}
Namespacep AbcParser::parseNsRef(const uint8_t* &pc) const
{
uint32_t index = readU30(pc);
if (index == 0)
{
return NULL; // AnyNamespace
}
if (index >= pool->constantNsCount)
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->constantNsCount));
return pool->cpool_ns[index];
}
#if defined(VMCFG_AOT) || defined(AVMPLUS_VERBOSE)
void AbcParser::parseTypeName(const uint8_t* &pc, Multiname& m) const
{
// only save the type name for now. verifier will resolve to traits
uint32_t index = readU30(pc);
if (index == 0)
{
// type is *
m.setNamespace(core->getPublicNamespace(pool));
m.setName(core->kAsterisk);
return;
}
if (index >= pool->cpool_mn_offsets.length())
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->cpool_mn_offsets.length()));
pool->parseMultiname(m, index);
}
#endif
uint32_t AbcParser::resolveBindingName(const uint8_t* &p, Multiname &m) const
{
uint32_t index = readU30(p);
if (index == 0 || index >= pool->cpool_mn_offsets.length())
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index),
core->toErrorString(pool->cpool_mn_offsets.length()));
pool->resolveBindingNameNoCheck(index, m, toplevel);
// Only builtin traits names can have multiple namespaces, which are used as an implementation
// mechanism of the versioning API, and express multiple introducing versions.
if (m.isBinding() && (m.isQName() || pool->isBuiltin))
return index;
else
{
toplevel->throwVerifyError(kCpoolEntryWrongTypeError, core->toErrorString(index));
return 0; // not reached
}
}
MethodInfo* AbcParser::resolveMethodInfo(uint32_t index) const
{
const uint32_t c = pool->methodCount();
if (index >= c)
toplevel->throwVerifyError(kMethodInfoExceedsCountError, core->toErrorString(index), core->toErrorString(c));
MethodInfo* f = pool->getMethodInfo(index);
if (!f)
toplevel->throwVerifyError(kMethodInfoOrderError, core->toErrorString(index));
return f;
}
Stringp AbcParser::resolveUtf8(uint32_t index) const
{
if (index > 0 && index < pool->constantStringCount)
{
return pool->getString(index);
}
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->constantStringCount));
return NULL;
}
Stringp AbcParser::parseName(const uint8_t* &pc) const
{
uint32_t index = readU30(pc);
if (index == 0)
return NULL;
return resolveUtf8(index);
}
PoolObject* AbcParser::parse(ApiVersion apiVersion)
{
// Loading a new ABC file always invalidates the traits cache(s)
core->tbCache()->flush();
core->tmCache()->flush();
// constant pool
parseCpool(apiVersion);
// parse all methodInfos in one pass. Nested functions must come before outer functions
parseMethodInfos();
// parse all metadataInfos - AVM+ doesn't care about this, so we are really just skipping them
parseMetadataInfos();
// parse classes. base classes must come first.
if (!parseInstanceInfos()) return NULL;
bool first = false;
if (CLASS_TYPE == NULL)
{
// if we haven't got types from the builtin file yet, do it now.
first = true;
core->traits.initInstanceTypes(pool);
// register "void"
addNamedTraits(core->getPublicNamespace(kApiVersion_VM_ALLVERSIONS), VOID_TYPE->name(), VOID_TYPE);
}
// type information about class objects
parseClassInfos();
if (first)
{
core->traits.initClassTypes(pool);
}
// scripts
if( !parseScriptInfos() ) return NULL;
// method bodies: code, exception info, and activation traits
parseMethodBodies();
return pool;
}
// check to see if we are trying to read past the file end or the beginning.
#define CHECK_POS(pos) do { if ((pos) < abcStart || (pos) >= abcEnd ) toplevel->throwVerifyError(kCorruptABCError); } while (0)
void AbcParser::parseExecPolicyAttributes(const uint8_t* metadata, MethodInfo* m)
{
// The metadata has already been scanned by parseMetadataInfo, thus
// there is no need to check for the end of the ABC block or to validate
// the range of the indices. We ignore unrecognized keys, but validate
// arguments on keys that we handle.
const uint8_t* p = metadata;
readU30(p); // Skip index for "ExecPolicy" string.
int values_count = readU30(p);
for (int q = 0; q < values_count; ++q)
{
Stringp key = pool->getString(readU30(p));
Stringp val = pool->getString(readU30(p));
if (key == core->kOSR) {
uint32_t threshold;
if (val == cachedOsrThresholdString) {
threshold = cachedOsrThresholdValue;
} else {
// For convenience and consistency, we require the value
// to have the form of an index property, though any string
// recognizable as an unsigned integer could be permitted.
// TODO: Provide a more specific error message.
if (!val->parseIndex(threshold))
toplevel->throwVerifyError(kCorruptABCError);
cachedOsrThresholdString = val;
cachedOsrThresholdValue = threshold;
}
// Unless OSR is globally disabled, set the OSR threshold for this
// method as specified in the attribute and enable OSR if nonzero.
// Silently ignore attempts to set an OSR threshold for a native method.
if (core->config.osr_enabled && !m->isNative())
m->setOSR(threshold);
}
}
}
/**
* setting up a traits that extends another one. Two parser passes are required,
* one for sizing the traits object and the other for allocating it and filling
* it in.
*/
Traits* AbcParser::parseTraits(uint16_t sizeofInstance,
uint16_t offsetofSlots,
Traits* base,
Namespacep ns,
Stringp name,
MethodInfo* script,
TraitsPosPtr traitsPos,
TraitsPosType posType,
Namespacep protectedNamespace,
bool makeFinal)
{
uint32_t nameCount = readU30(pos);
// Very generous check for nameCount being way too large.
if (nameCount > (uint32_t)(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
// Interfaces cant't have base traits
if (posType == TRAITSTYPE_INTERFACE && base != NULL)
toplevel->throwVerifyError(kCannotExtendError, name, core->toErrorString(base));
#ifdef AVMPLUS_VERBOSE
if (pool->isVerbose(VB_parse))
core->console << " trait_count=" << nameCount << "\n";
#endif
Traits* traits = Traits::newTraits(pool, base, sizeofInstance,offsetofSlots , traitsPos, posType);
traits->set_names(ns, name);
traits->protectedNamespace = protectedNamespace;
GCList<MethodInfo> methodsOf(core->GetGC(), kListInitialCapacity);
for (uint32_t i=0; i < nameCount; i++)
{
Multiname mn;
const uint32_t qn_index = resolveBindingName(pos, mn);
Namespacep ns;
const NamespaceSet* nss;
if (mn.namespaceCount() > 1) {
nss = (NamespaceSet*) mn.getNsset();
ns = nss->nsAt(0);
}
else {
ns = mn.getNamespace();
if (!ns)
toplevel->throwVerifyError(kIllegalNamespaceError);
nss = NamespaceSet::create(core->GetGC(), ns);
}
Stringp name = mn.getName();
CHECK_POS(pos);
int tag = *pos++;
TraitKind kind = (TraitKind) (tag & 0x0f);
uint32_t class_index = 0;
uint32_t method_index = 0;
uint32_t slot_id = 0;
uint32_t value_index = 0;
uint32_t earlyDispId = 0;
bool needsDxns = false;
// The ABC format restricts the metadata index to a 30-bit
// unsigned quantity, so uint32_t(~0) should be safe as a sentinel.
uint32_t execpolicy_index = uint32_t(~0);
#ifdef AVMPLUS_VERBOSE
Multiname typeName;
#endif
// Read in the trait entry.
switch (kind)
{
case TRAIT_Slot:
case TRAIT_Const:
slot_id = readU30(pos); // slot id
#ifdef AVMPLUS_VERBOSE
parseTypeName(pos, typeName);
#else
readU30(pos);
#endif
value_index = readU30(pos); // value index
if (value_index)
{
CHECK_POS(pos);
pos += 1; // value_kind
}
break;
case TRAIT_Class:
readU30(pos); // slot id
class_index = readU30(pos);
break;
case TRAIT_Getter:
case TRAIT_Setter:
case TRAIT_Method:
earlyDispId = readU30(pos); // disp id
method_index = readU30(pos);
break;
default:
// unsupported traits type
toplevel->throwVerifyError(kUnsupportedTraitsKindError, core->toErrorString(kind));
}
const uint8_t* meta_pos = pos;
if (tag & ATTR_metadata)
{
const uint32_t metadataCount = readU30(pos);
for (uint32_t metadata = 0; metadata < metadataCount; ++metadata)
{
const uint32_t index = readU30(pos);
if (index >= pool->metadataCount)
toplevel->throwVerifyError(kCorruptABCError);
Stringp name = metaNames[index];
if (name == core->kNeedsDxns)
needsDxns = true;
if (name == core->kExecPolicy) {
execpolicy_index = index;
}
}
}
switch (kind)
{
case TRAIT_Slot:
case TRAIT_Const:
case TRAIT_Class:
{
if (script)
addNamedScript(nss, name, script);
if (kind == TRAIT_Class)
{
// get the class type
if (class_index >= classCount)
toplevel->throwVerifyError(kClassInfoExceedsCountError, core->toErrorString(class_index), core->toErrorString(classCount));
// class_index could be legal (<classCount) but pointing to
// to a class not yet parsed (>= pool->classCount()), so handle that case.
Traits* ctraits = class_index < pool->classCount() ? pool->getClassTraits(class_index) : NULL;
if (!ctraits)
toplevel->throwVerifyError(kClassInfoOrderError, core->toErrorString(class_index));
#ifdef AVMPLUS_VERBOSE
if (pool->isVerbose(VB_parse))
{
core->console << " " << traitNames[kind]
<< " name=" << Multiname::Format(ns, name)
<< " slot_id=" << slot_id
<< " type=" << ctraits
<< "\n";
}
#endif
if (script)
{
// promote instance type to the vm-wide type table.
// map type name to traits
addNamedTraits(nss, name, ctraits->itraits);
if (tag & ATTR_metadata)
{
ctraits->itraits->setMetadataPos(meta_pos);
}
}
else
{
if (tag & ATTR_metadata)
{
ctraits->setMetadataPos(meta_pos);
}
}
}
else
{
#ifdef AVMPLUS_VERBOSE
if (pool->isVerbose(VB_parse))
{
core->console << " " << traitNames[kind]
<< " name=" << Multiname::Format(ns, name)
<< " slot_id=" << slot_id
<< " value_index=" << value_index
<< " type=" << typeName
<< "\n";
}
#endif
}
break;
}
case TRAIT_Getter:
case TRAIT_Setter:
case TRAIT_Method:
{
#ifdef AVMPLUS_VERBOSE
if (pool->isVerbose(VB_parse))
{
core->console << " " << traitNames[kind]
<< " name=" << Multiname::Format(ns, name)
<< " disp_id=" << earlyDispId << " (ignored)"
<< " method_index=" << method_index
<< " attr=" << ((tag&ATTR_final)?"final":"virtual");
if (tag&ATTR_override)
core->console << "|override";
core->console << "\n";
}
#endif
// id is unused here
MethodInfo* f = resolveMethodInfo(method_index);
if (core->config.methodNames)
{
pool->_method_name_indices.set(method_index, -int32_t(qn_index));
}
// Don't call makeMethodOf() yet: if the Traits are fuzzed
// (and thus verifyBindings() fails), we don't want to have
// the Method associated with these Traits, as trying to parse
// the Traits could crash.
methodsOf.add(f);
f->setKind(kind); // sets the _isGetter/_isSetter flags
if ((tag & ATTR_final) || makeFinal)
f->setFinal();
if (tag & ATTR_override)
f->setOverride();
if (needsDxns)
f->setNeedsDxns();
if (execpolicy_index < uint32_t(~0))
parseExecPolicyAttributes(pool->metadata_infos[execpolicy_index], f);
// only export one name for an accessor
// (note that addNamedScript checks for redundancy internally,
// no need to do so here)
if (script)
addNamedScript(nss, name, script);
break;
}
default:
// unsupported traits type
toplevel->throwVerifyError(kUnsupportedTraitsKindError, core->toErrorString(kind));
}
#ifdef AVMPLUS_VERBOSE
if (tag & ATTR_metadata)
{
uint32_t metadataCount = readU30(meta_pos);
for (uint32_t metadata = 0; metadata < metadataCount; ++metadata)
{
uint32_t index = readU30(meta_pos);
if (pool->isVerbose(VB_parse))
core->console << " [" << metaNames[index] << "]\n";
}
}
#endif
}
if (makeFinal)
traits->final = true;
traits->verifyBindings(toplevel);
// verifyBindings() hasn't thrown, so it's OK to associate the MethodInfos
// with the Traits.
for (uint32_t i = 0, n = methodsOf.length(); i < n; ++i)
{
if (!methodsOf[i]->makeMethodOf(traits))
toplevel->throwVerifyError(kCorruptABCError);
}
return traits;
}
void AbcParser::parseMethodInfos()
{
int methodCount = readU30(pos);
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << "method_count=" << methodCount << "\n";
}
#endif
int size = methodCount == 0 ? 1 : methodCount;
if (size > (abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
MMGC_MEM_TYPE(pool);
pool->_methods.ensureCapacity(size);
#ifdef DEBUGGER
if (core->debugger())
{
pool->_method_dmi.ensureCapacity(size);
for (int i = 0; i < size; ++i)
pool->_method_dmi.set(i, NULL);
}
#endif
if (core->config.methodNames)
{
pool->_method_name_indices.ensureCapacity(size);
}
#ifdef AVMPLUS_VERBOSE
const uint8_t* startpos = pos;
#endif
for (int i=0; i < methodCount; i++)
{
#ifdef AVMPLUS_VERBOSE
int offset = (int)(pos-startpos);
#endif
const uint8_t* info_pos = pos;
// MethodInfo
const int param_count = readU30(pos);
#ifdef VMCFG_AOT
const uint8_t* ret_type_pos = pos;
#endif
// @todo -- we should add an AbcParser equivalent of skipU32;
// then the next two clauses would be
// skipU32(pos, param_count+1);
// in non-verbose builds
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
Multiname returnTypeName;
parseTypeName(pos, returnTypeName);
core->console << " " << offset << ":method["<<i<<"]\n"
<< " returnType=" << returnTypeName << "\n"
<< " param_count=" << param_count
<< "\n";
}
else
#endif
{
readU30(pos);// return type name
}
for( int j = 1; j <= param_count; ++j)
{
#ifdef AVMPLUS_VERBOSE
Multiname multiname;
parseTypeName(pos, multiname);
if(pool->isVerbose(VB_parse)) {
core->console << " paramType["<<j<<"]="<< multiname << "\n";
}
#else
readU30(pos);
#endif
}
uint32_t name_index = readU30(pos);
(void)name_index;
CHECK_POS(pos);
uint8_t abcFlags = *pos++;
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << " name_index=" << name_index;
if (name_index > 0 && name_index < pool->constantStringCount)
core->console << " \"" << pool->getString(name_index) << "\"";
core->console << "\n flags=" << (uint32_t)abcFlags << "\n";
}
#endif
const NativeMethodInfo* ni = NULL;
if (abcFlags & abcMethod_NATIVE)
{
ni = natives ? natives->getNativeInfo(i) : NULL;
#ifdef VMCFG_VERIFYALL
if (core->config.verifyonly) {
// if it has native methods then treat it as builtin
pool->isBuiltin = true;
}
else
#endif
if (!ni) {
if (pool->isBuiltin) {
// If this assert hits, you're missing a native method. Method "i"
// corresponds to the function of the same number in
// source\avmglue\avmglue.h if you're running the Flash player.
AvmAssertMsg(0, "missing native method decl");
}
toplevel->throwVerifyError(kIllegalNativeMethodError);
}
}
#ifdef VMCFG_AOT
bool isCompiled = false;
AvmThunkNativeHandler handler;
// NativeMethodInfo.handler is a union of
// pointer to function and pointer to member function.
// Zero the structure so that the entire thing is
// initialized.
// See bugzilla#647660
NativeMethodInfo compiledMethodInfo = {{0}, 0, 0};
{
Multiname returnTypeName;
parseTypeName(ret_type_pos, returnTypeName);
if (!ni && natives->getCompiledInfo(&compiledMethodInfo, &handler, returnTypeName, i))
{
ni = &compiledMethodInfo;
isCompiled = true;
}
}
#endif
const int optional_count = (abcFlags & abcMethod_HAS_OPTIONAL) ? readU30(pos) : 0;
MethodInfo* info = MethodInfo::create(core->GetGC(), i, pool, info_pos, abcFlags, ni);
#ifdef VMCFG_AOT
if (isCompiled)
info->setAotCompiled(handler);
#endif
if (core->config.methodNames)
{
pool->_method_name_indices.set(i, int32_t(name_index));
}
if (abcFlags & abcMethod_HAS_OPTIONAL)
{
for( int j = 0; j < optional_count; ++j)
{
readU30(pos);
++pos; // Kind bytes for each default value
}
if (!optional_count || (optional_count > param_count))
{
// cannot have more optional params than total params
toplevel->throwVerifyError(kCorruptABCError);
}
}
if (abcFlags & abcMethod_HAS_PARAM_NAMES)
{
// AVMPlus doesn't care about the param names, just skip past them
for( int j = 0; j < param_count; ++j )
{
readU30(pos);
}
}
// save method info pointer. we will verify code later.
pool->_methods.set(i, info);
#ifdef DEBUGGER
// don't create the corresponding DebuggerMethodInfo yet, we'll do that in parseMethodBodies
#endif
}
}
void AbcParser::parseMetadataInfos()
{
uint32_t metadataCount = readU30(pos);
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << "metadata_count=" << metadataCount << "\n";
}
#endif
if (metadataCount > uint32_t(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
pool->metadata_infos.ensureCapacity(metadataCount);
pool->metadataCount = metadataCount;
if (metadataCount > 0)
{
metaNames.ensureCapacity(metadataCount-1); // Pre-size the list
for (uint32_t i=0; i < metadataCount; i++)
{
pool->metadata_infos.set(i, pos);
// MetadataInfo
uint32_t index = readU30(pos);
Stringp name = resolveUtf8(index);
metaNames.set(i, name);
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << " " << name;
}
#endif
int values_count = readU30(pos);
if (values_count > 0)
{
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << "(";
}
#endif
for(int q = 0; q < values_count; ++q)
{
uint32_t a = readU30(pos);
uint32_t b = readU30(pos);
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << a << "," << b;
if (q+1 < values_count)
core->console << " ";
}
#else
(void)a;
(void)b;
#endif
}
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << ")";
}
#endif
}
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << "\n";
}
#endif
}
}
}
void AbcParser::parseMethodBodies()
{
int bodyCount = readU30(pos);
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << "bodies_count=" << bodyCount << "\n";
}
#endif
#ifdef AVMPLUS_VERBOSE
const uint8_t* startpos = pos;
#endif
for (int i=0; i < bodyCount; i++)
{
#ifdef AVMPLUS_VERBOSE
int offset = (int)(pos-startpos);
#endif
uint32_t method_index = readU30(pos);
MethodInfo* info = resolveMethodInfo(method_index);
const uint8_t *body_pos = pos;
int max_stack = readU30(pos);
(void)max_stack;
int local_count = readU30(pos);
(void)local_count;
// can't do this check here because param_count isn't available
// until the method is resolved. (we could do some tricks to extract
// it, but we just defer this check to Verifier::verify instead.)
//if (local_count < info->param_count()+1)
//{
// // must have enough locals to hold all parameters including this
// toplevel->throwVerifyError(kCorruptABCError);
//}
// TODO change file format, just want local max_scope
int init_scope_depth = readU30(pos);
(void)init_scope_depth;
int max_scope_depth = readU30(pos);
(void)max_scope_depth;
int code_length = readU30(pos);
if (code_length <= 0
#ifdef VMCFG_AOT
&& !info->needActivation() // AOT allows a dummy body so that it can represent activation traits
#endif
)
{
toplevel->throwVerifyError(kInvalidCodeLengthError, core->toErrorString(code_length));
}
// check to see if we are trying to jump past the file end or the beginning.
if ( pos < abcStart || pos+code_length >= abcEnd )
toplevel->throwVerifyError(kCorruptABCError);
pos += code_length;
int exception_count = readU30(pos);
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << " " << offset << ":method["<<method_index<<"] max_stack=" << max_stack
<< " local_count=" << local_count
<< " init_scope_depth=" << init_scope_depth
<< " max_scope_depth=" << max_scope_depth
<< " code_length=" << code_length
<< " exception_count=" << exception_count
<< "\n";
}
#endif
if (exception_count != 0)
{
info->setHasExceptions();
for (int i=0; i<exception_count; i++)
{
// this will be parsed when method is verified.
// see MethodInfo::resolveSignature
#ifdef AVMPLUS_VERBOSE
int from = readU30(pos);
int to = readU30(pos);
int target = readU30(pos);
Multiname typeName;
parseTypeName(pos, typeName);
Multiname qn;
uint32_t name_index = (version != (46<<16|15)) ? readU30(pos) : 0;
if (name_index != 0)
{
if (name_index >= pool->cpool_mn_offsets.length())
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(name_index), core->toErrorString(pool->constantCount));
pool->parseMultiname(qn, name_index);
}
if (pool->isVerbose(VB_parse))
{
core->console << " exception["<<i<<"] from="<< from
<< " to=" << to
<< " target=" << target
<< " type=" << typeName
<< " name=";
if (name_index)
{
core->console << qn;
}
else
{
core->console << "(none)";
}
core->console << "\n";
}
#else
readU30(pos); // from
readU30(pos); // to
readU30(pos); // target
readU30(pos); // type name
if (version != (46<<16|15))
{
const uint32_t name_index = readU30(pos); // variable name
if (name_index >= pool->cpool_mn_offsets.length())
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(name_index), core->toErrorString(pool->constantCount));
}
#endif
}
}
if (info->hasMethodBody())
{
// Interface methods should not have bodies
Traits* declaringTraits = info->declaringTraits();
if (declaringTraits && declaringTraits->isInterface())
{
toplevel->throwVerifyError(kIllegalInterfaceMethodBodyError, core->toErrorString(info));
}
#ifdef DEBUGGER
if (core->debugger())
{
DebuggerMethodInfo* dmi = DebuggerMethodInfo::create(core, local_count, code_length, max_scope_depth - init_scope_depth);
pool->_method_dmi.set(method_index, dmi);
}
#endif
// if non-zero, we have a duplicate method body - throw a verify error
if (info->abc_body_pos())
{
toplevel->throwVerifyError(kDuplicateMethodBodyError, core->toErrorString(info));
}
AvmAssert(!info->isResolved());
info->set_abc_body_pos(body_pos);
// there will be a traits_count here, even if abcMethod_NEED_ACTIVATION is not
// set. So we parse the same way all the time. We could reduce file size and
// memory by omitting the count + traits completely.
const uint8_t* traits_pos = pos;
int nameCount = readU30(pos);
if (info->needActivation() || nameCount > 0)
{
pos = traits_pos;
Namespacep ns = NULL;
Stringp name = NULL;
#ifdef AVMPLUS_VERBOSE
if (core->config.methodNames)
{
ns = core->getPublicNamespace(pool);
name = core->internString(info->getMethodNameWithTraits(declaringTraits));
}
#endif
// activation traits are raw types, not subclasses of object. this is
// okay because they aren't accessable to the programming model.
Traits* act = parseTraits(sizeof(ScriptObject),
sizeof(ScriptObject),
NULL,
ns,
name,
NULL,
traits_pos,
TRAITSTYPE_ACTIVATION,
NULL);
info->init_activationTraits(act);
}
else
{
// TODO remove this assert once abc format is adjusted
AvmAssert(nameCount == 0);
}
}
else
{
#ifdef VMCFG_AOT
if (info->needActivation())
{
Namespacep ns = NULL;
Stringp name = NULL;
#ifdef AVMPLUS_VERBOSE
if (core->config.methodNames)
{
ns = core->getPublicNamespace(pool);
name = core->internString(info->getMethodNameWithTraits(info->declaringTraits()));
}
#endif
// activation traits are raw types, not subclasses of object. this is
// okay because they aren't accessable to the programming model.
Traits* act = parseTraits(sizeof(ScriptObject),
sizeof(ScriptObject),
NULL,
ns,
name,
NULL,
pos,
TRAITSTYPE_ACTIVATION,
NULL);
info->init_activationTraits(act);
pool->aotInfo->activationTraits[method_index] = act;
} else
#endif
{
// native methods should not have bodies!
toplevel->throwVerifyError(kIllegalNativeMethodBodyError, core->toErrorString(info));
}
}
}
}
void AbcParser::parseCpool(ApiVersion apiVersion)
{
pool = PoolObject::create(core, code, pos, apiVersion);
pool->domain = domain;
#ifdef VMCFG_AOT
AvmAssert(natives != 0);
pool->isBuiltin = natives->hasBuiltins();
pool->aotInfo = natives->get_aotInfo();
#else
pool->isBuiltin = (natives != NULL);
#endif
verbose_only(pool->verbose_vb = core->config.verbose_vb); // pool picks up global settings
verbose_only(const uint8_t* startpos);
uint32_t int_count = readU30(pos);
// sanity check to prevent huge allocations
if (int_count > (uint32_t)(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
DataList<int32_t>& cpool_int = pool->cpool_int;
cpool_int.ensureCapacity(int_count);
pool->constantIntCount = int_count;
verbose_only(startpos = pos);
for(uint32_t i = 1; i < int_count; ++i)
{
verbose_only(int offset = (int)(pos-startpos));
// S32 value
cpool_int.set(i, readS32(pos));
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << " " << offset << ":" << "cpool_int["<<(uint32_t)i<<"]="
<<constantNames[CONSTANT_Int] << " ";
core->console << cpool_int[i] << "\n";
}
#endif
}
uint32_t uint_count = readU30(pos);
if (uint_count > (uint32_t)(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
DataList<uint32_t>& cpool_uint = pool->cpool_uint;
cpool_uint.ensureCapacity(uint_count);
pool->constantUIntCount = uint_count;
verbose_only(startpos = pos);
for(uint32_t i = 1; i < uint_count; ++i)
{
verbose_only(int offset = (int)(pos-startpos));
// U32 value
cpool_uint.set(i, (unsigned)readS32(pos));
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << " " << offset << ":" << "cpool_uint["<<i<<"]="
<<constantNames[CONSTANT_UInt] << " ";
core->console << (double)cpool_uint[i];
core->console << "\n";
}
#endif
}
uint32_t double_count = readU30(pos);
if (double_count > (uint32_t)(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
GCList<GCDouble>& cpool_double = pool->cpool_double;
cpool_double.ensureCapacity(double_count);
pool->constantDoubleCount = double_count;
verbose_only(startpos = pos);
for(uint32_t i = 1; i < double_count; ++i)
{
verbose_only(int offset = (int)(pos-startpos));
double value = readDouble(pos);
cpool_double.set(i, (GCDouble*)(core->allocDouble(value)&~7));
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << " " << offset << ":" << "cpool_double["<<i<<"]="
<<constantNames[CONSTANT_Double] << " ";
core->console << value;
core->console << "\n";
}
#endif
}
#ifdef VMCFG_FLOAT
if(pool->hasFloatSupport())
{
// read float constant pool
uint32_t float_count = readU30(pos);
if (float_count > (uint32_t)(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
GCList<GCFloat>& cpool_float = pool->cpool_float;
cpool_float.ensureCapacity(float_count);
pool->constantFloatCount = float_count;
verbose_only(startpos = pos);
for(uint32_t i = 1; i < float_count; ++i)
{
verbose_only(int offset = (int)(pos-startpos));
float value = readFloat(pos);
cpool_float.set(i, (GCFloat*)(core->allocFloat(value)&~7));
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << " " << offset << ":" << "cpool_float["<<i<<"]="
<< constantNames[CONSTANT_Float] << " ";
core->console << value;
core->console << "\n";
}
#endif
}
// read float4 constant pool
uint32_t float4_count = readU30(pos);
if (float4_count > (uint32_t)(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
GCList<GCFloat4>& cpool_float4 = pool->cpool_float4;
cpool_float4.ensureCapacity(float4_count);
pool->constantFloat4Count = float4_count;
verbose_only(startpos = pos);
for(uint32_t i = 1; i < float4_count; ++i)
{
verbose_only(int offset = (int)(pos-startpos));
float4_t value = readFloat4(pos);
cpool_float4.set(i, (GCFloat4*)(core->allocFloat4(value)&~7));
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
const float* fp = reinterpret_cast<const float*>(&value);
core->console << " " << offset << ":" << "cpool_float4["<<i<<"]="
<< constantNames[CONSTANT_Float4] << " ";
core->console << fp[0] << "," << fp[1] << "," << fp[2] << "," << fp[3];
core->console << "\n";
}
#endif
}
}
else
{
pool->constantFloatCount = 0;
pool->constantFloat4Count = 0;
}
#endif // VMCFG_FLOAT
// In principle, if the numeric pools are sized to make element zero available then initialize that element.
//
// Note that for int, uint, double, float, and float4 we still throw VerifyError in the Verifier if element
// zero is referenced from an instruction, but that really should change, the documentation has traditionally
// said that they're available.
//
// In practice, the Debugger may actually access these values if they are referenced even in content that
// does not verify, so make them available always. Bugzilla 520888.
pool->cpool_int.set(0, 0);
pool->cpool_uint.set(0, 0);
pool->cpool_double.set(0, (GCDouble*) atomPtr(core->kNaN));
#ifdef VMCFG_FLOAT
pool->cpool_float.set(0, (GCFloat*) atomPtr(core->kFltNaN));
pool->cpool_float4.set(0, (GCFloat4*) atomPtr(core->kFlt4NaN));
#endif
uint32_t string_count = readU30(pos);
if (string_count > (uint32_t)(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
MMGC_MEM_TYPE(pool);
pool->setupConstantStrings(string_count);
#ifdef AVMPLUS_VERBOSE
startpos = pos;
#endif
pool->_abcStringStart = pos;
ConstantStringData* dataP = pool->_abcStrings->data;
AvmAssert(core->kEmptyString != NULL);
dataP->str = core->kEmptyString;
for(uint32_t i = 1; i < string_count; ++i)
{
#ifdef AVMPLUS_VERBOSE
int offset = (int)(pos-startpos);
#endif
// save the uint8_t position into the cpool array
(++dataP)->abcPtr = pos;
// number of characters
// todo - is compiler emitting no. of chars or no. of bytes?
// don't call readU30(), as it can throw. call readU32 and sanity-check ourself here.
uint32_t len = AvmCore::readU32(pos);
// check to see if we are trying to read past the file end or the beginning.
// FIXME: bug #545652, should check if UTF8 is valid, skipping for compatibility.
if ((len & 0xc0000000) || pos < abcStart || pos+len >= abcEnd)
{
// if we throw a verify error here, _abcStringEnd will never be set, and _abcStrings
// will be left in an inconsistent state. having _abcStringStart set but not _abcStringEnd
// can cause dynamicizeStrings to make poor decisions. So clean up before throwing.
pool->_abcStringStart = NULL;
pool->_abcStringEnd = NULL;
VMPI_memset(pool->_abcStrings->data, 0, string_count*sizeof(ConstantStringData*));
toplevel->throwVerifyError(kCorruptABCError);
}
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << " " << offset << ":" << "cpool_string["<<i<<"]="
<< constantNames[CONSTANT_Utf8] << " ";
core->console.writeN( (const char*)pos, len);
core->console << "\n";
}
#endif
// skip the UTF-8 string
pos += len;
}
pool->_abcStringEnd = pos;
uint32_t ns_count = readU30(pos);
if (ns_count > (uint32_t)(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
RCList<Namespace> &cpool_ns = pool->cpool_ns;
MMGC_MEM_TYPE(pool);
cpool_ns.ensureCapacity(ns_count);
pool->constantNsCount = ns_count;
#ifdef AVMPLUS_VERBOSE
startpos = pos;
#endif
for( uint32_t i = 1; i < ns_count; ++i )
{
#ifdef AVMPLUS_VERBOSE
int offset = (int)(pos-startpos);
#endif
CHECK_POS(pos);
CPoolKind kind = (CPoolKind) *(pos++);
switch(kind)
{
case CONSTANT_Namespace:
case CONSTANT_PackageNamespace:
case CONSTANT_PackageInternalNs:
case CONSTANT_ProtectedNamespace:
case CONSTANT_ExplicitNamespace:
case CONSTANT_StaticProtectedNs:
{
uint32_t index = readU30(pos);
Namespace::NamespaceType type = Namespace::NS_Public;
switch(kind)
{
case CONSTANT_PackageInternalNs:
type = Namespace::NS_PackageInternal;
break;
case CONSTANT_ProtectedNamespace:
type = Namespace::NS_Protected;
break;
case CONSTANT_ExplicitNamespace:
type = Namespace::NS_Explicit;
break;
case CONSTANT_StaticProtectedNs:
type = Namespace::NS_StaticProtected;
break;
default:
break;
}
if (index)
{
Stringp uri = resolveUtf8(index);
// Default everything to kApiVersion_VM_ALLVERSIONS for now;
// getBaseURIAndApiVersion() will overwrite apiVersion
// if the uri has a version mark. (Note that non-builtin
// ABC should never have version marks, but we call this anyway
// to strip any possible mark; this is of dubious value but it's
// what we've done in the past, so we'll continue to do so.)
ApiVersion apiVersion = kApiVersion_VM_ALLVERSIONS;
bool const hasVersionMark = core->getBaseURIAndApiVersion(uri, apiVersion);
if (pool->isBuiltin)
{
// Finally, builtin public namespaces that are in the versioned-uri list,
// but don't have an explicit version mark, get whisked off into the VM_INTERNAL
// hidey hole.
if (!hasVersionMark && type == Namespace::NS_Public && core->isVersionedURI(uri))
apiVersion = kApiVersion_VM_INTERNAL;
}
else
{
// If it's not builtin, it always gets the pool's apiVersion,
// even if the uri has a version mark (which shouldn't happen
// with standard tools, but a deliberately constructed ABC
// chunk could attempt to do so in order to access otherwise
// unavailable API). (But only for
if (type == Namespace::NS_Public)
apiVersion = pool->getApiVersion();
}
Namespacep ns = core->internNamespace(core->newNamespace(uri, type, apiVersion));
cpool_ns.set(i, ns);
}
else
{
// issue this looks wrong. should uri be ""?
Atom uri = undefinedAtom;
cpool_ns.set(i, core->internNamespace(core->newNamespace(uri, type)));
}
break;
}
case CONSTANT_PrivateNs:
{
uint32_t index = readU30(pos);
Stringp uri = index ? resolveUtf8(index) : (Stringp)core->kEmptyString;
Namespacep ns = Namespace::create(core->GetGC(), nullStringAtom, uri, Namespace::NS_Private, kApiVersion_VM_ALLVERSIONS);
cpool_ns.set(i, ns);
break;
}
default:
{
toplevel->throwVerifyError(kCpoolEntryWrongTypeError, core->toErrorString(i));
}
}
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << " " << offset << ":" << "cpool_ns["<<i<<"]="
<<constantNames[kind] << " " << cpool_ns[i] << "\n";
}
#endif
}
uint32_t ns_set_count = readU30(pos);
if (ns_set_count > (uint32_t)(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
GCList<NamespaceSet>& cpool_ns_set = pool->cpool_ns_set;
cpool_ns_set.ensureCapacity(ns_set_count);
pool->constantNsSetCount = ns_set_count;
#ifdef AVMPLUS_VERBOSE
startpos = pos;
#endif
for( uint32_t i = 1; i < ns_set_count; ++i)
{
#ifdef AVMPLUS_VERBOSE
int offset = (int)(pos-startpos);
#endif
uint32_t ns_count = readU30(pos);
if (ns_count > (uint32_t)(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
NamespaceSet* namespace_set = NamespaceSet::_create(core->GetGC(), ns_count);
for(uint32_t j=0; j < ns_count; ++j)
{
Namespacep ns = parseNsRef(pos);
if (!ns)
toplevel->throwVerifyError(kIllegalNamespaceError);
namespace_set->_initNsAt(j, ns);
}
cpool_ns_set.set(i, namespace_set);
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << " " << offset << ":" << "cpool_ns_set["<<i<<"]="
<< constantNames[CONSTANT_NamespaceSet] << " " << cpool_ns_set[i] << "\n";
}
#endif
}
uint32_t mn_count = readU30(pos);
if (mn_count > (uint32_t)(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
DataList<uint32_t>& cpool_mn_offsets = pool->cpool_mn_offsets;
MMGC_MEM_TYPE(pool);
cpool_mn_offsets.ensureCapacity(mn_count);
for(uint32_t i = 1; i < mn_count; ++i )
{
CHECK_POS(pos);
uint32_t const offset = uint32_t(pos - pool->_abcStart);
cpool_mn_offsets.set(i, offset);
CPoolKind kind = (CPoolKind)*pos++;
switch(kind)
{
case CONSTANT_Qname:
case CONSTANT_QnameA:
{
// U16 namespace_index
// U16 name_index
// parse a multiname with one namespace (aka qname)
parseNsRef(pos);
parseName(pos);
break;
}
case CONSTANT_RTQname:
case CONSTANT_RTQnameA:
{
// U16 name_index
// parse a multiname with just a name; ns fetched at runtime
parseName(pos);
break;
}
case CONSTANT_RTQnameL:
case CONSTANT_RTQnameLA:
{
break;
}
case CONSTANT_Multiname:
case CONSTANT_MultinameA:
{
parseName(pos);
uint32_t index = readU30(pos);
if (!index || index >= pool->constantNsSetCount)
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->constantNsSetCount));
// If it is in the range of Namespace Sets then it must be a namespace set/
break;
}
case CONSTANT_MultinameL:
case CONSTANT_MultinameLA:
{
uint32_t index = readU30(pos);
if (!index || index >= pool->constantNsSetCount)
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->constantNsSetCount));
// If it is in the range of Namespace Sets then it must be a namespace set.
break;
}
case CONSTANT_TypeName:
{
uint32_t index = readU30(pos);
// compare index against mn_count, *not* cpool_mn_offsets.length(), as the latter is still being built...
// it's ok to forward-reference here.
if(!index || index >= mn_count)
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(mn_count));
index = readU30(pos);
if(index != 1)
toplevel->throwVerifyError(kCorruptABCError);
index = readU30(pos);
// validate that parameterized type is valid. (note that index == 0 is
// legal here, and is used for Vector<*>)
if (index >= mn_count)
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(mn_count));
break;
}
default:
toplevel->throwVerifyError(kCpoolEntryWrongTypeError, core->toErrorString(i));
}
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << " " << offset << ":" << "cpool_mn["<<i<<"]="
<<constantNames[kind] << " ";
Multiname name;
pool->parseMultiname(name, i);
core->console << name;
core->console << "\n";
}
#endif
}
{
// Note that BitSet uses mmfx memory, not gc memory, so we must not
// throw an exception while it's alive, lest the memory leak. Hence
// the gyration with "goto corrupted" below.
BitSet seen(mn_count);
// verify that parameterized types have no cycles.
// we have to do this in a second pass since forward-references are legal
for (uint32_t i = 1; i < mn_count; ++i )
{
const uint8_t* p = pool->_abcStart + cpool_mn_offsets[i];
if (*p++ != CONSTANT_TypeName)
continue;
uint32_t basetype_index = readU30(p); // type being parameterized (currently, always Vector)
// the type being parameterized can't itself be a parameterized type.
// (note that this check also means cycles for this value are impossible
// and need not be checked)
const uint8_t* p_basetype = pool->_abcStart + cpool_mn_offsets[basetype_index];
if (*p_basetype == CONSTANT_TypeName)
goto corrupted; // don't throw: that would leak the BitSet
readU30(p); // param count (currently always 1)
uint32_t pt_index = readU30(p); // param type(s) (currently exactly one)
// now check for cycles. we can nest arbitrarily deep (eg Vector<Vector<Vector<uint>>>)
// but we can't have a cycle.
seen.reset();
for (;;)
{
if (pt_index == 0) // Vector<*> is a special case
break;
seen.set(pt_index);
const uint8_t* p_paramtype = pool->_abcStart + cpool_mn_offsets[pt_index];
if (*p_paramtype++ != CONSTANT_TypeName)
break;
readU30(p_paramtype); // skip type being parameterized (currently, always Vector)
readU30(p_paramtype); // skip param count (currently always 1)
pt_index = readU30(p_paramtype); // param type(s) (currently exactly one)
if (seen.get(pt_index))
goto corrupted; // don't throw: that would leak the BitSet
}
}
}
return;
corrupted:
toplevel->throwVerifyError(kCorruptABCError);
}
void AbcParser::addNamedTraits(NamespaceSetp nss, Stringp name, Traits* itraits)
{
// check to see if the base namespace has been added, if so then
// all versions have been added
Namespace* ns = nss->nsAt(0);
if (!ns->isPrivate()) {
ApiVersion apiVersion = kApiVersion_VM_INTERNAL;
for (NamespaceSetIterator iter(nss); iter.hasNext();)
{
Namespacep nsi = iter.next();
ApiVersion a = core->getValidApiVersion(nsi->getApiVersion());
if (a < apiVersion)
apiVersion = a;
}
ns = core->getVersionedNamespace(ns, apiVersion);
core->domainMgr()->addNamedTraits(pool, name, ns, itraits);
}
}
void AbcParser::addNamedTraits(Namespacep ns, Stringp name, Traits* itraits)
{
if (!ns->isPrivate()) {
core->domainMgr()->addNamedTraits(pool, name, ns, itraits);
}
}
void AbcParser::addNamedScript(NamespaceSetp nss, Stringp name, MethodInfo* script)
{
Namespacep ns = nss->nsAt(0); // just need one
if (!ns->isPrivate())
{
ApiVersion apiVersion = kApiVersion_VM_INTERNAL;
for (NamespaceSetIterator iter(nss); iter.hasNext();)
{
Namespacep nsi = iter.next();
ApiVersion a = core->getValidApiVersion(nsi->getApiVersion());
if (a < apiVersion)
apiVersion = a;
}
ns = core->getVersionedNamespace(ns, apiVersion);
}
core->domainMgr()->addNamedScript(pool, name, ns, script);
}
bool AbcParser::parseScriptInfos()
{
/*
U16 script_count
ScriptInfo[script_count]
{
U16 init_index // method_info index of init function
Traits script_traits // traits for the global object of this package
}
*/
uint32_t count = readU30(pos);
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << "script_count=" << count << "\n";
}
#endif
#ifdef AVMPLUS_VERBOSE
const uint8_t* startpos = pos;
#endif
if (count == 0)
{
return true;
}
if (count > (uint32_t)(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
pool->_scripts.ensureCapacity(count);
// make global objects subclasses of Object
for (uint32_t i=0; i < count; i++)
{
const uint8_t* script_pos = pos;
int init_index = readU30(pos);
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << " " << (int)(script_pos-startpos) << ":script[" << i << "]"
<< " init_index=" << init_index
<< "\n";
}
#endif
MethodInfo* script = resolveMethodInfo(init_index);
Traits* declaringTraits = script->declaringTraits();
if (declaringTraits != NULL)
{
// method has already been bound to a different type. Can't bind it twice because
// it can only have one environment, for its scope chain and super references.
toplevel->throwVerifyError(kAlreadyBoundError, core->toErrorString(script), core->toErrorString(declaringTraits));
}
Traits* traits = parseTraits(sizeof(ScriptObject),
sizeof(ScriptObject),
core->traits.object_itraits,
core->getPublicNamespace(pool),
core->kglobal,
script,
script_pos,
TRAITSTYPE_SCRIPT,
NULL);
if( !traits ) return false; // parseTraits failed
// global object, make it dynamic
traits->set_needsHashtable(true);
script->makeMethodOf(traits);
traits->init = script;
script->setStaticInit();
pool->_scripts.set(i, traits);
#ifdef VMCFG_AOT
pool->aotInfo->scriptTraits[i] = traits;
#endif
}
return true;
}
bool AbcParser::parseInstanceInfos()
{
classCount = readU30(pos);
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << "class_count=" << classCount <<"\n";
}
#endif
#ifdef AVMPLUS_VERBOSE
const uint8_t* startpos = pos;
#endif
if (classCount == 0)
{
return true;
}
if (classCount > uintptr_t(abcEnd - pos))
toplevel->throwVerifyError(kCorruptABCError);
// allocate room for class infos early, to handle nested classes
pool->_classes.ensureCapacity(classCount);
instances.ensureCapacity(classCount);
for (uint32_t i=0; i < classCount; i++)
{
const uint8_t* instancepos = pos;
// CONSTANT_QName name of this class
Multiname mn;
resolveBindingName(pos, mn);
Namespacep ns;
Stringp name;
name = mn.getName();
const NamespaceSet* nss;
if (mn.namespaceCount() > 1)
{
nss = mn.getNsset();
ns = nss->nsAt(0);
}
else
{
ns = mn.getNamespace();
if (!ns)
toplevel->throwVerifyError(kIllegalNamespaceError);
nss = NamespaceSet::create(core->GetGC(), ns);
}
// resolving base class type means class heirarchy must be a Tree
Traits* baseTraits = pool->resolveTypeName(pos, toplevel);
if ((baseTraits && baseTraits->final) ||
(CLASS_TYPE != NULL && baseTraits == CLASS_TYPE) ||
// note, builtins are allowed to override Function
(FUNCTION_TYPE != NULL && baseTraits == FUNCTION_TYPE && !pool->isBuiltin))
{
// error - attempt to extend final class
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console << mn << " can't extend final class " << baseTraits << "\n";
}
#endif
toplevel->throwVerifyError(kCannotExtendFinalClass, core->toErrorString(&mn));
}
if (baseTraits && baseTraits->isInterface())
{
// error, can't extend interface
toplevel->throwVerifyError(kCannotExtendError, core->toErrorString(&mn), core->toErrorString(baseTraits));
}
// read flags: bit 0: sealed
// bit 1: final
// bit 2: interface
// bit 3: protected
CHECK_POS(pos);
int flags = *pos++;
// read protected namespace
Namespacep protectedNamespace = NULL;
if (flags & 8)
{
protectedNamespace = parseNsRef(pos);
}
int interfaceCount = readU30(pos);
const uint8_t* interfacePos = pos;
if(interfaceCount)
{
if (interfaceCount >= 0x10000000)
toplevel->throwVerifyError(kCorruptABCError);
for( int x = 0; x < interfaceCount; ++ x )
{
Traits *t = pool->resolveTypeName(pos, toplevel);
if (!t || !t->isInterface())
{
// error, can't implement non-interface type
toplevel->throwVerifyError(kCannotImplementError, core->toErrorString(&mn), core->toErrorString(t));
}
}
}
// TODO make sure the inheritance is legal.
// - can't override final members
// - overrides agree with base class signature
uint32_t iinit_index = readU30(pos);
MethodInfo* iinit = resolveMethodInfo(iinit_index);
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
// TODO: fixup this math here, since the 2's are all wrong
core->console
<< " " << (int)(instancepos-startpos) << ":instance[" << i << "]"
<< " " << mn;
if (baseTraits)
core->console << " extends " << baseTraits;
core->console
<< " interface_count=" << interfaceCount
<< " iinit_index=" << iinit_index
<< "\n";
}
#endif
TraitsPosType postype = (flags & 4) ? TRAITSTYPE_INTERFACE : TRAITSTYPE_INSTANCE;
uint16_t sizeofInstance, offsetofSlots;
// If this is a native class, use the stated instance size and offset to slot area.
const NativeClassInfo* nativeEntry;
if (natives && (nativeEntry = natives->get_class(i)) != NULL && nativeEntry->sizeofInstance)
{
sizeofInstance = nativeEntry->sizeofInstance;
offsetofSlots = nativeEntry->offsetofSlotsInstance;
// note that 0 means "put the slots at the end of my immediate parent",
// but don't assert here: builtin classes without any slots (e.g., Object)
// emit a 0 here for simplicity. Since we won't be generating any slot
// offsets for the class anyway, it doesn't matter...
// AvmAssert(offsetofSlots > 0);
}
else
{
// Since nativegen.py emits "synthetic" C++ classes for all builtin classes (except interfaces),
// only non-builtin classes should hit this code; since they don't have C++ representations,
// "overlap" of fields between parent and child isn't possibly so we can safely declare the
// offsetOfSlots to be an the end of the base.
#ifdef VMCFG_VERIFYALL
// (Don't bother with this assert in verifyonly mode, as avmglue.abc (etc) will appear to be
// non-builtin (since we specify them as arguments, triggering false positives)
if (!core->config.verifyonly)
#endif
{
AvmAssert(postype == TRAITSTYPE_INTERFACE || !pool->isBuiltin);
}
// 0 means "put the slots at the end of my immediate parent"
offsetofSlots = 0;
// Assume ScriptObject for now
sizeofInstance = uint16_t(sizeof(ScriptObject));
// Search the inheritance chain for any native classes.
for (Traits* b = baseTraits; b != NULL; b = b->base)
{
if (b->getSizeOfInstance() > sizeof(ScriptObject))
{
// non-Object base class uses a subclass of ScriptObject, so use that size.
sizeofInstance = b->getSizeOfInstance();
break;
}
}
}
Traits* itraits = parseTraits(sizeofInstance,
offsetofSlots,
baseTraits,
ns,
name,
0,
instancepos,
postype,
protectedNamespace,
(flags&2) != 0);
if( !itraits ) return false;
if (!baseTraits && core->traits.object_itraits == NULL)
{
// save object traits
core->traits.object_itraits = itraits;
itraits->builtinType = BUILTIN_object;
}
// AS3 language decision: dynamic is not inherited.
itraits->set_needsHashtable((flags&1) == 0);
if (itraits->isInterface())
{
// check for slotCount != 0 now done at resolve time
// interface base must be *
if (baseTraits)
{
// error, can't extend this type
toplevel->throwVerifyError(kCannotExtendError, core->toErrorString(&mn), core->toErrorString(baseTraits));
}
}
Traits* declaringTraits = iinit->declaringTraits();
if (declaringTraits != NULL)
{
// method has already been bound to a different type. Can't bind it twice because
// it can only have one environment, for its scope chain and super references.
toplevel->throwVerifyError(kAlreadyBoundError, core->toErrorString(iinit), core->toErrorString(declaringTraits));
}
iinit->makeMethodOf(itraits);
itraits->init = iinit;
#ifdef AVMPLUS_VERBOSE
// print the interfaces. interfaces have been resolved above so any
// errors would have been reported earlier.
for (int j=0, n=interfaceCount; j < n; j++)
{
Traits *interfaceTraits = pool->resolveTypeName(interfacePos, toplevel);
if(pool->isVerbose(VB_parse)) {
core->console << " interface["<<j<<"]=" << interfaceTraits <<"\n";
}
}
#else
(void)interfacePos;
#endif
instances.set(i, itraits);
core->domainMgr()->addNamedInstanceTraits(pool, name, ns, itraits);
}
return true;
}
void AbcParser::parseClassInfos()
{
if (classCount == 0)
{
return;
}
#ifdef AVMPLUS_VERBOSE
const uint8_t* startpos = pos;
#endif
for (uint32_t i=0; i < classCount; i++)
{
// CONSTANT_Multiname name of this class
Traits* itraits = instances[i];
Namespacep ns = itraits->ns();
Stringp name = itraits->name();
const uint8_t* class_pos = pos;
uint32_t cinit_index = readU30(pos);
MethodInfo* cinit = resolveMethodInfo(cinit_index);
#ifdef AVMPLUS_VERBOSE
if(pool->isVerbose(VB_parse)) {
core->console
<< " " << (int)(class_pos-startpos) << ":class[" << i << "]"
<< " " << ns << "::" << name;
core->console
<< " cinit_index=" << cinit_index
<< "\n";
}
#endif
const NativeClassInfo* nativeEntry = natives ? natives->get_class(i) : NULL;
bool haveClassNativeInfo = nativeEntry && nativeEntry->sizeofClass;
Traits* ctraits = parseTraits(haveClassNativeInfo ? nativeEntry->sizeofClass : sizeof(ClassClosure),
haveClassNativeInfo ? nativeEntry->offsetofSlotsClass : sizeof(ClassClosure),
CLASS_TYPE,
ns,
core->internString(core->concatStrings(name, core->newConstantStringLatin1("$"))),
NULL,
class_pos,
TRAITSTYPE_CLASS,
itraits->protectedNamespace);
if (haveClassNativeInfo)
{
ctraits->hasCustomConstruct = nativeEntry->hasCustomConstruct;
itraits->isRestrictedInheritance = nativeEntry->isRestrictedInheritance;
itraits->isAbstractBase = nativeEntry->isAbstractBase;
}
ctraits->setCreateClassClosureProc(haveClassNativeInfo ? nativeEntry->createClassClosure : ClassClosure::createClassClosure);
Traits* declaringTraits = cinit->declaringTraits();
if (declaringTraits != NULL)
{
// method has already been bound to a different type. Can't bind it twice because
// it can only have one environment, for its scope chain and super references.
toplevel->throwVerifyError(kAlreadyBoundError, core->toErrorString(cinit), core->toErrorString(declaringTraits));
}
cinit->makeMethodOf(ctraits);
ctraits->init = cinit;
ctraits->itraits = itraits;
ctraits->set_needsHashtable(true);
cinit->setStaticInit();
pool->_classes.set(i, ctraits);
}
}
uint32_t AbcParser::readU30(const uint8_t*& p) const
{
// We have added kBufferPadding bytes to the end of the main swf buffer.
// Why? Here we can read from 1 to 5 bytes. If we were to
// put the required safety checks at each uint8_t read, we would slow
// parsing of the file down. With this buffer, only one check at the
// top of this function is necessary. (we will read on into our own memory)
CHECK_POS(p);
return toplevel->readU30(p);
}
double AbcParser::readDouble(const uint8_t* &p) const
{
// check to see if we are trying to read past the file end.
if (p < abcStart || p+7 >= abcEnd )
toplevel->throwVerifyError(kCorruptABCError);
double_overlay d;
d.words.lsw = p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
d.words.msw = p[4] | p[5]<<8 | p[6]<<16 | p[7]<<24;
p += 8;
return d.value;
}
#ifdef VMCFG_FLOAT
float AbcParser::readFloat(const uint8_t* &p) const
{
// check to see if we are trying to read past the file end.
if (p < abcStart || p+3 >= abcEnd )
toplevel->throwVerifyError(kCorruptABCError);
float_overlay flt;
flt.word = p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
p+=4;
return flt.value;
}
float4_t AbcParser::readFloat4(const uint8_t* &p) const
{
// check to see if we are trying to read past the file end.
if (p < abcStart || p + 15 >= abcEnd )
toplevel->throwVerifyError(kCorruptABCError);
float4_t val;
uint32_t* pval = reinterpret_cast<uint32_t*>(&val);
for(int i=0;i<4;i++){
pval[i] = p[0] | p[1]<<8 | p[2]<<16 | p[3]<<24;
p+=4;
}
return val;
}
#endif
}