/* -*- 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; ilocalNames[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.", "Vector$double" }, { "Vector.", "Vector$int" }, { "Vector.", "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 */ }