/* [<][>][^][v][top][bottom][index][help] */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is [Open Source Virtual Machine.].
*
* The Initial Developer of the Original Code is
* Adobe System Incorporated.
* Portions created by the Initial Developer are Copyright (C) 2004-2006
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Adobe AS3 Team
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef __avmplus_AvmCore__
#define __avmplus_AvmCore__
namespace avmplus
{
#define OBJECT_TYPE (core->traits.object_itraits)
#define CLASS_TYPE (core->traits.class_itraits)
#define FUNCTION_TYPE (core->traits.function_itraits)
#define ARRAY_TYPE (core->traits.array_itraits)
#define STRING_TYPE (core->traits.string_itraits)
#define NUMBER_TYPE (core->traits.number_itraits)
#define INT_TYPE (core->traits.int_itraits)
#define UINT_TYPE (core->traits.uint_itraits)
#define BOOLEAN_TYPE (core->traits.boolean_itraits)
#define VOID_TYPE (core->traits.void_itraits)
#define NULL_TYPE (core->traits.null_itraits)
#define NAMESPACE_TYPE (core->traits.namespace_itraits)
#define VECTORINT_TYPE (core->traits.vectorint_itraits)
#define VECTORUINT_TYPE (core->traits.vectoruint_itraits)
#define VECTORDOUBLE_TYPE (core->traits.vectordouble_itraits)
#define VECTOROBJ_TYPE (core->traits.vectorobj_itraits)
const int kBufferPadding = 16;
enum Runmode { RM_mixed, RM_jit_all, RM_interp_all };
enum VB_Bits {
// Output control bits for verbose mode
VB_builtins = 1<<0, // display output for builtins (default is to ignore any builtins)
VB_parse = 1<<1, // abc parsing information
VB_verify = 1<<2, // verification information
VB_interp = 1<<3, // interpreter information
VB_jit = 1<<4, // jit information
VB_traits = 1<<5, // traits creation information
};
struct Config
{
/**
* The verbose flag may be set to display each bytecode
* instruction as it is executed, along with a snapshot of
* the state of the stack and scope chain.
* @see VB_Bits for individual settings of these flags
*/
uint32_t verbose_vb;
// if true, record original names of methods at runtime.
// if false, don't (Function.toString will return things like "Function-21")
bool methodNames;
// give "Vector.<*>" instead of "Vector$object", etc
bool oldVectorMethodNames;
enum Runmode runmode;
/**
* To speed up initialization, we don't use jit on
* $init methods; we use interp instead. For testing
* purposes, one may want to force the jit to be used
* for all code including $init methods. The
* jit switch forces all code to run through the jit
* instead of interpreter.
*/
bool cseopt;
bool sse2;
bool fixed_esp;
bool use_cmov;
/**
* If this switch is set, executing code will check the
* "interrupted" flag to see whether an interrupt needs
* to be handled.
*/
bool interrupts;
bool verifyall;
bool show_stats;
bool tree_opt;
bool jitordie; // Always JIT, and if the JIT fails then abort
};
class MethodFrame;
/**
* The main class of the AVM+ virtual machine. This is the
* main entry point to the VM for running ActionScript code.
*/
class AvmCore : public MMgc::GCRoot
{
friend class MethodFrame;
friend class CodegenLIR;
friend class EnterCodeContext;
friend class EnterMethodEnv;
friend class ExceptionFrame;
public:
/**
* Default values for the config parameters. These need to be visible, because
* the shell's command line parsing needs to know what they are. Presumably
* other host environments might find them useful too.
*
* These are not conditionally included because the resulting code is a mess
* at no benefit.
*/
static const uint32_t verbose_default;
static const bool methodNames_default;
static const bool oldVectorMethodNames_default;
static const bool verifyall_default;
static const bool show_stats_default;
static const bool tree_opt_default;
static const Runmode runmode_default;
static const bool cseopt_default;
static const bool sse2_default;
static const bool fixed_esp_default;
static const bool interrupts_default;
static const bool jitordie_default;
#ifdef AVMPLUS_VERBOSE
// default set of flags to enable for "verbose" with no specific qualifiers
static const uint32_t DEFAULT_VERBOSE_ON;
#endif
public:
/**
* The console object. Text to be displayed to the developer
* or end-user can be directed to console, much like the cout
* object in C++ iostreams.
*
* The console object is a wrapper around the console output
* stream specified by the setConsoleStream method.
* Programs embedding AVM+ will typically implement
* the OutputStream interface and pass it in via
* setConsoleStream.
*/
PrintWriter console;
#ifdef VTUNE
iJIT_IsProfilingActiveFlags VTuneStatus;
iJIT_IsProfilingActiveFlags CheckVTuneStatus();
#endif // VTUNE
/**
* The GC used by this AVM instance
*/
MMgc::GC * const gc;
private:
#ifdef _DEBUG
// Only the thread used to create the AvmCore is allowed to modify currentMethodFrame (and thus, use EnterCodeContext).
// We don't enforce this in Release builds, but check for it and assert in Debug builds.
vmpi_thread_t codeContextThread;
#endif
#ifdef DEBUGGER
private:
Debugger* _debugger;
Profiler* _profiler;
Sampler* _sampler;
public:
Debugger* debugger() const;
Profiler* profiler() const;
Sampler* get_sampler() const;
void sampleCheck();
protected:
virtual Debugger* createDebugger(int tracelevel);
virtual Profiler* createProfiler();
virtual Sampler* createSampler();
public:
int langID;
bool passAllExceptionsToDebugger;
#endif
#ifdef AVMPLUS_VERIFYALL
public:
List<MethodInfo*, LIST_GCObjects> verifyQueue;
void enqFunction(MethodInfo* f);
void enqTraits(Traits* t);
void verifyEarly(Toplevel* toplevel, AbcEnv* abc_env);
#endif
private:
class LivePoolNode : public MMgc::GCRoot
{
public:
LivePoolNode* next;
MMgc::GCWeakRef* pool;
LivePoolNode(MMgc::GC* gc) : GCRoot(gc) {}
};
// note, allocated using mmfx_new, *not* gc memory
LivePoolNode* livePools;
public:
void addLivePool(PoolObject* pool);
public:
/**
* inlineable interrupt check; used by interpreter in situations
* where we must do more work before calling handleInterrupt, like
* in the body of OP_absjump.
*/
bool interruptCheck(bool interruptable);
/**
* on a backwards branch, check if the interrupt flag is enabled.
* used by interpreter only. A copy of this is inline-generated
* by CodegenLIR at loop headers.
*/
void branchCheck(MethodEnv *env, bool interruptable, int go);
private:
QCache* m_tbCache;
QCache* m_tmCache;
QCache* m_msCache;
public:
QCache* tbCache();
QCache* tmCache();
QCache* msCache();
struct CacheSizes
{
enum { DEFAULT_BINDINGS = 32, DEFAULT_METADATA = 1, DEFAULT_METHODS = 32 };
uint16_t bindings;
uint16_t metadata;
uint16_t methods;
inline CacheSizes() : bindings(DEFAULT_BINDINGS), metadata(DEFAULT_METADATA), methods(DEFAULT_METHODS) {}
};
// safe to call at any time, but calling tosses existing caches, thus has a perf hit --
// don't call cavalierly
void setCacheSizes(const CacheSizes& cs);
public:
/**
* Redirects the standard output of the VM to the specified
* output stream. Output from print() statements and
* error messages will be sent here.
* @param stream output stream to use for console output
*/
void setConsoleStream(OutputStream *stream);
/**
* GCCallback functions
*/
virtual void presweep();
virtual void postsweep();
virtual void oom(MMgc::MemoryStatus status);
Config config;
#ifdef FEATURE_NANOJIT // accessors
bool quiet_opt() const;
#if defined AVMPLUS_IA32 || defined AVMPLUS_AMD64
bool use_sse2() const;
#endif
#endif
#ifdef AVMPLUS_VERBOSE
bool isVerbose(uint32_t b) const;
static bool isBitSet(uint32_t v, uint32_t bit);
static uint32_t parseVerboseFlags(const char* arg);
#endif
void SetJITEnabled(bool isEnabled);
bool IsJITEnabled() const;
bool JITMustSucceed() const;
enum InterruptReason {
// normal state. must be 0 to allow efficient code for interrupt checks
NotInterrupted = 0,
// script is running too long
ScriptTimeout = 1,
// host-defined external interrupt, other than a script timeout.
ExternalInterrupt = 2
};
/**
* Check for stack overflow and call handler if it happens.
* This is the canonical stack overflow check called from Interpreter.
* An inlined version of this code is also generated by CodegenLIR::prologue().
*/
void stackCheck(MethodEnv* env);
/**
* Like the previous method but for a Toplevel* argument.
*/
void stackCheck(Toplevel* env);
/** set the stack limit that will be checked by executing AS3 code. */
void setStackLimit(uintptr_t p);
/** called by executing code when stack overflow is detected (sp < minstack) */
static void FASTCALL handleStackOverflowMethodEnv(MethodEnv* env);
/** called by executing code when stack overflow is detected (sp < minstack) */
static void FASTCALL handleStackOverflowToplevel(Toplevel* env);
/**
* Called by certain functions in PCRE to check for overflow. The state is
* kept in thread-local storage and is set up by call-ins to PCRE.
*/
static void FASTCALL checkPCREStackOverflow();
/**
* Set the state for call-ins to PCRE.
*/
static void setPCREContext(Toplevel* env);
private:
/**
* Stack limit set by host and checked by executing AS3 code.
* If the stack pointer goes below this value, handleStackOverflow is invoked,
* which in turn calls the host-provided virtual stackOverflow() handler.
*/
uintptr_t minstack;
/**
* the host-provided stack limit. we never change this, but
* it's used to save/restore minstack when minstack is moved
* to trigger interrupt handling.
*/
uintptr_t stack_limit;
/**
* If this field is not NotInterrupted, the host has requested that the currently
* executing AS3 code stop and invoke handleInterrupt. The field
* is checked directly by executing code.
*
* Set to ScriptTimeout for a timeout interrupt,
* ExternalInterrupt for an external (i.e., signal handler) interrupt.
*/
InterruptReason interrupted;
/**
* points to the topmost AS3 frame that's executing and provides
* the full AS3 callstack. Every AS3 method prolog/epilog updates
* this pointer. Exception catch handlers update this as well.
*/
MethodFrame* currentMethodFrame;
public:
/**
* This method will be invoked when the first exception
* frame is set up. This will be a good point to set
* minstack by calling setStackLimit().
*/
virtual void setStackBase();
/** Internal table of strings for boolean type ("true", "false") */
DRC(Stringp) booleanStrings[2];
/** Container object for traits of built-in classes */
BuiltinTraits traits;
/** PoolObject for built-in classes */
PoolObject* builtinPool;
/** Domain for built-in classes */
Domain* builtinDomain;
private:
/**
* The default namespace, "public"
*/
DRC(Namespacep) publicNamespace;
public:
/**
* The unnamed public namespaces
*/
NamespaceSet* publicNamespaces;
#ifdef AVMPLUS_WITH_JNI
Java* java; /* java vm control */
#endif
/**
* Execute an ABC file that has been parsed into a
* PoolObject.
* @param pool PoolObject containing the ABC file to
* execute
* @param domainEnv The DomainEnv object to execute
* against, or NULL if a new DomainEnv
* should be created
* @param toplevel the Toplevel object to execute against,
* or NULL if a Toplevel should be
* created.
* @param codeContext FIXME
* @throws Exception If an error occurs, an Exception object will
* be thrown using the AVM+ exceptions mechanism.
* Calls to handleActionBlock should be bracketed
* in TRY/CATCH.
*/
Atom handleActionPool(PoolObject* pool,
DomainEnv* domainEnv,
Toplevel* &toplevel,
CodeContext *codeContext);
ScriptEnv* prepareActionPool(PoolObject* pool,
DomainEnv* domainEnv,
Toplevel*& toplevel,
CodeContext *codeContext);
void exportDefs(Traits* traits, ScriptEnv* scriptEnv);
/**
* Parse the ABC block starting at offset start in code.
* @param code buffer holding the ABC block to parse
* @param start zero-indexed offset, in bytes, into the
* buffer where the code begins
* @param toplevel the Toplevel object to execute against,
* or NULL if a Toplevel should be
* created.
* @param domain FIXME
* @param ninit FIXME
* @param api The api version of the code being parsed. It must
* coorespond to one of the versions in api-versions.h
* @throws Exception If an error occurs, an Exception object will
* be thrown using the AVM+ exceptions mechanism.
* Calls to handleActionBlock should be bracketed
* in TRY/CATCH.
*/
PoolObject* parseActionBlock(ScriptBuffer code,
int start,
Toplevel* toplevel,
Domain* domain,
const NativeInitializer* ninit,
uint32_t api);
/**
* Execute the ABC block starting at offset start in code.
* @param code buffer holding the ABC block to execute
* @param start zero-indexed offset, in bytes, into the
* buffer where the code begins
* @param domainEnv FIXME
* @param toplevel the Toplevel object to execute against,
* or NULL if a Toplevel should be
* created.
* @param ninit FIXME
* @param codeContext FIXME
* @param api The api version of the code being parsed. It must
* coorespond to one of the versions in api-versions.h
* @throws Exception If an error occurs, an Exception object will
* be thrown using the AVM+ exceptions mechanism.
* Calls to handleActionBlock should be bracketed
* in TRY/CATCH.
*/
Atom handleActionBlock(ScriptBuffer code,
int start,
DomainEnv* domainEnv,
Toplevel* &toplevel,
const NativeInitializer* ninit,
CodeContext *codeContext,
uint32_t api);
#ifdef VMCFG_EVAL
/**
* Compile the source code held in 'code' and then execute it
* as for handleActionBlock() above.
*
* @param code The code to be compiled and executed. The string must be
* NUL-terminated and the NUL is not considered part of the
* input. If 'code' is not in UTF-16 format it will be converted
* to UTF-16 format, so it is highly advisable that the caller
* has created an UTF-16 string.
* @param filename The name of the file originating the code, or
* NULL if the source code does not originate from a file.
* If not NULL then ActionScript's 'include' directive will
* be allowed in the program and files will be loaded
* relative to 'filename'.
* @param domainEnv FIXME
* @param toplevel the Toplevel object to execute against,
* or NULL if a Toplevel should be
* created.
* @param ninit FIXME
* @param codeContext FIXME
* @param api The api version of the code being parsed. It must
* coorespond to one of the versions in api-versions.h
* @throws Exception If an error occurs, an Exception object will
* be thrown using the AVM+ exceptions mechanism.
* Calls to handleActionBlock should be bracketed
* in TRY/CATCH.
*/
Atom handleActionSource(String* code,
String* filename,
DomainEnv* domainEnv,
Toplevel* &toplevel,
const NativeInitializer* ninit,
CodeContext *codeContext,
uint32_t api);
/**
* Obtain input from a file to handle ActionScript's 'include' directive.
*
* This method invoked by the run-time compiler if the script uses 'include'
* and the use of 'include' is allowed because the script originated from
* a file; see 'handleActionSource()' above.
*
* 'referencingFilename' should be taken into
* account by this method if 'filename' is not an absolute file name.
*
* @param referencingFilename The name of the file from which the script
* containing the 'include' directive was loaded
* @param filename The filename in the 'include' directive.
* @return A string representing the contents of the file named by 'filename'.
* The string must NUL-terminated and the NUL is not considered part
* of the input. If the returned string is not in UTF-16 format then
* it will be converted to UTF-16 format, so it is highly advisable
* that the method has created an UTF-16 string. If the file cannot
* be opened or read then the return value should be NULL, an
* exception should not be thrown.
*/
virtual String* readFileForEval(String* referencingFilename, String* filename) = 0;
#endif // VMCFG_EVAL
/** Implementation of OP_equals */
Atom equals(Atom lhs, Atom rhs);
/**
* this is the abstract relational comparison algorithm according to ECMA 262 11.8.5
* @param lhs
* @param rhs
* @return trueAtom, falseAtom, or undefinedAtom
*/
Atom compare(Atom lhs, Atom rhs);
/** Implementation of OP_strictequals */
Atom stricteq(Atom lhs, Atom rhs);
/**
* Helper method; returns true if the atom is a tagged ScriptObject
* pointer. The actual type of the object is indicated by
* ScriptObject->vtable->traits.
*/
static bool isObject(Atom atom);
static bool isPointer(Atom atom);
static bool isNamespace(Atom atom);
static BindingKind bindingKind(Binding b);
static bool isMethodBinding(Binding b);
static bool isAccessorBinding(Binding b);
static bool hasSetterBinding(Binding b);
static bool hasGetterBinding(Binding b);
static int bindingToGetterId(Binding b);
static int bindingToSetterId(Binding b);
static int bindingToMethodId(Binding b);
static int bindingToSlotId(Binding b);
/** true if b is a var or a const */
static int isSlotBinding(Binding b);
static Binding makeSlotBinding(uintptr_t id, BindingKind kind);
static Binding makeMGSBinding(uintptr_t id, BindingKind kind);
static Binding makeGetSetBinding(Binding b);
/** true only if b is a var */
static int isVarBinding(Binding b);
/** true only if b is a const */
static int isConstBinding(Binding b);
/** Helper method; returns true if atom is an Function */
bool isFunction(Atom atom);
/** Helper method; returns true if atom's type is double */
static bool isDouble(Atom atom);
// removed, because it was being (erroneously) used to ask
// "will you fit in int32?", which was never right for 64-bit.
// instead, use atomIsIntptr(), which asks "will you fit in intptr?"
// static bool isInteger(Atom atom);
/** Helper method; returns true if atom's type is Number */
static bool isNumber(Atom atom);
/** Helper method; returns true if atom's type is boolean */
static bool isBoolean(Atom atom);
/** Helper method; returns true if atom's type is null */
static bool isNull(Atom atom);
/** Helper method; returns true if atom's type is undefined */
static bool isUndefined(Atom atom);
static bool isNullOrUndefined(Atom atom);
#ifdef AVMPLUS_VERBOSE
/** Disassembles an opcode and places the text in str. */
void formatOpcode(PrintWriter& out, const byte *pc, AbcOpcode opcode, ptrdiff_t off, PoolObject* pool);
# ifdef AVMPLUS_WORD_CODE
void formatOpcode(PrintWriter& out, const uintptr_t *pc, WordOpcode opcode, ptrdiff_t off, PoolObject* pool);
void formatBits(PrintWriter& buffer, uint32 bits);
# endif
static void formatMultiname(PrintWriter& out, uint32 index, PoolObject* pool);
#endif
/**
* @name interned constants
* Constants used frequently in the VM; these are typically
* identifiers that are part of the core language semantics
* like "prototype" and "constructor". These are interned
* up front and held in AvmCore for easy reference.
*/
/*@{*/
DRC(Stringp) kconstructor;
DRC(Stringp) kEmptyString;
DRC(Stringp) ktrue;
DRC(Stringp) kfalse;
DRC(Stringp) kundefined;
DRC(Stringp) knull;
DRC(Stringp) ktoString;
DRC(Stringp) ktoLocaleString;
DRC(Stringp) kvalueOf;
DRC(Stringp) klength;
DRC(Stringp) kobject;
DRC(Stringp) kfunction;
DRC(Stringp) kxml;
DRC(Stringp) kboolean;
DRC(Stringp) knumber;
DRC(Stringp) kstring;
DRC(Stringp) kuri;
DRC(Stringp) kprefix;
DRC(Stringp) kglobal;
DRC(Stringp) kcallee;
DRC(Stringp) kNeedsDxns;
DRC(Stringp) kAsterisk;
DRC(Stringp) kVersion;
#if VMCFG_METHOD_NAMES
DRC(Stringp) kanonymousFunc;
#endif
Atom kNaN;
DRC(Stringp) cachedChars[128];
/*@}*/
/** Constructor */
AvmCore(MMgc::GC *gc);
/** Destructor */
~AvmCore();
/**
* Parses builtin.abc into a PoolObject, to be executed
* later for each new Toplevel
*/
#ifdef DEBUGGER
void initBuiltinPool(int tracelevel);
#else
void initBuiltinPool();
#endif
/**
* Initializes the specified Toplevel object by running
* builtin.abc
*/
Toplevel* initTopLevel();
virtual Toplevel* createToplevel(AbcEnv* abcEnv);
/**
* Support for API versioning
*/
/**
* Set the AVM wide version information on startup.
*
* @param apis_start First first API version number
* @param apis_sizes Array of sizes of arrays of compatible APIs
* @param apis_count Count of API versions
* @param apis Array of arrays of compatible APIs
* @param uris_count Count of URIs
* @param uris Array of versioned URIs
*/
void setAPIInfo(uint32_t apis_start,
uint32_t apis_count,
uint32_t uris_count, const char** uris,
const int32_t* api_compat);
bool isVersionedURI(Stringp uri);
/**
* Get the AVM wide default API version.
*/
virtual int32_t getDefaultAPI() = 0;
/**
* Get the current API version. Uses the given PoolObject, or otherwise
* walks the scope chain for the first non-builtin method info and uses
* it's PoolObject.
*
* @param pool The caller's pool object.
*/
int32_t getAPI(PoolObject* pool);
/**
* Find the current public by walking the call stack
*/
Namespacep findPublicNamespace();
/**
* Get the public namespace associated with the given pool's version.
*
* @param pool The caller's pool object.
*/
Namespacep getPublicNamespace(PoolObject* pool);
/**
* Get any public namespace
*/
Namespacep getAnyPublicNamespace();
/**
* Get the public namespace associated with the given pool's version.
*
* @param version The version of public being requested.
*/
Namespacep getPublicNamespace(int32_t api);
/**
* Set the active api bit for the given api
*/
void setActiveAPI(int32_t api);
/**
* Get the bits for the currently active apis
*/
int32_t getActiveAPIs();
friend class ApiUtils;
/**
* toUInt32 is the ToUInt32 algorithm from
* ECMA-262 section 9.6, used in many of the
* native core objects
*/
static uint32_t toUInt32(Atom atom);
/**
* toInteger is the ToInteger algorithm from
* ECMA-262 section 9.4, used in many of the
* native core objects
*/
static double toInteger(Atom atom);
/**
* Converts the passed atom to a 32-bit signed integer.
* If the atom is already an integer, it is simply
* decoded. Otherwise, it is coerced to the int type
* and returned. This is ToInt32() from E3 section 9.5
*/
#ifdef AVMPLUS_64BIT
static int64_t integer64(Atom atom);
static int64_t integer64_i(Atom atom);
static int64_t integer64_d(double d);
#ifdef AVMPLUS_AMD64
static int64_t integer64_d_sse2(double d);
#endif
#endif
static int32_t integer(Atom atom);
// convert atom to integer when we know it is already a legal signed-32 bit int value
static int32_t integer_i(Atom a);
// convert atom to integer when we know it is already a legal unsigned-32 bit int value
static uint32_t integer_u(Atom a);
static int integer_d(double d);
Atom doubleToAtom(double n);
#if defined (AVMPLUS_IA32) || defined(AVMPLUS_AMD64)
static int integer_d_sse2(double d);
Atom doubleToAtom_sse2(double n);
#endif
private:
static int doubleToInt32(double d);
public:
static double number_d(Atom a);
/**
* intAtom is similar to the integer method, but returns
* an atom instead of a C++ int.
*/
Atom intAtom(Atom atom);
Atom uintAtom(Atom atom);
/**
* Converts the passed atom to a C++ bool.
* If the atom is already an E4 boolean, it is simply
* decoded. Otherwise, it is coerced to the boolean type
* and returned.
* [ed] 12/28/04 use int because bool is sometimes byte-wide.
*/
static int boolean(Atom atom);
/**
* Returns the passed atom's string representation.
* If the passed atom is not a string, it is coerced
* to type string using the ECMAScript coercion rules.
*/
Stringp string(Atom atom);
Stringp coerce_s(Atom atom);
/**
* Returns true if the passed atom is of string type.
*/
static bool isString(Atom atom);
static bool isName(Atom atom);
/**
* an interned atom is canonicalized in this way:
* boolean -> "true" or "false"
* number -> intern'ed string value
* string -> intern'ed string value
* object -> intern'ed result of toString()
*
* this way, interned atoms are suitable to be used as map keys and can
* be compared using ==.
* @param atom
* @return
*/
Stringp intern(Atom atom);
Namespacep internNamespace(Namespacep ns);
/** Helper function; reads a signed 24-bit integer from pc */
static int readS24(const byte *pc);
/**
* Returns the size of the instruction + all it's operands. For OP_lookupswitch the size will not include
* the size for the case targets.
*/
static int calculateInstructionWidth(const byte* p);
/**
* Read in some operands for the instruction located at *pc.
* Returns the size of the instruction, but will not read in all the case targets for
* an OP_lookupswitch, since there will be a variable number of them.
*/
static void readOperands(const byte* &pc, unsigned int& imm32, int& imm24, unsigned int& imm32b, int& imm8 );
/**
* Helper function; reads an unsigned 32-bit integer from pc
* See AbcParser::readS32 for more explanation of the variable length
* encoding scheme.
*
* 2nd argument is set to the actual size, in bytes, of the number read in,
* and third argument is the version of the ABC
*/
static uint32 readU30(const byte *&p)
{
// @todo -- needs to be moved into AvmCore-inlines.h,
// but first we must determine whether it should be inline, REALLY_INLINE, etc...
unsigned int result = p[0];
if (!(result & 0x00000080))
{
p++;
return result;
}
result = (result & 0x0000007f) | p[1]<<7;
if (!(result & 0x00004000))
{
p += 2;
return result;
}
result = (result & 0x00003fff) | p[2]<<14;
if (!(result & 0x00200000))
{
p += 3;
return result;
}
result = (result & 0x001fffff) | p[3]<<21;
if (!(result & 0x10000000))
{
p += 4;
return result;
}
result = (result & 0x0fffffff) | p[4]<<28;
p += 5;
return result;
}
// when you need to skip over a u30 and don't care about the result,
// this is slightly faster.
static void skipU30(const uint8_t*& p, int count = 1)
{
// @todo -- needs to be moved into AvmCore-inlines.h,
// but first we must determine whether it should be inline, REALLY_INLINE, etc...
while (count-- > 0)
{
if (!(p[0] & 0x80)) { p += 1; continue; }
if (!(p[1] & 0x80)) { p += 2; continue; }
if (!(p[2] & 0x80)) { p += 3; continue; }
if (!(p[3] & 0x80)) { p += 4; continue; }
//if (!(*p[4] & 0x80)) { p += 5; continue; } // test should be unnecessary
AvmAssert(!(p[4] & 0x80));
p += 5;
}
}
/** Helper function; reads an unsigned 16-bit integer from pc */
static int32_t readU16(const byte *pc);
private:
static const int k_atomDoesNotNeedCoerce_Masks[8];
public:
// note, return of true means we definitely DO NOT need a coerce,
// but return of false still means we *might* need to (ie, negating the result of this function
// isn't "needscoerce")
static bool atomDoesNotNeedCoerce(Atom a, BuiltinType bt);
/**
* this is the implementation of the actionscript "is" operator. similar to java's
* instanceof. returns true/false according to AS rules. in particular, it will return
* false if value==null.
*/
static bool istype(Atom atom, Traits* itraits);
/**
* this is the implementation of the actionscript "is" operator. similar to java's
* instanceof. returns true/false according to AS rules. in particular, it will return
* false if value==null.
*/
static Atom istypeAtom(Atom atom, Traits* itraits);
/**
* implements ECMA as operator. Returns the same value, or null.
*/
static Atom astype(Atom atom, Traits* expected);
/**
* implementation of OP_increg, decreg, increment, decrement which correspond to
* ++ and -- operators in AS.
*/
void increment_d(Atom *atom, int delta);
/**
* implementation of OP_increg, decreg, increment, decrement which correspond to
* ++ and -- operators in AS.
*/
void increment_i(Atom *atom, int delta);
/**
* ES3's internal ToPrimitive() function
*/
static Atom primitive(Atom atom);
/** OP_toboolean; ES3 ToBoolean() */
static Atom booleanAtom(Atom atom);
/** OP_tonumber; ES3 ToNumber */
Atom numberAtom(Atom atom);
/**
* ES3's internal ToNumber() function for internal use
*/
static double number(Atom atom);
/**
* The interrupt method is called from executing code
* when the interrupted flag is set. interrupt()
* MUST NOT RETURN; the caller expects a thrown exception.
*/
virtual void interrupt(Toplevel *env, InterruptReason) = 0;
/**
* called by the host to raise the AS3 interrupt exception.
* if AS3 code is executing, then soon after this call,
* interrupt() will be invoked by the currently executing function.
*/
void raiseInterrupt(InterruptReason reason);
// return true if there is a pending interrupt of the specific InterruptReason.
bool interruptCheckReason(InterruptReason r) const;
/**
* called by AS3 code when the interrupt is detected. Must
* not return!
*/
static void handleInterruptMethodEnv(MethodEnv*);
/**
* called by AS3 code when the interrupt is detected. Must
* not return!
*/
static void handleInterruptToplevel(Toplevel*);
/**
* This is called when the stack overflows
* (when the machine stack pointer is about to go below
* minstack)
*/
virtual void stackOverflow(Toplevel *env) = 0;
/**
* Throws an exception. Constructs an Exception object
* and calls throwException.
*/
void throwAtom(Atom atom);
/**
* The AVM+ equivalent of the C++ "throw" statement.
* Throws an exception, transferring control to the
* nearest CATCH block.
*/
void throwException(Exception *exception);
/**
* throwErrorV is a convenience function for throwing
* an exception with a formatted error message,
*/
void throwErrorV(ClassClosure *type, int errorID, Stringp arg1=0, Stringp arg2=0, Stringp arg3=0);
/**
* formatErrorMessageV is a convenience function for
* assembling an error message with varags.
*/
String* formatErrorMessageV( int errorID, Stringp arg1=0, Stringp arg2=0, Stringp arg3=0);
/**
* Convenience methods for converting various objects into value
* strings used for error message output.
*/
String* toErrorString(int d);
String* toErrorString(MethodInfo* m);
String* toErrorString(const Multiname& n);
String* toErrorString(const Multiname* n);
String* toErrorString(Namespacep ns);
String* toErrorString(const Traits* t);
String* toErrorString(const char* s);
String* toErrorString(const wchar* s);
String* atomToErrorString(Atom a);
/**
* getErrorMessage returns the format string for an
* errorID. Override to provide format strings for
* additional implementation-dependent error strings.
*/
virtual String* getErrorMessage(int errorID);
#ifdef DEBUGGER
/**
* willExceptionBeCaught walks all the way up the
* ActionScript stack to see if there is any "catch"
* statement which is going to catch the specified
* exception.
*/
bool willExceptionBeCaught(Exception* exception);
/**
* findErrorMessage searches an error messages table.
* Only available in debugger builds.
*/
String* findErrorMessage(int errorID,
int* mapTable,
const char** errorTable,
int numErrors);
/**
* Determines the language id of the given platform
*/
virtual int determineLanguage();
/**
* Creates a StackTrace from the current executing call stack
*/
StackTrace* newStackTrace();
#ifdef _DEBUG
void dumpStackTrace();
#endif
/** The call stack of currently executing code. */
CallStackNode *callStack;
#endif /* DEBUGGER */
CodeContext* codeContext() const;
Namespace* dxns() const;
/**
* implements OP_dxns. internedUri is expected to be from a constant
* pool and therefore already interned. Creates a namespace and
* saves it as the given method frame's default xml namespace.
*/
void setDxns(MethodFrame*, String* internedUri);
/**
* implements OP_dxnslate. uri is any value, which we must convert
* to an interned-string, then create an namespace from it,
* then save the value in the current method frame as the current
* default xml namespace.
*/
void setDxnsLate(MethodFrame*, Atom uri);
/** env of the highest catch handler on the call stack, or NULL */
ExceptionFrame *exceptionFrame;
Exception *exceptionAddr;
/**
* Searches the exception handler table of info for
* a try/catch block that contains the instruction at pc
* and matches the type of exception.
* @param info the method to search the exception handler
* table of
* @param pc the program counter at the point where
* the exception occurred; either a pointer into
* bytecode or into native compiled code
* @param exception the exception object that was thrown;
* used to match the type.
* @return ExceptionHandler object for the exception
* handler that matches, or re-throws the passed-in
* exception if no handler is found.
*/
ExceptionHandler* findExceptionHandler(MethodInfo *info,
sintptr pc,
Exception *exception);
ExceptionHandler* beginCatch(ExceptionFrame *ef,
MethodInfo *info, sintptr pc, Exception *exception);
/**
* Just like findExceptionHandler(), except that this function
* returns NULL if it can't find an exception handler, whereas
* findExceptionHandler() re-throws the passed-in exception if
* it can't find a handler.
*/
ExceptionHandler* findExceptionHandlerNoRethrow(MethodInfo *info,
sintptr pc,
Exception *exception);
/**
* Returns true if the passed atom is an XML object,
* as defined in the E4X Specification.
*/
static bool isXML(Atom atm);
/**
* Returns true if the passed atom is a XMLList object,
* as defined in the E4X Specification.
*/
static bool isXMLList(Atom atm);
static bool isXMLorXMLList(Atom atm);
/* Returns tru if the atom is a Date object */
static bool isDate(Atom atm);
// From http://www.w3.org/TR/2004/REC-xml-20040204/#NT-Name
static bool isLetter(wchar c);
static bool isDigit(wchar c);
static bool isCombiningChar(wchar c);
static bool isExtender(wchar c);
Stringp ToXMLString (Atom a);
Stringp EscapeElementValue (const Stringp s, bool removeLeadingTrailingWhitespace);
Stringp EscapeAttributeValue (Atom v);
/**
* Converts an Atom to a E4XNode if its traits match.
* Otherwise, null is returned. (An exception is NOT thrown)
*/
static E4XNode* atomToXML(Atom atm);
/**
* Converts an Atom to a XMLObject if its traits match.
* Otherwise, null is returned. (An exception is NOT thrown)
*/
static XMLObject* atomToXMLObject(Atom atm);
/**
* Converts an Atom to a XMLListObject if its traits match.
* Otherwise, null is returned. (An exception is NOT thrown)
*/
static XMLListObject* atomToXMLList(Atom atm);
/**
* Returns true if the passed atom is a QName object,
* as defined in the E4X Specification.
*/
static bool isQName(Atom atm);
/**
* Returns true if the passed atom is a Dictionary object,
* as defined in the E4X Specification.
*/
static bool isDictionary(Atom atm);
static bool isDictionaryLookup(Atom key, Atom obj);
/**
* Returns true if the passed atom is a valid XML name,
* as defined in the E4X Specification.
*/
bool isXMLName(Atom arg);
/**
* Converts an Atom to a QNameObject if its traits match.
* Otherwise, null is returned. (An exception is NOT thrown)
*/
static QNameObject* atomToQName(Atom atm);
/** Implementation of OP_typeof */
Stringp _typeof (Atom arg);
/** The XML entities table, used by E4X */
HeapHashtable* xmlEntities;
private:
static bool isBuiltinType(Atom atm, BuiltinType bt);
static bool isBuiltinTypeMask(Atom atm, int btmask);
private:
//
// this used to be Heap
//
/** size of interned String table */
int stringCount;
/** number of deleted entries in our String table */
int deletedCount;
#define AVMPLUS_STRING_DELETED ((Stringp)(1))
/** size of interned Namespace table */
int nsCount;
int numStrings;
int numNamespaces;
public:
static Namespacep atomToNamespace(Atom atom);
static double atomToDouble(Atom atom);
/**
* Convert an Atom of kStringType to a Stringp
* @param atom atom to convert. Note that the Atom
* must be of kStringType
*/
static Stringp atomToString(Atom atom);
// Avoid adding validation checks here and returning NULL. If this
// is returning a bad value, the higher level function should be fixed
// or AbcParser/Verifier should be enhanced to catch this case.
static ScriptObject* atomToScriptObject(const Atom atom);
// Helper function, allows generic objects to work with InlineHashtable
// and AtomArray, uses double type which is the only non-RC pointer tag.
// The key here is that these methods round-trip any pointer value to the
// same pointer value, there is no casting that might adjust the pointer.
static Atom genericObjectToAtom(const void* obj);
static const void* atomToGenericObject(Atom a);
static bool isGenericObject(Atom a);
private:
/** search the string intern table */
int findStringLatin1(const char* s, int len);
int findStringUTF16(const wchar* s, int len);
int findString(Stringp s);
/** search the namespace intern table */
int findNamespace(Namespacep ns, bool canRehash = true);
Namespacep gotNamespace(Stringp uri, int32_t api);
public:
// String creation. If len is omitted, zero-termination is assumed.
Stringp newStringLatin1(const char* str, int len = -1);
Stringp newStringUTF8(const char* str, int len = -1, bool strict = false);
Stringp newStringUTF16(const wchar* str, int len = -1);
// decodes UTF16LE or UTF16BE.
Stringp newStringEndianUTF16(bool littleEndian, const wchar* str, int len = -1);
// like newStringLatin1, but the string constant is assumed to remain valid
// for the life of the AvmCore. Generally, should only be used for literal
// strings, eg newConstantStringLatin1("foo")
Stringp newConstantStringLatin1(const char* str);
// variants on the newStringXXX() calls that also intern the string.
Stringp internStringLatin1(const char* s, int len = -1);
Stringp internStringUTF8(const char* s, int len = -1, bool constant = false);
Stringp internStringUTF16(const wchar* s, int len = -1);
// like internStringLatin1, but the string constant is assumed to remain valid
// for the life of the AvmCore. Generally, should only be used for literal
// strings, eg internStringLatin1("foo")
Stringp internConstantStringLatin1(const char* s);
/**
* intern the given string atom which has already been allocated
* @param atom
* @return
*/
Stringp internString(Stringp s);
Stringp internString(Atom atom);
Stringp internInt(int n);
Stringp internDouble(double d);
Stringp internUint32(uint32 ui);
#ifdef DEBUGGER
/**
* intern without allocating memory, returns NULL if its not already interned
*/
Stringp findInternedString(const char *s, int len);
#endif
static bool getIndexFromAtom(Atom a, uint32 *result);
static bool getIndexFromString(Stringp s, uint32 *result);
ScriptBufferImpl* newScriptBuffer(size_t size);
VTable* newVTable(Traits* traits, VTable* base, Toplevel* toplevel);
RegExpObject* newRegExp(RegExpClass* regExpClass,
Stringp pattern,
Stringp options);
ScriptObject* newObject(VTable* ivtable, ScriptObject *delegate);
FrameState* newFrameState(int frameSize, int scopeBase, int stackBase);
Namespacep newNamespace(Atom prefix, Atom uri, Namespace::NamespaceType type = Namespace::NS_Public);
Namespacep newNamespace(Atom uri, Namespace::NamespaceType type = Namespace::NS_Public);
Namespacep newNamespace(Stringp uri, Namespace::NamespaceType type = Namespace::NS_Public, int32_t api = 0);
Namespacep newPublicNamespace(Stringp uri);
Stringp uintToString(uint32 i);
Stringp intToString(int i);
Stringp doubleToString(double d);
Stringp concatStrings(Stringp s1, Stringp s2);
Atom uintToAtom(uint32_t n);
Atom intToAtom(int32_t n);
Atom allocDouble(double n);
void rehashStrings(int newlen);
void rehashNamespaces(int newlen);
// static version for smart pointers
static void atomWriteBarrier(MMgc::GC *gc, const void *container, Atom *address, Atom atomNew);
static void atomWriteBarrier_ctor(MMgc::GC *gc, const void *container, Atom *address, Atom atomNew);
static void atomWriteBarrier_dtor(Atom *address);
static void decrementAtomRegion(Atom *ar, int length);
public:
#ifdef AVMPLUS_VERBOSE
Stringp format(Atom atom);
#endif
#if VMCFG_METHOD_NAMES
Stringp formatAtomPtr(Atom atom);
#endif
private:
// hash set containing intern'ed strings
DRC(Stringp) * strings;
// hash set containing namespaces
DRC(Namespacep) * namespaces;
// API versioning state
uint32_t apis_start; // first api number
uint32_t apis_count; // count of apis
uint32_t uris_count; // count of uris
const char** uris; // array of uris
const int32_t* api_compat; // array of compatible api bit masks
int32_t largest_api;
int32_t active_api_flags;
#ifdef VMCFG_LOOKUP_CACHE
private:
// Saturating counter.
uint32_t lookup_cache_timestamp;
public:
uint32_t lookupCacheTimestamp() const;
bool lookupCacheIsValid(uint32_t t) const;
void invalidateLookupCache();
#endif
#ifdef VMCFG_NANOJIT
private:
// when set, we flush all binding caches at the end of the next gc sweep.
bool m_flushBindingCachesNextSweep;
public:
void flushBindingCachesNextSweep();
#endif
private:
friend class Traits;
Traits** _emptySupertypeList; // empty supertype list shared by many Traits
public:
// avoid multiple inheritance issues
class GCInterface : MMgc::GCCallback
{
public:
GCInterface(MMgc::GC * _gc) : MMgc::GCCallback(_gc), core(NULL) {}
void SetCore(AvmCore* _core) { this->core = _core; }
void presweep() { if(core) core->presweep(); }
void postsweep() { if(core) core->postsweep(); }
void log(const char *str) { if(core) core->console << str; }
void oom(MMgc::MemoryStatus status) { if(core) core->oom(status); }
private:
AvmCore *core;
};
GCInterface gcInterface;
};
/*
MethodFrame is a way of maintaining CodeContext and DXNS in a uniform way
in both Interpreter and JIT modes. CodeContext is a poorly-documented
structure that is exercised very little in current acceptance tests, but is
used extensively for Flash and AIR. The theory of operation:
-- Normally, the "active" CodeContext is that of the most-recently-called
non-builtin MethodEnv on the call stack.
-- native C++ code can override the current CodeContext by using EnterCodeContext(),
which just pushes another MethodFrame onto the stack...
it overrides the current CodeContext, but subsequent nested calls to non-builtin
methods will in turn override this.
-- The implementation is a bit convoluted, in the name of saving stack space.
A single field can contain either a MethodEnv* (for a normal MethodFrame)
or a CodeContext* (for one pushed by EnterCodeContext). This means that the top-of-stack
may not have the current MethodEnv* handy, so walking down the stack is necessary
to find it.
-- Note that MethodFrame doesn't contain a pointer to AvmCore*; this is by design
(as a stack-saving measure), as CodegenLIR doesn't need to save it (it can emit the proper constant value),
and all other callers have ready access to one.
*/
class MethodFrame
{
public:
// deliberately no ctor or dtor here.
// NOTE, the code in enter/exit is replicated in CodegenLIR.cpp;
// if you make changes here, you may need to make changes there as well.
void enter(AvmCore* core, MethodEnv* e);
void enter(AvmCore* core, CodeContext* cc);
void exit(AvmCore* core);
CodeContext* cc() const;
MethodEnv* env() const;
// Search for a frame that has a default namespace, starting on the given frame.
static Namespace* findDxns(const MethodFrame* start);
void setDxns(Namespace* ns);
public:
MethodFrame* next;
private:
friend class CodegenLIR;
enum {
IS_EXPLICIT_CODECONTEXT = 0x1,
DXNS_NOT_NULL = 0x2,
FLAGS_MASK = 0x3
};
uintptr_t envOrCodeContext;
Namespace* dxns; // NOTE: this struct is always stack-allocated (or via VMPI_alloca, which is just as good), so no DRC needed
};
/**
* ApiUtils provides some helper methods to friends of
* api versioning
*/
class ApiUtils
{
friend class AvmCore;
friend class AbcParser;
friend class Namespace;
friend class NativeInitializer;
friend class Traits;
friend class QNameObject;
friend class TypeDescriber;
friend class BuiltinTraits;
/**
* Return true if type is Namespace::NS_Pubilc and uri is one of the versioned
* namespaces in the list provided by the host
*/
static bool isVersionedNS(AvmCore* core, Namespace::NamespaceType type, Stringp uri);
/**
* Return an interned namespace that corresponds to the given a namespace
* and api bitmask
*/
static Namespacep getVersionedNamespace(AvmCore* core, Namespacep ns, API api);
/**
* Map an api bitmask to its cooresponding version number
*/
static uint32_t toVersion(AvmCore* core, API api);
/**
* Strip the given uri of its version marker, if it has one
*/
static Stringp getBaseURI(AvmCore* core, Stringp uri);
/**
* Return the API bitmask for the given uri, or zero if there is none
*/
static API getURIAPI(AvmCore* core, Stringp uri);
/**
* Return true if the given uri has a version marker
*/
static bool hasVersionMark(Stringp uri);
/**
* Return the API bitmask of all APIs compatible with the given API
*/
static API getCompatibleAPIs(AvmCore* core, API api);
/**
* Return the API bitmask for the smallest api
*/
static API getSmallestAPI();
/**
* Return the API bitmask for the largest api
*/
static API getLargestAPI(AvmCore* core);
enum {
MIN_API_MARK = 0xE000,
MAX_API_MARK = 0xF8FF
};
public:
/**
* Convert a version number to an api bitmask
*/
static API toAPI(AvmCore* core, uint32_t v);
};
}
#endif /* __avmplus_AvmCore__ */