Files
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

1099 lines
34 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/. */
#include "avmshell.h"
#ifdef DEBUGGER
namespace avmshell
{
/**
* The array of top level commands that we support.
* They are placed into a Nx2 array, whereby the first component
* is a String which is the command and the 2nd component is the
* integer identifier for the command.
*
* NOTE: order matters! For the case of a single character
* match, we let the first hit act like an unambiguous match.
*/
DebugCLI::StringInt DebugCLI::commandArray[] =
{
{ "break", CMD_BREAK },
{ "bt", INFO_STACK_CMD },
{ "continue", CMD_CONTINUE },
{ "delete", CMD_DELETE },
{ "finish", CMD_FINISH },
{ "help", CMD_HELP },
{ "?", CMD_HELP },
{ "info", CMD_INFO },
{ "list", CMD_LIST },
{ "next", CMD_NEXT },
{ "print", CMD_PRINT },
{ "quit", CMD_QUIT },
{ "step", CMD_STEP },
{ "set", CMD_SET },
{ "where", INFO_STACK_CMD },
{ NULL, 0 }
};
/**
* Info sub-commands
*/
DebugCLI::StringInt DebugCLI::infoCommandArray[] =
{
{ "arguments", INFO_ARGS_CMD },
{ "breakpoints", INFO_BREAK_CMD },
{ "files", INFO_FILES_CMD },
{ "functions", INFO_FUNCTIONS_CMD },
{ "locals", INFO_LOCALS_CMD },
{ "stack", INFO_STACK_CMD },
{ NULL, 0 }
};
DebugCLI::DebugCLI(avmplus::AvmCore *core, avmplus::Debugger::TraceLevel tracelevel)
: Debugger(core, tracelevel)
{
currentSourceLen = 0;
warnMissingSource = true;
}
DebugCLI::~DebugCLI()
{
if (currentSource) {
delete [] currentSource;
currentSource = NULL;
}
}
char* DebugCLI::nextToken()
{
char *out = currentToken;
if (currentToken) {
while (*currentToken) {
if (*currentToken == ' ' || *currentToken == '\r' || *currentToken == '\n' || *currentToken == '\t') {
*currentToken++ = 0;
break;
}
currentToken++;
}
currentToken = *currentToken ? currentToken : NULL;
}
return out;
}
/**
* Attempt to match given the given string against our set of commands
* @return the command code that was hit.
*/
int DebugCLI::determineCommand(DebugCLI::StringInt cmdList[],
const char *input,
int defCmd)
{
if (!input) return INFO_UNKNOWN_CMD;
int inputLen = (int)VMPI_strlen(input);
// first check for a comment
if (input[0] == '#') {
return CMD_COMMENT;
}
int match = -1;
bool ambiguous = false;
for (int i=0; cmdList[i].text; i++) {
if (!VMPI_strncmp(input, cmdList[i].text, inputLen)) {
if (match != -1) {
ambiguous = true;
break;
}
match = i;
}
}
/**
* 3 cases:
* - No hits, return unknown and let our caller
* dump the error.
* - We match unambiguously or we have 1 or more matches
* and the input is a single character. We then take the
* first hit as our command.
* - If we have multiple hits then we dump a 'ambiguous' message
* and puke quietly.
*/
if (match == -1) {
// no command match return unknown
return defCmd;
}
// only 1 match or our input is 1 character or first match is exact
else if (!ambiguous || inputLen == 1 || !VMPI_strcmp(cmdList[match].text, input)) {
return cmdList[match].id;
}
else {
// matches more than one command dump message and go
core->console << "Ambiguous command '" << input << "': ";
bool first = true;
for (int i=0; cmdList[i].text; i++) {
if (!VMPI_strncmp(cmdList[i].text, input, inputLen)) {
if (!first) {
core->console << ", ";
} else {
first = false;
}
core->console << cmdList[i].text;
}
}
core->console << ".\n";
return -1;
}
}
const char* DebugCLI::commandNumberToCommandName(DebugCLI::StringInt cmdList[],
int cmdNumber)
{
for (int i = 0; cmdList[i].text; i++)
{
if (cmdList[i].id == cmdNumber)
return cmdList[i].text;
}
return "?";
}
bool DebugCLI::printFrame(int k)
{
avmplus::Atom* ptr;
int count, line = -1;
avmplus::SourceInfo* src = NULL;
avmplus::AvmCore* core = avmplus::AvmCore::getActiveCore();
avmplus::DebugFrame* frame = core->debugger()->frameAt(k);
if (frame == NULL) return false;
// source information
frame->sourceLocation(src, line);
core->console << "#" << k << " ";
// this
avmplus::Atom a = avmplus::nullObjectAtom;
frame->dhis(a);
core->console << avmplus::asAtom(a) << ".";
// method
avmplus::MethodInfo* info = functionFor(src, line);
if (info) {
core->console << info->getMethodName();
} else {
avmplus::Stringp name = NULL;
if (frame->methodName(name))
core->console << name;
else
core->console << "???";
}
core->console << "(";
// dump args
frame->arguments(ptr, count);
for (int i=0; i<count; i++)
{
// write out the name
if (info)
{
avmplus::Stringp nm = info->getArgName(i);
if (nm != core->kundefined)
core->console << nm << "=";
}
core->console << avmplus::asAtom(*ptr++);
if (i<count-1)
core->console << ",";
}
core->console << ") at ";
if (src)
core->console << src->name() << ":" << (line) << "\n";
else
core->console << "???\n";
return true;
}
void DebugCLI::bt()
{
avmplus::AvmCore* core = avmplus::AvmCore::getActiveCore();
//core->stackTrace->dump(core->console);
//core->console << '\n';
// obtain information about each frame
int frameCount = core->debugger()->frameCount();
for(int k=0; k<frameCount; k++)
{
printFrame(k);
}
}
void DebugCLI::help()
{
StringInt* cmd;
// "help info" shows sub-commands of the "info" command
char* next = nextToken();
if (next && commandFor(next) == CMD_INFO)
cmd = infoCommandArray;
else
cmd = commandArray;
while (cmd->text)
{
core->console << cmd->text;
if (cmd->id == CMD_INFO)
core->console << " (see 'help " << cmd->text << "' for sub-commands)";
core->console << '\n';
cmd++;
}
}
avmplus::MethodInfo* DebugCLI::functionFor(avmplus::SourceInfo* src, int line)
{
avmplus::MethodInfo* info = NULL;
if (src)
{
// find the function at this location
int size = src->functionCount();
for(int i=0; i<size; i++)
{
avmplus::MethodInfo* m = src->functionAt(i);
if (line >= m->firstSourceLine() && line <= m->lastSourceLine())
{
info = m;
break;
}
}
}
return info;
}
// zero based
char* DebugCLI::lineStart(int linenum)
{
if (!currentSource && currentFile)
setCurrentSource(currentFile);
if (!currentSource) {
return NULL;
}
// linenumbers map to zero based array entry
char *ptr = currentSource;
for (int i=0; i<linenum; i++) {
// Scan to end of line
while (*ptr != '\n') {
if (!*ptr) {
return NULL;
}
ptr++;
}
// Advance past newline
ptr++;
}
return ptr;
}
void DebugCLI::displayLines(int line, int count)
{
if (!lineStart(0)) {
if (currentFile)
core->console << currentFile;
else
core->console << "<unknown>";
core->console <<":"<<line<<" ";
} else {
int lineAt = line;
while(count-- > 0)
{
char* ptr = lineStart(lineAt-1);
if (!ptr)
{
#if WRAP_AROUND
lineAt = 1;
count++; // reset line number to beginning skip this iteration
#else
break;
#endif
}
else
{
core->console << (lineAt) << ": ";
while (*ptr && *ptr != '\n')
core->console << *ptr++;
core->console << '\n';
lineAt++;
}
}
}
}
void DebugCLI::list(const char* line)
{
int currentLine = (core->callStack) ? core->callStack->linenum() : 0;
int linenum = (line) ? VMPI_atoi(line) : currentLine;
displayLines(linenum, 10);
}
void DebugCLI::printIP()
{
int line = (core->callStack) ? core->callStack->linenum() : 0;
displayLines(line, 1);
}
void DebugCLI::breakpoint(char *location)
{
if (!location)
{
core->console << "Bad format, should be: 'break [filename:]line'\n";
return;
}
avmplus::Stringp filename = currentFile;
char *colon = VMPI_strchr(location, ':');
if (colon) {
*colon = 0;
filename = core->internStringLatin1(location);
location = colon+1;
}
if (abcCount() == 0) {
core->console << "No abc file loaded\n";
return;
}
avmplus::SourceFile* sourceFile = NULL;
for (int i = 0, n = abcCount(); i < n; ++i)
{
avmplus::AbcFile* abcFile = (avmplus::AbcFile*)abcAt(i);
sourceFile = abcFile->sourceNamed(filename);
if (sourceFile)
break;
}
if (sourceFile == NULL) {
core->console << "No source available; can't set breakpoint.\n";
return;
}
int targetLine = VMPI_atoi(location);
if (breakpointSet(sourceFile, targetLine)) {
int breakpointId = ++breakpointCount;
core->console << "Breakpoint " << breakpointId << ": file "
<< filename
<< ", " << (targetLine) << ".\n";
BreakAction *breakAction = new (core->GetGC()) BreakAction(sourceFile,
breakpointId,
filename,
targetLine);
breakAction->prev = lastBreakAction;
if (lastBreakAction) {
lastBreakAction->next = breakAction;
} else {
firstBreakAction = breakAction;
}
lastBreakAction = breakAction;
} else {
core->console << "Could not locate specified line.\n";
}
}
void DebugCLI::showBreakpoints()
{
BreakAction *breakAction = firstBreakAction;
while (breakAction) {
breakAction->print(core->console);
breakAction = breakAction->next;
}
}
void DebugCLI::deleteBreakpoint(char *idstr)
{
int id = VMPI_atoi(idstr);
BreakAction *breakAction = firstBreakAction;
while (breakAction) {
if (breakAction->id == id) {
break;
}
breakAction = breakAction->next;
}
if (breakAction) {
if (breakAction->prev) {
breakAction->prev->next = breakAction->next;
} else {
firstBreakAction = breakAction->next;
}
if (breakAction->next) {
breakAction->next->prev = breakAction->prev;
} else {
lastBreakAction = breakAction->prev;
}
if (breakpointClear(breakAction->sourceFile, breakAction->linenum)) {
core->console << "Breakpoint " << id << " deleted.\n";
} else {
core->console << "Internal error; could not delete breakpoint.\n";
}
} else {
core->console << "Could not find breakpoint.\n";
}
}
void DebugCLI::locals(int frameNumber)
{
avmplus::Atom* ptr;
int count, line;
avmplus::SourceInfo* src = NULL;
avmplus::AvmCore* core = avmplus::AvmCore::getActiveCore();
avmplus::DebugFrame* frame = core->debugger()->frameAt(frameNumber);
if (frame == NULL) return;
// source information
frame->sourceLocation(src, line);
// method
avmplus::MethodInfo* info = functionFor(src, line);
if (info) {
frame->locals(ptr, count);
for(int i=0; i<count; i++) {
// write out the name
avmplus::Stringp nm = info->getLocalName(i);
core->console << i << ": ";
if (nm != core->kundefined)
core->console << nm;
else
core->console << "<local_" << i << ">";
core->console << " = " << avmplus::asAtom(*ptr++) << "\n";
}
}
}
void DebugCLI::arguments(int frameNumber)
{
int count;
avmplus::Atom* arr;
avmplus::AvmCore* core = avmplus::AvmCore::getActiveCore();
avmplus::DebugFrame* frame = core->debugger()->frameAt(frameNumber);
if (frame && frame->arguments(arr, count)) {
for (int i = 0; i < count; i++) {
avmplus::Stringp nm = NULL;
frame->argumentName(i, nm);
core->console << i << ": " << nm << " = " << avmplus::asAtom(*arr++) << "\n";
}
}
}
/**
* Given a pointer, trims leading and trailing whitespace. Note, this will
* modify the buffer -- it trims trailing whitespace by writing a null
* character.
*
* Returns the first offset past leading whitespace.
*/
static char* trim(char* ptr)
{
while (*ptr && (*ptr==' ' || *ptr=='\t'))
++ptr;
char* start = ptr;
while (*ptr)
++ptr;
while (ptr>start && (ptr[-1]==' ' || ptr[-1]=='\t'))
--ptr;
*ptr = '\0';
return start;
}
void DebugCLI::set()
{
char* line = currentToken;
char* expr = line;
char* equ = VMPI_strchr(currentToken, '=');
char* value = NULL;
if (equ)
{
*equ = '\0';
value = equ+1;
expr = trim(expr);
value = trim(value);
}
if (!expr || !*expr || !value || !*value)
{
core->console << " Bad format, should be: 'set {variable} = {value}' \n";
}
else
{
bool sawTrailingDot = false;
IValue* lvalue = evaluate(expr, &sawTrailingDot);
IValue* rvalue = evaluate(value, &sawTrailingDot);
AvmAssert(lvalue != NULL);
AvmAssert(rvalue != NULL);
if (!lvalue->isLValue())
core->console << " Can't assign to " << expr << '\n';
else
lvalue->set(rvalue->get());
}
}
class ScopeBuilder : public avmplus::CallStackNode::IScopeChainEnumerator
{
public:
ScopeBuilder(MMgc::GC* gc) : scopeChain(gc, avmplus::kListInitialCapacity) { }
/*virtual*/ void addScope(avmplus::Atom scope)
{
// null/undefined are not legal entries for scope, but
// enumerateScopeChainAtoms() can hand them to us, for entries
// within the max_scope range that aren't in use. just ignore 'em.
if (avmplus::AvmCore::isNullOrUndefined(scope))
return;
scopeChain.add(scope);
}
avmplus::AtomList scopeChain;
};
void DebugCLI::throwUndefinedVarError(const char* name)
{
avmplus::Stringp errorMessage = core->newStringLatin1("Error: Variable ");
errorMessage = errorMessage->appendLatin1(name);
errorMessage = errorMessage->appendLatin1(" is not defined.");
core->throwAtom(errorMessage->atom());
}
IValue* DebugCLI::getValue(const char *name)
{
avmplus::DebugStackFrame* frame = (avmplus::DebugStackFrame*)frameAt(0);
avmplus::Atom thisAtom;
frame->dhis(thisAtom);
avmplus::Stringp namestr = core->internStringLatin1(name, -1);
if (VMPI_strcmp(name, "this") == 0)
return new (core->gc) ConstantValue(thisAtom);
if (VMPI_strcmp(name, "NaN") == 0)
return new (core->gc) ConstantValue(core->kNaN);
if (namestr == core->kfalse)
return new (core->gc) ConstantValue(falseAtom);
if (namestr == core->ktrue)
return new (core->gc) ConstantValue(trueAtom);
if (namestr == core->knull)
return new (core->gc) ConstantValue(nullObjectAtom);
if (namestr == core->kundefined)
return new (core->gc) ConstantValue(undefinedAtom);
if (name[0] == '-' || VMPI_isdigit(name[0]))
return new (core->gc) ConstantValue(core->numberAtom(namestr->atom()));
if (name[0] == '\'' || name[0] == '"') // String literal
{
int32_t length = namestr->length();
if (length >= 2 && namestr->charAt(length-1) == namestr->charAt(0))
{
// Note, this doesn't do any escaping
return new (core->gc) ConstantValue(namestr->substr(1, length-2)->atom());
}
}
if (name[0] == '<') // XML or XMLList literal
{
if (avmplus::AvmCore::isObject(thisAtom))
{
avmplus::Toplevel* toplevel = avmplus::AvmCore::atomToScriptObject(thisAtom)->toplevel();
if (name[1] == '>')
return new (core->gc) ConstantValue(toplevel->xmlListClass()->ToXMLList(namestr->atom()));
else
return new (core->gc) ConstantValue(toplevel->xmlClass()->ToXML(namestr->atom()));
}
}
avmplus::Atom* ptr;
int count, line;
avmplus::SourceInfo* src;
// See if the name matches a function argument or local
frame->sourceLocation(src, line);
if (src)
{
avmplus::MethodInfo* info = functionFor(src, line);
if (info)
{
// see if the name matches a function argument
frame->arguments(ptr, count);
for(int i=0; i<count; i++)
{
avmplus::Stringp arg = info->getArgName(i);
if (arg->equalsLatin1(name))
{
// match!
return new (core->gc) ArgumentValue(frame, i);
}
}
// see if the name matches a local
frame->locals(ptr, count);
for(int i=0; i<count; i++)
{
avmplus::Stringp local = info->getLocalName(i);
if ( local->equalsLatin1(name))
{
// match!
return new (core->gc) LocalValue(frame, i);
}
}
}
}
// See if the name matches a property on the scope chain (which includes
// the 'this' object)
if (frame->trace)
{
avmplus::MethodEnv* env = frame->trace->env();
if (env)
{
ScopeBuilder scopeBuilder(core->GetGC());
frame->trace->enumerateScopeChainAtoms(scopeBuilder);
for (uint32_t i=0, n=scopeBuilder.scopeChain.length(); i<n; ++i)
{
StPropertyFinder finder(core, scopeBuilder.scopeChain.get(i), name);
finder.iterate();
if (finder.value)
return finder.value;
}
}
}
// Look for globals like 'Number'
avmplus::MethodEnv* env = frame->trace->env();
if (env)
{
avmplus::Multiname mn(core->getAnyPublicNamespace(),
core->internStringLatin1(name));
avmplus::ScriptEnv* script = core->domainMgr()->findScriptEnvInDomainEnvByMultiname(env->domainEnv(), mn);
if (script != (avmplus::ScriptEnv*)avmplus::BIND_NONE && script != (avmplus::ScriptEnv*)avmplus::BIND_AMBIGUOUS)
{
avmplus::ScriptObject* global = script->global;
if (global)
{
return new (core->gc) PropertyValue(global, mn);
}
}
}
throwUndefinedVarError(name);
return NULL; // unreachable
}
/**
* Accepts expressions of these forms:
* foo
* foo.bar
* foo.bar.baz
*
* Also allows literals: boolean, string, numeric, XML/XMLList,
* null, undefined.
*
* If the expression ends with a dot, e.g. "foo.bar.", then
* *outSawTrailingDot is set to true. This is used by the
* "print" command to allow "print foo." to print all members
* of variable "foo".
*/
IValue* DebugCLI::evaluate(char *expr, bool* outSawTrailingDot)
{
const char *name;
IValue* value = NULL;
bool firstPart = true;
*outSawTrailingDot = false;
while (expr)
{
name = expr;
char *dot = VMPI_strchr(expr, '.');
if (dot)
{
*dot = '\0';
name = expr;
expr = dot+1;
}
else
{
name = expr;
expr = NULL;
}
if (firstPart)
{
value = getValue(name);
AvmAssert(value != NULL);
}
else
{
if (*name)
{
// "foo.bar" -- find property "bar" of object "foo"
avmplus::Atom parent = value->get();
StPropertyFinder finder(core, parent, name);
finder.iterate();
value = finder.value;
if (!value)
{
if (avmplus::AvmCore::isObject(parent))
{
// Since we didn't find an existing property, we'll just construct
// one. If the parent object is dynamic, then calling get() on the
// PropertyVaulue we create will return undefined, and calling set()
// on it will define a new property. If the parent object is not
// dynamic, then get() and set() will throw exceptions. Either way,
// that's the correct behavior.
avmplus::Multiname mn(core->getAnyPublicNamespace(),
core->internStringLatin1(name));
value = new (core->gc) PropertyValue(avmplus::AvmCore::atomToScriptObject(parent), mn);
}
else
{
if (dot) *dot = '.'; // restore for our caller
throwUndefinedVarError(name);
return NULL; // unreachable
}
}
}
else
{
// "foo."
*outSawTrailingDot = true;
}
}
if (dot) *dot = '.'; // restore for our caller
firstPart = false;
}
return value;
}
/**
* Accepts expressions of these forms:
* foo
* foo.bar
* foo.bar.baz
* Also, allows a trailing dot, e.g. "foo.bar.", in which case all properties
* of foo.bar will be displayed.
*/
void DebugCLI::print(char *expr)
{
bool sawTrailingDot = false;
IValue* value = evaluate(expr, &sawTrailingDot);
AvmAssert(value != NULL);
if (sawTrailingDot)
{
// "foo." -- print all of object foo's properties
PropertyPrinter printer(core, this, value->get());
printer.iterate();
}
else
{
core->console << formatValue(value->get()) << '\n';
}
}
bool DebugCLI::filterException(avmplus::Exception *exception, bool /*willBeCaught*/)
{
// Filter exceptions when -d switch specified
if (activeFlag) {
core->console << "Exception has been thrown:\n"
<< core->string(exception->atom)
<< '\n';
enterDebugger();
return true;
}
return false;
}
void DebugCLI::info()
{
char *command = nextToken();
int cmd = infoCommandFor(command);
switch (cmd) {
case -1:
// ambiguous, we already printed error message
break;
case INFO_ARGS_CMD:
arguments(0);
break;
case INFO_LOCALS_CMD:
locals(0);
break;
case INFO_BREAK_CMD:
showBreakpoints();
break;
case INFO_UNKNOWN_CMD:
core->console << "Unknown command.\n";
break;
default:
core->console << "Command not implemented.\n";
break;
}
}
bool DebugCLI::debuggerInterruptOnEnter = false;
void DebugCLI::enterDebugger()
{
setCurrentSource( (core->callStack) ? (core->callStack->filename()) : 0 );
if (debuggerInterruptOnEnter) {
VMPI_debugBreak();
return;
}
for (;;) {
printIP();
core->console << "(asdb) ";
if (Platform::GetInstance()->getUserInput(commandLine, kMaxCommandLine) == NULL)
Platform::GetInstance()->exit(0);
commandLine[VMPI_strlen(commandLine)-1] = 0;
if (!commandLine[0]) {
VMPI_strcpy(commandLine, lastCommand);
} else {
VMPI_strcpy(lastCommand, commandLine);
}
currentToken = commandLine;
char *command = nextToken();
TRY(core, avmplus::kCatchAction_Ignore)
{
int cmd = commandFor(command);
switch (cmd) {
case -1:
// ambiguous, we already printed error message
break;
case CMD_COMMENT:
break;
case CMD_INFO:
info();
break;
case CMD_BREAK:
breakpoint(nextToken());
break;
case CMD_DELETE:
deleteBreakpoint(nextToken());
break;
case CMD_LIST:
list(nextToken());
break;
case CMD_UNKNOWN:
core->console << "Unknown command.\n";
break;
case CMD_QUIT:
Platform::GetInstance()->exit(0);
break;
case CMD_CONTINUE:
return;
case CMD_PRINT:
print(nextToken());
break;
case CMD_NEXT:
stepOver();
return;
case INFO_STACK_CMD:
bt();
break;
case CMD_FINISH:
stepOut();
return;
case CMD_HELP:
help();
break;
case CMD_STEP:
stepInto();
return;
case CMD_SET:
set();
break;
default:
core->console << "Command not implemented.\n";
break;
}
}
CATCH(avmplus::Exception *exception)
{
core->console << core->string(exception->atom) << '\n';
}
END_CATCH
END_TRY
}
}
void DebugCLI::setCurrentSource(avmplus::Stringp file)
{
if (!file)
return;
currentFile = file;
if (currentSource) {
delete [] currentSource;
currentSource = NULL;
currentSourceLen = 0;
}
// Open this file and suck it into memory
avmplus::StUTF8String currentFileUTF8(currentFile);
FileInputStream f(currentFileUTF8.c_str());
if (f.valid() && ((uint64_t)file->length() < UINT32_T_MAX)) { //cannot handle files > 4GB
currentSourceLen = (uint32_t) f.available();
currentSource = new char[currentSourceLen+1];
f.read(currentSource, currentSourceLen);
currentSource[currentSourceLen] = 0;
// whip through converting \r\n to space \n
for(int64_t i=0; i<currentSourceLen-1;i++) {
if (currentSource[i] == '\r' && currentSource[i+1] == '\n')
currentSource[i] = ' ';
}
} else if (warnMissingSource) {
core->console << "Could not find '" << currentFile << "'. Try running in the same directory as the .as file.\n";
warnMissingSource = false;
}
}
avmplus::Stringp DebugCLI::formatValue(avmplus::Atom value)
{
// An experiment: Printing XML and XMLList variables with their full toXMLString() form.
// if (core->isXMLorXMLList(value))
// {
// ScriptObject* object = AvmCore::atomToScriptObject(value);
// Multiname mn(core->getAnyPublicNamespace(), core->internStringLatin1("toXMLString"));
// Atom thisAtom = undefinedAtom;
// return core->string(object->toplevel()->callproperty(value, &mn, 1, &thisAtom, object->vtable));
// }
avmplus::StringBuffer sb(core);
sb << avmplus::asAtom(value);
return sb.toString();
}
//
// BreakAction
//
void BreakAction::print(avmplus::PrintWriter& out)
{
out << id << " at "
<< filename
<< ":" << (linenum) << '\n';
}
PropertyIterator::PropertyIterator(avmplus::AvmCore* core, avmplus::Atom atom)
: core(core)
{
if (avmplus::AvmCore::isObject(atom))
object = avmplus::AvmCore::atomToScriptObject(atom);
else
object = NULL;
}
void PropertyIterator::iterate()
{
if (!object)
return;
// Iterate over the object's traits
avmplus::Traits* t = object->traits();
while (t)
{
const avmplus::TraitsBindings* bindings = t->getTraitsBindings();
avmplus::StTraitsBindingsIterator iter(bindings);
while (iter.next()) {
avmplus::Stringp key = iter.key();
if (!key)
continue;
avmplus::Binding b = iter.value();
avmplus::Multiname mn(iter.ns(), key);
if (!process(&mn, avmplus::AvmCore::bindingKind(b)))
return;
}
t = t->base;
}
// Iterate over the object's dynamic properties
int index = 0;
while ((index = object->nextNameIndex(index)) != 0)
{
avmplus::Multiname mn(core->getAnyPublicNamespace(), core->string(object->nextName(index)));
if (!process(&mn, avmplus::BKIND_VAR))
return;
}
}
StPropertyFinder::StPropertyFinder(avmplus::AvmCore* core, avmplus::Atom atom, const char* propertyname)
: PropertyIterator(core, atom),
value(NULL),
propertyname(propertyname)
{
}
bool StPropertyFinder::process(avmplus::Multiname* key, avmplus::BindingKind /*bk*/)
{
if (key->getName()->equalsLatin1(propertyname))
{
value = new (core->gc) PropertyValue(object, *key);
return false; // stop iterating
}
return true;
}
PropertyPrinter::PropertyPrinter(avmplus::AvmCore* core, DebugCLI* debugger, avmplus::Atom atom)
: PropertyIterator(core, atom),
debugger(debugger)
{
}
bool PropertyPrinter::process(avmplus::Multiname* key, avmplus::BindingKind bk)
{
if (bk == avmplus::BKIND_CONST || bk == avmplus::BKIND_VAR || bk == avmplus::BKIND_GET || bk == avmplus::BKIND_GETSET)
{
avmplus::Atom value = object->toplevel()->getproperty(object->atom(), key, object->vtable);
switch (bk)
{
case avmplus::BKIND_CONST:
core->console << "const ";
break;
case avmplus::BKIND_VAR:
core->console << "var ";
break;
case avmplus::BKIND_GET:
case avmplus::BKIND_GETSET:
core->console << "function get ";
break;
}
core->console << avmplus::Multiname::FormatNameOnly(key);
if (bk == avmplus::BKIND_GET || bk == avmplus::BKIND_GETSET)
core->console << "()";
core->console << " = " << debugger->formatValue(value) << '\n';
}
return true;
}
}
#endif