root/core/avmplusDebugger.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. pool2abcIndex
  2. stepInto
  3. stepOver
  4. stepOut
  5. stepContinue
  6. breakpointSet
  7. breakpointClear
  8. debugLine
  9. debugFile
  10. debugMethod
  11. _debugMethod
  12. disableAllTracing
  13. traceMethod
  14. traceLine
  15. traceCallback
  16. traceArgumentsString
  17. processAbc
  18. scanResources
  19. scanCode
  20. frameCount
  21. frameAt
  22. locateTrace
  23. abcCount
  24. abcAt
  25. byteCount
  26. sourceCount
  27. sourceAt
  28. size
  29. sourceNamed
  30. sourceAdd
  31. functions
  32. name
  33. addLine
  34. functionCount
  35. functionAt
  36. setBreakpoint
  37. clearBreakpoint
  38. hasBreakpoint
  39. frameNbr
  40. sourceLocation
  41. dhis
  42. arguments
  43. setArgument
  44. locals
  45. setLocal
  46. argumentBounds
  47. localBounds
  48. indexOfFirstLocal

/* ***** BEGIN LICENSE BLOCK *****
 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
 *
 * The contents of this file are subject to the Mozilla Public License Version
 * 1.1 (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.mozilla.org/MPL/
 *
 * Software distributed under the License is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * The Original Code is [Open Source Virtual Machine.].
 *
 * The Initial Developer of the Original Code is
 * Adobe System Incorporated.
 * Portions created by the Initial Developer are Copyright (C) 1993-2006
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *   Adobe AS3 Team
 *
 * Alternatively, the contents of this file may be used under the terms of
 * either the GNU General Public License Version 2 or later (the "GPL"), or
 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 * in which case the provisions of the GPL or the LGPL are applicable instead
 * of those above. If you wish to allow use of your version of this file only
 * under the terms of either the GPL or the LGPL, and not to allow others to
 * use your version of this file under the terms of the MPL, indicate your
 * decision by deleting the provisions above and replace them with the notice
 * and other provisions required by the GPL or the LGPL. If you do not delete
 * the provisions above, a recipient may use your version of this file under
 * the terms of any one of the MPL, the GPL or the LGPL.
 *
 * ***** END LICENSE BLOCK ***** */


#include "avmplus.h"

#ifdef DEBUGGER

namespace avmplus
{
        using namespace MMgc;

        Debugger::Debugger(AvmCore *core, TraceLevel tracelevel)
                : astrace_console(tracelevel)
                , astrace_callback(TRACE_OFF)
                , in_trace(false)
                , astraceStartTime(VMPI_getTime())
                , core(core)
                , abcList(core->GetGC())
                , pool2abcIndex()
        {
        }

        Debugger::~Debugger()
        {
                disableAllTracing();
        }
        
        void Debugger::stepInto()
        {
                stepState.flag = true;
                stepState.depth = -1;

                // Check that core and core->callStack are non-null before dereferencing
                // them, so that it's possible to call stepInto() even before there is
                // a stackframe (so execution will stop at the first executed line of code).
                stepState.startingDepth = (core && core->callStack) ? core->callStack->depth() : 0;
        }

        void Debugger::stepOver()
        {
                stepState.flag = true;
                stepState.depth = core->callStack->depth();
                stepState.startingDepth = core->callStack->depth();
        }

        void Debugger::stepOut()
        {
                stepState.flag = true;
                stepState.depth = core->callStack->depth() - 1;
                stepState.startingDepth = core->callStack->depth();
        }

        void Debugger::stepContinue()
        {
                // Restore the previous stepping state
                stepState = oldStepState;
        }

        /**
         * Set a breakpoint in a particular source flie
         * NOTE: if the 'same' source file appears in 
         * multiple abc files, it is up to the caller
         * to ensure that this call is performed for each 
         * SourceInfo object.
         */
        bool Debugger::breakpointSet(SourceInfo* source, int linenum)
        {
                return source->setBreakpoint(linenum);
        }

        /**
         * Clear a breakpoint on a particular source file
         */
        bool Debugger::breakpointClear(SourceInfo* source, int linenum)
        {
                return source->clearBreakpoint(linenum);
        }

        void Debugger::debugLine(int linenum)
        {
                AvmAssert( core->callStack !=0 );
                if (!core->callStack)
                        return;

                AvmAssert(linenum > 0);

                int prev = core->callStack->linenum();
                core->callStack->set_linenum(linenum);

                int line = linenum;

                // line number has changed
                bool changed = (prev == line) ? false : true;
                bool exited =  (prev == -1) ? true : false; // are we being called as a result of function exit?
                if (!changed && !exited) 
                        return;  // still on the same line in the same function?
                
                Profiler* profiler = core->profiler();
                Sampler* s = core->get_sampler();
                if (profiler && profiler->profilingDataWanted && profiler->profileSwitch && !(s && s->sampling()))
                {
                        profiler->sendLineTimestamp(line);
                }

                // tracing information
                if (!exited)
                        traceLine(line);

                // check if we should stop due to breakpoint or step
                bool stop = false;
                if (stepState.flag)
                {
                        if (stepState.startingDepth != -1 && core->callStack->depth() < stepState.startingDepth)
                        {
                                // We stepped out of whatever function was executing when the
                                // stepInto/stepOver/stepOut command was executed.  We may be
                                // in the middle of a line of code, but we still want to stop
                                // immediately.  See bug 126633.
                                stop = true;
                        }
                        else if (!exited && (stepState.depth == -1 || core->callStack->depth() <= stepState.depth) ) 
                        {
                                // We reached the beginning of a new line of code.
                                stop = true;
                        }
                }

                // we didn't decide to stop due to a step, but check if we hit a breakpoint
                if (!stop && !exited)
                {
                        MethodInfo* f = core->callStack->info();
                        if (f && f->hasMethodBody()) 
                        {
                                AbcFile* abc = f->file();
                                if (abc)
                                {
                                        SourceFile* source = abc->sourceNamed( core->callStack->filename() );
                                        if (source && source->hasBreakpoint(line))
                                        {
                                                stop = true;
                                        }
                                }
                        }
                }

                // we still haven't decided to stop; check our watchpoints
                if (!stop && !exited)
                {
                        if (hitWatchpoint())
                                stop = true;
                }

                if (stop)
                {
                        // Terminate whatever step operation may have been happening.  But first,
                        // save the state of the step, so that if someone calls stepContinue(),
                        // then we can restore it.
                        StepState oldOldStepState = oldStepState; // save oldStepState in case of reentrancy
                        oldStepState = stepState; // save stepState so that stepContinue() can find it
                        stepState.clear(); // turn off stepping

                        enterDebugger();

                        oldStepState = oldOldStepState; // restore oldStepState
                }
        }

        void Debugger::debugFile(Stringp filename)
        {
                AvmAssert( core->callStack != 0 );
                if (!core->callStack)
                        return;

                AvmAssert(filename != 0);

                Stringp prev = core->callStack->filename();
                core->callStack->set_filename(filename);

                // filename changed
                if (prev != filename) 
                {
                        Profiler* profiler = core->profiler();
                        Sampler* s = core->get_sampler();
                        if (profiler && profiler->profilingDataWanted && !(s && s->sampling()))
                        {
                                profiler->sendDebugFileURL(filename);
                        }
                }
        }

        void Debugger::debugMethod(MethodEnv* /*env*/)
        {
                // nop
        }

        void Debugger::_debugMethod(MethodEnv* env)
        {
                traceMethod(env->method);

                // can't debug native methods
                if (!env->method->isNative()
#ifdef VMCFG_AOT
                        || env->method->isCompiledMethod()
#endif
            )
                        debugMethod(env);
        }

        void Debugger::disableAllTracing()
        {
                in_trace = true;
                astrace_callback = TRACE_OFF;
                astrace_console = TRACE_OFF;
                trace_callback = 0;
                in_trace = false;
        }
        
        void Debugger::traceMethod(MethodInfo* fnc, bool ignoreArgs)
        {
                if (in_trace) return;
                in_trace = true;
                
                // callback trace
                if (trace_callback && fnc && astrace_callback > TRACE_OFF)
                        traceCallback(0);
                
                if (astrace_console > TRACE_OFF)
                {
                        if (fnc)
                        {
                                // WARNING: don't change the format of output since outside utils depend on it
                                uint64 delta = VMPI_getTime() - astraceStartTime;
                                core->console << (uint32)(delta) << " AVMINF: MTHD ";
                                Stringp fname = fnc->getMethodName();
                                if (fname && (fname->length() > 0) )
                                        core->console << fname;
                                else
                                        core->console << "<unknown>";
                                
                                core->console << " (";

                                if (!ignoreArgs && core->callStack && (astrace_console == TRACE_METHODS_WITH_ARGS || astrace_console == TRACE_METHODS_AND_LINES_WITH_ARGS))
                                        core->console << traceArgumentsString();

                                core->console << ")";
                                if (!fnc->suggestInterp())
                                {
                                        core->console << " @ 0x";                       
                                        core->console.writeHexAddr( (uintptr)fnc->implGPR());
                                }
                                core->console << "\n";          
                        }
                }               
                in_trace = false;
        }

        void Debugger::traceLine(int line)
        {
                if (in_trace) return;
                in_trace = true;
                
                // callback trace
                AvmAssert(line != 0);
                if (trace_callback && astrace_callback >= TRACE_METHODS_AND_LINES)
                        traceCallback(line);
                
                // console level trace
                if (astrace_console >= TRACE_METHODS_AND_LINES)
                {
                        Stringp file = core->callStack->filename();

                        // WARNING: don't change the format of output since outside utils depend on it
                        uint64 delta = VMPI_getTime() - astraceStartTime;
                        core->console << (uint32)(delta) << " AVMINF: LINE ";
                        if (file)
                                core->console << "   " << line << "\t\t " << file << "\n";
                        else
                                core->console << "   " << line << "\t\t ??? \n";                        
                }
                in_trace = false;
        }

        void Debugger::traceCallback(int line)
        {
                if (!core->callStack && core->callStack->env())
                        return;
                        
                Stringp file = ( core->callStack->filename() ) ? Stringp(core->callStack->filename()) : Stringp(core->kEmptyString);
                Stringp name = core->kEmptyString;
                Stringp args = core->kEmptyString;

                MethodEnv* env = core->callStack->env();
                if (env->method)
                {
                        // normally, getMethodName omits nonpublic namespaces, but for this purpose,
                        // we want to include all namespaces.
                        const bool includeAllNamespaces = true;
                        name = env->method->getMethodName(includeAllNamespaces);
                        if (!name)
                                name = core->kEmptyString;
                        if ((line == 0) && (astrace_callback == TRACE_METHODS_WITH_ARGS || astrace_callback == TRACE_METHODS_AND_LINES_WITH_ARGS))
                                args = traceArgumentsString();
                }
                
                Atom argv[5] = { trace_callback->atom(), file->atom(), core->intToAtom(line), name->atom(), args->atom() };
                int argc = 4;

                TRY(core, kCatchAction_ReportAsError)
                {
                        trace_callback->call(argc, argv);
                }
                CATCH(Exception *exception)
                {
                        (void) exception;
                        //core->uncaughtException(exception);
                }
                END_CATCH
                END_TRY         
        }

        Stringp Debugger::traceArgumentsString()
        {
                Stringp args = core->kEmptyString;
                DebugStackFrame* frame = (DebugStackFrame*)frameAt(0);
                int count;
                Atom* arr;
                if (frame && frame->arguments(arr, count))
                {
                        Stringp comma = core->newConstantStringLatin1(",");
                        for(int i=0; i<count; i++)
                        {
                                args = core->concatStrings(args, core->format(arr[i]));
                                if (i+1 < count)
                                        args = core->concatStrings(args, comma);
                        }
                }
                return args;
        }
                
        /**
         * Called when an abc file is first decoded.
         * This method builds a list of source files
         * and methods, etc from the given abc file.
         */
        void Debugger::processAbc(PoolObject* pool, ScriptBuffer code)
        {
#ifdef VMCFG_AOT
                if(pool2abcIndex.get(pool) != NULL)
                        return;
#endif

                // first off we build an AbcInfo object 
                AbcFile* abc = new (core->GetGC()) AbcFile(core, (int)code.getSize());
                
                // now let's scan the abc resources pulling out what we need
                scanResources(abc, pool);

                // build a bridging table from pools to abcs
                uintptr index = abcList.size();
                pool2abcIndex.add(pool, (const void*)index);

                // at this point our abc object has been populated with
                // source file objects and should ready to go. 
                // so we add it to the list and we are done
                abcList.add(abc);
        }

        /**
         * Scans the pool object and pulls out information about the abc file
         * placing it in the AbcFile
         */
        void Debugger::scanResources(AbcFile* file, PoolObject* pool)
        {
                // walk all methods 
                for(uint32_t i=0, n = pool->methodCount(); i<n; i++)
                {
                        MethodInfo* f = pool->getMethodInfo(i);
                        if (f->hasMethodBody())
                        {
                                // yes there is code for this method
                                if (f->abc_body_pos())
                                {
                                        // if body_pos is null we havent got the body yet or
                                        // this is an interface method
                                        scanCode(file, pool, f);
                                }
                        }
                }
        }

        /**
         * Scans the bytecode and adds source level information to 
         * the abc info object
         */
        bool Debugger::scanCode(AbcFile* file, PoolObject* pool, MethodInfo* m)
        {
                const byte *abc_start = &m->pool()->code()[0];

                const byte *pos = m->abc_body_pos();

                m->setFile(file);

                AvmCore::skipU30(pos, 4); // max_stack; local_count; init_stack_depth; max_stack_depth;
                int code_len = AvmCore::readU30(pos);

                const byte *start = pos;
                const byte *end = pos + code_len;

                int size = 0;
                int op_count;
                SourceFile* active = NULL; // current source file
        for (const byte* pc=start; pc < end; pc += size)
        {
            op_count = opcodeInfo[*pc].operandCount;
                        if (op_count == -1 && *pc != OP_lookupswitch)
                                return false; // ohh very bad, verifier will catch this

                        size = AvmCore::calculateInstructionWidth(pc);

                        if (pc+size > end)
                                return false; // also bad, let the verifier will handle it

            switch (*pc)
            {
                                case OP_lookupswitch:
                                {
                                        // variable length instruction
                                        const byte *pc2 = pc+4;
                                        int case_count = 1 + readU30(pc2);
                    size += case_count*3;
                                        break;
                                }

                                case OP_debug:
                                {
                                        // form is 8bit type followed by pool entry
                                        // then 4Byte extra info
                                        int type = (uint8)*(pc+1);

                                        switch(type)
                                        {
                                                case DI_LOCAL:
                                                {
                                                        // in this case last word contains
                                                        // register and line number 
                                                        const byte* pc2 = pc+2;
                                                        int index = readU30(pc2);
                                                        int slot = (uint8)*(pc2);
                                                        //int line = readS24(pc+5);

                                                        
                                                        //Atom str = pool->cpool[index];
                                                        Stringp s = pool->getString(index);
                                                        m->setRegName(slot, s);
                                                }
                                        }

                                        break;
                                }

                                case OP_debugline:
                                {
                                        // this means that we have a new source line for the given offset
                                        const byte* pc2 = pc+1;
                                        int line = readU30(pc2);
                                        if (active == NULL)
                                                AvmAssert(0 == 1); // means OP_debugline appeared before OP_debugfile which is WRONG!  Fix compiler
                                        else
                                                active->addLine(line, m, (int)(pc - abc_start));
                                        break;
                                }

                                case OP_debugfile:
                                {
                                        // new or existing source file
                                        const byte* pc2 = pc+1;
                                        Stringp name = pool->getString(readU30(pc2));
                                        active = file->sourceNamed(name);
                                        if (active == NULL)
                                        {
                                                active = new (core->GetGC()) SourceFile(core->GetGC(), name);
                                                file->sourceAdd(active);
                                        }
                                        break;
                                }       
                        }
                }
                return true;
        }

        /**
         * Returns the call stack depth (i.e. number of frames).
         */
        int Debugger::frameCount()
        {
                const int MAX_FRAMES = 500; // we need a max, for perf. reasons (bug 175526)
                CallStackNode* trace = core->callStack;
                int count = 1;
                while( (trace = trace->next()) != 0 && count < MAX_FRAMES )
                        count++;
                return count;
        }

        /**
         * Set frame to point to the specified frame number
         * or null if frame number does not exist. 
         */
        DebugFrame* Debugger::frameAt(int frameNbr)
        {
                DebugFrame* frame = NULL;

                if (frameNbr >= 0)
                {
                        CallStackNode* trace = locateTrace(frameNbr);
                        if (trace)
                                frame = new (core->GetGC()) DebugStackFrame(frameNbr, trace, this);
                }
                return frame;
        }

        /**
         * Stack frames are labelled 0 to stackTrace->depth
         * With zero being the topmost or most recent frame.
         */
        CallStackNode* Debugger::locateTrace(int frameNbr)
        {
                int count = 0;
                CallStackNode* trace = core->callStack;
                while(count++ < frameNbr && trace != NULL)
                        trace = trace->next();

                return trace;
        }

        /**
         * # of abc files available
         */
        int Debugger::abcCount() const
        {
                return abcList.size();
        }

        /**
         * Get information on each of the abc files that
         * have been loaded. 
         */
        AbcInfo* Debugger::abcAt(int index) const
        {
                return abcList.get(index);
        }

        /**
         * Contains all known debug information regarding a single 
         * abc/swf file
         */
        AbcFile::AbcFile(AvmCore* core, int size)
                : core(core),
                  source(core->GetGC()),
                  byteCount(size)
        {
                sourcemap = new (core->GetGC()) HeapHashtable(core->GetGC());
        }

        int AbcFile::sourceCount() const
        {
                return source.size(); 
        }

        SourceInfo* AbcFile::sourceAt(int index) const
        {
                return source.get(index);
        }

        int AbcFile::size() const 
        { 
                return byteCount; 
        }

        /**
         * Find a source file with the given name
         */
        SourceFile* AbcFile::sourceNamed(Stringp name)
        {
                Atom atom = sourcemap->get(name->atom());
                if (AvmCore::isUndefined(atom))
                        return NULL;
                uint32 index = AvmCore::integer_u(atom);
                return source.get(index);
        }

        /**
         * Add source file to list; no check for uniqueness
         * of name
         */
        void AbcFile::sourceAdd(SourceFile* s)
        {
                uint32 index = source.add(s);
                sourcemap->add(s->name()->atom(), core->uintToAtom(index));
        }

        /**
         * new source info
         */
        SourceFile::SourceFile(MMgc::GC* gc, Stringp name)
                : named(name)
                , functions(gc)
        {
        }

        Stringp SourceFile::name() const 
        { 
                return named; 
        }

        /**
         * A line - offset pair should be recorded 
         */
        void SourceFile::addLine(int linenum, MethodInfo* func, int offset)
        {
                // Add the function to our list if it doesn't exist.  Use lastIndexOf() instead of
                // indexOf(), because this will be faster in the very common case where the function
                // already exists at the end of the list.
                int index = functions.lastIndexOf(func);
                if (index < 0)
                        index = functions.add(func);

                // line numbers for a given function don't always come in sequential
                // order -- for example, I've seen them come out of order if a function
                // contains an inner anonymous function -- so, update every time we get called
                func->updateSourceLines(linenum, offset);
                sourceLines.set(linenum);
        }

        int SourceFile::functionCount() const 
        { 
                return functions.size(); 
        }

        MethodInfo* SourceFile::functionAt(int index) const 
        {
                return functions.get(index); 
        }

        bool SourceFile::setBreakpoint(int linenum)
        {
                if (!sourceLines.get(linenum))
                        return false;
                breakpoints.set(linenum);
                return true;
        }

        bool SourceFile::clearBreakpoint(int linenum)
        {
                if (!breakpoints.get(linenum))
                        return false;
                breakpoints.clear(linenum);
                return true;
        }

        bool SourceFile::hasBreakpoint(int linenum)
        {
                return breakpoints.get(linenum);
        }

        DebugStackFrame::DebugStackFrame(int nbr, CallStackNode* tr, Debugger* debug)
                : trace(tr)
                , debugger(debug)
                , frameNbr(nbr)
        {
                AvmAssert(tr != NULL);
                AvmAssert(debug != NULL);
        }

        /**
         * Identifies the source file and source line number
         * corresponding to this frame 
         */
        bool DebugStackFrame::sourceLocation(SourceInfo*& source, int& linenum)
        {
                // use the method info to locate the abcfile / source 
                if (trace->info() && trace->filename() && debugger)
                {
                        uintptr index = (uintptr)debugger->pool2abcIndex.get(Atom(trace->info()->pool()));

                        AbcFile* abc = (AbcFile*)debugger->abcAt((int)index);
                        source = abc->sourceNamed(trace->filename());
                }
                linenum = trace->linenum();

                // valid info?
                return (source != NULL && linenum > 0);
        }

        /**
         * This pointer for the frame
         */
        bool DebugStackFrame::dhis(Atom& a)
        {
                bool worked = false;
                if (trace->framep() && trace->info())
                {
                        trace->info()->boxLocals(trace->framep(), 0, trace->traits(), &a, 0, 1); // pull framep[0] = [this] 
                        worked = true;
                }
                else
                {
                        a = undefinedAtom;
                }
                return worked;
        }

        /**
         * @return a pointer to an object Atom whose members are
         * the arguments passed into a function for this frame
         */
        bool DebugStackFrame::arguments(Atom*& ar, int& count)
        {
                bool worked = true;
                if (trace->framep() && trace->info())
                {
                        int firstArgument, pastLastArgument;
                        argumentBounds(&firstArgument, &pastLastArgument);
                        count = pastLastArgument - firstArgument;
                        if ((count > 0) && debugger)
                        {
                                // pull the args into an array -- skip [0] which is [this]
                                ar = (Atom*) debugger->core->GetGC()->Calloc(count, sizeof(Atom), GC::kContainsPointers|GC::kZero);
                                MethodInfo* info = trace->info();
                                info->boxLocals(trace->framep(), firstArgument, trace->traits(), ar, 0, count);
                        }
                }
                else
                {
                        worked = false;
                        count = 0;
                }
                return worked;
        }

        bool DebugStackFrame::setArgument(int which, Atom& val)
        {
                bool worked = false;
                if (trace->framep() && trace->info())
                {
                        int firstArgument, pastLastArgument;
                        argumentBounds(&firstArgument, &pastLastArgument);
                        int count = pastLastArgument - firstArgument;
                        if (count > 0 && which < count)
                        {
                                // copy the single arg over
                                MethodInfo* info = trace->info();
                                info->unboxLocals(&val, 0, trace->traits(), trace->framep(), firstArgument+which, 1);
                                worked = true;
                        }
                }
                return worked;
        }

        /**
         * @return a pointer to an object Atom whose members are
         * the active locals for this frame.
         */
        bool DebugStackFrame::locals(Atom*& ar, int& count)
        {
                bool worked = true;
                if (trace->framep() && trace->info())
                {
                        int firstLocal, pastLastLocal;
                        localBounds(&firstLocal, &pastLastLocal);
                        count = pastLastLocal - firstLocal;
                        AvmAssert(count >= 0);
                        if ((count > 0) && debugger)
                        {
                                // frame looks like [this][param0...paramN][local0...localN]
                                ar = (Atom*) debugger->core->GetGC()->Calloc(count, sizeof(Atom), GC::kContainsPointers|GC::kZero);
                                MethodInfo* info = trace->info();
                                info->boxLocals(trace->framep(), firstLocal, trace->traits(), ar, 0, count);

                                // If NEED_REST or NEED_ARGUMENTS is set, and the jit is being used, then the first
                                // local is actually not an atom at all -- it is an ArrayObject*.  So, we need to
                                // convert it to an atom.  (If the interpreter is being used instead of the jit, then
                                // it is stored as an atom.)
                                if (info->needRestOrArguments())
                                {
                                        int atomType = atomKind(ar[0]);
                                        if (atomType == 0) // 0 is not a legal atom type, so ar[0] is not an atom
                                        {
                                                ScriptObject* obj = (ScriptObject*)ar[0];
                                                ar[0] = obj->atom();
                                        }
                                }
                        }
                }
                else
                {
                        worked = false;
                        count = 0;
                }
                return worked;
        }

        bool DebugStackFrame::setLocal(int which, Atom& val)
        {
                bool worked = false;
                if (trace->framep() && trace->info())
                {
                        int firstLocal, pastLastLocal;
                        localBounds(&firstLocal, &pastLastLocal);
                        int count = pastLastLocal - firstLocal;
                        if (count > 0 && which < count)
                        {
                                MethodInfo* info = trace->info();
                                if (which == 0 && info->needRestOrArguments())
                                {
                                        // They are trying to modify the first local, but that is actually the special
                                        // array for "...rest" or for "arguments".  That is too complicated to allow
                                        // right now.  We're just going to fail the request.
                                }
                                else
                                {
                                        // copy the single arg over
                                        info->unboxLocals(&val, 0, trace->traits(), trace->framep(), firstLocal+which, 1);
                                        worked = true;
                                }
                        }
                }
                return worked;
        }

        // Returns the indices of the arguments: [firstArgument, pastLastArgument)
        void DebugStackFrame::argumentBounds(int* firstArgument, int* pastLastArgument)
        {
                *firstArgument = 1; // because [0] is 'this'
                *pastLastArgument = indexOfFirstLocal();
        }

        // Returns the indices of the locals: [firstLocal, pastLastLocal)
        void DebugStackFrame::localBounds(int* firstLocal, int* pastLastLocal)
        {
                *firstLocal = indexOfFirstLocal();
                if (trace->framep() && trace->info())
                {
                        const MethodSignature* ms = trace->info()->getMethodSignature();
                        *pastLastLocal = ms->local_count();
                }
                else
                {
                        *pastLastLocal = *firstLocal;
                }
        }

        int DebugStackFrame::indexOfFirstLocal()
        {
                // 'trace->argc' is the number of arguments that were actually passed in
                // to this function, but that is not what we want -- we want
                // 'info->param_count', because that is the number of arguments we were
                // *expecting* to get.  There are two reasons we want 'info->param_count':
                //
                // (1) if the caller passed in too many args, we want to ignore the
                //     trailing ones; and
                // (2) if the caller passed in too few args to a function that has some
                //     default parameters, we want to display the args with their default
                //     values.
                if (!trace->info())
                        return 0;
                MethodSignaturep ms = trace->info()->getMethodSignature();
                return 1 + ms->param_count();
        }

}

#endif /* DEBUGGER */

/* [<][>][^][v][top][bottom][index][help] */