wsharp%adobe.com 039e5a464d bug 375561. stejohns+ review. Various fixes from Flash player codebase
git-svn-id: svn://10.0.0.236/trunk@222472 18797224-902f-48f8-a5cc-f745e15eee43
2007-03-27 18:37:45 +00:00

1380 lines
37 KiB
C++

/* ***** 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 DECLARE_NATIVE_SCRIPTS() static NativeScriptInfo scriptEntries[];
#define BEGIN_NATIVE_SCRIPTS(_Class) NativeScriptInfo _Class::scriptEntries[] = {
#define NATIVE_SCRIPT(script_id, _Script) { script_id, (NativeScriptInfo::Handler)_Script::createGlobalObject, _Script::natives, sizeof(_Script) },
#define END_NATIVE_SCRIPTS() { -1, NULL, NULL, 0 } };
#define DECLARE_NATIVE_CLASSES() static NativeClassInfo classEntries[];
#define BEGIN_NATIVE_CLASSES(_Class) NativeClassInfo _Class::classEntries[] = {
#define NATIVE_CLASS(class_id, _Class, _Instance) { avmplus::NativeID::class_id, (NativeClassInfo::Handler)_Class::createClassClosure, _Class::natives, sizeof(_Class), sizeof(_Instance) },
#define END_NATIVE_CLASSES() { -1, NULL, NULL, 0, 0 } };
#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)
const int kBufferPadding = 16;
/**
* 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
{
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;
/**
* The GC used by this AVM instance
*/
MMgc::GC * const gc;
#ifdef DEBUGGER
/**
* For debugger versions of the VM, this is a pointer to
* the Debugger object.
*/
Debugger *debugger;
Profiler *profiler;
#endif
void branchCheck(MethodEnv *env, bool interruptable, int go)
{
if(go < 0)
{
#ifdef DEBUGGER
sampleCheck();
#endif
if (interruptable && interrupted)
interrupt(env);
}
}
#ifdef AVMPLUS_MIR
// MIR intermediate buffer pool
List<GrowableBuffer*, LIST_GCObjects> mirBuffers; // mir buffer pool
GrowableBuffer* requestNewMirBuffer(); // create a new buffer
GrowableBuffer* requestMirBuffer(); // get next buffer in list or a create a new one
void releaseMirBuffer(GrowableBuffer* buffer);
//GCSpinLock mirBufferLock;
#ifdef AVMPLUS_VERBOSE
MMgc::GCHashtable* codegenMethodNames;
#endif /* AVMPLUS_VERBOSE */
void initMultinameLate(Multiname& name, Atom index);
#endif /* MIR */
#ifdef AVMPLUS_PROFILE
StaticProfiler sprof;
DynamicProfiler dprof;
#endif
/**
* 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();
#ifdef AVMPLUS_VERBOSE
/**
* 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.
* Caution! This shoots out a ton of output!
*/
bool verbose;
#endif /* AVMPLUS_VERBOSE */
#ifdef AVMPLUS_INTERP
/**
* The turbo switch determines how bytecode is executed.
* When turbo is true, bytecode is translated to native code.
* When turbo is false, the gallop() interpreter loop is used.
* turbo defaults to true except on platforms where it is
* not supported, and except when debugging is in progress.
*
* Turbo is a debugger-only option. release builds always
* have it turned on. This means we can only build release
* builds on supported platforms.
*/
bool turbo;
#endif /* AVMPLUS_INTERP */
#ifdef AVMPLUS_MIR
#ifdef AVMPLUS_INTERP
/**
* To speed up initialization, we don't use MIR on
* $init methods; we use interp instead. For testing
* purposes, one may want to force the MIR to be used
* for all code including $init methods. The
* forcemir switch forces all code to run through MIR
* instead of interp.
*/
bool forcemir;
#endif
bool cseopt;
bool dceopt;
#if defined (AVMPLUS_IA32) || defined(AVMPLUS_AMD64)
bool sse2;
#endif
/**
* If this switch is set, executing code will check the
* "interrupted" flag to see whether an interrupt needs
* to be handled.
*/
bool interrupts;
/**
* If this is set to a nonzero value, executing code
* will check the stack pointer to make sure it
* doesn't go below this value.
*/
uint32 minstack;
/**
* This method will be invoked when the first exception
* frame is set up. This will be a good point to set
* minstack.
*/
virtual void setStackBase() {}
/**
* Genearate a graph for the basic blocks. Can be used by
* 'dot' utility to generate a jpg.
*/
#ifdef AVMPLUS_VERBOSE
bool bbgraph;
#endif //AVMPLUS_VERBOSE
#endif // AVMPLUS_MIR
#ifdef AVMPLUS_VERIFYALL
bool verifyall;
#endif
/** 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;
/** The location of the currently active defaultNamespace */
Namespace *const*dxnsAddr;
/**
* If this flag is set, an interrupt is in progress.
* This must be type int, not bool, since it will
* be checked by generated code.
*/
int interrupted;
/**
* The default namespace, "public", that all identifiers
* belong to
*/
DRC(Namespace*) publicNamespace;
VTable* namespaceVTable;
#ifdef FEATURE_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.
* @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 nativeMethods the NATIVE_METHOD table
* @param nativeClasses the NATIVE_CLASS table
* @param nativeScriptss the NATIVE_SCRIPT table
* @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,
AbstractFunction *nativeMethods[],
NativeClassInfo *nativeClasses[],
NativeScriptInfo *nativeScripts[]);
/**
* 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 toplevel the Toplevel object to execute against,
* or NULL if a Toplevel should be
* created.
* @param nativeMethods the NATIVE_METHOD table
* @param nativeClasses the NATIVE_CLASS table
* @param nativeScripts the NATIVE_SCRIPT table
* @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,
AbstractFunction *nativeMethods[],
NativeClassInfo *nativeClasses[],
NativeScriptInfo *nativeScripts[],
CodeContext *codeContext);
/**
* Initializes the native table with the specified
* class/script entries.
*/
void initNativeTables(NativeClassInfo *classEntries,
NativeScriptInfo *scriptEntries,
AbstractFunction *nativeMethods[],
NativeClassInfo* nativeClasses[],
NativeScriptInfo* nativeScripts[]);
/** Implementation of OP_equals */
Atom eq(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)
{
return (atom&7) == kObjectType && !isNull(atom);
}
static bool isPointer(Atom atom)
{
return (atom&7) < kSpecialType || (atom&7) == kDoubleType;
}
static bool isTraits(Atom type)
{
return type != 0 && (type&7) == 0;
}
static bool isNamespace(Atom atom)
{
return (atom&7) == kNamespaceType && !isNull(atom);
}
static bool isMethodBinding(Binding b)
{
return (b&7) == BIND_METHOD;
}
static bool isAccessorBinding(Binding b)
{
return (b&7) >= BIND_GET;
}
static bool hasSetterBinding(Binding b)
{
return (b&6) == BIND_SET;
}
static bool hasGetterBinding(Binding b)
{
return (b&5) == BIND_GET;
}
static int bindingToGetterId(Binding b)
{
AvmAssert(hasGetterBinding(b));
return urshift(b,3);
}
static int bindingToSetterId(Binding b)
{
AvmAssert(hasSetterBinding(b));
return 1+urshift(b,3);
}
static int bindingToMethodId(Binding b)
{
AvmAssert(isMethodBinding(b));
return urshift(b,3);
}
static int bindingToSlotId(Binding b)
{
AvmAssert(isSlotBinding(b));
return urshift(b,3);
}
/** true if b is a var or a const */
static int isSlotBinding(Binding b)
{
AvmAssert((BIND_CONST&6)==BIND_VAR);
return (b&6)==BIND_VAR;
}
/** true only if b is a var */
static int isVarBinding(Binding b)
{
return (b&7)==BIND_VAR;
}
/** true only if b is a const */
static int isConstBinding(Binding b)
{
return (b&7)==BIND_CONST;
}
/** Helper method; returns true if atom is an Function */
bool isFunction(Atom atom)
{
return istype(atom, traits.function_itraits);
}
/** Helper method; returns true if atom's type is double */
static bool isDouble(Atom atom)
{
return (atom&7) == kDoubleType;
}
/** Helper method; returns true if atom's type is int */
static bool isInteger(Atom atom)
{
return (atom&7) == kIntegerType;
}
/** Helper method; returns true if atom's type is Number */
static bool isNumber(Atom atom)
{
// accept kIntegerType(6) or kDoubleType(7)
return (atom&6) == kIntegerType;
}
/** Helper method; returns true if atom's type is boolean */
static bool isBoolean(Atom atom)
{
return (atom&7) == kBooleanType;
}
/** Helper method; returns true if atom's type is null */
static bool isNull(Atom atom)
{
return ISNULL(atom);
}
/** Helper method; returns true if atom's type is undefined */
static bool isUndefined(Atom atom)
{
return (atom == undefinedAtom);
}
static bool isNullOrUndefined(Atom atom)
{
return ((uintptr)atom) <= (uintptr)kSpecialType;
}
#ifdef AVMPLUS_VERBOSE
/** Disassembles an opcode and places the text in str. */
void formatOpcode(PrintWriter& out, const byte *pc, AbcOpcode opcode, int off, PoolObject* pool);
static void formatMultiname(PrintWriter& out, uint32 index, PoolObject* pool);
#endif
/**
* The resources table tracks what ABC's have been
* decoded, and avoids decoding the same one multiple
* times.
*/
Hashtable *resources;
/**
* @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;
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
*/
void initBuiltinPool();
/**
* Initializes the specified Toplevel object by running
* builtin.abc
*/
Toplevel* initTopLevel();
virtual size_t getToplevelSize() const;
virtual Toplevel* createToplevel(VTable *vtable);
public:
/**
* toUInt32 is the ToUInt32 algorithm from
* ECMA-262 section 9.6, used in many of the
* native core objects
*/
uint32 toUInt32(Atom atom) const
{
return (uint32)integer(atom);
}
/**
* toInteger is the ToInteger algorithm from
* ECMA-262 section 9.4, used in many of the
* native core objects
*/
double toInteger(Atom atom) const
{
if ((atom & 7) == kIntegerType) {
return (double) (atom>>3);
} else {
return MathUtils::toInt(number(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
*/
int integer(Atom atom) const;
// convert atom to integer when we know it is already a legal signed-32 bit int value
static int integer_i(Atom a)
{
if ((a&7) == kIntegerType)
return int(a>>3);
else
// TODO since we know value is legal int, use faster d->i
return MathUtils::real2int(atomToDouble(a));
}
// convert atom to integer when we know it is already a legal unsigned-32 bit int value
static uint32 integer_u(Atom a)
{
if ((a&7) == kIntegerType)
{
return uint32(a>>3);
}
else
{
// TODO figure out real2int for unsigned
return (uint32) atomToDouble(a);
}
}
static int integer_d(double d);
#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)
{
AvmAssert(isNumber(a));
if ((a&7) == kIntegerType)
return a>>3;
else
return atomToDouble(a);
}
/**
* intAtom is similar to the integer method, but returns
* an atom instead of a C++ int.
*/
Atom intAtom(Atom atom)
{
return intToAtom(integer(atom));
}
Atom uintAtom(Atom atom)
{
return uintToAtom(toUInt32(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.
*/
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)
{
return (atom&0x7) == kStringType && !isNull(atom);
}
static bool isName(Atom atom)
{
return isString(atom) && atomToString(atom)->isInterned();
}
/**
* 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);
Namespace* internNamespace(Namespace* ns);
/** Helper function; reads a signed 24-bit integer from pc */
static int readS24(const byte *pc)
{
#ifdef WIN32
// unaligned short access still faster than 2 byte accesses
return ((unsigned short*)pc)[0] | ((signed char*)pc)[2]<<16;
#else
return pc[0] | pc[1]<<8 | ((signed char*)pc)[2]<<16;
#endif
}
/**
* 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)
{
int a, b;
unsigned int c, d;
const byte* p2 = p;
readOperands(p2, c, a, d, b);
return int(p2-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 )
{
AbcOpcode opcode = (AbcOpcode)*pc++;
int op_count = opOperandCount[opcode];
imm8 = pc[0];
if( opcode == OP_pushbyte || opcode == OP_debug )
{
// these two operands have a byte as their first operand, which is not encoded
// with the variable length encoding scheme for bigger numbers, because it will
// never be larger than a byte.
--op_count;
pc++;
}
if( op_count > 0 )
{
if( opcode >= OP_ifnlt && opcode <= OP_lookupswitch )
{
imm24 = AvmCore::readS24(pc);
pc += 3;
}
else
{
imm32 = AvmCore::readU30(pc);
}
if( opcode == OP_debug )
{
--op_count; //OP_debug has a third operand of a byte
pc++;
}
if( op_count > 1 )
{
imm32b = AvmCore::readU30(pc);
}
}
}
/**
* 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)
{
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;
}
/** Helper function; reads an unsigned 16-bit integer from pc */
static int readU16(const byte *pc)
{
#ifdef WIN32
// unaligned short access still faster than 2 byte accesses
return *((unsigned short*)pc);
#else
return pc[0] | pc[1]<<8;
#endif
}
/**
* 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.
*/
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.
*/
Atom istypeAtom(Atom atom, Traits* itraits) {
return istype(atom, itraits) ? trueAtom : falseAtom;
}
/**
* 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
*/
Atom primitive(Atom atom);
/** OP_toboolean; ES3 ToBoolean() */
Atom booleanAtom(Atom atom);
/** OP_tonumber; ES3 ToNumber */
Atom numberAtom(Atom atom);
/**
* ES3's internal ToNumber() function for internal use
*/
double number(Atom atom) const;
#ifdef AVMPLUS_PROFILE
/**
* dump profiler stats
*/
void dump();
#endif
/**
* produce an atom from a string. used only for string constants.
* @param s
* @return
*/
Atom constant(const char *s)
{
return constantString(s)->atom();
}
Stringp constantString(const char *s)
{
return internString(newString(s));
}
/**
* The interrupt method is called from executing code
* when the interrupted flag is set.
*/
virtual void interrupt(MethodEnv *env) = 0;
/**
* This is called when the stack overflows
* (when the machine stack pointer is about to go below
* minstack)
*/
virtual void stackOverflow(MethodEnv *env) = 0;
void _stackOverflow(MethodEnv *env) { stackOverflow(env); }
/**
* 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(AbstractFunction* m);
String* toErrorString(Multiname* n);
String* toErrorString(Namespace* ns);
String* toErrorString(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();
int langID;
/** The call stack of currently executing code. */
CallStackNode *callStack;
/**
* Creates a StackTrace from the current executing call stack
*/
StackTrace* newStackTrace();
/**
Sampling profiler interface
*/
Sampler *sampler() { return &_sampler; }
void sampleCheck() { _sampler.sampleCheck(); }
bool sampling() { return _sampler.sampling; }
#ifdef _DEBUG
void dumpStackTrace();
#endif
#endif /* DEBUGGER */
CodeContextAtom codeContextAtom;
CodeContext* codeContext() const;
/** 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.
*/
bool isXML (Atom atm);
/* Returns tru if the atom is a Date object */
bool isDate(Atom atm);
// From http://www.w3.org/TR/2004/REC-xml-20040204/#NT-Name
bool isLetter (wchar c);
bool isDigit (wchar c);
bool isCombiningChar (wchar c);
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)
*/
E4XNode *atomToXML (Atom atm);
/**
* Converts an Atom to a XMLObject if its traits match.
* Otherwise, null is returned. (An exception is NOT thrown)
*/
XMLObject *atomToXMLObject (Atom atm);
/**
* Returns true if the passed atom is a XMLList object,
* as defined in the E4X Specification.
*/
bool isXMLList (Atom atm);
/**
* Converts an Atom to a XMLListObject if its traits match.
* Otherwise, null is returned. (An exception is NOT thrown)
*/
XMLListObject *atomToXMLList (Atom atm);
/**
* Returns true if the passed atom is a QName object,
* as defined in the E4X Specification.
*/
bool isQName (Atom atm);
/**
* Returns true if the passed atom is a Dictionary object,
* as defined in the E4X Specification.
*/
bool isDictionary (Atom atm);
bool isDictionaryLookup(Atom key, Atom obj)
{
return isObject(key) && isDictionary(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)
*/
QNameObject *atomToQName (Atom atm);
/** Implementation of OP_typeof */
Stringp _typeof (Atom arg);
/** The XML entities table, used by E4X */
Hashtable *xmlEntities;
private:
DECLARE_NATIVE_CLASSES()
DECLARE_NATIVE_SCRIPTS()
void registerNatives(NativeTableEntry *nativeMap, AbstractFunction *nativeMethods[]);
//
// 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 Namespace *atomToNamespace(Atom atom)
{
AvmAssert((atom&7)==kNamespaceType);
return (Namespace*)(atom&~7);
}
static double atomToDouble(Atom atom)
{
AvmAssert((atom&7)==kDoubleType);
double* obj = (double*)(atom&~7);
return *obj;
}
/**
* 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)
{
AvmAssert((atom&7)==kStringType);
return (Stringp)(atom&~7);
}
// 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)
{
AvmAssert((atom&7)==kObjectType);
return (ScriptObject*)(atom&~7);
}
// helper function, allows generic objects to work with Hashtable
// and AtomArray, uses double type which is the only non-RC
// GCObject type
static Atom gcObjectToAtom(const void* obj);
static const void* atomToGCObject(Atom a) { return (const void*)(a&~7); }
static bool isGCObject(Atom a) { return (a&7)==kDoubleType; }
private:
/** search the string intern table */
int findString(const wchar *s, int len);
/** search the namespace intern table */
int findNamespace(const Namespace *ns);
public:
#ifdef DEBUGGER
#if defined(MMGC_IA32) || defined(MMGC_IA64)
static inline uint32 FindOneBit(uint32 value)
{
#ifndef __GNUC__
_asm
{
bsr eax,[value];
}
#else
// DBC - This gets rid of a compiler warning and matchs PPC results where value = 0
register int result = ~0;
if (value)
{
asm (
"bsr %1, %0"
: "=r" (result)
: "m"(value)
);
}
return result;
#endif
}
#elif defined(MMGC_PPC)
static inline int FindOneBit(uint32 value)
{
register int index;
#ifdef DARWIN
asm ("cntlzw %0,%1" : "=r" (index) : "r" (value));
#else
register uint32 in = value;
asm { cntlzw index, in; }
#endif
return 31-index;
}
#else // generic platform
static int FindOneBit(uint32 value)
{
for (int i=0; i < 32; i++)
if (value & (1<<i))
return i;
// asm versions of this function are undefined if no bits are set
AvmAssert(false);
return 0;
}
#endif // MMGC_platform
#endif
public:
/**
* 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);
/**
* intern the given string and allocate it on the heap if necessary
* @param s
* @return
*/
Stringp internAlloc(const wchar *s, int len);
Stringp internAllocUtf8(const byte *s, int len);
// doesn't do any heap allocations if the string is interned
Stringp internAllocAscii(const char *str);
bool getIndexFromAtom (Atom a, uint32 *result) const
{
if (AvmCore::isInteger(a))
{
*result = uint32(a >> 3);
return true;
}
else
{
AvmAssert (AvmCore::isString(a));
return AvmCore::getIndexFromString (atomToString (a), result);
}
}
static bool getIndexFromString(Stringp s, uint32 *result);
ScriptBufferImpl* newScriptBuffer(size_t size);
VTable* newVTable(Traits* traits, VTable* base, ScopeChain* scope, AbcEnv* abcEnv, Toplevel* toplevel);
RegExpObject* newRegExp(RegExpClass* regExpClass,
Stringp pattern,
Stringp options);
ScriptObject* newObject(VTable* ivtable, ScriptObject *delegate);
// like newObject but runs init if there is one
ScriptObject* newActivation(VTable *vtable, ScriptObject *delegate);
/**
* traits with base traits (inheritance)
*/
Traits* newTraits(Traits *base,
int nameCount,
int interfaceCount,
size_t sizeofInstance);
FrameState* newFrameState(int frameSize, int scopeBase, int stackBase);
Namespace* newNamespace(Atom prefix, Atom uri, Namespace::NamespaceType type = Namespace::NS_Public);
Namespace* newNamespace(Atom uri, Namespace::NamespaceType type = Namespace::NS_Public);
Namespace* newNamespace(Stringp uri, Namespace::NamespaceType type = Namespace::NS_Public);
Namespace* newPublicNamespace(Stringp uri) { return newNamespace(uri); }
NamespaceSet* newNamespaceSet(int nsCount);
// String creation
Stringp newString(const char *str) const;
Stringp newString(const wchar *str) const;
Stringp newString(const char *str, int len) const;
Stringp uintToString(uint32 i);
Stringp intToString(int i);
Stringp doubleToString(double d);
Stringp concatStrings(Stringp s1, Stringp s2) const;
Atom doubleToAtom(double n);
Atom uintToAtom(uint32 n);
Atom intToAtom(int n);
Atom allocDouble(double n)
{
double *ptr = (double*)GetGC()->Alloc(sizeof(double), 0);
*ptr = n;
return kDoubleType | (uintptr)ptr;
}
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);
#ifdef MMGC_DRC
static void decrementAtomRegion(Atom *ar, int length);
#endif
#ifdef AVMPLUS_VERBOSE
public:
Stringp format(Atom atom);
Stringp formatAtomPtr(Atom atom);
#endif
private:
// hash set containing intern'ed strings
DRC(Stringp) * strings;
// hash set containing namespaces
DRC(Namespacep) * namespaces;
// avoid multiple inheritance issues
class GCInterface : MMgc::GCCallback
{
public:
GCInterface(MMgc::GC * _gc) : MMgc::GCCallback(_gc) {}
void SetCore(AvmCore* _core) { this->core = _core; }
void presweep() { core->presweep(); }
void postsweep() { core->postsweep(); }
void log(const char *str) { core->console << str; }
private:
AvmCore *core;
};
GCInterface gcInterface;
#ifdef DEBUGGER
private:
Sampler _sampler;
#endif
};
}
#endif /* __avmplus_AvmCore__ */