1177 lines
31 KiB
C++
1177 lines
31 KiB
C++
/* ***** BEGIN LICENSE BLOCK *****
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
*
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
* the License. You may obtain a copy of the License at
|
|
* http://www.mozilla.org/MPL/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is [Open Source Virtual Machine.].
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Adobe System Incorporated.
|
|
* Portions created by the Initial Developer are Copyright (C) 2004-2006
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Adobe AS3 Team
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
#include "avmshell.h"
|
|
|
|
#if defined(DARWIN) || defined(AVMPLUS_UNIX)
|
|
#include <sys/signal.h>
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
#ifdef WIN32
|
|
#include <Mmsystem.h>
|
|
#include "dbghelp.h"
|
|
bool P4Available();
|
|
#elif defined AVMPLUS_UNIX
|
|
bool P4Available();
|
|
#endif
|
|
|
|
static MMgc::FixedMalloc* fm = NULL;
|
|
|
|
#ifndef OVERRIDE_GLOBAL_NEW
|
|
// Custom new and delete operators
|
|
// User-defined operator new.
|
|
|
|
void *operator new(size_t size)
|
|
{
|
|
// 10.5 calls new before main
|
|
if (!fm)
|
|
{
|
|
MMgc::GCHeap::Init();
|
|
MMgc::FixedMalloc::Init();
|
|
|
|
fm = MMgc::FixedMalloc::GetInstance();
|
|
}
|
|
|
|
return fm->Alloc(size);
|
|
}
|
|
|
|
void *operator new[](size_t size)
|
|
{
|
|
// 10.5 calls new before main
|
|
if (!fm)
|
|
{
|
|
MMgc::GCHeap::Init();
|
|
MMgc::FixedMalloc::Init();
|
|
|
|
fm = MMgc::FixedMalloc::GetInstance();
|
|
}
|
|
|
|
return fm->Alloc(size);
|
|
}
|
|
|
|
// User-defined operator delete.
|
|
#ifdef _MAC
|
|
// CW9 wants the C++ official prototype, which means we must have an empty exceptions list for throw.
|
|
// (The fact exceptions aren't on doesn't matter.) - mds, 02/05/04
|
|
void operator delete( void *p) throw()
|
|
#else
|
|
void operator delete( void *p)
|
|
#endif
|
|
{
|
|
if (fm)
|
|
fm->Free(p);
|
|
}
|
|
|
|
#ifdef _MAC
|
|
// CW9 wants the C++ official prototype, which means we must have an empty exceptions list for throw.
|
|
// (The fact exceptions aren't on doesn't matter.) - mds, 02/05/04
|
|
void operator delete[]( void *p) throw()
|
|
#else
|
|
void operator delete[]( void *p )
|
|
#endif
|
|
{
|
|
if (fm)
|
|
fm->Free(p);
|
|
}
|
|
#endif // OVERRIDE_GLOBAL_NEW
|
|
|
|
|
|
namespace avmshell
|
|
{
|
|
#include "toplevel.cpp"
|
|
|
|
const int kScriptTimeout = 15;
|
|
const int kScriptGracePeriod = 5;
|
|
|
|
BEGIN_NATIVE_CLASSES(Shell)
|
|
NATIVE_CLASS(abcclass_avmplus_System, SystemClass, ScriptObject)
|
|
NATIVE_CLASS(abcclass_avmplus_File, FileClass, ScriptObject)
|
|
NATIVE_CLASS(abcclass_avmplus_Domain, DomainClass, DomainObject)
|
|
NATIVE_CLASS(abcclass_avmplus_StringBuilder, StringBuilderClass, StringBuilderObject)
|
|
NATIVE_CLASS(abcclass_avmplus_JObject, JObjectClass, JObject)
|
|
NATIVE_CLASS(abcclass_flash_utils_ByteArray, ByteArrayClass, ByteArrayObject)
|
|
NATIVE_CLASS(abcclass_flash_utils_ShortArray, ShortArrayClass, ShortArrayObject)
|
|
NATIVE_CLASS(abcclass_flash_utils_UShortArray, UShortArrayClass, UShortArrayObject)
|
|
NATIVE_CLASS(abcclass_flash_utils_IntArray, IntArrayClass, IntArrayObject)
|
|
NATIVE_CLASS(abcclass_flash_utils_UIntArray, UIntArrayClass, UIntArrayObject)
|
|
NATIVE_CLASS(abcclass_flash_utils_FloatArray, FloatArrayClass, FloatArrayObject)
|
|
NATIVE_CLASS(abcclass_flash_utils_DoubleArray, DoubleArrayClass, DoubleArrayObject)
|
|
NATIVE_CLASS(abcclass_flash_utils_Dictionary, DictionaryClass, DictionaryObject)
|
|
END_NATIVE_CLASSES()
|
|
|
|
BEGIN_NATIVE_SCRIPTS(Shell)
|
|
NATIVE_SCRIPT(0/*abcscript_avmplus_debugger*/, AvmplusScript)
|
|
END_NATIVE_SCRIPTS()
|
|
|
|
BEGIN_NATIVE_MAP(AvmplusScript)
|
|
END_NATIVE_MAP()
|
|
|
|
Shell *shell = NULL;
|
|
bool show_error = false;
|
|
|
|
#ifdef WIN32
|
|
void Shell::computeStackBase()
|
|
{
|
|
const int kStackMargin = 131072;
|
|
|
|
SYSTEM_INFO sysinfo;
|
|
GetSystemInfo(&sysinfo);
|
|
|
|
int dummy;
|
|
int sp = (int)(&dummy);
|
|
sp &= ~(sysinfo.dwPageSize-1);
|
|
|
|
MEMORY_BASIC_INFORMATION buf;
|
|
if (VirtualQuery((void*)sp, &buf, sizeof(buf)) == sizeof(buf)) {
|
|
minstack = (uint32)buf.AllocationBase + kStackMargin;
|
|
}
|
|
}
|
|
|
|
void CALLBACK TimeoutProc(UINT /*uTimerID*/,
|
|
UINT /*uMsg*/,
|
|
DWORD_PTR dwUser,
|
|
DWORD_PTR /*dw1*/,
|
|
DWORD_PTR /*dw2*/)
|
|
{
|
|
AvmCore *core = (AvmCore*)dwUser;
|
|
core->interrupted = true;
|
|
}
|
|
#else
|
|
void Shell::computeStackBase()
|
|
{
|
|
const int kMaxAvmPlusStack = 512*1024;
|
|
int sp;
|
|
#ifdef AVMPLUS_PPC
|
|
asm("mr %0,r1" : "=r" (sp));
|
|
#elif defined(AVMPLUS_ARM)
|
|
asm("mov %0,sp" : "=r" (sp));
|
|
#else
|
|
asm("movl %%esp,%0" : "=r" (sp));
|
|
#endif
|
|
minstack = sp-kMaxAvmPlusStack;
|
|
}
|
|
|
|
void alarmProc(int /*signum*/)
|
|
{
|
|
shell->interrupted = true;
|
|
}
|
|
#endif
|
|
|
|
void Shell::usage()
|
|
{
|
|
printf("avmplus shell " AVMPLUS_VERSION_USER " build " AVMPLUS_BUILD_CODE "\n\n");
|
|
printf("usage: avmplus\n");
|
|
#ifdef DEBUGGER
|
|
printf(" [-d] enter debugger on start\n");
|
|
#endif
|
|
#ifdef AVMPLUS_PROFILE
|
|
printf(" [-Ddprofile] dynamic instruction stats\n");
|
|
printf(" [-Dsprofile] show static instruction stats\n");
|
|
#endif /* AVMPLUS_PROFILE */
|
|
#ifdef _DEBUG
|
|
printf(" [-Dgreedy] collect before every allocation\n");
|
|
#endif /* _DEBUG */
|
|
#ifdef DEBUGGER
|
|
printf(" [-Dnogc] don't collect\n");
|
|
printf(" [-Dgcstats] generate statistics on gc\n");
|
|
printf(" [-Dnoincgc] don't use incremental collection\n");
|
|
printf(" [-Dastrace N] display AS execution information, where N is [1..4]\n");
|
|
#endif
|
|
|
|
#ifdef AVMPLUS_INTERP
|
|
printf(" [-Dinterp] do not generate machine code, interpret instead\n");
|
|
#endif /* AVMPLUS_INTERP */
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
printf(" [-Dverbose] trace every instruction (verbose!)\n");
|
|
printf(" [-Dbbgraph] output MIR basic block graphs for use with Graphviz\n");
|
|
#endif
|
|
|
|
#ifdef AVMPLUS_MIR
|
|
#ifdef AVMPLUS_INTERP
|
|
printf(" [-Dforcemir] use MIR always, never interp\n");
|
|
#endif /* AVMPLUS_INTERP */
|
|
|
|
printf(" [-Dnodce] disable DCE optimization \n");
|
|
printf(" [-Dnocse] disable CSE optimization \n");
|
|
|
|
#ifdef AVMPLUS_IA32
|
|
printf(" [-Dnosse] use FPU stack instead of SSE2 instructions\n");
|
|
#endif /* AVMPLUS_IA32 */
|
|
#endif
|
|
|
|
#ifdef AVMPLUS_VERIFYALL
|
|
printf(" [-Dverifyall] verify greedily instead of lazily\n");
|
|
#endif
|
|
|
|
printf(" [-Dtimeout] enforce maximum 15 seconds execution\n");
|
|
printf(" [-error] crash opens debug dialog, instead of dumping\n");
|
|
#ifdef AVMPLUS_INTERACTIVE
|
|
printf(" [-i] interactive mode\n");
|
|
#endif //AVMPLUS_INTERACTIVE
|
|
printf(" [-log]\n");
|
|
printf(" [-- args] args passed to AS3 program\n");
|
|
printf(" [-jargs ... ;] args passed to Java runtime\n");
|
|
printf(" filename.abc ...\n");
|
|
printf(" [--] application args\n");
|
|
exit(1);
|
|
}
|
|
|
|
void Shell::stackOverflow(MethodEnv *env)
|
|
{
|
|
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;
|
|
|
|
Toplevel *toplevel = env->vtable->toplevel;
|
|
|
|
Stringp errorMessage = getErrorMessage(kStackOverflowError);
|
|
Atom args[2] = { nullObjectAtom, errorMessage->atom() };
|
|
Atom errorAtom = toplevel->errorClass()->construct(1, args);
|
|
Exception *exception = new (GetGC()) Exception(errorAtom
|
|
#ifdef DEBUGGER
|
|
,this
|
|
#endif
|
|
);
|
|
|
|
// Restore stack overflow checks
|
|
inStackOverflow = false;
|
|
|
|
// Throw the stack overflow exception
|
|
throwException(exception);
|
|
}
|
|
|
|
void Shell::interrupt(MethodEnv *env)
|
|
{
|
|
interrupted = false;
|
|
|
|
Toplevel *toplevel = env->vtable->toplevel;
|
|
|
|
if (gracePeriod) {
|
|
// This script has already had its chance; it violated
|
|
// the grace period.
|
|
// Throw an exception it cannot catch.
|
|
Stringp errorMessage = getErrorMessage(kScriptTerminatedError);
|
|
Atom args[2] = { nullObjectAtom, errorMessage->atom() };
|
|
Atom errorAtom = toplevel->errorClass()->construct(1, args);
|
|
Exception *exception = new (GetGC()) Exception(errorAtom
|
|
#ifdef DEBUGGER
|
|
,this
|
|
#endif
|
|
);
|
|
exception->flags |= Exception::EXIT_EXCEPTION;
|
|
throwException(exception);
|
|
}
|
|
|
|
// Give the script an additional grace period to
|
|
// clean up, and throw an exception.
|
|
gracePeriod = true;
|
|
|
|
#ifdef WIN32
|
|
timeSetEvent(kScriptGracePeriod*1000,
|
|
kScriptGracePeriod*1000,
|
|
(LPTIMECALLBACK)TimeoutProc,
|
|
(DWORD_PTR)this,
|
|
TIME_ONESHOT);
|
|
#else
|
|
#ifndef AVMPLUS_ARM // TODO AVMPLUS_ARM
|
|
alarm(kScriptGracePeriod);
|
|
#endif
|
|
#endif
|
|
|
|
toplevel->throwError(kScriptTimeoutError);
|
|
}
|
|
|
|
void Shell::initShellPool()
|
|
{
|
|
AbstractFunction *nativeMethods[toplevel_abc_method_count];
|
|
NativeClassInfo *nativeClasses[toplevel_abc_class_count];
|
|
NativeScriptInfo *nativeScripts[toplevel_abc_script_count];
|
|
|
|
memset(nativeMethods, 0, sizeof(AbstractFunction*)*toplevel_abc_method_count);
|
|
memset(nativeClasses, 0, sizeof(NativeClassInfo*)*toplevel_abc_class_count);
|
|
memset(nativeScripts, 0, sizeof(NativeScriptInfo*)*toplevel_abc_script_count);
|
|
|
|
initNativeTables(classEntries, scriptEntries,
|
|
nativeMethods, nativeClasses, nativeScripts);
|
|
|
|
avmplus::ScriptBuffer code = newScriptBuffer(toplevel_abc_length);
|
|
memcpy(code.getBuffer(), toplevel_abc_data, toplevel_abc_length);
|
|
shellPool = parseActionBlock(code, 0, NULL, builtinDomain, nativeMethods, nativeClasses, nativeScripts);
|
|
}
|
|
|
|
Toplevel* Shell::initShellBuiltins()
|
|
{
|
|
// Initialize a new Toplevel. This will also create a new
|
|
// DomainEnv based on the builtinDomain.
|
|
Toplevel* toplevel = initTopLevel();
|
|
|
|
// Initialize the shell builtins in the new Toplevel
|
|
handleActionPool(shellPool,
|
|
toplevel->domainEnv(),
|
|
toplevel,
|
|
NULL);
|
|
|
|
return toplevel;
|
|
}
|
|
|
|
Shell::Shell(MMgc::GC *gc) : AvmCore(gc)
|
|
{
|
|
#ifdef DEBUGGER
|
|
debugCLI = NULL;
|
|
#endif
|
|
|
|
systemClass = NULL;
|
|
|
|
gracePeriod = false;
|
|
inStackOverflow = false;
|
|
|
|
consoleOutputStream = new (gc) ConsoleOutputStream();
|
|
setConsoleStream(consoleOutputStream);
|
|
|
|
computeStackBase();
|
|
}
|
|
|
|
bool Shell::executeProjector(int argc, char *argv[], int& exitCode)
|
|
{
|
|
TRY(this, kCatchAction_ReportAsError)
|
|
{
|
|
uint8 header[8];
|
|
|
|
char executablePath[256];
|
|
|
|
#ifdef WIN32
|
|
GetModuleFileName(NULL, executablePath, sizeof(executablePath));
|
|
#else
|
|
strncpy(executablePath, argv[0], sizeof(executablePath));
|
|
#endif
|
|
|
|
FileInputStream file(executablePath);
|
|
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;
|
|
}
|
|
|
|
int abcLength = (header[4] |
|
|
header[5]<<8 |
|
|
header[6]<<16 |
|
|
header[7]<<24);
|
|
|
|
ScriptBuffer code = newScriptBuffer(abcLength);
|
|
file.seek(file.length() - 8 - abcLength);
|
|
file.read(code.getBuffer(), abcLength);
|
|
|
|
initBuiltinPool();
|
|
initShellPool();
|
|
|
|
#ifdef DEBUGGER
|
|
// Create the debugger
|
|
debugCLI = new (GetGC()) DebugCLI(this);
|
|
debugger = debugCLI;
|
|
|
|
// Create the profiler
|
|
profiler = new (GetGC()) Profiler(this);
|
|
#endif
|
|
|
|
SystemClass::user_argc = argc-1;
|
|
SystemClass::user_argv = &argv[1];
|
|
|
|
// init toplevel internally
|
|
Toplevel* toplevel = initShellBuiltins();
|
|
|
|
// Create a new Domain for the user code
|
|
Domain* domain = new (GetGC()) Domain(this,
|
|
builtinDomain);
|
|
|
|
// Return a new DomainEnv for the user code
|
|
DomainEnv* domainEnv = new (GetGC()) DomainEnv(this,
|
|
domain,
|
|
toplevel->domainEnv());
|
|
|
|
ShellCodeContext* codeContext = new (GetGC()) ShellCodeContext();
|
|
codeContext->domainEnv = domainEnv;
|
|
|
|
// parse new bytecode
|
|
handleActionBlock(code, 0, domainEnv, toplevel, NULL, NULL, NULL, codeContext);
|
|
|
|
#ifdef DEBUGGER
|
|
delete profiler;
|
|
#endif
|
|
}
|
|
CATCH(Exception *exception)
|
|
{
|
|
#ifdef DEBUGGER
|
|
if (!(exception->flags & Exception::SEEN_BY_DEBUGGER))
|
|
{
|
|
console << string(exception->atom) << "\n";
|
|
}
|
|
if (exception->getStackTrace()) {
|
|
console << exception->getStackTrace()->format(this) << '\n';
|
|
}
|
|
delete profiler;
|
|
#else
|
|
// [ed] always show error, even in release mode,
|
|
// see bug #121382
|
|
console << string(exception->atom) << "\n";
|
|
#endif
|
|
exit(1);
|
|
}
|
|
END_CATCH
|
|
END_TRY
|
|
|
|
#ifdef AVMPLUS_PROFILE
|
|
dump();
|
|
#endif
|
|
|
|
exitCode = 0;
|
|
return true;
|
|
}
|
|
|
|
int Shell::main(int argc, char *argv[])
|
|
{
|
|
TRY(this, kCatchAction_ReportAsError)
|
|
{
|
|
#if defined (WIN32) || defined(AVMPLUS_UNIX)
|
|
if (!P4Available()) {
|
|
sse2 = false;
|
|
}
|
|
#endif
|
|
|
|
int exitCode = 0;
|
|
if (executeProjector(argc, argv, exitCode))
|
|
{
|
|
return exitCode;
|
|
}
|
|
|
|
if (argc < 2) {
|
|
usage();
|
|
}
|
|
|
|
int filenamesPos = -1;
|
|
int endFilenamePos = -1;
|
|
char *filename = NULL;
|
|
bool do_log = false;
|
|
#ifdef DEBUGGER
|
|
bool do_debugger = false;
|
|
#endif
|
|
bool do_interactive = false;
|
|
#ifdef AVMPLUS_VERBOSE
|
|
bool do_verbose = false;
|
|
#endif
|
|
|
|
for (int i=1; i<argc; i++) {
|
|
char *arg = argv[i];
|
|
// options available to development builds.
|
|
if (arg[0] == '-')
|
|
{
|
|
if (arg[1] == 'D') {
|
|
if (!strcmp(arg+2, "timeout")) {
|
|
interrupts = true;
|
|
|
|
#ifdef AVMPLUS_IA32
|
|
} else if (!strcmp(arg+2, "nosse")) {
|
|
sse2 = false;
|
|
#endif
|
|
|
|
#ifdef AVMPLUS_VERIFYALL
|
|
} else if (!strcmp(arg+2, "verifyall")) {
|
|
verifyall = true;
|
|
#endif /* AVMPLUS_VERIFYALL */
|
|
|
|
#ifdef _DEBUG
|
|
} else if (!strcmp(arg+2, "greedy")) {
|
|
GetGC()->greedy = true;
|
|
#endif /* _DEBUG */
|
|
|
|
#ifdef AVMPLUS_PROFILE
|
|
} else if (!strcmp(arg+2, "dprofile")) {
|
|
dprof.dprofile = true;
|
|
} else if (!strcmp(arg+2, "sprofile")) {
|
|
sprof.sprofile = true;
|
|
#endif /* AVMPLUS_PROFILE */
|
|
|
|
#ifdef DEBUGGER
|
|
} else if (!strcmp(arg+2, "gcstats")) {
|
|
GetGC()->gcstats = true;
|
|
} else if (!strcmp(arg+2, "nogc")) {
|
|
GetGC()->nogc = true;
|
|
} else if (!strcmp(arg+2, "noincgc")) {
|
|
GetGC()->incremental = false;
|
|
} else if (!strcmp(arg+2, "astrace")) {
|
|
avmplus::Debugger::astrace = (avmplus::Debugger::TraceLevel) strtol(argv[++i], 0, 10);
|
|
#endif /* DEBUGGER */
|
|
#ifdef AVMPLUS_INTERP
|
|
} else if (!strcmp(arg+2, "interp")) {
|
|
turbo = false;
|
|
#endif /* AVMPLUS_INTERP */
|
|
#ifdef AVMPLUS_VERBOSE
|
|
} else if (!strcmp(arg+2, "verbose")) {
|
|
do_verbose = true;
|
|
#endif
|
|
|
|
#ifdef AVMPLUS_MIR
|
|
#ifdef AVMPLUS_INTERP
|
|
} else if (!strcmp(arg+2, "forcemir")) {
|
|
forcemir = true;
|
|
#endif /* AVMPLUS_INTERP */
|
|
|
|
} else if (!strcmp(arg+2, "nodce")) {
|
|
dceopt = false;
|
|
|
|
} else if (!strcmp(arg+2, "nocse")) {
|
|
cseopt = false;
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
} else if (!strcmp(arg+2, "bbgraph")) {
|
|
bbgraph = true; // generate basic block graph (only valid with mir switch)
|
|
#endif
|
|
#endif /* AVMPLUS_MIR */
|
|
|
|
} else {
|
|
usage();
|
|
}
|
|
} else if (!strcmp(arg, "-log")) {
|
|
do_log = true;
|
|
#ifdef AVMPLUS_INTERACTIVE
|
|
} else if (!strcmp(arg, "-i")) {
|
|
do_interactive = true;
|
|
#endif //AVMPLUS_INTERACTIVE
|
|
}
|
|
else if (!strcmp(arg, "-error")) {
|
|
show_error = true;
|
|
#ifdef WIN32
|
|
SetErrorMode(0); // set to default
|
|
#endif // WIN32
|
|
}
|
|
#ifdef AVMPLUS_WITH_JNI
|
|
else if (!strcmp(arg, "-jargs")) {
|
|
// all the following args until the semi colon is for java.
|
|
//@todo fix up this hard limit
|
|
bool first = true;
|
|
Java::startup_options = new char[256];
|
|
memset(Java::startup_options, 0, 256);
|
|
|
|
for(i++; i<argc; i++)
|
|
{
|
|
if (*argv[i] == ';')
|
|
break;
|
|
if (!first) strcat(Java::startup_options, " ");
|
|
strcat(Java::startup_options, argv[i]);
|
|
first = false;
|
|
}
|
|
AvmAssert(strlen(Java::startup_options) < 256);
|
|
}
|
|
#endif /* AVMPLUS_WITH_JNI */
|
|
|
|
#ifdef DEBUGGER
|
|
else if (!strcmp(arg, "-d")) {
|
|
do_debugger = true;
|
|
}
|
|
#endif /* DEBUGGER */
|
|
|
|
else if(arg[1] == '-' && arg[2] == 0) {
|
|
endFilenamePos = i;
|
|
}
|
|
else {
|
|
usage();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (filenamesPos == -1)
|
|
filenamesPos = i;
|
|
filename = arg;
|
|
}
|
|
}
|
|
|
|
if (!filename && !do_interactive) {
|
|
usage();
|
|
}
|
|
|
|
if( do_log )
|
|
{
|
|
// open logfile based on last filename
|
|
const char* dot = strrchr(filename, '.');
|
|
if (!dot)
|
|
dot = filename+strlen(filename);
|
|
|
|
char* logname = new char[dot-filename+5]; // free upon exit
|
|
strcpy(logname,filename);
|
|
strcpy(logname+(dot-filename),".log");
|
|
printf("%s\n",filename); // but first print name to default stdout
|
|
freopen(logname, "w", stdout);
|
|
delete [] logname;
|
|
}
|
|
|
|
initBuiltinPool();
|
|
initShellPool();
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (do_verbose)
|
|
verbose = true;
|
|
#endif
|
|
|
|
#ifdef DEBUGGER
|
|
// Create the debugger
|
|
debugCLI = new (GetGC()) DebugCLI(this);
|
|
debugger = debugCLI;
|
|
|
|
// Create the profiler
|
|
profiler = new (GetGC()) Profiler(this);
|
|
|
|
if (do_debugger)
|
|
{
|
|
// Activate the debug CLI and stop at
|
|
// start of program
|
|
debugCLI->activate();
|
|
debugCLI->stepInto();
|
|
}
|
|
#endif
|
|
|
|
// start the 15 second timeout if applicable
|
|
if (interrupts) {
|
|
#ifdef WIN32
|
|
timeSetEvent(kScriptTimeout*1000,
|
|
kScriptTimeout*1000,
|
|
(LPTIMECALLBACK)TimeoutProc,
|
|
(DWORD_PTR)this,
|
|
TIME_ONESHOT);
|
|
#else
|
|
#ifndef AVMPLUS_ARM // TODO AVMPLUS_ARM
|
|
signal(SIGALRM, alarmProc);
|
|
alarm(kScriptTimeout);
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
if(endFilenamePos == -1)
|
|
endFilenamePos = argc;
|
|
SystemClass::user_argc = argc-endFilenamePos-1;
|
|
SystemClass::user_argv = &argv[endFilenamePos+1];
|
|
|
|
// init toplevel internally
|
|
Toplevel* toplevel = initShellBuiltins();
|
|
|
|
// Create a new Domain for the user code
|
|
Domain* domain = new (GetGC()) Domain(this,
|
|
builtinDomain);
|
|
|
|
// Return a new DomainEnv for the user code
|
|
DomainEnv* domainEnv = new (GetGC()) DomainEnv(this,
|
|
domain,
|
|
toplevel->domainEnv());
|
|
|
|
ShellCodeContext* lastCodeContext = 0;
|
|
|
|
// execute each abc file
|
|
for (int i=filenamesPos; filename && i < endFilenamePos; i++)
|
|
{
|
|
filename = argv[i];
|
|
|
|
#ifdef AVMPLUS_VERBOSE
|
|
if (verbose) {
|
|
console << "run " << filename << "\n";
|
|
}
|
|
#endif
|
|
|
|
FileInputStream f(filename);
|
|
bool isValid = f.valid();
|
|
if (!isValid) {
|
|
fprintf(stderr, "cannot open file: %s\n", filename);
|
|
#ifdef DEBUGGER
|
|
delete profiler;
|
|
#endif
|
|
if (!do_interactive)
|
|
return(1);
|
|
}
|
|
|
|
ShellCodeContext* codeContext = new (GetGC()) ShellCodeContext();
|
|
codeContext->domainEnv = domainEnv;
|
|
|
|
// parse new bytecode
|
|
if (isValid)
|
|
{
|
|
ScriptBuffer code = newScriptBuffer(f.available());
|
|
f.read(code.getBuffer(), f.available());
|
|
handleActionBlock(code, 0, domainEnv, toplevel, NULL, NULL, NULL, codeContext);
|
|
}
|
|
|
|
lastCodeContext = codeContext;
|
|
}
|
|
|
|
#ifdef MMGC_COUNTERS
|
|
console << "\nGC stats\n";
|
|
console << "mark item " << MMgc::GC::MarkItemCount << "\n";
|
|
console << "mark null ptr " << MMgc::GC::marknullCount << "\n";
|
|
console << "mark alloc " << MMgc::GC::markallocCount << "\n";
|
|
console << "mark large " << MMgc::GC::marklargeCount << "\n";
|
|
console << "mark skip " << MMgc::GC::markskipCount << "\n";
|
|
console << "TrapWriteCount " << MMgc::GC::TrapWriteCount << "\n";
|
|
|
|
console << "\nGCAlloc stats\n";
|
|
console << "SweepBlockCount " << MMgc::GCAlloc::SweepBlockCount << "\n";
|
|
console << "AllocCount " << MMgc::GCAlloc::AllocCount << "\n";
|
|
console << "FreeItemCount " << MMgc::GCAlloc::FreeItemCount << "\n";
|
|
#endif
|
|
|
|
#ifdef AVMPLUS_INTERACTIVE
|
|
if (do_interactive)
|
|
{
|
|
enum { kMaxCommandLine = 1024 };
|
|
char commandLine[kMaxCommandLine];
|
|
enum { kMaxFileName = 1024 };
|
|
char fileName[kMaxFileName];
|
|
char imports[kMaxCommandLine];
|
|
strcpy(imports, " ");
|
|
|
|
// some defaults
|
|
addToImports(imports, "C:\\src\\farm\\main\\as\\lib\\shell.abc");
|
|
addToImports(imports, "C:\\src\\farm\\main\\as\\lib\\global.abc");
|
|
|
|
STARTUPINFO si;
|
|
PROCESS_INFORMATION pi;
|
|
|
|
while(do_interactive)
|
|
{
|
|
console << "(avmplus) ";
|
|
fflush(stdout);
|
|
fgets(commandLine, kMaxCommandLine, stdin);
|
|
|
|
commandLine[strlen(commandLine)-1] = 0;
|
|
|
|
// build up the file that we are going to compile
|
|
bool compile = true;
|
|
bool exec = true;
|
|
fileName[0] = '\0';
|
|
if (strstr(commandLine, ".run ") == commandLine)
|
|
{
|
|
// arg
|
|
strcpy(fileName, &commandLine[5]);
|
|
|
|
// search for .as extension
|
|
const char* dotAt = strrchr(fileName, '.');
|
|
bool fail = true;
|
|
if (dotAt)
|
|
{
|
|
if (strcmp(dotAt, ".abc") == 0)
|
|
{
|
|
compile = false;
|
|
fail = false;
|
|
}
|
|
else if (strcmp(dotAt, ".as") == 0)
|
|
{
|
|
fail = false;
|
|
}
|
|
}
|
|
|
|
if (fail)
|
|
{
|
|
console << "only .as and .abc files are supported \n";
|
|
continue;
|
|
}
|
|
}
|
|
else if (strstr(commandLine, ".import ") == commandLine)
|
|
{
|
|
// add to the import list
|
|
strcpy(fileName, &commandLine[8]);
|
|
compile = false;
|
|
exec = false;
|
|
|
|
if (!addToImports(imports, fileName))
|
|
{
|
|
console << "file does not exist; not added to import list \n";
|
|
}
|
|
console << imports << "\n";
|
|
}
|
|
else if (strstr(commandLine, ".quit") == commandLine)
|
|
{
|
|
return 0;
|
|
}
|
|
else if (commandLine[0] == '\0' || (strstr(commandLine, ".help") == commandLine) )
|
|
{
|
|
console << "ActionScript source can be directly entered on the command line.\nIt will be compiled and executed once the enter key is pressed.\nThe following directives are also recognized\n" ;
|
|
console << ".run [f.as|f.abc] - runs f, compiles f.as first if required\n" ;
|
|
console << ".import f - add f to the -import list for compiling \n" ;
|
|
console << ".quit - exits this shell \n" ;
|
|
console << ".help - displays help information \n" ;
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// put our command line contents in a file
|
|
strcpy(fileName, "___file_for_io.as");
|
|
FILE* f = fopen(fileName , "w");
|
|
if (!f)
|
|
{
|
|
console << "i/o error \n";
|
|
return 1;
|
|
}
|
|
|
|
fputs(commandLine, f);
|
|
fclose(f);
|
|
}
|
|
|
|
// set up for the compile if needed
|
|
if (compile)
|
|
{
|
|
// Set the bInheritHandle flag so pipe handles are inherited.
|
|
SECURITY_ATTRIBUTES saAttr;
|
|
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
|
|
saAttr.bInheritHandle = TRUE;
|
|
saAttr.lpSecurityDescriptor = NULL;
|
|
|
|
HANDLE pRd, pWr;
|
|
CreatePipe(&pRd, &pWr, &saAttr, 64*kMaxCommandLine);
|
|
SetHandleInformation( pRd, HANDLE_FLAG_INHERIT, 0); // don't inherit read portion; only allow writes from child proc
|
|
SetHandleInformation( GetStdHandle(STD_INPUT_HANDLE), HANDLE_FLAG_INHERIT, 0); // don't inherit stdin
|
|
|
|
ZeroMemory( &si, sizeof(si) );
|
|
si.cb = sizeof(si);
|
|
ZeroMemory( &pi, sizeof(pi) );
|
|
si.hStdError = pWr;
|
|
si.hStdOutput = pWr;
|
|
si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
|
|
si.dwFlags = STARTF_USESTDHANDLES;
|
|
|
|
// now compile and wait
|
|
commandLine[0] = '\0';
|
|
strcpy(commandLine, "asc.exe -debug ");
|
|
strcat(commandLine, imports);
|
|
strcat(commandLine, fileName);
|
|
DWORD err = CreateProcess(0, commandLine, 0,0,TRUE,0,0,0, &si, &pi);
|
|
if (err)
|
|
{
|
|
// Wait until child process exits.
|
|
WaitForSingleObject( pi.hProcess, 20000 );
|
|
}
|
|
else
|
|
{
|
|
console << "failed to compile err=0x";
|
|
console.writeHexAddr(GetLastError());
|
|
console << "\n";
|
|
exec = false;
|
|
}
|
|
|
|
// Close process and thread handles.
|
|
CloseHandle( pi.hProcess );
|
|
CloseHandle( pi.hThread );
|
|
|
|
// now check the compile
|
|
CloseHandle(pWr); // Close the write end of the pipe before reading from the read end of the pipe.
|
|
commandLine[0] = '\0';
|
|
DWORD dwRead = 0;
|
|
ReadFile( pRd, commandLine, kMaxCommandLine, &dwRead, NULL);
|
|
if (dwRead > 0) commandLine[dwRead] = '\0';
|
|
if ( !strstr(commandLine, "bytes written") )
|
|
{
|
|
// failed compile
|
|
console << commandLine;
|
|
|
|
// dump the rest of the message
|
|
for(;;)
|
|
{
|
|
if (!ReadFile( pRd, commandLine, kMaxCommandLine, &dwRead, NULL) || dwRead == 0)
|
|
break;
|
|
|
|
console << commandLine;
|
|
}
|
|
exec = false;
|
|
console << "\n";
|
|
}
|
|
|
|
// now run the abc
|
|
int afterDot = strlen(fileName) - 2;
|
|
strcpy(&fileName[afterDot], "abc");
|
|
}
|
|
|
|
if (exec)
|
|
{
|
|
FileInputStream fl(fileName);
|
|
bool isValid = fl.valid();
|
|
if (isValid)
|
|
{
|
|
TRY(this, kCatchAction_ReportAsError)
|
|
{
|
|
ScriptBuffer code = newScriptBuffer(fl.available());
|
|
fl.read(code.getBuffer(), fl.available());
|
|
handleActionBlock(code, 0, domainEnv, toplevel, NULL, NULL, NULL, lastCodeContext);
|
|
}
|
|
CATCH(Exception *exception)
|
|
{
|
|
#ifdef DEBUGGER
|
|
if (!(exception->flags & 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
|
|
}
|
|
END_CATCH
|
|
END_TRY
|
|
}
|
|
else
|
|
{
|
|
console << "can't find " << fileName << "\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#endif //AVMPLUS_INTERACTIVE
|
|
|
|
#ifdef DEBUGGER
|
|
delete profiler;
|
|
#endif
|
|
}
|
|
CATCH(Exception *exception)
|
|
{
|
|
#ifdef DEBUGGER
|
|
if (!(exception->flags & Exception::SEEN_BY_DEBUGGER))
|
|
{
|
|
console << string(exception->atom) << "\n";
|
|
}
|
|
if (exception->getStackTrace()) {
|
|
console << exception->getStackTrace()->format(this) << '\n';
|
|
}
|
|
delete profiler;
|
|
#else
|
|
// [ed] always show error, even in release mode,
|
|
// see bug #121382
|
|
console << string(exception->atom) << "\n";
|
|
#endif
|
|
exit(1);
|
|
}
|
|
END_CATCH
|
|
END_TRY
|
|
|
|
#ifdef AVMPLUS_PROFILE
|
|
dump();
|
|
#endif
|
|
#ifdef AVMPLUS_WITH_JNI
|
|
if (Java::startup_options) delete Java::startup_options;
|
|
#endif /* AVMPLUS_WITH_JNI */
|
|
return 0;
|
|
}
|
|
|
|
#ifdef AVMPLUS_INTERACTIVE
|
|
int Shell::addToImports(char* imports, char* addition)
|
|
{
|
|
int worked = 0;
|
|
if (addition && addition[0] != '\0')
|
|
{
|
|
FileInputStream fl(addition);
|
|
if (fl.valid())
|
|
{
|
|
strcat(imports, " ");
|
|
strcat(imports, " -import \"");
|
|
strcat(imports, addition);
|
|
strcat(imports, "\" ");
|
|
worked = 1;
|
|
}
|
|
}
|
|
return worked;
|
|
}
|
|
#endif //AVMPLUS_INTERACTIVE
|
|
}
|
|
|
|
int _main(int argc, char *argv[])
|
|
{
|
|
if (!fm)
|
|
{
|
|
MMgc::GCHeap::Init();
|
|
MMgc::FixedMalloc::Init();
|
|
|
|
fm = MMgc::FixedMalloc::GetInstance();
|
|
}
|
|
|
|
MMgc::GCHeap* heap = MMgc::GCHeap::GetGCHeap();
|
|
|
|
// memory zero'ing check
|
|
/* int *foo = new int[2];
|
|
AvmAssert(memcmp(foo, "\0\0\0\0\0\0\0\0\0\0\0\0", 2*sizeof(int)) == 0);
|
|
delete foo;*/
|
|
|
|
int exitCode = 0;
|
|
{
|
|
MMgc::GC gc(heap);
|
|
avmshell::shell = new avmshell::Shell(&gc);
|
|
exitCode = avmshell::shell->main(argc, argv);
|
|
delete avmshell::shell;
|
|
}
|
|
|
|
MMgc::FixedMalloc::Destroy();
|
|
MMgc::GCHeap::Destroy();
|
|
fm = 0;
|
|
return exitCode;
|
|
}
|
|
|
|
#ifdef AVMPLUS_WIN32
|
|
unsigned long CrashFilter(LPEXCEPTION_POINTERS pException, int exceptionCode)
|
|
{
|
|
unsigned long result;
|
|
if ((result = UnhandledExceptionFilter(pException)) != EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
return result;
|
|
}
|
|
else if (avmshell::show_error)
|
|
{
|
|
// if -error option dont do a dump
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
printf("avmplus crash: exception 0x%08lX occurred\n", exceptionCode);
|
|
|
|
typedef BOOL (WINAPI *MINIDUMP_WRITE_DUMP)(
|
|
HANDLE hProcess,
|
|
DWORD ProcessId,
|
|
HANDLE hFile,
|
|
MINIDUMP_TYPE DumpType,
|
|
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
|
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
|
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
|
|
);
|
|
|
|
HMODULE hDbgHelp = LoadLibrary("dbghelp.dll");
|
|
MINIDUMP_WRITE_DUMP MiniDumpWriteDump_ = (MINIDUMP_WRITE_DUMP)GetProcAddress(hDbgHelp,
|
|
"MiniDumpWriteDump");
|
|
|
|
if (MiniDumpWriteDump_)
|
|
{
|
|
MINIDUMP_EXCEPTION_INFORMATION M;
|
|
const char DumpPath[] = "avmplusCrash.dmp";
|
|
|
|
M.ThreadId = GetCurrentThreadId();
|
|
M.ExceptionPointers = pException;
|
|
M.ClientPointers = 0;
|
|
|
|
printf("Writing minidump crash log to %s\n", DumpPath);
|
|
|
|
HANDLE hDumpFile = CreateFile(DumpPath, GENERIC_WRITE, 0,
|
|
NULL, CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL, NULL);
|
|
|
|
MiniDumpWriteDump_(GetCurrentProcess(),
|
|
GetCurrentProcessId(),
|
|
hDumpFile,
|
|
MiniDumpNormal,
|
|
(pException) ? &M : NULL, NULL, NULL);
|
|
|
|
CloseHandle(hDumpFile);
|
|
}
|
|
else
|
|
{
|
|
printf("minidump not available, no crash log written.\n");
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
SetErrorMode(SEM_NOGPFAULTERRORBOX);
|
|
int code = 0;
|
|
__try
|
|
{
|
|
code = _main(argc, argv);
|
|
}
|
|
__except(CrashFilter(GetExceptionInformation(), GetExceptionCode()))
|
|
{
|
|
code = -1;
|
|
}
|
|
if (avmshell::show_error) printf("error %d", code);
|
|
return code;
|
|
}
|
|
#else
|
|
|
|
#ifdef AVMPLUS_ARM
|
|
// TODO this is a hack until we learn how to determine stack top
|
|
// in ARM
|
|
int StackTop;
|
|
#endif
|
|
|
|
int main(int argc, char *argv[])
|
|
{
|
|
#ifdef AVMPLUS_ARM
|
|
int sp;
|
|
asm("mov %0,sp" : "=r" (sp));
|
|
StackTop = sp;
|
|
#endif
|
|
|
|
#ifdef AVMPLUS_MACH_EXCEPTIONS
|
|
GenericGuard::staticInit();
|
|
#endif
|
|
int code = _main(argc, argv);
|
|
if (avmshell::show_error) printf("error %d", code);
|
|
|
|
#ifdef AVMPLUS_MACH_EXCEPTIONS
|
|
GenericGuard::staticDestroy();
|
|
#endif
|
|
|
|
return code;
|
|
}
|
|
#endif
|
|
|
|
|