Files
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

609 lines
23 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/. */
#ifndef __avmplus_MethodInfo__
#define __avmplus_MethodInfo__
#ifdef AVMPLUS_VERBOSE
#define verbose_only(...) __VA_ARGS__
#else
#define verbose_only(...)
#endif
#ifdef verify
#undef verify
#endif
namespace avmplus
{
class ScopeOrTraits : public MMgc::GCInlineObject
{
private:
static const uintptr_t IS_SCOPE = 0x01;
private:
uintptr_t _scopeOrTraits; // if LSB clr, Traits* if LSB set, ScopeTypeChain*
public:
explicit ScopeOrTraits(Traits* t);
bool hasScope() const;
Traits* getTraits() const;
const ScopeTypeChain* getScope() const;
void setTraits(MMgc::GC* gc, void* container, Traits* t);
void setScope(MMgc::GC* gc, void* container, const ScopeTypeChain* s);
REALLY_INLINE void gcTrace(MMgc::GC* gc) { gc->TraceLocation(&_scopeOrTraits); }
};
#ifdef DEBUGGER
class AbcFile;
class GC_CPP_EXACT(DebuggerMethodInfo, MMgc::GCTraceableObject)
{
private:
explicit DebuggerMethodInfo(int32_t _local_count, uint32_t _codeSize, int32_t _max_scopes);
public:
static DebuggerMethodInfo* create(AvmCore* core, int32_t _local_count, uint32_t _codeSize, int32_t _max_scopes);
GC_DATA_BEGIN(DebuggerMethodInfo)
GCMember<AbcFile> GC_POINTER(file); // the abc file from which this method came
int32_t firstSourceLine; // source line number where function starts
int32_t lastSourceLine; // source line number where function ends
int32_t offsetInAbc; // offset in abc file
uint32_t codeSize; // abc size pre-jit, native size post jit
const int32_t local_count; // FIXME: merge with localCount above; this one may be visible to a debugger?
const int32_t max_scopes; // FIXME: merge with maxScopeDepth above; this one is not used by the VM but may be visible to a debugger?
Stringp GC_POINTERS_SMALL(localNames[1], local_count); // array of names for args and locals in framep order, written with explicit WBRC (actually local_count)
GC_DATA_END(DebuggerMethodInfo)
};
#endif
class MethodSignature;
class Deoptimizer;
/**
* MethodInfo is the base class for all functions that
* can be executed by the VM: Actionscript functions,
* native functions, etc.
*/
class GC_CPP_EXACT_WITH_HOOK(MethodInfo, MethodInfoProcHolder)
{
friend class CodegenLIR;
friend class BaseExecMgr;
friend class OSR;
public:
enum InitMethodStub { kInitMethodStub };
private:
// ctor for all normal methods.
MethodInfo(int32_t _method_id,
PoolObject* pool,
const uint8_t* abc_info_pos,
uint8_t abcFlags,
const NativeMethodInfo* native_info);
// special ctor for the methodinfo generated for slot-initialization when no init method is present
MethodInfo(InitMethodStub, Traits* declTraits);
#ifdef VMCFG_AOT
MethodInfo(InitMethodStub, Traits* declTraits, const NativeMethodInfo* native_info, const AvmThunkNativeHandler& handler, int32_t method_id);
#endif
public:
REALLY_INLINE static MethodInfo* create(MMgc::GC* gc,
int32_t _method_id,
PoolObject* pool,
const uint8_t* abc_info_pos,
uint8_t abcFlags,
const NativeMethodInfo* native_info)
{
return new (gc, MMgc::kExact) MethodInfo(_method_id, pool, abc_info_pos, abcFlags, native_info);
}
REALLY_INLINE static MethodInfo* create(MMgc::GC* gc, InitMethodStub stub, Traits* declTraits)
{
return new (gc, MMgc::kExact) MethodInfo(stub, declTraits);
}
#ifdef VMCFG_AOT
REALLY_INLINE static MethodInfo* create(MMgc::GC* gc, InitMethodStub stub, Traits* declTraits, const NativeMethodInfo* native_info, const AvmThunkNativeHandler& handler, int32_t method_id)
{
return new (gc, MMgc::kExact) MethodInfo(stub, declTraits, native_info, handler, method_id);
}
#endif
bool usesCallerContext() const;
// Builtin + non-native functions always need the dxns code emitted
// Builtin + native functions have flags to specify if they need the dxns code
bool usesDefaultXmlNamespace() const;
void resolveSignature(const Toplevel* toplevel);
#ifdef DEBUGGER
public:
Atom boxOneLocal(FramePtr src, int32_t srcPos, const uint8_t* sstArr);
void unboxOneLocal(Atom src, FramePtr dst, int32_t dstPos, const uint8_t* sstArr);
void boxLocals(FramePtr src, int32_t srcPos, const uint8_t* sstArr, Atom* dest, int32_t destPos, int32_t length);
void unboxLocals(const Atom* src, int32_t srcPos, const uint8_t* sstArr, FramePtr dest, int32_t destPos, int32_t length);
void setFile(AbcFile* file);
void setRegName(int32_t index, Stringp name);
Stringp getArgName(int32_t index);
Stringp getLocalName(int32_t index);
AbcFile* file() const;
int32_t firstSourceLine() const;
int32_t lastSourceLine() const;
int32_t offsetInAbc() const;
uint32_t codeSize() const;
int32_t local_count() const;
int32_t max_scopes() const;
void updateSourceLines(int32_t linenum, int32_t offset);
private:
Stringp getRegName(int32_t index) const;
DebuggerMethodInfo* dmi() const;
#endif
public:
void makeIntoPrototypeFunction(const Toplevel* toplevel, const ScopeTypeChain* fscope);
bool makeMethodOf(Traits* type);
bool isConstructor() const;
uint32_t hasExceptions() const;
uint32_t hasMethodBody() const;
uint32_t hasOptional() const;
uint32_t isNative() const;
uint32_t isNonInterruptible();
uint32_t isResolved() const;
uint32_t isFinal() const;
uint32_t needActivation() const;
uint32_t needArguments() const;
uint32_t needClosure() const;
uint32_t needRest() const;
uint32_t lazyRest() const; // Valid only during pass2 of verification or later
uint32_t needRestOrArguments() const;
uint32_t setsDxns() const;
uint32_t isStaticInit() const;
uint32_t hasFailedJit() const;
uint32_t isInterpreted() const;
uint32_t unboxThis() const;
uint32_t onlyUntypedParameters() const;
void setUnboxThis();
void setStaticInit();
void setHasFailedJit();
void setHasExceptions();
void setLazyRest();
void setNeedsDxns();
void setFinal();
void setOverride();
void makeNonInterruptible();
void setKind(TraitKind kind);
#ifdef VMCFG_AOT
uint32_t isAotCompiled() const;
void setAotCompiled(const AvmThunkNativeHandler& handler);
#endif
uint32_t osrEnabled() const;
void setOSR(uint32_t threshold);
public:
Stringp FASTCALL getMethodNameWithTraits(Traits* declaringTraits, bool includeAllNamespaces = false) const;
Stringp FASTCALL getMethodName(bool includeAllNamespaces = false) const;
PrintWriter& print(PrintWriter& prw) const;
#ifdef DEBUGGER
/**
* Basically the same as AvmPlusScriptableObject::bytesUsed().
*/
uint64_t bytesUsed();
#endif
public:
PoolObject* pool() const;
#ifdef VMCFG_AOT
AvmThunkNativeMethodHandler handler_method() const;
AvmThunkNativeFunctionHandler handler_function() const;
#endif
const uint8_t* abc_body_pos() const;
void set_abc_body_pos(const uint8_t* p);
uint32_t parse_code_length() const; // Parse code_len from ABC.
ExceptionHandlerTable* abc_exceptions() const;
void set_abc_exceptions(MMgc::GC* gc, ExceptionHandlerTable* e);
#ifdef VMCFG_WORDCODE
ExceptionHandlerTable* word_code_exceptions() const;
void set_word_code_exceptions(MMgc::GC* gc, ExceptionHandlerTable* e);
const uintptr_t* word_code_start() const;
void set_word_code(MMgc::GC* gc, TranslatedCode* translated_code);
#endif
int32_t method_id() const;
uint32_t unique_method_id() const;
Traits* declaringTraits() const;
const ScopeTypeChain* declaringScope() const;
Traits* activationTraits() const;
const ScopeTypeChain* activationScope() const;
void init_activationTraits(Traits* t);
void init_activationScope(const ScopeTypeChain*);
MethodSignaturep getMethodSignature();
void update_max_stack(int32_t max_stack);
#ifdef VMCFG_FLOAT
void forceLargeVarSize();
int32_t varShift(); // effectively, log2(varSize), i.e. 3 or 4. See exec.h for details.
#endif // VMCFG_FLOAT
/**
* Determine whether a function is trivial. A function is (conservatively) trivial
* if all these conditions hold:
*
* - it always terminates
* - it always terminates by returning, never by throwing exceptions
* (apart from system errors like stack overflow and out-of-memory)
* - it always returns 'undefined'
* - it does not update any non-local locations
*
* Stronger definitions of triviality are possible - for example, the condition on
* returning 'undefined' is a hack to avoid dealing with data flow analysis and
* determining whether RETURNVALUE will throw an exception - and some functions that
* are trivial by the above definition may still not be recognized by the implementation,
* obviously. The only condition is that if isTrivial returns true then the function
* is trivial by the above definition.
*/
bool isTrivial();
private:
MethodSignature* FASTCALL _getMethodSignature();
MethodSignature* FASTCALL _buildMethodSignature(const Toplevel* toplevel);
void _installRefToResolvedMethodSignature(MethodSignature* ms);
bool computeIsTrivial();
private:
struct NativeInfo
{
GprMethodProc thunker;
#if defined(VMCFG_TELEMETRY_SAMPLER) && !defined(VMCFG_AOT)
GprMethodProc samplerThunker;
#endif
#ifdef VMCFG_AOT
AvmThunkNativeHandler handler;
#endif
};
struct AbcInfo
{
const uint8_t* body_pos;
ExceptionHandlerTable* exceptions; // we write this once, in Verifier, with an explicit WB. so no GCMember<>.
uint32_t countdown; // Counts down calls plus backedges.
#ifdef VMCFG_WORDCODE
struct
{
TranslatedCode* translated_code; // The object that contains the code pointed to by body_pos, written with explicit WB
// We write this once, in WordcodeTranslator, with an explicit WB. so no GCMember<>.
// The contents are the same as the 'exceptions' structure above, except the 'from', 'to', and 'target' fields.
ExceptionHandlerTable* exceptions;
} word_code;
#endif
};
void gcTraceHook_MethodInfo(MMgc::GC* gc);
// ------------------------ DATA SECTION BEGIN
GC_DATA_BEGIN(MethodInfo)
private:
GCMember<MMgc::GCWeakRef> GC_POINTER(_msref); // our MethodSignature
ScopeOrTraits GC_STRUCTURE(_declarer);
ScopeOrTraits GC_STRUCTURE(_activation);
PoolObject* const GC_POINTER(_pool);
const uint8_t* const _abc_info_pos; // pointer to abc MethodInfo record
const int _method_id;
union
// This union is traced by the gcTraceHook()
{
NativeInfo _native; // stuff used only for Native methods (formerly in NativeMethod)
AbcInfo _abc; // stuff used only for bytecode methods (formerly in MethodInfo)
};
#ifdef AVMPLUS_SAMPLER
mutable GCMember<String> GC_POINTER(_methodName);
#endif
#ifdef VMCFG_HALFMOON
public:
// Per function metadata for deoptimization
// Should be allocated separtely from MethodInfo, because it need only
// exit for functions compiled with speculative code.
// TODO: we're only getting away w/o GC here because we never free these.
Deoptimizer* _deoptimizer;
Deoptimizer* _armed_deoptimizers;
#endif
private:
// -------- FLAGS SECTION
// (Set in ABC) need arguments[0..argc].
uint32_t _needArguments:1;
// (Set in ABC) need activation object
uint32_t _needActivation:1;
// (Set in ABC) need arguments[param_count+1..argc]
uint32_t _needRest:1;
// (Set in ABC) has optional parameters
uint32_t _hasOptional:1;
// (Set in ABC) allow extra args, but dont capture them
uint32_t _ignoreRest:1;
// (Set in ABC) method is native
uint32_t _isNative:1;
// (Set in ABC) method sets default namespace
uint32_t _setsDxns:1;
// (Set in ABC) method has table for parameter names
uint32_t _hasParamNames:1;
// Set iff this is a getter.
uint32_t _isGetter:1;
// Set iff this is a setter.
uint32_t _isSetter:1;
// Set iff this is an override of an inherited method.
uint32_t _isOverride:1;
uint32_t _isNonInterruptible:1;
uint32_t _unboxThis:1;
uint32_t _needsCodeContext:1;
uint32_t _needsDxns:1;
uint32_t _hasExceptions:1;
#ifdef VMCFG_VERIFYALL
uint32_t _isVerified:1;
uint32_t _isVerifyPending:1;
#endif
// indicates method is final, no overrides allowed
uint32_t _isFinal:1;
// indicates the function is a method, that pushes the
// receiver object onto the scope chain at method entry
uint32_t _needClosure:1;
// cleared to indicate that a function has no bytecode body
uint32_t _hasMethodBody:1;
// set once the signature types have been resolved and
// override signatures have been checked
uint32_t _isResolved:1;
// set to indictate that a function is a static initializer
uint32_t _isStaticInit:1;
// set to indicate that a function has been compiled
// to native code by the jit compiler
uint32_t _isJitImpl:1;
// set to indicate that an attempted jit compilation has failed
uint32_t _hasFailedJit:1;
// true if execution mechanism is the interpreter
uint32_t _isInterpImpl:1;
// see isTrivial() for definition of these two flags
uint32_t _isTrivial:1;
uint32_t _isNonTrivial:1;
// Set by setLazyRest when the verifier discovers that the method's rest array
// or arguments array can be allocated lazily (usually not at all). Only makes
// sense in combination with _needRest or _needArguments.
uint32_t _lazyRest:1;
// Set by _buildMethodSignature if all fixed or optional parameters
// to the function are untyped. (We use this to optimize the 'arguments'
// array, see RestArgAnalyzer.)
uint32_t _onlyUntypedParameters:1;
// begin AVMPLUS_UNCHECKED_HACK
// Note, this means "makeIntoPrototypeFunction has been called on me",
// *not* "I am a function on a prototype object".
uint32_t _isProtoFunc:1;
uint32_t _isUnchecked:1;
// end AVMPLUS_UNCHECKED_HACK
// Set if makeMethodOf() has ever been called for this MethodInfo.
uint32_t _hasMakeMethodOfBeenCalled:1;
#ifdef VMCFG_AOT
uint32_t _isAotCompiled:1;
#endif
// If true, Function.apply & call() use a fast path that only works
// for JIT and Native code, not interpreted code.
uint32_t _apply_fastpath:1;
// If true, execution policy permits OSR for this method.
// This does not guarantee that OSR will apply, however. See OSR::isSupported() for details.
uint32_t _osr_enabled:1;
#ifdef VMCFG_FLOAT
// if true, the function operates on float4_t values, and needs a double VARSIZE
uint32_t _has128bitLocals:1;
#endif
GC_DATA_END(MethodInfo)
// ------------------------ DATA SECTION END
};
class GC_CPP_EXACT_WITH_HOOK(MethodSignature, QCachedItem)
{
friend class MethodInfo;
public:
REALLY_INLINE static MethodSignature* create(MMgc::GC* gc, size_t extra, int32_t param_count)
{
return new (gc, MMgc::kExact, extra) MethodSignature(param_count);
}
private:
MethodSignature(int32_t param_count);
union AtomOrType
{
Traits* paramType;
Atom defaultValue;
};
public:
Traits* returnTraits() const;
BuiltinType returnTraitsBT() const;
int32_t param_count() const;
int32_t optional_count() const;
int32_t rest_offset() const;
int32_t max_stack() const;
int32_t local_count() const;
int32_t max_scope() const;
int32_t frame_size() const;
int32_t stack_base() const;
int32_t scope_base() const;
#ifdef VMCFG_WORDCODE
#else
const uint8_t* abc_code_start() const;
#endif
int32_t requiredParamCount() const;
Traits* paramTraits(int32_t i) const;
BuiltinType paramTraitsBT(int32_t i) const;
Atom getDefaultValue(int32_t i) const;
bool allowExtraArgs() const;
bool argcOk(int32_t argc) const;
void boxArgs(AvmCore* core, int32_t argc, const uint32_t* ap, Atom* out) const;
private:
void gcTraceHook_MethodSignature(MMgc::GC* gc);
// ------------------------ DATA SECTION BEGIN
GC_DATA_BEGIN(MethodSignature)
private:
Traits* GC_POINTER(_returnTraits); // written with explicit WB
#ifdef VMCFG_WORDCODE
#else
const uint8_t* _abc_code_start; // start of ABC body
#endif
int _param_count; // number of declared parameters including optionals
int _optional_count; // last optional_count params are optional
int _rest_offset; // offset to first rest arg, including the instance parameter. this is sum(sizeof(paramType(0..N)))
int _max_stack; // abc-only: max stack
int _local_count; // abc-only: maximum number of local registers
int _max_scope; // abc-only: maximum depth of local scope stack
int _frame_size; // abc-only: total size of frame in number of Atoms, derived from other values above
// Note that these two flags are "bool" because we will have to pad the struct anyway;
// might as well avoid per-bit access as long as we have the space.
bool _isNative; // dupe of owner's flag of same name.
bool _allowExtraArgs; // == _needRest | _needArguments | _ignoreRest
AtomOrType _args[1]; // lying, actually 1+param_count+optional_count. Traced by gcTraceHook().
GC_DATA_END(MethodSignature)
// ------------------------ DATA SECTION END
};
/**
* MethodRecognizer is an interface used to determine if a MethodInfo matches
* some criteria (e.g. a name).
*
* It also doubles as factory to produce recognizers based on a string input.
*/
class MethodRecognizer
{
public:
static MethodRecognizer* parse(const char** s, char separator);
virtual bool matches(const MethodInfo* m) const = 0;
virtual ~MethodRecognizer() {}
verbose_only( virtual PrintWriter& print(PrintWriter& prw) const = 0; )
};
/**
* A MethodRecognizer that performs an exact match comparision against a method name
*/
class MethodNameRecognizer : /* implements */ public MethodRecognizer
{
public:
MethodNameRecognizer(const char* pattern, size_t length);
virtual bool matches(const MethodInfo* m) const;
verbose_only( virtual PrintWriter& print(PrintWriter& prw) const; )
private:
const char* _pattern;
size_t _length;
};
/**
* A MethodRecognizer that performs an Perl Regular Expression comparision against a method name
*/
class MethodNameRegExRecognizer : /* implements */ public MethodRecognizer
{
public:
MethodNameRegExRecognizer(const char* pattern, size_t length);
virtual ~MethodNameRegExRecognizer();
virtual bool matches(const MethodInfo* m) const;
verbose_only( virtual PrintWriter& print(PrintWriter& prw) const; )
private:
const char* _pattern;
void* _regex;
bool _invalid; // bad expression?
};
/**
* A MethodRecognizer that performs compares a method's unique id against a range of id's.
*/
class MethodIdRecognizer : /* implements */ public MethodRecognizer
{
public:
MethodIdRecognizer(size_t low, size_t high);
virtual bool matches(const MethodInfo* m) const;
verbose_only( virtual PrintWriter& print(PrintWriter& prw) const; )
private:
size_t _low, _high;
};
/** Utility function for accessing possibly-unaligned doubles */
double unpack_double(const void* src);
/**
* Utility function to access values on the stack, then box them as
* Atoms, including handling of possibly unaligned double and float/float4
* values. Similar to makeAtom() in jit-calls.h (keep them sync'd).
*/
Atom nativeLocalToAtom(AvmCore* core, void* src, SlotStorageType sst);
}
#undef verbose_only
#endif /* __avmplus_MethodInfo__ */