Mozilla/mozilla/js/tamarin/core/PoolObject.cpp
wsharp%adobe.com 039e5a464d bug 375561. stejohns+ review. Various fixes from Flash player codebase
git-svn-id: svn://10.0.0.236/trunk@222472 18797224-902f-48f8-a5cc-f745e15eee43
2007-03-27 18:37:45 +00:00

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;
}
}