Initial source code drop

Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
This commit is contained in:
dmcallis committed 2013-12-02 16:04:04 -08:00
1 parent c414dd9af4
commit 65a0592776
8909 files changed
+1776458 -249

No files matched your search

+638
View File
@@ -0,0 +1,638 @@
/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "avmshell.h"
#include "shell_toplevel-classes.hh"
#include "shell_toplevel.cpp"
namespace avmshell
{
const int kScriptTimeout = 15;
const int kScriptGracePeriod = 5;
ShellCoreSettings::ShellCoreSettings()
: arguments(NULL)
, numargs(-1)
, nodebugger(false)
, astrace_console(0)
, do_verbose(0)
, verboseOnlyArg(NULL)
, enter_debugger_on_launch(false)
, interrupts(avmplus::AvmCore::interrupts_default)
, verifyall(avmplus::AvmCore::verifyall_default)
, verifyonly(avmplus::AvmCore::verifyonly_default)
, verifyquiet(avmplus::AvmCore::verifyquiet_default)
, greedy(false)
, nogc(false)
, incremental(true)
, exactgc(true)
, drc(true)
, drcValidation(false)
, markstackAllowance(0)
, fixedcheck(true)
, gcthreshold(0)
, langID(-1)
, jitordie(avmplus::AvmCore::jitordie_default)
, do_testSWFHasAS3(false)
, runmode(avmplus::AvmCore::runmode_default)
#ifdef VMCFG_NANOJIT
, njconfig()
, jitconfig()
, osr_enabled(avmplus::AvmCore::osr_enabled_default)
, osr_threshold(avmplus::AvmCore::osr_threshold_default)
, jitprof_level(avmplus::AvmCore::jitprof_level_default)
, policyRulesArg(NULL)
#endif
, st_component(NULL)
, st_category(NULL)
, st_name(NULL)
, apiVersionSeries(avmplus::kApiVersionSeries_FP)
, apiVersion(avmplus::kApiVersionLatest[avmplus::kApiVersionSeries_FP])
, swfVersion(avmplus::BugCompatibility::kLatest)
{
}
ShellToplevel::ShellToplevel(avmplus::AbcEnv* abcEnv) : avmplus::Toplevel(abcEnv)
{
}
ShellCore::ShellCore(MMgc::GC* gc, avmplus::ApiVersionSeries apiVersionSeries)
: avmplus::AvmCore(gc, apiVersionSeries)
{
systemClass = NULL;
gracePeriod = false;
inStackOverflow = false;
allowDebugger = -1; // aka "not yet set"
consoleOutputStream = new (gc) ConsoleOutputStream(this);
setConsoleStream(consoleOutputStream);
}
void ShellCore::stackOverflow(avmplus::Toplevel* toplevel)
{
if (inStackOverflow)
{
// Already handling a stack overflow, so do not
// re-enter handler.
return;
}
// Temporarily disable stack overflow checks
// so that we can construct an exception object.
// There should be plenty of margin before the
// actual stack bottom to do this.
inStackOverflow = true;
avmplus::Stringp errorMessage = getErrorMessage(kStackOverflowError);
GCRef<avmplus::ErrorObject> error = toplevel->errorClass()->constructObject(errorMessage->atom(), this->intToAtom(0));
avmplus::Exception *exception = new (GetGC()) avmplus::Exception(this, error->atom());
// Restore stack overflow checks
inStackOverflow = false;
// Throw the stack overflow exception
throwException(exception);
}
/* static */
void ShellCore::interruptTimerCallback(void* data)
{
((avmplus::AvmCore*)data)->raiseInterrupt(ScriptTimeout);
}
void ShellCore::interrupt(avmplus::Toplevel *toplevel, InterruptReason)
{
if (gracePeriod) {
// This script has already had its chance; it violated
// the grace period.
// Throw an exception it cannot catch.
avmplus::Stringp errorMessage = getErrorMessage(kScriptTerminatedError);
GCRef<avmplus::ErrorObject> error = toplevel->errorClass()->constructObject(errorMessage->atom(), this->intToAtom(0));
avmplus::Exception *exception = new (GetGC()) avmplus::Exception(this, error->atom());
exception->flags |= avmplus::Exception::EXIT_EXCEPTION;
throwException(exception);
}
// Give the script an additional grace period to
// clean up, and throw an exception.
gracePeriod = true;
Platform::GetInstance()->setTimer(kScriptGracePeriod, interruptTimerCallback, this);
toplevel->throwError(kScriptTimeoutError);
}
void ShellCore::initShellPool()
{
shell_domain = avmplus::Domain::newDomain(this, builtinDomain);
#ifdef VMCFG_AOT
avmplus::NativeInitializer shellNInit(this,
avmplus::NativeID::shell_toplevel_versioned_uris,
&shell_toplevel_aotInfo,
avmplus::NativeID::shell_toplevel_abc_method_count,
avmplus::NativeID::shell_toplevel_abc_class_count);
shellNInit.fillInClasses(avmplus::NativeID::shell_toplevel_classEntries);
shellNInit.fillInMethods(avmplus::NativeID::shell_toplevel_methodEntries);
shellPool = shellNInit.parseBuiltinABC(shell_domain);
#else
shellPool = AVM_INIT_BUILTIN_ABC_IN_DOMAIN(shell_toplevel, this, shell_domain);
#endif
}
ShellToplevel* ShellCore::createShellToplevel()
{
class CodeContextCreator : public avmplus::AvmCore::ICodeContextCreator
{
public:
MMgc::GC* gc;
explicit CodeContextCreator(MMgc::GC* _gc) : gc(_gc)
{
}
virtual avmplus::CodeContext* create(avmplus::DomainEnv* domainEnv, const avmplus::BugCompatibility* bugCompatibility)
{
return new (gc) ShellCodeContext(domainEnv, bugCompatibility);
}
};
// Initialize a new Toplevel. This will also create a new
// DomainEnv based on the builtinDomain.
CodeContextCreator ccc(GetGC());
ShellToplevel* shell_toplevel = (ShellToplevel*)initToplevel(ccc);
avmplus::DomainEnv* builtinDomainEnv = shell_toplevel->domainEnv();
AvmAssert(builtinDomainEnv->domain() == builtinDomain);
AvmAssert(builtinDomainEnv->base() == NULL);
shell_domainEnv = avmplus::DomainEnv::newDomainEnv(this, shell_domain, builtinDomainEnv);
// Initialize the shell builtins in the new Toplevel
// use the same bugCompatibility that the base builtins use
const avmplus::BugCompatibility* shell_bugCompatibility = shell_toplevel->abcEnv()->codeContext()->bugCompatibility();
ShellCodeContext* shell_codeContext = new(GetGC()) ShellCodeContext(shell_domainEnv, shell_bugCompatibility);
//handleActionPool(shellPool, shell_toplevel, shell_codeContext);
shell_toplevel->shellClasses = prepareBuiltinActionPool<avmplus::shell_toplevelClassManifest>(shellPool, shell_toplevel, shell_codeContext);
#ifdef VMCFG_VERIFYALL
if (!config.verifyonly)
#endif
{
// these are here solely to verify the classes are constructable from C++
// (they are not constructable from AS3)
shell_toplevel->shellClasses->get_NativeBaseClass()->constructObject();
shell_toplevel->shellClasses->get_NativeBaseAS3Class()->constructObject();
// test "friend" metadata
AvmAssert(shell_toplevel->shellClasses->get_ShellCoreFriend1Class()->get_foo() == 42);
AvmAssert(shell_toplevel->shellClasses->get_ShellCoreFriend2Class()->get_bar() == 101);
}
return shell_toplevel;
}
/*virtual*/ avmplus::Toplevel* ShellCore::createToplevel(avmplus::AbcEnv* abcEnv)
{
return ShellToplevel::create(GetGC(), abcEnv);
}
#ifdef VMCFG_EVAL
// FIXME, this is currently hokey for several reasons:
//
// - Does not try to determine whether input is Latin1, UTF8, or indeed, already UTF16,
// but assumes UTF8, which can be dangerous. Falls back to latin1 if the utf8 conversion
// fails, this seems ill-defined in the string layer though so it's just one more hack.
//
// - Does not create an UTF16 string. The string layer is actually broken on this count,
// because requesting an empty UTF16 string returns a constant that is a Latin1 string,
// and appending to it won't force the representation to UTF16 unless the data require
// that to happen. See <URL:https://bugzilla.mozilla.org/show_bug.cgi?id=473995>.
//
// - May incur copying because the terminating NUL is not accounted for in the original
// creation
//
// Much useful guidance is found at http://www.unicode.org/faq/utf_bom.html.
avmplus::String* ShellCore::decodeBytesAsUTF16String(uint8_t* bytes, uint32_t nbytes, bool terminate)
{
avmplus::String* s = newStringUTF8((const char*)bytes, nbytes);
if (s == NULL)
s = newStringLatin1((const char*)bytes, nbytes);
if (terminate)
s = s->appendLatin1("\0", 1);
return s;
}
avmplus::String* ShellCore::readFileForEval(avmplus::String* referencingFile, avmplus::String* filename)
{
// FIXME, filename sanitazion is more complicated than this
if (referencingFile != NULL && filename->charAt(0) != '/' && filename->charAt(0) != '\\') {
// find the last slash if any, truncate the string there, append the
// new filename
int32_t x = referencingFile->lastIndexOf(newStringLatin1("/"));
if (x != -1)
filename = referencingFile->substring(0,x+1)->append(filename);
}
filename = filename->appendLatin1("\0", 1);
// FIXME, not obvious that UTF8 is correct for all operating systems (far from it!)
avmplus::StUTF8String fn(filename);
FileInputStream f(fn.c_str());
if (!f.valid() || (uint64_t) f.length() >= UINT32_T_MAX)
return NULL;
uint32_t nbytes = (uint32_t) f.available();
uint8_t* bytes = new uint8_t[nbytes];
f.read(bytes, nbytes);
avmplus::String* str = decodeBytesAsUTF16String(bytes, nbytes, true);
delete [] bytes;
return str;
}
// input is always NUL-terminated
void ShellCore::evaluateString(avmplus::String* input, bool record_time)
{
setStackLimit();
TRY(this, avmplus::kCatchAction_ReportAsError)
{
// Always Latin-1 here
input = input->appendLatin1("\0", 1);
double then = 0, now = 0;
if (record_time)
then = VMPI_getDate();
avmplus::ApiVersion apiVersion = this->getApiVersionFromCallStack();
avmplus::Atom result = handleActionSource(input, /*filename*/NULL, shell_toplevel, /*ninit*/NULL, user_codeContext, apiVersion);
if (record_time)
now = VMPI_getDate();
if (result != avmplus::undefinedAtom)
console << string(result) << "\n";
if (record_time)
console << "Elapsed time: " << (now - then)/1000 << "s\n";
}
CATCH(avmplus::Exception *exception)
{
#ifdef DEBUGGER
if (!(exception->flags & avmplus::Exception::SEEN_BY_DEBUGGER))
{
console << string(exception->atom) << "\n";
}
if (exception->getStackTrace()) {
console << exception->getStackTrace()->format(this) << '\n';
}
#else
console << string(exception->atom) << "\n";
#endif
}
END_CATCH
END_TRY
}
#endif // VMCFG_EVAL
#ifdef AVMSHELL_PROJECTOR_SUPPORT
// Run a known projector file
int ShellCore::executeProjector(char *executablePath)
{
AvmAssert(isValidProjectorFile(executablePath));
uint8_t header[8];
FileInputStream file(executablePath);
file.seek(file.length() - 8);
file.read(header, 8);
int abcLength = (header[4] |
header[5]<<8 |
header[6]<<16 |
header[7]<<24);
avmplus::ScriptBuffer code = newScriptBuffer(abcLength);
file.seek(file.length() - 8 - abcLength);
file.read(code.getBuffer(), abcLength);
ShellCoreSettings settings;
return handleArbitraryExecutableContent(settings.do_testSWFHasAS3, code, executablePath);
}
/* static */
bool ShellCore::isValidProjectorFile(const char* filename)
{
FileInputStream file(filename);
uint8_t header[8];
if (!file.valid())
return false;
file.seek(file.length() - 8);
file.read(header, 8);
// Check the magic number
if (header[0] != 0x56 || header[1] != 0x34 || header[2] != 0x12 || header[3] != 0xFA)
return false;
return true;
}
#endif // AVMSHELL_PROJECTOR_SUPPORT
#ifdef VMCFG_SELFTEST
void ShellCore::executeSelftest(ShellCoreSettings& settings)
{
setStackLimit();
avmplus::selftests(this, settings.st_component, settings.st_category, settings.st_name);
}
#endif
ShellToplevel* ShellCore::setup(const ShellCoreSettings& settings)
{
#ifdef VMCFG_AOT
if(nAOTInfos == 0) {
console << "To run an AOT enabled avmshell you must link in some AOT compiled ABC blocks.\n";
return NULL;
}
#endif
// set the default api version
this->defaultAPIVersion = settings.apiVersion;
this->defaultBugCompatibilityVersion = settings.swfVersion;
this->bugzilla444630 = (this->defaultBugCompatibilityVersion >= avmplus::BugCompatibility::kSWF10);
// This is obscure but well-defined: the clearing of this flag is allowed
// at any time, see comment for checkFixedMemory in GCHeap.h.
if (!settings.fixedcheck)
MMgc::GCHeap::GetGCHeap()->Config().clearCheckFixedMemory();
config.interrupts = settings.interrupts;
#ifdef VMCFG_VERIFYALL
config.verifyall = settings.verifyall;
config.verifyonly = settings.verifyonly;
#endif
config.jitordie = settings.jitordie;
#ifdef VMCFG_NANOJIT
config.njconfig = settings.njconfig;
config.jitconfig = settings.jitconfig;
config.osr_threshold = settings.osr_threshold;
config.jitprof_level = settings.jitprof_level;
config.compilePolicyRules = settings.policyRulesArg;
#endif
#ifdef AVMPLUS_VERBOSE
config.verboseOnlyString = settings.verboseOnlyArg;
if (settings.do_verbose & avmplus::VB_builtins)
config.verbose_vb = settings.do_verbose; // ~builtins then skip verbose settings during setup()
#endif
config.runmode = settings.runmode;
// verbose requires methodnames (in avmshell, anyway), before calling initBuiltinPool.
if (settings.do_verbose)
config.methodNames = true;
#ifdef DEBUGGER
// debugger in avmshell always enables methodnames.
if (allowDebugger)
config.methodNames = true;
#endif
#ifdef DEBUGGER
langID = settings.langID;
#endif
TRY(this, avmplus::kCatchAction_ReportAsError)
{
setStackLimit();
allowDebugger = !settings.nodebugger;
setCacheSizes(settings.cacheSizes);
SystemClass::user_argc = settings.numargs;
SystemClass::user_argv = settings.arguments;
#ifdef DEBUGGER
initBuiltinPool((avmplus::Debugger::TraceLevel)settings.astrace_console);
#else
initBuiltinPool();
#endif
initShellPool();
// init toplevel internally
shell_toplevel = createShellToplevel();
// Create a new Domain/DomainEnv for the user code
avmplus::Domain* user_domain = avmplus::Domain::newDomain(this, shell_domain);
avmplus::DomainEnv* user_domainEnv = avmplus::DomainEnv::newDomainEnv(this, user_domain, shell_domainEnv);
const avmplus::BugCompatibility* user_bugCompatibility = createBugCompatibility(defaultBugCompatibilityVersion);
this->user_codeContext = new (GetGC()) ShellCodeContext(user_domainEnv, user_bugCompatibility);
#ifdef AVMPLUS_VERBOSE
config.verbose_vb = settings.do_verbose; // builtins is done, so propagate verbose
#endif
return shell_toplevel;
}
CATCH(avmplus::Exception *exception)
{
#ifdef DEBUGGER
if (!(exception->flags & avmplus::Exception::SEEN_BY_DEBUGGER))
console << string(exception->atom) << "\n";
if (exception->getStackTrace())
console << exception->getStackTrace()->format(this) << '\n';
#else
// [ed] always show error, even in release mode,
// see bug #121382
console << string(exception->atom) << "\n";
#endif /* DEBUGGER */
return NULL;
}
END_CATCH
END_TRY
}
#ifdef AVMPLUS_VERBOSE
const char* ShellCore::identifyDomain(avmplus::Domain* domain)
{
return domain == builtinDomain ? "builtin" : (domain == shell_domain ? "shell" : NULL);
}
#endif
int ShellCore::evaluateFile(ShellCoreSettings& settings, const char* filename)
{
#ifdef VMCFG_AOT
avmplus::ScriptBuffer dummyScriptBuffer;
return handleArbitraryExecutableContent(settings.do_testSWFHasAS3, dummyScriptBuffer, NULL);
#endif
if (config.interrupts)
Platform::GetInstance()->setTimer(kScriptTimeout, interruptTimerCallback, this);
#ifdef AVMPLUS_VERBOSE
if (config.verbose_vb)
console << "run " << filename << "\n";
#endif
FileInputStream f(filename);
bool isValid = f.valid() && ((uint64_t)f.length() < UINT32_T_MAX); //currently we cannot read files > 4GB
if (!isValid) {
console << "cannot open file: " << filename << "\n";
return(1);
}
// parse new bytecode
avmplus::ScriptBuffer code = newScriptBuffer((size_t)f.available());
f.read(code.getBuffer(), (size_t)f.available());
#ifdef DEBUGGER
if (settings.enter_debugger_on_launch)
{
// Activate the debug CLI and stop at
// start of program
debugCLI()->activate();
debugCLI()->stepInto();
}
#else
// placate MSVC - settings is unreferenced if this is not here
(void)settings.enter_debugger_on_launch;
#endif
return handleArbitraryExecutableContent(settings.do_testSWFHasAS3, code, filename);
}
int ShellCore::evaluateScriptBuffer(avmplus::ScriptBuffer& buffer, bool enter_debugger_on_launch)
{
(void)enter_debugger_on_launch;
// FIXME
if (config.interrupts)
Platform::GetInstance()->setTimer(kScriptTimeout, interruptTimerCallback, this);
#ifdef DEBUGGER
if (enter_debugger_on_launch)
{
// Activate the debug CLI and stop at
// start of program
debugCLI()->activate();
debugCLI()->stepInto();
}
#endif
return handleArbitraryExecutableContent(false, buffer, "<ByteArray buffer>");
}
int ShellCore::handleArbitraryExecutableContent(bool do_testSWFHasAS3, avmplus::ScriptBuffer& code, const char * filename)
{
setStackLimit();
int exitCode = 1;
TRY(this, avmplus::kCatchAction_ReportAsError)
{
if (do_testSWFHasAS3 && !isSwf(code))
return 1;
#ifdef VMCFG_AOT
if (filename == NULL) {
handleAOT(shell_toplevel, user_codeContext);
} else
#endif
if (avmplus::AbcParser::canParse(code) == 0) {
#ifdef VMCFG_VERIFYALL
if (config.verbose_vb & avmplus::VB_verify)
console << "ABC " << filename << "\n";
#endif
avmplus::ApiVersion apiVersion = this->getApiVersionFromCallStack();
handleActionBlock(code, 0, shell_toplevel, NULL, user_codeContext, apiVersion);
}
else if (isSwf(code)) {
#ifdef VMCFG_VERIFYALL
if (config.verbose_vb & avmplus::VB_verify)
console << "SWF " << filename << "\n";
#endif
bool result = handleSwf(filename, code, shell_toplevel, user_codeContext, do_testSWFHasAS3);
if (do_testSWFHasAS3)
return int(!result); // "no abc" is "false" but translates to "1" for the exit code, and "true" becomes "0"
}
else {
#ifdef VMCFG_EVAL
AvmCore* core = shell_toplevel->core();
if (!core->config.verifyonly) {
// FIXME: I'm assuming code is UTF8 - OK for now, but easy to go wrong; it could be 8-bit ASCII
avmplus::String* code_string = decodeBytesAsUTF16String(code.getBuffer(), (uint32_t)code.getSize(), true);
avmplus::String* filename_string = decodeBytesAsUTF16String((uint8_t*)filename, (uint32_t)VMPI_strlen(filename));
avmplus::ScriptBuffer empty; // With luck: allow the
code = empty; // buffer to be garbage collected
avmplus::ApiVersion apiVersion = this->getApiVersionFromCallStack();
handleActionSource(code_string, filename_string, shell_toplevel, NULL, user_codeContext, apiVersion);
}
#else
console << "unknown input format in file: " << filename << "\n";
return(1);
#endif // VMCFG_EVAL
}
}
CATCH(avmplus::Exception *exception)
{
if (!(exception->flags & avmplus::Exception::EXIT_EXCEPTION)) {
// uncaught ordinary exception
getIsolate()->getAggregate()->stateTransition(getIsolate(), avmplus::Isolate::EXCEPTION);
}
TRY(this, avmplus::kCatchAction_ReportAsError)
{
if (!(exception->flags & avmplus::Exception::SUPPRESS_ERROR_REPORT)) {
#ifdef DEBUGGER
if (!(exception->flags & avmplus::Exception::SEEN_BY_DEBUGGER))
{
console << string(exception->atom) << "\n";
}
if (exception->getStackTrace()) {
console << exception->getStackTrace()->format(this) << '\n';
}
#else
// [ed] always show error, even in release mode,
// see bug #121382
console << string(exception->atom) << "\n";
#endif /* DEBUGGER */
} else {
avmplus::Atom payload = exception->atom;
if (atomKind(payload) == kObjectType) {
avmplus::ScriptObject* object = AvmCore::atomToScriptObject(payload);
if (object) {
avmplus::Atom code = object->getStringProperty(internStringLatin1("exitCode"));
if (atomKind(code) == kIntptrType) {
exitCode = (int)avmplus::atomGetIntptr(code);
}
}
}
}
}
CATCH(avmplus::Exception * e2)
{
(void)e2;
console << "Sorry, an exception occurred but could not be reported\n";
}
END_CATCH
END_TRY
return exitCode;
}
END_CATCH
END_TRY
return 0;
}
}