public JavaDebugDisassemblyStream(JavaDebugCodeContext executionContext)
        {
            Contract.Requires<ArgumentNullException>(executionContext != null, "executionContext");

            _executionContext = executionContext;
            _bytecode = _executionContext.Location.GetMethod().GetBytecodes();
            _disassembledMethod = BytecodeDisassembler.Disassemble(_bytecode);

            var constantPool = executionContext.Location.GetDeclaringType().GetConstantPool();
            ReadOnlyCollection<ExceptionTableEntry> exceptionTable;
            try
            {
                exceptionTable = executionContext.Location.GetMethod().GetExceptionTable();
            }
            catch (DebuggerException)
            {
                exceptionTable = new ReadOnlyCollection<ExceptionTableEntry>(new ExceptionTableEntry[0]);
            }

            _evaluationStackDepths = BytecodeDisassembler.GetEvaluationStackDepths(_disassembledMethod, constantPool, exceptionTable);
        }
Example #2
0
        public static ImmutableList <int?> GetEvaluationStackDepths(DisassembledMethod disassembledMethod, ReadOnlyCollection <ConstantPoolEntry> constantPool, ReadOnlyCollection <ExceptionTableEntry> exceptionTable)
        {
            Contract.Requires <ArgumentNullException>(disassembledMethod != null, "disassembledMethod");
            Contract.Requires <ArgumentNullException>(constantPool != null, "constantPool");
            Contract.Requires <ArgumentNullException>(exceptionTable != null, "exceptionTable");

            Contract.Ensures(Contract.Result <ImmutableList <int?> >() != null);
            Contract.Ensures(Contract.Result <ImmutableList <int?> >().Count == disassembledMethod.Instructions.Count);

            int?[]      depths    = new int?[disassembledMethod.Instructions.Count];
            Queue <int> workQueue = new Queue <int>();

            // can obviously start at the beginning of the method
            depths[0] = 0;
            workQueue.Enqueue(0);
            // can also start inside each exception handler
            foreach (var entry in exceptionTable)
            {
                int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == entry.HandlerOffset);
                if (!depths[nextIndex].HasValue)
                {
                    depths[nextIndex] = 1;
                    workQueue.Enqueue(nextIndex);
                }
            }

            while (workQueue.Count > 0)
            {
                int             index       = workQueue.Dequeue();
                JavaInstruction instruction = disassembledMethod.Instructions[index];
                int             netImpact;

                List <string> argumentSignatures  = null;
                string        returnTypeSignature = null;
                if (instruction.OpCode.FlowControl == JavaFlowControl.Call)
                {
                    IConstantMemberReference memberReference = (IConstantMemberReference)constantPool[instruction.Operands.ConstantPoolIndex - 1];
                    ConstantNameAndType      nameAndType     = (ConstantNameAndType)constantPool[memberReference.NameAndTypeIndex - 1];
                    ConstantUtf8             descriptor      = (ConstantUtf8)constantPool[nameAndType.DescriptorIndex - 1];
                    string signature = descriptor.Value;
                    SignatureHelper.ParseMethodSignature(signature, out argumentSignatures, out returnTypeSignature);
                }

                switch (instruction.OpCode.StackBehaviorPop)
                {
                case JavaStackBehavior.Pop0:
                    netImpact = 0;
                    break;

                case JavaStackBehavior.Pop1:
                case JavaStackBehavior.PopI:
                case JavaStackBehavior.PopI8:
                case JavaStackBehavior.PopR4:
                case JavaStackBehavior.PopR8:
                case JavaStackBehavior.PopRef:
                    netImpact = -1;
                    break;

                case JavaStackBehavior.Pop1_Pop1:
                case JavaStackBehavior.PopI_PopI:
                case JavaStackBehavior.PopI8_PopI8:
                case JavaStackBehavior.PopI8_PopI:
                case JavaStackBehavior.PopR4_PopR4:
                case JavaStackBehavior.PopR8_PopR8:
                case JavaStackBehavior.PopRef_Pop1:
                case JavaStackBehavior.PopRef_PopI:
                    netImpact = -2;
                    break;

                case JavaStackBehavior.PopRef_PopI_PopI:
                case JavaStackBehavior.PopRef_PopI_PopI8:
                case JavaStackBehavior.PopRef_PopI_PopR4:
                case JavaStackBehavior.PopRef_PopI_PopR8:
                case JavaStackBehavior.PopRef_PopI_PopRef:
                case JavaStackBehavior.PopRef_PopRef:
                    netImpact = -3;
                    break;

                case JavaStackBehavior.PopVar:
                    switch (instruction.OpCode.OpCode)
                    {
                    case JavaOpCodeTag.Dup_x2:
                    case JavaOpCodeTag.Dup2:
                    case JavaOpCodeTag.Dup2_x1:
                    case JavaOpCodeTag.Dup2_x2:
                        netImpact = 1;
                        break;

                    case JavaOpCodeTag.Invokestatic:
                        netImpact = -argumentSignatures.Count;
                        if (returnTypeSignature != "V")
                        {
                            netImpact++;
                        }
                        break;

                    case JavaOpCodeTag.Invokeinterface:
                    case JavaOpCodeTag.Invokespecial:
                    case JavaOpCodeTag.Invokevirtual:
                        netImpact = -argumentSignatures.Count - 1;
                        if (returnTypeSignature != "V")
                        {
                            netImpact++;
                        }
                        break;

                    case JavaOpCodeTag.Multianewarray:
                        netImpact = -instruction.Operands.Dimensions;
                        break;

                    default:
                        throw new FormatException();
                    }

                    break;

                default:
                    throw new FormatException();
                }

                switch (instruction.OpCode.StackBehaviorPush)
                {
                case JavaStackBehavior.Push0:
                    break;

                case JavaStackBehavior.Push1:
                case JavaStackBehavior.PushI:
                case JavaStackBehavior.PushI8:
                case JavaStackBehavior.PushR4:
                case JavaStackBehavior.PushR8:
                case JavaStackBehavior.PushRef:
                case JavaStackBehavior.PushRet:
                    netImpact++;
                    break;

                case JavaStackBehavior.Push1_Push1:
                    netImpact += 2;
                    break;

                case JavaStackBehavior.PushVar:
                    // these are all handled in the pop section
                    break;

                default:
                    throw new FormatException();
                }

                switch (instruction.OpCode.FlowControl)
                {
                case JavaFlowControl.Next:
                case JavaFlowControl.Break:
                case JavaFlowControl.Call:
                case JavaFlowControl.ConditionalBranch:
                case JavaFlowControl.Special:
                    if (!depths[index + 1].HasValue)
                    {
                        depths[index + 1] = depths[index] + netImpact;
                        workQueue.Enqueue(index + 1);
                    }

                    if (instruction.OpCode.FlowControl == JavaFlowControl.ConditionalBranch)
                    {
                        goto case JavaFlowControl.Branch;
                    }

                    break;

                case JavaFlowControl.Branch:
                    switch (instruction.OpCode.OpCode)
                    {
                    case JavaOpCodeTag.Lookupswitch:
                    {
                        LookupSwitchData switchData = (LookupSwitchData)disassembledMethod.SwitchData[instruction.Operands.SwitchDataIndex];
                        foreach (var pair in switchData.Pairs)
                        {
                            int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == instruction.Offset + pair.Value);
                            if (!depths[nextIndex].HasValue)
                            {
                                depths[nextIndex] = depths[index] + netImpact;
                                workQueue.Enqueue(nextIndex);
                            }
                        }

                        break;
                    }

                    case JavaOpCodeTag.Tableswitch:
                    {
                        TableSwitchData switchData = (TableSwitchData)disassembledMethod.SwitchData[instruction.Operands.SwitchDataIndex];
                        foreach (var offset in switchData.Offsets)
                        {
                            int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == instruction.Offset + offset);
                            if (!depths[nextIndex].HasValue)
                            {
                                depths[nextIndex] = depths[index] + netImpact;
                                workQueue.Enqueue(nextIndex);
                            }
                        }

                        break;
                    }

                    default:
                    {
                        // single branch target
                        int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == instruction.Offset + instruction.Operands.BranchTarget);
                        if (!depths[nextIndex].HasValue)
                        {
                            depths[nextIndex] = depths[index] + netImpact;
                            workQueue.Enqueue(nextIndex);
                        }

                        break;
                    }
                    }

                    break;

                case JavaFlowControl.Return:
                    // no work in this method following this instruction
                    break;

                case JavaFlowControl.Throw:
                    // 'catch' blocks are handled separately
                    break;

                case JavaFlowControl.Meta:
                    throw new NotImplementedException();

                default:
                    throw new FormatException();
                }
            }

            return(new ImmutableList <int?>(depths));
        }
 private static int GetInstructionAtOffset(DisassembledMethod disassembledMethod, long codeIndex)
 {
     return disassembledMethod.Instructions.FindIndex(i => i.Offset == codeIndex);
 }
        public static ImmutableList<int?> GetEvaluationStackDepths(DisassembledMethod disassembledMethod, ReadOnlyCollection<ConstantPoolEntry> constantPool, ReadOnlyCollection<ExceptionTableEntry> exceptionTable)
        {
            Contract.Requires<ArgumentNullException>(disassembledMethod != null, "disassembledMethod");
            Contract.Requires<ArgumentNullException>(constantPool != null, "constantPool");
            Contract.Requires<ArgumentNullException>(exceptionTable != null, "exceptionTable");

            Contract.Ensures(Contract.Result<ImmutableList<int?>>() != null);
            Contract.Ensures(Contract.Result<ImmutableList<int?>>().Count == disassembledMethod.Instructions.Count);

            int?[] depths = new int?[disassembledMethod.Instructions.Count];
            Queue<int> workQueue = new Queue<int>();

            // can obviously start at the beginning of the method
            depths[0] = 0;
            workQueue.Enqueue(0);
            // can also start inside each exception handler
            foreach (var entry in exceptionTable)
            {
                int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == entry.HandlerOffset);
                if (!depths[nextIndex].HasValue)
                {
                    depths[nextIndex] = 1;
                    workQueue.Enqueue(nextIndex);
                }
            }

            while (workQueue.Count > 0)
            {
                int index = workQueue.Dequeue();
                JavaInstruction instruction = disassembledMethod.Instructions[index];
                int netImpact;

                List<string> argumentSignatures = null;
                string returnTypeSignature = null;
                if (instruction.OpCode.FlowControl == JavaFlowControl.Call)
                {
                    IConstantMemberReference memberReference = (IConstantMemberReference)constantPool[instruction.Operands.ConstantPoolIndex - 1];
                    ConstantNameAndType nameAndType = (ConstantNameAndType)constantPool[memberReference.NameAndTypeIndex - 1];
                    ConstantUtf8 descriptor = (ConstantUtf8)constantPool[nameAndType.DescriptorIndex - 1];
                    string signature = descriptor.Value;
                    SignatureHelper.ParseMethodSignature(signature, out argumentSignatures, out returnTypeSignature);
                }

                switch (instruction.OpCode.StackBehaviorPop)
                {
                case JavaStackBehavior.Pop0:
                    netImpact = 0;
                    break;

                case JavaStackBehavior.Pop1:
                case JavaStackBehavior.PopI:
                case JavaStackBehavior.PopI8:
                case JavaStackBehavior.PopR4:
                case JavaStackBehavior.PopR8:
                case JavaStackBehavior.PopRef:
                    netImpact = -1;
                    break;

                case JavaStackBehavior.Pop1_Pop1:
                case JavaStackBehavior.PopI_PopI:
                case JavaStackBehavior.PopI8_PopI8:
                case JavaStackBehavior.PopI8_PopI:
                case JavaStackBehavior.PopR4_PopR4:
                case JavaStackBehavior.PopR8_PopR8:
                case JavaStackBehavior.PopRef_Pop1:
                case JavaStackBehavior.PopRef_PopI:
                    netImpact = -2;
                    break;

                case JavaStackBehavior.PopRef_PopI_PopI:
                case JavaStackBehavior.PopRef_PopI_PopI8:
                case JavaStackBehavior.PopRef_PopI_PopR4:
                case JavaStackBehavior.PopRef_PopI_PopR8:
                case JavaStackBehavior.PopRef_PopI_PopRef:
                case JavaStackBehavior.PopRef_PopRef:
                    netImpact = -3;
                    break;

                case JavaStackBehavior.PopVar:
                    switch (instruction.OpCode.OpCode)
                    {
                    case JavaOpCodeTag.Dup_x2:
                    case JavaOpCodeTag.Dup2:
                    case JavaOpCodeTag.Dup2_x1:
                    case JavaOpCodeTag.Dup2_x2:
                        netImpact = 1;
                        break;

                    case JavaOpCodeTag.Invokestatic:
                        netImpact = -argumentSignatures.Count;
                        if (returnTypeSignature != "V")
                            netImpact++;
                        break;

                    case JavaOpCodeTag.Invokeinterface:
                    case JavaOpCodeTag.Invokespecial:
                    case JavaOpCodeTag.Invokevirtual:
                        netImpact = -argumentSignatures.Count - 1;
                        if (returnTypeSignature != "V")
                            netImpact++;
                        break;

                    case JavaOpCodeTag.Multianewarray:
                        netImpact = -instruction.Operands.Dimensions;
                        break;

                    default:
                        throw new FormatException();
                    }

                    break;

                default:
                    throw new FormatException();
                }

                switch (instruction.OpCode.StackBehaviorPush)
                {
                case JavaStackBehavior.Push0:
                    break;

                case JavaStackBehavior.Push1:
                case JavaStackBehavior.PushI:
                case JavaStackBehavior.PushI8:
                case JavaStackBehavior.PushR4:
                case JavaStackBehavior.PushR8:
                case JavaStackBehavior.PushRef:
                case JavaStackBehavior.PushRet:
                    netImpact++;
                    break;

                case JavaStackBehavior.Push1_Push1:
                    netImpact += 2;
                    break;

                case JavaStackBehavior.PushVar:
                    // these are all handled in the pop section
                    break;

                default:
                    throw new FormatException();
                }

                switch (instruction.OpCode.FlowControl)
                {
                case JavaFlowControl.Next:
                case JavaFlowControl.Break:
                case JavaFlowControl.Call:
                case JavaFlowControl.ConditionalBranch:
                case JavaFlowControl.Special:
                    if (!depths[index + 1].HasValue)
                    {
                        depths[index + 1] = depths[index] + netImpact;
                        workQueue.Enqueue(index + 1);
                    }

                    if (instruction.OpCode.FlowControl == JavaFlowControl.ConditionalBranch)
                        goto case JavaFlowControl.Branch;

                    break;

                case JavaFlowControl.Branch:
                    switch (instruction.OpCode.OpCode)
                    {
                    case JavaOpCodeTag.Lookupswitch:
                        {
                            LookupSwitchData switchData = (LookupSwitchData)disassembledMethod.SwitchData[instruction.Operands.SwitchDataIndex];
                            foreach (var pair in switchData.Pairs)
                            {
                                int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == instruction.Offset + pair.Value);
                                if (!depths[nextIndex].HasValue)
                                {
                                    depths[nextIndex] = depths[index] + netImpact;
                                    workQueue.Enqueue(nextIndex);
                                }
                            }

                            break;
                        }

                    case JavaOpCodeTag.Tableswitch:
                        {
                            TableSwitchData switchData = (TableSwitchData)disassembledMethod.SwitchData[instruction.Operands.SwitchDataIndex];
                            foreach (var offset in switchData.Offsets)
                            {
                                int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == instruction.Offset + offset);
                                if (!depths[nextIndex].HasValue)
                                {
                                    depths[nextIndex] = depths[index] + netImpact;
                                    workQueue.Enqueue(nextIndex);
                                }
                            }

                            break;
                        }

                    default:
                        {
                            // single branch target
                            int nextIndex = disassembledMethod.Instructions.FindIndex(i => i.Offset == instruction.Offset + instruction.Operands.BranchTarget);
                            if (!depths[nextIndex].HasValue)
                            {
                                depths[nextIndex] = depths[index] + netImpact;
                                workQueue.Enqueue(nextIndex);
                            }

                            break;
                        }
                    }

                    break;

                case JavaFlowControl.Return:
                    // no work in this method following this instruction
                    break;

                case JavaFlowControl.Throw:
                    // 'catch' blocks are handled separately
                    break;

                case JavaFlowControl.Meta:
                    throw new NotImplementedException();

                default:
                    throw new FormatException();
                }

                if (workQueue.Count == 0)
                {
                    for (int i = 0; i < depths.Length; i++)
                    {
                        if (depths[i].HasValue)
                            continue;

                        depths[i] = 1;
                        workQueue.Enqueue(i);
                        break;
                    }
                }
            }

            return new ImmutableList<int?>(depths);
        }
            public StepEventFilter(EventKind internalEventKind, RequestId requestId, SuspendPolicy suspendPolicy, IEnumerable<EventRequestModifier> modifiers, ThreadId thread, JvmtiEnvironment environment, JniEnvironment nativeEnvironment, StepSize size, StepDepth depth)
                : base(internalEventKind, requestId, suspendPolicy, modifiers, thread)
            {
                if (size == StepSize.Statement && JavaVM.DisableStatementStepping)
                    size = StepSize.Line;

                _size = size;
                _depth = depth;

                // gather reference information for the thread
                using (var threadHandle = environment.VirtualMachine.GetLocalReferenceForThread(nativeEnvironment, thread))
                {
                    if (threadHandle.IsAlive)
                    {
                        jvmtiError error = environment.GetFrameLocation(threadHandle.Value, 0, out _lastMethod, out _lastLocation);
                        if (error == jvmtiError.None)
                            error = environment.GetFrameCount(threadHandle.Value, out _stackDepth);

                        if (error == jvmtiError.None)
                            _hasMethodInfo = true;

                        UpdateLastLine(environment);

                        if (error == jvmtiError.None && size == StepSize.Statement && (depth == StepDepth.Over || depth == StepDepth.Into))
                        {
                            byte[] bytecode;
                            JvmtiErrorHandler.ThrowOnFailure(environment.GetBytecodes(_lastMethod, out bytecode));
                            _disassembledMethod = BytecodeDisassembler.Disassemble(bytecode);

                            TaggedReferenceTypeId declaringClass;
                            JvmtiErrorHandler.ThrowOnFailure(environment.GetMethodDeclaringClass(nativeEnvironment, _lastMethod, out declaringClass));
                            using (var classHandle = environment.VirtualMachine.GetLocalReferenceForClass(nativeEnvironment, declaringClass.TypeId))
                            {
                                int constantPoolCount;
                                byte[] data;
                                JvmtiErrorHandler.ThrowOnFailure(environment.GetConstantPool(classHandle.Value, out constantPoolCount, out data));

                                List<ConstantPoolEntry> entryList = new List<ConstantPoolEntry>();
                                int currentPosition = 0;
                                for (int i = 0; i < constantPoolCount - 1; i++)
                                {
                                    entryList.Add(ConstantPoolEntry.FromBytes(data, ref currentPosition));
                                    switch (entryList.Last().Type)
                                    {
                                    case ConstantType.Double:
                                    case ConstantType.Long:
                                        // these entries take 2 slots
                                        entryList.Add(ConstantPoolEntry.Reserved);
                                        i++;
                                        break;

                                    default:
                                        break;
                                    }
                                }

                                _constantPool = entryList.AsReadOnly();

                                string classSignature;
                                string classGenericSignature;
                                JvmtiErrorHandler.ThrowOnFailure(environment.GetClassSignature(classHandle.Value, out classSignature, out classGenericSignature));
                                string methodName;
                                string methodSignature;
                                string methodGenericSignature;
                                JvmtiErrorHandler.ThrowOnFailure(environment.GetMethodName(_lastMethod, out methodName, out methodSignature, out methodGenericSignature));

                                jobject classLoader;
                                JvmtiErrorHandler.ThrowOnFailure(environment.GetClassLoader(classHandle.Value, out classLoader));
                                long classLoaderTag;
                                JvmtiErrorHandler.ThrowOnFailure(environment.TagClassLoader(classLoader, out classLoaderTag));

                                ReadOnlyCollection<ExceptionTableEntry> exceptionTable;
                                JvmtiErrorHandler.ThrowOnFailure(environment.VirtualMachine.GetExceptionTable(classLoaderTag, classSignature, methodName, methodSignature, out exceptionTable));

                                _evaluationStackDepths = BytecodeDisassembler.GetEvaluationStackDepths(_disassembledMethod, _constantPool, exceptionTable);
                            }
                        }
                    }
                }
            }