Merge remote-tracking branch 'redux/master' into sh4-pool
[tamarin-stm.git] / core / avmplusDebugger.cpp
blobcecec83bcdf26cb762eee3649a4edc9258f9a5a1
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
14 * License.
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.
23 * Contributor(s):
24 * Adobe AS3 Team
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 ***** */
41 #include "avmplus.h"
43 #ifdef DEBUGGER
45 namespace avmplus
47 using namespace MMgc;
49 Debugger::Debugger(AvmCore *core, TraceLevel tracelevel)
50 : astrace_console(tracelevel)
51 , astrace_callback(TRACE_OFF)
52 , in_trace(false)
53 , astraceStartTime(VMPI_getTime())
54 , core(core)
55 , abcList(core->GetGC(), kListInitialCapacity)
56 , pool2abcIndex()
60 Debugger::~Debugger()
62 disableAllTracing();
65 void Debugger::stepInto()
67 stepState.flag = true;
68 stepState.depth = -1;
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;
96 /**
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
101 * SourceInfo object.
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)
120 return;
122 AvmAssert(linenum > 0);
124 int prev = core->callStack->linenum();
125 core->callStack->set_linenum(linenum);
127 int line = 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
136 if (!exited)
137 traceLine(line);
139 // check if we should stop due to breakpoint or step
140 bool stop = false;
141 if (stepState.flag)
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.
149 stop = true;
151 else if (!exited && (stepState.depth == -1 || core->callStack->depth() <= stepState.depth) )
153 // We reached the beginning of a new line of code.
154 stop = true;
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();
162 #ifdef VMCFG_AOT
163 if (f && (f->hasMethodBody() || f->isAotCompiled()))
164 #else
165 if (f && f->hasMethodBody())
166 #endif
168 AbcFile* abc = f->file();
169 if (abc)
171 SourceFile* source = abc->sourceNamed( core->callStack->filename() );
172 if (source && source->hasBreakpoint(line))
174 stop = true;
180 // we still haven't decided to stop; check our watchpoints
181 if (!stop && !exited)
183 if (hitWatchpoint())
184 stop = true;
187 if (stop)
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
196 enterDebugger();
198 oldStepState = oldOldStepState; // restore oldStepState
202 void Debugger::debugFile(Stringp filename)
204 AvmAssert( core->callStack != 0 );
205 if (!core->callStack)
206 return;
208 AvmAssert(filename != 0);
210 core->callStack->set_filename(filename);
213 void Debugger::debugMethod(MethodEnv* /*env*/)
215 // nop
218 void Debugger::_debugMethod(MethodEnv* env)
220 traceMethod(env->method);
222 // can't debug native methods
223 if (!env->method->isNative()
224 #ifdef VMCFG_AOT
225 || env->method->isAotCompiled()
226 #endif
228 debugMethod(env);
231 void Debugger::disableAllTracing()
233 in_trace = true;
234 astrace_callback = TRACE_OFF;
235 astrace_console = TRACE_OFF;
236 trace_callback = 0;
237 in_trace = false;
240 void Debugger::traceMethod(MethodInfo* fnc, bool ignoreArgs)
242 if (in_trace) return;
243 in_trace = true;
245 // callback trace
246 if (trace_callback && fnc && astrace_callback > TRACE_OFF)
247 traceCallback(0);
249 if (astrace_console > TRACE_OFF)
251 if (fnc)
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;
259 else
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";
270 in_trace = false;
273 void Debugger::traceLine(int line)
275 if (in_trace) return;
276 in_trace = true;
278 // callback trace
279 AvmAssert(line != 0);
280 if (trace_callback && astrace_callback >= TRACE_METHODS_AND_LINES)
281 traceCallback(line);
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 ";
291 if (file)
292 core->console << " " << line << "\t\t " << file << "\n";
293 else
294 core->console << " " << line << "\t\t ??? \n";
296 in_trace = false;
299 void Debugger::traceCallback(int line)
301 if (!(core->callStack && core->callStack->env()))
302 return;
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();
309 if (env->method)
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);
315 if (!name)
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() };
325 int argc = 4;
327 TRY(core, kCatchAction_ReportAsError)
329 trace_callback->call(argc, argv);
331 CATCH(Exception *exception)
333 (void) exception;
334 //core->uncaughtException(exception);
336 END_CATCH
337 END_TRY
340 void Debugger::traceArgumentsString(PrintWriter& prw)
342 DebugStackFrame* frame = (DebugStackFrame*)frameAt(0);
343 int count;
344 Atom* arr;
345 if (frame && frame->arguments(arr, count))
347 for(int i=0; i<count; i++)
349 prw << asAtom(arr[i]);
350 if (i+1 < count)
351 prw << ",";
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)
363 #ifdef VMCFG_AOT
364 if(pool2abcIndex.get(pool) != NULL)
365 return;
366 #endif
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
381 abcList.add(abc);
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)
390 // walk all methods
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();
417 m->setFile(file);
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;
425 uintptr_t size = 0;
426 int op_count;
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)
438 return false;
440 if (pc+size > end)
441 return false; // also bad, let the verifier will handle it
443 switch (*pc)
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)
453 return false;
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)
458 return false;
459 size += case_skip;
460 break;
463 case OP_debug:
465 // form is 8bit type followed by pool entry
466 // then 4Byte extra info
467 int type = (uint8_t)*(pc+1);
469 switch(type)
471 case DI_LOCAL:
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);
489 break;
492 case OP_debugline:
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);
497 if (active != NULL)
498 active->addLine(line, m, (int)(pc - abc_start));
499 // else, OP_line occurred before OP_file: fix the compiler!
500 break;
503 case OP_debugfile:
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);
512 if (active == NULL)
514 active = SourceFile::create(core->GetGC(), name);
515 file->sourceAdd(active);
517 break;
521 return true;
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;
531 if (trace == NULL)
532 return 0;
533 int count = 1;
534 while( (trace = trace->next()) != 0 && count < MAX_FRAMES )
535 count++;
536 return count;
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;
547 if (frameNbr >= 0)
549 CallStackNode* trace = locateTrace(frameNbr);
550 if (trace)
551 frame = new (core->GetGC()) DebugStackFrame(frameNbr, trace, this);
553 return frame;
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)
562 int count = 0;
563 CallStackNode* trace = core->callStack;
564 while(count++ < frameNbr && trace != NULL)
565 trace = trace->next();
567 return trace;
571 * # of abc files available
573 int Debugger::abcCount() const
575 return abcList.length();
579 Atom Debugger::autoAtomAt(DebugFrame* frame, int index, AutoVarKind kind) {
580 Atom* arr = NULL;
581 Atom dhis;
582 int count = 0;
583 bool success;
584 switch (kind) {
585 case AUTO_LOCAL:
586 success = frame->locals(arr, count);
587 break;
589 case AUTO_ARGUMENT:
590 success = frame->arguments(arr, count);
591 break;
592 case AUTO_THIS:
593 AvmAssert(index == 0);
594 success = frame->dhis(dhis);
595 if (success) {
596 arr = &dhis;
598 break;
599 default:
600 AvmAssert(false);
601 return unreachableAtom;
603 if (success) {
604 if (index >= 0 && index < count) {
605 return arr[index];
608 return unreachableAtom;
611 // interactive
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;
648 int line;
649 SourceInfo* src = NULL;
650 // source information
651 frame->sourceLocation(src, line);
652 MethodInfo* info = functionFor(src, line, frame);
653 if (!info) return NULL;
654 switch (kind) {
655 case AUTO_LOCAL:
656 return info->getLocalName(index);
657 case AUTO_ARGUMENT:
658 return info->getArgName(index);
659 case AUTO_THIS:
660 return NULL;
661 default:
662 AvmAssert(false);
664 return NULL;
667 int Debugger::autoVarCount(DebugStackFrame* frame, AutoVarKind kind) {
668 if (frame == NULL) return 0;
669 int line, count;
670 Atom* ptr;
671 SourceInfo* src = NULL;
672 // source information
673 frame->sourceLocation(src, line);
674 MethodInfo* info = functionFor(src, line, frame);
675 if (!info) return 0;
676 switch (kind) {
677 case AUTO_LOCAL:
678 return frame->locals(ptr, count) ? count : -1;
679 case AUTO_ARGUMENT:
680 return frame->arguments(ptr, count) ? count : -1;
681 case AUTO_THIS:
682 return 1;
683 default:
684 AvmAssert(false);
686 return -1;
689 Stringp Debugger::methodNameAt(DebugStackFrame* frame) {
690 if (frame == NULL) return NULL;
691 int line;
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
703 fflush(stdout);
706 void Debugger::printAtom(Atom atom) {
707 StringBuffer buf(AvmCore::getActiveCore());
708 buf.writeAtom(atom);
709 fputs(buf.c_str(), stdout);
710 // flush in case debugger output is interleaved with process output
711 fflush(stdout);
714 void Debugger::printMethod(MethodInfo* info) {
715 String* name = info->getMethodName();
716 if (!name) return;
717 StUTF8String buf(info->getMethodName());
718 fputs(buf.c_str(), stdout);
719 // flush in case debugger output is interleaved with process output
720 fflush(stdout);
723 MethodInfo* Debugger::functionFor(SourceInfo* src, int line, DebugStackFrame* frame)
725 MethodInfo* info = NULL;
726 if (src)
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())
735 info = m;
736 break;
740 if (!info && frame) {
741 // fallback on frame info
742 return frame->trace->info();
744 return info;
748 * Get information on each of the abc files that
749 * have been loaded.
751 AbcInfo* Debugger::abcAt(int index) const
753 return abcList.get(index);
757 * Contains all known debug information regarding a single
758 * abc/swf file
760 AbcFile::AbcFile(AvmCore* core, int size)
761 : core(core),
762 source(core->GetGC(), kListInitialCapacity),
763 byteCount(size)
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
780 return byteCount;
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))
790 return NULL;
791 uint32_t index = AvmCore::integer_u(atom);
792 return source.get(index);
796 * Add source file to list; no check for uniqueness
797 * of name
799 void AbcFile::sourceAdd(SourceFile* s)
801 source.add(s);
802 sourcemap->add(s->name()->atom(), core->uintToAtom(source.length()-1));
806 * new source info
808 SourceFile::SourceFile(MMgc::GC* gc, Stringp name)
809 : named(name)
810 , functions(gc, kListInitialCapacity)
814 Stringp SourceFile::name() const
816 return named;
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);
828 if (index < 0)
830 functions.add(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))
854 return false;
855 breakpoints.set(linenum);
856 return true;
859 bool SourceFile::clearBreakpoint(int linenum)
861 if (!breakpoints.get(linenum))
862 return false;
863 breakpoints.clear(linenum);
864 return true;
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)
882 : trace(tr)
883 , debugger(debug)
884 , frameNbr(nbr)
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();
906 // valid info?
907 return (source != NULL && linenum > 0);
911 * This pointer for the frame
913 bool DebugStackFrame::dhis(Atom& a)
915 bool worked = false;
916 if (trace->framep() && trace->info())
918 trace->info()->boxLocals(trace->framep(), 0, trace->types(), &a, 0, 1); // pull framep[0] = [this]
919 worked = true;
921 else
923 a = undefinedAtom;
925 return worked;
928 bool DebugStackFrame::methodName(Stringp& result) {
929 if (trace->info()) {
930 //PoolObject* pool = trace->info()->pool();
931 //return trace->info()->pool()->getMethodInfoName(trace->info()->method_id());
932 result = trace->info()->getMethodName();
933 return true;
934 } else return false;
937 bool DebugStackFrame::argumentName(int which, Stringp& result) {
938 if (trace->info()) {
939 result = trace->info()->getArgName(which);
940 return true;
941 } else return false;
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)
953 bool worked = true;
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);
967 else
969 worked = false;
970 count = 0;
972 return worked;
975 bool DebugStackFrame::setArgument(int which, Atom& val)
977 bool worked = false;
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);
988 worked = true;
991 return worked;
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)
1000 bool worked = true;
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();
1029 else
1031 worked = false;
1032 count = 0;
1034 return worked;
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.
1054 else
1056 // copy the single arg over
1057 info->unboxLocals(&val, 0, trace->types(), trace->framep(), firstLocal+which, 1);
1058 worked = true;
1062 return worked;
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())
1078 #ifdef VMCFG_AOT
1079 *pastLastLocal = trace->info()->local_count();
1080 if (*pastLastLocal < *firstLocal)
1081 *pastLastLocal = *firstLocal;
1082 #else
1083 const MethodSignature* ms = trace->info()->getMethodSignature();
1084 *pastLastLocal = ms->local_count();
1085 #endif
1087 else
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
1104 // values.
1105 if (!trace->info())
1106 return 0;
1107 MethodSignaturep ms = trace->info()->getMethodSignature();
1108 return 1 + ms->param_count();
1113 #endif /* DEBUGGER */