879 lines
23 KiB
C++
879 lines
23 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) 2004-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
|
|
{
|
|
PoolObject::PoolObject(AvmCore* core, ScriptBuffer& sb, const byte* startPos) :
|
|
core(core),
|
|
cpool_int(0),
|
|
cpool_uint(0),
|
|
cpool_double(core->GetGC(), 0),
|
|
cpool_string(core->GetGC(), 0),
|
|
cpool_ns(core->GetGC(), 0),
|
|
cpool_ns_set(core->GetGC(), 0),
|
|
cpool_mn(0),
|
|
methods(core->GetGC(), 0),
|
|
metadata_infos(0),
|
|
cinits(core->GetGC(), 0),
|
|
scripts(core->GetGC(), 0),
|
|
abcStart(startPos)
|
|
#ifdef AVMPLUS_VERIFYALL
|
|
,verifyQueue(core->GetGC(), 0)
|
|
#endif
|
|
{
|
|
namedTraits = new(core->GetGC()) MultinameHashtable();
|
|
m_code = sb.getImpl();
|
|
#ifdef AVMPLUS_MIR
|
|
codeBuffer = new (core->GetGC()) GrowableBuffer(core->GetGC()->GetGCHeap());
|
|
#endif
|
|
version = AvmCore::readU16(&code()[0]) | AvmCore::readU16(&code()[2])<<16;
|
|
}
|
|
|
|
PoolObject::~PoolObject()
|
|
{
|
|
#ifdef AVMPLUS_MIR
|
|
delete codeBuffer;
|
|
#endif
|
|
}
|
|
|
|
AbstractFunction* PoolObject::getMethodInfo(uint32 index)
|
|
{
|
|
AvmAssert(index < methodCount);
|
|
return methods[index];
|
|
}
|
|
|
|
const byte* PoolObject::getMetadataInfoPos(uint32 index)
|
|
{
|
|
AvmAssert (index < metadataCount);
|
|
return metadata_infos[index];
|
|
}
|
|
|
|
Traits* PoolObject::getBuiltinTraits(Stringp name) const
|
|
{
|
|
AvmAssert(BIND_NONE == 0);
|
|
return (Traits*) namedTraits->getName(name);
|
|
}
|
|
|
|
Traits* PoolObject::getTraits(Stringp name, Namespace* ns, bool recursive/*=true*/) const
|
|
{
|
|
// look for class in VM-wide type table
|
|
Traits* t = domain->getNamedTraits(name, ns, recursive);
|
|
|
|
// look for class in current ABC file
|
|
if (t == NULL)
|
|
t = (Traits*) namedTraits->get(name, ns);
|
|
return t;
|
|
}
|
|
|
|
Traits* PoolObject::getTraits(Stringp name, bool recursive/*=true*/) const
|
|
{
|
|
return getTraits(name, core->publicNamespace, recursive);
|
|
}
|
|
|
|
Traits* PoolObject::getTraits(Multiname* mname, const Toplevel* toplevel, bool recursive/*=true*/) const
|
|
{
|
|
// do full lookup of multiname, error if more than 1 match
|
|
// return Traits if 1 match, NULL if 0 match, throw ambiguity error if >1 match
|
|
Traits* match = NULL;
|
|
if (mname->isBinding())
|
|
{
|
|
// multiname must not be an attr name, have wildcards, or have runtime parts.
|
|
for (int i=0, n=mname->namespaceCount(); i < n; i++)
|
|
{
|
|
Traits* t = getTraits(mname->getName(), mname->getNamespace(i), recursive);
|
|
if (t != NULL)
|
|
{
|
|
if (match == NULL)
|
|
{
|
|
match = t;
|
|
}
|
|
else if (match != t)
|
|
{
|
|
// ambiguity
|
|
toplevel->throwReferenceError(kAmbiguousBindingError, mname);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return match;
|
|
}
|
|
|
|
void PoolObject::addNamedTraits(Stringp name, Namespace* ns, Traits* traits)
|
|
{
|
|
namedTraits->add(name, ns, (Binding)traits);
|
|
}
|
|
|
|
#ifdef AVMPLUS_VERIFYALL
|
|
void PoolObject::enq(AbstractFunction* f)
|
|
{
|
|
if (!f->isVerified() && !(f->flags & AbstractFunction::VERIFY_PENDING))
|
|
{
|
|
f->flags |= AbstractFunction::VERIFY_PENDING;
|
|
verifyQueue.add(f);
|
|
}
|
|
}
|
|
|
|
void PoolObject::enq(Traits* t)
|
|
{
|
|
for (int i=0, n=t->methodCount; i < n; i++)
|
|
{
|
|
AbstractFunction* f = t->getMethod(i);
|
|
if (f)
|
|
enq(f);
|
|
}
|
|
}
|
|
|
|
void PoolObject::processVerifyQueue(Toplevel* toplevel)
|
|
{
|
|
while (!verifyQueue.isEmpty())
|
|
{
|
|
AbstractFunction* f = verifyQueue.removeLast();
|
|
AvmAssert(!f->isVerified());
|
|
f->verify(toplevel);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
Namespace* PoolObject::getNamespace(int index) const
|
|
{
|
|
return cpool_ns[index];
|
|
}
|
|
|
|
NamespaceSet* PoolObject::getNamespaceSet(int index) const
|
|
{
|
|
return cpool_ns_set[index];
|
|
}
|
|
|
|
Stringp PoolObject::getString(int index) const
|
|
{
|
|
return cpool_string[index];
|
|
}
|
|
|
|
Atom PoolObject::getDefaultValue(const Toplevel* toplevel, uint32 index, CPoolKind kind, Traits* t) const
|
|
{
|
|
AvmAssert(index != 0);
|
|
// Look in the cpool specified by kind
|
|
switch(kind)
|
|
{
|
|
case CONSTANT_Int:
|
|
if( index >= constantIntCount )
|
|
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(constantIntCount));
|
|
return core->intToAtom(cpool_int[index]);
|
|
|
|
case CONSTANT_UInt:
|
|
if( index >= constantUIntCount )
|
|
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(constantUIntCount));
|
|
return core->uintToAtom(cpool_uint[index]);
|
|
|
|
case CONSTANT_Double:
|
|
if( index >= constantDoubleCount )
|
|
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(constantDoubleCount));
|
|
return kDoubleType|(uintptr)cpool_double[index];
|
|
|
|
case CONSTANT_Utf8:
|
|
if( index >= constantStringCount )
|
|
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(constantStringCount));
|
|
return cpool_string[index]->atom();
|
|
|
|
case CONSTANT_True:
|
|
return trueAtom;
|
|
|
|
case CONSTANT_False:
|
|
return falseAtom;
|
|
|
|
case CONSTANT_Namespace:
|
|
case CONSTANT_PackageNamespace:
|
|
case CONSTANT_PackageInternalNs:
|
|
case CONSTANT_ProtectedNamespace:
|
|
case CONSTANT_ExplicitNamespace:
|
|
case CONSTANT_StaticProtectedNs:
|
|
case CONSTANT_PrivateNs:
|
|
if( index >= constantNsCount )
|
|
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(constantNsCount));
|
|
return cpool_ns[index]->atom();
|
|
|
|
case CONSTANT_Null:
|
|
return nullObjectAtom;
|
|
|
|
default:
|
|
{
|
|
// Multinames & NamespaceSets are invalid default values.
|
|
if (t)
|
|
{
|
|
Multiname qname(t->ns, t->name);
|
|
toplevel->throwVerifyError(kIllegalDefaultValue, core->toErrorString(&qname));
|
|
}
|
|
else
|
|
{
|
|
toplevel->throwVerifyError(kCorruptABCError);
|
|
}
|
|
return undefinedAtom; // not reached
|
|
}
|
|
}
|
|
}
|
|
|
|
void PoolObject::parseMultiname(const byte *pos, Multiname& m) const
|
|
{
|
|
// the multiname has already been validated so we don't do
|
|
// any checking here, we just fill in the Multiname object
|
|
// with the information we have parsed.
|
|
|
|
int index;
|
|
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)
|
|
|
|
index = AvmCore::readU30(pos);
|
|
if (!index)
|
|
m.setAnyNamespace();
|
|
else
|
|
m.setNamespace(getNamespace(index));
|
|
|
|
index = AvmCore::readU30(pos);
|
|
if (!index)
|
|
m.setAnyName();
|
|
else
|
|
m.setName(getString(index));
|
|
|
|
m.setQName();
|
|
m.setAttr(kind==CONSTANT_QnameA);
|
|
break;
|
|
}
|
|
|
|
case CONSTANT_RTQname:
|
|
case CONSTANT_RTQnameA:
|
|
{
|
|
// U16 name_index
|
|
// parse a multiname with just a name; ns fetched at runtime
|
|
|
|
index = AvmCore::readU30(pos);
|
|
if (!index)
|
|
m.setAnyName();
|
|
else
|
|
m.setName(getString(index));
|
|
|
|
m.setQName();
|
|
m.setRtns();
|
|
m.setAttr(kind==CONSTANT_RTQnameA);
|
|
break;
|
|
}
|
|
|
|
case CONSTANT_RTQnameL:
|
|
case CONSTANT_RTQnameLA:
|
|
{
|
|
m.setQName();
|
|
m.setRtns();
|
|
m.setRtname();
|
|
m.setAttr(kind==CONSTANT_RTQnameLA);
|
|
break;
|
|
}
|
|
|
|
case CONSTANT_Multiname:
|
|
case CONSTANT_MultinameA:
|
|
{
|
|
index = AvmCore::readU30(pos);
|
|
if (!index)
|
|
m.setAnyName();
|
|
else
|
|
m.setName(getString(index));
|
|
|
|
index = AvmCore::readU30(pos);
|
|
AvmAssert(index != 0);
|
|
m.setNsset(getNamespaceSet(index));
|
|
|
|
m.setAttr(kind==CONSTANT_MultinameA);
|
|
break;
|
|
}
|
|
|
|
case CONSTANT_MultinameL:
|
|
case CONSTANT_MultinameLA:
|
|
{
|
|
m.setRtname();
|
|
|
|
index = AvmCore::readU30(pos);
|
|
AvmAssert(index != 0);
|
|
m.setNsset(getNamespaceSet(index));
|
|
|
|
m.setAttr(kind==CONSTANT_MultinameLA);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
AvmAssert(false);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
Atom PoolObject::resolveQName(const byte* &p, Multiname &m, const Toplevel* toplevel) const
|
|
{
|
|
uint32 index = AvmCore::readU30(p);
|
|
|
|
if (index == 0 || index >= constantMnCount)
|
|
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(constantMnCount));
|
|
|
|
Atom a = cpool_mn[index];
|
|
|
|
parseMultiname(a, m);
|
|
if (!m.isQName())
|
|
toplevel->throwVerifyError(kCpoolEntryWrongTypeError, core->toErrorString(index));
|
|
|
|
return a;
|
|
}
|
|
|
|
|
|
Traits* PoolObject::resolveTypeName(const byte* &pc, const Toplevel* toplevel, bool allowVoid/*=false*/) const
|
|
{
|
|
// only save the type name for now. verifier will resolve to traits
|
|
uint32 index = AvmCore::readU30(pc);
|
|
if (index == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
// check contents is a multiname. in the cpool, and type system, kObjectType means multiname.
|
|
if (index >= constantMnCount)
|
|
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(constantMnCount));
|
|
|
|
Atom a = cpool_mn[index];
|
|
|
|
Multiname m;
|
|
parseMultiname(a, m);
|
|
|
|
Traits* t = getTraits(&m, toplevel);
|
|
if (!t)
|
|
{
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (!toplevel || !toplevel->verifyErrorClass())
|
|
core->console << "class not found: " << &m << "\n";
|
|
#endif
|
|
toplevel->throwVerifyError(kClassNotFoundError, core->toErrorString(&m));
|
|
}
|
|
|
|
if (!allowVoid && t == VOID_TYPE)
|
|
toplevel->throwVerifyError(kIllegalVoidError);
|
|
|
|
return t;
|
|
}
|
|
|
|
void PoolObject::allowEarlyBinding(Traits* t, bool& slot) const
|
|
{
|
|
// the compiler can early bind to a type's slots when it's defined
|
|
// in the same abc file (ensuring it came from the same compiler)
|
|
// or when the base class came from another abc file and has zero slots
|
|
// this ensures you cant use the early opcodes to access an external type's
|
|
// private members.
|
|
slot = true;
|
|
while (t != NULL && t->slotCount > 0 && slot)
|
|
{
|
|
if (t->pool != this)
|
|
{
|
|
if(t->slotCount > 0)
|
|
slot = false;
|
|
}
|
|
t = t->base;
|
|
}
|
|
}
|
|
|
|
void PoolObject::resolveTraits(Traits *traits, int firstSlot, const Toplevel* toplevel)
|
|
{
|
|
int offset = traits->sizeofInstance;
|
|
int padoffset = -1;
|
|
if (traits->base && traits->base->base)
|
|
{
|
|
AvmAssert(traits->base->getTotalSize() != 0);
|
|
offset += traits->base->getTotalSize() - traits->base->sizeofInstance;
|
|
}
|
|
|
|
const byte* pos = traits->getTraitsPos();
|
|
AvmAssert(pos != NULL);
|
|
|
|
int nameCount = AvmCore::readU30(pos);
|
|
|
|
int slot_id = firstSlot;
|
|
|
|
AbcGen gen(core->GetGC(), traits->slotCount * 7);
|
|
|
|
bool earlySlotBinding;
|
|
allowEarlyBinding(traits, earlySlotBinding);
|
|
|
|
for (int i=0; i < nameCount; i++)
|
|
{
|
|
Multiname qn;
|
|
resolveQName(pos, qn, toplevel);
|
|
Stringp name = qn.getName();
|
|
Namespace* ns = qn.getNamespace();
|
|
int tag = (TraitKind)pos[0];
|
|
TraitKind kind = (TraitKind) (tag & 0x0f); //Get rid of the flags
|
|
pos += 1;
|
|
|
|
switch (kind)
|
|
{
|
|
case TRAIT_Slot:
|
|
case TRAIT_Const:
|
|
{
|
|
// compute the slot
|
|
uint32 useSlotId = AvmCore::readU30(pos);
|
|
if(!earlySlotBinding ) useSlotId = 0;
|
|
if (!useSlotId)
|
|
useSlotId = slot_id++;
|
|
else
|
|
useSlotId--;
|
|
|
|
// compute the type
|
|
Traits* slotTraits = resolveTypeName(pos, toplevel);
|
|
|
|
// default value
|
|
int value_index = AvmCore::readU30(pos);
|
|
|
|
CPoolKind value_kind = (CPoolKind)0;
|
|
if (value_index)
|
|
{
|
|
value_kind = (CPoolKind)*(pos++);
|
|
}
|
|
|
|
// default value for this slot.
|
|
int slotOffset;
|
|
if ((slotTraits == NUMBER_TYPE)
|
|
#ifdef AVMPLUS_64BIT
|
|
|| ((slotTraits != INT_TYPE) && (slotTraits != UINT_TYPE) && (slotTraits != BOOLEAN_TYPE))
|
|
#endif
|
|
)
|
|
{
|
|
// 8-aligned, 8-byte field
|
|
if (offset&7)
|
|
{
|
|
padoffset = offset;
|
|
offset += 4;
|
|
}
|
|
slotOffset = offset;
|
|
offset += 8;
|
|
}
|
|
else
|
|
{
|
|
// 4-aligned, 4-byte field
|
|
if (padoffset != -1)
|
|
{
|
|
slotOffset = padoffset;
|
|
padoffset = -1;
|
|
}
|
|
else
|
|
{
|
|
slotOffset = offset;
|
|
offset += 4;
|
|
}
|
|
}
|
|
|
|
traits->setSlotInfo(value_index, useSlotId, toplevel, slotTraits, slotOffset, value_kind, gen);
|
|
if( tag & ATTR_metadata )
|
|
{
|
|
traits->setSlotMetadataPos(useSlotId, pos);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TRAIT_Class:
|
|
{
|
|
// compute the slot
|
|
uint32 useSlotId = AvmCore::readU30(pos);
|
|
if( !earlySlotBinding ) useSlotId = 0;
|
|
if (!useSlotId)
|
|
useSlotId = slot_id++;
|
|
else
|
|
useSlotId--;
|
|
|
|
// get the class type
|
|
uint32 class_info = AvmCore::readU30(pos);
|
|
if (class_info >= classCount)
|
|
toplevel->throwVerifyError(kClassInfoExceedsCountError, core->toErrorString(class_info), core->toErrorString(classCount));
|
|
|
|
AbstractFunction* cinit = cinits[class_info];
|
|
if (!cinit)
|
|
toplevel->throwVerifyError(kClassInfoOrderError, core->toErrorString(class_info));
|
|
|
|
int slotOffset;
|
|
#ifdef AVMPLUS_64BIT
|
|
// 8-aligned, 8-byte field
|
|
if (offset&7)
|
|
{
|
|
padoffset = offset;
|
|
offset += 4;
|
|
}
|
|
slotOffset = offset;
|
|
offset += 8;
|
|
#else
|
|
// 4-aligned, 4-byte field
|
|
if (padoffset != -1)
|
|
{
|
|
slotOffset = padoffset;
|
|
padoffset = -1;
|
|
}
|
|
else
|
|
{
|
|
slotOffset = offset;
|
|
offset += 4;
|
|
}
|
|
#endif
|
|
|
|
traits->setSlotInfo(0, useSlotId, toplevel, cinit->declaringTraits, slotOffset, CPoolKind(0), gen);
|
|
break;
|
|
}
|
|
|
|
case TRAIT_Method:
|
|
{
|
|
int earlyDispId = AvmCore::readU30( pos );
|
|
(void)earlyDispId;
|
|
|
|
uint32 method_info = AvmCore::readU30(pos);
|
|
|
|
// method_info range already checked in AbcParser
|
|
AvmAssert(method_info < methodCount);
|
|
AbstractFunction *f = getMethodInfo(method_info);
|
|
AvmAssert(f != NULL);
|
|
|
|
// disp_id assigned by abcParser, this binding must exist already.
|
|
Binding b = traits->get(name, ns);
|
|
|
|
AvmAssert(AvmCore::isMethodBinding(b));
|
|
int disp_id = AvmCore::bindingToMethodId(b);
|
|
|
|
// !!@ Ed says there may be an earlier place in AbcParser to catch this
|
|
if (traits->getMethod(disp_id) && traits->getOverride(ns,name,tag,toplevel) == BIND_NONE)
|
|
{
|
|
toplevel->throwVerifyError(kDuplicateDispIdError, core->toErrorString(traits->getMethod(disp_id)), core->toErrorString(disp_id));
|
|
}
|
|
traits->setMethod(disp_id, f);
|
|
if( tag & ATTR_metadata )
|
|
{
|
|
traits->setMethodMetadataPos(disp_id, pos);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case TRAIT_Getter:
|
|
case TRAIT_Setter:
|
|
{
|
|
int earlyDispId = AvmCore::readU30(pos);
|
|
(void)earlyDispId;
|
|
|
|
uint32 method_info = AvmCore::readU30(pos);
|
|
|
|
// method_info already checked in AbcParser
|
|
AvmAssert(method_info < methodCount);
|
|
AbstractFunction* f = getMethodInfo(method_info);
|
|
AvmAssert(f != NULL);
|
|
|
|
Binding b = traits->get(name, ns);
|
|
|
|
AvmAssert(b==BIND_NONE || AvmCore::isAccessorBinding(b));
|
|
uint32 disp_id = kind == TRAIT_Getter ? AvmCore::bindingToGetterId(b) : AvmCore::bindingToSetterId(b);
|
|
AvmAssert(disp_id < traits->methodCount);
|
|
// !!@ Ed says there may be an earlier place in AbcParser to catch this
|
|
if (traits->getMethod(disp_id))
|
|
{
|
|
Binding baseBinding = traits->getOverride(ns,name,tag,toplevel);
|
|
if (kind == TRAIT_Getter && !AvmCore::hasGetterBinding(baseBinding) ||
|
|
kind == TRAIT_Setter && !AvmCore::hasSetterBinding(baseBinding))
|
|
{
|
|
toplevel->throwVerifyError(kDuplicateDispIdError, core->toErrorString(traits->getMethod(disp_id)), core->toErrorString(disp_id));
|
|
}
|
|
}
|
|
traits->setMethod(disp_id, f);
|
|
if( tag & ATTR_metadata )
|
|
{
|
|
traits->setMethodMetadataPos(disp_id, pos);
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
// unsupported traits type
|
|
toplevel->throwVerifyError(kUnsupportedTraitsKindError, core->toErrorString(kind));
|
|
}
|
|
|
|
// skip metadata
|
|
if( tag & ATTR_metadata )
|
|
{
|
|
int metaCount = AvmCore::readU30(pos);
|
|
for( int metadata = 0; metadata < metaCount; ++metadata )
|
|
{
|
|
AvmCore::readU30(pos);
|
|
}
|
|
}
|
|
}
|
|
|
|
int *offsets = traits->getOffsets();
|
|
for (uint32 i=0, n=traits->slotCount; i < n; i++)
|
|
{
|
|
if (offsets[i] == 0)
|
|
{
|
|
// sparse slot table. make types default to *
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (verbose)
|
|
{
|
|
core->console << "WARNING: slot " << i+1 << " on " << traits << " not defined by compiler. Using *\n";
|
|
}
|
|
#endif
|
|
|
|
AvmAssert(0);
|
|
int slotOffset;
|
|
// 4-aligned, 4-byte field
|
|
if (padoffset != -1)
|
|
{
|
|
slotOffset = padoffset;
|
|
padoffset = -1;
|
|
}
|
|
else
|
|
{
|
|
slotOffset = offset;
|
|
offset += 4;
|
|
}
|
|
|
|
traits->setSlotInfo(0, i, toplevel, NULL, slotOffset, CPoolKind(0), gen);
|
|
}
|
|
}
|
|
|
|
// if initialization code gen is required, create a new method body and write it to traits->init->body_pos
|
|
if(gen.size() > 0)
|
|
{
|
|
AbcGen newMethodBody(core->GetGC(), 16+gen.size());
|
|
|
|
// insert body preamble
|
|
MethodInfo *init;
|
|
|
|
if(traits->init) {
|
|
init = (MethodInfo*)(AbstractFunction*)traits->init;
|
|
const byte *pos = init->body_pos;
|
|
if( !init->body_pos ) toplevel->throwVerifyError(kCorruptABCError);
|
|
|
|
int maxStack = AvmCore::readU30(pos);
|
|
// the code we're generating needs at least 2
|
|
maxStack = maxStack > 1 ? maxStack : 2;
|
|
newMethodBody.writeInt(maxStack); // max_stack
|
|
newMethodBody.writeInt(AvmCore::readU30(pos)); //local_count
|
|
newMethodBody.writeInt(AvmCore::readU30(pos)); //init_scope_depth
|
|
newMethodBody.writeInt(AvmCore::readU30(pos)); //max_scope_depth
|
|
|
|
// skip real code length
|
|
int code_length = AvmCore::readU30(pos);
|
|
|
|
// if first instruction is OP_constructsuper keep it as first instruction
|
|
if(*pos == OP_constructsuper)
|
|
{
|
|
gen.getBytes().insert(0, OP_constructsuper);
|
|
// don't invoke it again later
|
|
pos++;
|
|
code_length--;
|
|
}
|
|
|
|
gen.abs_jump(pos, code_length);
|
|
|
|
} else {
|
|
// make one
|
|
init = new (core->GetGC()) MethodInfo();
|
|
init->declaringTraits = traits;
|
|
init->pool = this;
|
|
init->param_count = 0;
|
|
init->restOffset = 4; // sizeof(this)
|
|
init->initParamTypes(1);
|
|
init->setParamType(0, traits);
|
|
init->setReturnType(VOID_TYPE);
|
|
traits->init = init;
|
|
init->flags |= AbstractFunction::LINKED;
|
|
|
|
newMethodBody.writeInt(2); // max_stack
|
|
newMethodBody.writeInt(1); //local_count
|
|
newMethodBody.writeInt(1); //init_scope_depth
|
|
newMethodBody.writeInt(1); //max_scope_depth
|
|
|
|
gen.returnvoid();
|
|
}
|
|
|
|
newMethodBody.writeInt(gen.size()); // code length
|
|
newMethodBody.writeBytes(gen.getBytes());
|
|
|
|
// no exceptions, when we jump to the real code, we'll read the exceptions for that code
|
|
newMethodBody.writeInt(0);
|
|
|
|
// the verifier and interpreter don't read the activation traits so stop here
|
|
byte *newBytes = (byte*) core->GetGC()->Alloc(newMethodBody.size());
|
|
memcpy(newBytes, newMethodBody.getBytes().getData(), newMethodBody.size());
|
|
//init->body_pos = newBytes;
|
|
WB(core->GetGC(), init, &init->body_pos, newBytes);
|
|
}
|
|
|
|
if (traits->base && traits->base->base && traits->base->hashTableOffset != -1)
|
|
{
|
|
traits->hashTableOffset = traits->base->hashTableOffset + traits->sizeofInstance - traits->base->sizeofInstance;
|
|
}
|
|
else if (traits->needsHashtable)
|
|
{
|
|
traits->hashTableOffset = offset;
|
|
offset += sizeof(Hashtable);
|
|
}
|
|
|
|
traits->setTotalSize((size_t) offset);
|
|
}
|
|
|
|
Binding Traits::getOverride(Namespace* ns, Stringp name, int tag, const Toplevel *toplevel) const
|
|
{
|
|
int kind = tag & 0x0f;
|
|
Binding baseBinding = BIND_NONE;
|
|
if (base)
|
|
{
|
|
if (protectedNamespace == ns && base->protectedNamespace)
|
|
{
|
|
baseBinding = base->findBinding(name, base->protectedNamespace);
|
|
}
|
|
else
|
|
{
|
|
baseBinding = base->findBinding(name, ns);
|
|
}
|
|
}
|
|
if (baseBinding == BIND_NONE)
|
|
{
|
|
if (tag & ATTR_override)
|
|
{
|
|
// error if override attr set, and nothing is actually overridden
|
|
Multiname qname(ns,name);
|
|
toplevel->throwVerifyError(kIllegalOverrideError, toplevel->core()->toErrorString(&qname), toplevel->core()->toErrorString((Traits*)this));
|
|
}
|
|
return BIND_NONE;
|
|
}
|
|
|
|
switch (kind)
|
|
{
|
|
case TRAIT_Method:
|
|
{
|
|
if (AvmCore::isMethodBinding(baseBinding))
|
|
{
|
|
if (!(tag & ATTR_override))
|
|
{
|
|
// error if override flag not set, and something is overridden
|
|
break;
|
|
}
|
|
return baseBinding;
|
|
}
|
|
// error, method can only override a virtual method
|
|
break;
|
|
}
|
|
case TRAIT_Getter:
|
|
{
|
|
if (AvmCore::hasGetterBinding(baseBinding))
|
|
{
|
|
if (!(tag & ATTR_override))
|
|
{
|
|
// error if override flag not set, and something is overridden
|
|
break;
|
|
}
|
|
return baseBinding;
|
|
}
|
|
if ((baseBinding&7) == BIND_SET)
|
|
{
|
|
if (tag & ATTR_override)
|
|
{
|
|
// error if override attr set, and nothing is actually overridden
|
|
break;
|
|
}
|
|
return baseBinding;
|
|
}
|
|
// error, method can only override a virtual method
|
|
break;
|
|
}
|
|
case TRAIT_Setter:
|
|
{
|
|
if (AvmCore::hasSetterBinding(baseBinding))
|
|
{
|
|
if (!(tag & ATTR_override))
|
|
{
|
|
// error if override flag not set, and something is overridden
|
|
break;
|
|
}
|
|
return baseBinding;
|
|
}
|
|
if ((baseBinding&7) == BIND_GET)
|
|
{
|
|
if (tag & ATTR_override)
|
|
{
|
|
// error if override attr set, and nothing is actually overridden
|
|
break;
|
|
}
|
|
return baseBinding;
|
|
}
|
|
// error, method can only override a virtual method
|
|
break;
|
|
}
|
|
default:
|
|
// internal error to call getOverride on a non-method trait
|
|
AvmAssert(false);
|
|
break;
|
|
}
|
|
Multiname qname(ns,name);
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (pool->verbose)
|
|
core->console << "illegal override in "<< this << ": " << &qname <<"\n";
|
|
#endif
|
|
toplevel->throwVerifyError(kIllegalOverrideError, toplevel->core()->toErrorString(&qname), toplevel->core()->toErrorString((Traits*)this));
|
|
return BIND_NONE;
|
|
}
|
|
|
|
void PoolObject::addPrivateNamedScript(Stringp name, Namespace* ns, AbstractFunction *script)
|
|
{
|
|
privateNamedScripts.add(name, ns, (Atom)script);
|
|
}
|
|
|
|
AbstractFunction* PoolObject::getNamedScript(Multiname* multiname) const
|
|
{
|
|
AbstractFunction *f = domain->getNamedScript(multiname);
|
|
if(!f)
|
|
{
|
|
f = (AbstractFunction*)privateNamedScripts.getMulti(multiname);
|
|
}
|
|
return f;
|
|
}
|
|
}
|