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) 1993-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 ***** */
49 Debugger::Debugger(AvmCore
*core
, TraceLevel tracelevel
)
50 : astrace_console(tracelevel
)
51 , astrace_callback(TRACE_OFF
)
53 , astraceStartTime(VMPI_getTime())
55 , abcList(core
->GetGC(), kListInitialCapacity
)
65 void Debugger::stepInto()
67 stepState
.flag
= true;
70 // Check that core and core->callStack are non-null before dereferencing
71 // them, so that it's possible to call stepInto() even before there is
72 // a stackframe (so execution will stop at the first executed line of code).
73 stepState
.startingDepth
= (core
&& core
->callStack
) ? core
->callStack
->depth() : 0;
76 void Debugger::stepOver()
78 stepState
.flag
= true;
79 stepState
.depth
= core
->callStack
->depth();
80 stepState
.startingDepth
= core
->callStack
->depth();
83 void Debugger::stepOut()
85 stepState
.flag
= true;
86 stepState
.depth
= core
->callStack
->depth() - 1;
87 stepState
.startingDepth
= core
->callStack
->depth();
90 void Debugger::stepContinue()
92 // Restore the previous stepping state
93 stepState
= oldStepState
;
97 * Set a breakpoint in a particular source flie
98 * NOTE: if the 'same' source file appears in
99 * multiple abc files, it is up to the caller
100 * to ensure that this call is performed for each
103 bool Debugger::breakpointSet(SourceInfo
* source
, int linenum
)
105 return source
->setBreakpoint(linenum
);
109 * Clear a breakpoint on a particular source file
111 bool Debugger::breakpointClear(SourceInfo
* source
, int linenum
)
113 return source
->clearBreakpoint(linenum
);
116 void Debugger::debugLine(int linenum
)
118 AvmAssert( core
->callStack
!=0 );
119 if (!core
->callStack
)
122 AvmAssert(linenum
> 0);
124 int prev
= core
->callStack
->linenum();
125 core
->callStack
->set_linenum(linenum
);
129 // line number has changed
130 bool changed
= (prev
== line
) ? false : true;
131 bool exited
= (prev
== -1) ? true : false; // are we being called as a result of function exit?
132 if (!changed
&& !exited
)
133 return; // still on the same line in the same function?
135 // tracing information
139 // check if we should stop due to breakpoint or step
143 if (stepState
.startingDepth
!= -1 && core
->callStack
->depth() < stepState
.startingDepth
)
145 // We stepped out of whatever function was executing when the
146 // stepInto/stepOver/stepOut command was executed. We may be
147 // in the middle of a line of code, but we still want to stop
148 // immediately. See bug 126633.
151 else if (!exited
&& (stepState
.depth
== -1 || core
->callStack
->depth() <= stepState
.depth
) )
153 // We reached the beginning of a new line of code.
158 // we didn't decide to stop due to a step, but check if we hit a breakpoint
159 if (!stop
&& !exited
)
161 MethodInfo
* f
= core
->callStack
->info();
163 if (f
&& (f
->hasMethodBody() || f
->isAotCompiled()))
165 if (f
&& f
->hasMethodBody())
168 AbcFile
* abc
= f
->file();
171 SourceFile
* source
= abc
->sourceNamed( core
->callStack
->filename() );
172 if (source
&& source
->hasBreakpoint(line
))
180 // we still haven't decided to stop; check our watchpoints
181 if (!stop
&& !exited
)
189 // Terminate whatever step operation may have been happening. But first,
190 // save the state of the step, so that if someone calls stepContinue(),
191 // then we can restore it.
192 StepState oldOldStepState
= oldStepState
; // save oldStepState in case of reentrancy
193 oldStepState
= stepState
; // save stepState so that stepContinue() can find it
194 stepState
.clear(); // turn off stepping
198 oldStepState
= oldOldStepState
; // restore oldStepState
202 void Debugger::debugFile(Stringp filename
)
204 AvmAssert( core
->callStack
!= 0 );
205 if (!core
->callStack
)
208 AvmAssert(filename
!= 0);
210 core
->callStack
->set_filename(filename
);
213 void Debugger::debugMethod(MethodEnv
* /*env*/)
218 void Debugger::_debugMethod(MethodEnv
* env
)
220 traceMethod(env
->method
);
222 // can't debug native methods
223 if (!env
->method
->isNative()
225 || env
->method
->isAotCompiled()
231 void Debugger::disableAllTracing()
234 astrace_callback
= TRACE_OFF
;
235 astrace_console
= TRACE_OFF
;
240 void Debugger::traceMethod(MethodInfo
* fnc
, bool ignoreArgs
)
242 if (in_trace
) return;
246 if (trace_callback
&& fnc
&& astrace_callback
> TRACE_OFF
)
249 if (astrace_console
> TRACE_OFF
)
253 // WARNING: don't change the format of output since outside utils depend on it
254 uint64_t delta
= VMPI_getTime() - astraceStartTime
;
255 core
->console
<< (uint32_t)(delta
) << " AVMINF: MTHD ";
256 Stringp fname
= fnc
->getMethodName();
257 if (fname
&& (fname
->length() > 0) )
258 core
->console
<< fname
;
260 core
->console
<< "<unknown>";
262 core
->console
<< " (";
264 if (!ignoreArgs
&& core
->callStack
&& (astrace_console
== TRACE_METHODS_WITH_ARGS
|| astrace_console
== TRACE_METHODS_AND_LINES_WITH_ARGS
))
265 traceArgumentsString(core
->console
);
267 core
->console
<< ")\n";
273 void Debugger::traceLine(int line
)
275 if (in_trace
) return;
279 AvmAssert(line
!= 0);
280 if (trace_callback
&& astrace_callback
>= TRACE_METHODS_AND_LINES
)
283 // console level trace
284 if (astrace_console
>= TRACE_METHODS_AND_LINES
)
286 Stringp file
= core
->callStack
->filename();
288 // WARNING: don't change the format of output since outside utils depend on it
289 uint64_t delta
= VMPI_getTime() - astraceStartTime
;
290 core
->console
<< (uint32_t)(delta
) << " AVMINF: LINE ";
292 core
->console
<< " " << line
<< "\t\t " << file
<< "\n";
294 core
->console
<< " " << line
<< "\t\t ??? \n";
299 void Debugger::traceCallback(int line
)
301 if (!(core
->callStack
&& core
->callStack
->env()))
304 Stringp file
= ( core
->callStack
->filename() ) ? Stringp(core
->callStack
->filename()) : Stringp(core
->kEmptyString
);
305 Stringp name
= core
->kEmptyString
;
306 Stringp args
= core
->kEmptyString
;
308 MethodEnv
* env
= core
->callStack
->env();
311 // normally, getMethodName omits nonpublic namespaces, but for this purpose,
312 // we want to include all namespaces.
313 const bool includeAllNamespaces
= true;
314 name
= env
->method
->getMethodName(includeAllNamespaces
);
316 name
= core
->kEmptyString
;
317 if ((line
== 0) && (astrace_callback
== TRACE_METHODS_WITH_ARGS
|| astrace_callback
== TRACE_METHODS_AND_LINES_WITH_ARGS
)) {
318 StringBuffer
sb(core
);
319 traceArgumentsString(sb
);
320 args
= sb
.toString();
324 Atom argv
[5] = { trace_callback
->atom(), file
->atom(), core
->intToAtom(line
), name
->atom(), args
->atom() };
327 TRY(core
, kCatchAction_ReportAsError
)
329 trace_callback
->call(argc
, argv
);
331 CATCH(Exception
*exception
)
334 //core->uncaughtException(exception);
340 void Debugger::traceArgumentsString(PrintWriter
& prw
)
342 DebugStackFrame
* frame
= (DebugStackFrame
*)frameAt(0);
345 if (frame
&& frame
->arguments(arr
, count
))
347 for(int i
=0; i
<count
; i
++)
349 prw
<< asAtom(arr
[i
]);
357 * Called when an abc file is first decoded.
358 * This method builds a list of source files
359 * and methods, etc from the given abc file.
361 void Debugger::processAbc(PoolObject
* pool
, ScriptBuffer code
)
364 if(pool2abcIndex
.get(pool
) != NULL
)
368 // first off we build an AbcInfo object
369 AbcFile
* abc
= AbcFile::create(core
->GetGC(), core
, (int)code
.getSize());
371 // now let's scan the abc resources pulling out what we need
372 scanResources(abc
, pool
);
374 // build a bridging table from pools to abcs
375 uintptr_t index
= abcList
.length();
376 pool2abcIndex
.add(pool
, (const void*)index
);
378 // at this point our abc object has been populated with
379 // source file objects and should ready to go.
380 // so we add it to the list and we are done
385 * Scans the pool object and pulls out information about the abc file
386 * placing it in the AbcFile
388 void Debugger::scanResources(AbcFile
* file
, PoolObject
* pool
)
391 for(uint32_t i
=0, n
= pool
->methodCount(); i
<n
; i
++)
393 MethodInfo
* f
= pool
->getMethodInfo(i
);
394 if (f
->hasMethodBody())
396 // yes there is code for this method
397 if (f
->abc_body_pos())
399 // if body_pos is null we havent got the body yet or
400 // this is an interface method
401 scanCode(file
, pool
, f
);
408 * Scans the bytecode and adds source level information to
409 * the abc info object
411 bool Debugger::scanCode(AbcFile
* file
, PoolObject
* pool
, MethodInfo
* m
)
413 const uint8_t *abc_start
= &m
->pool()->code()[0];
415 const uint8_t *pos
= m
->abc_body_pos();
419 AvmCore::skipU32(pos
, 4); // max_stack; local_count; init_stack_depth; max_stack_depth;
420 uint32_t code_len
= AvmCore::readU32(pos
); // checked earlier by AbcParser::parseMethodInfos()
422 const uint8_t *start
= pos
;
423 const uint8_t *end
= pos
+ code_len
;
427 SourceFile
* active
= NULL
; // current source file
428 for (const uint8_t* pc
=start
; pc
< end
; pc
+= size
)
430 op_count
= opcodeInfo
[*pc
].operandCount
;
431 if (op_count
== -1 && *pc
!= OP_lookupswitch
)
432 return false; // ohh very bad, verifier will catch this
434 size
= AvmCore::calculateInstructionWidth(pc
);
436 // if pc+size overflows, we're definitely hosed
437 if (uintptr_t(pc
) > uintptr_t(-1) - size
)
441 return false; // also bad, let the verifier will handle it
445 case OP_lookupswitch
:
447 // variable length instruction
448 const uint8_t *pc2
= pc
+4;
449 // case_count is a U30, so if the high bits are set,
450 // bail and let Verifier throw the error
451 uint32_t const case_count
= readU32(pc2
);
452 if (case_count
& 0xc0000000)
454 // case_count*3 can't overflow a U32...
455 uint32_t const case_skip
= (case_count
+ 1) * 3;
456 // ...but size could, so check to see if it will.
457 if (size
> uintptr_t(-1) - case_skip
)
465 // form is 8bit type followed by pool entry
466 // then 4Byte extra info
467 int type
= (uint8_t)*(pc
+1);
473 // in this case last word contains
474 // register and line number
475 const uint8_t* pc2
= pc
+2;
476 uint32_t index
= AvmCore::readU32(pc2
);
477 int slot
= (uint8_t)*(pc2
);
478 //int line = readS24(pc+5);
479 if (index
>= pool
->constantStringCount
)
480 break; // let the verifier handle this
483 //Atom str = pool->cpool[index];
484 Stringp s
= pool
->getString(index
);
485 m
->setRegName(slot
, s
);
494 // this means that we have a new source line for the given offset
495 const uint8_t* pc2
= pc
+1;
496 int line
= readU32(pc2
);
498 active
->addLine(line
, m
, (int)(pc
- abc_start
));
499 // else, OP_line occurred before OP_file: fix the compiler!
505 // new or existing source file
506 const uint8_t* pc2
= pc
+1;
507 uint32_t index
= AvmCore::readU32(pc2
);
508 if (index
>= pool
->constantStringCount
)
509 break; // let the verifier handle this
510 Stringp name
= pool
->getString(index
);
511 active
= file
->sourceNamed(name
);
514 active
= SourceFile::create(core
->GetGC(), name
);
515 file
->sourceAdd(active
);
525 * Returns the call stack depth (i.e. number of frames).
527 int Debugger::frameCount()
529 const int MAX_FRAMES
= 500; // we need a max, for perf. reasons (bug 175526)
530 CallStackNode
* trace
= core
->callStack
;
534 while( (trace
= trace
->next()) != 0 && count
< MAX_FRAMES
)
540 * Set frame to point to the specified frame number
541 * or null if frame number does not exist.
543 DebugFrame
* Debugger::frameAt(int frameNbr
)
545 DebugFrame
* frame
= NULL
;
549 CallStackNode
* trace
= locateTrace(frameNbr
);
551 frame
= new (core
->GetGC()) DebugStackFrame(frameNbr
, trace
, this);
557 * Stack frames are labelled 0 to stackTrace->depth
558 * With zero being the topmost or most recent frame.
560 CallStackNode
* Debugger::locateTrace(int frameNbr
)
563 CallStackNode
* trace
= core
->callStack
;
564 while(count
++ < frameNbr
&& trace
!= NULL
)
565 trace
= trace
->next();
571 * # of abc files available
573 int Debugger::abcCount() const
575 return abcList
.length();
579 Atom
Debugger::autoAtomAt(DebugFrame
* frame
, int index
, AutoVarKind kind
) {
586 success
= frame
->locals(arr
, count
);
590 success
= frame
->arguments(arr
, count
);
593 AvmAssert(index
== 0);
594 success
= frame
->dhis(dhis
);
601 return unreachableAtom
;
604 if (index
>= 0 && index
< count
) {
608 return unreachableAtom
;
612 Atom
Debugger::autoAtomKindAt(DebugFrame
* frame
, int autoIndex
, AutoVarKind kind
) {
613 if (!frame
) return unreachableAtom
;
614 else return atomKind(autoAtomAt(frame
, autoIndex
, kind
));
617 ScriptObject
* Debugger::autoVarAsObject(DebugFrame
* frame
, int index
, AutoVarKind kind
)
619 if (!frame
) return NULL
; // should have tested for error earlier
620 return AvmCore::atomToScriptObject(autoAtomAt(frame
, index
, kind
));
623 Stringp
Debugger::autoVarAsString(DebugFrame
* frame
, int index
, AutoVarKind kind
)
625 if (!frame
) return NULL
; // should have tested for error earlier
626 return AvmCore::atomToString(autoAtomAt(frame
, index
, kind
));
629 bool Debugger::autoVarAsBoolean(DebugFrame
* frame
, int index
, AutoVarKind kind
)
631 if (!frame
) return false; // should have tested for error earlier
632 Atom value
= autoAtomAt(frame
, index
, kind
);
633 return value
== trueAtom
? true : false;
636 double Debugger::autoVarAsInteger(DebugFrame
* frame
, int index
, AutoVarKind kind
) {
637 if (!frame
) return MathUtils::kNaN
; // should have tested for error earlier
638 return AvmCore::number_d(autoAtomAt(frame
, index
, kind
));
641 double Debugger::autoVarAsDouble(DebugFrame
* frame
, int index
, AutoVarKind kind
) {
642 if (!frame
) return MathUtils::kNaN
; // should have tested for error earlier
643 return AvmCore::atomToDouble(autoAtomAt(frame
, index
, kind
));
646 Stringp
Debugger::autoVarName(DebugStackFrame
* frame
, int index
, AutoVarKind kind
) {
647 if (frame
== NULL
) return NULL
;
649 SourceInfo
* src
= NULL
;
650 // source information
651 frame
->sourceLocation(src
, line
);
652 MethodInfo
* info
= functionFor(src
, line
, frame
);
653 if (!info
) return NULL
;
656 return info
->getLocalName(index
);
658 return info
->getArgName(index
);
667 int Debugger::autoVarCount(DebugStackFrame
* frame
, AutoVarKind kind
) {
668 if (frame
== NULL
) return 0;
671 SourceInfo
* src
= NULL
;
672 // source information
673 frame
->sourceLocation(src
, line
);
674 MethodInfo
* info
= functionFor(src
, line
, frame
);
678 return frame
->locals(ptr
, count
) ? count
: -1;
680 return frame
->arguments(ptr
, count
) ? count
: -1;
689 Stringp
Debugger::methodNameAt(DebugStackFrame
* frame
) {
690 if (frame
== NULL
) return NULL
;
692 SourceInfo
* src
= NULL
;
693 // source information
694 frame
->sourceLocation(src
, line
);
695 MethodInfo
* info
= functionFor(src
, line
, frame
);
696 return info
? info
->getMethodName() : NULL
;
699 void Debugger::printString(Stringp string
) {
700 StUTF8String
buf(string
);
701 fputs(buf
.c_str(), stdout
);
702 // flush in case debugger output is interleaved with process output
706 void Debugger::printAtom(Atom atom
) {
707 StringBuffer
buf(AvmCore::getActiveCore());
709 fputs(buf
.c_str(), stdout
);
710 // flush in case debugger output is interleaved with process output
714 void Debugger::printMethod(MethodInfo
* info
) {
715 String
* name
= info
->getMethodName();
717 StUTF8String
buf(info
->getMethodName());
718 fputs(buf
.c_str(), stdout
);
719 // flush in case debugger output is interleaved with process output
723 MethodInfo
* Debugger::functionFor(SourceInfo
* src
, int line
, DebugStackFrame
* frame
)
725 MethodInfo
* info
= NULL
;
728 // find the function at this location
729 int size
= src
->functionCount();
730 for(int i
=0; i
<size
; i
++)
732 MethodInfo
* m
= src
->functionAt(i
);
733 if (line
>= m
->firstSourceLine() && line
<= m
->lastSourceLine())
740 if (!info
&& frame
) {
741 // fallback on frame info
742 return frame
->trace
->info();
748 * Get information on each of the abc files that
751 AbcInfo
* Debugger::abcAt(int index
) const
753 return abcList
.get(index
);
757 * Contains all known debug information regarding a single
760 AbcFile::AbcFile(AvmCore
* core
, int size
)
762 source(core
->GetGC(), kListInitialCapacity
),
765 sourcemap
= HeapHashtable::create(core
->GetGC());
768 int AbcFile::sourceCount() const
770 return source
.length();
773 SourceInfo
* AbcFile::sourceAt(int index
) const
775 return source
.get(index
);
778 int AbcFile::size() const
784 * Find a source file with the given name
786 SourceFile
* AbcFile::sourceNamed(Stringp name
)
788 Atom atom
= sourcemap
->get(name
->atom());
789 if (AvmCore::isUndefined(atom
))
791 uint32_t index
= AvmCore::integer_u(atom
);
792 return source
.get(index
);
796 * Add source file to list; no check for uniqueness
799 void AbcFile::sourceAdd(SourceFile
* s
)
802 sourcemap
->add(s
->name()->atom(), core
->uintToAtom(source
.length()-1));
808 SourceFile::SourceFile(MMgc::GC
* gc
, Stringp name
)
810 , functions(gc
, kListInitialCapacity
)
814 Stringp
SourceFile::name() const
820 * A line - offset pair should be recorded
822 void SourceFile::addLine(int linenum
, MethodInfo
* func
, int offset
)
824 // Add the function to our list if it doesn't exist. Use lastIndexOf() instead of
825 // indexOf(), because this will be faster in the very common case where the function
826 // already exists at the end of the list.
827 int index
= functions
.lastIndexOf(func
);
831 index
= functions
.length() - 1;
834 // line numbers for a given function don't always come in sequential
835 // order -- for example, I've seen them come out of order if a function
836 // contains an inner anonymous function -- so, update every time we get called
837 func
->updateSourceLines(linenum
, offset
);
838 sourceLines
.set(linenum
);
841 int SourceFile::functionCount() const
843 return functions
.length();
846 MethodInfo
* SourceFile::functionAt(int index
) const
848 return functions
.get(index
);
851 bool SourceFile::setBreakpoint(int linenum
)
853 if (!sourceLines
.get(linenum
))
855 breakpoints
.set(linenum
);
859 bool SourceFile::clearBreakpoint(int linenum
)
861 if (!breakpoints
.get(linenum
))
863 breakpoints
.clear(linenum
);
867 bool SourceFile::hasBreakpoint(int linenum
)
869 return breakpoints
.get(linenum
);
872 bool DebugFrame::methodName(Stringp
&) {
873 return false; // base implementation
876 bool DebugFrame::argumentName(int, Stringp
&) {
877 return false; // base implementation
881 DebugStackFrame::DebugStackFrame(int nbr
, CallStackNode
* tr
, Debugger
* debug
)
886 AvmAssert(tr
!= NULL
);
887 AvmAssert(debug
!= NULL
);
891 * Identifies the source file and source line number
892 * corresponding to this frame
894 bool DebugStackFrame::sourceLocation(SourceInfo
*& source
, int& linenum
)
896 // use the method info to locate the abcfile / source
897 if (trace
->info() && trace
->filename() && debugger
)
899 uintptr_t index
= (uintptr_t)debugger
->pool2abcIndex
.get(Atom(trace
->info()->pool()));
901 AbcFile
* abc
= (AbcFile
*)debugger
->abcAt((int)index
);
902 source
= abc
->sourceNamed(trace
->filename());
904 linenum
= trace
->linenum();
907 return (source
!= NULL
&& linenum
> 0);
911 * This pointer for the frame
913 bool DebugStackFrame::dhis(Atom
& a
)
916 if (trace
->framep() && trace
->info())
918 trace
->info()->boxLocals(trace
->framep(), 0, trace
->types(), &a
, 0, 1); // pull framep[0] = [this]
928 bool DebugStackFrame::methodName(Stringp
& result
) {
930 //PoolObject* pool = trace->info()->pool();
931 //return trace->info()->pool()->getMethodInfoName(trace->info()->method_id());
932 result
= trace
->info()->getMethodName();
937 bool DebugStackFrame::argumentName(int which
, Stringp
& result
) {
939 result
= trace
->info()->getArgName(which
);
948 * @return a pointer to an object Atom whose members are
949 * the arguments passed into a function for this frame
951 bool DebugStackFrame::arguments(Atom
*& ar
, int& count
)
954 if (trace
->framep() && trace
->info())
956 int firstArgument
, pastLastArgument
;
957 argumentBounds(&firstArgument
, &pastLastArgument
);
958 count
= pastLastArgument
- firstArgument
;
959 if ((count
> 0) && debugger
)
961 // pull the args into an array -- skip [0] which is [this]
962 ar
= (Atom
*) debugger
->core
->GetGC()->Calloc(count
, sizeof(Atom
), GC::kContainsPointers
|GC::kZero
);
963 MethodInfo
* info
= trace
->info();
964 info
->boxLocals(trace
->framep(), firstArgument
, trace
->types(), ar
, 0, count
);
975 bool DebugStackFrame::setArgument(int which
, Atom
& val
)
978 if (trace
->framep() && trace
->info())
980 int firstArgument
, pastLastArgument
;
981 argumentBounds(&firstArgument
, &pastLastArgument
);
982 int count
= pastLastArgument
- firstArgument
;
983 if (count
> 0 && which
< count
)
985 // copy the single arg over
986 MethodInfo
* info
= trace
->info();
987 info
->unboxLocals(&val
, 0, trace
->types(), trace
->framep(), firstArgument
+which
, 1);
995 * @return a pointer to an object Atom whose members are
996 * the active locals for this frame.
998 bool DebugStackFrame::locals(Atom
*& ar
, int& count
)
1001 if (trace
->framep() && trace
->info())
1003 int firstLocal
, pastLastLocal
;
1004 localBounds(&firstLocal
, &pastLastLocal
);
1005 count
= pastLastLocal
- firstLocal
;
1006 AvmAssert(count
>= 0);
1007 if ((count
> 0) && debugger
)
1009 // frame looks like [this][param0...paramN][local0...localN]
1010 ar
= (Atom
*) debugger
->core
->GetGC()->Calloc(count
, sizeof(Atom
), GC::kContainsPointers
|GC::kZero
);
1011 MethodInfo
* info
= trace
->info();
1012 info
->boxLocals(trace
->framep(), firstLocal
, trace
->types(), ar
, 0, count
);
1014 // If abcMethod_NEED_REST or abcMethod_NEED_ARGUMENTS is set, and the jit is being used, then the first
1015 // local is actually not an atom at all -- it is an ArrayObject*. So, we need to
1016 // convert it to an atom. (If the interpreter is being used instead of the jit, then
1017 // it is stored as an atom.)
1018 if (info
->needRestOrArguments())
1020 int atomType
= atomKind(ar
[0]);
1021 if (atomType
== 0) // 0 is not a legal atom type, so ar[0] is not an atom
1023 ScriptObject
* obj
= (ScriptObject
*)ar
[0];
1024 ar
[0] = obj
->atom();
1037 bool DebugStackFrame::setLocal(int which
, Atom
& val
)
1039 bool worked
= false;
1040 if (trace
->framep() && trace
->info())
1042 int firstLocal
, pastLastLocal
;
1043 localBounds(&firstLocal
, &pastLastLocal
);
1044 int count
= pastLastLocal
- firstLocal
;
1045 if (count
> 0 && which
< count
)
1047 MethodInfo
* info
= trace
->info();
1048 if (which
== 0 && info
->needRestOrArguments())
1050 // They are trying to modify the first local, but that is actually the special
1051 // array for "...rest" or for "arguments". That is too complicated to allow
1052 // right now. We're just going to fail the request.
1056 // copy the single arg over
1057 info
->unboxLocals(&val
, 0, trace
->types(), trace
->framep(), firstLocal
+which
, 1);
1065 // Returns the indices of the arguments: [firstArgument, pastLastArgument)
1066 void DebugStackFrame::argumentBounds(int* firstArgument
, int* pastLastArgument
)
1068 *firstArgument
= 1; // because [0] is 'this'
1069 *pastLastArgument
= indexOfFirstLocal();
1072 // Returns the indices of the locals: [firstLocal, pastLastLocal)
1073 void DebugStackFrame::localBounds(int* firstLocal
, int* pastLastLocal
)
1075 *firstLocal
= indexOfFirstLocal();
1076 if (trace
->framep() && trace
->info())
1079 *pastLastLocal
= trace
->info()->local_count();
1080 if (*pastLastLocal
< *firstLocal
)
1081 *pastLastLocal
= *firstLocal
;
1083 const MethodSignature
* ms
= trace
->info()->getMethodSignature();
1084 *pastLastLocal
= ms
->local_count();
1089 *pastLastLocal
= *firstLocal
;
1093 int DebugStackFrame::indexOfFirstLocal()
1095 // 'trace->argc' is the number of arguments that were actually passed in
1096 // to this function, but that is not what we want -- we want
1097 // 'info->param_count', because that is the number of arguments we were
1098 // *expecting* to get. There are two reasons we want 'info->param_count':
1100 // (1) if the caller passed in too many args, we want to ignore the
1101 // trailing ones; and
1102 // (2) if the caller passed in too few args to a function that has some
1103 // default parameters, we want to display the args with their default
1107 MethodSignaturep ms
= trace
->info()->getMethodSignature();
1108 return 1 + ms
->param_count();
1113 #endif /* DEBUGGER */