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

767 lines
27 KiB
C++

/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "avmplus.h"
#ifdef VMCFG_NANOJIT
# include "CodegenLIR.h"
#endif
namespace avmplus
{
// This assert is here to ensure that "GCDouble" is the same
// bit value as "double"; GCDouble exists solely to provide
// a GCObject-descended type to satisfy new constraints on List.
MMGC_STATIC_ASSERT(sizeof(GCDouble) == sizeof(double));
#ifdef VMCFG_FLOAT
// same for float4_t/GCFloat4
MMGC_STATIC_ASSERT(sizeof(GCFloat4) == sizeof(float4_t) && sizeof(GCFloat4)%16 == 0 );
// GCFloat is trickier: allocations in heap are 8-byte aligned.
// The static assertions here will verify that
// GCFloat's size is greater than float, 8-byte aligned, and
// the "value" is right at the beginning of a "GCFloat" so that it is
// safe to reinterpret-cast a GCFLoat* to float*.
MMGC_STATIC_ASSERT(sizeof(GCFloat)%8==0 && sizeof(GCFloat)>=sizeof(float)
&& offsetof(GCFloat,value)==0 );
#endif
PoolObject::PoolObject(AvmCore* core, ScriptBuffer& sb, const uint8_t* startPos, ApiVersion apiVersion) :
core(core),
cpool_int(core->GetGC(), 0),
cpool_uint(core->GetGC(), 0),
cpool_double(core->GetGC(), 0),
#ifdef VMCFG_FLOAT
cpool_float(core->GetGC(), 0),
cpool_float4(core->GetGC(), 0),
#endif
cpool_ns(core->GetGC(), 0),
cpool_ns_set(core->GetGC(), 0),
#ifdef VMCFG_HALFMOON
cpool_const_double(core->GetGC(), 0),
cpool_const_string(core->GetGC(), 0),
#endif
#ifndef AVMPLUS_64BIT
cpool_int_atoms(core->GetGC(), 0),
cpool_uint_atoms(core->GetGC(), 0),
#endif
cpool_mn_offsets(core->GetGC(), 0),
metadata_infos(core->GetGC(), 0),
m_loadedTraits(MultinameTraitsHashtable::create(core->GetGC())),
m_cachedTraits(MultinameTraitsHashtable::create(core->GetGC())),
m_loadedScripts(MultinameMethodInfoHashtable::create(core->GetGC())),
m_cachedScripts(MultinameMethodInfoHashtable::create(core->GetGC())),
_code(sb.getImpl()),
_abcStart(startPos),
_abcStringStart(NULL),
_abcStringEnd(NULL),
_abcStrings(NULL),
_classes(core->GetGC(), 0),
_scripts(core->GetGC(), 0),
_methods(core->GetGC(), 0)
#ifdef DEBUGGER
, _method_dmi(core->GetGC(), 0)
#endif
, _method_name_indices(core->GetGC(), 0)
, _apiVersion(apiVersion)
, _uniqueId(-1)
#ifdef VMCFG_AOT
, aotInfo(NULL)
, aotRoot(NULL)
#endif
{
version = AvmCore::readU16(&code()[0]) | AvmCore::readU16(&code()[2])<<16;
core->addLivePool(this);
}
PoolObject::~PoolObject()
{
#ifdef VMCFG_AOT
if (aotRoot)
delete aotRoot;
#endif
#ifdef VMCFG_NANOJIT
mmfx_delete(codeMgr);
#endif
}
void PoolObject::dynamicizeStrings()
{
if (!MMgc::GC::GetGC(this)->Destroying())
{
// make all strings created so far dynamic,
// making sure that no pointers into ABC data persist
// (string 0 is always core->kEmptyString: skip it)
ConstantStringData* dataP = _abcStrings->data;
for (uint32_t i = 1; i < constantStringCount; i++)
{
++dataP;
if (dataP->abcPtr >= _abcStringStart && dataP->abcPtr < _abcStringEnd)
{
// it's still a raw ABC ptr, not a String
continue;
}
Stringp s = dataP->str;
// in theory, only index 0 should be the empty string... but in practice,
// any index could be empty, and makeDynamic doesn't work on zero-length strings.
// since that call doesn't have easy access to an AvmCore, do the check here.
// (s can be null in the obscure case of a fuzzed ABC)
if (!s || s->isEmpty())
{
// all zero-length strings should be kEmptyString.
AvmAssert(!s || s == core->kEmptyString);
continue;
}
s->makeDynamic(_abcStringStart, uint32_t(_abcStringEnd - _abcStringStart));
}
}
}
Namespace* PoolObject::getNamespace(int32_t index) const
{
return cpool_ns[index];
}
NamespaceSetp PoolObject::getNamespaceSet(int32_t index) const
{
return cpool_ns_set[index];
}
////////////////////////////////////////////////////////////////////
bool ConstantStringContainer::gcTrace(MMgc::GC* gc, size_t cursor)
{
gc->TraceLocation(&pool);
size_t cap = pool->constantStringCount;
const uint32_t work_increment = 2000/sizeof(void*);
if (work_increment * cursor >= cap)
return false;
size_t work = work_increment;
bool more = true;
if (work_increment * (cursor + 1) >= cap) {
work = cap - (work_increment * cursor);
more = false;
}
const uint8_t *start = pool->_abcStringStart;
const uint8_t *end = pool->_abcStringEnd;
// Skip strings into the ABC data, everything else is a real String pointer
for ( size_t i=0 ; i < work ; i++ ) {
ConstantStringData& item = data[(work_increment * cursor) + i];
if (item.abcPtr >= start && item.abcPtr < end)
continue;
gc->TraceLocation(&item.str);
}
return more;
}
void PoolObject::setupConstantStrings(uint32_t count)
{
// Always allocate slot 0 in the data array, which will be
// initialized to an empty String* in AbcParser::parseCpool().
// We also avoid invoking Calloc() with a size of zero,
// which will cause an assertion to fail.
// TODO: If there are no strings, we should be able to set
// data = NULL, size = 0, and avoid calling the allocator.
// Slot 0 should never be accessed, and the code guards against
// such accesses generally, but there are presently exceptions.
// With the guards spread over much code, it is safer to do
// as we do here. This problem extends to other constant pool
// entry types, and should be cleaned up. See bug 557684.
//
// Be sure to zero the memory or garbage will be touched by
// the presweep handler that cleans up the string pool, see
// Bugzilla 574427.
if (count == 0)
count = 1;
// FIXME: worry about overflow in the 'extra' computation
_abcStrings = ConstantStringContainer::create(core->gc, (count-1) * sizeof(ConstantStringData), this);
constantStringCount = count;
}
Stringp PoolObject::getString(int32_t index) const
{
ConstantStringData* dataP = _abcStrings->data + index;
if (dataP->abcPtr >= _abcStringStart && dataP->abcPtr < _abcStringEnd)
{
// String not created yet; grab the pointer to the (verified) ABC data
uint32_t len = AvmCore::readU32(dataP->abcPtr);
// strict=false for bug-compatibility with swfs with incorrect utf8 encoding of strings
Stringp s = core->internStringUTF8((const char*) dataP->abcPtr, len, true, false);
s->Stick(); // FIXME - Bugzilla 596918: The Stick() call is dodgy.
dataP->abcPtr = NULL; // Important to clear it - what's there is not an RCObject*, don't let WBRC see it
WBRC(core->gc, _abcStrings, &dataP->str, s);
}
return dataP->str;
}
/*static*/ bool PoolObject::isLegalDefaultValue(BuiltinType bt, Atom value)
{
switch (bt)
{
case BUILTIN_any:
return true;
case BUILTIN_object:
return (value != undefinedAtom);
case BUILTIN_number:
return AvmCore::isNumber(value);
#ifdef VMCFG_FLOAT
case BUILTIN_float:
return AvmCore::isFloat(value);
case BUILTIN_float4:
return AvmCore::isFloat4(value);
#endif
case BUILTIN_boolean:
return AvmCore::isBoolean(value);
case BUILTIN_uint:
if (AvmCore::isDouble(value))
{
const double d = AvmCore::number_d(value);
if (d == (uint32_t)d)
return true;
}
return atomIsIntptr(value) && atomCanBeUint32(value);
case BUILTIN_int:
if (AvmCore::isDouble(value))
{
const double d = AvmCore::number_d(value);
if (d == (int32_t)d)
return true;
}
return atomIsIntptr(value) && atomCanBeInt32(value);
case BUILTIN_string:
return AvmCore::isNull(value) || AvmCore::isString(value);
case BUILTIN_namespace:
return AvmCore::isNull(value) || AvmCore::isNamespace(value);
default:
return AvmCore::isNull(value);
}
//return false; // unreachable
}
Atom PoolObject::getLegalDefaultValue(const Toplevel* toplevel, uint32_t index, CPoolKind kind, Traits* t)
{
// toplevel actually can be null, when resolving the builtin classes...
// but they should never cause verification errors in functioning builds
//AvmAssert(toplevel != NULL);
const BuiltinType bt = Traits::getBuiltinType(t);
uint32_t maxcount = 0;
Atom value;
if (index)
{
// Look in the cpool specified by kind
switch (kind)
{
case CONSTANT_Int:
{
if (index >= (maxcount = constantIntCount))
goto range_error;
const int32_t i = cpool_int[index];
#ifdef AVMPLUS_64BIT
value = core->intToAtom(i);
AvmAssert(atomIsIntptr(value) && atomCanBeInt32(value));
#else
// LIR relies on the return values from this being "sticky" so it can insert them inline.
// that's true for everything but int/uints that overflow, so special-case them.
// @todo this can/should go away when we convert to 64-bit Box atoms.
if (!cpool_int_atoms.length())
{
cpool_int_atoms.ensureCapacity(constantIntCount);
for (uint32_t j = 0; j < constantIntCount; ++j)
cpool_int_atoms.set(j, 0);
AvmAssert(cpool_int_atoms.length() == constantIntCount);
}
value = (Atom)cpool_int_atoms[index];
if (value == 0)
{
value = core->intToAtom(i);
if (AvmCore::isDouble(value))
{
cpool_int_atoms.set(index, value);
}
AvmAssert(AvmCore::isNumber(value));
}
else
{
AvmAssert(AvmCore::isDouble(value));
}
#endif
break;
}
case CONSTANT_UInt:
{
if (index >= (maxcount = constantUIntCount))
goto range_error;
const int32_t u = cpool_uint[index];
#ifdef AVMPLUS_64BIT
value = core->uintToAtom(u);
AvmAssert(atomIsIntptr(value) && atomCanBeUint32(value));
#else
// LIR relies on the return values from this being "sticky" so it can insert them inline.
// that's true for everything but int/uints that overflow, so special-case them.
// @todo this can/should go away when we convert to 64-bit Box atoms.
if (!cpool_uint_atoms.length())
{
cpool_uint_atoms.ensureCapacity(constantUIntCount);
for (uint32_t j = 0; j < constantUIntCount; ++j)
cpool_uint_atoms.set(j, 0);
AvmAssert(cpool_uint_atoms.length() == constantUIntCount);
}
value = (Atom)cpool_uint_atoms[index];
if (value == 0)
{
value = core->uintToAtom(u);
if (AvmCore::isDouble(value))
{
cpool_uint_atoms.set(index, value);
}
AvmAssert(AvmCore::isNumber(value));
}
else
{
AvmAssert(AvmCore::isDouble(value));
}
#endif
break;
}
case CONSTANT_Double:
if (index >= (maxcount = constantDoubleCount))
goto range_error;
value = kDoubleType|(uintptr_t)cpool_double[index];
break;
#ifdef VMCFG_FLOAT
case CONSTANT_Float:
if (index >= (maxcount = constantFloatCount))
goto range_error;
value = kSpecialBibopType|(uintptr_t)cpool_float[index];
break;
case CONSTANT_Float4:
if (index >= (maxcount = constantFloat4Count))
goto range_error;
value = kSpecialBibopType|(uintptr_t)cpool_float4[index];
break;
#endif
case CONSTANT_Utf8:
if (index >= (maxcount = constantStringCount))
goto range_error;
value = getString(index)->atom();
break;
case CONSTANT_True:
value = trueAtom;
break;
case CONSTANT_False:
value = falseAtom;
break;
case CONSTANT_Namespace:
case CONSTANT_PackageNamespace:
case CONSTANT_PackageInternalNs:
case CONSTANT_ProtectedNamespace:
case CONSTANT_ExplicitNamespace:
case CONSTANT_StaticProtectedNs:
case CONSTANT_PrivateNs:
if (index >= (maxcount = constantNsCount))
goto range_error;
value = cpool_ns[index]->atom();
break;
case CONSTANT_Null:
value = nullObjectAtom;
break;
default:
// Multinames & NamespaceSets are invalid default values.
goto illegal_default_error;
}
}
else
{
switch (bt)
{
case BUILTIN_any:
value = undefinedAtom;
break;
#ifdef VMCFG_FLOAT
case BUILTIN_float:
value = core->kFltNaN;
break;
case BUILTIN_float4:
value = core->kFlt4NaN;
break;
#endif
case BUILTIN_number:
value = core->kNaN;
break;
case BUILTIN_boolean:
value = falseAtom;
break;
case BUILTIN_int:
case BUILTIN_uint:
value = (zeroIntAtom);
break;
case BUILTIN_string:
value = nullStringAtom;
break;
case BUILTIN_namespace:
value = nullNsAtom;
break;
case BUILTIN_object:
default:
value = nullObjectAtom;
break;
}
}
if (!isLegalDefaultValue(bt, value))
goto illegal_default_error;
return value;
illegal_default_error:
if (toplevel)
{
if (t)
{
toplevel->throwVerifyError(kIllegalDefaultValue, core->toErrorString(Multiname(t->ns(), t->name())));
}
else
{
toplevel->throwVerifyError(kCorruptABCError);
}
}
AvmAssert(!"unhandled verify error");
return undefinedAtom; // not reached
range_error:
if (toplevel)
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(maxcount));
AvmAssert(!"unhandled verify error");
return undefinedAtom; // not reached
}
void PoolObject::parseMultiname(const uint8_t *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.
int32_t 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::readU32(pos);
if (!index)
m.setAnyNamespace();
else
m.setNamespace(getNamespace(index));
index = AvmCore::readU32(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::readU32(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::readU32(pos);
if (!index)
m.setAnyName();
else
m.setName(getString(index));
index = AvmCore::readU32(pos);
AvmAssert(index != 0);
m.setNsset(getNamespaceSet(index));
m.setAttr(kind==CONSTANT_MultinameA);
break;
}
case CONSTANT_MultinameL:
case CONSTANT_MultinameLA:
{
m.setRtname();
index = AvmCore::readU32(pos);
AvmAssert(index != 0);
m.setNsset(getNamespaceSet(index));
m.setAttr(kind==CONSTANT_MultinameLA);
break;
}
case CONSTANT_TypeName:
{
index = AvmCore::readU32(pos);
// Note that AbcParser ensures that the Multiname we're parsing can't
// be a CONSTANT_TypeName, thus the recursion here can't be more
// than a single level deep.
parseMultiname(_abcStart + cpool_mn_offsets[index], m);
index = AvmCore::readU32(pos);
AvmAssert(index==1);
m.setTypeParameter(AvmCore::readU32(pos));
break;
}
default:
AvmAssert(false);
}
return;
}
void PoolObject::resolveBindingNameNoCheck(uint32_t index, Multiname &m, const Toplevel* toplevel) const
{
// FIXME: should only assert and not throw, pending correctness verification, see bug https://bugzilla.mozilla.org/show_bug.cgi?id=557541
if (index == 0 || index >= cpool_mn_offsets.length())
{
if (toplevel)
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(cpool_mn_offsets.length()));
AvmAssert(!"unhandled verify error");
}
parseMultiname(m, index);
if (!(m.isBinding() && (m.isQName() || isBuiltin)))
{
if (toplevel)
toplevel->throwVerifyError(kCpoolEntryWrongTypeError, core->toErrorString(index));
AvmAssert(!"unhandled verify error");
}
}
Traits* PoolObject::resolveTypeName(uint32_t index, const Toplevel* toplevel, bool allowVoid/*=false*/)
{
// only save the type name for now. verifier will resolve to traits
if (index == 0)
{
return NULL;
}
// check contents is a multiname. in the cpool, and type system, kObjectType means multiname.
if (index >= cpool_mn_offsets.length())
{
if (toplevel)
toplevel->throwVerifyError(kCpoolIndexRangeError, core->toErrorString(index), core->toErrorString(cpool_mn_offsets.length()));
AvmAssert(!"unhandled verify error");
}
Multiname m;
parseMultiname(m, index);
Traits* t = core->domainMgr()->findTraitsInPoolByMultiname(this, m);
if (t == (Traits*)BIND_AMBIGUOUS)
{
if (toplevel)
toplevel->throwReferenceError(kAmbiguousBindingError, m);
AvmAssert(!"unhandled verify error");
}
if(m.isParameterizedType())
{
core->stackCheck(const_cast<Toplevel*>(toplevel));
Traits* param_traits = resolveTypeName(m.getTypeParameter(), toplevel);
t = resolveParameterizedType(toplevel, t, param_traits);
}
if (!t)
{
#ifdef AVMPLUS_VERBOSE
if (!toplevel || !toplevel->verifyErrorClass())
core->console << "class not found: " << m << " index=" << (uint32_t)index << "\n";
#endif
if (toplevel)
toplevel->throwVerifyError(kClassNotFoundError, core->toErrorString(&m));
AvmAssert(!"unhandled verify error");
}
if (!allowVoid && t == VOID_TYPE)
{
if (toplevel)
toplevel->throwVerifyError(kIllegalVoidError);
AvmAssert(!"unhandled verify error");
}
return t;
}
Traits* PoolObject::resolveParameterizedType(const Toplevel* toplevel, Traits* base, Traits* param_traits) const
{
Traits* r = NULL;
if (base == core->traits.vector_itraits)
{
// Only vector is parameterizable for now...
switch (Traits::getBuiltinType(param_traits))
{
case BUILTIN_any:
r = core->traits.vectorobj_itraits;
break;
case BUILTIN_int:
r = core->traits.vectorint_itraits;
break;
case BUILTIN_uint:
r = core->traits.vectoruint_itraits;
break;
#ifdef VMCFG_FLOAT
case BUILTIN_float:
r = core->traits.vectorfloat_itraits;
break;
case BUILTIN_float4:
r = core->traits.vectorfloat4_itraits;
break;
#endif
case BUILTIN_number:
r = core->traits.vectordouble_itraits;
break;
default:
{
PoolObject* pool = param_traits->pool;
Stringp fullname = VectorClass::makeVectorClassName(core, param_traits);
r = core->domainMgr()->findTraitsInPoolByNameAndNS(pool, fullname, base->ns());
if (!r)
{
r = core->traits.vectorobj_itraits->newParameterizedITraits(fullname, base->ns(), param_traits);
r->verifyBindings(toplevel);
core->domainMgr()->addNamedTraits(pool, fullname, base->ns(), r);
}
break;
}
}
}
return r;
}
// search metadata record at meta_pos for name, return true if present
bool PoolObject::hasMetadataName(const uint8_t* meta_pos, const String* name)
{
AvmAssert(meta_pos && name->isInterned());
uint32_t metadata_count = AvmCore::readU32(meta_pos);
for (uint32_t i=0; i < metadata_count; i++) {
uint32_t metadata_index = AvmCore::readU32(meta_pos);
const uint8_t* metadata_pos = getMetadataInfoPos(metadata_index);
if (metadata_pos) {
uint32_t name_index = AvmCore::readU32(metadata_pos);
AvmCore::skipU32(metadata_pos, 1); // skip val_count
if (name_index > 0 && name_index < constantStringCount &&
getString(name_index) == name)
return true;
}
}
return false;
}
void PoolObject::initPrecomputedMultinames()
{
if (precompNames == NULL)
{
MMgc::GC* gc = core->GetGC();
uint32_t nNames = cpool_mn_offsets.length();
precompNames = ExactStructContainer<HeapMultiname>::create(gc, destroyPrecomputedMultinames, nNames);
for (uint32_t i=1; i < nNames; i++) {
Multiname mn;
parseMultiname(mn, i);
precompNames->get(i).setMultiname(gc, precompNames, mn);
}
}
}
void PoolObject::destroyPrecomputedMultinames(ExactStructContainer<HeapMultiname>* self)
{
// Destroy each HeapMultiname to properly decrement reference counts on
// RC'd parts of HeapMultiname as soon as possible.
MMgc::GC* gc = MMgc::GC::GetGC(self);
Multiname mn;
uint32_t nNames = self->capacity();
for (uint32_t i=1; i < nNames; i++)
self->get(i).setMultiname(gc, self, mn);
}
Stringp PoolObject::getMethodInfoName(uint32_t i)
{
Stringp name = NULL;
if (core->config.methodNames && (uint32_t(i) < uint32_t(this->_method_name_indices.length())))
{
const int32_t index = this->_method_name_indices[i];
if (index >= 0)
{
if (hasString(index))
name = this->getString(index);
}
else
{
// Precomputed multinames may not be inited yet, but we'll need them eventually,
// so go ahead and init them now
this->initPrecomputedMultinames();
const Multiname& mn = precompNames->get(-index);
StringBuffer sb(core);
sb << Multiname::Format(&mn);
name = sb.toString();
}
}
return name;
}
}