Source code drop for the Actionscript virtual machine in the Adobe Flash Player for December 2013
2284 lines
88 KiB
C++
2284 lines
88 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_AvmCore__
|
|
#define __avmplus_AvmCore__
|
|
|
|
#include "Exception.h" // CatchAction
|
|
|
|
#ifdef VMCFG_TELEMETRY
|
|
namespace telemetry
|
|
{
|
|
class ITelemetry;
|
|
}
|
|
#endif
|
|
|
|
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 FLOAT_TYPE (core->traits.float_itraits)
|
|
#define FLOAT4_TYPE (core->traits.float4_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 VECTORFLOAT_TYPE (core->traits.vectorfloat_itraits)
|
|
#define VECTORFLOAT4_TYPE (core->traits.vectorfloat4_itraits)
|
|
#define VECTOROBJ_TYPE (core->traits.vectorobj_itraits)
|
|
|
|
const int kBufferPadding = 16;
|
|
|
|
enum VB_Bits {
|
|
// Output control bits for verbose mode
|
|
VB_builtins = 1<<31 // display output for builtins (default is to ignore any builtins)
|
|
,VB_parse = 1<<30 // abc parsing information
|
|
,VB_verify = 1<<29 // verification information
|
|
,VB_interp = 1<<28 // interpreter information
|
|
,VB_jit = 1<<27 // jit information
|
|
,VB_traits = 1<<26 // traits creation information
|
|
,VB_execpolicy = 1<<25 // execution policy information
|
|
,VB_raw = 1<<24 // native code (via nanojit) is displayed in raw (i.e unbuffered bottom-up) fashion.
|
|
,VB_lircfg = 1<<23 // produce a control-flow graph of any resultant lir (gml format) in extended basic blocks
|
|
,VB_lircfg_bb = 1<<22 // same as above, except each vertex is a basic block
|
|
,VB_lircfg_ins = 1<<21 // same as above, except each vertex is an instruction
|
|
// @warning make sure these don't collide with LC_xxx bits
|
|
};
|
|
|
|
typedef UnmanagedPointerList<MethodRecognizer*> MethodRecognizerSet;
|
|
|
|
#ifdef VMCFG_NANOJIT
|
|
// ABC->LIR JIT options. This doesn't belong here, but
|
|
// it cannot go in CodegenLIR.h to avoid a dependency cycle.
|
|
struct JitConfig
|
|
{
|
|
// Generate inline fastpaths, introducing new control flow.
|
|
// Distinct from non-speculative inlining and specialization.
|
|
bool opt_inline;
|
|
|
|
// Initialize with default options.
|
|
JitConfig() : opt_inline(true) {}
|
|
};
|
|
#endif
|
|
|
|
struct Config
|
|
{
|
|
#ifdef VMCFG_NANOJIT
|
|
// options for nanojit
|
|
nanojit::Config njconfig;
|
|
// options for CodegenLIR
|
|
JitConfig jitconfig;
|
|
|
|
#endif
|
|
/**
|
|
* 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;
|
|
|
|
/**
|
|
* A list of MethodRecognizers that is used to determine
|
|
* whether verbose output is to occur on a given method
|
|
* or not. If this list is empty then verbosity is enabled
|
|
* for all methods (assuming its on).
|
|
*/
|
|
const char* verboseOnlyString;
|
|
|
|
// 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;
|
|
|
|
/**
|
|
* Global default OSR threshold. If there is no OSR threshold specified
|
|
* for a method in an ExecPolicy attribute, this value will be used.
|
|
* A threshold value of zero is interpreted specially, meaning that the
|
|
* legacy non-OSR compilation policy will be in force. Otherwise, it is the
|
|
* initial value of a counter that will be decremented upon each entry to
|
|
* the method or traversal of a backward branch therein, triggering compilation
|
|
* when the counter reaches zero.
|
|
*
|
|
* Note that the policy attribute is orthogonal to the debugging-oriented
|
|
* compilation override expressed via compilePolicyRules below. Perhaps some
|
|
* coordination may be appropriate, e.g., extending compilePolicyRules to
|
|
* understand OSR as well as legacy and interp-only/jit-only modes.
|
|
*/
|
|
uint32_t osr_threshold;
|
|
|
|
/**
|
|
* If false, OSR is disabled completely, and the legacy policy applies
|
|
* regardless of the value of the global OSR threshold default or per-method
|
|
* overrides via the ExecPolicy attribute. This provides a way to definitively
|
|
* suppress OSR even for content that chooses to opt-in via ExecPolicy, thus
|
|
* facilitating certain diagnostic scenarios.
|
|
*/
|
|
bool osr_enabled;
|
|
|
|
uint32_t jitprof_level;
|
|
const char* compilePolicyRules; // JIT compilation override
|
|
|
|
/**
|
|
* 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 verifyonly;
|
|
bool verifyquiet;
|
|
|
|
bool jitordie; // Always JIT, and if the JIT fails then abort
|
|
};
|
|
|
|
class MethodFrame;
|
|
|
|
// Use this for picking apart and putting together double values from constituent
|
|
// words. "words.msw" is the most significant word, containing sign bit, exponent,
|
|
// and most significant bits of mantissa. "words.lsw" is the least significant
|
|
// word, containing the least significant bits of mantissa. "bits" are two words
|
|
// you can use if you don't care about the word order or if you're constructing
|
|
// a double from a machine representation to which you only have a byte pointer.
|
|
// All words are native-endianness.
|
|
//
|
|
// (Note the structure tag 'words' is required, the Solaris C++ compiler does not
|
|
// support unnamed structures as of May, 2010.
|
|
|
|
union double_overlay
|
|
{
|
|
double_overlay() {}
|
|
double_overlay(double d) { value=d; }
|
|
double_overlay(uint64_t v) { bits64=v; }
|
|
|
|
double value;
|
|
#if defined VMCFG_BIG_ENDIAN || defined VMCFG_DOUBLE_MSW_FIRST
|
|
struct { uint32_t msw, lsw; } words;
|
|
#else
|
|
struct { uint32_t lsw, msw; } words;
|
|
#endif
|
|
uint32_t bits32[2];
|
|
uint64_t bits64;
|
|
};
|
|
|
|
union float_overlay
|
|
{
|
|
float_overlay() {}
|
|
float_overlay(float f) { value=f; }
|
|
float_overlay(uint32_t v) { word=v; }
|
|
|
|
float value;
|
|
uint32_t word;
|
|
};
|
|
|
|
// Regular expression compilation cache.
|
|
//
|
|
// The cache is used by AvmCore and is a cache for compiled regular expressions.
|
|
// The cache significantly reduces the cost of using literal regular expressions
|
|
// in loops (rather than manually hoisting them out of the loop), and it does so
|
|
// at low complexity.
|
|
//
|
|
// See Bugzilla 573504 for more information.
|
|
|
|
// A single entry in the regex cache. Sits inside a GCRoot.
|
|
|
|
class RegexCacheEntry
|
|
{
|
|
friend class RegexCache;
|
|
|
|
private:
|
|
RegexCacheEntry()
|
|
: timestamp(0)
|
|
, hits(0)
|
|
, optionFlags(0)
|
|
, global(false)
|
|
, hasNamedGroups(false)
|
|
{
|
|
}
|
|
|
|
void clear()
|
|
{
|
|
pattern = NULL;
|
|
options = NULL;
|
|
regex = NULL;
|
|
timestamp = 0;
|
|
hits = 0;
|
|
}
|
|
|
|
public:
|
|
bool match(String* pattern, String* options)
|
|
{
|
|
return pattern == this->pattern && options == this->options;
|
|
}
|
|
|
|
MMgc::GCRoot::GCMember<String> pattern;
|
|
MMgc::GCRoot::GCMember<String> options;
|
|
MMgc::GCRoot::GCMember<CompiledRegExp> regex;
|
|
|
|
uint64_t timestamp;
|
|
uint32_t hits;
|
|
int optionFlags;
|
|
bool global;
|
|
bool hasNamedGroups;
|
|
};
|
|
|
|
// Cache replacement is LRU based on a simple timestamping mechanism. There's also
|
|
// a mechanism to disable the cache if the number of entries that are used just once
|
|
// significantly outweigh the uses of the entries that are used more than once,
|
|
// that mechanism cuts the overhead of cache maintenance for programs that don't
|
|
// benefit from the cache.
|
|
|
|
class RegexCache
|
|
{
|
|
friend class AvmCore;
|
|
|
|
public:
|
|
/**
|
|
* Search for a match in the regex cache, set found to 'true' if there was
|
|
* a match, otherwise evict a member, set found to 'false'. In all cases
|
|
* return a structure that can be updated.
|
|
*/
|
|
RegexCacheEntry& findCachedRegex(bool& found, String* source, String* options);
|
|
|
|
/**
|
|
* Search for a match in the regex cache, return 'true' if it's there or
|
|
* 'false' if not.
|
|
*/
|
|
bool testCachedRegex(String* source, String* options);
|
|
|
|
/**
|
|
* Return true iff the cache has been disabled. Note the cache is re-enabled
|
|
* following every GC.
|
|
*/
|
|
bool disabled() { return m_disabled; }
|
|
|
|
private:
|
|
RegexCache();
|
|
|
|
/**
|
|
* Clear the cache, reenable it if it was disabled, reset utilization data.
|
|
*/
|
|
void clear();
|
|
|
|
private:
|
|
// These parameters are not subject to a lot of experimental validation, in
|
|
// particular, the parameters for cache enabling/disabling are really just
|
|
// gut feelings. More experimental validation would be good but we don't
|
|
// have a lot of content that uses regular expressions.
|
|
|
|
static const size_t cacheSize = 4; // Gut feeling: 4 is about right
|
|
static const uint64_t wastedWorkMultiplier = 10; // Gut feeling: utilization should be above 10%
|
|
static const uint64_t warmupTicks = 1000; // Number of ticks before we start worrying about hit rate
|
|
|
|
RegexCacheEntry m_entries[cacheSize]; // Cache storage
|
|
uint64_t m_timestamp; // Timestamp value for LRU
|
|
uint64_t m_wasted; // Utility of wasted cache entries (sum of usage counts == 1)
|
|
uint64_t m_useful; // Utility of useful cache entries (sum of usage counts > 1)
|
|
bool m_disabled; // true iff the cache has been (temporarily) disabled
|
|
};
|
|
|
|
/**
|
|
* Container for bug compatibility flags.
|
|
*
|
|
* NOTE the allocation code in AvmCore::createBugCompatibility assumes this structure
|
|
* is pointer-free and allocates it as pointer-free memory (bugzilla 596529).
|
|
* Subclassers should consider doing the same.
|
|
*/
|
|
class BugCompatibility : public MMgc::LeafObject
|
|
{
|
|
public:
|
|
enum Version
|
|
{
|
|
kSWF9, // SWF9 (Flash Player 9.x)
|
|
kSWF10, // SWF10 (Flash Player 10.0 & 10.1)
|
|
kSWF11, // SWF11 (Flash Player 10.2 Spicy)
|
|
kSWF12, // SWF12 (Flash Player 10.3 Wasabi)
|
|
kSWF13, // SWF13 (Flash Player 11.0 Serrano)
|
|
kSWF14, // SWF14 (Flash Player 11.1 Anza)
|
|
kSWF15, // SWF15 (Flash Player 11.2 Brannan)
|
|
kSWF16, // SWF16 (Flash Player 11.3 Cyril)
|
|
kSWF17, // SWF17 (Flash Player 11.4 Dolores)
|
|
kSWF18, // SWF18 (Flash Player 11.5 Ellis)
|
|
kSWF19, // SWF19 (Flash Player 11.6 Folsom)
|
|
kSWF20, // SWF20 (Flash Player 11.7 Geary)
|
|
kSWF21, // SWF21 (Flash Player 11.8 Harrison)
|
|
kSWF22, // SWF22 (Flash Player 11.9 Irving)
|
|
|
|
VersionCount,
|
|
|
|
kLatest = VersionCount - 1 // alias for "most recent"
|
|
|
|
};
|
|
|
|
/** These are the external names for swf versions - all integers.
|
|
*
|
|
* The names must correspond to the Version enum above. The value will
|
|
* be '9' for kSWF9, and so on.
|
|
*/
|
|
static uint32_t const kNames[BugCompatibility::VersionCount];
|
|
|
|
/** Compatibility bug fixes in the AVM - an overview.
|
|
*
|
|
* The AVM provides bug-compatible behavior to specific released versions
|
|
* in both AS3 and C++ code. The compatibility problem is orthogonal to
|
|
* the API versioning problem (where names may be introduced globally or
|
|
* in classes and interfaces but are visible only to certain versions).
|
|
* However, groups of bug fixes appear in specific named verions of the AVM,
|
|
* and since the API versioning mechansim already deals with named versions
|
|
* the API mechanism can be used to control bug-compatibility at a high
|
|
* level. When the host code requests that a certain API version be enabled,
|
|
* the bug-compatibility flags for that version are turned on, enabling
|
|
* bug fixes appropriate for that version.
|
|
*
|
|
* In the C++ level bug-compatibility is handled by having one flag for each
|
|
* bug for which we provide a compatibility switch. C++ code tests
|
|
* these flags, it never checks for a specific version name.
|
|
*
|
|
* The flags are made available to AS3 code through an global function,
|
|
* 'bugzilla(n:int)', which is in an internal namespace (part of the
|
|
* 'VM_INTERNAL' API). It returns true if the flag for the numbered bug is
|
|
* set, otherwise false.
|
|
*
|
|
* The flags always name a bug in Bugzilla, which means that bugs named here
|
|
* can /only/ be about the one thing that is fixed.
|
|
*
|
|
* A short description is usually good to have by each of these, eg the subject
|
|
* line from the bug report. If the subject line is not very good then please
|
|
* fix it first.
|
|
*
|
|
* Keep them in numeric order, please.
|
|
*
|
|
* DO NOT SET THESE FLAGS EXPLICITLY! ALWAYS CALL THE CONSTRUCTOR!
|
|
*/
|
|
|
|
// Entities are not escaped when appending String content to XML
|
|
//
|
|
// This and the "root" variant are the one and only special case we support,
|
|
// due to existing usage we need to maintain. The rules are:
|
|
//
|
|
// -- if bugzilla444630 is set, we always escape the entities
|
|
// -- if bugzilla444630 is clr,
|
|
// then we use AvmCore::bugzilla444630 to determine whether to escape
|
|
//
|
|
// The reason for this hackery is that existing Flash9/10 content assumes that
|
|
// the root SWF defines the behavior, and all subordinate SWFs will follow it,
|
|
// regardless of their version.
|
|
//
|
|
// This is some ugly hackery, but a necessary evil for legacy content. Please don't
|
|
// ever use this antipattern for new bugs.
|
|
unsigned bugzilla444630:1;
|
|
|
|
unsigned bugzilla456852a:1; // Different runtime error for invalid constructor call when -Ojit set
|
|
unsigned bugzilla456852b:1; // Different runtime error for negative vector index when -Ojit set
|
|
unsigned bugzilla504525:1; // Vector.concat processes arguments in reverse order
|
|
unsigned bugzilla513018:1; // parseFloat accepts illegal number syntax
|
|
unsigned bugzilla513039:1; // Number.toFixed(0) returns incorrect numbers, rounding issues
|
|
unsigned bugzilla524122:1; // Incorrect optimization for integers in numeric sort
|
|
unsigned bugzilla526662:1; // XMLParser stops at NUL char
|
|
unsigned bugzilla532454:1; // Sort incorrectly converts result of user-supplied comparison function to integer
|
|
unsigned bugzilla539094:1; // OSR (On-Stack replacement of interpreted methods by JIT-ted methods)
|
|
unsigned bugzilla551587:1; // MathClass:_min() does not correctly handle -0
|
|
unsigned bugzilla558863:1; // in operator on bytearray throws exception for non-natural number
|
|
unsigned bugzilla574600:1; // Vector.<>.AS3::map returns no value
|
|
unsigned bugzilla585791:1; // String.localeCompare with a null String object returns 0
|
|
unsigned bugzilla598683:1; // Bad XML with unterminated node with namespace not throwing correctly
|
|
unsigned bugzilla609416:1; // encodeURIComponent and decodeURIComponent give wrong output when input contains surrogate pairs
|
|
unsigned bugzilla654807:1; // sealed subclasses of Array act sort-of-dynamic
|
|
unsigned bugzilla661330:1; // Array.length behavior doesn't follow ECMA262 near 2^32-1
|
|
unsigned bugzilla678952:1; // Operations on Vector.<C> do not make use of or provide information about C
|
|
unsigned bugzilla619148:1; // Stack traces in release builds
|
|
unsigned watson3548918:1; // For appendChild, if the newChild is already in the tree, it is first removed.
|
|
unsigned watson3573621:1; // HTTPStatusEvent is not sent when the request has to have a crossdomain policy file checked.
|
|
protected:
|
|
friend class AvmCore;
|
|
|
|
// You should never construct a BugCompatibility directly;
|
|
// instead, call AvmCore::createBugCompatibility, so that embedders
|
|
// can create concrete subclasses if they so desire.
|
|
explicit BugCompatibility(Version v);
|
|
};
|
|
|
|
// Internal to AvmCore, do not use for other purposes
|
|
class GC_CPP_EXACT(LivePoolNode, MMgc::GCRoot)
|
|
{
|
|
GC_DATA_BEGIN(LivePoolNode)
|
|
public:
|
|
LivePoolNode* next;
|
|
MMgc::GCWeakRef* GC_POINTER(pool);
|
|
GC_DATA_END(LivePoolNode)
|
|
|
|
LivePoolNode(MMgc::GC* gc) : GCRoot(gc, MMgc::kExact) {}
|
|
};
|
|
|
|
/**
|
|
* The main class of the AVM+ virtual machine. This is the
|
|
* main entry point to the VM for running ActionScript code.
|
|
*
|
|
* ----- NOTE!! NOTE!! NOTE!! NOTE!! --------------------------
|
|
*
|
|
* AvmCore internal types and data fields are grouped at the beginning of the class
|
|
* definition in order to give exact tracing a credible shot at staying correct.
|
|
* PLEASE add data fields only in the correct group.
|
|
*
|
|
* ----- NOTE!! NOTE!! NOTE!! NOTE!! --------------------------
|
|
*/
|
|
class AvmCore : public MMgc::GCRoot
|
|
{
|
|
friend class CodegenLIR;
|
|
friend class halfmoon::JitFriend;
|
|
friend class Deoptimizer;
|
|
friend class EnterCodeContext;
|
|
friend class EnterMethodEnv;
|
|
friend class ExceptionFrame;
|
|
friend class MethodFrame;
|
|
friend class Traits;
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// BEGIN private data definitions
|
|
|
|
private:
|
|
enum {
|
|
MIN_API_MARK = 0xE294, // that's "0xE000 + 660", 660 being an odd legacy wart
|
|
MAX_API_MARK = 0xF8FF
|
|
};
|
|
|
|
// END private data definitions
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// BEGIN public data definitions
|
|
|
|
public:
|
|
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,
|
|
|
|
// Safepoint poll!
|
|
SafepointPoll = 3
|
|
};
|
|
|
|
// 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;
|
|
};
|
|
|
|
class ICodeContextCreator
|
|
{
|
|
public:
|
|
virtual ~ICodeContextCreator() {}
|
|
virtual CodeContext* create(DomainEnv* domainEnv, const BugCompatibility* bugCompatibility) = 0;
|
|
};
|
|
|
|
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) {}
|
|
};
|
|
|
|
bool enterEventLoop;
|
|
bool inEventLoop;
|
|
|
|
// END public data definitions
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// BEGIN untraced private fields
|
|
|
|
private:
|
|
#ifdef DEBUGGER
|
|
Sampler* _sampler; // Sampler is a GCRoot
|
|
#endif
|
|
|
|
/**
|
|
* 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.
|
|
* SafepointPoll for safepoint poll (interrupt handler will return).
|
|
*/
|
|
InterruptReason interrupted;
|
|
InterruptReason pending_interrupt;
|
|
friend class EnterSafepointManager; // To access the above field.
|
|
|
|
|
|
/**
|
|
* 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;
|
|
|
|
#ifdef VMCFG_TELEMETRY_SAMPLER
|
|
public:
|
|
/*
|
|
Sampler functionality - used by the host application to do periodic sampling of the
|
|
actionscript call stack. It works like this:
|
|
- the host app tells the VM to enable sampling
|
|
- the host app then periodically requests the VM to take a sample
|
|
- at the next available opportunity, the VM will pause script
|
|
execution and call the host's takeSample function().
|
|
- from within takeSample(), the host can fetch the current call stack, in the
|
|
form of an array of MethodInfo (which is an opaque token whose details are known
|
|
only to the VM).
|
|
- from time to time, the host may call functionHandleToString() to fetch the
|
|
human-readable string corresponding to a MethodInfo.
|
|
*/
|
|
|
|
// Enable/disable the sampler. The parameter is a callback object which
|
|
// provides the host's takeSample() function. Sampling becomes enabled if the parameter
|
|
// is non-null. A null parameter disables sampling.
|
|
void enableSampler(telemetry::ISampler*);
|
|
|
|
// Request the VM to call ISampler::takeSample() as soon as possible, from within the VM's execution
|
|
// thread. requestSample() can be called from any thread. This function will only
|
|
// be called when sampling is enabled. Multiple calls to this function have the same effect as a single call.
|
|
void requestSample();
|
|
|
|
// Clear the request that was set by requestSample(). The host will always call this from
|
|
// within takeSample(), and perhaps at other times as well.
|
|
void clearSampleRequest();
|
|
|
|
// Find out if the current call stack is empty. This function will only ever be called from
|
|
// within ISampler::takeSample(), as called from the VM.
|
|
bool isCallStackEmpty();
|
|
|
|
// Copy the current call stack into 'stackBuffer'. Returns the number of stack items.
|
|
// This function will only ever be called from within ISampler::takeSample(),
|
|
// as called from the VM. If the stack is deeper than maxDepth, we fill stackBuffer
|
|
// to the maximum and then return maxDepth+1. The deepest stack frame should appear
|
|
// first in the buffer.
|
|
unsigned int recordCallStack(telemetry::FunctionHandle* stackBuffer, unsigned int maxDepth);
|
|
|
|
// Find out the function name corresponding to a given call stack item.
|
|
// We retain ownership of the object pointed to by the returned pointer.
|
|
// This function can be called from the host at any time, not only from inside takeSample().
|
|
avmplus::Stringp functionHandleToString(telemetry::FunctionHandle item);
|
|
|
|
// Dumps the current MethodFrame stack to the given string buffer.
|
|
// This stack trace works on Release player builds as well.
|
|
void GetStackTrace(StringBuffer &buffer);
|
|
|
|
/*
|
|
Functions and data that support sampling.
|
|
*/
|
|
|
|
/** if non-zero, it means that the VM should call takeSample() or takeSampleWrapper() as soon as possible. */
|
|
unsigned int sampleTicks;
|
|
|
|
/** whether or not the sampler is currently enabled */
|
|
bool samplerEnabled;
|
|
|
|
/** one of these functions is called by the executing actionscript code,
|
|
whenever that code notices that sampleTicks != 0. They just call sampler->takeSample() */
|
|
static void FASTCALL takeSampleWrapper(AvmCore *theCore);
|
|
void FASTCALL takeSample();
|
|
|
|
private:
|
|
/** the sampler object */
|
|
telemetry::ISampler* sampler;
|
|
#endif
|
|
|
|
#ifdef VMCFG_STACK_METRICS
|
|
public:
|
|
/** Smallest stackpointer value sampled since last reset. */
|
|
uintptr_t minStack;
|
|
|
|
/** Largest stackpointer value sampled since last reset. */
|
|
uintptr_t maxStack;
|
|
#endif
|
|
|
|
private:
|
|
// note, allocated using mmfx_new, *not* gc memory
|
|
LivePoolNode* livePools;
|
|
|
|
static const int k_atomDoesNotNeedCoerce_Masks[8];
|
|
|
|
/** size of interned String table */
|
|
int stringCount;
|
|
|
|
/** number of deleted entries in our String table */
|
|
int deletedCount;
|
|
|
|
/** size of interned Namespace table */
|
|
int nsCount;
|
|
|
|
int numStrings;
|
|
int numNamespaces;
|
|
|
|
// a running count of MethodInfos used to generate unique id's for the PoolObjects
|
|
uint32_t currentMethodInfoCount;
|
|
|
|
// API versioning state
|
|
ApiVersionSeries const m_activeApiVersionSeries;
|
|
uint32_t const m_activeApiVersionSeriesMask;
|
|
|
|
#ifdef VMCFG_NANOJIT
|
|
// when set, we flush all binding caches at the end of the next gc sweep.
|
|
bool m_flushBindingCachesNextSweep;
|
|
#endif
|
|
|
|
// END untraced private fields
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// BEGIN untraced public fields
|
|
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 verifyonly_default;
|
|
static const bool verifyquiet_default;
|
|
static const Runmode runmode_default;
|
|
static const bool osr_enabled_default;
|
|
static const uint32_t osr_threshold_default;
|
|
static const uint32_t jitprof_level_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
|
|
|
|
/**
|
|
* 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 _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
|
|
|
|
// Please see BugCompatibility for an explanation of this field.
|
|
bool bugzilla444630;
|
|
|
|
#ifdef DEBUGGER
|
|
int langID;
|
|
bool passAllExceptionsToDebugger;
|
|
#endif
|
|
|
|
Config config;
|
|
|
|
// execution manager, responsible for all invocation
|
|
ExecMgr* exec;
|
|
|
|
/** The call stack of currently executing code. */
|
|
CallStackNode *callStack;
|
|
|
|
/** env of the highest catch handler on the call stack, or NULL */
|
|
ExceptionFrame *exceptionFrame;
|
|
|
|
GCInterface gcInterface;
|
|
|
|
// END untraced public fields
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// BEGIN traced private fields
|
|
|
|
private:
|
|
#ifdef DEBUGGER
|
|
Debugger* _debugger;
|
|
Profiler* _profiler;
|
|
#endif
|
|
QCache* m_tbCache;
|
|
QCache* m_tmCache;
|
|
QCache* m_msCache;
|
|
DomainMgr* m_domainMgr;
|
|
public:
|
|
// FIXME props
|
|
WeakRefList<DomainEnv> m_domainEnvs;
|
|
private:
|
|
|
|
|
|
// hash set containing intern'ed strings
|
|
GCMember<String> * strings;
|
|
|
|
// hash set containing namespaces
|
|
GCMember<Namespace> * namespaces;
|
|
|
|
// API versioning state
|
|
HeapHashtable* m_versionedURIs;
|
|
#ifdef _DEBUG
|
|
HeapHashtable* m_unversionedURIs;
|
|
#endif
|
|
|
|
GCMember<UnscannedTraitsArray> _emptySupertypeList; // empty supertype list shared by many Traits
|
|
|
|
FixedHeapRef<Isolate> m_isolate;
|
|
|
|
// END traced private fields
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// BEGIN traced public fields
|
|
|
|
public:
|
|
RegexCache m_regexCache; // Substructure with traceable data
|
|
|
|
/** Internal table of strings for boolean type ("true", "false") */
|
|
GCMember<String> 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;
|
|
|
|
/** BugCompatibility used to initialize the built-in classes */
|
|
const BugCompatibility* builtinBugCompatibility;
|
|
|
|
/**
|
|
* The default namespace, "public"
|
|
*/
|
|
GCMember<Namespace> publicNamespace;
|
|
|
|
/**
|
|
* The unnamed public namespaces
|
|
*/
|
|
NamespaceSet* publicNamespaces;
|
|
|
|
/**
|
|
* @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.
|
|
*/
|
|
/*@{*/
|
|
|
|
GCMember<String> kconstructor;
|
|
GCMember<String> kEmptyString;
|
|
GCMember<String> ktrue;
|
|
GCMember<String> kfalse;
|
|
GCMember<String> kundefined;
|
|
GCMember<String> knull;
|
|
GCMember<String> ktoString;
|
|
GCMember<String> ktoLocaleString;
|
|
GCMember<String> kvalueOf;
|
|
GCMember<String> kvalue;
|
|
GCMember<String> klength;
|
|
GCMember<String> kobject;
|
|
GCMember<String> kxml;
|
|
GCMember<String> kfunction;
|
|
GCMember<String> kboolean;
|
|
GCMember<String> kvoid;
|
|
GCMember<String> knumber;
|
|
#ifdef VMCFG_FLOAT
|
|
GCMember<String> kfloat;
|
|
GCMember<String> kfloat4;
|
|
GCMember<String> knumeric;
|
|
#endif
|
|
GCMember<String> kstring;
|
|
GCMember<String> kuri;
|
|
GCMember<String> kprefix;
|
|
GCMember<String> kglobal;
|
|
GCMember<String> kcallee;
|
|
GCMember<String> kNeedsDxns;
|
|
GCMember<String> kVersion;
|
|
GCMember<String> kanonymousFunc;
|
|
// From here at least the spelling mirrors exactly the spelling in the string constant
|
|
GCMember<String> klittleEndian;
|
|
GCMember<String> kbigEndian;
|
|
GCMember<String> kparent;
|
|
GCMember<String> kattribute;
|
|
GCMember<String> kcomment;
|
|
GCMember<String> kprocessing_instruction;
|
|
GCMember<String> kelement;
|
|
GCMember<String> ktext;
|
|
GCMember<String> kattributeAdded;
|
|
GCMember<String> kattributeRemoved;
|
|
GCMember<String> kattributeChanged;
|
|
GCMember<String> knodeAdded;
|
|
GCMember<String> knodeRemoved;
|
|
GCMember<String> knodeChanged;
|
|
GCMember<String> knamespaceAdded;
|
|
GCMember<String> knamespaceRemoved;
|
|
GCMember<String> knamespaceSet;
|
|
GCMember<String> knameSet;
|
|
GCMember<String> ktextSet;
|
|
GCMember<String> klocalName;
|
|
GCMember<String> kindex;
|
|
GCMember<String> kinput;
|
|
GCMember<String> kemptyCtor;
|
|
GCMember<String> kMath;
|
|
GCMember<String> kNumber;
|
|
|
|
GCMember<String> kAsterisk; // '*'
|
|
GCMember<String> kColon; // ':'
|
|
GCMember<String> kUnderscore; // '_'
|
|
GCMember<String> kXML1998NS; // http://www.w3.org/XML/1998/namespace
|
|
GCMember<String> kzero; // '0' - 'kZero' conflicts with MMgc
|
|
GCMember<String> kClassS; // 'Class$'
|
|
GCMember<String> kVectorNumber; // 'Vector.<Number>'
|
|
#ifdef VMCFG_FLOAT
|
|
GCMember<String> kVectorFloat; // 'Vector.<float>'
|
|
GCMember<String> kVectorFloat4; // 'Vector.<float4>'
|
|
#endif
|
|
GCMember<String> kVectorint; // 'Vector.<int>'
|
|
GCMember<String> kVectoruint; // 'Vector.<uint>'
|
|
GCMember<String> kVectorAny; // 'Vector.<*>'
|
|
#ifdef VMCFG_FLOAT
|
|
GCMember<String> kx; // Used
|
|
GCMember<String> ky; // for
|
|
GCMember<String> kz; // JSON
|
|
GCMember<String> kw; // formatting
|
|
#endif
|
|
|
|
Atom kNaN;
|
|
#ifdef VMCFG_FLOAT
|
|
Atom kFltNaN;
|
|
Atom kFltOne; // needed for increment
|
|
Atom kFltMinusOne; // needed for decrement
|
|
Atom kFlt4NaN;
|
|
#endif
|
|
GCMember<String> workerStates[10];
|
|
|
|
GCMember<String> kExecPolicy; // execution policy attribute
|
|
GCMember<String> kOSR; // OSR threshold
|
|
GCMember<String> cachedChars[128];
|
|
/*@}*/
|
|
|
|
Exception *exceptionAddr;
|
|
|
|
/** The XML entities table, used by E4X */
|
|
HeapHashtable* xmlEntities;
|
|
|
|
// END traced public fields
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// BEGIN methods (private/public intermixed)
|
|
|
|
public:
|
|
// return the BugCompatibility that is associated with the current
|
|
// CodeContext. If there is no current CodeContext, return the
|
|
// BugCompatibility associated with the VM's builtins.
|
|
// This call will never return NULL.
|
|
const BugCompatibility* currentBugCompatibility() const;
|
|
|
|
// To be called post-PoolObjects' population to assign it a unique id amongst pools
|
|
void assignPoolId(PoolObject* pool);
|
|
|
|
/**
|
|
* 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();
|
|
virtual uintptr_t getStackBase() const
|
|
{
|
|
return 0;
|
|
}
|
|
/**
|
|
* Restore the stack base and limit based on a base of p.
|
|
* Restore the stack base when leaving a thread.
|
|
*/
|
|
virtual void restoreStackBase(uintptr_t p)
|
|
{
|
|
(void) p;
|
|
// NOTE: the shell currently does not implement setStackBase() or restoreStackBase().
|
|
}
|
|
#ifdef _DEBUG
|
|
virtual void tryHook();
|
|
#endif
|
|
virtual void catchHook(CatchAction action);
|
|
|
|
#ifdef DEBUGGER
|
|
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();
|
|
#endif
|
|
|
|
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);
|
|
|
|
public:
|
|
QCache* tbCache();
|
|
QCache* tmCache();
|
|
QCache* msCache();
|
|
|
|
// 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(GCOutputStream *stream);
|
|
void setConsoleStream(NonGCOutputStream *stream);
|
|
|
|
/**
|
|
* GCCallback functions
|
|
*/
|
|
virtual void presweep();
|
|
virtual void postsweep();
|
|
virtual void oom(MMgc::MemoryStatus status);
|
|
|
|
public:
|
|
DomainMgr* domainMgr() const;
|
|
|
|
#ifdef VMCFG_NANOJIT // accessors
|
|
#if defined AVMPLUS_IA32 || defined AVMPLUS_AMD64
|
|
bool use_sse2() const;
|
|
#endif
|
|
#endif
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
bool isVerbose(uint32_t b, MethodInfo* info=NULL);
|
|
static uint32_t parseVerboseFlags(const char* arg, char*& badFlag);
|
|
|
|
/**
|
|
* A list of MethodRecognizers that is used to determine
|
|
* whether verbose output is to occur on a given method
|
|
* or not. If this list is empty then verbosity is enabled
|
|
* for all methods (assuming its on).
|
|
*/
|
|
MethodRecognizerSet _verboseRestrictedTo;
|
|
|
|
virtual const char* identifyDomain(Domain* domain);
|
|
#endif
|
|
|
|
/**
|
|
* 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);
|
|
|
|
#ifdef VMCFG_STACK_METRICS
|
|
/** Reset stack metrics. */
|
|
void initStackMetrics();
|
|
|
|
/** Return smallest stackpointer value sampled since last reset. */
|
|
uintptr_t getMinStack();
|
|
|
|
/** Return largest stackpointer value sampled since last reset. */
|
|
uintptr_t getMaxStack();
|
|
|
|
/**
|
|
* Record stackpointer sample. Called internally by the VM at entry to
|
|
* each Actionscript function, whether interpreted or compiled.
|
|
*/
|
|
void FASTCALL recordStackPointer();
|
|
static void FASTCALL recordStackPointerWrapper(AvmCore *theCore);
|
|
#endif
|
|
|
|
/** 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);
|
|
|
|
/**
|
|
* Calls destroy on PCREContext to release the thread local variable key
|
|
* back to the system. This is only used on platforms (e.g. Android) that do not
|
|
* call the destructor of the static variable when the library is unloaded
|
|
* (on Android this is an OS bug in Android 2.2, and probably earlier).
|
|
*/
|
|
static void releasePCREContext();
|
|
|
|
private:
|
|
ScriptEnv* initOneScript(Toplevel* toplevel, AbcEnv* abcEnv, Traits* scriptTraits);
|
|
ScriptEnv* initAllScripts(Toplevel* toplevel, AbcEnv* abcEnv);
|
|
Atom callScriptEnvEntryPoint(ScriptEnv* main);
|
|
|
|
public:
|
|
/**
|
|
* Execute an ABC file that has been parsed into a
|
|
* PoolObject.
|
|
* @param pool PoolObject containing the ABC file to
|
|
* execute
|
|
* @param toplevel the Toplevel object to execute against.
|
|
* @param codeContext CodeContext to use for execution (implicitly specifies DomainEnv)
|
|
* @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,
|
|
Toplevel* toplevel,
|
|
CodeContext *codeContext);
|
|
|
|
template<class CLASSMANIFEST>
|
|
CLASSMANIFEST* prepareBuiltinActionPool(PoolObject* pool,
|
|
Toplevel* toplevel,
|
|
CodeContext* codeContext);
|
|
|
|
/**
|
|
* 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,
|
|
ApiVersion apiVersion,
|
|
Stringp name = NULL);
|
|
|
|
// This is equivalent to a call to parseActionBlock,
|
|
// followed by a call to the now-defunct prepareActionPool.
|
|
// Basically, it does everything handleActionBlock does
|
|
// *except* that it doesn't actually call the main entry
|
|
// point, but rather, returns it to the caller. (Needed
|
|
// by Flash in QueueAbcBuffer.)
|
|
ScriptEnv* prepareActionBlock(ScriptBuffer code,
|
|
Toplevel* toplevel,
|
|
Domain* domain,
|
|
CodeContext* codeContext,
|
|
ApiVersion apiVersion);
|
|
|
|
/**
|
|
* 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.
|
|
* @param ninit FIXME
|
|
* @param codeContext CodeContext to use for execution (implicitly specifies DomainEnv)
|
|
* @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,
|
|
Toplevel* toplevel,
|
|
const NativeInitializer* ninit,
|
|
CodeContext *codeContext,
|
|
ApiVersion apiVersion,
|
|
Stringp name = NULL);
|
|
|
|
#ifdef VMCFG_AOT
|
|
/**
|
|
* Parse and execute the ABC blocks linked into the runtime by the AOT
|
|
* compiler.
|
|
* @param toplevel the Toplevel object to execute against.
|
|
* @param codeContext CodeContext to use for execution (implicitly specifies DomainEnv)
|
|
* @throws Exception If an error occurs, an Exception object will
|
|
* be thrown using the AVM+ exceptions mechanism.
|
|
* Calls to handleAOT should be bracketed
|
|
* in TRY/CATCH.
|
|
*/
|
|
void handleAOT(Toplevel* toplevel, CodeContext* codeContext);
|
|
#endif
|
|
|
|
#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 toplevel the Toplevel object to execute against.
|
|
* @param ninit FIXME
|
|
* @param codeContext CodeContext to use for execution (implicitly specifies DomainEnv)
|
|
* @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,
|
|
Toplevel* toplevel,
|
|
const NativeInitializer* ninit,
|
|
CodeContext *codeContext,
|
|
ApiVersion apiVersion);
|
|
|
|
/**
|
|
* 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
|
|
|
|
/**
|
|
* When ABC is unloaded, we might not be able to free all associated
|
|
* data structures immediately without some help: the VM caches some
|
|
* information (eg TraitsBindings) that might prevent a timely collection.
|
|
* Calling this is a hint from the client that cached information should
|
|
* be flushed more aggressively to encourage faster collection of the discarded
|
|
* ABC. Note that it is safe to call this "too often", but doing so will hurt
|
|
* performance; you need call it only once per ABC unloading event.
|
|
*/
|
|
void handleAbcUnloaded();
|
|
|
|
/** 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
|
|
*/
|
|
static Atom FASTCALL compare(Atom lhs, Atom rhs);
|
|
|
|
/** Implementation of OP_strictequals */
|
|
static Atom FASTCALL 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);
|
|
|
|
/** Helper method; returns true if atom's type is float */
|
|
static bool isFloat(Atom atom);
|
|
|
|
/** Helper method; returns true if atom's type is float4 */
|
|
static bool isFloat4(Atom atom);
|
|
|
|
// removed, because it was being (erroneously) used to ask
|
|
// "will you fit in int32_t?", 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 Number or Float*/
|
|
static bool isNumberOrFloat(Atom atom);
|
|
|
|
/** Helper method; returns true if atom's type is Number or Float or Float4*/
|
|
static bool isNumeric(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 uint8_t *pc, const uint8_t *code_end, AbcOpcode opcode, ptrdiff_t off, PoolObject* pool);
|
|
#ifdef VMCFG_WORDCODE
|
|
void formatOpcode(PrintWriter& out, const uintptr_t *pc, const uintptr_t *code_end, WordOpcode opcode, ptrdiff_t off, PoolObject* pool);
|
|
void formatBits(PrintWriter& buffer, uint32_t bits);
|
|
#endif
|
|
static void formatMultiname(PrintWriter& out, uint32_t index, PoolObject* pool);
|
|
#endif
|
|
/** Constructor */
|
|
AvmCore(MMgc::GC *gc, ApiVersionSeries apiVersionSeries);
|
|
|
|
/** Destructor */
|
|
~AvmCore();
|
|
|
|
/**
|
|
* Convenience method to access the AvmCore associated
|
|
* the current thread. Performance may vary across platforms
|
|
* since the implementation depends on thread local storage.
|
|
*/
|
|
static AvmCore* getActiveCore();
|
|
|
|
/**
|
|
* 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. codeContextCreator will be used to create the
|
|
* CodeContext (so clients that subclass CodeContext can create one
|
|
* as appropriate).
|
|
*/
|
|
Toplevel* initToplevel(ICodeContextCreator& codeContextCreator);
|
|
|
|
/**
|
|
* If a client needs to subclass Toplevel, it should override this
|
|
* method to create the proper subclass. (AvmCore will never create a
|
|
* a Toplevel directly, it will use this as a factory method.)
|
|
*/
|
|
virtual Toplevel* createToplevel(AbcEnv* abcEnv) = 0;
|
|
|
|
/**
|
|
* If a client need to subclass BugCompatibility, it should override this
|
|
* method to create the proper subclass. (AvmCore will never create a
|
|
* a BugCompatibility directly, it will use this as a factory method.)
|
|
*/
|
|
virtual const BugCompatibility* createBugCompatibility(BugCompatibility::Version v);
|
|
|
|
/**
|
|
* Support for API versioning
|
|
*/
|
|
|
|
/**
|
|
* Add to the list of URIs that must be versioned for namespaces.
|
|
*
|
|
* @param uris NULL-terminated list of URIs that will need versioning
|
|
*/
|
|
void addVersionedURIs(char const* const* uris);
|
|
|
|
/**
|
|
* Get the AVM wide default API version.
|
|
*/
|
|
virtual ApiVersion getDefaultAPI() = 0;
|
|
|
|
/**
|
|
* Find the ApiVersion from the first non-builtin caller on the AS3 call stack.
|
|
* If the AS3 call stack is empty, return getDefaultAPI().
|
|
*/
|
|
ApiVersion getApiVersionFromCallStack();
|
|
|
|
/**
|
|
* If the given Namespace has a version that allows it to be used by
|
|
* the ApiVersion active on the call stack, return true; otherwise return false.
|
|
* This is equivalent to ns->getApiVersion() <= getApiVersionFromCallStack(),
|
|
* but it's easy for client code to get the comparison reversed, so this is
|
|
* useful syntactic sugar.
|
|
*/
|
|
bool isNamespaceVisibleToApiVersionFromCallStack(Namespace* ns);
|
|
|
|
/**
|
|
* 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 a public namespace that will match with any/all ApiVersions.
|
|
*/
|
|
Namespacep getAnyPublicNamespace();
|
|
|
|
/**
|
|
* Get the public namespace associated with a specific ApiVersion.
|
|
*/
|
|
Namespacep getPublicNamespace(ApiVersion apiVersion);
|
|
|
|
/**
|
|
* If the ApiVersion is valid for the current ApiVersionSeries, return true.
|
|
* Otherwise, return false.
|
|
*/
|
|
bool isValidApiVersion(ApiVersion apiVersion);
|
|
|
|
/**
|
|
* If the ApiVersion is not valid for the current ApiVersionSeries, return the
|
|
* ApiVersion that is the smallest valid superset. (This might be VM_INTERNAL)
|
|
*/
|
|
ApiVersion getValidApiVersion(ApiVersion apiVersion);
|
|
|
|
/**
|
|
* 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
|
|
*/
|
|
static int32_t integer(Atom atom);
|
|
|
|
#ifdef VMCFG_FLOAT
|
|
/**
|
|
* Converts the passed atom to a 32-bit float number.
|
|
* If the atom is already an float, it is simply
|
|
* decoded. Otherwise, it is coerced to the float type
|
|
* and returned.
|
|
*/
|
|
static float singlePrecisionFloat(Atom atom);
|
|
|
|
/**
|
|
* Converts the passed atom to 4 32-bit float numbers (a float4_t).
|
|
* If the atom is already an float4, it is simply
|
|
* decoded. Otherwise, it is coerced to the float4 type
|
|
* and returned.
|
|
*/
|
|
static void float4(float4_t* retval, Atom atom);
|
|
#define float4_decl_v(var) float4_t var##v; AvmCore::float4(&var##v,var);
|
|
#endif
|
|
|
|
// 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);
|
|
#ifdef VMCFG_FLOAT
|
|
Atom floatToAtom(float n);
|
|
Atom float4ToAtom(const float4_t& n);
|
|
#endif
|
|
|
|
#if defined (AVMPLUS_IA32) || defined(AVMPLUS_AMD64)
|
|
static int integer_d_sse2(double d);
|
|
Atom doubleToAtom_sse2(double n);
|
|
#endif
|
|
|
|
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);
|
|
Atom floatAtom(Atom atom);
|
|
Atom float4Atom(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);
|
|
|
|
Namespacep cloneNamespace(Namespacep ns);
|
|
|
|
|
|
/** Helper function; reads a signed 24-bit integer from pc */
|
|
static int readS24(const uint8_t *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 uint8_t* 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 uint8_t* &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.
|
|
*/
|
|
static uint32_t readU32(const uint8_t *&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 U32 and don't care about the result,
|
|
// this is slightly faster.
|
|
static void skipU32(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 uint8_t *pc);
|
|
|
|
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_inclocal, declocal, increment, decrement which correspond to
|
|
* ++ and -- operators in AS.
|
|
*
|
|
* This function is now used only on the interpreter slowpath, and has been restricted
|
|
* to apply only to kIntptrType and kDoubleType atoms.
|
|
*/
|
|
Atom increment_number_d(Atom atom, int delta);
|
|
|
|
/**
|
|
* implementation of OP_inclocal_i, declocal_i, increment_i, decrement_i which correspond to
|
|
* ++ and -- operators in AS.
|
|
*/
|
|
Atom 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);
|
|
|
|
/** AS3 ToNumeric */
|
|
Atom numericAtom(Atom atom);
|
|
|
|
// actual implementation for numberAtom/numericAtom - avoids code duplication
|
|
template<bool> Atom numericAtomImpl(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 host if the current interrupt should be cleared
|
|
*/
|
|
void clearInterrupt();
|
|
|
|
/**
|
|
* 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 unless it's a safepoint poll interrupt.
|
|
*/
|
|
static void handleInterruptMethodEnv(MethodEnv*);
|
|
|
|
/**
|
|
* called by AS3 code when the interrupt is detected. Must
|
|
* not return unless it's a safepoint poll interrupt.
|
|
*/
|
|
static void handleInterruptToplevel(Toplevel*, bool canUnwindStack);
|
|
|
|
/**
|
|
* 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);
|
|
|
|
#ifndef VMCFG_DEBUGGER_STUB
|
|
/**
|
|
* findErrorMessage searches an error messages table.
|
|
* Only available in debugger builds.
|
|
*/
|
|
String* findErrorMessage(int errorID,
|
|
int* mapTable,
|
|
const char** errorTable,
|
|
int numErrors);
|
|
#endif
|
|
|
|
/**
|
|
* Determines the language id of the given platform
|
|
*/
|
|
virtual int determineLanguage();
|
|
|
|
#ifdef _DEBUG
|
|
void dumpStackTrace();
|
|
#endif
|
|
|
|
#endif /* DEBUGGER */
|
|
|
|
/**
|
|
* Creates a StackTrace from the current executing call stack
|
|
*/
|
|
StackTrace* newStackTrace();
|
|
|
|
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);
|
|
|
|
/**
|
|
* 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,
|
|
intptr_t 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. Also, sets *ordinal to the index of
|
|
* the handler in the exception handler table, if a handler is
|
|
* found.
|
|
*/
|
|
ExceptionHandler* findExceptionHandlerNoRethrow(MethodInfo *info,
|
|
intptr_t pc,
|
|
Exception *exception,
|
|
int32_t *ordinal);
|
|
|
|
/**
|
|
* 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);
|
|
|
|
private:
|
|
static bool isBuiltinType(Atom atm, BuiltinType bt);
|
|
static bool isBuiltinTypeMask(Atom atm, int btmask);
|
|
|
|
public:
|
|
|
|
static Namespacep atomToNamespace(Atom atom);
|
|
|
|
static double atomToDouble(Atom atom);
|
|
#ifdef VMCFG_FLOAT
|
|
static float atomToFloat (Atom atom);
|
|
static float4_t atomToFloat4(Atom atom);
|
|
#endif //
|
|
|
|
/**
|
|
* 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,
|
|
// 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);
|
|
int findStringWithIndex(uint32_t value);
|
|
|
|
int numStringsCheckLoadBalance();
|
|
|
|
/** search the namespace intern table */
|
|
int findNamespace(Namespacep ns, bool canRehash = true);
|
|
|
|
Namespacep gotNamespace(uintptr_t uriAndType, ApiVersion apiVersion);
|
|
|
|
public:
|
|
/**
|
|
* If the uri is versioned, set apiVersion to the version and strip the uri string, and return true.
|
|
* If the uri is unversioned, leave uri and apiVersion unchanged and return false.
|
|
*/
|
|
bool getBaseURIAndApiVersion(Stringp& uri, ApiVersion& apiVersion);
|
|
|
|
Namespacep getVersionedNamespace(Namespacep ns, ApiVersion apiVersion);
|
|
|
|
bool isVersionedURI(Stringp uri);
|
|
|
|
/**
|
|
* Convert a version name to a version enum
|
|
*/
|
|
static bool parseApiVersion(const char* p, ApiVersion& apiVersion, ApiVersionSeries& apiVersionSeries);
|
|
|
|
// String creation. If len is omitted, zero-termination is assumed.
|
|
Stringp newStringLatin1(const char* str, int len = -1);
|
|
/**
|
|
* Create a string out of UTF-8 data. WARNING: If strict is true,
|
|
* the return value may be NULL. Callers must check for this condition
|
|
* to avoid crashes due to malformed UTF-8 character sequences!
|
|
* If strict is false, malformed UTF-8 sequences are copied character
|
|
* by character.
|
|
*/
|
|
Stringp newStringUTF8(const char* str, int len = -1, bool strict = false);
|
|
/**
|
|
* Create a string out of UTF-16 data. WARNING: If strict is true,
|
|
* the return value may be NULL. Callers must check for this condition
|
|
* to avoid crashes due to malformed UTF-16 surrogate pairs!
|
|
* If strict is false, malformed UTF-16 surrogate pairs are copied as
|
|
* two characters.
|
|
*/
|
|
Stringp newStringUTF16(const wchar* str, int len = -1, bool strict = false);
|
|
|
|
// decodes UTF16LE or UTF16BE. Same restriction as newStringUTF16().
|
|
Stringp newStringEndianUTF16(bool littleEndian, const wchar* str, int len = -1, bool strict = false);
|
|
|
|
// 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, bool strict = true);
|
|
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 internForeignString(Stringp s);
|
|
Stringp internString(Atom atom);
|
|
Stringp internInt(int n);
|
|
#ifdef VMCFG_FLOAT
|
|
Stringp internFloat(float f);
|
|
Stringp internFloat4(const float4_t& f);
|
|
#endif
|
|
Stringp internDouble(double d);
|
|
Stringp internUint32(uint32_t ui);
|
|
|
|
/* true return value implies *result set to interned string for ui;
|
|
* false return value implies ui is not in intern table. */
|
|
bool isInternedUint(uint32_t ui, Stringp *result);
|
|
|
|
Stringp internSubstring(Stringp s, int32_t start, int32_t end);
|
|
|
|
Stringp internIntSlowPath(int value);
|
|
|
|
Stringp cloneString(Stringp other);
|
|
|
|
#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_t *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, ApiVersion apiVersion);
|
|
// Like newNamespace(), but versioned such that it will match all versions of the namespace
|
|
// with the same uri/type. This is useful for glue code that (e.g.) needs to look up specific
|
|
// Bindings in the builtins. (Analogous to "getAnyPublicNamespace" for arbitrary names.)
|
|
Namespacep newAnyNamespace(Stringp uri, Namespace::NamespaceType type);
|
|
Namespacep newPublicNamespace(Stringp uri);
|
|
|
|
Stringp uintToString(uint32_t i);
|
|
Stringp intToString(int i);
|
|
Stringp doubleToString(double d);
|
|
#ifdef VMCFG_FLOAT
|
|
Stringp floatToString(float f);
|
|
Stringp float4ToString(const float4_t& f);
|
|
#endif
|
|
Stringp concatStrings(Stringp s1, Stringp s2);
|
|
|
|
Atom uintToAtom(uint32_t n);
|
|
Atom intToAtom(int32_t n);
|
|
|
|
Atom allocDouble(double n);
|
|
#ifdef VMCFG_FLOAT
|
|
Atom allocFloat(float n);
|
|
Atom allocFloat4(const float4_t& n);
|
|
#endif
|
|
|
|
void rehashStrings(int newlen);
|
|
void rehashNamespaces(int newlen, bool canFail=false);
|
|
void rehashNamespacesIfPossible(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);
|
|
|
|
// fills the area with zero
|
|
static void decrementAtomRegion(Atom *ar, int length);
|
|
// fills the area with nullObjectAtom
|
|
static void decrementAtomRegion_null(Atom *ar, int length);
|
|
|
|
#ifdef VMCFG_NANOJIT
|
|
public:
|
|
void flushBindingCachesNextSweep();
|
|
#endif
|
|
|
|
#ifdef VMCFG_TELEMETRY
|
|
public:
|
|
telemetry::ITelemetry* getTelemetry();
|
|
protected:
|
|
void setTelemetry(telemetry::ITelemetry* telemetry);
|
|
private:
|
|
telemetry::ITelemetry* m_telemetry; // Owned by the AvmCore subclass which calls SetTelemetry
|
|
#endif
|
|
|
|
public:
|
|
void setIsolate(Isolate* isolate);
|
|
Isolate* getIsolate() const;
|
|
virtual int getIsolateDesc () const;
|
|
virtual int evaluateScriptBuffer(ScriptBuffer& buffer, bool enter_debugger_on_enter);
|
|
|
|
|
|
// END methods (private/public intermixed)
|
|
////////////////////////////////////////////////////////////////////
|
|
};
|
|
|
|
class Deoptimizer;
|
|
|
|
/*
|
|
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;
|
|
#ifdef VMCFG_HALFMOON
|
|
bool isInterp; // TODO: We need a more efficient way to represent this.
|
|
#ifdef DEBUG
|
|
Deoptimizer* deopt; // NOTE: This is used only to support an assertion.
|
|
#endif
|
|
#endif
|
|
|
|
private:
|
|
friend class CodegenLIR;
|
|
friend class OSR;
|
|
friend class halfmoon::JitFriend;
|
|
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 avmStackAlloc, which is just as good), so no GCMember needed
|
|
};
|
|
|
|
// NOTE: this is a temporary fix until OOM handling hooks in to Avm exceptions generally;
|
|
// see https://bugzilla.mozilla.org/show_bug.cgi?id=611078
|
|
#ifdef DEBUGGER
|
|
class AvmCoreAutoEnter : public MMgc::AbortUnwindObject
|
|
{
|
|
public:
|
|
AvmCoreAutoEnter(AvmCore* core);
|
|
~AvmCoreAutoEnter();
|
|
virtual void Unwind();
|
|
private:
|
|
MMgc::EnterFrame* m_ef;
|
|
AvmCore* m_savedCore;
|
|
CallStackNode* m_savedCallStack;
|
|
};
|
|
#define AVMCORE_ENTER(c) avmplus::AvmCoreAutoEnter __avmcore_auto_enter(c);
|
|
#else
|
|
#define AVMCORE_ENTER(c)
|
|
#endif
|
|
|
|
}
|
|
|
|
#endif /* __avmplus_AvmCore__ */
|