1 /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
2 /* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
3 /* ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
16 * The Original Code is [Open Source Virtual Machine.].
18 * The Initial Developer of the Original Code is
19 * Adobe System Incorporated.
20 * Portions created by the Initial Developer are Copyright (C) 2004-2006
21 * the Initial Developer. All Rights Reserved.
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
46 * The array of top level commands that we support.
47 * They are placed into a Nx2 array, whereby the first component
48 * is a String which is the command and the 2nd component is the
49 * integer identifier for the command.
51 * NOTE: order matters! For the case of a single character
52 * match, we let the first hit act like an unambiguous match.
54 DebugCLI::StringInt
DebugCLI::commandArray
[] =
56 { "break", CMD_BREAK
},
57 { "bt", INFO_STACK_CMD
},
58 { "continue", CMD_CONTINUE
},
59 { "delete", CMD_DELETE
},
60 { "finish", CMD_FINISH
},
66 { "print", CMD_PRINT
},
70 { "where", INFO_STACK_CMD
},
77 DebugCLI::StringInt
DebugCLI::infoCommandArray
[] =
79 { "arguments", INFO_ARGS_CMD
},
80 { "breakpoints", INFO_BREAK_CMD
},
81 { "files", INFO_FILES_CMD
},
82 { "functions", INFO_FUNCTIONS_CMD
},
83 { "locals", INFO_LOCALS_CMD
},
84 { "stack", INFO_STACK_CMD
},
88 DebugCLI::DebugCLI(AvmCore
*core
, Debugger::TraceLevel tracelevel
)
89 : Debugger(core
, tracelevel
)
92 warnMissingSource
= true;
98 delete [] currentSource
;
103 char* DebugCLI::nextToken()
105 char *out
= currentToken
;
107 while (*currentToken
) {
108 if (*currentToken
== ' ' || *currentToken
== '\r' || *currentToken
== '\n' || *currentToken
== '\t') {
114 currentToken
= *currentToken
? currentToken
: NULL
;
120 * Attempt to match given the given string against our set of commands
121 * @return the command code that was hit.
123 int DebugCLI::determineCommand(DebugCLI::StringInt cmdList
[],
127 if (!input
) return INFO_UNKNOWN_CMD
;
129 int inputLen
= (int)VMPI_strlen(input
);
131 // first check for a comment
132 if (input
[0] == '#') {
137 bool ambiguous
= false;
139 for (int i
=0; cmdList
[i
].text
; i
++) {
140 if (!VMPI_strncmp(input
, cmdList
[i
].text
, inputLen
)) {
151 * - No hits, return unknown and let our caller
153 * - We match unambiguously or we have 1 or more matches
154 * and the input is a single character. We then take the
155 * first hit as our command.
156 * - If we have multiple hits then we dump a 'ambiguous' message
160 // no command match return unknown
163 // only 1 match or our input is 1 character or first match is exact
164 else if (!ambiguous
|| inputLen
== 1 || !VMPI_strcmp(cmdList
[match
].text
, input
)) {
165 return cmdList
[match
].id
;
168 // matches more than one command dump message and go
169 core
->console
<< "Ambiguous command '" << input
<< "': ";
171 for (int i
=0; cmdList
[i
].text
; i
++) {
172 if (!VMPI_strncmp(cmdList
[i
].text
, input
, inputLen
)) {
174 core
->console
<< ", ";
178 core
->console
<< cmdList
[i
].text
;
181 core
->console
<< ".\n";
186 const char* DebugCLI::commandNumberToCommandName(DebugCLI::StringInt cmdList
[],
189 for (int i
= 0; cmdList
[i
].text
; i
++)
191 if (cmdList
[i
].id
== cmdNumber
)
192 return cmdList
[i
].text
;
198 bool DebugCLI::printFrame(int k
)
201 int count
, line
= -1;
202 SourceInfo
* src
= NULL
;
203 AvmCore
* core
= AvmCore::getActiveCore();
204 DebugFrame
* frame
= core
->debugger()->frameAt(k
);
205 if (frame
== NULL
) return false;
207 // source information
208 frame
->sourceLocation(src
, line
);
210 core
->console
<< "#" << k
<< " ";
213 Atom a
= nullObjectAtom
;
215 core
->console
<< asAtom(a
) << ".";
218 MethodInfo
* info
= functionFor(src
, line
);
220 core
->console
<< info
->getMethodName();
223 if (frame
->methodName(name
))
224 core
->console
<< name
;
226 core
->console
<< "???";
229 core
->console
<< "(";
232 frame
->arguments(ptr
, count
);
233 for (int i
=0; i
<count
; i
++)
235 // write out the name
238 Stringp nm
= info
->getArgName(i
);
239 if (nm
!= core
->kundefined
)
240 core
->console
<< nm
<< "=";
243 core
->console
<< asAtom(*ptr
++);
245 core
->console
<< ",";
247 core
->console
<< ") at ";
249 core
->console
<< src
->name() << ":" << (line
) << "\n";
251 core
->console
<< "???\n";
258 AvmCore
* core
= AvmCore::getActiveCore();
259 //core->stackTrace->dump(core->console);
260 //core->console << '\n';
262 // obtain information about each frame
263 int frameCount
= core
->debugger()->frameCount();
264 for(int k
=0; k
<frameCount
; k
++)
270 void DebugCLI::help()
274 // "help info" shows sub-commands of the "info" command
275 char* next
= nextToken();
276 if (next
&& commandFor(next
) == CMD_INFO
)
277 cmd
= infoCommandArray
;
283 core
->console
<< cmd
->text
;
284 if (cmd
->id
== CMD_INFO
)
285 core
->console
<< " (see 'help " << cmd
->text
<< "' for sub-commands)";
286 core
->console
<< '\n';
291 MethodInfo
* DebugCLI::functionFor(SourceInfo
* src
, int line
)
293 MethodInfo
* info
= NULL
;
296 // find the function at this location
297 int size
= src
->functionCount();
298 for(int i
=0; i
<size
; i
++)
300 MethodInfo
* m
= src
->functionAt(i
);
301 if (line
>= m
->firstSourceLine() && line
<= m
->lastSourceLine())
312 char* DebugCLI::lineStart(int linenum
)
314 if (!currentSource
&& currentFile
)
315 setCurrentSource(currentFile
);
317 if (!currentSource
) {
321 // linenumbers map to zero based array entry
322 char *ptr
= currentSource
;
323 for (int i
=0; i
<linenum
; i
++) {
324 // Scan to end of line
325 while (*ptr
!= '\n') {
331 // Advance past newline
337 void DebugCLI::displayLines(int line
, int count
)
341 core
->console
<< currentFile
;
343 core
->console
<< "<unknown>";
344 core
->console
<<":"<<line
<<" ";
349 char* ptr
= lineStart(lineAt
-1);
354 count
++; // reset line number to beginning skip this iteration
361 core
->console
<< (lineAt
) << ": ";
362 while (*ptr
&& *ptr
!= '\n')
363 core
->console
<< *ptr
++;
364 core
->console
<< '\n';
371 void DebugCLI::list(const char* line
)
373 int currentLine
= (core
->callStack
) ? core
->callStack
->linenum() : 0;
374 int linenum
= (line
) ? VMPI_atoi(line
) : currentLine
;
375 displayLines(linenum
, 10);
378 void DebugCLI::printIP()
380 int line
= (core
->callStack
) ? core
->callStack
->linenum() : 0;
381 displayLines(line
, 1);
384 void DebugCLI::breakpoint(char *location
)
388 core
->console
<< "Bad format, should be: 'break [filename:]line'\n";
392 Stringp filename
= currentFile
;
393 char *colon
= VMPI_strchr(location
, ':');
397 filename
= core
->internStringLatin1(location
);
401 if (abcCount() == 0) {
402 core
->console
<< "No abc file loaded\n";
406 SourceFile
* sourceFile
= NULL
;
407 for (int i
= 0, n
= abcCount(); i
< n
; ++i
)
409 AbcFile
* abcFile
= (AbcFile
*)abcAt(i
);
410 sourceFile
= abcFile
->sourceNamed(filename
);
415 if (sourceFile
== NULL
) {
416 core
->console
<< "No source available; can't set breakpoint.\n";
420 int targetLine
= VMPI_atoi(location
);
422 if (breakpointSet(sourceFile
, targetLine
)) {
423 int breakpointId
= ++breakpointCount
;
424 core
->console
<< "Breakpoint " << breakpointId
<< ": file "
426 << ", " << (targetLine
) << ".\n";
428 BreakAction
*breakAction
= new (core
->GetGC()) BreakAction(sourceFile
,
432 breakAction
->prev
= lastBreakAction
;
433 if (lastBreakAction
) {
434 lastBreakAction
->next
= breakAction
;
436 firstBreakAction
= breakAction
;
438 lastBreakAction
= breakAction
;
440 core
->console
<< "Could not locate specified line.\n";
444 void DebugCLI::showBreakpoints()
446 BreakAction
*breakAction
= firstBreakAction
;
447 while (breakAction
) {
448 breakAction
->print(core
->console
);
449 breakAction
= breakAction
->next
;
453 void DebugCLI::deleteBreakpoint(char *idstr
)
455 int id
= VMPI_atoi(idstr
);
457 BreakAction
*breakAction
= firstBreakAction
;
458 while (breakAction
) {
459 if (breakAction
->id
== id
) {
462 breakAction
= breakAction
->next
;
466 if (breakAction
->prev
) {
467 breakAction
->prev
->next
= breakAction
->next
;
469 firstBreakAction
= breakAction
->next
;
471 if (breakAction
->next
) {
472 breakAction
->next
->prev
= breakAction
->prev
;
474 lastBreakAction
= breakAction
->prev
;
476 if (breakpointClear(breakAction
->sourceFile
, breakAction
->linenum
)) {
477 core
->console
<< "Breakpoint " << id
<< " deleted.\n";
479 core
->console
<< "Internal error; could not delete breakpoint.\n";
482 core
->console
<< "Could not find breakpoint.\n";
486 void DebugCLI::locals(int frameNumber
)
490 SourceInfo
* src
= NULL
;
491 AvmCore
* core
= AvmCore::getActiveCore();
492 DebugFrame
* frame
= core
->debugger()->frameAt(frameNumber
);
493 if (frame
== NULL
) return;
495 // source information
496 frame
->sourceLocation(src
, line
);
499 MethodInfo
* info
= functionFor(src
, line
);
501 frame
->locals(ptr
, count
);
502 for(int i
=0; i
<count
; i
++) {
503 // write out the name
504 Stringp nm
= info
->getLocalName(i
);
505 core
->console
<< i
<< ": ";
506 if (nm
!= core
->kundefined
)
509 core
->console
<< "<local_" << i
<< ">";
510 core
->console
<< " = " << asAtom(*ptr
++) << "\n";
515 void DebugCLI::arguments(int frameNumber
)
519 AvmCore
* core
= AvmCore::getActiveCore();
520 DebugFrame
* frame
= core
->debugger()->frameAt(frameNumber
);
521 if (frame
&& frame
->arguments(arr
, count
)) {
522 for (int i
= 0; i
< count
; i
++) {
524 frame
->argumentName(i
, nm
);
525 core
->console
<< i
<< ": " << nm
<< " = " << asAtom(*arr
++) << "\n";
530 Atom
DebugCLI::ease2Atom(const char* to
, Atom baseline
)
532 // first make a string out of the value
533 Atom a
= core
->newStringLatin1(to
)->atom();
535 // using the type of baseline try to convert to into an appropriate Atom
536 if (core
->isNumber(baseline
))
537 return core
->numberAtom(a
);
538 else if (core
->isBoolean(baseline
))
539 return AvmCore::booleanAtom(a
);
541 return nullStringAtom
;
545 * Given a pointer, trims leading and trailing whitespace. Note, this will
546 * modify the buffer -- it trims trailing whitespace by writing a null
549 * Returns the first offset past leading whitespace.
551 static char* trim(char* ptr
)
553 while (*ptr
&& (*ptr
==' ' || *ptr
=='\t'))
560 while (ptr
>start
&& (ptr
[-1]==' ' || ptr
[-1]=='\t'))
569 char* line
= currentToken
;
571 char* equ
= VMPI_strchr(currentToken
, '=');
581 if (!expr
|| !*expr
|| !value
|| !*value
)
583 core
->console
<< " Bad format, should be: 'set {variable} = {value}' \n";
587 bool sawTrailingDot
= false;
588 IValue
* lvalue
= evaluate(expr
, &sawTrailingDot
);
589 IValue
* rvalue
= evaluate(value
, &sawTrailingDot
);
590 AvmAssert(lvalue
!= NULL
);
591 AvmAssert(rvalue
!= NULL
);
592 if (!lvalue
->isLValue())
593 core
->console
<< " Can't assign to " << expr
<< '\n';
595 lvalue
->set(rvalue
->get());
599 class ScopeBuilder
: public CallStackNode::IScopeChainEnumerator
602 ScopeBuilder(MMgc::GC
* gc
) : scopeChain(gc
, kListInitialCapacity
) { }
603 /*virtual*/ void addScope(Atom scope
)
605 // null/undefined are not legal entries for scope, but
606 // enumerateScopeChainAtoms() can hand them to us, for entries
607 // within the max_scope range that aren't in use. just ignore 'em.
609 if (AvmCore::isNullOrUndefined(scope
))
612 scopeChain
.add(scope
);
618 void DebugCLI::throwUndefinedVarError(const char* name
)
620 Stringp errorMessage
= core
->newStringLatin1("Error: Variable ");
621 errorMessage
= errorMessage
->appendLatin1(name
);
622 errorMessage
= errorMessage
->appendLatin1(" is not defined.");
623 core
->throwAtom(errorMessage
->atom());
626 IValue
* DebugCLI::getValue(const char *name
)
628 DebugStackFrame
* frame
= (DebugStackFrame
*)frameAt(0);
630 frame
->dhis(thisAtom
);
632 Stringp namestr
= core
->internStringLatin1(name
, -1);
633 if (VMPI_strcmp(name
, "this") == 0)
634 return new (core
->gc
) ConstantValue(thisAtom
);
635 if (VMPI_strcmp(name
, "NaN") == 0)
636 return new (core
->gc
) ConstantValue(core
->kNaN
);
637 if (namestr
== core
->kfalse
)
638 return new (core
->gc
) ConstantValue(falseAtom
);
639 if (namestr
== core
->ktrue
)
640 return new (core
->gc
) ConstantValue(trueAtom
);
641 if (namestr
== core
->knull
)
642 return new (core
->gc
) ConstantValue(nullObjectAtom
);
643 if (namestr
== core
->kundefined
)
644 return new (core
->gc
) ConstantValue(undefinedAtom
);
645 if (name
[0] == '-' || VMPI_isdigit(name
[0]))
646 return new (core
->gc
) ConstantValue(core
->numberAtom(namestr
->atom()));
647 if (name
[0] == '\'' || name
[0] == '"') // String literal
649 int32_t length
= namestr
->length();
650 if (length
>= 2 && namestr
->charAt(length
-1) == namestr
->charAt(0))
652 // Note, this doesn't do any escaping
653 return new (core
->gc
) ConstantValue(namestr
->substr(1, length
-2)->atom());
656 if (name
[0] == '<') // XML or XMLList literal
658 if (AvmCore::isObject(thisAtom
))
660 Toplevel
* toplevel
= AvmCore::atomToScriptObject(thisAtom
)->toplevel();
662 return new (core
->gc
) ConstantValue(toplevel
->xmlListClass()->ToXMLList(namestr
->atom()));
664 return new (core
->gc
) ConstantValue(toplevel
->xmlClass()->ToXML(namestr
->atom()));
672 // See if the name matches a function argument or local
673 frame
->sourceLocation(src
, line
);
676 MethodInfo
* info
= functionFor(src
, line
);
679 // see if the name matches a function argument
680 frame
->arguments(ptr
, count
);
681 for(int i
=0; i
<count
; i
++)
683 Stringp arg
= info
->getArgName(i
);
684 if (arg
->equalsLatin1(name
))
687 return new (core
->gc
) ArgumentValue(frame
, i
);
691 // see if the name matches a local
692 frame
->locals(ptr
, count
);
693 for(int i
=0; i
<count
; i
++)
695 Stringp local
= info
->getLocalName(i
);
696 if ( local
->equalsLatin1(name
))
699 return new (core
->gc
) LocalValue(frame
, i
);
705 // See if the name matches a property on the scope chain (which includes
706 // the 'this' object)
709 MethodEnv
* env
= frame
->trace
->env();
712 ScopeBuilder
scopeBuilder(core
->GetGC());
713 frame
->trace
->enumerateScopeChainAtoms(scopeBuilder
);
715 for (uint32_t i
=0, n
=scopeBuilder
.scopeChain
.length(); i
<n
; ++i
)
717 StPropertyFinder
finder(core
, scopeBuilder
.scopeChain
.get(i
), name
);
725 // Look for globals like 'Number'
726 MethodEnv
* env
= frame
->trace
->env();
729 Multiname
mn(core
->getAnyPublicNamespace(),
730 core
->internStringLatin1(name
));
731 ScriptEnv
* script
= core
->domainMgr()->findScriptEnvInDomainEnvByMultiname(env
->domainEnv(), mn
);
732 if (script
!= (ScriptEnv
*)BIND_NONE
&& script
!= (ScriptEnv
*)BIND_AMBIGUOUS
)
734 ScriptObject
* global
= script
->global
;
737 return new (core
->gc
) PropertyValue(global
, mn
);
742 throwUndefinedVarError(name
);
743 return NULL
; // unreachable
747 * Accepts expressions of these forms:
752 * Also allows literals: boolean, string, numeric, XML/XMLList,
755 * If the expression ends with a dot, e.g. "foo.bar.", then
756 * *outSawTrailingDot is set to true. This is used by the
757 * "print" command to allow "print foo." to print all members
760 IValue
* DebugCLI::evaluate(char *expr
, bool* outSawTrailingDot
)
763 IValue
* value
= NULL
;
764 bool firstPart
= true;
766 *outSawTrailingDot
= false;
771 char *dot
= VMPI_strchr(expr
, '.');
786 value
= getValue(name
);
787 AvmAssert(value
!= NULL
);
793 // "foo.bar" -- find property "bar" of object "foo"
794 Atom parent
= value
->get();
795 StPropertyFinder
finder(core
, parent
, name
);
797 value
= finder
.value
;
800 if (AvmCore::isObject(parent
))
802 // Since we didn't find an existing property, we'll just construct
803 // one. If the parent object is dynamic, then calling get() on the
804 // PropertyVaulue we create will return undefined, and calling set()
805 // on it will define a new property. If the parent object is not
806 // dynamic, then get() and set() will throw exceptions. Either way,
807 // that's the correct behavior.
808 Multiname
mn(core
->getAnyPublicNamespace(),
809 core
->internStringLatin1(name
));
810 value
= new (core
->gc
) PropertyValue(AvmCore::atomToScriptObject(parent
), mn
);
814 if (dot
) *dot
= '.'; // restore for our caller
815 throwUndefinedVarError(name
);
816 return NULL
; // unreachable
823 *outSawTrailingDot
= true;
827 if (dot
) *dot
= '.'; // restore for our caller
835 * Accepts expressions of these forms:
839 * Also, allows a trailing dot, e.g. "foo.bar.", in which case all properties
840 * of foo.bar will be displayed.
842 void DebugCLI::print(char *expr
)
844 bool sawTrailingDot
= false;
845 IValue
* value
= evaluate(expr
, &sawTrailingDot
);
846 AvmAssert(value
!= NULL
);
850 // "foo." -- print all of object foo's properties
851 PropertyPrinter
printer(core
, this, value
->get());
856 core
->console
<< formatValue(value
->get()) << '\n';
860 bool DebugCLI::filterException(Exception
*exception
, bool /*willBeCaught*/)
862 // Filter exceptions when -d switch specified
864 core
->console
<< "Exception has been thrown:\n"
865 << core
->string(exception
->atom
)
873 void DebugCLI::info()
875 char *command
= nextToken();
876 int cmd
= infoCommandFor(command
);
880 // ambiguous, we already printed error message
885 case INFO_LOCALS_CMD
:
891 case INFO_UNKNOWN_CMD
:
892 core
->console
<< "Unknown command.\n";
895 core
->console
<< "Command not implemented.\n";
900 bool DebugCLI::debuggerInterruptOnEnter
= false;
901 void DebugCLI::enterDebugger()
903 setCurrentSource( (core
->callStack
) ? (core
->callStack
->filename()) : 0 );
904 if (debuggerInterruptOnEnter
) {
912 core
->console
<< "(asdb) ";
913 if (Platform::GetInstance()->getUserInput(commandLine
, kMaxCommandLine
) == NULL
)
914 Platform::GetInstance()->exit(0);
916 commandLine
[VMPI_strlen(commandLine
)-1] = 0;
918 if (!commandLine
[0]) {
919 VMPI_strcpy(commandLine
, lastCommand
);
921 VMPI_strcpy(lastCommand
, commandLine
);
924 currentToken
= commandLine
;
926 char *command
= nextToken();
927 int cmd
= commandFor(command
);
929 TRY(core
, kCatchAction_Ignore
)
933 // ambiguous, we already printed error message
941 breakpoint(nextToken());
944 deleteBreakpoint(nextToken());
950 core
->console
<< "Unknown command.\n";
953 Platform::GetInstance()->exit(0);
979 core
->console
<< "Command not implemented.\n";
983 CATCH(Exception
*exception
)
985 core
->console
<< core
->string(exception
->atom
) << '\n';
992 void DebugCLI::setCurrentSource(Stringp file
)
1000 delete [] currentSource
;
1001 currentSource
= NULL
;
1002 currentSourceLen
= 0;
1005 // Open this file and suck it into memory
1006 StUTF8String
currentFileUTF8(currentFile
);
1007 FileInputStream
f(currentFileUTF8
.c_str());
1008 if (f
.valid() && ((uint64_t)file
->length() < UINT32_T_MAX
)) { //cannot handle files > 4GB
1009 currentSourceLen
= (uint32_t) f
.available();
1010 currentSource
= new char[currentSourceLen
+1];
1011 f
.read(currentSource
, currentSourceLen
);
1012 currentSource
[currentSourceLen
] = 0;
1014 // whip through converting \r\n to space \n
1015 for(int64_t i
=0; i
<currentSourceLen
-1;i
++) {
1016 if (currentSource
[i
] == '\r' && currentSource
[i
+1] == '\n')
1017 currentSource
[i
] = ' ';
1019 } else if (warnMissingSource
) {
1020 core
->console
<< "Could not find '" << currentFile
<< "'. Try running in the same directory as the .as file.\n";
1021 warnMissingSource
= false;
1025 Stringp
DebugCLI::formatValue(Atom value
)
1027 // An experiment: Printing XML and XMLList variables with their full toXMLString() form.
1028 // if (core->isXMLorXMLList(value))
1030 // ScriptObject* object = AvmCore::atomToScriptObject(value);
1031 // Multiname mn(core->getAnyPublicNamespace(), core->internStringLatin1("toXMLString"));
1032 // Atom thisAtom = undefinedAtom;
1033 // return core->string(object->toplevel()->callproperty(value, &mn, 1, &thisAtom, object->vtable));
1036 StringBuffer
sb(core
);
1037 sb
<< asAtom(value
);
1038 return sb
.toString();
1045 void BreakAction::print(PrintWriter
& out
)
1049 << ":" << (linenum
) << '\n';
1052 PropertyIterator::PropertyIterator(AvmCore
* core
, Atom atom
)
1055 if (AvmCore::isObject(atom
))
1056 object
= AvmCore::atomToScriptObject(atom
);
1061 void PropertyIterator::iterate()
1066 // Iterate over the object's traits
1067 Traits
* t
= object
->traits();
1070 const TraitsBindings
* bindings
= t
->getTraitsBindings();
1071 StTraitsBindingsIterator
iter(bindings
);
1072 while (iter
.next()) {
1073 Stringp key
= iter
.key();
1076 Binding b
= iter
.value();
1077 Multiname
mn(iter
.ns(), key
);
1078 if (!process(&mn
, AvmCore::bindingKind(b
)))
1084 // Iterate over the object's dynamic properties
1086 while ((index
= object
->nextNameIndex(index
)) != 0)
1088 Multiname
mn(core
->getAnyPublicNamespace(), core
->string(object
->nextName(index
)));
1089 if (!process(&mn
, BKIND_VAR
))
1094 StPropertyFinder::StPropertyFinder(AvmCore
* core
, Atom atom
, const char* propertyname
)
1095 : PropertyIterator(core
, atom
),
1097 propertyname(propertyname
)
1101 bool StPropertyFinder::process(Multiname
* key
, BindingKind
/*bk*/)
1103 if (key
->getName()->equalsLatin1(propertyname
))
1105 value
= new (core
->gc
) PropertyValue(object
, *key
);
1106 return false; // stop iterating
1112 PropertyPrinter::PropertyPrinter(AvmCore
* core
, DebugCLI
* debugger
, Atom atom
)
1113 : PropertyIterator(core
, atom
),
1118 bool PropertyPrinter::process(Multiname
* key
, BindingKind bk
)
1120 if (bk
== BKIND_CONST
|| bk
== BKIND_VAR
|| bk
== BKIND_GET
|| bk
== BKIND_GETSET
)
1122 Atom value
= object
->toplevel()->getproperty(object
->atom(), key
, object
->vtable
);
1126 core
->console
<< "const ";
1129 core
->console
<< "var ";
1133 core
->console
<< "function get ";
1136 core
->console
<< Multiname::FormatNameOnly(key
);
1137 if (bk
== BKIND_GET
|| bk
== BKIND_GETSET
)
1138 core
->console
<< "()";
1139 core
->console
<< " = " << debugger
->formatValue(value
) << '\n';