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

1231 lines
42 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"
#include "pcre.h"
//#define DOPROF
//#include "../vprof/vprof.h"
namespace avmplus
{
using namespace MMgc;
/**
* MethodInfo wrapper around a system-generated init method. Used when
* there is no init method defined in the abc; this only occurs for activation
* object traits and catch-block activation traits.
*/
MethodInfo::MethodInfo(InitMethodStub, Traits* declTraits) :
_msref(declTraits->pool->core->GetGC()->emptyWeakRef),
_declarer(declTraits),
_activation(NULL),
_pool(declTraits->pool),
_abc_info_pos(NULL),
_method_id(-1),
_hasMethodBody(1),
_isResolved(1)
#ifdef VMCFG_FLOAT
,_has128bitLocals(0)
#endif // VMCFG_FLOAT
{
declTraits->core->exec->init(this, NULL);
}
#ifdef VMCFG_AOT
MethodInfo::MethodInfo(InitMethodStub, Traits* declTraits, const NativeMethodInfo* native_info, const AvmThunkNativeHandler& handler, int32_t method_id) :
MethodInfoProcHolder(),
_msref(declTraits->pool->core->GetGC()->emptyWeakRef),
_declarer(declTraits),
_activation(NULL),
_pool(declTraits->pool),
_abc_info_pos(NULL),
_method_id(method_id),
_hasMethodBody(1),
_isResolved(1)
#ifdef VMCFG_FLOAT
,_has128bitLocals(0)
#endif // VMCFG_FLOAT
{
AvmAssert(native_info != NULL);
this->_native.thunker = native_info->thunker;
this->setAotCompiled(handler);
declTraits->core->exec->init(this, native_info);
}
#endif
/**
* ordinary MethodInfo for abc or native method.
*/
MethodInfo::MethodInfo(int32_t method_id,
PoolObject* pool,
const uint8_t* abc_info_pos,
uint8_t abcFlags,
const NativeMethodInfo* native_info) :
_msref(pool->core->GetGC()->emptyWeakRef),
_declarer(NULL),
_activation(NULL),
_pool(pool),
_abc_info_pos(abc_info_pos),
_method_id(method_id),
_needArguments((abcFlags & abcMethod_NEED_ARGUMENTS) != 0),
_needActivation((abcFlags & abcMethod_NEED_ACTIVATION) != 0),
_needRest((abcFlags & abcMethod_NEED_REST) != 0),
_hasOptional((abcFlags & abcMethod_HAS_OPTIONAL) != 0),
_ignoreRest((abcFlags & abcMethod_IGNORE_REST) != 0),
_isNative((abcFlags & abcMethod_NATIVE) != 0),
_setsDxns((abcFlags & abcMethod_SETS_DXNS) != 0),
_hasParamNames((abcFlags & abcMethod_HAS_PARAM_NAMES) != 0),
_hasMethodBody(1) // assume we have bytecode body until set otherwise
#ifdef VMCFG_FLOAT
,_has128bitLocals(0)
#endif // VMCFG_FLOAT
{
AvmAssert(method_id >= 0);
if (native_info)
{
_needsCodeContext = 1;
_needsDxns = 1;
_hasMethodBody = 0;
}
pool->core->exec->init(this, native_info);
}
void MethodInfo::gcTraceHook_MethodInfo(MMgc::GC* gc)
{
if (_isNative) {
// _native - nothing to do
}
else {
gc->TraceLocation(&_abc.exceptions);
#ifdef VMCFG_WORDCODE
gc->TraceLocation(&_abc.word_code.translated_code);
gc->TraceLocation(&_abc.word_code.exceptions);
#endif
}
}
void MethodInfo::init_activationTraits(Traits* t)
{
AvmAssert(_activation.getTraits() == NULL);
_activation.setTraits(pool()->core->GetGC(), this, t);
}
void MethodInfo::init_activationScope(const ScopeTypeChain* scope)
{
AvmAssert(!_activation.hasScope());
_activation.setScope(pool()->core->GetGC(), this, scope);
}
#ifdef VMCFG_FLOAT
REALLY_INLINE float unpack_float(const void* src)
{
return *(const float*)src;
}
REALLY_INLINE float4_t unpack_float4(const void* src){
return AvmThunkUnbox_FLOAT4(float4_t,*(const Atom*)src);
}
#endif // VMCFG_FLOAT
REALLY_INLINE
void MethodInfo::_installRefToResolvedMethodSignature(MethodSignature* ms)
{
AvmAssert(isResolved());
AvmAssert(_msref->isNull());
_msref = ms->GetWeakRef();
PoolObject* pool = this->pool();
AvmCore* core = pool->core;
core->msCache()->add(ms);
}
// note that the "local" can be a true local (0..local_count-1)
// or an entry on the scopechain (local_count...(local_count+max_scope)-1)
// FIXME: this is exactly the same as makeatom() in jit-calls.h,
// except for the decoding of unaligned doubles
Atom nativeLocalToAtom(AvmCore* core, void* src, SlotStorageType sst)
{
switch (sst)
{
case SST_double:
return core->doubleToAtom(unpack_double(src));
#ifdef VMCFG_FLOAT
case SST_float:
return core->floatToAtom(unpack_float(src));
case SST_float4:
return core->float4ToAtom(unpack_float4(src));
#endif // VMCFG_FLOAT
default:
AvmAssert(false);
// fall through.
case SST_int32:
return core->intToAtom(*(const int32_t*)src);
case SST_uint32:
return core->uintToAtom(*(const uint32_t*)src);
case SST_bool32:
return *(const int32_t*)src ? trueAtom : falseAtom;
case SST_atom:
return *(const Atom*)src;
case SST_string:
return (*(const Stringp*)src)->atom();
case SST_namespace:
return (*(const Namespacep*)src)->atom();
case SST_scriptobject:
return (*(ScriptObject**)src)->atom();
}
}
// this looks deceptively similar to nativeLocalToAtom, but is subtly different:
// for locals, int/uint/bool are always stored as a 32-bit value in the 4 bytes
// of the memory slot (regardless of wordsize and endianness);
// for args, int/uint/bool are always expanded to full intptr size. In practice
// this only makes a difference on big-endian 64-bit systems (eg PPC64) which makes
// it a hard bug to notice.
static Atom nativeArgToAtom(AvmCore* core, void* src, BuiltinType bt)
{
switch (bt)
{
#ifdef VMCFG_FLOAT
case BUILTIN_float:
return core->floatToAtom(unpack_float(src));
case BUILTIN_float4:
return core->float4ToAtom(unpack_float4(src));
#endif // VMCFG_FLOAT
case BUILTIN_number:
{
return core->doubleToAtom(unpack_double(src));
}
case BUILTIN_int:
{
return core->intToAtom((int32_t)*(const intptr_t*)src);
}
case BUILTIN_uint:
{
return core->uintToAtom((uint32_t)*(const uintptr_t*)src);
}
case BUILTIN_boolean:
{
return *(const intptr_t*)src ? trueAtom : falseAtom;
}
case BUILTIN_any:
case BUILTIN_object:
case BUILTIN_void:
{
return *(const Atom*)src;
}
case BUILTIN_string:
{
return (*(const Stringp*)src)->atom();
}
case BUILTIN_namespace:
{
return (*(const Namespacep*)src)->atom();
}
default:
{
return (*(ScriptObject**)src)->atom();
}
}
}
#ifdef DEBUGGER
/*static*/ DebuggerMethodInfo* DebuggerMethodInfo::create(AvmCore* core, int32_t local_count, uint32_t codeSize, int32_t max_scopes)
{
MMgc::GC* gc = core->GetGC();
const uint32_t extra = (local_count <= 1) ? 0 : (sizeof(Stringp)*(local_count-1));
DebuggerMethodInfo* dmi = new (gc, MMgc::kExact, extra) DebuggerMethodInfo(local_count, codeSize, max_scopes);
const Stringp undef = core->kundefined;
for (int32_t i=0; i<local_count; i++)
{
WBRC(gc, dmi, &dmi->localNames[i], undef);
}
return dmi;
}
AbcFile* MethodInfo::file() const
{
DebuggerMethodInfo* dmi = this->dmi();
return dmi ? (AbcFile*)(dmi->file) : NULL;
}
int32_t MethodInfo::firstSourceLine() const
{
DebuggerMethodInfo* dmi = this->dmi();
return dmi ? dmi->firstSourceLine : 0;
}
int32_t MethodInfo::lastSourceLine() const
{
DebuggerMethodInfo* dmi = this->dmi();
return dmi ? dmi->lastSourceLine : 0;
}
int32_t MethodInfo::offsetInAbc() const
{
DebuggerMethodInfo* dmi = this->dmi();
return dmi ? dmi->offsetInAbc : 0;
}
uint32_t MethodInfo::codeSize() const
{
DebuggerMethodInfo* dmi = this->dmi();
return dmi ? dmi->codeSize : 0;
}
int32_t MethodInfo::local_count() const
{
DebuggerMethodInfo* dmi = this->dmi();
return dmi ? dmi->local_count : 0;
}
int32_t MethodInfo::max_scopes() const
{
DebuggerMethodInfo* dmi = this->dmi();
return dmi ? dmi->max_scopes : 0;
}
void MethodInfo::setFile(AbcFile* file)
{
DebuggerMethodInfo* dmi = this->dmi();
if (dmi)
dmi->file = file;
}
Stringp MethodInfo::getArgName(int32_t index)
{
return getRegName(index);
}
Stringp MethodInfo::getLocalName(int32_t index)
{
return getRegName(index+getMethodSignature()->param_count());
}
void MethodInfo::updateSourceLines(int32_t linenum, int32_t offset)
{
DebuggerMethodInfo* dmi = this->dmi();
if (dmi)
{
if (dmi->firstSourceLine == 0 || linenum < dmi->firstSourceLine)
dmi->firstSourceLine = linenum;
if (dmi->offsetInAbc == 0 || offset < dmi->offsetInAbc)
dmi->offsetInAbc = offset;
if (dmi->lastSourceLine == 0 || linenum > dmi->lastSourceLine)
dmi->lastSourceLine = linenum;
}
}
// Note: dmi() can legitimately return NULL (for MethodInfo's that were synthesized by Traits::genInitBody,
// or for MethodInfo's that have no body, e.g. interface methods).
DebuggerMethodInfo* MethodInfo::dmi() const
{
// rely on the fact that not-in-pool MethodInfo returns -1,
// which will always be > methodCount as uint32_t
const uint32_t method_id = uint32_t(this->method_id());
AvmAssert(_pool->core->debugger() != NULL);
// getDebuggerMethodInfo quietly returns NULL for out-of-range.
return _pool->getDebuggerMethodInfo(method_id);
}
Stringp MethodInfo::getRegName(int32_t slot) const
{
DebuggerMethodInfo* dmi = this->dmi();
if (dmi && slot >= 0 && slot < dmi->local_count)
return dmi->localNames[slot];
return this->pool()->core->kundefined;
}
void MethodInfo::setRegName(int32_t slot, Stringp name)
{
DebuggerMethodInfo* dmi = this->dmi();
if (!dmi || slot < 0 || slot >= dmi->local_count)
return;
AvmCore* core = this->pool()->core;
// [mmorearty 5/3/05] temporary workaround for bug 123237: if the register
// already has a name, don't assign a new one
if (dmi->localNames[slot] != core->kundefined)
return;
WBRC(core->GetGC(), dmi, &dmi->localNames[slot], core->internString(name));
}
// note that the "local" can be a true local (0..local_count-1)
// or an entry on the scopechain (local_count...(local_count+max_scope)-1)
Atom MethodInfo::boxOneLocal(FramePtr src, int32_t srcPos, const uint8_t* sstArr)
{
// if we are running jit then the types are native and we need to box them.
if (_isJitImpl)
{
src = FramePtr(uintptr_t(src) + (srcPos << VARSHIFT(this)));
AvmAssert(sstArr != NULL);
return nativeLocalToAtom(this->pool()->core, src, (SlotStorageType)sstArr[srcPos]);
}
else
{
AvmAssert(sstArr == NULL);
src = FramePtr(uintptr_t(src) + srcPos*sizeof(Atom));
return *(const Atom*)src;
}
}
/**
* convert ap[start]...ap[start+count-1] entries from their native types into
* Atoms. The result is placed into out[to]...out[to+count-1].
*
* The traitArr is used to determine the type of conversion that should take place.
* traitArr[start]...traitArr[start+count-1] are used.
*
* If the method is interpreted then we just copy the Atom, no conversion is needed.
*/
void MethodInfo::boxLocals(FramePtr src, int32_t srcPos, const uint8_t* sstArr, Atom* dest, int32_t destPos, int32_t length)
{
for (int32_t i = srcPos, n = srcPos+length; i < n; i++)
{
#ifdef VMCFG_AOT
AvmAssert(i >= 0 && (dmi() == NULL || i < local_count()));
#else
AvmAssert(i >= 0 && i < getMethodSignature()->local_count());
#endif
dest[destPos++] = boxOneLocal(src, i, sstArr);
}
}
// note that the "local" can be a true local (0..local_count-1)
// or an entry on the scopechain (local_count...(local_count+max_scope)-1)
void MethodInfo::unboxOneLocal(Atom src, FramePtr dst, int32_t dstPos, const uint8_t* sstArr)
{
if (_isJitImpl)
{
AvmAssert(sstArr != NULL);
dst = FramePtr(uintptr_t(dst) + (dstPos << VARSHIFT(this)));
switch ((SlotStorageType)sstArr[dstPos])
{
case SST_double:
{
*(double*)dst = AvmCore::number_d(src);
break;
}
#ifdef VMCFG_FLOAT
case SST_float:
{
*(float*)dst = AvmCore::atomToFloat(src);
break;
}
case SST_float4:
{
AvmAssert( (((uintptr_t)dst) & 0xf) == 0); // should be 16-byte aligned.
*(float4_t*)dst = AvmCore::atomToFloat4(src);
break;
}
#endif // VMCFG_FLOAT
case SST_int32:
{
*(int32_t*)dst = AvmCore::integer_i(src);
break;
}
case SST_uint32:
{
*(uint32_t*)dst = AvmCore::integer_u(src);
break;
}
case SST_bool32:
{
*(int32_t*)dst = (int32_t)atomGetBoolean(src);
break;
}
case SST_atom:
{
*(Atom*)dst = src;
break;
}
default: // SST_string, SST_namespace, SST_scriptobject
{
// ScriptObject, String, Namespace, or Null
*(void**)dst = atomPtr(src);
break;
}
}
}
else
{
AvmAssert(sstArr == NULL);
dst = FramePtr(uintptr_t(dst) + dstPos*sizeof(Atom));
*(Atom*)dst = src;
}
}
/**
* convert in[0]...in[count-1] entries from Atoms to native types placing them
* in ap[start]...out[start+count-1].
*
* The traitArr is used to determine the type of conversion that should take place.
* traitArr[start]...traitArr[start+count-1] are used.
*
* If the method is interpreted then we just copy the Atom, no conversion is needed.
*/
void MethodInfo::unboxLocals(const Atom* src, int32_t srcPos, const uint8_t* sstArr, FramePtr dest, int32_t destPos, int32_t length)
{
for (int32_t i = destPos, n = destPos+length; i < n; i++)
{
#ifdef VMCFG_AOT
AvmAssert(i >= 0 && (dmi() == NULL || i < local_count()));
#else
AvmAssert(i >= 0 && i < getMethodSignature()->local_count());
#endif
unboxOneLocal(src[srcPos++], dest, i, sstArr);
}
}
#endif //DEBUGGER
PrintWriter& MethodInfo::print(PrintWriter& prw) const
{
String* n = getMethodName();
return n ? prw << n << "()"
: prw << "?()";
}
bool MethodInfo::makeMethodOf(Traits* traits)
{
AvmAssert(!isResolved());
// begin AVMPLUS_UNCHECKED_HACK
AvmAssert(!_isProtoFunc);
// end AVMPLUS_UNCHECKED_HACK
_hasMakeMethodOfBeenCalled = 1;
if (_declarer.getTraits() == NULL)
{
_declarer.setTraits(pool()->core->GetGC(), this, traits);
_needClosure = 1;
return true;
}
else
{
#ifdef AVMPLUS_VERBOSE
if (pool()->isVerbose(VB_parse))
pool()->core->console << "WARNING: method " << this << " was already bound to " << declaringTraits() << "\n";
#endif
return false;
}
}
void MethodInfo::makeIntoPrototypeFunction(const Toplevel* toplevel, const ScopeTypeChain* fscope)
{
// Once a MethodInfo has been made a method of a particular class,
// it is an error to attempt to use it for OP_newfunction; we may have
// jitted code with the method and made early-binding assumptions about the
// receiver type that this would invalidate (by allowing us to call the method
// with a different type of object).
if (_hasMakeMethodOfBeenCalled)
toplevel->throwVerifyError(kCorruptABCError);
AvmAssert(_declarer.getScope() == NULL);
// begin AVMPLUS_UNCHECKED_HACK
this->_isProtoFunc = 1;
// end AVMPLUS_UNCHECKED_HACK
// make sure param & return types are fully resolved.
// this does not set the verified flag, so real verification will
// still happen before the function runs the first time.
resolveSignature(toplevel);
// Signature resolved successfully; now safe to set declaring scope.
// Set scope after resolveSignature; latter may fail to verify
// and such failures should be effect-free (as much as possible).
_declarer.setScope(toplevel->core()->GetGC(), this, fscope);
}
/**
* convert native args to atoms. argc is the number of
* args, not counting the instance which is arg[0]. the
* layout is [instance][arg1..argN]
*/
void MethodSignature::boxArgs(AvmCore* core, int32_t argc, const uint32_t* ap, Atom* out) const
{
MMGC_STATIC_ASSERT(sizeof(Atom) == sizeof(void*)); // if this ever changes, this function needs smartening
typedef const Atom* ConstAtomPtr;
// box the typed args, up to param_count
const int32_t param_count = this->param_count();
for (int32_t i=0; i <= argc; i++)
{
const BuiltinType bt = (i <= param_count) ? this->paramTraitsBT(i) : BUILTIN_any;
out[i] = nativeArgToAtom(core, (void*)ap, bt);
ap += (bt == BUILTIN_number ? sizeof(double) : sizeof(Atom)) / sizeof(int32_t);
}
}
MethodSignature* FASTCALL MethodInfo::_buildMethodSignature(const Toplevel* toplevel)
{
PoolObject* pool = this->pool();
AvmCore* core = pool->core;
GC* gc = core->GetGC();
const uint8_t* pos = this->_abc_info_pos;
const uint32_t param_count = pos ? AvmCore::readU32(pos) : 0;
uint32_t optional_count = 0;
uint32_t rest_offset = 0;
Traits* returnType;
Traits* receiverType;
// we only need param_count+optional_count extra, but we don't know
// optional_count yet, so over-allocate for the worst case. (we know optional_count<=param_count).
// this wastes space, but since we only cache a few dozen of these, it's preferable to walking the data twice
// to get an exact count.
//
// It is /critical/ to correct exact tracing of the MethodSignature that param_count is written
// into the object before any types are written into _args, and that optional_count is written
// into the object before any default values are written into _args.
const uint32_t extra = sizeof(MethodSignature::AtomOrType) * (param_count + (hasOptional() ? param_count : 0));
MethodSignature* ms = MethodSignature::create(gc, extra, param_count);
if (pos)
{
returnType = pool->resolveTypeName(pos, toplevel, /*allowVoid=*/true);
receiverType = _needClosure ? declaringTraits() : core->traits.object_itraits;
rest_offset = argSize(receiverType);
// begin AVMPLUS_UNCHECKED_HACK
uint32_t untyped_args = 0;
// end AVMPLUS_UNCHECKED_HACK
for (uint32_t i=1; i <= param_count; i++)
{
Traits* argType = pool->resolveTypeName(pos, toplevel);
WB(gc, ms, &ms->_args[i].paramType, argType);
rest_offset += argSize(argType);
// begin AVMPLUS_UNCHECKED_HACK
untyped_args += (argType == NULL);
// end AVMPLUS_UNCHECKED_HACK
}
AvmCore::skipU32(pos); // name_index;
pos++; // abcFlags;
// begin AVMPLUS_UNCHECKED_HACK
if (untyped_args == param_count)
_onlyUntypedParameters = 1;
// toplevel!=NULL check is so we only check when resolveSignature calls us (not subsequently)
if (toplevel != NULL && _isProtoFunc)
{
// HACK - compiler should do this, and only to toplevel functions
// that meet the E4 criteria for being an "unchecked function"
// the tests below are too loose
// if all params and return types are Object then make all params optional=undefined
if (param_count == 0)
_ignoreRest = 1;
if (!hasOptional() && returnType == NULL && param_count > 0 && untyped_args == param_count)
{
_hasOptional = 1;
_ignoreRest = 1;
_isUnchecked = 1;
// oops -- the ms we allocated is too small, has no space for optional values
// but it's easy to re-create, especially since we know the types are all null
const uint32_t extra = sizeof(MethodSignature::AtomOrType) * (param_count + param_count);
ms = MethodSignature::create(gc, extra, param_count);
// don't need to re-set paramTypes: they are inited to NULL which is the right value
}
}
if (_isUnchecked)
{
optional_count = param_count;
ms->_optional_count = optional_count;
for (uint32_t j=0; j < optional_count; j++)
{
//WBATOM(gc, ms, &ms->_args[param_count+1+j].defaultValue, undefinedAtom);
// since we know we're going from NULL->undefinedAtom we can skip the WBATOM call
ms->_args[param_count+1+j].defaultValue = undefinedAtom;
}
}
else
// end AVMPLUS_UNCHECKED_HACK
if (hasOptional())
{
optional_count = AvmCore::readU32(pos);
ms->_optional_count = optional_count;
for (uint32_t j=0; j < optional_count; j++)
{
const int32_t param = param_count-optional_count+1+j;
const int32_t index = AvmCore::readU32(pos);
CPoolKind kind = (CPoolKind)*pos++;
// check that the default value is legal for the param type
Traits* argType = ms->_args[param].paramType;
const Atom value = pool->getLegalDefaultValue(toplevel, index, kind, argType);
WBATOM(gc, ms, &ms->_args[param_count+1+j].defaultValue, value);
}
}
if (!isNative())
{
const uint8_t* body_pos = this->abc_body_pos();
if (body_pos)
{
ms->_max_stack = AvmCore::readU32(body_pos);
ms->_local_count = AvmCore::readU32(body_pos);
const int32_t init_scope_depth = AvmCore::readU32(body_pos);
ms->_max_scope = AvmCore::readU32(body_pos) - init_scope_depth;
#ifdef VMCFG_WORDCODE
#else
ms->_abc_code_start = body_pos;
AvmCore::skipU32(ms->_abc_code_start); // code_length
#endif
}
}
}
else
{
// this is a synthesized MethodInfo from genInitBody
AvmAssert(param_count == 0);
rest_offset = sizeof(Atom);
returnType = pool->core->traits.void_itraits;
receiverType = declaringTraits();
// values derived from Traits::genInitBody()
const int32_t max_stack = 2;
const int32_t local_count = 1;
const int32_t init_scope_depth = 1;
const int32_t max_scope_depth = 1;
ms->_max_stack = max_stack;
ms->_local_count = local_count;
ms->_max_scope = max_scope_depth - init_scope_depth;
#if defined(VMCFG_WORDCODE) || defined(VMCFG_AOT)
#else
ms->_abc_code_start = this->abc_body_pos();
AvmCore::skipU32(ms->_abc_code_start, 5);
#endif
}
ms->_frame_size = ms->_local_count + ms->_max_scope + ms->_max_stack;
#ifndef AVMPLUS_64BIT
// The interpreter wants this to be padded to a doubleword boundary because
// it allocates two objects in a single alloca() request - the frame and
// auxiliary storage, in that order - and wants the second object to be
// doubleword aligned.
ms->_frame_size = (ms->_frame_size + 1) & ~1;
#endif
// _param_count and _optional_count were written earlier for reasons of GC correctness
//ms->_param_count = param_count;
//ms->_optional_count = optional_count;
ms->_rest_offset = rest_offset;
ms->_isNative = this->_isNative;
ms->_allowExtraArgs = this->_needRest || this->_needArguments || this->_ignoreRest;
WB(gc, ms, &ms->_returnTraits, returnType);
WB(gc, ms, &ms->_args[0].paramType, receiverType);
return ms;
}
MethodSignature* FASTCALL MethodInfo::_getMethodSignature()
{
AvmAssert(this->isResolved());
// note: MethodSignaturep are always built the first time in resolveSignature; this is only
// executed for subsequent re-buildings. Thus we pass NULL for toplevel (it's only used
// for verification errors, but those will have been caught prior to this) and for
// abcGen and imtBuilder (since those only need to be done once).
MethodSignature* ms = _buildMethodSignature(NULL);
_installRefToResolvedMethodSignature(ms);
return ms;
}
void MethodInfo::update_max_stack(int32_t max_stack)
{
MethodSignature* ms = (MethodSignature*)_msref->get();
if (ms)
{
ms->_max_stack = max_stack;
}
}
uint32_t MethodInfo::parse_code_length() const
{
// Parse the code length out of ABC sice it is not stored elsewhere.
const uint8_t* pos = abc_body_pos();
AvmCore::skipU32(pos, 4);
return AvmCore::readU32(pos);
}
void MethodInfo::resolveSignature(const Toplevel* toplevel)
{
if (!isResolved())
{
MethodSignature* ms = _buildMethodSignature(toplevel);
if (!isNative())
{
if (ms->frame_size() < 0 || ms->local_count() < 0 || ms->max_scope() < 0)
toplevel->throwVerifyError(kCorruptABCError);
}
if (ms->paramTraits(0) != NULL && ms->paramTraits(0)->isInterface())
_hasMethodBody = 0;
_isResolved = 1;
_installRefToResolvedMethodSignature(ms);
pool()->core->exec->notifyMethodResolved(this, ms);
}
}
#ifdef DEBUGGER
uint64_t MethodInfo::bytesUsed()
{
uint64_t size = sizeof(MethodInfo);
size += getMethodSignature()->param_count() * 2 * sizeof(Atom);
size += codeSize();
return size;
}
#endif
bool MethodInfo::usesCallerContext() const
{
return pool()->isBuiltin && (!_isNative || _needsCodeContext);
}
// Builtin + non-native functions always need the dxns code emitted
// Builtin + native functions have flags to specify if they need the dxns code
bool MethodInfo::usesDefaultXmlNamespace() const
{
return pool()->isBuiltin && (!_isNative || _needsDxns);
}
bool MethodInfo::isTrivial()
{
if (_isTrivial)
return true;
if (_isNonTrivial)
return false;
bool trivial = computeIsTrivial();
if (trivial)
_isTrivial = 1;
else
_isNonTrivial = 1;
return trivial;
}
// Compute a conservative approximation to triviality. It is possible to
// do better. For example, IFTRUE and IFFALSE don't throw exceptions but
// termination is assured only if all branches are forward; also, RETURNVALUE
// is acceptable if data flow and type analyses shows that the returned value
// will not trigger a coercion exception. (That's true for many instructions
// generally.)
bool MethodInfo::computeIsTrivial()
{
#ifdef VMCFG_AOT
return false;
#else
// Initial attribute guard:
//
// _needRest and _needArguments are here because they invoke
// the Array constructor, ie, they may update system state.
//
// _isNative and _hasMethodBody are here because those methods have
// no bytecode, and this computation is bytecode-based.
//
// _setsDxns is here because the "default xml namespace" semantics
// are wild & crazy and some testing suggests that all bets are
// off when using it. (Belt and suspenders, belt and suspenders...)
//
// _needActivation is here mostly as a shortcut; it will usually pair
// with NEWACTIVATION and GETPROPERTY and so on, and so the method will
// be tossed out anyway.
if (_needActivation ||
_needRest ||
_needArguments ||
_isNative ||
_setsDxns ||
!_hasMethodBody)
return false;
const uint8_t* pc = _abc.body_pos;
AvmCore::skipU32(pc, 4);
uint32_t code_length = AvmCore::readU32(pc);
const uint8_t* code_end = pc + code_length;
while (pc < code_end) {
const uint8_t* nextpc = pc;
uint32_t imm30=0, imm30b=0;
int32_t imm8=0, imm24=0;
AbcOpcode opcode = (AbcOpcode) *pc;
AvmCore::readOperands(nextpc, imm30, imm24, imm30b, imm8);
// We may want to fold this logic into the table in ActionBlockConstants.cpp
// rather than having a dedicated switch.
switch (opcode) {
case OP_nop:
case OP_kill:
case OP_label:
case OP_popscope:
case OP_pushnull:
case OP_pushundefined:
case OP_pushbyte:
case OP_pushshort:
case OP_pushtrue:
case OP_pushfalse:
case OP_pushnan:
case OP_pop:
case OP_dup:
case OP_swap:
case OP_pushstring:
case OP_pushint:
case OP_pushuint:
case OP_pushdouble:
case OP_pushscope:
case OP_pushnamespace:
case OP_returnvoid:
case OP_getlocal:
case OP_getlocal0:
case OP_getlocal1:
case OP_getlocal2:
case OP_getlocal3:
case OP_setlocal:
case OP_setlocal0:
case OP_setlocal1:
case OP_setlocal2:
case OP_setlocal3:
case OP_getglobalscope:
case OP_getscopeobject:
case OP_getouterscope:
case OP_getglobalslot:
case OP_typeof:
case OP_not:
pc = nextpc;
break;
case OP_bkpt:
case OP_debug:
case OP_debugline:
case OP_debugfile:
case OP_bkptline:
case OP_timestamp:
#ifdef DEBUGGER
// Debugging instructions are nontrivial if the debugger
// is present, we don't want to confuse the programmer.
if (pool()->core->debugger() != NULL)
return false;
#endif
pc = nextpc;
break;
default:
return false;
}
}
return true;
#endif
}
bool MethodInfo::isConstructor() const
{
return declaringTraits()->init == this;
}
Stringp MethodInfo::getMethodName(bool includeAllNamespaces) const
{
Stringp methodName = NULL;
#ifdef AVMPLUS_SAMPLER
// We cache method names, because the profiler requests them over and
// over. (Bug 2547382)
methodName = _methodName;
#endif
if (!methodName)
{
Traits* declaringTraits = this->declaringTraits();
methodName = getMethodNameWithTraits(declaringTraits, includeAllNamespaces);
#ifdef AVMPLUS_SAMPLER
Sampler* sampler = declaringTraits ? declaringTraits->core->get_sampler() : NULL;
if (sampler && sampler->sampling())
_methodName = methodName;
#endif
}
return methodName;
}
Stringp MethodInfo::getMethodNameWithTraits(Traits* t, bool includeAllNamespaces) const
{
Stringp name = NULL;
const int32_t method_id = this->method_id();
PoolObject* pool = this->pool();
AvmCore* core = pool->core;
if (core->config.methodNames)
{
name = pool->getMethodInfoName(method_id);
if (name && name->length() == 0)
{
name = core->kanonymousFunc;
}
if (t)
{
StringBuffer sb(core);
t->print(sb, includeAllNamespaces);
Stringp tname = sb.toString();
if (core->config.oldVectorMethodNames)
{
// Tamarin used to incorrectly return the internal name of these
// Vector types rather than the "official" name due to initialization
// order. Names on the left are "more correct" but old builds might
// require the "classic" name for compatibility purposes, so check.
struct NameMapRec { const char* n; const char* o; };
static const NameMapRec kNameMap[4] =
{
{ "Vector.<Number>", "Vector$double" },
{ "Vector.<int>", "Vector$int" },
{ "Vector.<uint>", "Vector$uint" },
{ "Vector.<*>", "Vector$object" },
};
for (int32_t i = 0; i < 4; ++i)
{
if (tname->equalsLatin1(kNameMap[i].n))
{
tname = core->newConstantStringLatin1(kNameMap[i].o);
break;
}
}
};
if (this == t->init)
{
// careful, name could be null, that's ok for init methods
if (t->posType() == TRAITSTYPE_SCRIPT)
{
name = tname->appendLatin1("$init");
}
else if (t->posType() == TRAITSTYPE_CLASS)
{
name = tname->appendLatin1("cinit");
}
else
{
AvmAssert(t->isInstanceType() || t->isActivationTraits());
name = tname;
}
}
else if (name)
{
const char* sep;
if (_isGetter)
sep = "/get ";
else if (_isSetter)
sep = "/set ";
else
sep = "/";
name = tname->appendLatin1(sep)->append(name);
}
}
}
// if config.methodNames isn't set, might as well still return a non-null result
if (name == NULL)
name = core->concatStrings(core->newConstantStringLatin1("MethodInfo-"), core->intToString(method_id));
return name;
}
void MethodSignature::gcTraceHook_MethodSignature(MMgc::GC* gc)
{
int32_t numitems = 1+_param_count+_optional_count;
for ( int32_t i=0 ; i < numitems ; i++ ) {
if (atomKind(_args[i].defaultValue) == 0)
gc->TraceLocation(&_args[i].paramType);
}
}
/**
* MethodRecognizer and the associated helper functions
*/
#ifdef AVMPLUS_VERBOSE
// PrinterWriter interface
PrintWriter& MethodRecognizer::print(PrintWriter& prw) const { AvmAssert(0); return prw; }
PrintWriter& MethodNameRecognizer::print(PrintWriter& prw) const
{
prw << "methods with a name that exactly matches '";
prw.writeN(_pattern, _length);
prw << "'";
return prw;
}
PrintWriter& MethodNameRegExRecognizer::print(PrintWriter& prw) const
{
prw << "methods with names matching '";
prw.write(_pattern);
prw << "'";
return prw;
}
PrintWriter& MethodIdRecognizer::print(PrintWriter& prw) const
{
prw << "methods processed in the range ";
if (_low) prw << (uint64_t)_low;
prw << "-";
if (_high) prw << (uint64_t)_high;
return prw;
}
#endif
#if defined(VMCFG_COMPILEPOLICY) || defined(AVMPLUS_VERBOSE)
// Exact string match
MethodNameRecognizer::MethodNameRecognizer(const char* pattern, size_t length)
: _pattern(pattern)
, _length(length)
{}
bool MethodNameRecognizer::matches(const MethodInfo* m) const
{
String* nm = m->getMethodName();
bool match = nm->equalsLatin1(_pattern, (int32_t)_length);
return match;
}
// RegEx string match on the name
MethodNameRegExRecognizer::MethodNameRegExRecognizer(const char* pattern, size_t length)
{
char* str = new char[length + 1];
VMPI_strncpy(str, pattern, length);
str[length]='\0';
_pattern = str;
_invalid = false;
int options = PCRE_UTF8;
int error, errOffset;
const char* errStr;
_regex = (void*)pcre_compile2(_pattern, options, &error, &errStr, &errOffset, NULL);
// effective but not so pretty error handling.
if (_regex == NULL)
{
delete[] _pattern;
size_t errLen = (errStr == NULL) ? 0 : VMPI_strlen(errStr);
const char* msg = "*** REGULAR EXPRESSION PARSE ERROR *** : ";
const char* msgcont = " in : ";
str = new char[VMPI_strlen(msg) + errLen + VMPI_strlen(msgcont) + length + 1];
VMPI_strcpy(str, msg);
(errLen) ? VMPI_strcat(str, errStr) : 0;
VMPI_strcat(str, msgcont);
VMPI_strncat(str, pattern, length);
_pattern = str;
_invalid = true;
}
}
MethodNameRegExRecognizer::~MethodNameRegExRecognizer()
{
delete[] _pattern;
_pattern = NULL;
(pcre_free)((pcre*)_regex);
_regex = NULL;
}
bool MethodNameRegExRecognizer::matches(const MethodInfo* m) const
{
StUTF8String nm( m->getMethodName() );
int matches = (_invalid) ? -1 : pcre_exec((pcre*)_regex, NULL, nm.c_str(), nm.length(), 0, PCRE_NO_UTF8_CHECK, NULL, 0);
return (matches >= 0);
}
// Unique id compare
MethodIdRecognizer::MethodIdRecognizer(size_t low, size_t high)
: _low(low)
, _high(high)
{}
bool MethodIdRecognizer::matches(const MethodInfo* m) const
{
uint32_t currentId = m->unique_method_id();
bool match = false;
if (_low == 0)
match = currentId <= _high;
else if (_high == 0)
match = currentId >= _low;
else
match = currentId >= _low && currentId <= _high;
return match;
}
static const char* parseDigit(const char* c, size_t* val)
{
*val = VMPI_atoi(c);
while( VMPI_isdigit(*c) )
c++;
return c;
}
/**
* Read a method specification from the given string and return
* the appropriate MethodSpecifier. The original ptr to the string
* is updated to reflect the location where the parsing stopped.
*/
MethodRecognizer* MethodRecognizer::parse(const char** ptr, char separator)
{
MethodRecognizer* r = NULL;
const char* s = *ptr;
// check for numeric ranges of the form
// 'n', 'n-' , '-m' , 'n-m'
if (*s == '-')
{
size_t high = 0;
s = parseDigit(&s[1], &high);
r = mmfx_new( MethodIdRecognizer(0, high) );
}
else if (VMPI_isdigit(*s))
{
size_t low = 0;
size_t high = 0;
s = parseDigit(s, &low);
if (*s && *s == '-')
s = parseDigit(&s[1], &high);
else if (*s)
high = low; // single #
r = mmfx_new( MethodIdRecognizer(low, high) );
}
else if (*s == '%')
{
// regex case; read until '%'
const char* c = s+1;
while( *c && *c != '%')
c++;
r = mmfx_new( MethodNameRegExRecognizer(s+1,c-(s+1)) );
s = c;
}
else
{
// string case; read until ','
const char* c = s;
while( *c && *c != separator)
c++;
r = mmfx_new( MethodNameRecognizer(s,c-s) );
s = c;
}
*ptr = s;
return r;
}
#endif /* VMCFG_COMPILEPOLICY || AVMPLUS_VERBOSE */
}