1977 lines
53 KiB
C++
1977 lines
53 KiB
C++
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is [Open Source Virtual Machine.].
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Adobe System Incorporated.
|
|
* Portions created by the Initial Developer are Copyright (C) 1993-2006
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Adobe AS3 Team
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
#include "avmplus.h"
|
|
|
|
namespace avmplus
|
|
{
|
|
/**
|
|
* parse a .abc file completely
|
|
* @param code
|
|
* @return
|
|
*/
|
|
PoolObject* AbcParser::decodeAbc(AvmCore* core, ScriptBuffer code,
|
|
Toplevel* toplevel,
|
|
Domain* domain,
|
|
AbstractFunction *nativeMethods[],
|
|
NativeClassInfo *nativeClasses[],
|
|
NativeScriptInfo *nativeScripts[])
|
|
{
|
|
if (code.getSize() < 4)
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
int version = AvmCore::readU16(&code[0]) | AvmCore::readU16(&code[2])<<16;
|
|
|
|
#ifdef AVMPLUS_PROFILE
|
|
DynamicProfiler::StackMark mark(OP_decode, &core->dprof);
|
|
#endif
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (core->verbose)
|
|
core->console << "major=" << (version&0xFFFF) << " minor=" << (version>>16) << "\n";
|
|
#endif
|
|
|
|
switch (version)
|
|
{
|
|
case (46<<16|16):
|
|
{
|
|
AbcParser parser(core, code, toplevel, domain, nativeMethods, nativeClasses, nativeScripts);
|
|
PoolObject *pObject = parser.parse();
|
|
if ( !pObject )
|
|
toplevel->throwVerifyError( kCorruptABCError );
|
|
else
|
|
return pObject;
|
|
}
|
|
default:
|
|
toplevel->throwVerifyError(kInvalidMagicError, core->toErrorString(version>>16), core->toErrorString(version&0xFFFF));
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
AbcParser::AbcParser(AvmCore* core, ScriptBuffer code,
|
|
Toplevel* toplevel,
|
|
Domain* domain,
|
|
AbstractFunction *nativeMethods[],
|
|
NativeClassInfo *nativeClasses[],
|
|
NativeScriptInfo *nativeScripts[])
|
|
: toplevel(toplevel),
|
|
domain(domain),
|
|
instances(core->GetGC(), 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->nativeMethods = nativeMethods;
|
|
this->nativeClasses = nativeClasses;
|
|
this->nativeScripts = nativeScripts;
|
|
|
|
abcStart = &code[0];
|
|
abcEnd = &code[(int)code.getSize()];
|
|
|
|
classCount = 0;
|
|
metaNames = NULL;
|
|
}
|
|
|
|
AbcParser::~AbcParser()
|
|
{
|
|
if (metaNames)
|
|
core->GetGC()->Free(metaNames);
|
|
}
|
|
|
|
int AbcParser::computeInstanceSize(int class_id,
|
|
Traits* base) const
|
|
{
|
|
// If this is a native class, return the stated instance size
|
|
NativeClassInfo* nativeEntry;
|
|
if (nativeClasses && (nativeEntry = nativeClasses[class_id]) != 0)
|
|
{
|
|
return nativeEntry->sizeofInstance;
|
|
}
|
|
|
|
// Search the inheritance chain for any native classes.
|
|
while (base)
|
|
{
|
|
if (base->sizeofInstance > sizeof(ScriptObject))
|
|
{
|
|
// non-Object base class uses a subclass of ScriptObject, so use that size.
|
|
return base->sizeofInstance;
|
|
}
|
|
base = base->base;
|
|
}
|
|
|
|
// no derived native classes found
|
|
return sizeof(ScriptObject);
|
|
}
|
|
|
|
Namespace* AbcParser::parseNsRef(const byte* &pc) const
|
|
{
|
|
uint32 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];
|
|
}
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
void AbcParser::parseTypeName(const byte* &pc, Multiname& m) const
|
|
{
|
|
// only save the type name for now. verifier will resolve to traits
|
|
uint32 index = readU30(pc);
|
|
if (index == 0)
|
|
{
|
|
// type is *
|
|
m.setNamespace(core->publicNamespace);
|
|
m.setName(core->kAsterisk);
|
|
return;
|
|
}
|
|
|
|
if (index >= pool->constantMnCount)
|
|
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->constantMnCount));
|
|
|
|
pool->parseMultiname(m, index);
|
|
}
|
|
#endif
|
|
|
|
Atom AbcParser::resolveQName(const byte* &p, Multiname &m) const
|
|
{
|
|
uint32 index = readU30(p);
|
|
if (index == 0 || index >= pool->constantMnCount)
|
|
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->constantCount));
|
|
|
|
Atom a = pool->cpool_mn[index];
|
|
|
|
pool->parseMultiname(a, m);
|
|
if (!m.isQName())
|
|
toplevel->throwVerifyError(kCpoolEntryWrongTypeError, core->toErrorString(index));
|
|
|
|
return a;
|
|
}
|
|
|
|
AbstractFunction *AbcParser::resolveMethodInfo(uint32 index) const
|
|
{
|
|
if (index >= pool->methodCount)
|
|
toplevel->throwVerifyError(kMethodInfoExceedsCountError, core->toErrorString(index), core->toErrorString(pool->methodCount));
|
|
|
|
AbstractFunction *f = pool->getMethodInfo(index);
|
|
if (!f)
|
|
toplevel->throwVerifyError(kMethodInfoOrderError, core->toErrorString(index));
|
|
|
|
return f;
|
|
}
|
|
|
|
Stringp AbcParser::resolveUtf8(uint32 index) const
|
|
{
|
|
if (index > 0 && index < pool->constantStringCount )
|
|
{
|
|
return pool->cpool_string[index];
|
|
}
|
|
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(pool->constantStringCount));
|
|
return NULL;
|
|
}
|
|
|
|
Stringp AbcParser::parseName(const byte* &pc) const
|
|
{
|
|
uint32 index = readU30(pc);
|
|
if (index == 0)
|
|
return NULL;
|
|
return resolveUtf8(index);
|
|
}
|
|
|
|
PoolObject* AbcParser::parse()
|
|
{
|
|
#ifdef FEATURE_BUFFER_GUARD // no Carbon
|
|
TRY(this->core, kCatchAction_Rethrow)
|
|
{
|
|
// catches any access violation exceptions and sends control to
|
|
// the CATCH block below.
|
|
BufferGuard guard(_ef.jmpbuf);
|
|
this->guard = &guard;
|
|
|
|
#endif // FEATURE_BUFFER_GUARD
|
|
|
|
// constant pool
|
|
parseCpool();
|
|
|
|
// 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->publicNamespace, 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();
|
|
#ifdef FEATURE_BUFFER_GUARD // no buffer guard in Carbon builds
|
|
}
|
|
CATCH(Exception *exception)
|
|
{
|
|
guard->disable();
|
|
guard = NULL;
|
|
if( exception->isValid())
|
|
// Re-throw if exception exists
|
|
core->throwException(exception);
|
|
else
|
|
// create new exception
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
}
|
|
END_CATCH
|
|
END_TRY
|
|
#endif // FEATURE_BUFFER_GUARD
|
|
|
|
return pool;
|
|
}
|
|
|
|
/**
|
|
* 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(Traits* base, Namespace* ns, Stringp name, AbstractFunction* script, int interfaceDelta, Namespace* protectedNamespace /*=NULL*/)
|
|
{
|
|
const byte* traits_pos = pos;
|
|
unsigned int nameCount = readU30(pos);
|
|
|
|
// Very generous check for nameCount being way too large.
|
|
if (nameCount > (unsigned int)(abcEnd - pos))
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
core->console << " trait_count=" << nameCount << "\n";
|
|
#endif
|
|
|
|
Traits* traits = core->newTraits(base, nameCount, interfaceDelta, sizeof(ScriptObject));
|
|
|
|
traits->setTraitsPos(traits_pos);
|
|
traits->ns = ns;
|
|
traits->name = name;
|
|
traits->pool = pool;
|
|
traits->protectedNamespace = protectedNamespace;
|
|
|
|
// Copy protected traits from base class into new protected namespace
|
|
if (base && base->protectedNamespace && protectedNamespace)
|
|
{
|
|
for (int i=0; (i = base->next(i)) != 0; )
|
|
{
|
|
if (base->nsAt(i) == base->protectedNamespace)
|
|
{
|
|
traits->add(base->keyAt(i), protectedNamespace, base->valueAt(i));
|
|
}
|
|
}
|
|
}
|
|
|
|
uint32 slotCount, methodCount;
|
|
if (base)
|
|
{
|
|
slotCount = base->slotCount;
|
|
methodCount = base->methodCount;
|
|
}
|
|
else
|
|
{
|
|
slotCount = 0;
|
|
methodCount = 0;
|
|
}
|
|
|
|
bool earlySlotBinding;
|
|
|
|
pool->allowEarlyBinding(base, earlySlotBinding);
|
|
|
|
for (unsigned int i=0; i < nameCount; i++)
|
|
{
|
|
Multiname qn;
|
|
resolveQName(pos, qn);
|
|
Namespace* ns = qn.getNamespace();
|
|
// TODO name can come out null and fire all kinds of asserts from a broken compiler
|
|
// and crash a release build, not sure if null is valid in any cases but there's definitely
|
|
// a missing verify error here somewhere
|
|
Stringp name = qn.getName();
|
|
#ifdef SAFE_PARSE
|
|
// check to see if we are trying to read past the file end or the beginning.
|
|
if (pos < abcStart || pos >= abcEnd )
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
#endif //SAFE_PARSE
|
|
int tag = *pos++;
|
|
TraitKind kind = (TraitKind) (tag & 0x0f);
|
|
|
|
AbstractFunction *f = NULL;
|
|
switch (kind)
|
|
{
|
|
case TRAIT_Slot:
|
|
case TRAIT_Const:
|
|
case TRAIT_Class:
|
|
{
|
|
uint32 slot_id = readU30(pos);
|
|
if (!earlySlotBinding) slot_id = 0;
|
|
if (!slot_id)
|
|
{
|
|
// vm assigns slot
|
|
slot_id = slotCount++;
|
|
}
|
|
else
|
|
{
|
|
if (slot_id > nameCount)
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
// compiler assigned slot
|
|
if (slot_id > slotCount)
|
|
slotCount = slot_id;
|
|
slot_id--;
|
|
}
|
|
|
|
if (base && slot_id < base->slotCount)
|
|
{
|
|
// slots are final.
|
|
toplevel->throwVerifyError(kIllegalOverrideError, core->toErrorString(&qn), core->toErrorString(base));
|
|
}
|
|
|
|
// a slot cannot override anything else.
|
|
if (traits->get(name, ns) != BIND_NONE)
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
if (script)
|
|
addNamedScript(ns, name, script);
|
|
|
|
if( kind == TRAIT_Slot || kind == TRAIT_Const)
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
// type name
|
|
Multiname typeName;
|
|
parseTypeName(pos, typeName);
|
|
|
|
// default value index
|
|
int value_index = readU30(pos);
|
|
CPoolKind value_kind;
|
|
if( value_index )
|
|
{
|
|
#ifdef SAFE_PARSE
|
|
// check to see if we are trying to read past the file end or the beginning.
|
|
if (pos < abcStart || pos >= abcEnd )
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
#endif //SAFE_PARSE
|
|
value_kind = (CPoolKind)*(pos++);
|
|
}
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " " << traitNames[kind]
|
|
<< " name=" << Multiname::format(core,ns,name)
|
|
<< " slot_id=" << slot_id
|
|
<< " value_index=" << value_index
|
|
<< " type=" << &typeName
|
|
<< "\n";
|
|
}
|
|
#else
|
|
readU30(pos); // type
|
|
int value_index = readU30(pos); // default value
|
|
if( value_index )
|
|
{
|
|
pos++; // byte for the value kind
|
|
}
|
|
#endif
|
|
|
|
// create a binding
|
|
// only touches hashtable
|
|
traits->defineSlot(name, ns,
|
|
slot_id, kind==TRAIT_Slot ? BIND_VAR : BIND_CONST);
|
|
|
|
}
|
|
else
|
|
{
|
|
AvmAssert(kind == TRAIT_Class);
|
|
// get the class type
|
|
int class_info = readU30(pos);
|
|
if (class_info >= classCount)
|
|
toplevel->throwVerifyError(kClassInfoExceedsCountError, core->toErrorString(class_info), core->toErrorString(classCount));
|
|
|
|
AbstractFunction* cinit = pool->cinits[class_info];
|
|
if (!cinit)
|
|
toplevel->throwVerifyError(kClassInfoOrderError, core->toErrorString(class_info));
|
|
|
|
Traits* ctraits = cinit->declaringTraits;
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " " << traitNames[kind]
|
|
<< " name=" << Multiname::format(core, ns, name)
|
|
<< " slot_id=" << slot_id
|
|
<< " type=" << ctraits
|
|
<< "\n";
|
|
}
|
|
#endif
|
|
|
|
if (script)
|
|
{
|
|
// promote instance type to the vm-wide type table.
|
|
Traits* itraits = ctraits->itraits;
|
|
|
|
// map type name to traits
|
|
addNamedTraits(ns, name, itraits);
|
|
|
|
if ( tag & ATTR_metadata )
|
|
{
|
|
itraits->setMetadataPos(pos);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( tag & ATTR_metadata )
|
|
{
|
|
ctraits->setMetadataPos(pos);
|
|
}
|
|
}
|
|
|
|
// create a binding
|
|
// only touches hashtable
|
|
traits->defineSlot(name, ns, slot_id, BIND_CONST);
|
|
|
|
}
|
|
break;
|
|
}
|
|
case TRAIT_Getter:
|
|
case TRAIT_Setter:
|
|
case TRAIT_Method:
|
|
{
|
|
int earlyDispId = readU30( pos );
|
|
(void)earlyDispId;
|
|
uint32 method_info = readU30(pos);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " " << traitNames[kind]
|
|
<< " name=" << Multiname::format(core, ns, name)
|
|
<< " disp_id=" << earlyDispId << " (ignored)"
|
|
<< " method_info=" << method_info
|
|
<< " attr=" << ((tag&ATTR_final)?"final":"virtual");
|
|
if (tag&ATTR_override)
|
|
core->console << "|override";
|
|
core->console << "\n";
|
|
}
|
|
#endif
|
|
|
|
f = resolveMethodInfo(method_info);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
Stringp s1 = traits->format(core);
|
|
Stringp s2 = core->newString(kind == TRAIT_Method ? "/" : (kind == TRAIT_Getter ? "/get " : "/set "));
|
|
Stringp s3 = Multiname::format(core,ns,name);
|
|
Stringp s4 = core->concatStrings(s2,s3);
|
|
f->name = core->concatStrings(s1, s4);
|
|
//delete s1 - can't delete, it may be the traits name string;
|
|
delete s2;
|
|
//delete s3 - can't delete, it may be the qname name string;
|
|
delete s4;
|
|
#endif
|
|
|
|
// since this function is ref'ed here, we know the receiver type.
|
|
if (!f->makeMethodOf(traits))
|
|
{
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
}
|
|
|
|
if (tag & ATTR_final)
|
|
f->flags |= AbstractFunction::FINAL;
|
|
|
|
if (tag & ATTR_override)
|
|
f->flags |= AbstractFunction::OVERRIDE;
|
|
|
|
Binding baseBinding = traits->getOverride(ns, name, tag, toplevel);
|
|
|
|
// issue can you ever import a getter/setter?
|
|
// is it okay to call this more than once on the same
|
|
// name/script pair?
|
|
|
|
// only export one name for an accessor
|
|
if (script && !domain->getNamedScript(name,ns))
|
|
addNamedScript(ns, name, script);
|
|
|
|
if (kind == TRAIT_Method)
|
|
{
|
|
if (baseBinding == BIND_NONE)
|
|
{
|
|
traits->defineSlot(name, ns, methodCount, BIND_METHOD);
|
|
// accessors require 2 vtable slots, methods only need 1.
|
|
methodCount ++;
|
|
}
|
|
else if (AvmCore::isMethodBinding(baseBinding))
|
|
{
|
|
// something got overridden, need new name entry for this subclass
|
|
// but keep the existing disp_id
|
|
int disp_id = AvmCore::bindingToMethodId(baseBinding);
|
|
traits->defineSlot(name, ns, disp_id, BIND_METHOD);
|
|
}
|
|
else
|
|
{
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
}
|
|
}
|
|
else if (kind == TRAIT_Getter)
|
|
{
|
|
// if nothing in base class, look for existing binding on this class,
|
|
// in case setter has already been defined.
|
|
if (baseBinding == BIND_NONE)
|
|
baseBinding = traits->get(name, ns);
|
|
|
|
if (baseBinding == BIND_NONE)
|
|
{
|
|
traits->defineSlot(name, ns, methodCount, BIND_GET);
|
|
// accessors require 2 vtable slots, methods only need 1.
|
|
methodCount += 2;
|
|
}
|
|
else if (AvmCore::isAccessorBinding(baseBinding))
|
|
{
|
|
// something maybe got overridden, need new name entry for this subclass
|
|
// but keep the existing disp_id
|
|
int disp_id = urshift(baseBinding,3);
|
|
int bkind = baseBinding & 7;
|
|
if (bkind == BIND_SET)
|
|
bkind = BIND_GETSET;
|
|
traits->defineSlot(name, ns, disp_id, bkind);
|
|
}
|
|
else
|
|
{
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
}
|
|
}
|
|
else // TRAIT_Setter
|
|
{
|
|
// if nothing in base class, look for existing binding on this class,
|
|
// in case getter has already been defined.
|
|
if (baseBinding == BIND_NONE)
|
|
baseBinding = traits->get(name, ns);
|
|
|
|
if (baseBinding == BIND_NONE)
|
|
{
|
|
traits->defineSlot(name, ns, methodCount, BIND_SET);
|
|
// accessors require 2 vtable slots, methods only need 1.
|
|
methodCount += 2;
|
|
}
|
|
else if (AvmCore::isAccessorBinding(baseBinding))
|
|
{
|
|
// something maybe got overridden, need new name entry for this subclass
|
|
// but keep the existing disp_id
|
|
// both get & set bindings use the get id. set_id = get_id + 1.
|
|
int disp_id = urshift(baseBinding,3);
|
|
int bkind = baseBinding & 7;
|
|
if (bkind == BIND_GET)
|
|
bkind = BIND_GETSET;
|
|
traits->defineSlot(name, ns, disp_id, bkind);
|
|
}
|
|
else
|
|
{
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
// unsupported traits type
|
|
toplevel->throwVerifyError(kUnsupportedTraitsKindError, core->toErrorString(kind));
|
|
}
|
|
|
|
if ( tag & ATTR_metadata )
|
|
{
|
|
int metadataCount = readU30(pos);
|
|
for( int metadata = 0; metadata < metadataCount; ++metadata)
|
|
{
|
|
int index = readU30(pos);
|
|
Stringp name = metaNames[index];
|
|
if (name == core->kNeedsDxns && f != NULL)
|
|
f->flags |= AbstractFunction::NEEDS_DXNS;
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (name == kVerboseVerify && f != NULL)
|
|
f->flags |= AbstractFunction::VERBOSE_VERIFY;
|
|
if (pool->verbose)
|
|
core->console << " [" << metaNames[index] << "]\n";
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
traits->slotCount = slotCount;
|
|
traits->methodCount = methodCount;
|
|
return traits;
|
|
}
|
|
|
|
void AbcParser::parseMethodInfos()
|
|
{
|
|
int methodCount = readU30(pos);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
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);
|
|
pool->methodCount = methodCount;
|
|
|
|
#if defined(AVMPLUS_PROFILE) || defined(AVMPLUS_VERBOSE) || defined(DEBUGGER)
|
|
const byte* startpos = pos;
|
|
#endif
|
|
|
|
for (int i=0; i < methodCount; i++)
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
int offset = pos-startpos;
|
|
#endif
|
|
// MethodInfo
|
|
int param_count = readU30(pos);
|
|
|
|
const byte* info_pos = pos;
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
Multiname returnTypeName;
|
|
parseTypeName(pos, returnTypeName);
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " " << offset << ":method["<<i<<"]\n"
|
|
<< " returnType=" << &returnTypeName << "\n"
|
|
<< " param_count=" << param_count
|
|
<< "\n";
|
|
}
|
|
#else
|
|
readU30(pos);// return type name
|
|
#endif
|
|
|
|
for( int j = 1; j <= param_count; ++j)
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
Multiname multiname;
|
|
parseTypeName(pos, multiname);
|
|
if (pool->verbose)
|
|
core->console << " paramType["<<j<<"]="<< &multiname << "\n";
|
|
#else
|
|
readU30(pos);
|
|
#endif
|
|
}
|
|
|
|
uint32 name_index = readU30(pos);
|
|
(void)name_index;
|
|
#ifdef SAFE_PARSE
|
|
// check to see if we are trying to read past the file end or the beginning.
|
|
if (pos < abcStart || pos >= abcEnd )
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
#endif //SAFE_PARSE
|
|
int flags = *pos++;
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " name_index=" << name_index;
|
|
if (name_index > 0 && name_index < pool->constantStringCount)
|
|
core->console << " \"" << pool->cpool_string[name_index] << "\"";
|
|
core->console << "\n flags=" << flags << "\n";
|
|
}
|
|
#endif
|
|
|
|
AbstractFunction *info;
|
|
if (!(flags & AbstractFunction::NATIVE))
|
|
{
|
|
MethodInfo *methodInfo = new (core->GetGC()) MethodInfo();
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (name_index != 0)
|
|
methodInfo->name = resolveUtf8(name_index);
|
|
else
|
|
methodInfo->name = core->concatStrings(core->newString("MethodInfo-"), core->intToString(i));
|
|
#endif
|
|
info = methodInfo;
|
|
}
|
|
else
|
|
{
|
|
if (!nativeMethods)
|
|
{
|
|
toplevel->throwVerifyError(kIllegalNativeMethodError);
|
|
}
|
|
info = nativeMethods[i];
|
|
|
|
// 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(info != 0, "missing native method decl");
|
|
|
|
// todo assert that the .abc signature matches the C++ code?
|
|
info->flags |= AbstractFunction::ABSTRACT_METHOD;
|
|
}
|
|
|
|
info->info_pos = info_pos;
|
|
info->pool = pool;
|
|
info->method_id = i;
|
|
info->param_count = param_count;
|
|
info->initParamTypes(param_count+1);
|
|
info->flags |= flags;
|
|
|
|
if (flags & AbstractFunction::HAS_OPTIONAL)
|
|
{
|
|
int optional_count = readU30(pos);
|
|
|
|
info->optional_count = optional_count;
|
|
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( flags & AbstractFunction::HAS_PARAM_NAMES)
|
|
{
|
|
// AVMPlus doesn't care about the param names, just skip past them
|
|
for( int j = 0; j < info->param_count; ++j )
|
|
{
|
|
readU30(pos);
|
|
}
|
|
}
|
|
|
|
// save method info pointer. we will verify code later.
|
|
pool->methods.set(i, info);
|
|
}
|
|
#ifdef AVMPLUS_PROFILE
|
|
core->sprof.methodsSize += (pos-startpos);
|
|
#endif
|
|
}
|
|
|
|
|
|
void AbcParser::parseMetadataInfos()
|
|
{
|
|
int metadataCount = readU30(pos);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
core->console << "metadata_count=" << metadataCount << "\n";
|
|
#endif
|
|
|
|
if (metadataCount > (abcEnd - pos))
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
pool->metadata_infos.ensureCapacity(metadataCount);
|
|
pool->metadataCount = metadataCount;
|
|
|
|
if (metadataCount > 0)
|
|
{
|
|
metaNames = (Stringp*) core->GetGC()->Calloc(metadataCount, sizeof(Stringp), MMgc::GC::kContainsPointers);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
kVerboseVerify = core->constantString("VerboseVerify");
|
|
#endif
|
|
|
|
for (int i=0; i < metadataCount; i++)
|
|
{
|
|
pool->metadata_infos.set(i, pos);
|
|
// MetadataInfo
|
|
uint32 index = readU30(pos);
|
|
Stringp name = resolveUtf8(index);
|
|
// constant pool names are stuck and always reachable via PoolObject, DRC or WB
|
|
metaNames[i] = name;
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
core->console << " " << name;
|
|
int values_count = readU30(pos);
|
|
if (values_count > 0)
|
|
{
|
|
if (pool->verbose)
|
|
core->console << "(";
|
|
for(int q = 0; q < values_count; ++q)
|
|
{
|
|
int a = readU30(pos);
|
|
int b = readU30(pos);
|
|
if (pool->verbose)
|
|
{
|
|
core->console << a << "," << b;
|
|
if (q+1 < values_count)
|
|
core->console << " ";
|
|
}
|
|
}
|
|
if (pool->verbose)
|
|
core->console << ")";
|
|
}
|
|
if (pool->verbose)
|
|
core->console << "\n";
|
|
|
|
#else // AVMPLUS_VERBOSE
|
|
|
|
// MetadataInfo
|
|
int values_count = readU30(pos);
|
|
for(int q = 0; q < values_count; ++q)
|
|
{
|
|
readU30(pos);
|
|
readU30(pos);
|
|
}
|
|
|
|
#endif // AVMPLUS_VERBOSE
|
|
}
|
|
}
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
//StaticProfiler::methodsSize += (pos-startpos);
|
|
#endif
|
|
}
|
|
|
|
void AbcParser::parseMethodBodies()
|
|
{
|
|
int bodyCount = readU30(pos);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
core->console << "bodies_count=" << bodyCount << "\n";
|
|
#endif
|
|
|
|
#if defined(AVMPLUS_PROFILE) || defined(AVMPLUS_VERBOSE) || defined(DEBUGGER)
|
|
const byte* startpos = pos;
|
|
#endif
|
|
|
|
for (int i=0; i < bodyCount; i++)
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
int offset = pos-startpos;
|
|
#endif
|
|
|
|
uint32 method_info = readU30(pos);
|
|
AbstractFunction* info = resolveMethodInfo(method_info);
|
|
|
|
const byte *body_pos = pos;
|
|
|
|
int max_stack = readU30(pos);
|
|
(void)max_stack;
|
|
|
|
int local_count = readU30(pos);
|
|
|
|
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)
|
|
{
|
|
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->verbose)
|
|
{
|
|
core->console << " " << offset << ":method["<<method_info<<"] 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->flags |= AbstractFunction::HAS_EXCEPTIONS;
|
|
for (int i=0; i<exception_count; i++)
|
|
{
|
|
// this will be parsed when method is verified.
|
|
// see AbstractFunction::resolveSignature
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
int from = readU30(pos);
|
|
int to = readU30(pos);
|
|
int target = readU30(pos);
|
|
Multiname typeName;
|
|
parseTypeName(pos, typeName);
|
|
|
|
Multiname qn;
|
|
uint32 name_index = (version != (46<<16|15)) ? readU30(pos) : 0;
|
|
if (name_index != 0)
|
|
{
|
|
if (name_index >= pool->constantMnCount)
|
|
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(name_index), core->toErrorString(pool->constantCount));
|
|
pool->parseMultiname(qn, name_index);
|
|
}
|
|
|
|
if (pool->verbose)
|
|
{
|
|
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))
|
|
{
|
|
uint32 name_index = readU30(pos); // variable name
|
|
if (name_index >= pool->constantMnCount)
|
|
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(name_index), core->toErrorString(pool->constantCount));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
if (info->hasMethodBody())
|
|
{
|
|
MethodInfo *methodInfo = (MethodInfo*) info;
|
|
|
|
// Interface methods should not have bodies
|
|
if (info->declaringTraits && info->declaringTraits->isInterface)
|
|
{
|
|
toplevel->throwVerifyError(kIllegalInterfaceMethodBodyError, core->toErrorString(info));
|
|
}
|
|
|
|
#ifdef DEBUGGER
|
|
methodInfo->local_count = local_count;
|
|
methodInfo->codeSize = code_length;
|
|
methodInfo->max_scopes = max_scope_depth - init_scope_depth;
|
|
#endif
|
|
|
|
// if non-zero, we have a duplicate method body - throw a verify error
|
|
if (methodInfo->body_pos)
|
|
{
|
|
toplevel->throwVerifyError(kDuplicateMethodBodyError, core->toErrorString(info));
|
|
}
|
|
|
|
methodInfo->body_pos = body_pos;
|
|
|
|
// there will be a traits_count here, even if NEED_ACTIVATION is not
|
|
// set. So we parse the same way all the time. We could reduce file size and
|
|
// memory by ommitting the count + traits comletely.
|
|
|
|
const byte* traits_pos = pos;
|
|
int nameCount = readU30(pos);
|
|
if ((methodInfo->flags & AbstractFunction::NEED_ACTIVATION) || nameCount > 0)
|
|
{
|
|
pos = traits_pos;
|
|
// activation traits are raw types, not subclasses of object. this is
|
|
// okay because they aren't accessable to the programming model.
|
|
#ifdef AVMPLUS_VERBOSE
|
|
methodInfo->activationTraits = parseTraits(NULL,
|
|
core->publicNamespace,
|
|
core->internString(methodInfo->name),
|
|
0, 0);
|
|
#else
|
|
methodInfo->activationTraits = parseTraits(NULL, NULL, NULL, false, 0);
|
|
#endif
|
|
methodInfo->activationTraits->final = true;
|
|
}
|
|
else
|
|
{
|
|
// TODO remove this assert once abc format is adjusted
|
|
AvmAssert(nameCount == 0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// native methods should not have bodies!
|
|
toplevel->throwVerifyError(kIllegalNativeMethodBodyError, core->toErrorString(info));
|
|
}
|
|
}
|
|
|
|
#ifdef AVMPLUS_PROFILE
|
|
core->sprof.bodiesSize += (pos-startpos);
|
|
#endif
|
|
}
|
|
|
|
void AbcParser::parseCpool()
|
|
{
|
|
pool = new (core->GetGC()) PoolObject(core, code, pos);
|
|
pool->domain = domain;
|
|
pool->isBuiltin = (nativeMethods != NULL);
|
|
|
|
uint32 int_count = readU30(pos);
|
|
// sanity check to prevent huge allocations
|
|
if (int_count > (uint32)(abcEnd - pos))
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
List<int,LIST_NonGCObjects>& cpool_int = pool->cpool_int;
|
|
cpool_int.ensureCapacity(int_count);
|
|
pool->constantIntCount = int_count;
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
pool->verbose = core->verbose;
|
|
#endif
|
|
|
|
#if defined(AVMPLUS_PROFILE) || defined(AVMPLUS_VERBOSE) || defined(DEBUGGER)
|
|
const byte* startpos = pos;
|
|
#endif
|
|
|
|
for(uint32 i = 1; i < int_count; ++i)
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
int offset = pos-startpos;
|
|
#endif
|
|
// S32 value
|
|
cpool_int.set(i, readS32(pos));
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " " << offset << ":" << "cpool_int["<<i<<"]="
|
|
<<constantNames[CONSTANT_Int] << " ";
|
|
core->console << cpool_int[i] << "\n";
|
|
}
|
|
#endif
|
|
|
|
}
|
|
#ifdef AVMPLUS_PROFILE
|
|
core->sprof.cpoolIntSize = (pos-startpos);
|
|
#endif
|
|
|
|
uint32 uint_count = readU30(pos);
|
|
if (uint_count > (uint32)(abcEnd - pos))
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
List<uint32,LIST_NonGCObjects>& cpool_uint = pool->cpool_uint;
|
|
cpool_uint.ensureCapacity(uint_count);
|
|
pool->constantUIntCount = uint_count;
|
|
|
|
#if defined(AVMPLUS_PROFILE) || defined(AVMPLUS_VERBOSE) || defined(DEBUGGER)
|
|
startpos = pos;
|
|
#endif
|
|
|
|
for(uint32 i = 1; i < uint_count; ++i)
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
int offset = pos-startpos;
|
|
#endif
|
|
// U32 value
|
|
cpool_uint.set(i, (unsigned)readS32(pos));
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " " << offset << ":" << "cpool_uint["<<i<<"]="
|
|
<<constantNames[CONSTANT_UInt] << " ";
|
|
core->console << (double)cpool_uint[i];
|
|
core->console << "\n";
|
|
}
|
|
#endif
|
|
|
|
}
|
|
#ifdef AVMPLUS_PROFILE
|
|
core->sprof.cpoolUIntSize = (pos-startpos);
|
|
#endif
|
|
|
|
uint32 double_count = readU30(pos);
|
|
if (double_count > (uint32)(abcEnd - pos))
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
List<double*, LIST_GCObjects>& cpool_double = pool->cpool_double;
|
|
cpool_double.ensureCapacity(double_count);
|
|
pool->constantDoubleCount = double_count;
|
|
|
|
#if defined(AVMPLUS_VERBOSE) || defined(DEBUGGER)
|
|
startpos = pos;
|
|
#endif
|
|
|
|
for(uint32 i = 1; i < double_count; ++i)
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
int offset = pos-startpos;
|
|
#endif
|
|
// double value
|
|
union {
|
|
double value;
|
|
sint64 bits;
|
|
#ifdef AVMPLUS_ARM
|
|
uint32 words[2];
|
|
#endif
|
|
} u;
|
|
u.bits = readS64(pos);
|
|
#ifdef AVMPLUS_ARM
|
|
uint32 t = u.words[0];
|
|
u.words[0] = u.words[1];
|
|
u.words[1] = t;
|
|
#endif
|
|
cpool_double.set(i, (double*)(core->allocDouble(u.value)&~7));
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " " << offset << ":" << "cpool_double["<<i<<"]="
|
|
<<constantNames[CONSTANT_Double] << " ";
|
|
core->console << *cpool_double[i];
|
|
core->console << "\n";
|
|
}
|
|
#endif
|
|
|
|
}
|
|
|
|
#ifdef AVMPLUS_PROFILE
|
|
core->sprof.cpoolDoubleSize = (pos-startpos);
|
|
#endif
|
|
|
|
uint32 string_count = readU30(pos);
|
|
if (string_count > (uint32)(abcEnd - pos))
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
List<Stringp, LIST_RCObjects> &cpool_string = pool->cpool_string;
|
|
MMGC_MEM_TYPE(pool);
|
|
cpool_string.ensureCapacity(string_count);
|
|
pool->constantStringCount = string_count;
|
|
|
|
#if defined(AVMPLUS_VERBOSE) || defined(DEBUGGER)
|
|
startpos = pos;
|
|
#endif
|
|
|
|
for(uint32 i = 1; i < string_count; ++i)
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
int offset = pos-startpos;
|
|
#endif
|
|
|
|
// number of characters
|
|
// todo - is compiler emitting no. of chars or no. of bytes?
|
|
int len = readU30(pos);
|
|
|
|
// check to see if we are trying to read past the file end or the beginning.
|
|
if (pos < abcStart || pos+len >= abcEnd )
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
// don't need to create an atom for this now, because
|
|
// each caller will take care of it.
|
|
Stringp s = core->internAllocUtf8(pos, len);
|
|
#ifdef MMGC_DRC
|
|
// MIR skips WB on string constants so make them sticky
|
|
s->Stick();
|
|
#endif
|
|
cpool_string.set(i, s);
|
|
pos += len;
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " " << offset << ":" << "cpool_string["<<i<<"]="
|
|
<<constantNames[CONSTANT_Utf8] << " ";
|
|
core->console << core->format(cpool_string[i]->atom());
|
|
core->console << "\n";
|
|
}
|
|
#endif
|
|
|
|
}
|
|
#ifdef AVMPLUS_PROFILE
|
|
core->sprof.cpoolStrSize = (pos-startpos);
|
|
#endif
|
|
|
|
uint32 ns_count = readU30(pos);
|
|
if (ns_count > (uint32)(abcEnd - pos))
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
List<Namespace*, LIST_RCObjects> &cpool_ns = pool->cpool_ns;
|
|
|
|
MMGC_MEM_TYPE(pool);
|
|
cpool_ns.ensureCapacity(ns_count);
|
|
pool->constantNsCount = ns_count;
|
|
|
|
#if defined(AVMPLUS_VERBOSE) || defined(DEBUGGER)
|
|
startpos = pos;
|
|
#endif
|
|
for( uint32 i = 1; i < ns_count; ++i )
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
int offset = pos-startpos;
|
|
#endif
|
|
#ifdef SAFE_PARSE
|
|
// check to see if we are trying to read past the file end or the beginning.
|
|
if ( pos < abcStart || pos >= abcEnd )
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
#endif // SAFE_PARSE
|
|
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 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;
|
|
}
|
|
|
|
if (index)
|
|
{
|
|
Stringp uri = resolveUtf8(index);
|
|
cpool_ns.set(i, core->internNamespace(core->newNamespace(uri, type)));
|
|
}
|
|
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 index = readU30(pos);
|
|
Stringp uri = index ? resolveUtf8(index) : (Stringp)core->kEmptyString;
|
|
Namespace* ns = new (core->GetGC()) Namespace(nullStringAtom, uri, Namespace::NS_Private);
|
|
cpool_ns.set(i, ns);
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
toplevel->throwVerifyError(kCpoolEntryWrongTypeError, core->toErrorString(i));
|
|
}
|
|
}
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " " << offset << ":" << "cpool_ns["<<i<<"]="
|
|
<<constantNames[kind] << " ";
|
|
core->console << core->format(cpool_ns[i]->atom());
|
|
core->console << "\n";
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef AVMPLUS_PROFILE
|
|
core->sprof.cpoolNsSize = (pos-startpos);
|
|
#endif
|
|
|
|
|
|
uint32 ns_set_count = readU30(pos);
|
|
if (ns_set_count > (uint32)(abcEnd - pos))
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
List<NamespaceSet*, LIST_GCObjects>& cpool_ns_set = pool->cpool_ns_set;
|
|
cpool_ns_set.ensureCapacity(ns_set_count);
|
|
pool->constantNsSetCount = ns_set_count;
|
|
|
|
#if defined(AVMPLUS_VERBOSE) || defined(DEBUGGER)
|
|
startpos = pos;
|
|
#endif
|
|
|
|
for( uint32 i = 1; i < ns_set_count; ++i)
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
int offset = pos-startpos;
|
|
#endif
|
|
uint32 ns_count = readU30(pos);
|
|
|
|
if (ns_count > (uint32)(abcEnd - pos))
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
NamespaceSet* namespace_set = core->newNamespaceSet(ns_count);
|
|
|
|
Namespace** nss = namespace_set->namespaces;
|
|
for(uint32 j=0; j < ns_count; ++j)
|
|
{
|
|
Namespace* ns = parseNsRef(pos);
|
|
if (!ns)
|
|
toplevel->throwVerifyError(kIllegalNamespaceError);
|
|
//namespace_set->namespaces[j] = ns;
|
|
WBRC(core->GetGC(), namespace_set, &nss[j], ns);
|
|
}
|
|
cpool_ns_set.set(i, namespace_set);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " " << offset << ":" << "cpool_ns_set["<<i<<"]="
|
|
<<constantNames[CONSTANT_NamespaceSet] << " ";
|
|
core->console << cpool_ns_set[i]->format(core);
|
|
core->console << "\n";
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef AVMPLUS_PROFILE
|
|
core->sprof.cpoolNsSetSize = (pos-startpos);
|
|
#endif
|
|
|
|
uint32 mn_count = readU30(pos);
|
|
if (mn_count > (uint32)(abcEnd - pos))
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
// TODO: why Atom? its actually a list of positions
|
|
List<Atom,LIST_NonGCObjects>& cpool_mn = pool->cpool_mn;
|
|
MMGC_MEM_TYPE(pool);
|
|
cpool_mn.ensureCapacity(mn_count);
|
|
pool->constantMnCount = mn_count;
|
|
|
|
#if defined(AVMPLUS_VERBOSE) || defined(DEBUGGER)
|
|
startpos = pos;
|
|
#endif
|
|
for(uint32 i = 1; i < mn_count; ++i )
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
int offset = pos-startpos;
|
|
#endif
|
|
#ifdef SAFE_PARSE
|
|
// check to see if we are trying to read past the file end or the beginning.
|
|
if ( pos < abcStart || pos >= abcEnd )
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
#endif // SAFE_PARSE
|
|
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)
|
|
cpool_mn.set(i, pool->posToAtom(pos-1));
|
|
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
|
|
cpool_mn.set(i, pool->posToAtom(pos-1));
|
|
parseName(pos);
|
|
break;
|
|
}
|
|
case CONSTANT_RTQnameL:
|
|
case CONSTANT_RTQnameLA:
|
|
{
|
|
cpool_mn.set(i, pool->posToAtom(pos-1));
|
|
break;
|
|
}
|
|
|
|
case CONSTANT_Multiname:
|
|
case CONSTANT_MultinameA:
|
|
{
|
|
cpool_mn.set(i, pool->posToAtom(pos-1));
|
|
|
|
parseName(pos);
|
|
|
|
uint32 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:
|
|
{
|
|
cpool_mn.set(i, pool->posToAtom(pos-1));
|
|
|
|
uint32 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;
|
|
}
|
|
|
|
default:
|
|
toplevel->throwVerifyError(kCpoolEntryWrongTypeError, core->toErrorString(i));
|
|
}
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " " << offset << ":" << "cpool_mn["<<i<<"]="
|
|
<<constantNames[kind] << " ";
|
|
Multiname name;
|
|
pool->parseMultiname(name, i);
|
|
core->console << &name;
|
|
core->console << "\n";
|
|
}
|
|
#endif
|
|
}
|
|
#ifdef AVMPLUS_PROFILE
|
|
core->sprof.cpoolMnSize = (pos-startpos);
|
|
#endif
|
|
|
|
}
|
|
|
|
void AbcParser::addNamedTraits(Namespace* ns, Stringp name, Traits* itraits)
|
|
{
|
|
if (!ns->isPrivate() && !domain->namedTraits->get(name, ns))
|
|
{
|
|
domain->namedTraits->add(name, ns, (Atom)itraits);
|
|
}
|
|
else
|
|
{
|
|
// duplicate class
|
|
//Multiname qname(ns,name);
|
|
//toplevel->definitionErrorClass()->throwError(kRedefinedError, core->toErrorString(&qname));
|
|
}
|
|
}
|
|
|
|
void AbcParser::addNamedScript(Namespace* ns, Stringp name, AbstractFunction* script)
|
|
{
|
|
AbstractFunction* s = (AbstractFunction*) domain->namedScripts->get(name, ns);
|
|
if (!s)
|
|
{
|
|
if(ns->isPrivate())
|
|
{
|
|
pool->addPrivateNamedScript(name, ns, script);
|
|
}
|
|
else
|
|
{
|
|
domain->namedScripts->add(name, ns, (Atom)script);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// duplicate definition
|
|
//Multiname qname(ns, name);
|
|
//toplevel->definitionErrorClass()->throwError(kRedefinedError, core->toErrorString(&qname));
|
|
}
|
|
}
|
|
|
|
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 count = readU30(pos);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console << "script_count=" << count << "\n";
|
|
}
|
|
#endif
|
|
|
|
#if defined(AVMPLUS_PROFILE) || defined(AVMPLUS_VERBOSE) || defined(DEBUGGER)
|
|
const byte* startpos = pos;
|
|
#endif
|
|
|
|
if (count == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (count > (uint32)(abcEnd - pos))
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
pool->scripts.ensureCapacity(count);
|
|
pool->scriptCount = count;
|
|
|
|
// make global objects subclasses of Object
|
|
|
|
for (uint32 i=0; i < count; i++)
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
const byte* script_pos = pos;
|
|
#endif
|
|
|
|
int init_index = readU30(pos);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console << " " << (int)(script_pos-startpos) << ":script[" << i << "]"
|
|
<< " init_index=" << init_index
|
|
<< "\n";
|
|
}
|
|
#endif
|
|
AbstractFunction* script = resolveMethodInfo(init_index);
|
|
AvmAssert(script->declaringTraits == NULL);
|
|
|
|
if (script->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((Traits*)script->declaringTraits));
|
|
}
|
|
|
|
Traits* traits = parseTraits(OBJECT_TYPE,
|
|
core->publicNamespace, core->kglobal,
|
|
script, 0);
|
|
|
|
if( !traits ) return false; // parseTraits failed
|
|
|
|
traits->final = true;
|
|
|
|
// global object, make it dynamic
|
|
NativeScriptInfo* nativeEntry;
|
|
if (nativeScripts && (nativeEntry=nativeScripts[i]) != 0)
|
|
{
|
|
traits->sizeofInstance = nativeEntry->sizeofInstance;
|
|
traits->setNativeScriptInfo(nativeEntry);
|
|
AvmAssert(traits->sizeofInstance >= sizeof(ScriptObject));
|
|
}
|
|
else
|
|
{
|
|
traits->sizeofInstance = sizeof(ScriptObject);
|
|
}
|
|
traits->needsHashtable = true;
|
|
|
|
script->makeMethodOf(traits);
|
|
traits->init = script;
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
script->name = core->concatStrings(traits->format(core), core->newString("$init"));
|
|
#endif
|
|
|
|
#if defined(AVMPLUS_INTERP) && defined(AVMPLUS_MIR)
|
|
if (!core->forcemir)
|
|
{
|
|
// suggest that we don't jit the $init methods
|
|
script->flags |= AbstractFunction::SUGGEST_INTERP;
|
|
}
|
|
#endif /* AVMPLUS_INTERP */
|
|
|
|
pool->scripts.set(i, script);
|
|
|
|
// initial scope chain is []
|
|
traits->scope = ScopeTypeChain::create(core->GetGC(),NULL,0);
|
|
}
|
|
#ifdef AVMPLUS_PROFILE
|
|
core->sprof.scriptsSize = (pos-startpos);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
// helper for interface flattening
|
|
void AbcParser::addTraits(Hashtable *ht, Traits *traits, Traits *baseTraits)
|
|
{
|
|
if(!baseTraits || !baseTraits->containsInterface(traits))
|
|
{
|
|
ht->add((Atom)traits, traits);
|
|
Traitsp *interfaces = traits->getInterfaces();
|
|
for (int i=0, n=traits->interfaceCapacity; i < n; i++) {
|
|
Traits* t = interfaces[i];
|
|
if (t != NULL && t->isInterface) {
|
|
addTraits(ht, t, baseTraits);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool AbcParser::parseInstanceInfos()
|
|
{
|
|
classCount = readU30(pos);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
core->console << "class_count=" << classCount <<"\n";
|
|
#endif
|
|
|
|
#if defined(AVMPLUS_PROFILE) || defined(AVMPLUS_VERBOSE) || defined(DEBUGGER)
|
|
const byte* startpos = pos;
|
|
#endif
|
|
|
|
if (classCount == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (classCount > (abcEnd - pos))
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
// allocate room for class infos early, to handle nested classes
|
|
pool->cinits.ensureCapacity(classCount);
|
|
pool->classCount = classCount;
|
|
|
|
instances.ensureCapacity(classCount);
|
|
|
|
for (int i=0; i < classCount; i++)
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
const byte* classpos = pos;
|
|
#endif // AVMPLUS_VERBOSE
|
|
|
|
// CONSTANT_QName name of this class
|
|
Namespace* ns;
|
|
Stringp name;
|
|
|
|
Multiname qname;
|
|
resolveQName(pos, qname);
|
|
ns = qname.getNamespace();
|
|
name = qname.getName();
|
|
|
|
// 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 ||
|
|
FUNCTION_TYPE != NULL && baseTraits == FUNCTION_TYPE)
|
|
{
|
|
// error - attempt to extend final class
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
core->console << &qname << " can't extend final class " << baseTraits << "\n";
|
|
#endif
|
|
toplevel->throwVerifyError(kCannotExtendFinalClass, core->toErrorString(&qname));
|
|
}
|
|
|
|
if (baseTraits && baseTraits->isInterface)
|
|
{
|
|
// error, can't extend interface
|
|
toplevel->throwVerifyError(kCannotExtendError, core->toErrorString(&qname), core->toErrorString(baseTraits));
|
|
}
|
|
|
|
// read flags: bit 0: sealed
|
|
// bit 1: final
|
|
// bit 2: interface
|
|
// bit 3: protected
|
|
#ifdef SAFE_PARSE
|
|
// check to see if we are trying to read past the file end or the beginning.
|
|
if ( pos < abcStart || pos >= abcEnd )
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
#endif // SAFE_PARSE
|
|
int flags = *pos++;
|
|
|
|
// read protected namespace
|
|
Namespace *protectedNamespace = NULL;
|
|
if (flags & 8)
|
|
{
|
|
protectedNamespace = parseNsRef(pos);
|
|
}
|
|
|
|
int interfaceCount = readU30(pos);
|
|
int interfaceDelta = 0;
|
|
const byte* interfacePos = pos;
|
|
|
|
if(interfaceCount)
|
|
{
|
|
Hashtable *ht = new (core->GetGC()) Hashtable(core->GetGC(), interfaceCount*2);
|
|
for( int x = 0; x < interfaceCount; ++ x )
|
|
{
|
|
Traits *t = pool->resolveTypeName(pos, toplevel);
|
|
|
|
if (!t || !t->isInterface)
|
|
{
|
|
// error, can't extend interface
|
|
toplevel->throwVerifyError(kCannotImplementError, core->toErrorString(&qname), core->toErrorString(t));
|
|
}
|
|
|
|
addTraits(ht, t, baseTraits);
|
|
}
|
|
interfaceDelta = ht->getSize();
|
|
delete ht;
|
|
}
|
|
|
|
// TODO make sure the inheritance is legal.
|
|
// - can't override final members
|
|
// - overrides agree with base class signature
|
|
|
|
uint32 iinit_index = readU30(pos);
|
|
AbstractFunction *iinit = resolveMethodInfo(iinit_index);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
// TODO: fixup this math here, since the 2's are all wrong
|
|
core->console
|
|
<< " " << (int)(classpos-startpos) << ":instance[" << i << "]"
|
|
<< " " << &qname;
|
|
|
|
if (baseTraits)
|
|
core->console << " extends " << baseTraits;
|
|
|
|
core->console
|
|
<< " interface_count=" << interfaceCount
|
|
<< " iinit_index=" << iinit_index
|
|
<< "\n";
|
|
}
|
|
#endif
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
iinit->name = core->concatStrings(Multiname::format(core,ns,name), core->newString("$iinit"));
|
|
#endif
|
|
|
|
Traits* itraits = parseTraits(baseTraits, ns, name, 0, interfaceDelta, protectedNamespace);
|
|
if( !itraits ) return false;
|
|
if (!baseTraits && core->traits.object_itraits == NULL)
|
|
{
|
|
// save object traits
|
|
core->traits.object_itraits = itraits;
|
|
itraits->isObjectType = true;
|
|
}
|
|
|
|
// AS3 language decision: dynamic is not inherited.
|
|
itraits->needsHashtable = (flags&1) == 0;
|
|
itraits->final = (flags&2) != 0;
|
|
itraits->sizeofInstance = computeInstanceSize(i, baseTraits);
|
|
|
|
if (flags & 4)
|
|
{
|
|
itraits->isInterface = true;
|
|
|
|
if (itraits->slotCount != 0)
|
|
{
|
|
toplevel->throwVerifyError(kIllegalSlotError, core->toErrorString(itraits));
|
|
}
|
|
|
|
// interface base must be *
|
|
if (baseTraits)
|
|
{
|
|
// error, can't extend this type
|
|
toplevel->throwVerifyError(kCannotExtendError, core->toErrorString(&qname), core->toErrorString(baseTraits));
|
|
}
|
|
}
|
|
|
|
if (iinit->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((Traits*)iinit->declaringTraits));
|
|
}
|
|
|
|
iinit->makeMethodOf(itraits);
|
|
itraits->init = iinit;
|
|
|
|
// parse the interfaces. resolving type refs here means interface heirarchy must be a DAG.
|
|
for (int j=0, n=interfaceCount; j < n; j++)
|
|
{
|
|
Traits *interfaceTraits = pool->resolveTypeName(interfacePos, toplevel);
|
|
|
|
itraits->addInterface(interfaceTraits);
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
core->console << " interface["<<j<<"]=" << interfaceTraits <<"\n";
|
|
#endif
|
|
}
|
|
|
|
AvmAssert(itraits->interfaceSlop() == 0);
|
|
|
|
instances.set(i, itraits);
|
|
|
|
if (pool->getTraits(name, ns, false) == NULL)
|
|
{
|
|
pool->addNamedTraits(name, ns, itraits);
|
|
}
|
|
else
|
|
{
|
|
// error, can't redefine a class or interface
|
|
//toplevel->definitionErrorClass()->throwError(kRedefinedError, core->toErrorString(&qname));
|
|
}
|
|
}
|
|
|
|
#ifdef AVMPLUS_PROFILE
|
|
core->sprof.instancesSize = (pos-startpos);
|
|
#endif
|
|
|
|
return true;
|
|
}
|
|
|
|
void AbcParser::parseClassInfos()
|
|
{
|
|
if (classCount == 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
#if defined(AVMPLUS_PROFILE) || defined(AVMPLUS_VERBOSE) || defined(DEBUGGER)
|
|
const byte* startpos = pos;
|
|
#endif
|
|
|
|
for (int i=0; i < classCount; i++)
|
|
{
|
|
// CONSTANT_Multiname name of this class
|
|
Traits* itraits = instances[i];
|
|
Namespace* ns = itraits->ns;
|
|
Stringp name = itraits->name;
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
const byte* class_pos = pos;
|
|
#endif
|
|
|
|
uint32 cinit_index = readU30(pos);
|
|
AbstractFunction *cinit = resolveMethodInfo(cinit_index);
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
{
|
|
core->console
|
|
<< " " << (int)(class_pos-startpos) << ":class[" << i << "]"
|
|
<< " " << ns << "::" << name;
|
|
|
|
core->console
|
|
<< " cinit_index=" << cinit_index
|
|
<< "\n";
|
|
}
|
|
#endif
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
Stringp cinitName = core->concatStrings(name, core->newString("$cinit"));
|
|
cinit->name = Multiname::format(core,ns,cinitName);
|
|
#endif
|
|
|
|
Traits* ctraits = parseTraits(CLASS_TYPE,
|
|
ns, core->internString(core->concatStrings(name, core->newString("$"))), 0, 0, itraits->protectedNamespace);
|
|
|
|
NativeClassInfo *nativeEntry;
|
|
if (nativeClasses && (nativeEntry=nativeClasses[i]) != 0)
|
|
{
|
|
ctraits->sizeofInstance = nativeEntry->sizeofClass;
|
|
ctraits->setNativeClassInfo(nativeEntry);
|
|
}
|
|
else
|
|
{
|
|
ctraits->sizeofInstance = sizeof(ClassClosure);
|
|
}
|
|
|
|
AvmAssert(cinit->declaringTraits == NULL);
|
|
|
|
if (cinit->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((Traits*)cinit->declaringTraits));
|
|
}
|
|
|
|
cinit->makeMethodOf(ctraits);
|
|
ctraits->init = cinit;
|
|
ctraits->itraits = itraits;
|
|
ctraits->final = true;
|
|
ctraits->needsHashtable = true;
|
|
|
|
#if defined(AVMPLUS_INTERP) && defined(AVMPLUS_MIR)
|
|
if (!core->forcemir)
|
|
{
|
|
// suggest that we don't jit the class initializer
|
|
cinit->flags |= AbstractFunction::SUGGEST_INTERP;
|
|
}
|
|
#endif /* AVMPLUS_INTERP */
|
|
|
|
pool->cinits.set(i, cinit);
|
|
}
|
|
|
|
#ifdef AVMPLUS_PROFILE
|
|
core->sprof.classesSize = (pos-startpos);
|
|
#endif
|
|
}
|
|
|
|
unsigned int AbcParser::readU30(const byte *&p) const
|
|
{
|
|
#ifdef SAFE_PARSE
|
|
// 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 byte 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)
|
|
if ( p >= abcEnd || p < abcStart )
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
#endif //SAFE_PARSE
|
|
return toplevel->readU30(p);
|
|
}
|
|
|
|
}
|