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 * The StringIntArray object provides a convenient wrapper class
52 * that implements the List interface.
54 * NOTE: order matters! For the case of a single character
55 * match, we let the first hit act like an unambiguous match.
57 DebugCLI::StringIntArray
DebugCLI::commandArray
[] =
59 { "break", CMD_BREAK
},
60 { "bt", INFO_STACK_CMD
},
61 { "continue", CMD_CONTINUE
},
62 { "delete", CMD_DELETE
},
63 { "finish", CMD_FINISH
},
68 { "print", CMD_PRINT
},
74 { "where", INFO_STACK_CMD
},
81 DebugCLI::StringIntArray
DebugCLI::infoCommandArray
[] =
83 { "arguments", INFO_ARGS_CMD
},
84 { "breakpoints", INFO_BREAK_CMD
},
85 { "files", INFO_FILES_CMD
},
86 { "functions", INFO_FUNCTIONS_CMD
},
87 { "locals", INFO_LOCALS_CMD
},
88 { "stack", INFO_STACK_CMD
},
95 DebugCLI::StringIntArray
DebugCLI::showCommandArray
[] =
97 { "break", SHOW_BREAK_CMD
},
98 { "functions", SHOW_FUNC_CMD
},
99 { "memory", SHOW_MEM_CMD
},
100 { "variable", SHOW_VAR_CMD
},
104 DebugCLI::DebugCLI(AvmCore
*core
, Debugger::TraceLevel tracelevel
)
105 : Debugger(core
, tracelevel
)
107 currentSourceLen
= 0;
108 warnMissingSource
= true;
111 DebugCLI::~DebugCLI()
114 delete [] currentSource
;
115 currentSource
= NULL
;
119 char* DebugCLI::nextToken()
121 char *out
= currentToken
;
123 while (*currentToken
) {
124 if (*currentToken
== ' ' || *currentToken
== '\r' || *currentToken
== '\n' || *currentToken
== '\t') {
130 currentToken
= *currentToken
? currentToken
: NULL
;
136 * Attempt to match given the given string against our set of commands
137 * @return the command code that was hit.
139 int DebugCLI::determineCommand(DebugCLI::StringIntArray cmdList
[],
143 if (!input
) return INFO_UNKNOWN_CMD
;
145 int inputLen
= (int)VMPI_strlen(input
);
147 // first check for a comment
148 if (input
[0] == '#') {
153 bool ambiguous
= false;
155 for (int i
=0; cmdList
[i
].text
; i
++) {
156 if (!VMPI_strncmp(input
, cmdList
[i
].text
, inputLen
)) {
167 * - No hits, return unknown and let our caller
169 * - We match unambiguously or we have 1 or more matches
170 * and the input is a single character. We then take the
171 * first hit as our command.
172 * - If we have multiple hits then we dump a 'ambiguous' message
176 // no command match return unknown
179 // only 1 match or our input is 1 character or first match is exact
180 else if (!ambiguous
|| inputLen
== 1 || !VMPI_strcmp(cmdList
[match
].text
, input
)) {
181 return cmdList
[match
].id
;
184 // matches more than one command dump message and go
185 core
->console
<< "Ambiguous command '" << input
<< "': ";
187 for (int i
=0; cmdList
[i
].text
; i
++) {
188 if (!VMPI_strncmp(cmdList
[i
].text
, input
, inputLen
)) {
190 core
->console
<< ", ";
194 core
->console
<< cmdList
[i
].text
;
197 core
->console
<< ".\n";
202 const char* DebugCLI::commandNumberToCommandName(DebugCLI::StringIntArray cmdList
[],
205 for (int i
= 0; cmdList
[i
].text
; i
++)
207 if (cmdList
[i
].id
== cmdNumber
)
208 return cmdList
[i
].text
;
214 bool DebugCLI::printFrame(int k
)
217 int count
, line
= -1;
218 SourceInfo
* src
= NULL
;
219 AvmCore
* core
= AvmCore::getActiveCore();
220 DebugFrame
* frame
= core
->debugger()->frameAt(k
);
221 if (frame
== NULL
) return false;
223 // source information
224 frame
->sourceLocation(src
, line
);
226 core
->console
<< "#" << k
<< " ";
229 Atom a
= nullObjectAtom
;
231 core
->console
<< core
->format(a
) << ".";
234 MethodInfo
* info
= functionFor(src
, line
);
236 core
->console
<< info
->getMethodName();
239 if (frame
->methodName(name
))
240 core
->console
<< name
;
242 core
->console
<< "???";
245 core
->console
<< "(";
248 frame
->arguments(ptr
, count
);
249 for (int i
=0; i
<count
; i
++)
251 // write out the name
253 if (info
&& frame
->argumentName(i
, nm
))
254 core
->console
<< nm
<< "=";
256 core
->console
<< core
->format(*ptr
++);
258 core
->console
<< ",";
260 core
->console
<< ") at ";
262 core
->console
<< src
->name() << ":" << (line
) << "\n";
264 core
->console
<< "???\n";
271 AvmCore
* core
= AvmCore::getActiveCore();
272 //core->stackTrace->dump(core->console);
273 //core->console << '\n';
275 // obtain information about each frame
276 int frameCount
= core
->debugger()->frameCount();
277 for(int k
=0; k
<frameCount
; k
++)
283 MethodInfo
* DebugCLI::functionFor(SourceInfo
* src
, int line
)
285 MethodInfo
* info
= NULL
;
288 // find the function at this location
289 int size
= src
->functionCount();
290 for(int i
=0; i
<size
; i
++)
292 MethodInfo
* m
= src
->functionAt(i
);
293 if (line
>= m
->firstSourceLine() && line
<= m
->lastSourceLine())
304 char* DebugCLI::lineStart(int linenum
)
306 if (!currentSource
&& currentFile
)
307 setCurrentSource(currentFile
);
309 if (!currentSource
) {
313 // linenumbers map to zero based array entry
314 char *ptr
= currentSource
;
315 for (int i
=0; i
<linenum
; i
++) {
316 // Scan to end of line
317 while (*ptr
!= '\n') {
323 // Advance past newline
329 void DebugCLI::displayLines(int line
, int count
)
333 core
->console
<< currentFile
;
335 core
->console
<< "<unknown>";
336 core
->console
<<":"<<line
<<" ";
341 char* ptr
= lineStart(lineAt
-1);
346 count
++; // reset line number to beginning skip this iteration
353 core
->console
<< (lineAt
) << ": ";
354 while (*ptr
&& *ptr
!= '\n')
355 core
->console
<< *ptr
++;
356 core
->console
<< '\n';
363 void DebugCLI::list(const char* line
)
365 int currentLine
= (core
->callStack
) ? core
->callStack
->linenum() : 0;
366 int linenum
= (line
) ? VMPI_atoi(line
) : currentLine
;
367 displayLines(linenum
, 10);
370 void DebugCLI::printIP()
372 int line
= (core
->callStack
) ? core
->callStack
->linenum() : 0;
373 displayLines(line
, 1);
376 void DebugCLI::breakpoint(char *location
)
378 Stringp filename
= currentFile
;
379 char *colon
= VMPI_strchr(location
, ':');
383 filename
= core
->internStringLatin1(location
);
387 if (abcCount() == 0) {
388 core
->console
<< "No abc file loaded\n";
392 SourceFile
* sourceFile
= NULL
;
393 for (int i
= 0, n
= abcCount(); i
< n
; ++i
)
395 AbcFile
* abcFile
= (AbcFile
*)abcAt(i
);
396 sourceFile
= abcFile
->sourceNamed(filename
);
401 if (sourceFile
== NULL
) {
402 core
->console
<< "No source available; can't set breakpoint.\n";
406 int targetLine
= VMPI_atoi(location
);
408 int breakpointId
= ++breakpointCount
;
410 if (breakpointSet(sourceFile
, targetLine
)) {
411 core
->console
<< "Breakpoint " << breakpointId
<< ": file "
413 << ", " << (targetLine
) << ".\n";
415 BreakAction
*breakAction
= new (core
->GetGC()) BreakAction(sourceFile
,
419 breakAction
->prev
= lastBreakAction
;
420 if (lastBreakAction
) {
421 lastBreakAction
->next
= breakAction
;
423 firstBreakAction
= breakAction
;
425 lastBreakAction
= breakAction
;
427 core
->console
<< "Could not locate specified line.\n";
431 void DebugCLI::showBreakpoints()
433 BreakAction
*breakAction
= firstBreakAction
;
434 while (breakAction
) {
435 breakAction
->print(core
->console
);
436 breakAction
= breakAction
->next
;
440 void DebugCLI::deleteBreakpoint(char *idstr
)
442 int id
= VMPI_atoi(idstr
);
444 BreakAction
*breakAction
= firstBreakAction
;
445 while (breakAction
) {
446 if (breakAction
->id
== id
) {
449 breakAction
= breakAction
->next
;
453 if (breakAction
->prev
) {
454 breakAction
->prev
->next
= breakAction
->next
;
456 firstBreakAction
= breakAction
->next
;
458 if (breakAction
->next
) {
459 breakAction
->next
->prev
= breakAction
->prev
;
461 lastBreakAction
= breakAction
->prev
;
463 if (breakpointClear(breakAction
->sourceFile
, breakAction
->linenum
)) {
464 core
->console
<< "Breakpoint " << id
<< " deleted.\n";
466 core
->console
<< "Internal error; could not delete breakpoint.\n";
469 core
->console
<< "Could not find breakpoint.\n";
473 Atom
DebugCLI::autoAtomAt(DebugFrame
* frame
, int index
, AutoVarKind kind
) {
479 success
= frame
->locals(arr
, count
);
483 success
= frame
->arguments(arr
, count
);
487 return unreachableAtom
;
490 if (index
>= 0 && index
< count
) {
494 return unreachableAtom
;
498 Atom
DebugCLI::autoAtomKindAt(int frameNumber
, int autoIndex
, AutoVarKind kind
) {
499 AvmCore
* core
= AvmCore::getActiveCore();
500 DebugFrame
* frame
= core
->debugger()->frameAt(frameNumber
);
501 if (!frame
) return unreachableAtom
;
502 else return atomKind(autoAtomAt(frame
, autoIndex
, kind
));
505 ScriptObject
* DebugCLI::autoVarAsObject(int frameNumber
, int index
, AutoVarKind kind
)
507 AvmCore
* core
= AvmCore::getActiveCore();
508 DebugFrame
* frame
= core
->debugger()->frameAt(frameNumber
);
509 if (!frame
) return NULL
; // should have tested for error earlier
510 return AvmCore::atomToScriptObject(autoAtomAt(frame
, index
, kind
));
513 Stringp
DebugCLI::autoVarAsString(int frameNumber
, int index
, AutoVarKind kind
)
515 AvmCore
* core
= AvmCore::getActiveCore();
516 DebugFrame
* frame
= core
->debugger()->frameAt(frameNumber
);
517 if (!frame
) return NULL
; // should have tested for error earlier
518 return AvmCore::atomToString(autoAtomAt(frame
, index
, kind
));
521 bool DebugCLI::autoVarAsBoolean(int frameNumber
, int index
, AutoVarKind kind
)
523 AvmCore
* core
= AvmCore::getActiveCore();
524 DebugFrame
* frame
= core
->debugger()->frameAt(frameNumber
);
525 if (!frame
) return false; // should have tested for error earlier
526 Atom value
= autoAtomAt(frame
, index
, kind
);
527 return value
== trueAtom
? true : false;
530 double DebugCLI::autoVarAsInteger(int frameNumber
, int index
, AutoVarKind kind
) {
531 AvmCore
* core
= AvmCore::getActiveCore();
532 DebugFrame
* frame
= core
->debugger()->frameAt(frameNumber
);
533 if (!frame
) return MathUtils::kNaN
; // should have tested for error earlier
534 return AvmCore::number_d(autoAtomAt(frame
, index
, kind
));
537 double DebugCLI::autoVarAsDouble(int frameNumber
, int index
, AutoVarKind kind
) {
538 AvmCore
* core
= AvmCore::getActiveCore();
539 DebugFrame
* frame
= core
->debugger()->frameAt(frameNumber
);
540 if (!frame
) return MathUtils::kNaN
; // should have tested for error earlier
541 return AvmCore::atomToDouble(autoAtomAt(frame
, index
, kind
));
544 bool DebugCLI::locals(int frameNumber
)
548 SourceInfo
* src
= NULL
;
549 AvmCore
* core
= AvmCore::getActiveCore();
550 DebugFrame
* frame
= core
->debugger()->frameAt(frameNumber
);
551 if (frame
== NULL
) return false;
553 // source information
554 frame
->sourceLocation(src
, line
);
557 MethodInfo
* info
= functionFor(src
, line
);
559 frame
->locals(ptr
, count
);
560 for(int i
=0; i
<count
; i
++) {
561 // write out the name
562 Stringp nm
= info
->getLocalName(i
);
563 if (nm
!= core
->kundefined
)
564 core
->console
<< i
<< ": " << nm
;
566 core
->console
<< "<local_" << i
<< ">";
567 core
->console
<< " = " << core
->format(*ptr
++) << "\n";
574 bool DebugCLI::arguments(int frameNumber
)
578 AvmCore
* core
= AvmCore::getActiveCore();
579 DebugFrame
* frame
= core
->debugger()->frameAt(frameNumber
);
580 if (frame
&& frame
->arguments(arr
, count
)) {
581 for (int i
= 0; i
< count
; i
++) {
583 frame
->argumentName(i
, nm
);
584 core
->console
<< i
<< ": " << nm
<< " = " << core
->format(*arr
++) << "\n";
591 Atom
DebugCLI::ease2Atom(const char* to
, Atom baseline
)
593 // first make a string out of the value
594 Atom a
= core
->newStringLatin1(to
)->atom();
596 // using the type of baseline try to convert to into an appropriate Atom
597 if (core
->isNumber(baseline
))
598 return core
->numberAtom(a
);
599 else if (core
->isBoolean(baseline
))
600 return AvmCore::booleanAtom(a
);
602 return nullStringAtom
;
607 const char* what
= nextToken();
608 const char* equ
= nextToken();
609 const char* to
= nextToken();
610 if (!to
|| !equ
|| !what
|| *equ
!= '=')
612 core
->console
<< " Bad format, should be: 'set {variable} = {value}' \n";
616 // look for the varable in our locals or args.
620 DebugFrame
* frame
= core
->debugger()->frameAt(0);
622 // source information
623 frame
->sourceLocation(src
, line
);
626 core
->console
<< "Unable to locate debug information for current source file, so no local or argument names known\n";
631 MethodInfo
* info
= functionFor(src
, line
);
634 core
->console
<< "Unable to find method debug information, so no local or argument names known\n";
638 frame
->arguments(ptr
, count
);
639 for(int i
=0; i
<count
; i
++)
641 Stringp arg
= info
->getArgName(i
);
642 if (arg
->equalsLatin1(what
))
645 Atom a
= ease2Atom(to
, ptr
[i
]);
646 if (a
== undefinedAtom
)
647 core
->console
<< " Type mismatch : current value is " << core
->format(ptr
[i
]);
649 frame
->setArgument(i
, a
);
654 frame
->locals(ptr
, count
);
655 for(int i
=0; i
<count
; i
++)
657 Stringp local
= info
->getLocalName(i
);
658 if ( local
->equalsLatin1(what
))
661 Atom a
= ease2Atom(to
, ptr
[i
]);
662 if (a
== undefinedAtom
)
663 core
->console
<< " Type mismatch : current value is " << core
->format(ptr
[i
]);
665 frame
->setLocal(i
, a
);
672 void DebugCLI::print(const char *name
)
675 core
->console
<< "Must specify a name.\n";
679 // todo deal with exceptions
681 core
->getAnyPublicNamespace(),
682 core
->internStringLatin1(name
)
687 Atom objAtom
= env
->findproperty(outerScope
, scopes
, extraScopes
, &mname
, false);
688 Atom valueAtom
= env
->getproperty(objAtom
, &mname
);
689 core
->console
<< core
->string(valueAtom
) << '\n';
693 bool DebugCLI::filterException(Exception
*exception
, bool /*willBeCaught*/)
695 // Filter exceptions when -d switch specified
697 core
->console
<< "Exception has been thrown:\n"
698 << core
->string(exception
->atom
)
706 void DebugCLI::info()
708 char *command
= nextToken();
709 int cmd
= infoCommandFor(command
);
713 // ambiguous, we already printed error message
715 case INFO_LOCALS_CMD
:
721 case INFO_UNKNOWN_CMD
:
722 core
->console
<< "Unknown command.\n";
725 core
->console
<< "Command not implemented.\n";
730 void DebugCLI::enterDebugger()
732 setCurrentSource( (core
->callStack
) ? (core
->callStack
->filename()) : 0 );
737 core
->console
<< "(asdb) ";
738 Platform::GetInstance()->getUserInput(commandLine
, kMaxCommandLine
);
740 commandLine
[VMPI_strlen(commandLine
)-1] = 0;
742 if (!commandLine
[0]) {
743 VMPI_strcpy(commandLine
, lastCommand
);
745 VMPI_strcpy(lastCommand
, commandLine
);
748 currentToken
= commandLine
;
750 char *command
= nextToken();
751 int cmd
= commandFor(command
);
755 // ambiguous, we already printed error message
761 breakpoint(nextToken());
764 deleteBreakpoint(nextToken());
770 core
->console
<< "Unknown command.\n";
773 Platform::GetInstance()->exit(0);
796 core
->console
<< "Command not implemented.\n";
802 void DebugCLI::setCurrentSource(Stringp file
)
810 delete [] currentSource
;
811 currentSource
= NULL
;
812 currentSourceLen
= 0;
815 // Open this file and suck it into memory
816 StUTF8String
currentFileUTF8(currentFile
);
817 FileInputStream
f(currentFileUTF8
.c_str());
818 if (f
.valid() && ((uint64_t)file
->length() < UINT32_T_MAX
)) { //cannot handle files > 4GB
819 currentSourceLen
= (uint32_t) f
.available();
820 currentSource
= new char[currentSourceLen
+1];
821 f
.read(currentSource
, currentSourceLen
);
822 currentSource
[currentSourceLen
] = 0;
824 // whip through converting \r\n to space \n
825 for(int64_t i
=0; i
<currentSourceLen
-1;i
++) {
826 if (currentSource
[i
] == '\r' && currentSource
[i
+1] == '\n')
827 currentSource
[i
] = ' ';
829 } else if (warnMissingSource
) {
830 core
->console
<< "Could not find '" << currentFile
<< "'. Try running in the same directory as the .as file.\n";
831 warnMissingSource
= false;
839 void BreakAction::print(PrintWriter
& out
)
843 << ":" << (linenum
) << '\n';