pax_global_header00006660000000000000000000000064136124311350014511gustar00rootroot0000000000000052 comment=46bd01d2b220856f50835e5d2c5e277c4571915f byebug-11.1.1/000077500000000000000000000000001361243113500130475ustar00rootroot00000000000000byebug-11.1.1/.bundle/000077500000000000000000000000001361243113500143765ustar00rootroot00000000000000byebug-11.1.1/.bundle/config000066400000000000000000000000331361243113500155620ustar00rootroot00000000000000--- BUNDLE_PATH: ".bundle" byebug-11.1.1/.clang-format000066400000000000000000000006511361243113500154240ustar00rootroot00000000000000AlwaysBreakAfterDefinitionReturnType: All BreakBeforeBinaryOperators: NonAssignment BreakBeforeBraces: Custom BraceWrapping: AfterControlStatement: true AfterEnum: true AfterFunction: true AfterStruct: true AfterUnion: true BeforeElse: true ColumnLimit: 0 ContinuationIndentWidth: 2 IndentCaseLabels: true MaxEmptyLinesToKeep: 2 PointerAlignment: Right SpaceAfterCStyleCast: false SpacesBeforeTrailingComments: 1 byebug-11.1.1/.codeclimate.yml000066400000000000000000000007261361243113500161260ustar00rootroot00000000000000--- version: "2" checks: argument-count: enabled: false complex-logic: enabled: false file-lines: enabled: false method-complexity: enabled: false method-count: enabled: false method-lines: enabled: false nested-control-flow: enabled: false return-statements: enabled: false similar-code: enabled: false identical-code: enabled: false exclude_patterns: - .bundle/ - coverage/ - doc/ - tmp/ byebug-11.1.1/.github/000077500000000000000000000000001361243113500144075ustar00rootroot00000000000000byebug-11.1.1/.github/FUNDING.yml000066400000000000000000000000411361243113500162170ustar00rootroot00000000000000--- tidelift: "rubygems/byebug" byebug-11.1.1/.github/ISSUE_TEMPLATE.md000066400000000000000000000010741361243113500171160ustar00rootroot00000000000000## Problem description Replace this text with a description of the problem you're having. Make sure that you're running the latest stable release of both byebug and ruby and that the problem you're reporting hasn't been reported (and potentially fixed) already. ## Expected behavior Describe here how you expected byebug to behave in this particular situation. ## Actual behavior Describe here what actually happened. ## Steps to reproduce the problem This is extremely important! Providing us with a reliable way to reproduce a problem will expedite its solution. byebug-11.1.1/.github/workflows/000077500000000000000000000000001361243113500164445ustar00rootroot00000000000000byebug-11.1.1/.github/workflows/build.yml000066400000000000000000000077311361243113500202760ustar00rootroot00000000000000--- name: build on: pull_request: push: branches: - master jobs: test: container: deividrodriguez/byebug:${{ matrix.version }}-${{ matrix.line_editor }}-${{ matrix.compiler }} runs-on: ubuntu-18.04 strategy: matrix: version: ["2.4.9", "2.5.7", "2.6.5", "2.7.0", "head"] line_editor: ["libedit", "readline"] compiler: ["clang", "gcc"] steps: - uses: actions/checkout@v2 - name: Run CI checks run: | bin/setup.sh bin/rake - name: Set ENV for codeclimate (pull_request) run: | git fetch --no-tags --prune --depth=1 origin +refs/heads/$GITHUB_HEAD_REF:refs/remotes/origin/$GITHUB_HEAD_REF echo "::set-env name=GIT_BRANCH::$GITHUB_HEAD_REF" echo "::set-env name=GIT_COMMIT_SHA::$(git rev-parse origin/$GITHUB_HEAD_REF)" if: github.event_name == 'pull_request' - name: Set ENV for codeclimate (push) run: | echo "::set-env name=GIT_BRANCH::$GITHUB_REF" echo "::set-env name=GIT_COMMIT_SHA::$GITHUB_SHA" if: github.event_name == 'push' - name: Save coverage run: cc-test-reporter format-coverage --output coverage/codeclimate.${{ matrix.version }}-${{ matrix.line_editor }}-${{ matrix.compiler }}.json - uses: actions/upload-artifact@v1 with: name: coverage-${{ matrix.version }}-${{ matrix.line_editor }}-${{ matrix.compiler }} path: coverage/codeclimate.${{ matrix.version }}-${{ matrix.line_editor }}-${{ matrix.compiler }}.json timeout-minutes: 15 coverage: container: deividrodriguez/byebug:2.7.0-readline-gcc runs-on: ubuntu-18.04 env: CC_TEST_REPORTER_ID: 02530029b1e956220f05076c590b84b9ab078362c9083312eb2ad41cab138408 needs: test steps: - uses: actions/download-artifact@v1 with: name: coverage-2.4.9-libedit-clang path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.4.9-libedit-gcc path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.4.9-readline-clang path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.4.9-readline-gcc path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.5.7-libedit-clang path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.5.7-libedit-gcc path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.5.7-readline-clang path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.5.7-readline-gcc path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.6.5-libedit-clang path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.6.5-libedit-gcc path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.6.5-readline-clang path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.6.5-readline-gcc path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.7.0-libedit-clang path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.7.0-libedit-gcc path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.7.0-readline-clang path: coverage - uses: actions/download-artifact@v1 with: name: coverage-2.7.0-readline-gcc path: coverage - name: Aggregate & upload results to Code Climate run: | cc-test-reporter sum-coverage coverage/codeclimate.*.json cc-test-reporter upload-coverage byebug-11.1.1/.github/workflows/build_head_images.yml000066400000000000000000000015771361243113500226060ustar00rootroot00000000000000--- name: build_head_images on: schedule: - cron: "0 0 * * *" jobs: build: container: deividrodriguez/byebug:2.6.5-readline-gcc runs-on: ubuntu-18.04 strategy: matrix: line_editor: ["libedit", "readline"] compiler: ["clang", "gcc"] steps: - uses: actions/checkout@v2 - name: Install Docker client run: | apk add curl curl -L -o /tmp/docker.tgz https://download.docker.com/linux/static/stable/x86_64/docker-18.09.0.tgz tar -xz --strip-components 1 -C /usr/bin -f /tmp/docker.tgz - name: Setup environment run: bin/setup.sh - name: Build and push docker image run: bin/rake docker:build_and_push_head[${{ matrix.line_editor }},${{ matrix.compiler }}] env: DOCKER_USER: ${{ secrets.DOCKER_USER }} DOCKER_PASS: ${{ secrets.DOCKER_PASS }} byebug-11.1.1/.github/workflows/fedora.yml000066400000000000000000000010531361243113500204260ustar00rootroot00000000000000--- name: fedora on: pull_request: push: branches: - master jobs: test: container: fedora:31 runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - name: Install OS dependencies run: dnf install -y ruby ruby-devel make gcc redhat-rpm-config - name: Install bundler run: gem install bundler:2.1.4 - name: Install development dependencies run: bundle install - name: Install byebug run: bin/rake install - name: Run byebug run: byebug -h byebug-11.1.1/.gitignore000066400000000000000000000003311361243113500150340ustar00rootroot00000000000000# # Only generated files & Byebug specific files to be kept here. # tmp pkg doc .yardoc coverage .gdb_history .bundle/* !.bundle/config .byebugrc .byebug_history .envrc lib/byebug/byebug.so lib/byebug/byebug.bundle byebug-11.1.1/.mdlrc000066400000000000000000000000351361243113500141470ustar00rootroot00000000000000rules "~MD002,~MD013,~MD024" byebug-11.1.1/.rubocop.yml000066400000000000000000000347631361243113500153360ustar00rootroot00000000000000--- require: - rubocop-performance AllCops: Exclude: - tmp/**/* DisplayCopNames: true DisplayStyleGuide: true DisabledByDefault: true TargetRubyVersion: 2.4 Layout/AccessModifierIndentation: Enabled: true Layout/ArgumentAlignment: Enabled: true Layout/ArrayAlignment: Enabled: true Layout/HashAlignment: Enabled: true Layout/ParameterAlignment: Enabled: true Layout/BlockAlignment: Enabled: true Layout/BlockEndNewline: Enabled: true Layout/CaseIndentation: Enabled: true Layout/ClosingHeredocIndentation: Enabled: true Layout/ClosingParenthesisIndentation: Enabled: true Layout/CommentIndentation: Enabled: true Layout/ConditionPosition: Enabled: true Layout/DefEndAlignment: Enabled: true Layout/DotPosition: Enabled: true Layout/ElseAlignment: Enabled: true Layout/EmptyComment: Enabled: true Layout/EmptyLineAfterGuardClause: Enabled: true Layout/EmptyLineAfterMagicComment: Enabled: true Layout/EmptyLineBetweenDefs: Enabled: true Layout/EmptyLines: Enabled: true Layout/EmptyLinesAroundAccessModifier: Enabled: true Layout/EmptyLinesAroundArguments: Enabled: true Layout/EmptyLinesAroundBeginBody: Enabled: true Layout/EmptyLinesAroundBlockBody: Enabled: true Layout/EmptyLinesAroundClassBody: Enabled: true Layout/EmptyLinesAroundExceptionHandlingKeywords: Enabled: true Layout/EmptyLinesAroundMethodBody: Enabled: true Layout/EmptyLinesAroundModuleBody: Enabled: true Layout/EndAlignment: Enabled: true Layout/EndOfLine: Enabled: true Layout/ExtraSpacing: AllowForAlignment: false Enabled: true Layout/AssignmentIndentation: Enabled: true Layout/FirstArgumentIndentation: Enabled: true Layout/FirstArrayElementIndentation: Enabled: true Layout/FirstHashElementIndentation: Enabled: true Layout/FirstParameterIndentation: Enabled: true Layout/HeredocIndentation: Enabled: true Layout/IndentationConsistency: Enabled: true Layout/IndentationWidth: Enabled: true Layout/InitialIndentation: Enabled: true Layout/LeadingEmptyLines: Enabled: true Layout/LeadingCommentSpace: Enabled: true Layout/MultilineArrayBraceLayout: Enabled: true Layout/MultilineBlockLayout: Enabled: true Layout/MultilineHashBraceLayout: Enabled: true Layout/MultilineMethodCallBraceLayout: Enabled: true Layout/MultilineMethodCallIndentation: Enabled: true Layout/MultilineMethodDefinitionBraceLayout: Enabled: true Layout/MultilineOperationIndentation: Enabled: true Layout/RescueEnsureAlignment: Enabled: true Layout/SpaceAfterColon: Enabled: true Layout/SpaceAfterComma: Enabled: true Layout/SpaceAfterMethodName: Enabled: true Layout/SpaceAfterNot: Enabled: true Layout/SpaceAfterSemicolon: Enabled: true Layout/SpaceAroundBlockParameters: Enabled: true Layout/SpaceAroundEqualsInParameterDefault: Enabled: true Layout/SpaceAroundKeyword: Enabled: true Layout/SpaceAroundOperators: Enabled: true Layout/SpaceBeforeBlockBraces: Enabled: true Layout/SpaceBeforeComma: Enabled: true Layout/SpaceBeforeComment: Enabled: true Layout/SpaceBeforeFirstArg: Enabled: true Layout/SpaceBeforeSemicolon: Enabled: true Layout/SpaceInLambdaLiteral: Enabled: true Layout/SpaceInsideArrayLiteralBrackets: Enabled: true Layout/SpaceInsideArrayPercentLiteral: Enabled: true Layout/SpaceInsideBlockBraces: Enabled: true Layout/SpaceInsideHashLiteralBraces: Enabled: true Layout/SpaceInsideParens: Enabled: true Layout/SpaceInsidePercentLiteralDelimiters: Enabled: true Layout/SpaceInsideRangeLiteral: Enabled: true Layout/SpaceInsideReferenceBrackets: Enabled: true Layout/SpaceInsideStringInterpolation: Enabled: true Layout/Tab: Enabled: true Layout/TrailingEmptyLines: Enabled: true Layout/TrailingWhitespace: Enabled: true Lint/AmbiguousBlockAssociation: Enabled: true Lint/AmbiguousOperator: Enabled: true Lint/AmbiguousRegexpLiteral: Enabled: true Lint/AssignmentInCondition: Enabled: true Lint/BigDecimalNew: Enabled: true Lint/BooleanSymbol: Enabled: true Lint/CircularArgumentReference: Enabled: true Lint/Debugger: Enabled: true Lint/DeprecatedClassMethods: Enabled: true Lint/DisjunctiveAssignmentInConstructor: Enabled: true Lint/DuplicateCaseCondition: Enabled: true Lint/DuplicateMethods: Enabled: true Lint/DuplicateHashKey: Enabled: true Lint/EachWithObjectArgument: Enabled: true Lint/ElseLayout: Enabled: true Lint/EmptyEnsure: Enabled: true Lint/EmptyExpression: Enabled: true Lint/EmptyInterpolation: Enabled: true Lint/EmptyWhen: Enabled: true Lint/EndInMethod: Enabled: true Lint/EnsureReturn: Enabled: true Lint/ErbNewArguments: Enabled: true Lint/FlipFlop: Enabled: true Lint/FloatOutOfRange: Enabled: true Lint/FormatParameterMismatch: Enabled: true Lint/SuppressedException: Enabled: true Lint/ImplicitStringConcatenation: Enabled: true Lint/IneffectiveAccessModifier: Enabled: true Lint/InheritException: Enabled: true Lint/LiteralAsCondition: Enabled: true Lint/LiteralInInterpolation: Enabled: true Lint/Loop: Enabled: true Lint/MissingCopEnableDirective: Enabled: true Lint/MultipleComparison: Enabled: true Lint/NestedMethodDefinition: Enabled: true Lint/NestedPercentLiteral: Enabled: true Lint/NextWithoutAccumulator: Enabled: true Lint/NonLocalExitFromIterator: Enabled: true Lint/OrderedMagicComments: Enabled: true Lint/ParenthesesAsGroupedExpression: Enabled: true Lint/PercentStringArray: Enabled: true Lint/PercentSymbolArray: Enabled: true Lint/RandOne: Enabled: true Lint/RedundantCopDisableDirective: Enabled: true Lint/RedundantCopEnableDirective: Enabled: true Lint/RedundantRequireStatement: Enabled: true Lint/RedundantSplatExpansion: Enabled: true Lint/RedundantWithIndex: Enabled: true Lint/RedundantWithObject: Enabled: true Lint/RegexpAsCondition: Enabled: true Lint/RequireParentheses: Enabled: true Lint/RescueException: Enabled: true Lint/RescueType: Enabled: true Lint/ReturnInVoidContext: Enabled: true Lint/SafeNavigationChain: Enabled: true Lint/SafeNavigationConsistency: Enabled: true Lint/SafeNavigationWithEmpty: Enabled: true Lint/ScriptPermission: Enabled: true Lint/ShadowedArgument: Enabled: true Lint/ShadowedException: Enabled: true Lint/ShadowingOuterLocalVariable: Enabled: true Lint/RedundantStringCoercion: Enabled: true Lint/Syntax: Enabled: true Lint/ToJSON: Enabled: true Lint/UnderscorePrefixedVariableName: Enabled: true Lint/UnifiedInteger: Enabled: true Lint/UnreachableCode: Enabled: true Lint/UnusedBlockArgument: Enabled: true Lint/UnusedMethodArgument: Enabled: true Lint/UriEscapeUnescape: Enabled: true Lint/UriRegexp: Enabled: true Lint/UselessAccessModifier: Enabled: true Lint/UselessAssignment: Enabled: true Lint/UselessComparison: Enabled: true Lint/UselessElseWithoutRescue: Enabled: true Lint/UselessSetterCall: Enabled: true Lint/Void: Enabled: true Metrics: Enabled: false Naming/AccessorMethodName: Enabled: true Naming/AsciiIdentifiers: Enabled: true Naming/BinaryOperatorParameterName: Enabled: true Naming/ClassAndModuleCamelCase: Enabled: true Naming/ConstantName: Enabled: true Naming/FileName: Enabled: true Exclude: - .simplecov Naming/HeredocDelimiterCase: Enabled: true Naming/HeredocDelimiterNaming: Enabled: true Naming/MemoizedInstanceVariableName: Enabled: true Naming/MethodName: Enabled: true Naming/PredicateName: Enabled: true Naming/RescuedExceptionsVariableName: Enabled: true Naming/BlockParameterName: Enabled: true Naming/MethodParameterName: Enabled: true Naming/VariableName: Enabled: true Naming/VariableNumber: Enabled: true Performance/Caller: Enabled: true Performance/Casecmp: Enabled: true Performance/CompareWithBlock: Enabled: true Performance/Count: Enabled: true Performance/Detect: Enabled: true Performance/DoubleStartEndWith: Enabled: true Performance/EndWith: Enabled: true Performance/FixedSize: Enabled: true Performance/FlatMap: Enabled: true Performance/InefficientHashSearch: Enabled: true Performance/RangeInclude: Enabled: true Performance/RedundantBlockCall: Enabled: true Performance/RedundantMatch: Enabled: true Performance/RedundantMerge: Enabled: true Performance/RegexpMatch: Enabled: true Performance/ReverseEach: Enabled: true Performance/Size: Enabled: true Performance/StartWith: Enabled: true Performance/StringReplacement: Enabled: true Performance/TimesMap: Enabled: true Performance/UnfreezeString: Enabled: true Performance/UriDefaultParser: Enabled: true Style/AccessModifierDeclarations: Enabled: true Style/Alias: Enabled: true Style/AndOr: Enabled: true Style/ArrayJoin: Enabled: true Style/Attr: Enabled: true Style/BarePercentLiterals: Enabled: true Style/BeginBlock: Enabled: true Style/BlockComments: Enabled: true Style/BlockDelimiters: Enabled: true Style/BracesAroundHashParameters: Enabled: true Style/CaseEquality: Enabled: true Style/CharacterLiteral: Enabled: true Style/ClassAndModuleChildren: Enabled: true Style/ClassCheck: Enabled: true Style/ClassMethods: Enabled: true Style/ClassVars: Enabled: true Style/ColonMethodCall: Enabled: true Style/ColonMethodDefinition: Enabled: true Style/CommandLiteral: Enabled: true Style/CommentAnnotation: Enabled: true Style/CommentedKeyword: Enabled: true Style/ConditionalAssignment: Enabled: true Style/DefWithParentheses: Enabled: true Style/Dir: Enabled: true Style/Documentation: Enabled: true Style/DoubleNegation: Enabled: true Style/EachForSimpleLoop: Enabled: true Style/EachWithObject: Enabled: true Style/EmptyBlockParameter: Enabled: true Style/EmptyCaseCondition: Enabled: true Style/EmptyElse: Enabled: true Style/EmptyLambdaParameter: Enabled: true Style/EmptyLiteral: Enabled: true Style/EmptyMethod: Enabled: true EnforcedStyle: expanded Style/Encoding: Enabled: true Style/EndBlock: Enabled: true Style/EvalWithLocation: Enabled: true Style/EvenOdd: Enabled: true Style/ExpandPathArguments: Enabled: true Style/FloatDivision: Enabled: true Style/For: Enabled: true Style/FormatString: Enabled: true Style/FormatStringToken: Enabled: true Style/FrozenStringLiteralComment: Enabled: true Style/GlobalVars: Enabled: true Style/GuardClause: Enabled: true Style/HashSyntax: Enabled: true Style/IdenticalConditionalBranches: Enabled: true Style/IfInsideElse: Enabled: true Style/IfUnlessModifier: Enabled: true Style/IfUnlessModifierOfIfUnless: Enabled: true Style/IfWithSemicolon: Enabled: true Style/InfiniteLoop: Enabled: true Style/InverseMethods: Enabled: true Style/Lambda: Enabled: true Style/LambdaCall: Enabled: true Style/LineEndConcatenation: Enabled: true Style/MethodCallWithoutArgsParentheses: Enabled: true Style/MethodDefParentheses: Enabled: true Style/MethodMissingSuper: Enabled: true Style/MinMax: Enabled: true Style/MissingRespondToMissing: Enabled: true Style/MixinGrouping: Enabled: true Style/MixinUsage: Enabled: true Style/ModuleFunction: Enabled: true EnforcedStyle: extend_self Style/MultilineBlockChain: Enabled: true Style/MultilineIfModifier: Enabled: true Style/MultilineIfThen: Enabled: true Style/MultilineMemoization: Enabled: true Style/MultilineTernaryOperator: Enabled: true Style/MultipleComparison: Enabled: true Style/MutableConstant: Enabled: true Style/NegatedIf: Enabled: true Style/NegatedUnless: Enabled: true Style/NegatedWhile: Enabled: true Style/NestedModifier: Enabled: true Style/NestedParenthesizedCalls: Enabled: true Style/NestedTernaryOperator: Enabled: true Style/Next: Enabled: true Style/NilComparison: Enabled: true Style/NonNilCheck: Enabled: true Style/Not: Enabled: true Style/NumericLiteralPrefix: Enabled: true Style/NumericLiterals: Enabled: true Style/NumericPredicate: Enabled: true Style/OneLineConditional: Enabled: true Style/OptionalArguments: Enabled: true Style/OrAssignment: Enabled: true Style/ParallelAssignment: Enabled: true Style/ParenthesesAroundCondition: Enabled: true Style/PercentLiteralDelimiters: Enabled: true Style/PercentQLiterals: Enabled: true Style/PerlBackrefs: Enabled: true Style/PreferredHashMethods: Enabled: true Style/Proc: Enabled: true Style/RandomWithOffset: Enabled: true Style/RedundantBegin: Enabled: true Style/RedundantCapitalW: Enabled: true Style/RedundantCondition: Enabled: true Style/RedundantConditional: Enabled: true Style/RedundantException: Enabled: true Style/RedundantFreeze: Enabled: true Style/RedundantInterpolation: Enabled: true Style/RedundantParentheses: Enabled: true Style/RedundantReturn: Enabled: true Style/RedundantSelf: Enabled: true Style/RedundantSort: Enabled: true Style/RedundantSortBy: Enabled: true Style/RedundantPercentQ: Enabled: true Style/RegexpLiteral: Enabled: true Style/RescueModifier: Enabled: true Style/RescueStandardError: Enabled: true Style/SafeNavigation: Enabled: true Style/Sample: Enabled: true Style/SelfAssignment: Enabled: true Style/Semicolon: Enabled: true Style/SignalException: Enabled: true Style/SingleLineMethods: Enabled: true Style/SpecialGlobalVars: Enabled: true Style/StabbyLambdaParentheses: Enabled: true Style/StderrPuts: Enabled: true Style/StringLiterals: Enabled: true EnforcedStyle: double_quotes Style/StringLiteralsInInterpolation: Enabled: true Style/Strip: Enabled: true Style/StructInheritance: Enabled: true Style/SymbolArray: Enabled: true Style/SymbolLiteral: Enabled: true Style/SymbolProc: Enabled: true Style/TernaryParentheses: Enabled: true Style/TrailingBodyOnClass: Enabled: true Style/TrailingBodyOnMethodDefinition: Enabled: true Style/TrailingBodyOnModule: Enabled: true Style/TrailingCommaInArguments: Enabled: true Style/TrailingCommaInArrayLiteral: Enabled: true Style/TrailingCommaInHashLiteral: Enabled: true Style/TrailingMethodEndStatement: Enabled: true Style/TrailingUnderscoreVariable: Enabled: true Style/TrivialAccessors: Enabled: true ExactNameMatch: true Style/UnlessElse: Enabled: true Style/UnpackFirst: Enabled: true Style/VariableInterpolation: Enabled: true Style/WhenThen: Enabled: true Style/WhileUntilDo: Enabled: true Style/WhileUntilModifier: Enabled: true Style/WordArray: Enabled: true Style/YodaCondition: Enabled: true Style/ZeroLengthPredicate: Enabled: true byebug-11.1.1/.simplecov000066400000000000000000000002101361243113500150420ustar00rootroot00000000000000# frozen_string_literal: true SimpleCov.command_name ENV["MINITEST_TEST"] || "MiniTest" SimpleCov.add_filter ".bundle" SimpleCov.start byebug-11.1.1/CHANGELOG.md000066400000000000000000001003071361243113500146610ustar00rootroot00000000000000# Changelog ## [Unreleased] ## [11.1.1] - 2020-01-24 ### Fixed * [#635](https://github.com/deivid-rodriguez/byebug/pull/635): usage on Fedora 31 or any other `byebug` installation performed by a `rubygems` copy customized by setting `Gem.install_extension_in_lib` to false. ## [11.1.0] - 2020-01-20 ### Added * Official support for MRI 2.7 ([@yui-knk]). ### Fixed * [#562](https://github.com/deivid-rodriguez/byebug/pull/562): post mortem mode landing in the wrong line when coming from an exception inside a `Kernel.load` call. ### Removed * Support for MRI 2.3. Byebug no longer installs on this platform. ## [11.0.1] - 2019-03-18 ### Fixed * [#546](https://github.com/deivid-rodriguez/byebug/pull/546): `continue!` to ignore further `byebug` calls. * [#545](https://github.com/deivid-rodriguez/byebug/pull/545): `skip` autolisting code for intermediate skipped breakpoints. ## [11.0.0] - 2019-02-15 ### Added * [#377](https://github.com/deivid-rodriguez/byebug/pull/377): `skip` to continue until the next breakpoint as long as it is different from the current one. You can use this command to get out of loops, for example ([@tacnoman]). * [#524](https://github.com/deivid-rodriguez/byebug/pull/524): `continue!` (or `continue unconditionally`) to continue until the end of the program regardless of the currently enabled breakpoints ([@tacnoman]). ### Fixed * [#527](https://github.com/deivid-rodriguez/byebug/pull/527): `break` help text to clarify placeholders from literals. * [#528](https://github.com/deivid-rodriguez/byebug/pull/528): `quit!` help to not show a space between "quit" and "!". ### Removed * Support for MRI 2.2. Byebug no longer installs on this platform. ## [10.0.2] - 2018-03-30 ### Fixed * [#447](https://github.com/deivid-rodriguez/byebug/pull/447): Error when using byebug with `debase` gem ([@tzmfreedom]). ## [10.0.1] - 2018-03-21 ### Fixed * [#443](https://github.com/deivid-rodriguez/byebug/pull/443): Error when using byebug with `debase` gem ([@tzmfreedom]). ## [10.0.0] - 2018-01-26 ### Changed * Breaking on methods now stops on the first effective line of a method, not on the line containing the `def` keyword. ### Added * [#393](https://github.com/deivid-rodriguez/byebug/pull/393): Show valid breakpoint locations when invalid location given ([@ko1]). * [#397](https://github.com/deivid-rodriguez/byebug/pull/397): Ruby 2.5.0 support ([@yui-knk]). * Log host & port when launching byebug's client in remote mode. * [#82](https://github.com/deivid-rodriguez/byebug/issues/82): Some love & tests to remote debugging. * [#141](https://github.com/deivid-rodriguez/byebug/issues/141): `remote_byebug` shortcut to start the most common case for remote debugging. ### Fixed * [#419](https://github.com/deivid-rodriguez/byebug/pull/419): Properly ignore ruby fullpath executable when passed to byebug script. * [#141](https://github.com/deivid-rodriguez/byebug/issues/141): Remote server crash when interrupting client. * [#274](https://github.com/deivid-rodriguez/byebug/issues/274): Remote server crash when interrupting client. * [#239](https://github.com/deivid-rodriguez/byebug/issues/239): Control server thread being able to `interrupt` main thread only a single time. ## [9.1.0] - 2017-08-22 ### Added * Better UI messages for breakpoint management. ### Fixed * `where` command failing on instance_exec block stack frames. * [#321](https://github.com/deivid-rodriguez/byebug/pull/321): `restart` command crashing in certain cases because of a missing `require "English"` ([@akaneko3]). * [#321](https://github.com/deivid-rodriguez/byebug/pull/321): `restart` command crashing when debugged script is not executable or has no shebang ([@akaneko3]). ### Removed * Ruby 2.0 and Ruby 2.1 official & unofficial support. Byebug no longer installs on these platforms. ## [9.0.6] - 2016-09-29 ### Fixed * [#241](https://github.com/deivid-rodriguez/byebug/issues/241): Error when using `byebug` with a ruby compiled against libedit. * [#277](https://github.com/deivid-rodriguez/byebug/pull/277): Allow `Byebug.start_server` to yield the block passed to it when the actual port is already known ([@cben]). * [#275](https://github.com/deivid-rodriguez/byebug/pull/275): Use a standard license name so it can be more reliably used by tools. ## [9.0.5] - 2016-05-28 ### Fixed * Error loading rc file when `ENV["HOME"]` is unset. ## [9.0.4] - 2016-05-19 ### Fixed * Errors in rc file not being displayed to the user. ## [9.0.3] - 2016-05-16 ### Fixed * [#256](https://github.com/deivid-rodriguez/byebug/issues/256): Unfriendly output in byebug's executable when no script specified. * Unfriendly output in byebug's executable when script doesn't exist. * Unfriendly output in byebug's executable when script has invalid code. ## [9.0.2] - 2016-05-15 ### Fixed * [#263](https://github.com/deivid-rodriguez/byebug/pull/263): Skip to get a line in eval context ([@k0kubun]). * [#264](https://github.com/deivid-rodriguez/byebug/pull/264): Debugger getting disabled after `continue` even when linetrace is enabled ([@k0kubun]). ## [9.0.1] - 2016-05-14 ### Fixed * [#201](https://github.com/deivid-rodriguez/byebug/issues/201): `quit` never exiting when remote debugging. ## [9.0.0] - 2016-05-11 ### Fixed * `irb` command unintentionally changing $PROGRAM_NAME. * `pry` command failing. * Unrelated error message when using `pry` command and Pry not installed. * [#239](https://github.com/deivid-rodriguez/byebug/issues/239): Interrupting program execution from remote control interface ([@izaera]). ### Removed * Official Ruby 2.0.0 support. `var local` no longer works in Ruby 2.0. The rest of the commands should still work as before, but `byebug` is no longer tested against this version so they might start breaking in the future. ## [8.2.5] - 2016-04-27 ### Fixed * [#244](https://github.com/deivid-rodriguez/byebug/pull/244): Allows paths with spaces ([@HookyQR]). * [#244](https://github.com/deivid-rodriguez/byebug/pull/244): Allows paths with colons ([@HookyQR]). ## [8.2.4] - 2016-04-08 ### Fixed * Reverts [#211](https://github.com/deivid-rodriguez/byebug/pull/211) which leads to an unusable debugger. ## [8.2.3] - 2016-04-07 ### Fixed * Better interaction with utilities like RSpec when hitting Ctrl-C. * [#197](https://github.com/deivid-rodriguez/byebug/issues/197): `irb` command when original program modified ARGV ([@josephks]). * [#211](https://github.com/deivid-rodriguez/byebug/pull/211): Unusable debugger when stdin redirected ([@sethk]). * [#223](https://github.com/deivid-rodriguez/byebug/issues/223): RC file loading when no explicit flag included. * [#175](https://github.com/deivid-rodriguez/byebug/issues/175): Installation on some Windows systems. * [#226](https://github.com/deivid-rodriguez/byebug/issues/226): Installation on some Windows systems. ## [8.2.2] - 2016-02-01 ### Fixed * Bug in rc file loading where most initialization commands would not be run. ## [8.2.1] - 2015-11-26 ### Fixed * Bug in evaluations using "eval". ## [8.2.0] - 2015-11-12 ### Fixed * [#184](https://github.com/deivid-rodriguez/byebug/issues/184): Due to the way of running evaluations in a separate thread. * [#188](https://github.com/deivid-rodriguez/byebug/issues/188): Due to the way of running evaluations in a separate thread. ### Added * `debug` command to evaluate things in a separate thread, since this behavior was removed from default `eval` to fix the above issues. ## [8.1.0] - 2015-11-09 ### Fixed * Command history should be specific per project. * Better error message in certain edge cases when printing the backtrace. * Bug in evaluator which would show information about having stopped at a breakpoint in some cases. ### Added * Ability to autolist source code after `frame` command. * Ability to stop at lines where methods return. ## [8.0.1] - 2015-11-07 ### Fixed * Error stream wouldn't be properly reset when using standalone `byebug`. * Confusing error message for invalid breakpoint locations. ## [8.0.0] - 2015-11-05 ### Fixed * [#183](https://github.com/deivid-rodriguez/byebug/issues/183). Compilation in Ruby 2.0. Regression introduced in [7.0.0]. * "Return value is: nil" would be displayed when stopping right before the end of a class definition. We want to avoid showing anything instead. ### Changed * Plugins now need to implement an `at_end` method (separate from `at_return`) in their custom processors. ## [7.0.0] - 2015-11-04 ### Fixed * [#177](https://github.com/deivid-rodriguez/byebug/issues/177). Some issues with formatting results of evaluations. * [#144](https://github.com/deivid-rodriguez/byebug/issues/144). Ruby process after using byebug does no longer get slow. * [#121](https://github.com/deivid-rodriguez/byebug/issues/121). `byebug` commands inside code evaluated from debugger's prompt are now properly working. * Another evaluation bug in autocommands. * `finish 0` command would sometimes fail to stop right before exiting the current frame. * Runner's `--[no-]stop` option now works ([@windwiny]). * Change variable name `bool`, avoid conflict clang's predefined macro. ### Removed * `ps` command. ### Changed * [#166](https://github.com/deivid-rodriguez/byebug/issues/166). Don't load the entire library on require, but only when a `byebug` call is issued ([@bquorning]). * The above fix to the `finish 0` command cause `byebug`'s entrypoint to require 3 steps out instead of 2. In general, plugins using `Byebug::Context.step_out` will need to be changed to consider "c return events" as well. ### Added * `autopry` setting that calls `pry` on every stop. * Return value information to debugger's output when `finish 0` is used. ## [6.0.2] - 2015-08-20 ### Fixed * The user should always be given back a prompt unless (s)he explicitly states the opposite. This provides a more general fix to the bug resolved in [6.0.1]. ## [6.0.1] - 2015-08-19 ### Fixed * Bug in evaluation where the user would lose the command prompt when entering an expression with a syntax error. ## [6.0.0] - 2015-08-17 ### Removed * `autoeval` setting. I haven't heard of anyone setting it to false. * `pp`, `putl`, `eval`. People just want to evaluate Ruby code, so the less magic the better. Most of the people probably were not aware that `byebug` was overriding stuff like `pp` or `eval`. Only keeping `ps` as the single "enhanced evaluation" command. * `verbose` setting. * `info catch` command. Use `catch` without arguments instead. * `R` command alias for `restart`. ### Changed * `info args` is now `var args`. * `interrupt` is now aliased to `int`, not to `i`. * API to define custom commands and subcommands (see the Command class). ### Fixed * [#140](https://github.com/deivid-rodriguez/byebug/issues/140). `help` command not showing the list of available commands and their descriptions. * [#147](https://github.com/deivid-rodriguez/byebug/issues/147). Setting breakpoints at symlinked files. ### Added * API to define custom command processors (see the CommandProcessor class). ## [5.0.0] - 2015-05-18 ### Fixed * [#136](https://github.com/deivid-rodriguez/byebug/issues/136). `frame` command not working with negative numbers ([@ark6]). ### Added * IDE support and a new command/subcommand API for plugins. * Add a "savefile" setting holding the file where "save" command saves current debugger's state. ### Changed * `disable` no longer disable all breakpoints, it just shows command's help instead. To disable all breakpoints now you need to do `disable breakpoints` (or `dis b`). Similarly, you can't no longer use `dis 1 2 3` but need to do `dis b 1 2 3` to disable specific breakpoints. The same applies to the `enable` command. ### Removed * `help set ` no longer works. `help set` includes that same output and it's not verbose enough so that this is a problem. Same with `help show `. ## [4.0.5] - 2015-04-02 ### Fixed * [#131](https://github.com/deivid-rodriguez/byebug/issues/131). * Thread commands help format should be consistent with the rest of the help system now. ## [4.0.4] - 2015-03-27 ### Fixed * [#127](https://github.com/deivid-rodriguez/byebug/issues/127). ## [4.0.3] - 2015-03-19 ### Fixed * Unused variable warning in `context.c`. ## [4.0.2] - 2015-03-16 ### Fixed * [#118](https://github.com/deivid-rodriguez/byebug/issues/118). Remove `rb-readline` as a dependency and show a help message whenever requiring `readline` fails instead. ## [4.0.1] - 2015-03-13 ### Fixed * .yml files needed for printers support were missing from the release... :S * [#118](https://github.com/deivid-rodriguez/byebug/issues/118). Add `readline` as a dependency. ## [4.0.0] - 2015-03-13 ### Added * `untracevar` command that stops tracing a global variable. * Window CI build through AppVeyor. * OSX CI build through Travis. * Style enforcement through RuboCop. * C style enforment using the `indent` command line utility. * Some remote debugging tests ([@eric-hu]). * Printer's support ([@astashov]). ### Changed * A lot of internal refactoring. * `tracevar` now requires the full global variable name (with "$"). * [#92](https://github.com/deivid-rodriguez/byebug/issues/92). The `catch` command is not allowed in post_mortem mode anymore. It was not working anyways. * [#85](https://github.com/deivid-rodriguez/byebug/issues/85). `step` is now more user friendly when used in combination with `up`. * `var const` can now be called without an argument and will show constants in the current scope. * `break` with a class name now creates breakpoints regardless of class not being yet defined. If that's the case, it gives a warning but the class is created anyways. ### Fixed * Code reloading issues. * `set fullpath` was not showing fullpaths. Now it is. * [#93](https://github.com/deivid-rodriguez/byebug/issues/93): `up`, `down` and `frame` commands now work in post_mortem mode. * rc file (`.byebugrc`) loading: invalid commands are just ignored instead of aborting, global (home) rc file is now properly loaded before project's file. * [#93](https://github.com/deivid-rodriguez/byebug/issues/93). Backtraces not working in `post_mortem` mode. * 'cmd1 ; cmd2 ; ...; cmdN' syntax which allows running several commands sequentially. * [#101](https://github.com/deivid-rodriguez/byebug/issues/101). `finish` command not stopping at the correct line. * [#106](https://github.com/deivid-rodriguez/byebug/issues/106). `break` with namespaced class, like `break A::B#c` should now work. * Command history is now persisted before exiting byebug. * Setting breakpoint in a method would stop not only at the beginning of the method but also at the beginning of every block inside the method. * [#122](https://github.com/deivid-rodriguez/byebug/issues/122). Setting breakpoints on module methods ([@x-yuri]). ### Removed * `autoreload` setting as it's not necessary anymore. Code should always be up to date. * `reload` command for the same reason. * Gem dependency on `debugger-linecache`. * `step+`, `step-`, `next+`, `next-`, `set/show linetrace_plus` and `set/show forcestep` commands. These were all mechanisms to deal with TracePoint API event dupplication, but this duplicated events have been completely removed from the API since [r48609]( bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/48609), so they are no longer necessary. * `info file` subcommands: `info file breakpoints`, `info file mtime`, `info file sha1`, `info file all`. Now all information is listed under `info file`. * `testing` setting. It was just a hack to be able to test `byebug`. Nobody was supposed to actually use it! * `var class` command, just use Ruby (`self.class.class_variables`). * `p` command, just use `eval`, or just type your expression and `byebug` will autoevaluate it. * `exit` alias for `quit`. ## [3.5.1] - 2014-09-29 ### Fixed * [#79](https://github.com/deivid-rodriguez/byebug/issues/79). Windows installation. * `condition` command not properly detecting invalid breakpoint ids. ## [3.5.0] - 2014-09-28 ### Fixed * [#81](https://github.com/deivid-rodriguez/byebug/issues/81). Byebug's history messing up other programs using Readline. * Readline's history not being properly saved and inmediately available. * User not being notified when trying to debug a non existent script. ### Changed * Complete rewrite of byebug's history. * Complete rewrite of list command. * Docs about stacktrace related commands (`up`, `down`, `frame`, `backtrace`). ## [3.4.2] - 2014-09-26 ### Fixed * [#67](https://github.com/deivid-rodriguez/byebug/issues/67). Debugging commands invoked by ruby executable, as in `byebug -- ruby -Itest a_test.rb -n test_something`. ## [3.4.1] - 2014-09-25 ### Fixed * [#54](https://github.com/deivid-rodriguez/byebug/issues/54). Use of threads inside `eval` command. * `list` command not listing backwards after reaching the end of the file. ## [3.4.0] - 2014-09-01 ### Fixed * deivid-rodriguez/pry-byebug#32 in a better way. ## [3.3.0] - 2014-08-28 ### Fixed * `set verbose` command. * `set post_mortem false` command. * Debugger stopping in `byebug`'s internal frames in some cases. * `backtrace` crashing when `fullpath` setting disabled and calculated stack size being smaller than the real one. ### Changed * The `-t` option for `bin/byebug` now turns tracing on whereas the `-x` option tells byebug to run the initialization file (.byebugrc) on startup. This is the default behaviour though. * `bin/byebug` libified and tests added. ### Removed * `info locals` command. Use `var local` instead. * `info instance_variables` command. Use `var instance` instead. * `info global_variables` command. Use `var global` instead. * `info variables` command. Use `var all` instead. * `irb` command stepping capabilities, see [8e226d0](https://github.com/deivid-rodriguez/byebug/commit/8e226d0). * `script` and `restart-script` options for `bin/byebug`. ## [3.2.0] - 2014-08-02 ### Fixed * [#71](https://github.com/deivid-rodriguez/byebug/issues/71). Remote debugging ([@shuky19]). * [#69](https://github.com/deivid-rodriguez/byebug/issues/69). `source` command ([@Olgagr]). ### Removed * `post_mortem` activation through `Byebug.post_mortem`. Use `set post_mortem` instead. * `info stack` command. Use `where` instead. * `method iv` command. Use `var instance` instead. * [#77](https://github.com/deivid-rodriguez/byebug/issues/77). Warning. ## [3.1.2] - 2014-04-23 ### Fixed * `post_mortem` mode in `bin/byebug` (really). * Line tracing in `bin/byebug`. ## [3.1.1] - 2014-04-23 ### Fixed * `post_mortem` mode in bin/byebug. ## [3.1.0] - 2014-04-23 ### Removed * `show commands` command. Use `history` instead. * Byebug.start accepting options. Any program settings you want applied from the start should be set in `.byebugrc`. * `trace` command. Use `set linetrace` for line tracing and `tracevar` for global variable tracing. * `show version` command. Use `byebug --version` to check byebug's version. * `set arg` setting. Use the `restart` command instead. ### Changed * `linetrace_plus` setting renamed to `tracing_plus`. ### Added * `history` command to check byebug's history of previous commands. ## [3.0.0] - 2014-04-17 ### Fixed * Plain `byebug` not working when `pry-byebug` installed. * `post_mortem` mode. * Command history not being saved after regular program termination. * [#54](https://github.com/deivid-rodriguez/byebug/issues/54). (Again) calling `Byebug.start` with `Timeout.timeout` ([@zmoazeni]). ### Added * Allow disabling `post_mortem` mode. ### Changed * `show commands` command for listing history of previous commands now behaves like shell's `history` command. * `show/set history filename` is now `show/set histfile`. * `show/set history size` is now `show/set histsize`. * `show/set history save` is now `show/set autosave`. * `finish` semantics, see [61f9b4d](https://github.com/deivid-rodriguez/byebug/commit/61f9b4d). * Use per project history file by default. ### Removed * The `init` option for `Byebug.start`. Information to make the `restart` command work is always saved now. ## [2.7.0] - 2014-02-24 ### Fixed * [#52](https://github.com/deivid-rodriguez/byebug/issues/52). `IGNORED_FILES` slowing down startup. * [#53](https://github.com/deivid-rodriguez/byebug/issues/53). Calling `Byebug.start` with `Timeout.timeout`. * [#54](https://github.com/deivid-rodriguez/byebug/issues/54). Calling `Byebug.start` with `Timeout.timeout`. ## [2.6.0] - 2014-02-08 ### Fixed * Circular dependency affecting `pry-byebug` ([@andreychernih]). ## [2.5.0] - 2013-12-14 ### Added * Support for `sublime-debugger`. ## [2.4.1] - 2013-12-05 ### Fixed * [#40](https://github.com/deivid-rodriguez/byebug/issues/40). Installation error in Mac OSX ([@luislavena]). ## [2.4.0] - 2013-12-02 ### Fixed * `thread list` showing too many threads. * Fix setting post mortem mode with `set post_mortem`. Now this is the only post mortem functionality available as specifying `Byebug.post_mortem` with a block has been removed in this version. ### Added * (Again) `debugger` as an alias to `byebug` ([@wallace]). * `-R` option for `bin/byebug` to specify server's hostname:port for remote debugging ([@mrkn]). ### Changed * Use `require` instead of `require_relative` for loading byebug's extension library ([@nobu]). * `trace variable $foo` should be now `trace variable $foo`. ## [2.3.1] - 2013-10-17 ### Fixed * Breakpoint removal. * Broken test suite. ## [2.3.0] - 2013-10-09 ### Added * Compatibility with Phusion Passenger Enterprise ([@FooBarWidget]). ### Changed * More minimalist help system. ## [2.2.2] - 2013-09-25 ### Fixed * Compilation issue in 64 bit systems. ## [2.2.1] - 2013-09-24 ### Fixed * [#26](https://github.com/deivid-rodriguez/byebug/issues/26). Compilation issue introduced in [2.2.0]. ### Changed * `show/set stack_trace_on_error` is now `show/set stack_on_error`. ## [2.2.0] - 2013-09-22 ### Fixed * Stack size calculations. * Setting `post_mortem` mode. ### Added * `verbose` setting for TracePoint API event inspection. ### Changed * Warning free byebug. * Allow `edit ` without a line number. ## [2.1.1] - 2013-09-10 ### Fixed * Debugging code inside `-e` Ruby flag. ## [2.1.0] - 2013-09-08 ### Fixed * Remote debugging display. * `eval` crashing when inspecting raised an exception (reported by [@iblue]). ### Changed * `enable breakpoints` now enables every breakpoint. * `disable breakpoints` now disables every breakpoint. ## [2.0.0] - 2013-08-30 ### Added * "Official" definition of a command API. * Thread support. ### Removed * `jump` command. It had never worked. ### Changed * Several internal refactorings. ## [1.8.2] - 2013-08-16 ### Fixed * `save` command now saves the list of `displays`. * Stack size calculation. ### Changed * More user friendly regexps for commands. * Better help for some commands. ## [1.8.1] - 2013-08-12 ### Fixed * Major regression introduced in [1.8.0]. ## [1.8.0] - 2013-08-12 ### Added * Remote debugging support. ## [1.7.0] - 2013-08-03 ### Added * List command automatically called after callstack navigation commands. * C-frames specifically marked in the callstack. * C-frames skipped when navigating the callstack. ## [1.6.1] - 2013-07-10 ### Fixed * Windows compatibiliy: compilation and terminal width issues. ## [1.6.0] - 2013-07-10 ### Fixed * `byebug` placed at the end of a block or method call not working as expected. * `autolist` being applied when Ruby `-e` option used. ### Changed * Backtrace callstyles. Use `long` for detailed frames in callstack and `short` for more concise frames. ## [1.5.0] - 2013-06-21 ### Fixed * Incomplete backtraces when the debugger was not started at program startup. ## [1.4.2] - 2013-06-20 ### Fixed * `help command subcommand` command. * Interaction with Rails Console debugging flag. * `post_mortem` mode when running byebug from the outset. * `no-quit` flag when running byebug from the outset. ## [1.4.1] - 2013-06-15 ### Fixed * Crash when printing some filenames in backtraces. * Allow byebug developers to easily use compilers different from gcc ([@GarthSnyder]). ## [1.4.0] - 2013-06-05 ### Fixed * Memory leaks causing `byebug` to randomly crash. ### Changed * Use the Debug Inspector API for backtrace information. ## [1.3.1] - 2013-06-02 ### Fixed * Interaction with Rails debugging flag. * Crash when trying to print lines of code containing the character '%'. * `basename` and `linetrace` options not working together. ## [1.3.0] - 2013-05-25 ### Added * Support colon-delimited include paths in command-line front-end ([@ender672]). ## [1.2.0] - 2013-05-20 ### Fixed * Ctrl+C during command line editing (works like pry/irb). ### Added * `pry` command. ## [1.1.1] - 2013-05-07 ### Added * `pry-byebug` compatibility. ### Changed * Better help system. * Code cleanup. ## [1.1.0] - 2013-04-30 ### Added * Post Mortem support. ## [1.0.3] - 2013-04-23 ### Fixed * Negative line numbers shown by list command at the beginning of file. * `backtrace` command segfaulting when trying to show info on some frame args. Don't know the reason yet, but the exception is handled now and command does not segfault anymore. ### Changed * `autoreload` is set by default now. * Try some thread support (not even close to usable). ## [1.0.2] - 2013-04-09 ### Fixed * backtraces messed up when using both `next`/`step` and backtrace navigation commands. ### Changed * `autolist` and `autoeval` are default settings now. ## [1.0.1] - 2013-04-06 ### Fixed * Byebug not loading properly. ## [1.0.0] - 2013-03-29 ### Fixed * Green test suite. ## 0.0.1 - 2013-03-18 ### Added * Initial release. [Unreleased]: https://github.com/deivid-rodriguez/byebug/compare/v10.0.2...HEAD [11.1.1]: https://github.com/deivid-rodriguez/byebug/compare/v11.1.0...v11.1.1 [11.1.0]: https://github.com/deivid-rodriguez/byebug/compare/v11.0.1...v11.1.0 [11.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v11.0.0...v11.0.1 [11.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v10.0.2...v11.0.0 [10.0.2]: https://github.com/deivid-rodriguez/byebug/compare/v10.0.1...v10.0.2 [10.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v10.0.0...v10.0.1 [10.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v9.1.0...v10.0.0 [9.1.0]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.6...v9.1.0 [9.0.6]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.5...v9.0.6 [9.0.5]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.4...v9.0.5 [9.0.4]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.3...v9.0.4 [9.0.3]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.2...v9.0.3 [9.0.2]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.1...v9.0.2 [9.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v9.0.0...v9.0.1 [9.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v8.2.5...v9.0.0 [8.2.5]: https://github.com/deivid-rodriguez/byebug/compare/v8.2.4...v8.2.5 [8.2.4]: https://github.com/deivid-rodriguez/byebug/compare/v8.2.3...v8.2.4 [8.2.3]: https://github.com/deivid-rodriguez/byebug/compare/v8.2.2...v8.2.3 [8.2.2]: https://github.com/deivid-rodriguez/byebug/compare/v8.2.1...v8.2.2 [8.2.1]: https://github.com/deivid-rodriguez/byebug/compare/v8.2.0...v8.2.1 [8.2.0]: https://github.com/deivid-rodriguez/byebug/compare/v8.1.0...v8.2.0 [8.1.0]: https://github.com/deivid-rodriguez/byebug/compare/v8.0.1...v8.1.0 [8.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v8.0.0...v8.0.1 [8.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v7.0.0...v8.0.0 [7.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v6.0.2...v7.0.0 [6.0.2]: https://github.com/deivid-rodriguez/byebug/compare/v6.0.1...v6.0.2 [6.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v6.0.0...v6.0.1 [6.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v5.0.0...v6.0.0 [5.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v4.0.5...v5.0.0 [4.0.5]: https://github.com/deivid-rodriguez/byebug/compare/v4.0.4...v4.0.5 [4.0.4]: https://github.com/deivid-rodriguez/byebug/compare/v4.0.3...v4.0.4 [4.0.3]: https://github.com/deivid-rodriguez/byebug/compare/v4.0.2...v4.0.3 [4.0.2]: https://github.com/deivid-rodriguez/byebug/compare/v4.0.1...v4.0.2 [4.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v4.0.0...v4.0.1 [4.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v3.5.1...v4.0.0 [3.5.1]: https://github.com/deivid-rodriguez/byebug/compare/v3.5.0...v3.5.1 [3.5.0]: https://github.com/deivid-rodriguez/byebug/compare/v3.4.2...v3.5.0 [3.4.2]: https://github.com/deivid-rodriguez/byebug/compare/v3.4.1...v3.4.2 [3.4.1]: https://github.com/deivid-rodriguez/byebug/compare/v3.4.0...v3.4.1 [3.4.0]: https://github.com/deivid-rodriguez/byebug/compare/v3.3.0...v3.4.0 [3.3.0]: https://github.com/deivid-rodriguez/byebug/compare/v3.2.0...v3.3.0 [3.2.0]: https://github.com/deivid-rodriguez/byebug/compare/v3.1.2...v3.2.0 [3.1.2]: https://github.com/deivid-rodriguez/byebug/compare/v3.1.1...v3.1.2 [3.1.1]: https://github.com/deivid-rodriguez/byebug/compare/v3.1.0...v3.1.1 [3.1.0]: https://github.com/deivid-rodriguez/byebug/compare/v3.0.0...v3.1.0 [3.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.7.0...v3.0.0 [2.7.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.6.0...v2.7.0 [2.6.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.5.0...v2.6.0 [2.5.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.4.1...v2.5.0 [2.4.1]: https://github.com/deivid-rodriguez/byebug/compare/v2.4.0...v2.4.1 [2.4.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.3.1...v2.4.0 [2.3.1]: https://github.com/deivid-rodriguez/byebug/compare/v2.3.0...v2.3.1 [2.3.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.2.2...v2.3.0 [2.2.2]: https://github.com/deivid-rodriguez/byebug/compare/v2.2.1...v2.2.2 [2.2.1]: https://github.com/deivid-rodriguez/byebug/compare/v2.2.0...v2.2.1 [2.2.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.1.1...v2.2.0 [2.1.1]: https://github.com/deivid-rodriguez/byebug/compare/v2.1.0...v2.1.1 [2.1.0]: https://github.com/deivid-rodriguez/byebug/compare/v2.0.0...v2.1.0 [2.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.8.2...v2.0.0 [1.8.2]: https://github.com/deivid-rodriguez/byebug/compare/v1.8.1...v1.8.2 [1.8.1]: https://github.com/deivid-rodriguez/byebug/compare/v1.8.0...v1.8.1 [1.8.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.7.0...v1.8.0 [1.7.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.6.1...v1.7.0 [1.6.1]: https://github.com/deivid-rodriguez/byebug/compare/v1.6.0...v1.6.1 [1.6.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.5.0...v1.6.0 [1.5.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.4.2...v1.5.0 [1.4.2]: https://github.com/deivid-rodriguez/byebug/compare/v1.4.1...v1.4.2 [1.4.1]: https://github.com/deivid-rodriguez/byebug/compare/v1.4.0...v1.4.1 [1.4.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.3.1...v1.4.0 [1.3.1]: https://github.com/deivid-rodriguez/byebug/compare/v1.3.0...v1.3.1 [1.3.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.2.0...v1.3.0 [1.2.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.1.1...v1.2.0 [1.1.1]: https://github.com/deivid-rodriguez/byebug/compare/v1.1.0...v1.1.1 [1.1.0]: https://github.com/deivid-rodriguez/byebug/compare/v1.0.3...v1.1.0 [1.0.3]: https://github.com/deivid-rodriguez/byebug/compare/v1.0.2...v1.0.3 [1.0.2]: https://github.com/deivid-rodriguez/byebug/compare/v1.0.1...v1.0.2 [1.0.1]: https://github.com/deivid-rodriguez/byebug/compare/v1.0.0...v1.0.1 [1.0.0]: https://github.com/deivid-rodriguez/byebug/compare/v0.0.1...v1.0.0 [@akaneko3]: https://github.com/akaneko3 [@andreychernih]: https://github.com/andreychernih [@ark6]: https://github.com/ark6 [@astashov]: https://github.com/astashov [@bquorning]: https://github.com/bquorning [@cben]: https://github.com/cben [@ender672]: https://github.com/ender672 [@eric-hu]: https://github.com/eric-hu [@FooBarWidget]: https://github.com/FooBarWidget [@GarthSnyder]: https://github.com/GarthSnyder [@HookyQR]: https://github.com/HookyQR [@iblue]: https://github.com/iblue [@izaera]: https://github.com/izaera [@josephks]: https://github.com/josephks [@k0kubun]: https://github.com/k0kubun [@ko1]: https://github.com/ko1 [@luislavena]: https://github.com/luislavena [@mrkn]: https://github.com/mrkn [@nobu]: https://github.com/nobu [@Olgagr]: https://github.com/Olgagr [@sethk]: https://github.com/sethk [@shuky19]: https://github.com/shuky19 [@tacnoman]: https://github.com/tacnoman [@tzmfreedom]: https://github.com/tzmfreedom [@wallace]: https://github.com/wallace [@windwiny]: https://github.com/windwiny [@x-yuri]: https://github.com/x-yuri [@yui-knk]: https://github.com/yui-knk [@zmoazeni]: https://github.com/zmoazeni byebug-11.1.1/CONTRIBUTING.md000066400000000000000000000051531361243113500153040ustar00rootroot00000000000000# CONTRIBUTING Please note that this project is released with a [Contributor Code of Conduct](code_of_conduct.md). By participating in this project you agree to abide by its terms. ## Bug Reports * Try to reproduce the issue against the latest revision. There might be unrealeased work that fixes your problem! * Ensure that your issue has not already been reported. * Include the steps you carried out to produce the problem. If we can't reproduce it, we can't fix it. * Include the behavior you observed along with the behavior you expected, and why you expected it. ## Development dependencies * `Byebug` depends on Ruby's TracePoint API provided by `ruby-core`. This is a young API and a lot of bugs have been recently corrected, so make sure you always have the lastest patch level release installed. * The recommended tool to manage development dependencies is `bundler`. Run `gem install bundler` to install it. * Running `bundle install` inside a local clone of `byebug` will get development dependencies installed. ## Running the test suite * Make sure you compile the C-extension using `bin/rake compile`. Otherwise you won't be able to use `byebug`. * Run the test suite using the default rake task (`bin/rake`). This task is composed of 3 subtasks: `bin/rake compile`, `bin/rake test` & `bin/rake lint`. * If you want to run specific tests, use the provided test runner, like so: * Specific test files. For example, `bin/minitest test/commands/break_test.rb` * Specific test classes. For example, `bin/minitest BreakAtLinesTest` * Specific tests. For example, `bin/minitest test_catch_removes_specific_catchpoint` * Specific fully qualified tests. For example, `bin/minitest BreakAtLinesTest#test_setting_breakpoint_sets_correct_fields` * You can combine any of them and you will get the union of all filters. For example: `bin/minitest BreakAtLinesTest test_catch_removes_specific_catchpoint` ## Code style * Byebug uses several style checks to check code style consistent. You can run those using `bin/rake lint`. ## Byebug as a C-extension Byebug is a gem developed as a C-extension. The debugger internal's functionality is implemented in C (the interaction with the TracePoint API). The rest of the gem is implemented in Ruby. Normally you won't need to touch the C-extension, but it will obviously depended on the bug you're trying to fix or the feature you are willing to add. You can learn more about C-extensions [here](https://tenderlovemaking.com/2009/12/18/writing-ruby-c-extensions-part-1.html) or [here](https://tenderlovemaking.com/2010/12/11/writing-ruby-c-extensions-part-2.html). byebug-11.1.1/GUIDE.md000066400000000000000000001562261361243113500142420ustar00rootroot00000000000000# GUIDE ## Introduction ### First Steps A handful of commands are enough to get started using `byebug`. The following session illustrates these commands. Take the following sample file: ```ruby # # The n'th triangle number: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n # def triangle(n) tri = 0 0.upto(n) { |i| tri += i } tri end t = triangle(3) puts t ``` Let's debug it. ```bash $ byebug /path/to/triangle.rb [1, 10] in /path/to/triangle.rb 1: # 2: # The n'th triangle number: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n 3: # => 4: def triangle(n) 5: tri = 0 6: 7: 0.upto(n) { |i| tri += i } 8: 9: tri 10: end (byebug) ``` We are currently stopped before the first executable line of the program: line 4 of `triangle.rb`. If you are used to less dynamic languages and have used debuggers for more statically compiled languages like C, C++, or Java, it may seem odd to be stopped before a function definition but in Ruby line 4 is executed. Byebug's prompt is `(byebug)`. If the program has died and you are in post-mortem debugging, `(byebug:post-mortem)` is used instead. If the program has terminated normally and the `--no-quit` option has been specified in the command line, the prompt will be `(byebug:ctrl)` instead. The commands available change depending on the program's state. Byebug automatically lists 10 lines of code centered around the current line every time it is stopped. The current line is marked with `=>`. If the range would overflow the beggining or the end of the file, byebug will move it accordingly so that only actual real lines of code are displayed. Now let us step through the program. ```bash (byebug) step [5, 14] in /path/to/triangle.rb 5: tri = 0 6: 7: 0.upto(n) { |i| tri += i } 8: 9: tri 10: end 11: => 12: t = triangle(3) 13: puts t (byebug) # hit enter [1, 10] in /path/to/triangle.rb 1: # 2: # The n'th triangle number: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n 3: # 4: def triangle(n) => 5: tri = 0 6: 7: 0.upto(n) { |i| tri += i } 8: 9: tri 10: end (byebug) eval tri nil (byebug) step [2, 11] in /path/to/triangle.rb 2: # The n'th triangle number: triangle(n) = n*(n+1)/2 = 1 + 2 + ... + n 3: # 4: def triangle(n) 5: tri = 0 6: => 7: 0.upto(n) { |i| tri += i } 8: 9: tri 10: end 11: (byebug) eval tri 0 ``` The first `step` command runs the script one executable unit. The second command we entered was just hitting the return key: `byebug` remembers the last command you entered was `step` and runs it again. One way to print the values of variables is `eval` (there are other ways). When we look at the value of `tri` the first time, we see it is `nil`. Again we are stopped _before_ the assignment on line 5, and this variable hadn't been set previously. However after issuing another `step` command we see that the value is 0 as expected. If every time we stop we want to see the value of `tri` to see how things are going, there is a better way by setting a display expression: ```bash (byebug) display tri 1: tri = 0 ``` Now let us run the program until right before we return from the function. We'll want to see which lines get run, so we turn on _line tracing_. If we don't want whole paths to be displayed when tracing, we can turn on _basename_. ```bash (byebug) set linetrace linetrace is on (byebug) set basename basename is on (byebug) finish 0 Tracing: triangle.rb:7 0.upto(n) { |i| tri += i } 1: tri = 0 Tracing: triangle.rb:7 0.upto(n) { |i| tri += i } 1: tri = 0 Tracing: triangle.rb:7 0.upto(n) { |i| tri += i } 1: tri = 1 Tracing: triangle.rb:7 0.upto(n) { |i| tri += i } 1: tri = 3 Tracing: triangle.rb:9 tri 1: tri = 6 1: tri = 6 [4, 13] in /home/davidr/Proyectos/byebug/triangle.rb 4: def triangle(n) 5: tri = 0 6: 7: 0.upto(n) { |i| tri += i } 8: 9: tri => 10: end 11: 12: t = triangle(3) 13: puts t (byebug) quit Really quit? (y/n) y ``` So far, so good. As you can see from the above, to get out of `byebug`, one can issue a `quit` command (or the abbreviation `q`). If you want to quit without being prompted, suffix the command with an exclamation mark, e.g., `q!`. ### Second Sample Session: Delving Deeper In this section we'll introduce breakpoints, the call stack and restarting. Below we will debug a simple Ruby program to solve the classic Towers of Hanoi puzzle. It is augmented by the bane of programming: some command-parameter processing with error checking. ```ruby # # Solves the classic Towers of Hanoi puzzle. # def hanoi(n, a, b, c) hanoi(n - 1, a, c, b) if n - 1 > 0 puts "Move disk #{a} to #{b}" hanoi(n - 1, c, b, a) if n - 1 > 0 end n_args = $ARGV.length raise("*** Need number of disks or no parameter") if n_args > 1 n = 3 if n_args > 0 begin n = $ARGV[0].to_i rescue ValueError raise("*** Expecting an integer, got: #{$ARGV[0]}") end end raise("*** Number of disks should be between 1 and 100") if n < 1 || n > 100 hanoi(n, :a, :b, :c) ``` Recall in the first section it was stated that before the `def` is run, the method it names is undefined. Let's check that out. First let's see what private methods we can call before running `def hanoi`. ```bash $ byebug path/to/hanoi.rb 1: # 2: # Solves the classic Towers of Hanoi puzzle. 3: # 4: def hanoi(n, a, b, c) 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end (byebug) private_methods public private include using define_method default_src_encoding DelegateClass Digest timeout initialize_copy initialize_dup initialize_clone sprintf format Integer Float String Array Hash warn raise fail global_variables __method__ __callee__ __dir__ eval local_variables iterator? block_given? catch throw loop respond_to_missing? trace_var untrace_var at_exit syscall open printf print putc puts gets readline select readlines ` p test srand rand trap load require require_relative autoload autoload? proc lambda binding caller caller_locations exec fork exit! system spawn sleep exit abort Rational Complex set_trace_func gem_original_require Pathname pp y URI rubygems_require initialize singleton_method_added singleton_method_removed singleton_method_undefined method_missing (byebug) private_methods.member?(:hanoi) false ``` `private_methods` is not a byebug command but a Ruby feature. By default, when `byebug` doesn't understand a command, it will evaluate it as if it was a Ruby command. You can use any Ruby to inspect your program's state at the place it is stopped. Now let's see what happens after stepping: ```bash (byebug) step [5, 14] in /path/to/hanoi.rb 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end 11: => 12: n_args = $ARGV.length 13: 14: raise("*** Need number of disks or no parameter") if n_args > 1 (byebug) private_methods.member?(:hanoi) true (byebug) ``` Okay, lets go on and talk about program arguments. ```bash (byebug) $ARGV [] ``` Oops. We forgot to specify any parameters to this program. Let's try again. We can use the `restart` command here. ```bash (byebug) restart 3 Re exec'ing: /path/to/exe/byebug /path/to/hanoi.rb 3 [1, 10] in /path/to/hanoi.rb 1: # 2: # Solves the classic Towers of Hanoi puzzle. 3: # => 4: def hanoi(n, a, b, c) 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end (byebug) break 5 Created breakpoint 1 at /path/to/hanoi.rb:5 (byebug) continue Stopped by breakpoint 1 at /path/to/hanoi.rb:5 [1, 10] in /path/to/hanoi.rb 1: # 2: # Solves the classic Towers of Hanoi puzzle. 3: # 4: def hanoi(n, a, b, c) => 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end (byebug) display n 1: n = 3 (byebug) display a 2: a = :a (byebug) display b 3: b = :b (byebug) undisplay 3 (byebug) continue Stopped by breakpoint 1 at /path/to/hanoi.rb:5 1: n = 2 2: a = :a [1, 10] in /path/to/hanoi.rb 1: # 2: # Solves the classic Towers of Hanoi puzzle. 3: # 4: def hanoi(n, a, b, c) => 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end (byebug) c Stopped by breakpoint 1 at /path/to/hanoi.rb:5 1: n = 1 2: a = :a [1, 10] in /path/to/hanoi.rb 1: # 2: # Solves the classic Towers of Hanoi puzzle. 3: # 4: def hanoi(n, a, b, c) => 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end (byebug) set nofullpath fullpath is off (byebug) where --> #0 Object.hanoi(n#Fixnum, a#Symbol, b#Symbol, c#Symbol) at .../shortpath/to/hanoi.rb:5 #1 Object.hanoi(n#Fixnum, a#Symbol, b#Symbol, c#Symbol) at .../shortpath/to/hanoi.rb:5 #2 at .../Proyectos/byebug/hanoi.rb:28 (byebug) ``` In the above we added new commands: `break` (see [breakpoints]()), which indicates to stop just before that line of code is run, and `continue`, which resumes execution. To remove a display expression `undisplay` is used. If we give a display number, just that display expression is removed. We also used a new command `where`(see [backtrace]()) to show the callstack. In the above situation, starting from the bottom line we see we called the `hanoi` method from line 28 of the file `hanoi.rb` and the `hanoi` method called itself two more times at line 5. In the callstack we show a _current frame_ mark, the frame number, the method being called, the names of the parameters, the types those parameters _currently_ have and the file-line position. Remember it's possible that when the program was called the parameters had different types, since the types of variables can change dynamically. You can alter the style of what to show in the trace (see [callstyle]()). Now let's move around the callstack. ```bash (byebug) undisplay Clear all expressions? (y/n) y (byebug) n_args NameError Exception: undefined local variable or method `n_args' for main:Object (byebug) frame 2 [19, 28] in /path/to/hanoi.rb 19: begin 20: n = $ARGV[0].to_i 21: rescue ValueError 22: raise("*** Expecting an integer, got: #{$ARGV[0]}") 23: end 24: end 25: 26: raise("*** Number of disks should be between 1 and 100") if n < 1 || n > 100 27: => 28: hanoi(n, :a, :b, :c) (byebug) n_args 1 (byebug) eval n 3 (byebug) down 2 [1, 10] in /path/to/hanoi.rb 1: # 2: # Solves the classic Towers of Hanoi puzzle. 3: # 4: def hanoi(n, a, b, c) => 5: hanoi(n - 1, a, c, b) if n - 1 > 0 6: 7: puts "Move disk #{a} to #{b}" 8: 9: hanoi(n - 1, c, b, a) if n - 1 > 0 10: end (byebug) eval n 2 ``` Notice in the above to get the value of variable `n` we had to use a print command like `eval n`. If we entered just `n`, that would be taken to mean byebug command `next`. In the current scope, variable `n_args` is not defined. However I can change to the top-most frame by using the `frame 2` command. Notice that inside frame #2, the value of `n_args` can be shown. Also note that the value of variable `n` is different. ### Attaching to a running program with `byebug` In the previous sessions we've been calling byebug right at the outset, but there is another mode of operation you might use. If there's a lot of code that needs to be run before the part you want to inspect, it might not be efficient or convenient to run byebug from the outset. In this section we'll show how to enter the code in the middle of your program, while delving more into byebug's operation. We will also use unit testing. Using unit tests will greatly reduce the amount of debugging needed, while at the same time, will increase the quality of your program. What we'll do is take the `triangle` code from the first session and write a unit test for that. In a sense we did write a tiny test for the program which was basically the last line where we printed the value of `triangle(3)`. This test however wasn't automated: the expectation is that someone would look at the output and verify that what was printed is what was expected. Before we can turn that into something that can be `required`, we probably want to remove that output. However I like to keep in that line so that when I look at the file, I have an example of how to run it. Therefore we will conditionally run this line if that file is invoked directly, but skip it if it is not. _NOTE: `byebug` resets `$0` to try to make things like this work._ ```ruby if __FILE__ == $PROGRAM_NAME t = triangle(3) puts t end ``` Okay, we're now ready to write our unit test and we'll use the `minitest` framework for that. Here's the test code, it should be placed in the same directory as `triangle.rb`. ```ruby require "minitest/autorun" require_relative "triangle.rb" class TestTriangle < Minitest::Test def test_basic solutions = [] 0.upto(5) { |i| solutions << triangle(i) } assert_equal([0, 1, 3, 6, 10, 15], solutions, "First 5 triangle numbers") end end ``` Let's say we want to stop before the first statement in our test method, we'll add the following: ```ruby ... def test_basic byebug solutions = [] ... ``` Now we run the program, requiring `byebug` ```bash $ ruby -rbyebug test_triangle.rb Run options: --seed 31679 # Running: [2, 11] in test_triangle.rb 2: require_relative "triangle.rb" 3: 4: class TestTriangle < Minitest::Test 5: def test_basic 6: byebug => 7: solutions = [] 8: 9: 0.upto(5) { |i| solutions << triangle(i) } 10: 11: assert_equal([0, 1, 3, 6, 10, 15], solutions, "First 5 triangle numbers") (byebug) ``` and we see that we are stopped at line 7 just before the initialization of the list `solutions`. Now let's see where we are... ```bash (byebug) set nofullpath Displaying frame's full file names is off. (byebug) bt --> #0 TestTriangle.test_basic at .../Proyectos/byebug/test_triangle.rb:7 #1 block (3 levels) in Minitest::Test.run at .../lib/minitest/test.rb:108 #2 Minitest::Test.capture_exceptions at .../lib/minitest/test.rb:206 #3 block (2 levels) in Minitest::Test.run at .../lib/minitest/test.rb:105 #4 Minitest::Test.time_it at .../lib/minitest/test.rb:258 #5 block in Minitest::Test.run at .../lib/minitest/test.rb:104 #6 #.on_signal(name#String, action#Proc) at .../minitest-5.5.0/lib/minitest.rb:321 #7 Minitest::Test.with_info_handler(&block#Proc) at .../lib/minitest/test.rb:278 #8 Minitest::Test.run at .../lib/minitest/test.rb:103 #9 #.run_one_method(klass#Class, method_name#String) at .../minitest-5.5.0/lib/minitest.rb:768 #10 #.run_one_method(klass#Class, method_name#String, reporter#Minitest::CompositeReporter) at .../minitest-5.5.0/lib/minitest.rb:295 #11 block (2 levels) in #.run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:289 ͱ-- #12 Array.each at .../minitest-5.5.0/lib/minitest.rb:288 #13 block in #.run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:288 #14 #.on_signal(name#String, action#Proc) at .../minitest-5.5.0/lib/minitest.rb:321 #15 #.with_info_handler(reporter#Minitest::CompositeReporter, &block#Proc) at .../minitest-5.5.0/lib/minitest.rb:308 #16 #.run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:287 #17 block in #.__run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:150 ͱ-- #18 Array.map at .../minitest-5.5.0/lib/minitest.rb:150 #19 #.__run(reporter#Minitest::CompositeReporter, options#Hash) at .../minitest-5.5.0/lib/minitest.rb:150 #20 #.run(args#Array) at .../minitest-5.5.0/lib/minitest.rb:127 #21 block in #.autorun at .../minitest-5.5.0/lib/minitest.rb:56 (byebug) ``` We get the same result as if we had run byebug from the outset. ### Debugging Oddities: How debugging Ruby may be different from other languages If you are used to debugging in other languages like C, C++, Perl, Java or even Bash (see [bashdb](http://bashdb.sourceforge.net)), there may be a number of things that seem or feel a little bit different and may confuse you. A number of these things aren't oddities of the debugger per se but differences in how Ruby works compared to those other languages. Because Ruby works a little differently from those other languages, writing a debugger has to also be a little different as well if it is to be useful. In this respect, using Byebug may help you understand Ruby better. We've already seen one such difference: the fact that we stop on method definitions or `def`'s and that is because these are in fact executable statements. In other compiled languages this would not happen because that's already been done when you compile the program (or in Perl when it scans in the program). In this section we'll consider some other things that might throw off new users to Ruby who are familiar with other languages and debugging in them. #### Bouncing Around in Blocks (iterators) When debugging languages with coroutines like Python and Ruby, a method call may not necessarily go to the first statement after the method header. It's possible that the call will continue after a `yield` statement from a prior call. ```ruby # # Enumerator for primes # class SievePrime def initialize @odd_primes = [] end def next_prime candidate = 2 yield candidate not_prime = false candidate += 1 loop do @odd_primes.each do |p| not_prime = (0 == (candidate % p)) break if not_prime end unless not_prime @odd_primes << candidate yield candidate end candidate += 2 end end end SievePrime.new.next_prime do |prime| puts prime break if prime > 10 end ``` ```bash $ byebug primes.rb [1, 10] in /path/to/primes.rb 1: # 2: # Enumerator for primes 3: # => 4: class SievePrime 5: def initialize 6: @odd_primes = [] 7: end 8: 9: def self.next_prime(&block) 10: candidate = 2 (byebug) set linetrace line tracing is on. (byebug) set basename basename in on. (byebug) step 9 Tracing: primes.rb:5 def initialize Tracing: primes.rb:9 def next_prime Tracing: primes.rb:31 SievePrime.new.next_prime do |prime| Tracing: primes.rb:6 @odd_primes = [] Tracing: primes.rb:10 candidate = 2 Tracing: primes.rb:11 yield candidate Tracing: primes.rb:32 puts prime 2 Tracing: primes.rb:33 break if prime > 10 Tracing: primes.rb:12 not_prime = false [7, 16] in /path/to/primes.rb 7: end 8: 9: def next_prime 10: candidate = 2 11: yield candidate => 12: not_prime = false 13: candidate += 1 14: 15: loop do 16: @odd_primes.each do |p| 17: not_prime = (0 == (candidate % p)) (byebug) ``` The loop between lines 31-34 gets interleaved between those of `SievePrime#next_prime`, lines 9-28 above. #### No Parameter Values in a Call Stack In traditional debuggers, in a call stack you can generally see the names of the parameters and the values that were passed in. Ruby is a very dynamic language and it tries to be efficient within the confines of the language definition. Values generally aren't taken out of a variable or expression and pushed onto a stack. Instead a new scope is created and the parameters are given initial values. Parameter passing is by _reference_ not by _value_ as it is say Algol, C, or Perl. During the execution of a method, parameter values can change (and often do). In fact even the _class_ of the object can change. So at present, the name of the parameter is shown. The call-style setting ([callstyle]()) can be used to set whether the name is shown or the name and the _current_ class of the object. #### Lines You Can Stop At Consider the following little Ruby program. ```ruby "Yes it does" =~ / (Yes) \s+ it \s+ does /ix puts $1 ``` The stopping points that Ruby records are the last two lines, lines 5 and 6. Inside `byebug` you can get a list of stoppable lines for a file using the `info file` command. ### Threading support Byebug supports debugging Ruby programs making use of multiple threads. Let's consider the following sample program: ```ruby class Company def initialize(task) @tasks, @results = Queue.new, Queue.new @tasks.push(task) end def run manager = Thread.new { manager_routine } employee = Thread.new { employee_routine } sleep 6 go_home(manager) go_home(employee) end # # An employee doing his thing # def employee_routine loop do if @tasks.empty? have_a_break(0.1) else work_hard(@tasks.pop) end end end # # A manager doing his thing # def manager_routine loop do if @results.empty? have_a_break(1) else show_off(@results.pop) end end end private def show_off(result) puts result end def work_hard(task) task ** task end def have_a_break(amount) sleep amount end def go_home(person) person.kill end end Company.new(10).run ``` The `Company` class simulates a real company. The company has a manager and an employee represented by 2 threads: they work concurrently to achieve the company's targets. * The employee looks for tasks to complete. If there are tasks, it works hard to complete them. Otherwise he has a quick break. ```ruby # # An employee doing his thing # def employee_routine loop do if @tasks.empty? have_a_break(0.1) else work_hard(@tasks.pop) end end end ``` * The manager, on the other hand, sits there all day and sporadically checks whether there are any results to show off. ```ruby # # A manager doing his thing # def manager_routine loop do if @results.empty? have_a_break(1) else show_off(@results.pop) end end end ``` We do some abstractions easily readable in the code. Our tasks are just a `Queue` of numbers, so are our results. What our employer does when he works is some calculation with those numbers and what the manager does with the results is printing them to the screen. We instantiate a new company with an initial task and after running that company we expect the result to be printed in the screen, but it is not. Lets debug our sample program: ```bash [1, 10] in /path/to/company.rb => 1: class Company 2: def initialize(task) 3: @tasks, @results = Queue.new, Queue.new 4: 5: @tasks.push(task) 6: end 7: 8: def run 9: manager = Thread.new { manager_routine } 10: employee = Thread.new { employee_routine } (byebug) l [11, 20] in /path/to/company.rb 11: 12: sleep 6 13: 14: go_home(manager) 15: go_home(employee) 16: end 17: 18: # 19: # An employee doing his thing 20: # (byebug) c 12 Stopped by breakpoint 1 at /path/to/company.rb:12 [7, 16] in /path/to/company.rb 7: 8: def run 9: manager = Thread.new { manager_routine } 10: employee = Thread.new { employee_routine } 11: => 12: sleep 6 13: 14: go_home(manager) 15: go_home(employee) 16: end (byebug) th l + 1 # /path/to/company.rb:12 2 # 3 # ``` What we have done here is just start our program and advance to the point inmediately after our `employee` and `manager` threads have been created. We can then check that the threads are there using the `thread list` command. Now we want to debug both of this threads to check what's happening and look for the bug. ```bash (byebug) th switch 3 [5, 14] in /path/to/company.rb 5: @tasks.push(task) 6: end 7: 8: def run 9: manager = Thread.new { manager_routine } => 10: employee = Thread.new { employee_routine } 11: 12: sleep 6 13: 14: go_home(manager) (byebug) th stop 1; th stop 2 $ 1 # /path/to/company.rb:12 $ 2 # /path/to/company.rb:9 (byebug) th l $ 1 # /path/to/company.rb:12 $ 2 # /path/to/company.rb:55 + 3 # /path/to/company.rb:10 ``` We have started by debugging the `employee` thread. To do that, we switch to that thread using the `thread switch 3` command. The thread number is the one specified by `thread list`, we know this is our worker thread because `thread list` specifies where the thread is defined in the file (and its current position if the thread is currently running). After that we stopped the main thread and the worker thread, using the command `thread stop`. We do this because we want to focus on the employee thread first and don't want the program to finish while we are debugging. Notice that stopped threads are marked with the "$" symbol whereas the current thread is marked with the "+" symbol. ```bash (byebug) s [17, 26] in /path/to/company.rb 17: 18: # 19: # An employee doing his thing 20: # 21: def employee_routine => 22: loop do 23: if @tasks.empty? 24: have_a_break(0.1) 25: else 26: work_hard(@tasks.pop) (byebug) s [18, 27] in /path/to/company.rb 18: # 19: # An employee doing his thing 20: # 21: def employee_routine 22: loop do => 23: if @tasks.empty? 24: have_a_break(0.1) 25: else 26: work_hard(@tasks.pop) 27: end (byebug) n [21, 30] in /path/to/company.rb 21: def employee_routine 22: loop do 23: if @tasks.empty? 24: have_a_break(0.1) 25: else => 26: work_hard(@tasks.pop) 27: end 28: end 29: end 30: (byebug) s [49, 58] in /path/to/company.rb 49: def show_off(result) 50: puts result 51: end 52: 53: def work_hard(task) => 54: task ** task 55: end 56: 57: def have_a_break(amount) 58: sleep amount (byebug) s [21, 30] in /path/to/company.rb 21: # 22: # An employee doing his thing 23: # 24: def employee_routine 25: loop do => 26: if @tasks.empty? 27: have_a_break(0.1) 28: else 29: work_hard(@tasks.pop) 30: end (byebug) n [22, 31] in /path/to/company.rb 22: # An employee doing his thing 23: # 24: def employee_routine 25: loop do 26: if @tasks.empty? => 27: have_a_break(0.1) 28: else 29: work_hard(@tasks.pop) 30: end 31: end (byebug) n [21, 30] in /path/to/company.rb 21: # 22: # An employee doing his thing 23: # 24: def employee_routine 25: loop do => 26: if @tasks.empty? 27: have_a_break(0.1) 28: else 29: work_hard(@tasks.pop) 30: end 31: end (byebug) ``` Everything seems fine in this thread. The first iteration the employee will do his job, and after that it will just check for new tasks and sleep. Let's debug the manager task now: ```bash (byebug) th resume 2 2 # /path/to/company.rb:12 (byebug) th switch 2 2 # /path/to/company.rb:12 [7, 16] in /path/to/company.rb 7: 8: # 9: # A CEO running his company 10: # 11: def run => 12: manager = Thread.new { manager_routine } 13: employee = Thread.new { employee_routine } 14: 15: sleep 6 16: (byebug) ``` We used the command `thread resume` to restart the manager's thread and then switch to it using `thread switch`. It's important to resume the thread's execution before switching to it, otherwise we'll get a hang because we cannot run a sleeping thread. Now we can investigate the problem in the employer's side: ```bash (byebug) s [30, 39] in /path/to/company.rb 30: 31: # 32: # A manager doing his thing 33: # 34: def manager_routine => 35: loop do 36: if @results.empty? 37: have_a_break(1) 38: else 39: show_off(@results.pop) (byebug) s [31, 40] in /path/to/company.rb 31: # 32: # A manager doing his thing 33: # 34: def manager_routine 35: loop do => 36: if @results.empty? 37: have_a_break(1) 38: else 39: show_off(@results.pop) 40: end (byebug) n [32, 41] in /path/to/company.rb 32: # A manager doing his thing 33: # 34: def manager_routine 35: loop do 36: if @results.empty? => 37: have_a_break(1) 38: else 39: show_off(@results.pop) 40: end 41: end (byebug) n [31, 40] in /path/to/company.rb 31: # 32: # A manager doing his thing 33: # 34: def manager_routine 35: loop do => 36: if @results.empty? 37: have_a_break(1) 38: else 39: show_off(@results.pop) 40: end (byebug) ``` Now we can see the problem, the `@results` variable is always empty! The employee forgot to leave the results in his manager's deck. We fix it by changing the line ```ruby work_hard(@tasks.pop) ``` in the `employee_routine` method with the line ```ruby @results << work_hard(@tasks.pop) ``` To be continued... * More complex examples with objects, pretty printing and irb. * Line tracing and non-interactive tracing. * Post-mortem debugging. ## Getting in & out ### Starting byebug There is a wrapper script called `byebug` which basically `require`'s the gem then loads `byebug` before its argument (the program to be debugged) is started. If you don't need to pass dash options to your program, which might be confused with byebug options, then you don't need to add the `--`. To get a brief list of options and descriptions, use the `--help` option. ```bash $ byebug --help byebug 3.5.1 Usage: byebug [options] -- -d, --debug Set $DEBUG=true -I, --include list Add to paths to $LOAD_PATH -m, --[no-]post-mortem Use post-mortem mode -q, --[no-]quit Quit when script finishes -x, --[no-]rc Run byebug initialization file -s, --[no-]stop Stop when script is loaded -r, --require file Require library before script -R, --remote [host:]port Remote debug [host:]port -t, --[no-]trace Turn on line tracing -v, --version Print program version -h, --help Display this message ``` Many options appear as a long option name, such as `--help` and a short one letter option name, such as `-h`. The list of options is detailed below: #### -h | --help It causes `byebug` to print some basic help and exit. #### -v | --version It causes `byebug` to print its version number and exit. #### -d | --debug Sets `$DEBUG` to `true`. Compatible with Ruby's flag. #### -I | --include path Adds `path` to load path. `path` can be a single path or a colon separated path list. #### -m | --post-mortem If your program raises an exception that isn't caught you can enter byebug for inspection of what went wrong. You may also want to use this option in conjunction with `--no-stop`. See also [Post-Mortem Debugging](). #### --no-quit Keep inside `byebug` after your program terminates normally. #### --no-stop Normally `byebug` stops before executing the first statement. If instead you want it to start running initially and perhaps break it later in the execution, use this option. #### -r | --require lib Requires the library before executing the script. This option is compatible with Ruby's. #### -t | --trace Turns on line tracing. Running `byebug --trace .rb` is pretty much like running `ruby -rtracer .rb`. If all you want to do however is get a line trace, `tracer` is most likely faster than `byebug`. ```bash $ time byebug --trace --no-stop hanoi.rb > /dev/null real 0m0.743s user 0m0.668s sys 0m0.068s $ time ruby -rtracer hanoi.rb > /dev/null real 0m0.077s user 0m0.072s sys 0m0.004s ``` ### Byebug default options Byebug has many command-line options,; it seems that some people want to set them differently from the defaults. For example, some people may want `--no-quit` to be the default behavior. One could write a wrapper script or set a shell alias to handle this. ### Command Files A command file is a file of lines that are `byebug` commands. Comments (lines starting with `#`) may also be included. An empty line in a command file does nothing; it does not mean to repeat the last command, as it would from the terminal. When you start `byebug`, it automatically executes commands from its _init file_, called `.byebugrc`. During startup, `byebug` does the following: * __Processes command line options and operands.__ Reads the init file in your current directory, if any, and then checks your home directory. The home directory is the directory named in the `$HOME` or `$HOMEPATH` environment variable. Thus, you can have more than one init file, one generic in your home directory, and another, specific to the program you are debugging, in the directory where you invoke `byebug`. You can also request the execution of a command file with the `source` command (see [Source]()). ### Quitting byebug To exit `byebug`, use the `quit` command (abbreviated to `q`). Normally, if you are in an interactive session, this command will prompt to ask if you really want to quit. If you want to quit without being prompted, enter `quit unconditionally` (abbreviated to `q!`). Another way to terminate byebug is to use the `kill` command. This does the more forceful `kill -9`. It can be used in cases where `quit` doesn't work (I haven't seen those yet). ### Calling byebug from inside your program Running a program from byebug adds a bit of overhead and slows it down a little. Furthermore, by necessity, debuggers change the operation of the program they are debugging. And this can lead to unexpected and unwanted differences. It has happened so often that the term [Heisenbugs](https://en.wikipedia.org/wiki/Heisenbug) was coined to describe the situation where using a debugger (among other possibilities) changes the behavior of the program so that the bug doesn't manifest itself anymore. There is another way to get into byebug which adds no overhead or slowdown until you reach the point at which you want to start debugging. However here you must change the script and make an explicit call to byebug. Because byebug isn't involved before the first call, there is no overhead and the script will run at the same speed as if there were no byebug. To enter byebug this way, just drop `byebug` in whichever line you want to start debugging at. You also have to require byebug somehow. If using bundler, it will take care of that for you, otherwise you can use the ruby `-r` flag or add `require "byebug"` in the line previous to the `byebug` call. If speed is crucial, you may want to start and stop this around certain sections of code, using `Byebug.start` and `Byebug.stop`. Alternatively, instead of issuing an explicit `Byebug.stop` you can add a block to the `Byebug.start` and debugging is turned on for that block. If the block of code raises an uncaught exception that would cause the block to terminate, the `stop` will occur. See [Byebug.start with a block](). When `byebug`is run, `.byebugrc` is read. You may want to enter byebug at several points in the program where there is a problem you want to investigate. And since `byebug` is just a method call it's possible to enclose it in a conditional expression, for example ```ruby byebug if "bar" == foo and 20 == iter_count ``` ### Restarting Byebug You can restart the program using `restart [program args]`. This is a re-exec - all byebug state is lost. If command arguments are passed, those are used. Otherwise program arguments from the last invocation are used. You won't be able to restart your program in all cases. First, the program should have been invoked at the outset rather than having been called from inside your program or invoked as a result of post-mortem handling. Also, since this relies on the OS `exec` call, this command is available only if your OS supports `exec`. ## Debugging remote programs It is possible to set up debugging so that you can issue byebug commands from outside the process running the Ruby code. In fact, you might even be on a different computer than the one running the Ruby program. To setup remote debugging, drop the following somewhere before the point in the program that you want to debug (In Rails, the `config/environments/development.rb` could be a good candidate). ```ruby require "byebug/core" Byebug.wait_connection = true Byebug.start_server("localhost", ) ``` Once this piece gets executed, you can connect to the remote debugger from your local machine, by running: `byebug -R localhost:`. Next, at a place of program execution which gets run just before the code you want to debug, add a call to `byebug` as was done without remote execution: ```ruby # work, work, work... byebug some ruby code # byebug will stop before this line is run ``` ## Byebug Command Reference ### Command Syntax Usually a command is put on a single line. There is no limit on how long it can be. It starts with a command name, which is followed by arguments whose meaning depends on the command name. For example, the command `step` accepts an argument which is the number of times to step, as in `step 5`. You can also use the `step` command with no arguments. Some commands do not allow any arguments. Multiple commands can be put on a line by separating each with a semicolon `;`. You can disable the meaning of a semicolon to separate commands by escaping it with a backslash. For example, you might want to enter the following code to compute the 5th Fibonacci number. ```bash (byebug) fib1=0; fib2=1; 5.times {|temp| temp=fib1; fib1=fib2; fib2 += temp } 0 1 SyntaxError Exception: /home/davidr/Proyectos/sample_app/trace.rb:1: syntax error, unexpected end-of-input, expecting '}' 5.times { |temp| temp=fib1 ^ nil 1 SyntaxError Exception: /home/davidr/Proyectos/sample_app/trace.rb:1: syntax error, unexpected tSTRING_DEND, expecting end-of-input fib2 += temp } ^ nil (byebug) fib1=0\; fib2=1\; 5.times {|temp| temp=fib1\; fib1=fib2\; fib2 += temp } 5 (byebug) fib2 8 ``` You might also consider using the [irb]() or [pry]() commands and then you won't have to escape semicolons. A blank line as input (typing just ``) means to repeat the previous command. Byebug uses readline, which handles line editing and retrieval of previous commands. Up arrow, for example, moves to the previous byebug command; down arrow moves to the next more recent command (provided you are not already at the last command). Command history is saved in file `.byebug_history`. A limit is put on the history size. You can see this with the `show history size` command. See [history]() for history parameters. ### Command Output In the command-line interface, when `byebug` is waiting for input it presents a prompt of the form `(byebug)`. If the program has terminated normally the prompt will be `(byebug:ctrl)` and in post-mortem debugging it will be `(byebug:post-mortem)`. Whenever `byebug` gives an error message such as for an invalid command or an invalid location position, it will generally preface the message with `***`. ### Command Help Once inside `byebug` you can always ask it for information on its commands using the `help` command. You can use `help` (abbreviated `h`) with no arguments to display a short list of named classes of commands ```bash (byebug) help break -- Sets breakpoints in the source code catch -- Handles exception catchpoints condition -- Sets conditions on breakpoints continue -- Runs until program ends, hits a breakpoint or reaches a line delete -- Deletes breakpoints disable -- Disables breakpoints or displays display -- Evaluates expressions every time the debugger stops down -- Moves to a lower frame in the stack trace edit -- Edits source files enable -- Enables breakpoints or displays finish -- Runs the program until frame returns frame -- Moves to a frame in the call stack help -- Helps you using byebug history -- Shows byebug's history of commands info -- Shows several informations about the program being debugged interrupt -- Interrupts the program irb -- Starts an IRB session kill -- Sends a signal to the current process list -- Lists lines of source code method -- Shows methods of an object, class or module next -- Runs one or more lines of code pry -- Starts a Pry session quit -- Exits byebug restart -- Restarts the debugged program save -- Saves current byebug session to a file set -- Modifies byebug settings show -- Shows byebug settings skip -- Runs until the next breakpoint as long as it is different from the current one source -- Restores a previously saved byebug session step -- Steps into blocks or methods one or more times thread -- Commands to manipulate threads tracevar -- Enables tracing of a global variable undisplay -- Stops displaying all or some expressions when program stops untracevar -- Stops tracing a global variable up -- Moves to a higher frame in the stack trace var -- Shows variables and its values where -- Displays the backtrace ``` With a command name, `help` displays information on how to use the command. ```bash (byebug) help list l[ist][[-=]][ nn-mm] Lists lines of source code Lists lines forward from current line or from the place where code was last listed. If "list-" is specified, lists backwards instead. If "list=" is specified, lists from current line regardless of where code was last listed. A line range can also be specified to list specific sections of code. (byebug) ``` A number of commands, namely `info`, `set`, `show`, `enable` and `disable`, have many sub-parameters or _subcommands_. When you ask for help for one of these commands, you will get help for all of the subcommands that command offers. Sometimes you may want help only on a subcommand and to do this just follow the command with its subcommand name. For example, `help info breakpoints`will just give help about the `info breakpoints` command. Furthermore it will give longer help than the summary information that appears when you ask for help. You don't need to list the full subcommand name, just enough of the letters to make that subcommand distinct from others will do. For example, `help info b` is the same as `help info breakpoints`. Some examples follow. ```bash (byebug) help info info[ subcommand] Generic command for showing things about the program being debugged. -- List of "info" subcommands: -- info args -- Argument variables of current stack frame info breakpoints -- Status of user-settable breakpoints info catch -- Exceptions that can be caught in the current stack frame info display -- Expressions to display when program stops info file -- Info about a particular file read in info files -- File names and timestamps of files read in info line -- Line number and filename of current position in source file info program -- Execution status of the program ``` ```bash (byebug) help info breakpoints Status of user-settable breakpoints. Without argument, list info about all breakpoints. With an integer argument, list info on that breakpoint. ``` ```bash (byebug) help info b Status of user-settable breakpoints. Without argument, list info about all breakpoints. With an integer argument, list info on that breakpoint. ``` ### Control Commands: quit, restart, source #### Quit To exit `byebug`, type `quit` (abbreviated to `q`). Normally, if you are in an interactive session, this command will prompt you to confirm you really want to quit. If you want to quit without being prompted, enter `quit unconditionally` (abbreviated to `q!`). #### Restart To restart the program, use the `restart|r` command. This is a re-exec - all `byebug` state is lost. If command arguments are passed, those are used. Otherwise program arguments from the last invocation are used. You won't be able to restart your program in all cases. First, the program should have been invoked at the outset rather than having been called from inside your program or invoked as a result of post-mortem handling. #### Source You can run `byebug` commands inside a file, using the command `source `. The lines in a command file are executed sequentially. They are not printed as they are executed. If there is an error, execution proceeds to the next command in the file. For information about command files that get run automatically on startup see [Command Files](). ### Display Commands: display, undisplay #### Display If you find that you want to print the value of an expression frequently (to see how it changes), you might want to add it to the *automatic display list** so that `byebug` evaluates it each time your program stops or after a line is printed if line tracing is enabled. Each expression added to the list is given a number to identify it; to remove an expression from the list, you specify that number. The automatic display looks like this: ```bash (byebug) display n 1: n = 3 ``` This display shows item numbers, expressions and their current values. If the expression is undefined or illegal the expression will be printed but no value will appear. ```bash (byebug) display undefined_variable 2: undefined_variable = (byebug) display 1/0 3: 1/0 = ``` If you use `display` with no argument, `byebug` will display the current values of the expressions in the list, just as it is done when your program stops. Using `info display` has the same effect. #### Undisplay To remove an item from the list, use `undisplay` followed by the number identifying the expression you want to remove. `undisplay` does not repeat if you press ``after using it (otherwise you would just get the error _No display number n_) You can also temporarily disable or enable display expressions, so that the will not be printed but they won't be forgotten either, so you can toggle them again later. To do that, use `disable display` or `enable display` followed by the expression number. ### Evaluation of expressions: irb, pry To examine and change data in your script you can just evaluate any Ruby code from `byebug`'s prompt. Any input that is not recognized as a command will be evaluated, so `byebug` essentially works as a REPL. If you want to evaluate something that conflicts with a `byebug` command, just use Ruby's `eval`. For example, if you want to print a variable called `n`, type `eval n` because typing just `n` will execute `byebug`'s command `next`. Finally, if you need more advanced functionality from REPL's, you can enter `irb` or `pry` using `irb` or `pry` commands. The binding's environment will be set to the current state in the program. When you leave the repl and go back to `byebug`'s command prompt we show the file, line and text position of the program. If you issue a `list` without location information, the default location used is the current line rather than the current position that may have got updated via a prior `list` command. ``` $ byebug triangle.rb [1, 10] in /path/to/triangle.rb 1: # Compute the n'th triangle number, the hard way: triangle(n) == (n*(n+1))/2 => 2: def triangle(n) 3: tri = 0 4: 0.upto(n) do |i| 5: tri += i 6: end 7: tri 8: end 9: 10: if __FILE__ == $0 (byebug) irb irb(main):001:0> (0..6).inject { |sum, i| sum += i } => 21 irb(main):002:0> exit (byebug) ``` ### Printing variables: var Byebug can print many different information about variables. Such as * `var const `. Show the constants of ``. This is basically listing variables and their values in `.constant`. * `var instance `. Show the instance variables of ``. This is basically listing `.instance_variables`. * `var instance`. Show instance_variables of `self`. * `var local`. Show local variables. * `var global`. Show global variables. * `var all`. Show local, global and instance and class variables of `self`. * `method instance `. Show methods of ``. Basically this is the same as running `.instance_methods(false)`. * `method `. Show methods of the class or module ``. Basically this is the same as running `.methods`. ### Examining Program Source Files: list `byebug` can print parts of your script's source. When your script stops, `byebug` spontaneously lists the source code around the line where it stopped that line. It does that when you change the current stack frame as well. Implicitly there is a default line location. Each time a list command is run that implicit location is updated, so that running several list commands in succession shows a contiguous block of program text. If you don't need code context displayed every time, you can issue the `set noautolist` command. Now whenever you want code listed, you can explicitly issue the `list` command or its abbreviation `l`. Notice that when a second listing is displayed, we continue listing from the place we last left off. When the beginning or end of the file is reached, the line range to be shown is adjusted so "it doesn't overflow". You can set the `noautolist` option by default by dropping `set noautolist` in byebug's startup file `.byebugrc`. If you want to set how many lines to be printed by default rather than use the initial number of lines, 10, use the `set listsize` command ([listsize()). To see the entire program in one shot, give an explicit starting and ending line number. You can print other portions of source files by giving explicit position as a parameter to the list command. There are several ways to specify what part of the file you want to print. `list nnn` prints lines centered around line number `nnn` in the current source file. `l` prints more lines, following the last lines printed. `list -` prints lines just before the lines last printed. `list nnn-mmm` prints lines between `nnn` and `mmm` inclusive. `list =` prints lines centered around where the script is stopped. Repeating a `list` command with `RET` discards the argument, so it is equivalent to typing just `list`. This is more useful than listing the same lines again. An exception is made for an argument of `-`: that argument is preserved in repetition so that each repetition moves up in the source file. ### Editing Source files: edit To edit a source file, use the `edit` command. The editor of your choice is invoked with the current line set to the active line in the program. Alternatively, you can give a line specification to specify what part of the file you want to edit. You can customize `byebug` to use any editor you want by using the `EDITOR` environment variable. The only restriction is that your editor (say `ex`) recognizes the following command-line syntax: ``` ex +nnn file ``` The optional numeric value `+nnn` specifies the line number in the file where you want to start editing. For example, to configure `byebug` to use the `vi` editor, you could use these commands with the `sh` shell: ```bash EDITOR=/usr/bin/vi export EDITOR byebug ... ``` or in the `csh` shell, ```bash setenv EDITOR /usr/bin/vi byebug ... ``` ### The stack trace When your script has stopped, one thing you'll probably want to know is where it stopped and some idea of how it got there. Each time your script calls a method or enters a block, information about this action is saved. This information is what we call a _stack frame_ or just a _frame_. The set of all frames at a certain point in the program's execution is called the _stack trace_ or just the _stack_. Each frame contains a line number and the source-file name that the line refers to. If the frame is the beginning of a method it also contains the method name. When your script is started, the stack has only one frame, that of the `main` method. This is called the _initial frame_ or the _outermost frame_. Each time a method is called, a new frame is added to the stack trace. Each time a method returns, the frame for that method invocation is removed. If a method is recursive, there can be many frames for the same method. The frame for the method in which execution is actually occurring is called the _innermost frame_. This is the most recently created of all the stack frames that still exist. Every time the debugger stops, one entry in the stack is selected as the current frame. Many byebug commands refer implicitly to the selected block. In particular, whenever you ask Byebug to list lines without giving a line number or location the value is found in the selected frame. There are special commands to select whichever frame you're interested in, such as `up`, `down` and `frame`. After switching frames, when you issue a `list` command without any position information, the position used is the location in the frame that you just switched between, rather than a location that got updated via a prior `list` command. Byebug assigns numbers to all existing stack frames, starting with zero for the _innermost frame_, one for the frame that called it, and so on upward. These numbers do not really exist in your script, they are assigned by Byebug to give you a way of designating stack frames in commands. ### Printing the Stack: `where` command The command `where`, aliased to `bt` or `backtrace` prints the call stack., It shows one line per frame, for many frames, starting with the place that you are stopped at (frame zero), followed by its caller (frame one), and on up the stack. Each frame is numbered and can be referred to in the `frame` command. The position of the current frame is marked with `-->`. The are some special frames generated for methods that are implemented in C. One such method is `each`. They are marked differently in the call stack to indicate that we cannot switch to those frames. This is because they have no source code in Ruby, so we can not debug them using Byebug. ```bash (byebug) where --> #0 Object.gcd(a#Fixnum, b#Fixnum) at line gcd.rb:6 #1 at line gcd.rb:19 ``` ### Selecting a frame: `up`, `down` and `frame` commands * `up `: Move `n` frames up the stack, towards the outermost frame (higher frame numbers, frames that have existed longer). `n` defaults to one. * `down `: Move `n` frames down the stack, towards the _innermost frame_ (lower frame numbers, frames that were created more recently). `n` defaults to one. * `frame `: Allows you to move to an arbitrary frame. `n` is the stack frame number or 0 if no frame number is given. `frame 0` will show the current and most recent stack frame. If a negative number is given, counting is from the other end of the stack frame, so `frame -1` shows the least-recent, outermost stack frame. Without an argument, `frame` prints the current stack frame. byebug-11.1.1/Gemfile000066400000000000000000000006411361243113500143430ustar00rootroot00000000000000# frozen_string_literal: true source "https://rubygems.org" gemspec gem "chandler", "0.9.0" gem "mdl", "0.8.0" gem "minitest", "~> 5.11" gem "pry", "0.12.2" gem "rake", "~> 13.0" gem "rake-compiler", "~> 1.0" gem "rexml", "~> 3.2" # TODO: remove when https://github.com/gettalong/kramdown/pull/638 released gem "rubocop", "0.79.0" gem "rubocop-performance", "~> 1.3" gem "simplecov", "0.17.1" gem "yard", "0.9.24" byebug-11.1.1/Gemfile.lock000066400000000000000000000036601361243113500152760ustar00rootroot00000000000000PATH remote: . specs: byebug (11.1.1) GEM remote: https://rubygems.org/ specs: addressable (2.7.0) public_suffix (>= 2.0.2, < 5.0) ast (2.4.0) chandler (0.9.0) netrc octokit (>= 2.2.0) coderay (1.1.2) docile (1.3.2) faraday (1.0.0) multipart-post (>= 1.2, < 3) jaro_winkler (1.5.4) json (2.3.0) kramdown (2.1.0) kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) mdl (0.8.0) kramdown (~> 2.0) kramdown-parser-gfm (~> 1.0) mixlib-cli (~> 2.1, >= 2.1.1) mixlib-config (>= 2.2.1, < 4) method_source (0.9.2) minitest (5.14.0) mixlib-cli (2.1.1) mixlib-config (3.0.1) tomlrb multipart-post (2.1.1) netrc (0.11.0) octokit (4.15.0) faraday (>= 0.9) sawyer (~> 0.8.0, >= 0.5.3) parallel (1.19.1) parser (2.7.0.1) ast (~> 2.4.0) pry (0.12.2) coderay (~> 1.1.0) method_source (~> 0.9.0) public_suffix (4.0.3) rainbow (3.0.0) rake (13.0.1) rake-compiler (1.1.0) rake rexml (3.2.3) rubocop (0.79.0) jaro_winkler (~> 1.5.1) parallel (~> 1.10) parser (>= 2.7.0.1) rainbow (>= 2.2.2, < 4.0) ruby-progressbar (~> 1.7) unicode-display_width (>= 1.4.0, < 1.7) rubocop-performance (1.5.2) rubocop (>= 0.71.0) ruby-progressbar (1.10.1) sawyer (0.8.2) addressable (>= 2.3.5) faraday (> 0.8, < 2.0) simplecov (0.17.1) docile (~> 1.1) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) simplecov-html (0.10.2) tomlrb (1.2.8) unicode-display_width (1.6.0) yard (0.9.24) PLATFORMS ruby DEPENDENCIES bundler (~> 2.0) byebug! chandler (= 0.9.0) mdl (= 0.8.0) minitest (~> 5.11) pry (= 0.12.2) rake (~> 13.0) rake-compiler (~> 1.0) rexml (~> 3.2) rubocop (= 0.79.0) rubocop-performance (~> 1.3) simplecov (= 0.17.1) yard (= 0.9.24) BUNDLED WITH 2.1.4 byebug-11.1.1/LICENSE000066400000000000000000000024601361243113500140560ustar00rootroot00000000000000Copyright (c) 2018 David Rodríguez All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. byebug-11.1.1/README.md000066400000000000000000000170241361243113500143320ustar00rootroot00000000000000# Byebug [![Version][gem]][gem_url] [![Tidelift][tid]][tid_url] [![Coverage][cov]][cov_url] [![Gitter][irc]][irc_url] [gem]: https://img.shields.io/gem/v/byebug.svg [tid]: https://tidelift.com/badges/github/deivid-rodriguez/byebug [cov]: https://api.codeclimate.com/v1/badges/f1a1bec582752c22da80/test_coverage [irc]: https://img.shields.io/badge/IRC%20(gitter)-devs%20%26%20users-brightgreen.svg [gem_url]: https://rubygems.org/gems/byebug [tid_url]: https://tidelift.com/subscription/pkg/rubygems-byebug?utm_source=rubygems-byebug&utm_medium=readme_badge [cov_url]: https://codeclimate.com/github/deivid-rodriguez/byebug/test_coverage [irc_url]: https://gitter.im/deivid-rodriguez/byebug Byebug is a simple to use and feature rich debugger for Ruby. It uses the TracePoint API for execution control and the Debug Inspector API for call stack navigation. Therefore, Byebug doesn't depend on internal core sources. Byebug is also fast because it is developed as a C extension and reliable because it is supported by a full test suite. The debugger permits the ability to understand what is going on _inside_ a Ruby program while it executes and offers many of the traditional debugging features such as: * Stepping: Running your program one line at a time. * Breaking: Pausing the program at some event or specified instruction, to examine the current state. * Evaluating: Basic REPL functionality, although [pry] does a better job at that. * Tracking: Keeping track of the different values of your variables or the different lines executed by your program. ## For enterprise Byebug for enterprise is available via the Tidelift Subscription. [Learn more][Tidelift for enterprise]. ## Build Status Linux ![](https://github.com/deivid-rodriguez/byebug/workflows/build/badge.svg) Windows [![Vey][vey]][vey_url] [vey]: https://ci.appveyor.com/api/projects/status/github/deivid-rodriguez/byebug?svg=true [vey_url]: https://ci.appveyor.com/project/deivid-rodriguez/byebug ## Requirements * _Required_: MRI 2.4.0 or higher. * _Recommended_: MRI 2.6.4 or higher (MRI 2.6.0 to 2.6.3 contain a regression causing unbalanced call/return events in some cases, breaking the `next` command). ## Install ```shell gem install byebug ``` Alternatively, if you use `bundler`: ```shell bundle add byebug --group "development, test" ``` ## Usage ### From within the Ruby code Simply include `byebug` wherever you want to start debugging and the execution will stop there. For example, if you were debugging Rails, you would add `byebug` to your code: ```ruby def index byebug @articles = Article.find_recent end ``` And then start a Rails server: ```shell bin/rails s ``` Once the execution gets to your `byebug` command, you will receive a debugging prompt. ### From the command line If you want to debug a Ruby script without editing it, you can invoke byebug from the command line. ```shell byebug myscript.rb ``` ## Byebug's commands Command | Aliases | Subcommands ------- | ------- | ----------- `backtrace` | `bt` `w` `where`| `break` | `b` | `catch` | `cat` | `condition` | `cond` | `continue` | `c` `cont` | `continue!` | `c!` `cont!` | `debug` | | `delete` | `del` | `disable` | `dis` | `breakpoints` `display` `display` | `disp` | `down` | | `edit` | `ed` | `enable` | `en` | `breakpoints` `display` `finish` | `fin` | `frame` | `f` | `help` | `h` | `history` | `hist` | `info` | `i` | `args` `breakpoints` `catch` `display` `file` `line` `program` `interrupt` | `int` | `irb` | | `kill` | | `list` | `l` | `method` | `m` | `instance` `next` | `n` | `pry` | | `quit` | `q` | `quit!` | `q!` | `restart` | | `save` | `sa` | `set` | | `autoirb` `autolist` `autopry` `autosave` `basename` `callstyle` `fullpath` `histfile` `histsize` `linetrace` `listsize` `post_mortem` `savefile` `stack_on_error` `width` `show` | | `autoirb` `autolist` `autopry` `autosave` `basename` `callstyle` `fullpath` `histfile` `histsize` `linetrace` `listsize` `post_mortem` `savefile` `stack_on_error` `width` `skip` | `sk` | `source` | `so` | `step` | `s` | `thread` | `th` | `current` `list` `resume` `stop` `switch` `tracevar` | `tr` | `undisplay` | `undisp` | `untracevar`| `untr` | `up` | | `var` | `v` | `all` `constant` `global` `instance` `local` ## Semantic Versioning Byebug attempts to follow [semantic versioning](https://semver.org) and bump major version only when backwards incompatible changes are released. Backwards compatibility is targeted to [pry-byebug] and any other plugins relying on `byebug`. ## Getting Started Read [byebug's markdown guide](https://github.com/deivid-rodriguez/byebug/blob/master/GUIDE.md) to get started. Proper documentation will be eventually written. ## Related projects * [pry-byebug] adds `next`, `step`, `finish`, `continue` and `break` commands to `pry` using `byebug`. * [ruby-debug-passenger] adds a rake task that restarts Passenger with Byebug connected. * [minitest-byebug] starts a byebug session on minitest failures. * [sublime_debugger] provides a plugin for ruby debugging on Sublime Text. * [atom-byebug] provides integration with the Atom editor [EXPERIMENTAL]. ## Contribute See [Getting Started with Development](CONTRIBUTING.md). ## Funding Subscribe to [Tidelift][Tidelift support] to ensure byebug stays actively maintained, and at the same time get licensing assurances and timely security notifications for your open source dependencies. You can also help `byebug` by leaving a small (or big) tip through [Liberapay]. ## Security contact information Please use the Tidelift security contact to [report a security vulnerability]. Tidelift will coordinate the fix and disclosure. ## Credits Everybody who has ever contributed to this forked and reforked piece of software, especially: * @ko1, author of the awesome TracePoint API for Ruby. * @cldwalker, [debugger]'s maintainer. * @denofevil, author of [debase], the starting point of this. * @kevjames3 for testing, bug reports and the interest in the project. * @FooBarWidget for working and helping with remote debugging. [debugger]: https://github.com/cldwalker/debugger [pry]: https://github.com/pry/pry [debase]: https://github.com/denofevil/debase [pry-byebug]: https://github.com/deivid-rodriguez/pry-byebug [ruby-debug-passenger]: https://github.com/davejamesmiller/ruby-debug-passenger [minitest-byebug]: https://github.com/kaspth/minitest-byebug [sublime_debugger]: https://github.com/shuky19/sublime_debugger [atom-byebug]: https://github.com/izaera/atom-byebug [Liberapay]: https://liberapay.com/byebug/donate [Tidelift]: https://tidelift.com/subscription/pkg/rubygems-byebug?utm_source=rubygems-byebug&utm_medium=readme_text [Tidelift for enterprise]: https://tidelift.com/subscription/pkg/rubygems-byebug?utm_source=rubygems-byebug&utm_medium=referral&utm_campaign=github&utm_content=enterprise [Tidelift support]: https://tidelift.com/subscription/pkg/rubygems-byebug?utm_source=rubygems-byebug&utm_medium=referral&utm_campaign=github&utm_content=support [report a security vulnerability]: https://tidelift.com/security byebug-11.1.1/Rakefile000066400000000000000000000055711361243113500145240ustar00rootroot00000000000000# frozen_string_literal: true require "bundler/gem_tasks" require "chandler/tasks" require "rake/extensiontask" require "yard" # # Add chandler as a prerequisite for `rake release` # task "release:rubygem_push" => "chandler:push" # # Prepend DevKit into compilation phase # if Gem.win_platform? desc "Activates DevKit" task :devkit do begin require "devkit" rescue LoadError abort "Failed to load DevKit required for compilation" end end task compile: :devkit end spec = Gem::Specification.load("byebug.gemspec") Rake::ExtensionTask.new("byebug", spec) { |ext| ext.lib_dir = "lib/byebug" } desc "Runs the test suite" task :test do require_relative "test/minitest_runner" exit 1 unless Byebug::MinitestRunner.new.run end namespace :lint do desc "Run all linters" task all: %i[clang_format executables tabs trailing_whitespace rubocop mdl] require_relative "tasks/linter" desc "Run clang_format on C files" task :clang_format do puts "Running linter on C files" CLangFormatLinter.new.run end desc "Check unnecessary execute permissions" task :executables do puts "Checking for unnecessary executables" ExecutableLinter.new.run end desc "Check for tabs" task :tabs do puts "Checking for unnecessary tabs" TabLinter.new.run end desc "Check for trailing whitespace" task :trailing_whitespace do puts "Checking for unnecessary trailing whitespace" TrailingWhitespaceLinter.new.run end require "rubocop/rake_task" desc "Checks ruby code style with RuboCop" RuboCop::RakeTask.new desc "Checks markdown code style with Markdownlint" task :mdl do puts "Running mdl" sh("mdl", *Dir.glob("*.md")) end desc "Checks shell code style with shellcheck" task :shellcheck do puts "Running shellcheck" sh("shellcheck", *Dir.glob("bin/*.sh")) end end desc "Runs lint tasks" task lint: "lint:all" namespace :docker do require_relative "docker/manager" desc "Build all docker images" task :build_all do Docker::Manager.build_all end desc "Build the default docker image" task :build do Docker::Manager.build_default end desc "Build a ruby trunk image" task :build_and_push_head, %i[line_editor compiler] do |_t, opts| manager = Docker::Manager.new( version: "head", line_editor: opts[:line_editor], compiler: opts[:compiler] ) manager.build manager.login manager.push end desc "Test all docker images" task :test_all do Docker::Manager.test_all end desc "Test the default docker image" task :test do Docker::Manager.test_default end desc "Push all docker images to dockerhub" task :push_all do Docker::Manager.push_all end desc "Push the default docker image to dockerhub" task :push do Docker::Manager.push_default end end task default: %i[compile test lint] YARD::Rake::YardocTask.new byebug-11.1.1/Ruby_Grant_2014_Report.md000066400000000000000000000423301361243113500174500ustar00rootroot00000000000000# Threading Support for Byebug ## Motivation Having a fully featured stable debugger is important for most programming languages. It makes the language more attractive for beginners and for users coming from other languages, because it's a very adequate tool not only for bug fixing but also just for playing around with a language's features or studying code not written by ourselves. With this in mind, the main purpose of Byebug since it was started was to make it an atractive tool for beginners (I was actually a Ruby beginner during the initial development phase of Byebug so I was making heavy use of my own tool too). The main features supported by Byebug are: * Breaking. Pause the program at some event or specified instruction, to examine the current state. Related commands: `break`, `catch`, `condition`, `delete`, `enable`, `disable`. * Analyzing. Studying program status at a certain point during its execution (including right after termination). Specifically, we can: * Inspect and move around the backtrace (`backtrace`, `up`, `down` and `frame` commands). * Have a basic REPL functionality, evaluating custom code (`eval`, `irb`, `pry`, `method`, `pp`, `ps`, `putl`, `var` commands). * Look and change the program's source code (`edit`, `list`, `info` commands). * Stepping: Running your program one line or instruction at a time, or until specific points in the program are reached. Related commands: `step`, `next`, `continue`, `finish`, `kill`, `quit`, `restart`. * Tracking: Keeping track of the different values of your variables or the different lines executed by your program. Related commands: `display`, `undisplay`, `tracevar`, `untracevar`, `set linetrace`. This features have been working very well as long as the debugged program would have no multiple Ruby threads, but would just not work when the program would use different threads. Notice that this would affect developers making use of threads, but was also affecting users not necessarily knowing anything about threads, because very well know libraries out there transparently make use of them (for example, `capybara-webkit` or Ruby's stdlib `Timeout` module). So Byebug needed a way to debug multithreaded programs that was both: * Reliable: no deadlock, no killed threads when they are not related to user's code. * Useful: allow debugging issues with multithreaded programs. To do that, we would need to provide the user with the ability to stop/resume specific threads, list active threads and switch between threads. This is what this grant is about. ## The feature The addition of threading support to Byebug's debugger allows users to properly debug programs making use of Ruby's threads. This includes listing active threads and their statuses, switching execution to specific threads and temporarily pausing/resuming threads. To try out the feature, you might want to use a real application (a Rails app for example) using threads or just follow the sample session about threads included in Byebug's Guide. See [here]( https://github.com/deivid-rodriguez/byebug/blob/master/GUIDE.md#threading-support) for details. The feature is also fully tested. You can clone _byebug_'s repo and then run ```shell bundle install # Install dependencies rake compile # Compile the C-extension ruby -w -Ilib test/test_helper.rb test/commands/thread_test.rb ``` This is the list of available commands and a short explanation of its usage: * _thread list_: Lists threads. This is equivalent to Ruby's `Thread.list`, but it has the following format: * A mark '+' for the current thread. * A mark '$' for a stopped thread. * An internal `id` for the thread, specific to Byebug. * Ruby's id and status for the thread, in the format `# :`. * Current file and line number location of the thread's execution, in the format `file:line`. * _thread current_: Shows the `thread list` entry for the current thread, just like the `frame` command shows the current frame whereas the `backtrace` command shows the whole backtrace. * _thread stop_: Allows the user to temporarily stop the execution of a thread. This is useful when we want to focus on debugging specific threads and want to make sure some other thread stays unchanged, or if we want our main thread to "wait for us" and don't finish until we tell it to. * _thread resume_: Allows resuming threads previously stopped with `thread stop`. It can be used to resume normal program execution, once we've introduced a change that could fix our issue, for example. * _thread switch_: Switches current thread and context to another thread. After issuing this command, execution will be stopped in a different place in the source code and we'll get a different backtrace. The target thread can't be in the sleeping state so we might have to issue the `thread resume` command before running this command. ## The implementation The TracePoint API was not well suited for this feature. It includes a `THREAD_BEGIN` and a `THREAD_END` events, but they are generated when the execution is first delegated to the thread and not when the thread is created. We want the threads to be available to the user (in a "sleep" state) from their creation, so we need to resort to "other trickery". ### Mantaining a global thread list Byebug mantains a global hash table of active threads which is constructed dinamically as TracePoint API events are received. Every time an event is processed, we look for a thread matching the current thread in our threads table and we set up that context to be the current context (when we talk about "context" in Byebug we mean the program's state in a specific moment during its execution). If the thread is not found (first event of the thread), we create a new entry in the table. This is done by the `thread_context_lookup` method in `threads.c`. Periodically, the table is cleaned up of dead threads, using the `cleanup_dead_threads` method in `threads.c`. This method needs to make use of Ruby because the C-extension API does not seem to have utilities to check for thread status. This might be a big performance penalty for programs using a big number of threads, so at some point we might want to either have a `rb_thread_status` method available to C-extensions, or add a workaround inside Byebug such as not cleaning the threads table for every event but only every "N" events, where "N" is big enough so this cleanup is not a performance bottleneck anymore. Nevertheless, I've tried latest byebug with some Rails apps and haven't noticed any performance issues. ### Thread syncronization The biggest challenge of implementing threading in Byebug has been this one. While our user is stopping at his Byebug prompt, the scheduler can (and does) schedule different threads to be run, so TracePoint API events are generated for other threads. We want everything halted while the user is in control so we need to lock the processing of this events until the user gives control back to the debugger. To do that we've used a global lock in the C-extension, that ensures that a single TracePoint API event is processed at the same time. At the beginning of the processing of every event, we call the `acquire_lock` method that will either: * Obtain the global lock if it's free (or the current thread already has it because the previous event was also from the same thread). In this case, the event is processed normally. * Go to sleep and pass execution on to another thread if the lock is currently being hold by another thread. Notice that we need to specifically call `rb_thread_stop` here because C-extensions are not preemptive in the sense that the scheduler won't automatically switch thread execution while in a C-extension just like it does when running "regular Ruby code". So if we don't call `rb_thread_stop`, the execution will just deadlock here. At the end of the processing of every event, we call the `release_lock` method that will release the lock and pass on the execution to another thread (that will probably be halted in `acquire_lock` and will be able to pass through once the lock is released. ### Specifics of some commands #### thread list We currently manually syncronize our thread list with the one given by Ruby ( `Thread.list`) when this command is executed, but once this feature is well tested we can probably get rid of that check and just trust our table that should always be up to date and exactly the same as `Thread.list`. #### thread stop To implement this command, we needed to add some global flags. A function `rb_thread_stop` is available for C-extensions to stop the current thread, but when the user issues this command, the target thread is not the current thread so we can't directly use that method as it doesn't accept a target thread argument. Instead, we set a global flag, `CTX_FL_SUSPEND` and check that in `acquire_lock` to prevent thread execution. So even if the global lock is free, we never delegate execution to the suspended thread. #### thread resume The only specific comment about the implementation of this command is the `CTX_FL_WAS_RUNNING` flag. This flag is used to remember the thread's status when a thread was suspended so the `thread resume` command can correctly restore it. It `CTX_FL_WAS_RUNNING` is set when we run `thread resume` we need to call `rb_thread_wakeup` to restore the "running" status. #### thread switch This command was a bit problematic. Users will probably expect nothing to be run when they issue this command, just a "context change". However, we actually need to let program's execution to succesfully achieve the context change, so that the new "current thread" is the one we are switching to and we can properly show backtrace and file location information for the new thread. So the idea here is to force the scheduler to inmediately delegate execution control to the target thread so that the next TracePoint API event generated belongs to that thread and we can inmediately stop execution again without running anything else. To achieve this, `thread switch` does the following: * Saves the target thread in a global `next_thread` variable. * Sets a breakpoint for the next event to be receive. * Releases the user prompt and gives control back to the debugger. To make this work, we need to change the way we `release_lock` after every event has been processed. We needn't just release the lock but also force the scheduler the give control to the thread specified by `next_thread`. To implement this, we add a double linked list where we mantain the list of threads whose execution is being hold by Byebug's global lock. Threads are added to this list in `acquire_lock` and removed in `release_lock`. In the `release_lock` method we pop `next_thread` from the list if `next_thread` is set or _any_ thread otherwise. Then we call `rb_thread_run` on the popped thread to delegate control to that thread. ### Byebug's REPL and threads Byebug's debugger includes a REPL aside from it's built-in commands. Anything that's not recognized as a Byebug command will be automatically evaluated as Ruby code. This has proven to be a very useful feature for users to the point the some people consider Byebug as an `irb` or `pry` alternative. The new threading feature would not play nice with the REPL when the command to be evaluated included thread stuff. Users would get either a 'No threads alive. deadlock?' error or a proper deadlock. For an example of this issues, have a look at [here](https://github.com/deivid-rodriguez/byebug/issues/115). This would happen because `byebug`'s global lock wouldn't be released before evaluating stuff, so if an evaluated command created new threads or switched to previously created threads, we would get a deadlock because those threads wouldn't be able to run because thread execution would be hold by Byebug's current thread. To solve this issues, we exposed to Ruby a couple of methods `Byebug.lock` and `Byebug.unlock` to would call `acquire_lock` and `release_lock`, and then implemented the following method: ```ruby def allowing_other_threads Byebug.unlock res = yield Byebug.lock res end ``` and call it before evaluating anything in Byebug's prompt. This solved issues when evaluating stuff from the user's prompt. ## The code After the explanation of the current implementation, I think we've gone through every bit of code relating to threads. I copy the relevant file `threads.c` in the C-extension for completeness. ```c #include /* Threads table class */ static VALUE cThreadsTable; /* If not Qnil, holds the next thread that must be run */ VALUE next_thread = Qnil; /* To allow thread syncronization, we must stop threads when debugging */ VALUE locker = Qnil; static int t_tbl_mark_keyvalue(st_data_t key, st_data_t value, st_data_t tbl) { UNUSED(tbl); rb_gc_mark((VALUE) key); if (!value) return ST_CONTINUE; rb_gc_mark((VALUE) value); return ST_CONTINUE; } static void t_tbl_mark(void *data) { threads_table_t *t_tbl = (threads_table_t *) data; st_table *tbl = t_tbl->tbl; st_foreach(tbl, t_tbl_mark_keyvalue, (st_data_t) tbl); } static void t_tbl_free(void *data) { threads_table_t *t_tbl = (threads_table_t *) data; st_free_table(t_tbl->tbl); xfree(t_tbl); } /* * Creates a numeric hash whose keys are the currently active threads and * whose values are their associated contexts. */ VALUE create_threads_table(void) { threads_table_t *t_tbl; t_tbl = ALLOC(threads_table_t); t_tbl->tbl = st_init_numtable(); return Data_Wrap_Struct(cThreadsTable, t_tbl_mark, t_tbl_free, t_tbl); } /* * Checks a single entry in the threads table. * * If it has no associated context or the key doesn't correspond to a living * thread, the entry is removed from the thread's list. */ static int check_thread_i(st_data_t key, st_data_t value, st_data_t data) { UNUSED(data); if (!value) return ST_DELETE; if (!is_living_thread((VALUE) key)) return ST_DELETE; return ST_CONTINUE; } /* * Checks whether a thread is either in the running or sleeping state. */ int is_living_thread(VALUE thread) { VALUE status = rb_funcall(thread, rb_intern("status"), 0); if (NIL_P(status) || status == Qfalse) return 0; if (rb_str_cmp(status, rb_str_new2("run")) == 0 || rb_str_cmp(status, rb_str_new2("sleep")) == 0) return 1; return 0; } /* * Checks threads table for dead/finished threads. */ void cleanup_dead_threads(void) { threads_table_t *t_tbl; Data_Get_Struct(threads, threads_table_t, t_tbl); st_foreach(t_tbl->tbl, check_thread_i, 0); } /* * Looks up a context in the threads table. If not present, it creates it. */ void thread_context_lookup(VALUE thread, VALUE * context) { threads_table_t *t_tbl; Data_Get_Struct(threads, threads_table_t, t_tbl); if (!st_lookup(t_tbl->tbl, thread, context) || !*context) { *context = context_create(thread); st_insert(t_tbl->tbl, thread, *context); } } /* * Holds thread execution while another thread is active. * * Thanks to this, all threads are "frozen" while the user is typing commands. */ void acquire_lock(debug_context_t * dc) { while ((!NIL_P(locker) && locker != rb_thread_current()) || CTX_FL_TEST(dc, CTX_FL_SUSPEND)) { add_to_locked(rb_thread_current()); rb_thread_stop(); if (CTX_FL_TEST(dc, CTX_FL_SUSPEND)) CTX_FL_SET(dc, CTX_FL_WAS_RUNNING); } locker = rb_thread_current(); } /* * Releases our global lock and passes execution on to another thread, either * the thread specified by +next_thread+ or any other thread if +next_thread+ * is nil. */ void release_lock(void) { VALUE thread; cleanup_dead_threads(); locker = Qnil; if (NIL_P(next_thread)) thread = pop_from_locked(); else { remove_from_locked(next_thread); thread = next_thread; } if (thread == next_thread) next_thread = Qnil; if (!NIL_P(thread) && is_living_thread(thread)) rb_thread_run(thread); } /* * call-seq: * Byebug.unlock -> nil * * Unlocks global switch so other threads can run. */ static VALUE Unlock(VALUE self) { UNUSED(self); release_lock(); return locker; } /* * call-seq: * Byebug.lock -> Thread.current * * Locks global switch to reserve execution to current thread exclusively. */ static VALUE Lock(VALUE self) { debug_context_t *dc; VALUE context; UNUSED(self); if (!is_living_thread(rb_thread_current())) rb_raise(rb_eRuntimeError, "Current thread is dead!"); thread_context_lookup(rb_thread_current(), &context); Data_Get_Struct(context, debug_context_t, dc); acquire_lock(dc); return locker; } /* * * Document-class: ThreadsTable * * == Sumary * * Hash table holding currently active threads and their associated contexts */ void Init_threads_table(VALUE mByebug) { cThreadsTable = rb_define_class_under(mByebug, "ThreadsTable", rb_cObject); rb_define_module_function(mByebug, "unlock", Unlock, 0); rb_define_module_function(mByebug, "lock", Lock, 0); } ``` ## Future work With the tasks performed in this grant, threading support is finished. The next tasks will be to make sure the feature is working fine for our users and fix any issues that might come up. Regarding Byebug as a whole, the idea is that the next major release will include a full rewrite / review of remote debugging support, and will make sure that editor plugins or graphical debuggers can easily use byebug under the hood. byebug-11.1.1/appveyor.yml000066400000000000000000000030031361243113500154330ustar00rootroot00000000000000--- install: - set PATH=C:\Ruby%ruby_version%\bin;C:\Program Files\Git\cmd;C:/Windows/system32;C:\Program Files\Git\usr\bin - ps: | if ($env:ruby_version -like "*head*") { $(new-object net.webclient).DownloadFile("https://github.com/oneclick/rubyinstaller2/releases/download/rubyinstaller-head/rubyinstaller-$env:ruby_version.exe", "$pwd/ruby-setup.exe") cmd /c ruby-setup.exe /verysilent /dir=C:/Ruby$env:ruby_version } - git clone -q --depth=5 --no-tags --branch=byebug https://github.com/MSP-greg/rb-readline.git C:\rb-readline - ps: $env:ruby_abi_vers = (&ruby.exe -e "puts RbConfig::CONFIG['ruby_version']" | Out-String).Trim() - set n_dir=C:\Ruby%ruby_version%\lib\ruby\site_ruby\%ruby_abi_vers% - attrib.exe -r %n_dir%\*.rb - del /q %n_dir%\readline.rb - del /q %n_dir%\rbreadline.rb - copy C:\rb-readline\lib\readline.rb %n_dir%\readline.rb - copy C:\rb-readline\lib\rbreadline.rb %n_dir%\rbreadline.rb - echo %PATH% - ruby --version - where ruby - gem --version - where gem - bash --version - where bash build_script: - bash -lc bin/setup.sh test_script: - ruby bin/rake compile test environment: matrix: - ruby_version: 24-x64 - ruby_version: 25-x64 - ruby_version: 26-x64 - ruby_version: head-x64 cache: - .bundle matrix: allow_failures: - ruby_version: head-x64 branches: only: - master notifications: - provider: Email on_build_success: false on_build_failure: false on_build_status_changed: true byebug-11.1.1/bin/000077500000000000000000000000001361243113500136175ustar00rootroot00000000000000byebug-11.1.1/bin/bundle000077500000000000000000000013211361243113500150130ustar00rootroot00000000000000#!/usr/bin/env ruby # frozen_string_literal: true require "rubygems" m = Module.new do extend self def gemfile File.expand_path("../Gemfile", __dir__) end def lockfile "#{gemfile}.lock" end def lockfile_version return unless File.file?(lockfile) lockfile_contents = File.read(lockfile) regexp = /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/ regexp.match(lockfile_contents)[1] end def bundler_version @bundler_version ||= lockfile_version end def load_bundler! ENV["BUNDLE_GEMFILE"] ||= gemfile activate_bundler(bundler_version) end def activate_bundler(bundler_version) gem "bundler", bundler_version end end m.load_bundler! byebug-11.1.1/bin/minitest000077500000000000000000000003011361243113500153730ustar00rootroot00000000000000#!/usr/bin/env ruby # frozen_string_literal: true load File.expand_path("bundle", __dir__) require "bundler/setup" require_relative "../test/minitest_runner" Byebug::MinitestRunner.new.run byebug-11.1.1/bin/rake000077500000000000000000000002301361243113500144620ustar00rootroot00000000000000#!/usr/bin/env ruby # frozen_string_literal: true load File.expand_path("bundle", __dir__) require "bundler/setup" load Gem.bin_path("rake", "rake") byebug-11.1.1/bin/rubocop000077500000000000000000000002361361243113500152170ustar00rootroot00000000000000#!/usr/bin/env ruby # frozen_string_literal: true load File.expand_path("bundle", __dir__) require "bundler/setup" load Gem.bin_path("rubocop", "rubocop") byebug-11.1.1/bin/setup.sh000077500000000000000000000002401361243113500153120ustar00rootroot00000000000000#!/usr/bin/env bash set -eo pipefail set +x gem update --system 3.1.2 gem install bundler --version 2.1.4 --force bundle install --jobs 3 --retry 3 set -x byebug-11.1.1/byebug.gemspec000066400000000000000000000021751361243113500156760ustar00rootroot00000000000000# frozen_string_literal: true require_relative "lib/byebug/version" Gem::Specification.new do |s| s.name = "byebug" s.version = Byebug::VERSION s.authors = ["David Rodriguez", "Kent Sibilev", "Mark Moseley"] s.email = "deivid.rodriguez@riseup.net" s.license = "BSD-2-Clause" s.homepage = "https://github.com/deivid-rodriguez/byebug" s.summary = "Ruby fast debugger - base + CLI" s.description = "Byebug is a Ruby debugger. It's implemented using the TracePoint C API for execution control and the Debug Inspector C API for call stack navigation. The core component provides support that front-ends can build on. It provides breakpoint handling and bindings for stack frames among other things and it comes with an easy to use command line interface." s.required_ruby_version = ">= 2.4.0" s.files = Dir["lib/**/*.rb", "lib/**/*.yml", "ext/**/*.[ch]", "LICENSE"] s.bindir = "exe" s.executables = ["byebug"] s.extra_rdoc_files = %w[CHANGELOG.md CONTRIBUTING.md README.md GUIDE.md] s.extensions = ["ext/byebug/extconf.rb"] s.require_path = "lib" s.add_development_dependency "bundler", "~> 2.0" end byebug-11.1.1/code_of_conduct.md000066400000000000000000000062501361243113500165110ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at deivid.rodriguez@riseup.net. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version] [homepage]: https://contributor-covenant.org [version]: https://contributor-covenant.org/version/1/4/ byebug-11.1.1/docker/000077500000000000000000000000001361243113500143165ustar00rootroot00000000000000byebug-11.1.1/docker/Dockerfile000066400000000000000000000046221361243113500163140ustar00rootroot00000000000000FROM alpine:3.10 # skip installing gem documentation RUN mkdir -p /usr/local/etc \ && { \ echo "install: --no-document"; \ echo "update: --no-document"; \ } >> /usr/local/etc/gemrc ARG ruby_download_url= ARG ruby_download_sha256= ARG line_edit_lib= ARG line_edit_config= ARG compiler= ENV CC $compiler RUN set -ex \ \ && baseDeps=" \ bash \ ca-certificates \ cmake \ clang \ gcc \ libffi-dev \ libressl-dev \ make \ " \ && buildOnlyDeps=" \ autoconf \ bison \ build-base \ bzip2-dev \ dpkg \ dpkg-dev \ gdbm-dev \ glib-dev \ libc-dev \ libxml2-dev \ libxslt-dev \ $line_edit_lib \ linux-headers \ ncurses-dev \ ruby \ " \ && apk add --no-cache --virtual .ruby-builddeps $baseDeps $buildOnlyDeps \ \ && wget -O ruby.tar.xz "$ruby_download_url" \ && if [ "$ruby_download_sha256" != "" ]; then echo "$ruby_download_sha256 *ruby.tar.xz" | sha256sum -c -; fi \ \ && mkdir -p /usr/src/ruby \ && tar -xJf ruby.tar.xz -C /usr/src/ruby --strip-components=1 \ && rm ruby.tar.xz \ \ && cd /usr/src/ruby \ \ && autoconf \ && gnuArch="$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)" \ && ./configure \ --disable-werror \ --build="$gnuArch" \ --disable-install-doc \ $line_edit_config \ --enable-shared \ && make -j "$(nproc)" \ && make install \ \ && libDeps="$( \ scanelf --needed --nobanner --format '%n#p' --recursive /usr/local \ | tr ',' '\n' \ | sort -u \ | awk 'system("[ -e /usr/local/lib/" $1 " ]") == 0 { next } { print "so:" $1 }' \ )" \ && runOnlyDeps=" \ $libDeps \ binutils \ git \ libc-dev \ libc6-compat \ " \ && apk add --virtual .ruby-rundeps $baseDeps $runOnlyDeps \ && apk del .ruby-builddeps \ && cd / \ && rm -r /usr/src/ruby RUN SHELLCHECK_VERSION=v0.5.0 \ && wget "https://storage.googleapis.com/shellcheck/shellcheck-$SHELLCHECK_VERSION.linux.x86_64.tar.xz" \ && tar -xJf shellcheck-$SHELLCHECK_VERSION.linux.x86_64.tar.xz \ && cp shellcheck-$SHELLCHECK_VERSION/shellcheck /usr/bin/ RUN ln -s clang-format /usr/bin/clang-format-8 ENV BUNDLE_SILENCE_ROOT_WARNING=1 RUN TEST_REPORTER_URL=https://codeclimate.com/downloads/test-reporter/test-reporter-0.6.0-linux-amd64 \ && wget -O /usr/local/bin/cc-test-reporter $TEST_REPORTER_URL \ && chmod +x /usr/local/bin/cc-test-reporter WORKDIR /byebug byebug-11.1.1/docker/manager.rb000066400000000000000000000103351361243113500162570ustar00rootroot00000000000000# frozen_string_literal: true require "net/http" require "yaml" require "open3" module Docker # # Manages byebug docker images # class Manager VERSIONS = %w[ 2.4.9 2.5.7 2.6.5 2.7.0 ].freeze LINE_EDITORS = %w[ readline libedit ].freeze COMPILERS = %w[ gcc clang ].freeze attr_reader :version, :line_editor, :compiler def initialize(version:, line_editor:, compiler:) @version = version @line_editor = line_editor @compiler = compiler end def login self.class.login end def build command = <<-COMMAND docker build \ --tag "#{tag}" \ --build-arg "ruby_download_url=#{download_url}" \ --build-arg "ruby_download_sha256=#{download_sha256}" \ --build-arg "compiler=#{compiler}" \ --build-arg "line_edit_lib=#{line_editor_package}" \ --build-arg "line_edit_config=#{line_editor_configure_flag}" \ --file "docker/Dockerfile" \ . COMMAND print "Building image #{tag} via `#{squish(command)}`... " run(command) end def test command = <<-COMMAND docker run --rm -v$(pwd):/byebug #{tag} bash -c 'bin/setup.sh && bin/rake' COMMAND print "Testing image #{tag}: #{squish(command)} " run(command) end def push print "Pushing image #{tag}... " run("docker push #{tag}") end class << self def build_default for_last_version_variants(&:build) end def test_default for_last_version_variants(&:test) end def push_default login for_last_version_variants(&:push) end def build_all for_all_images(&:build) end def test_all for_all_images(&:test) end def push_all login for_all_images(&:push) end def release_info @release_info ||= YAML.safe_load( Net::HTTP.get(URI.parse(releases_url)), [Date] ) end def run(*command) output, status = Open3.capture2e(*command) success = status.success? puts(success ? "✔" : "❌") return if success puts output abort end def login command = %W[ docker login -u #{ENV['DOCKER_USER']} -p #{ENV['DOCKER_PASS']} ] print "Logging in to dockerhub... " run(*command) end private def releases_url "https://raw.githubusercontent.com/ruby/www.ruby-lang.org/master/_data/releases.yml" end def for_all_images(&block) VERSIONS.each do |version| for_variants_of(version, &block) end end def for_last_version_variants(&block) for_variants_of(VERSIONS.last, &block) end def for_variants_of(version) COMPILERS.each do |compiler| LINE_EDITORS.each do |line_editor| manager = new( version: version, line_editor: line_editor, compiler: compiler ) yield(manager) end end end end private def line_editor_package line_editor == "readline" ? "readline-dev" : "libedit-dev" end def line_editor_configure_flag line_editor == "readline" ? "" : "--enable-libedit" end def download_url if version == "head" "#{download_url_base}/snapshot.tar.xz" else "#{download_url_base}/#{abi_version}/ruby-#{version}.tar.xz" end end def abi_version version.split(".")[0..1].join(".") end def download_url_base "https://cache.ruby-lang.org/pub/ruby" end def release_info self.class.release_info end def download_sha256 version_info = release_info.find { |entry| entry["version"] == version } return unless version_info version_info["sha256"]["xz"] end def run(*command) self.class.run(*command) end def tag "deividrodriguez/byebug:#{version}-#{line_editor}-#{compiler}" end def squish(command) command.gsub(/[\n ]+/, " ").strip end end end byebug-11.1.1/exe/000077500000000000000000000000001361243113500136305ustar00rootroot00000000000000byebug-11.1.1/exe/byebug000077500000000000000000000001631361243113500150330ustar00rootroot00000000000000#!/usr/bin/env ruby # frozen_string_literal: true require_relative "../lib/byebug/runner" Byebug::Runner.new.run byebug-11.1.1/ext/000077500000000000000000000000001361243113500136475ustar00rootroot00000000000000byebug-11.1.1/ext/byebug/000077500000000000000000000000001361243113500151245ustar00rootroot00000000000000byebug-11.1.1/ext/byebug/breakpoint.c000066400000000000000000000270251361243113500174340ustar00rootroot00000000000000#include "byebug.h" #ifdef _WIN32 #include #endif #if defined DOSISH #define isdirsep(x) ((x) == '/' || (x) == '\\') #else #define isdirsep(x) ((x) == '/') #endif static VALUE cBreakpoint; static int breakpoint_max; static ID idEval; static VALUE eval_expression(VALUE args) { return rb_funcall2(rb_mKernel, idEval, 2, RARRAY_PTR(args)); } /* * call-seq: * breakpoint.enabled? -> bool * * Returns +true+ if breakpoint is enabled, false otherwise. */ static VALUE brkpt_enabled(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return breakpoint->enabled; } /* * call-seq: * breakpoint.enabled = true | false * * Enables or disables breakpoint. */ static VALUE brkpt_set_enabled(VALUE self, VALUE enabled) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return breakpoint->enabled = enabled; } /* * call-seq: * breakpoint.expr -> string * * Returns a conditional expression which indicates when this breakpoint should * be activated. */ static VALUE brkpt_expr(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return breakpoint->expr; } /* * call-seq: * breakpoint.expr = string | nil * * Sets or unsets the conditional expression which indicates when this * breakpoint should be activated. */ static VALUE brkpt_set_expr(VALUE self, VALUE expr) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); breakpoint->expr = NIL_P(expr) ? expr : StringValue(expr); return expr; } /* * call-seq: * breakpoint.hit_condition -> symbol * * Returns the hit condition of the breakpoint: +nil+ if it is an * unconditional breakpoint, or :greater_or_equal, :equal or :modulo otherwise */ static VALUE brkpt_hit_condition(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); switch (breakpoint->hit_condition) { case HIT_COND_GE: return ID2SYM(rb_intern("greater_or_equal")); case HIT_COND_EQ: return ID2SYM(rb_intern("equal")); case HIT_COND_MOD: return ID2SYM(rb_intern("modulo")); case HIT_COND_NONE: default: return Qnil; } } /* * call-seq: * breakpoint.hit_condition = symbol * * Sets the hit condition of the breakpoint which must be one of the following * values: * * +nil+ if it is an unconditional breakpoint, or * :greater_or_equal(:ge), :equal(:eq), :modulo(:mod) */ static VALUE brkpt_set_hit_condition(VALUE self, VALUE value) { breakpoint_t *breakpoint; ID id_value; Data_Get_Struct(self, breakpoint_t, breakpoint); id_value = rb_to_id(value); if (rb_intern("greater_or_equal") == id_value || rb_intern("ge") == id_value) breakpoint->hit_condition = HIT_COND_GE; else if (rb_intern("equal") == id_value || rb_intern("eq") == id_value) breakpoint->hit_condition = HIT_COND_EQ; else if (rb_intern("modulo") == id_value || rb_intern("mod") == id_value) breakpoint->hit_condition = HIT_COND_MOD; else rb_raise(rb_eArgError, "Invalid condition parameter"); return value; } /* * call-seq: * breakpoint.hit_count -> int * * Returns the number of times this breakpoint has been hit. */ static VALUE brkpt_hit_count(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return INT2FIX(breakpoint->hit_count); } /* * call-seq: * breakpoint.hit_value -> int * * Returns the hit value of the breakpoint, namely, a value to build a * condition on the number of hits of the breakpoint. */ static VALUE brkpt_hit_value(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return INT2FIX(breakpoint->hit_value); } /* * call-seq: * breakpoint.hit_value = int * * Sets the hit value of the breakpoint. This allows the user to set conditions * on the number of hits to enable/disable the breakpoint. */ static VALUE brkpt_set_hit_value(VALUE self, VALUE value) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); breakpoint->hit_value = FIX2INT(value); return value; } /* * call-seq: * breakpoint.id -> int * * Returns the id of the breakpoint. */ static VALUE brkpt_id(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return INT2FIX(breakpoint->id); } /* * call-seq: * breakpoint.pos -> string or int * * Returns the position of this breakpoint, either a method name or a line * number. */ static VALUE brkpt_pos(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); if (breakpoint->type == BP_METHOD_TYPE) return rb_str_new2(rb_id2name(breakpoint->pos.mid)); else return INT2FIX(breakpoint->pos.line); } /* * call-seq: * breakpoint.source -> string * * Returns the source file of the breakpoint. */ static VALUE brkpt_source(VALUE self) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); return breakpoint->source; } static void mark_breakpoint(breakpoint_t *breakpoint) { rb_gc_mark(breakpoint->source); rb_gc_mark(breakpoint->expr); } static VALUE brkpt_create(VALUE klass) { breakpoint_t *breakpoint = ALLOC(breakpoint_t); return Data_Wrap_Struct(klass, mark_breakpoint, xfree, breakpoint); } static VALUE brkpt_initialize(VALUE self, VALUE source, VALUE pos, VALUE expr) { breakpoint_t *breakpoint; Data_Get_Struct(self, breakpoint_t, breakpoint); breakpoint->type = FIXNUM_P(pos) ? BP_POS_TYPE : BP_METHOD_TYPE; if (breakpoint->type == BP_POS_TYPE) breakpoint->pos.line = FIX2INT(pos); else breakpoint->pos.mid = SYM2ID(pos); breakpoint->id = ++breakpoint_max; breakpoint->source = StringValue(source); breakpoint->enabled = Qtrue; breakpoint->expr = NIL_P(expr) ? expr : StringValue(expr); breakpoint->hit_count = 0; breakpoint->hit_value = 0; breakpoint->hit_condition = HIT_COND_NONE; return Qnil; } static int filename_cmp_impl(VALUE source, char *file) { char *source_ptr, *file_ptr; long s_len, f_len, min_len; long s, f; int dirsep_flag = 0; s_len = RSTRING_LEN(source); f_len = strlen(file); min_len = s_len < f_len ? s_len : f_len; source_ptr = RSTRING_PTR(source); file_ptr = file; for (s = s_len - 1, f = f_len - 1; s >= s_len - min_len && f >= f_len - min_len; s--, f--) { if ((source_ptr[s] == '.' || file_ptr[f] == '.') && dirsep_flag) return 1; if (isdirsep(source_ptr[s]) && isdirsep(file_ptr[f])) dirsep_flag = 1; #ifdef DOSISH_DRIVE_LETTER else if (s == 0) return (toupper(source_ptr[s]) == toupper(file_ptr[f])); #endif else if (source_ptr[s] != file_ptr[f]) return 0; } return 1; } static int filename_cmp(VALUE source, char *file) { #ifdef _WIN32 return filename_cmp_impl(source, file); #else #ifdef PATH_MAX char path[PATH_MAX + 1]; path[PATH_MAX] = 0; return filename_cmp_impl(source, realpath(file, path) != NULL ? path : file); #else char *path; int result; path = realpath(file, NULL); result = filename_cmp_impl(source, path == NULL ? file : path); free(path); return result; #endif #endif } static int classname_cmp(VALUE name, VALUE klass) { VALUE mod_name; VALUE class_name = NIL_P(name) ? rb_str_new2("main") : name; if (NIL_P(klass)) return 0; mod_name = rb_mod_name(klass); return (!NIL_P(mod_name) && rb_str_cmp(class_name, mod_name) == 0); } static int check_breakpoint_by_hit_condition(VALUE rb_breakpoint) { breakpoint_t *breakpoint; if (NIL_P(rb_breakpoint)) return 0; Data_Get_Struct(rb_breakpoint, breakpoint_t, breakpoint); breakpoint->hit_count++; if (Qtrue != breakpoint->enabled) return 0; switch (breakpoint->hit_condition) { case HIT_COND_NONE: return 1; case HIT_COND_GE: { if (breakpoint->hit_count >= breakpoint->hit_value) return 1; break; } case HIT_COND_EQ: { if (breakpoint->hit_count == breakpoint->hit_value) return 1; break; } case HIT_COND_MOD: { if (breakpoint->hit_count % breakpoint->hit_value == 0) return 1; break; } } return 0; } static int check_breakpoint_by_pos(VALUE rb_breakpoint, char *file, int line) { breakpoint_t *breakpoint; if (NIL_P(rb_breakpoint)) return 0; Data_Get_Struct(rb_breakpoint, breakpoint_t, breakpoint); if (Qfalse == breakpoint->enabled || breakpoint->type != BP_POS_TYPE || breakpoint->pos.line != line) return 0; return filename_cmp(breakpoint->source, file); } static int check_breakpoint_by_method(VALUE rb_breakpoint, VALUE klass, ID mid, VALUE self) { breakpoint_t *breakpoint; if (NIL_P(rb_breakpoint)) return 0; Data_Get_Struct(rb_breakpoint, breakpoint_t, breakpoint); if (Qfalse == breakpoint->enabled || breakpoint->type != BP_METHOD_TYPE || breakpoint->pos.mid != mid) return 0; if (classname_cmp(breakpoint->source, klass) || ((rb_type(self) == T_CLASS || rb_type(self) == T_MODULE) && classname_cmp(breakpoint->source, self))) return 1; return 0; } static int check_breakpoint_by_expr(VALUE rb_breakpoint, VALUE bind) { breakpoint_t *breakpoint; VALUE args, expr_result; if (NIL_P(rb_breakpoint)) return 0; Data_Get_Struct(rb_breakpoint, breakpoint_t, breakpoint); if (Qfalse == breakpoint->enabled) return 0; if (NIL_P(breakpoint->expr)) return 1; args = rb_ary_new3(2, breakpoint->expr, bind); expr_result = rb_protect(eval_expression, args, 0); return RTEST(expr_result); } extern VALUE find_breakpoint_by_pos(VALUE breakpoints, VALUE source, VALUE pos, VALUE bind) { VALUE breakpoint; char *file; int line; int i; file = RSTRING_PTR(source); line = FIX2INT(pos); for (i = 0; i < RARRAY_LENINT(breakpoints); i++) { breakpoint = rb_ary_entry(breakpoints, i); if (check_breakpoint_by_pos(breakpoint, file, line) && check_breakpoint_by_expr(breakpoint, bind) && check_breakpoint_by_hit_condition(breakpoint)) { return breakpoint; } } return Qnil; } extern VALUE find_breakpoint_by_method(VALUE breakpoints, VALUE klass, ID mid, VALUE bind, VALUE self) { VALUE breakpoint; int i; for (i = 0; i < RARRAY_LENINT(breakpoints); i++) { breakpoint = rb_ary_entry(breakpoints, i); if (check_breakpoint_by_method(breakpoint, klass, mid, self) && check_breakpoint_by_expr(breakpoint, bind) && check_breakpoint_by_hit_condition(breakpoint)) { return breakpoint; } } return Qnil; } void Init_byebug_breakpoint(VALUE mByebug) { breakpoint_max = 0; cBreakpoint = rb_define_class_under(mByebug, "Breakpoint", rb_cObject); rb_define_alloc_func(cBreakpoint, brkpt_create); rb_define_method(cBreakpoint, "initialize", brkpt_initialize, 3); rb_define_method(cBreakpoint, "enabled?", brkpt_enabled, 0); rb_define_method(cBreakpoint, "enabled=", brkpt_set_enabled, 1); rb_define_method(cBreakpoint, "expr", brkpt_expr, 0); rb_define_method(cBreakpoint, "expr=", brkpt_set_expr, 1); rb_define_method(cBreakpoint, "hit_count", brkpt_hit_count, 0); rb_define_method(cBreakpoint, "hit_condition", brkpt_hit_condition, 0); rb_define_method(cBreakpoint, "hit_condition=", brkpt_set_hit_condition, 1); rb_define_method(cBreakpoint, "hit_value", brkpt_hit_value, 0); rb_define_method(cBreakpoint, "hit_value=", brkpt_set_hit_value, 1); rb_define_method(cBreakpoint, "id", brkpt_id, 0); rb_define_method(cBreakpoint, "pos", brkpt_pos, 0); rb_define_method(cBreakpoint, "source", brkpt_source, 0); idEval = rb_intern("eval"); } byebug-11.1.1/ext/byebug/byebug.c000066400000000000000000000474321361243113500165570ustar00rootroot00000000000000#include "byebug.h" static VALUE mByebug; /* Ruby Byebug Module object */ static VALUE tracing = Qfalse; static VALUE post_mortem = Qfalse; static VALUE verbose = Qfalse; static VALUE catchpoints = Qnil; static VALUE breakpoints = Qnil; static VALUE tracepoints = Qnil; static VALUE raised_exception = Qnil; static ID idPuts; static ID idEmpty; /* Hash table with active threads and their associated contexts */ VALUE threads = Qnil; /* * call-seq: * Byebug.breakpoints -> array * * Returns an array of breakpoints. */ static VALUE Breakpoints(VALUE self) { UNUSED(self); if (NIL_P(breakpoints)) breakpoints = rb_ary_new(); return breakpoints; } /* * call-seq: * Byebug.catchpoints -> hash * * Returns the catchpoints hash. */ static VALUE Catchpoints(VALUE self) { UNUSED(self); return catchpoints; } /* * call-seq: * Byebug.raised_exception -> exception * * Returns raised exception when in post_mortem mode. */ static VALUE Raised_exception(VALUE self) { UNUSED(self); return raised_exception; } #define IS_STARTED (!NIL_P(catchpoints)) static void check_started() { if (!IS_STARTED) { rb_raise(rb_eRuntimeError, "Byebug is not started yet."); } } static void trace_print(rb_trace_arg_t *trace_arg, debug_context_t *dc, const char *file_filter, const char *debug_msg) { char *fullpath = NULL; const char *basename; int filtered = 0; const char *event = rb_id2name(SYM2ID(rb_tracearg_event(trace_arg))); VALUE rb_path = rb_tracearg_path(trace_arg); const char *path = NIL_P(rb_path) ? "" : RSTRING_PTR(rb_path); int line = NUM2INT(rb_tracearg_lineno(trace_arg)); VALUE rb_mid = rb_tracearg_method_id(trace_arg); const char *mid = NIL_P(rb_mid) ? "(top level)" : rb_id2name(SYM2ID(rb_mid)); VALUE rb_cl = rb_tracearg_defined_class(trace_arg); VALUE rb_cl_name = NIL_P(rb_cl) ? rb_cl : rb_mod_name(rb_cl); const char *defined_class = NIL_P(rb_cl_name) ? "" : RSTRING_PTR(rb_cl_name); if (!trace_arg) return; if (file_filter) { #ifndef _WIN32 fullpath = realpath(path, NULL); #endif basename = fullpath ? strrchr(fullpath, '/') : path; if (!basename || strncmp(basename + 1, file_filter, strlen(file_filter))) filtered = 1; #ifndef _WIN32 free(fullpath); #endif } if (!filtered) { if (debug_msg) rb_funcall(mByebug, idPuts, 1, rb_sprintf("[#%d] %s\n", dc->thnum, debug_msg)); else rb_funcall(mByebug, idPuts, 1, rb_sprintf("%*s [#%d] %s@%s:%d %s#%s\n", dc->calced_stack_size, "", dc->thnum, event, path, line, defined_class, mid)); } } static void cleanup(debug_context_t *dc) { dc->stop_reason = CTX_STOP_NONE; release_lock(); } #define EVENT_TEARDOWN cleanup(dc); #define EVENT_SETUP \ debug_context_t *dc; \ VALUE context; \ rb_trace_arg_t *trace_arg; \ \ UNUSED(data); \ \ if (!is_living_thread(rb_thread_current())) \ return; \ \ thread_context_lookup(rb_thread_current(), &context); \ Data_Get_Struct(context, debug_context_t, dc); \ \ trace_arg = rb_tracearg_from_tracepoint(trace_point); \ if (verbose == Qtrue) \ trace_print(trace_arg, dc, 0, 0); \ \ if (CTX_FL_TEST(dc, CTX_FL_IGNORE)) \ return; \ \ acquire_lock(dc); #define CALL_EVENT_SETUP \ dc->calced_stack_size++; \ dc->steps_out = dc->steps_out < 0 ? -1 : dc->steps_out + 1; #define RETURN_EVENT_SETUP \ dc->calced_stack_size--; \ \ if (dc->steps_out == 1) \ dc->steps = 1; #define RETURN_EVENT_TEARDOWN \ dc->steps_out = dc->steps_out <= 0 ? -1 : dc->steps_out - 1; /* Functions that return control to byebug after the different events */ static VALUE call_at(VALUE ctx, debug_context_t *dc, ID mid, int argc, VALUE arg) { struct call_with_inspection_data cwi; VALUE argv[1]; argv[0] = arg; cwi.dc = dc; cwi.ctx = ctx; cwi.id = mid; cwi.argc = argc; cwi.argv = &argv[0]; return call_with_debug_inspector(&cwi); } static VALUE call_at_line(VALUE ctx, debug_context_t *dc) { return call_at(ctx, dc, rb_intern("at_line"), 0, Qnil); } static VALUE call_at_tracing(VALUE ctx, debug_context_t *dc) { return call_at(ctx, dc, rb_intern("at_tracing"), 0, Qnil); } static VALUE call_at_breakpoint(VALUE ctx, debug_context_t *dc, VALUE breakpoint) { dc->stop_reason = CTX_STOP_BREAKPOINT; return call_at(ctx, dc, rb_intern("at_breakpoint"), 1, breakpoint); } static VALUE call_at_catchpoint(VALUE ctx, debug_context_t *dc, VALUE exp) { dc->stop_reason = CTX_STOP_CATCHPOINT; return call_at(ctx, dc, rb_intern("at_catchpoint"), 1, exp); } static VALUE call_at_return(VALUE ctx, debug_context_t *dc, VALUE return_value) { dc->stop_reason = CTX_STOP_BREAKPOINT; return call_at(ctx, dc, rb_intern("at_return"), 1, return_value); } static VALUE call_at_end(VALUE ctx, debug_context_t *dc) { dc->stop_reason = CTX_STOP_BREAKPOINT; return call_at(ctx, dc, rb_intern("at_end"), 0, Qnil); } static void call_at_line_check(VALUE ctx, debug_context_t *dc, VALUE breakpoint) { dc->stop_reason = CTX_STOP_STEP; if (!NIL_P(breakpoint)) call_at_breakpoint(ctx, dc, breakpoint); byebug_reset_stepping_stop_points(dc); call_at_line(ctx, dc); } /* TracePoint API event handlers */ static void line_event(VALUE trace_point, void *data) { VALUE brkpnt, file, line, binding; EVENT_SETUP; file = rb_tracearg_path(trace_arg); line = rb_tracearg_lineno(trace_arg); binding = rb_tracearg_binding(trace_arg); if (RTEST(tracing)) call_at_tracing(context, dc); if (!CTX_FL_TEST(dc, CTX_FL_IGNORE_STEPS)) dc->steps = dc->steps <= 0 ? -1 : dc->steps - 1; if (dc->calced_stack_size <= dc->dest_frame) { dc->dest_frame = dc->calced_stack_size; CTX_FL_UNSET(dc, CTX_FL_IGNORE_STEPS); dc->lines = dc->lines <= 0 ? -1 : dc->lines - 1; } if (dc->steps == 0 || dc->lines == 0) call_at_line_check(context, dc, Qnil); else { brkpnt = Qnil; if (!NIL_P(breakpoints)) brkpnt = find_breakpoint_by_pos(breakpoints, file, line, binding); if (!NIL_P(brkpnt)) call_at_line_check(context, dc, brkpnt); } EVENT_TEARDOWN; } static void call_event(VALUE trace_point, void *data) { VALUE brkpnt, klass, msym, mid, binding, self; EVENT_SETUP; if (dc->calced_stack_size <= dc->dest_frame) CTX_FL_UNSET(dc, CTX_FL_IGNORE_STEPS); CALL_EVENT_SETUP; msym = rb_tracearg_method_id(trace_arg); mid = SYM2ID(msym); klass = rb_tracearg_defined_class(trace_arg); binding = rb_tracearg_binding(trace_arg); self = rb_tracearg_self(trace_arg); brkpnt = Qnil; if (!NIL_P(breakpoints)) brkpnt = find_breakpoint_by_method(breakpoints, klass, mid, binding, self); if (!NIL_P(brkpnt)) { call_at_breakpoint(context, dc, brkpnt); call_at_line(context, dc); } EVENT_TEARDOWN; } static void return_event(VALUE trace_point, void *data) { VALUE brkpnt, file, line, binding; EVENT_SETUP; RETURN_EVENT_SETUP; if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET))) { byebug_reset_stepping_stop_points(dc); call_at_return(context, dc, rb_tracearg_return_value(trace_arg)); } else if (!NIL_P(breakpoints)) { file = rb_tracearg_path(trace_arg); /* * @todo Sometimes the TracePoint API gives some return events without * file:line information, so we need to guard for nil until we know what's * going on. This happens, for example, with active_support core extensions: * * [#7] call@.../core_ext/numeric/conversions.rb:124 Fixnum#to_s * [#7] b_call@.../core_ext/numeric/conversions.rb:124 BigDecimal#to_s * [#7] line@.../core_ext/numeric/conversions.rb:125 BigDecimal#to_s * [#7] c_call@.../core_ext/numeric/conversions.rb:125 Kernel#is_a? * [#7] c_return@.../core_ext/numeric/conversions.rb:125 Kernel#is_a? * [#7] line@.../core_ext/numeric/conversions.rb:131 BigDecimal#to_s * [#7] c_call@.../core_ext/numeric/conversions.rb:131 Fixnum#to_default_s * [#7] c_return@.../core_ext/numeric/conversions.rb:131 Fixnum#to_default_s * [#7] b_return@/hort/core_ext/numeric/conversions.rb:133 BigDecimal#to_s * [#7] return@:0 Fixnum#to_s # => This guy... */ if (NIL_P(file)) rb_warn("The TracePoint API emitted a return event without file information. It might be a bug, please report this."); else { line = rb_tracearg_lineno(trace_arg); binding = rb_tracearg_binding(trace_arg); brkpnt = find_breakpoint_by_pos(breakpoints, file, line, binding); if (!NIL_P(brkpnt)) call_at_return(context, dc, rb_tracearg_return_value(trace_arg)); } } RETURN_EVENT_TEARDOWN; EVENT_TEARDOWN; } static void end_event(VALUE trace_point, void *data) { EVENT_SETUP; RETURN_EVENT_SETUP; if ((dc->steps_out == 0) && (CTX_FL_TEST(dc, CTX_FL_STOP_ON_RET))) { byebug_reset_stepping_stop_points(dc); call_at_end(context, dc); } RETURN_EVENT_TEARDOWN; EVENT_TEARDOWN; } static void raw_call_event(VALUE trace_point, void *data) { EVENT_SETUP; CALL_EVENT_SETUP; EVENT_TEARDOWN; } static void raw_return_event(VALUE trace_point, void *data) { EVENT_SETUP; RETURN_EVENT_SETUP; RETURN_EVENT_TEARDOWN; EVENT_TEARDOWN; } static void raise_event(VALUE trace_point, void *data) { VALUE expn_class, ancestors, pm_context; int i; debug_context_t *new_dc; EVENT_SETUP; raised_exception = rb_tracearg_raised_exception(trace_arg); if (post_mortem == Qtrue && !rb_ivar_defined(raised_exception, rb_intern("@__bb_context"))) { pm_context = context_dup(dc); rb_ivar_set(raised_exception, rb_intern("@__bb_context"), pm_context); Data_Get_Struct(pm_context, debug_context_t, new_dc); rb_debug_inspector_open(context_backtrace_set, (void *)new_dc); } if (NIL_P(catchpoints) || dc->calced_stack_size == 0 || RHASH_TBL(catchpoints)->num_entries == 0) { EVENT_TEARDOWN; return; } expn_class = rb_obj_class(raised_exception); ancestors = rb_mod_ancestors(expn_class); for (i = 0; i < RARRAY_LENINT(ancestors); i++) { VALUE ancestor_class, module_name, hit_count; ancestor_class = rb_ary_entry(ancestors, i); module_name = rb_mod_name(ancestor_class); hit_count = rb_hash_aref(catchpoints, module_name); /* increment exception */ if (!NIL_P(hit_count)) { rb_hash_aset(catchpoints, module_name, INT2FIX(FIX2INT(hit_count) + 1)); call_at_catchpoint(context, dc, raised_exception); call_at_line(context, dc); break; } } EVENT_TEARDOWN; } /* Setup TracePoint functionality */ static void register_tracepoints(VALUE self) { int i; VALUE traces = tracepoints; UNUSED(self); if (NIL_P(traces)) { int line_msk = RUBY_EVENT_LINE; int call_msk = RUBY_EVENT_CALL; int ret_msk = RUBY_EVENT_RETURN | RUBY_EVENT_B_RETURN; int end_msk = RUBY_EVENT_END; int raw_call_msk = RUBY_EVENT_C_CALL | RUBY_EVENT_B_CALL | RUBY_EVENT_CLASS; int raw_ret_msk = RUBY_EVENT_C_RETURN; int raise_msk = RUBY_EVENT_RAISE; VALUE tpLine = rb_tracepoint_new(Qnil, line_msk, line_event, 0); VALUE tpCall = rb_tracepoint_new(Qnil, call_msk, call_event, 0); VALUE tpReturn = rb_tracepoint_new(Qnil, ret_msk, return_event, 0); VALUE tpEnd = rb_tracepoint_new(Qnil, end_msk, end_event, 0); VALUE tpCCall = rb_tracepoint_new(Qnil, raw_call_msk, raw_call_event, 0); VALUE tpCReturn = rb_tracepoint_new(Qnil, raw_ret_msk, raw_return_event, 0); VALUE tpRaise = rb_tracepoint_new(Qnil, raise_msk, raise_event, 0); traces = rb_ary_new(); rb_ary_push(traces, tpLine); rb_ary_push(traces, tpCall); rb_ary_push(traces, tpReturn); rb_ary_push(traces, tpEnd); rb_ary_push(traces, tpCCall); rb_ary_push(traces, tpCReturn); rb_ary_push(traces, tpRaise); tracepoints = traces; } for (i = 0; i < RARRAY_LENINT(traces); i++) rb_tracepoint_enable(rb_ary_entry(traces, i)); } static void clear_tracepoints(VALUE self) { int i; UNUSED(self); for (i = RARRAY_LENINT(tracepoints) - 1; i >= 0; i--) rb_tracepoint_disable(rb_ary_entry(tracepoints, i)); } /* Byebug's Public API */ /* * call-seq: * Byebug.contexts -> array * * Returns an array of all contexts. */ static VALUE Contexts(VALUE self) { volatile VALUE list; volatile VALUE new_list; VALUE context; threads_table_t *t_tbl; debug_context_t *dc; int i; UNUSED(self); check_started(); new_list = rb_ary_new(); list = rb_funcall(rb_cThread, rb_intern("list"), 0); for (i = 0; i < RARRAY_LENINT(list); i++) { VALUE thread = rb_ary_entry(list, i); thread_context_lookup(thread, &context); rb_ary_push(new_list, context); } Data_Get_Struct(threads, threads_table_t, t_tbl); st_clear(t_tbl->tbl); for (i = 0; i < RARRAY_LENINT(new_list); i++) { context = rb_ary_entry(new_list, i); Data_Get_Struct(context, debug_context_t, dc); st_insert(t_tbl->tbl, dc->thread, context); } return new_list; } /* * call-seq: * Byebug.thread_context(thread) -> context * * Returns context of the thread passed as an argument. */ static VALUE Thread_context(VALUE self, VALUE thread) { VALUE context; UNUSED(self); check_started(); thread_context_lookup(thread, &context); return context; } /* * call-seq: * Byebug.current_context -> context * * Returns the current context. * Note: Byebug.current_context.thread == Thread.current */ static VALUE Current_context(VALUE self) { VALUE context; UNUSED(self); thread_context_lookup(rb_thread_current(), &context); return context; } /* * call-seq: * Byebug.started? -> bool * * Returns +true+ byebug is started. */ static VALUE Started(VALUE self) { UNUSED(self); return IS_STARTED ? Qtrue : Qfalse; } /* * call-seq: * Byebug.stop -> bool * * This method disables byebug. It returns +true+ if byebug was already * disabled, otherwise it returns +false+. */ static VALUE Stop(VALUE self) { UNUSED(self); if (IS_STARTED) { clear_tracepoints(self); breakpoints = Qnil; catchpoints = Qnil; return Qfalse; } return Qtrue; } static VALUE Stoppable(VALUE self) { VALUE context; debug_context_t *dc; if (!IS_STARTED) return Qfalse; if (!NIL_P(breakpoints) && rb_funcall(breakpoints, idEmpty, 0) == Qfalse) return Qfalse; if (!NIL_P(catchpoints) && rb_funcall(catchpoints, idEmpty, 0) == Qfalse) return Qfalse; if (post_mortem == Qtrue) return Qfalse; if (RTEST(tracing)) return Qfalse; context = Current_context(self); if (!NIL_P(context)) { Data_Get_Struct(context, debug_context_t, dc); if (dc->steps > 0) return Qfalse; } return Qtrue; } /* * call-seq: * Byebug.start -> bool * * The return value is the value of !Byebug.started? before issuing the * +start+; That is, +true+ is returned, unless byebug was previously started. */ static VALUE Start(VALUE self) { if (IS_STARTED) return Qfalse; catchpoints = rb_hash_new(); threads = create_threads_table(); register_tracepoints(self); return Qtrue; } /* * call-seq: * Byebug.debug_load(file, stop = false) -> nil * * Same as Kernel#load but resets current context's frames. * +stop+ parameter forces byebug to stop at the first line of code in +file+ */ static VALUE Debug_load(int argc, VALUE *argv, VALUE self) { VALUE file, stop, context; debug_context_t *dc; VALUE status = Qnil; int state = 0; UNUSED(self); if (rb_scan_args(argc, argv, "11", &file, &stop) == 1) stop = Qfalse; Start(self); context = Current_context(self); Data_Get_Struct(context, debug_context_t, dc); dc->calced_stack_size = 1; if (RTEST(stop)) dc->steps = 1; rb_load_protect(file, 0, &state); if (0 != state) { status = rb_errinfo(); byebug_reset_stepping_stop_points(dc); } return status; } /* * call-seq: * Byebug.tracing? -> bool * * Returns +true+ if global tracing is enabled. */ static VALUE Tracing(VALUE self) { UNUSED(self); return tracing; } /* * call-seq: * Byebug.tracing = bool * * Sets the global tracing flag. */ static VALUE Set_tracing(VALUE self, VALUE value) { UNUSED(self); tracing = RTEST(value) ? Qtrue : Qfalse; return value; } /* * call-seq: * Byebug.verbose? -> bool * * Returns +true+ if global verbose flag for TracePoint API events is enabled. */ static VALUE Verbose(VALUE self) { UNUSED(self); return verbose; } /* * call-seq: * Byebug.verbose = bool * * Sets the global verbose flag for TracePoint API events is enabled. */ static VALUE Set_verbose(VALUE self, VALUE value) { UNUSED(self); verbose = RTEST(value) ? Qtrue : Qfalse; return value; } /* * call-seq: * Byebug.post_mortem? -> bool * * Returns +true+ if post-mortem debugging is enabled. */ static VALUE Post_mortem(VALUE self) { UNUSED(self); return post_mortem; } /* * call-seq: * Byebug.post_mortem = bool * * Sets post-moterm flag. */ static VALUE Set_post_mortem(VALUE self, VALUE value) { UNUSED(self); post_mortem = RTEST(value) ? Qtrue : Qfalse; return value; } /* * call-seq: * Byebug.add_catchpoint(exception) -> exception * * Adds a new exception to the catchpoints hash. */ static VALUE Add_catchpoint(VALUE self, VALUE value) { UNUSED(self); if (TYPE(value) != T_STRING) rb_raise(rb_eTypeError, "value of a catchpoint must be String"); rb_hash_aset(catchpoints, rb_str_dup(value), INT2FIX(0)); return value; } /* * Document-class: Byebug * * == Summary * * This is a singleton class allows controlling byebug. Use it to start/stop * byebug, set/remove breakpoints, etc. */ void Init_byebug() { mByebug = rb_define_module("Byebug"); rb_define_module_function(mByebug, "add_catchpoint", Add_catchpoint, 1); rb_define_module_function(mByebug, "breakpoints", Breakpoints, 0); rb_define_module_function(mByebug, "catchpoints", Catchpoints, 0); rb_define_module_function(mByebug, "contexts", Contexts, 0); rb_define_module_function(mByebug, "current_context", Current_context, 0); rb_define_module_function(mByebug, "debug_load", Debug_load, -1); rb_define_module_function(mByebug, "post_mortem?", Post_mortem, 0); rb_define_module_function(mByebug, "post_mortem=", Set_post_mortem, 1); rb_define_module_function(mByebug, "raised_exception", Raised_exception, 0); rb_define_module_function(mByebug, "start", Start, 0); rb_define_module_function(mByebug, "started?", Started, 0); rb_define_module_function(mByebug, "stop", Stop, 0); rb_define_module_function(mByebug, "stoppable?", Stoppable, 0); rb_define_module_function(mByebug, "thread_context", Thread_context, 1); rb_define_module_function(mByebug, "tracing?", Tracing, 0); rb_define_module_function(mByebug, "tracing=", Set_tracing, 1); rb_define_module_function(mByebug, "verbose?", Verbose, 0); rb_define_module_function(mByebug, "verbose=", Set_verbose, 1); Init_threads_table(mByebug); Init_byebug_context(mByebug); Init_byebug_breakpoint(mByebug); rb_global_variable(&breakpoints); rb_global_variable(&catchpoints); rb_global_variable(&tracepoints); rb_global_variable(&raised_exception); rb_global_variable(&threads); idPuts = rb_intern("puts"); idEmpty = rb_intern("empty?"); } byebug-11.1.1/ext/byebug/byebug.h000066400000000000000000000070361361243113500165600ustar00rootroot00000000000000#ifndef BYEBUG #define BYEBUG #include #include /* To prevent unused parameter warnings */ #define UNUSED(x) (void)(x) /* flags */ #define CTX_FL_DEAD (1 << 1) /* this context belonged to a dead thread */ #define CTX_FL_IGNORE (1 << 2) /* this context belongs to ignored thread */ #define CTX_FL_SUSPEND (1 << 3) /* thread currently suspended */ #define CTX_FL_TRACING (1 << 4) /* call at_tracing method */ #define CTX_FL_WAS_RUNNING (1 << 5) /* thread was previously running */ #define CTX_FL_STOP_ON_RET (1 << 6) /* can stop on method 'end' */ #define CTX_FL_IGNORE_STEPS (1 << 7) /* doesn't countdown steps to break */ /* macro functions */ #define CTX_FL_TEST(c, f) ((c)->flags & (f)) #define CTX_FL_SET(c, f) \ do \ { \ (c)->flags |= (f); \ } while (0) #define CTX_FL_UNSET(c, f) \ do \ { \ (c)->flags &= ~(f); \ } while (0) /* types */ typedef enum { CTX_STOP_NONE, CTX_STOP_STEP, CTX_STOP_BREAKPOINT, CTX_STOP_CATCHPOINT } ctx_stop_reason; typedef struct { int calced_stack_size; int flags; ctx_stop_reason stop_reason; VALUE thread; int thnum; int dest_frame; /* next stop's frame if stopped by next */ int lines; /* # of lines in dest_frame before stopping */ int steps; /* # of steps before stopping */ int steps_out; /* # of returns before stopping */ VALUE backtrace; /* [[loc, self, klass, binding], ...] */ } debug_context_t; typedef enum { LOCATION, SELF, CLASS, BINDING } frame_part; struct call_with_inspection_data { debug_context_t *dc; VALUE ctx; ID id; int argc; VALUE *argv; }; typedef struct { st_table *tbl; } threads_table_t; enum bp_type { BP_POS_TYPE, BP_METHOD_TYPE }; enum hit_condition { HIT_COND_NONE, HIT_COND_GE, HIT_COND_EQ, HIT_COND_MOD }; typedef struct { int id; enum bp_type type; VALUE source; union { int line; ID mid; } pos; VALUE expr; VALUE enabled; int hit_count; int hit_value; enum hit_condition hit_condition; } breakpoint_t; /* functions from locker.c */ extern void byebug_add_to_locked(VALUE thread); extern VALUE byebug_pop_from_locked(); extern void byebug_remove_from_locked(VALUE thread); /* functions from threads.c */ extern void Init_threads_table(VALUE mByebug); extern VALUE create_threads_table(void); extern void thread_context_lookup(VALUE thread, VALUE *context); extern int is_living_thread(VALUE thread); extern void acquire_lock(debug_context_t *dc); extern void release_lock(void); /* global variables */ extern VALUE threads; extern VALUE next_thread; /* functions from context.c */ extern void Init_byebug_context(VALUE mByebug); extern VALUE byebug_context_create(VALUE thread); extern VALUE context_dup(debug_context_t *context); extern void byebug_reset_stepping_stop_points(debug_context_t *context); extern VALUE call_with_debug_inspector(struct call_with_inspection_data *data); extern VALUE context_backtrace_set(const rb_debug_inspector_t *inspector, void *data); /* functions from breakpoint.c */ extern void Init_byebug_breakpoint(VALUE mByebug); extern VALUE find_breakpoint_by_pos(VALUE breakpoints, VALUE source, VALUE pos, VALUE bind); extern VALUE find_breakpoint_by_method(VALUE breakpoints, VALUE klass, VALUE mid, VALUE bind, VALUE self); #endif byebug-11.1.1/ext/byebug/context.c000066400000000000000000000376221361243113500167660ustar00rootroot00000000000000#include "byebug.h" static VALUE cContext; static VALUE cDebugThread; static int thnum_max = 0; /* "Step", "Next" and "Finish" do their work by saving information about where * to stop next. byebug_reset_stepping_stop_points removes/resets this information. */ extern void byebug_reset_stepping_stop_points(debug_context_t *context) { context->dest_frame = -1; context->lines = -1; context->steps = -1; context->steps_out = -1; } /* * call-seq: * context.dead? -> bool * * Returns +true+ if context doesn't represent a live context and is created * during post-mortem exception handling. */ static inline VALUE Context_dead(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_DEAD) ? Qtrue : Qfalse; } static void context_mark(void *data) { debug_context_t *context = (debug_context_t *)data; rb_gc_mark(context->backtrace); } static VALUE dc_backtrace(const debug_context_t *context) { return context->backtrace; } static int dc_stack_size(debug_context_t *context) { if (NIL_P(dc_backtrace(context))) return 0; return RARRAY_LENINT(dc_backtrace(context)); } extern VALUE byebug_context_create(VALUE thread) { debug_context_t *context = ALLOC(debug_context_t); context->flags = 0; context->thnum = ++thnum_max; context->thread = thread; byebug_reset_stepping_stop_points(context); context->stop_reason = CTX_STOP_NONE; rb_debug_inspector_open(context_backtrace_set, (void *)context); context->calced_stack_size = dc_stack_size(context) + 1; if (rb_obj_class(thread) == cDebugThread) CTX_FL_SET(context, CTX_FL_IGNORE); return Data_Wrap_Struct(cContext, context_mark, 0, context); } extern VALUE context_dup(debug_context_t *context) { debug_context_t *new_context = ALLOC(debug_context_t); memcpy(new_context, context, sizeof(debug_context_t)); byebug_reset_stepping_stop_points(new_context); new_context->backtrace = context->backtrace; CTX_FL_SET(new_context, CTX_FL_DEAD); return Data_Wrap_Struct(cContext, context_mark, 0, new_context); } static VALUE dc_frame_get(const debug_context_t *context, int frame_index, frame_part type) { VALUE frame; if (NIL_P(dc_backtrace(context))) rb_raise(rb_eRuntimeError, "Backtrace information is not available"); if (frame_index >= RARRAY_LENINT(dc_backtrace(context))) rb_raise(rb_eRuntimeError, "That frame doesn't exist!"); frame = rb_ary_entry(dc_backtrace(context), frame_index); return rb_ary_entry(frame, type); } static VALUE dc_frame_location(const debug_context_t *context, int frame_index) { return dc_frame_get(context, frame_index, LOCATION); } static VALUE dc_frame_self(const debug_context_t *context, int frame_index) { return dc_frame_get(context, frame_index, SELF); } static VALUE dc_frame_class(const debug_context_t *context, int frame_index) { return dc_frame_get(context, frame_index, CLASS); } static VALUE dc_frame_binding(const debug_context_t *context, int frame_index) { return dc_frame_get(context, frame_index, BINDING); } static VALUE load_backtrace(const rb_debug_inspector_t *inspector) { VALUE backtrace = rb_ary_new(); VALUE locs = rb_debug_inspector_backtrace_locations(inspector); int i; for (i = 0; i < RARRAY_LENINT(locs); i++) { VALUE frame = rb_ary_new(); rb_ary_push(frame, rb_ary_entry(locs, i)); rb_ary_push(frame, rb_debug_inspector_frame_self_get(inspector, i)); rb_ary_push(frame, rb_debug_inspector_frame_class_get(inspector, i)); rb_ary_push(frame, rb_debug_inspector_frame_binding_get(inspector, i)); rb_ary_push(backtrace, frame); } return backtrace; } extern VALUE context_backtrace_set(const rb_debug_inspector_t *inspector, void *data) { debug_context_t *dc = (debug_context_t *)data; dc->backtrace = load_backtrace(inspector); return Qnil; } static VALUE open_debug_inspector_i(const rb_debug_inspector_t *inspector, void *data) { struct call_with_inspection_data *cwi = (struct call_with_inspection_data *)data; cwi->dc->backtrace = load_backtrace(inspector); return rb_funcall2(cwi->ctx, cwi->id, cwi->argc, cwi->argv); } static VALUE open_debug_inspector(struct call_with_inspection_data *cwi) { return rb_debug_inspector_open(open_debug_inspector_i, cwi); } static VALUE open_debug_inspector_ensure(VALUE v) { return open_debug_inspector((struct call_with_inspection_data *)v); } static VALUE close_debug_inspector(struct call_with_inspection_data *cwi) { cwi->dc->backtrace = Qnil; return Qnil; } static VALUE close_debug_inspector_ensure(VALUE v) { return close_debug_inspector((struct call_with_inspection_data *)v); } extern VALUE call_with_debug_inspector(struct call_with_inspection_data *data) { return rb_ensure(open_debug_inspector_ensure, (VALUE)data, close_debug_inspector_ensure, (VALUE)data); } #define FRAME_SETUP \ debug_context_t *context; \ VALUE frame_no; \ int frame_n; \ Data_Get_Struct(self, debug_context_t, context); \ if (!rb_scan_args(argc, argv, "01", &frame_no)) \ frame_n = 0; \ else \ frame_n = FIX2INT(frame_no); /* * call-seq: * context.frame_binding(frame_position = 0) -> binding * * Returns frame's binding. */ static VALUE Context_frame_binding(int argc, VALUE *argv, VALUE self) { FRAME_SETUP; return dc_frame_binding(context, frame_n); } /* * call-seq: * context.frame_class(frame_position = 0) -> class * * Returns frame's defined class. */ static VALUE Context_frame_class(int argc, VALUE *argv, VALUE self) { FRAME_SETUP; return dc_frame_class(context, frame_n); } /* * call-seq: * context.frame_file(frame_position = 0) -> string * * Returns the name of the file in the frame. */ static VALUE Context_frame_file(int argc, VALUE *argv, VALUE self) { VALUE loc, absolute_path; FRAME_SETUP; loc = dc_frame_location(context, frame_n); absolute_path = rb_funcall(loc, rb_intern("absolute_path"), 0); if (!NIL_P(absolute_path)) return absolute_path; return rb_funcall(loc, rb_intern("path"), 0); } /* * call-seq: * context.frame_line(frame_position = 0) -> int * * Returns the line number in the file in the frame. */ static VALUE Context_frame_line(int argc, VALUE *argv, VALUE self) { VALUE loc; FRAME_SETUP; loc = dc_frame_location(context, frame_n); return rb_funcall(loc, rb_intern("lineno"), 0); } /* * call-seq: * context.frame_method(frame_position = 0) -> sym * * Returns the sym of the method in the frame. */ static VALUE Context_frame_method(int argc, VALUE *argv, VALUE self) { VALUE loc; FRAME_SETUP; loc = dc_frame_location(context, frame_n); return rb_str_intern(rb_funcall(loc, rb_intern("label"), 0)); } /* * call-seq: * context.frame_self(frame_postion = 0) -> obj * * Returns self object of the frame. */ static VALUE Context_frame_self(int argc, VALUE *argv, VALUE self) { FRAME_SETUP; return dc_frame_self(context, frame_n); } /* * call-seq: * context.ignored? -> bool * * Returns the ignore flag for the context, which marks whether the associated * thread is ignored while debugging. */ static inline VALUE Context_ignored(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_IGNORE) ? Qtrue : Qfalse; } /* * call-seq: * context.resume -> nil * * Resumes thread from the suspended mode. */ static VALUE Context_resume(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (!CTX_FL_TEST(context, CTX_FL_SUSPEND)) return Qnil; CTX_FL_UNSET(context, CTX_FL_SUSPEND); if (CTX_FL_TEST(context, CTX_FL_WAS_RUNNING)) rb_thread_wakeup(context->thread); return Qnil; } /* * call-seq: * context.backtrace-> Array * * Returns the frame stack of a context. */ static inline VALUE Context_backtrace(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return dc_backtrace(context); } static VALUE Context_stop_reason(VALUE self) { debug_context_t *context; const char *symbol; Data_Get_Struct(self, debug_context_t, context); if (CTX_FL_TEST(context, CTX_FL_DEAD)) symbol = "post-mortem"; else switch (context->stop_reason) { case CTX_STOP_STEP: symbol = "step"; break; case CTX_STOP_BREAKPOINT: symbol = "breakpoint"; break; case CTX_STOP_CATCHPOINT: symbol = "catchpoint"; break; case CTX_STOP_NONE: default: symbol = "none"; } return ID2SYM(rb_intern(symbol)); } /* * call-seq: * context.step_into(steps, frame = 0) * * Stops the current context after a number of +steps+ are made from frame * +frame+ (by default the newest one). */ static VALUE Context_step_into(int argc, VALUE *argv, VALUE self) { VALUE steps, v_frame; int n_args, from_frame; debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (context->calced_stack_size == 0) rb_raise(rb_eRuntimeError, "No frames collected."); n_args = rb_scan_args(argc, argv, "11", &steps, &v_frame); if (FIX2INT(steps) <= 0) rb_raise(rb_eRuntimeError, "Steps argument must be positive."); from_frame = n_args == 1 ? 0 : FIX2INT(v_frame); if (from_frame < 0 || from_frame >= context->calced_stack_size) rb_raise(rb_eRuntimeError, "Destination frame (%d) is out of range (%d)", from_frame, context->calced_stack_size); else if (from_frame > 0) CTX_FL_SET(context, CTX_FL_IGNORE_STEPS); context->steps = FIX2INT(steps); context->dest_frame = context->calced_stack_size - from_frame; return steps; } /* * call-seq: * context.step_out(n_frames = 1, force = false) * * Stops after +n_frames+ frames are finished. +force+ parameter (if true) * ensures that the execution will stop in the specified frame even when there * are no more instructions to run. In that case, it will stop when the return * event for that frame is triggered. */ static VALUE Context_step_out(int argc, VALUE *argv, VALUE self) { int n_args, n_frames; VALUE v_frames, force; debug_context_t *context; n_args = rb_scan_args(argc, argv, "02", &v_frames, &force); n_frames = n_args == 0 ? 1 : FIX2INT(v_frames); Data_Get_Struct(self, debug_context_t, context); if (n_frames < 0 || n_frames > context->calced_stack_size) rb_raise(rb_eRuntimeError, "You want to finish %d frames, but stack size is only %d", n_frames, context->calced_stack_size); context->steps_out = n_frames; if (n_args == 2 && RTEST(force)) CTX_FL_SET(context, CTX_FL_STOP_ON_RET); else CTX_FL_UNSET(context, CTX_FL_STOP_ON_RET); return Qnil; } /* * call-seq: * context.step_over(lines, frame = 0) * * Steps over +lines+ lines in frame +frame+ (by default the newest one) or * higher (if frame +frame+ finishes). */ static VALUE Context_step_over(int argc, VALUE *argv, VALUE self) { int n_args, frame; VALUE lines, v_frame; debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (context->calced_stack_size == 0) rb_raise(rb_eRuntimeError, "No frames collected."); n_args = rb_scan_args(argc, argv, "11", &lines, &v_frame); frame = n_args == 1 ? 0 : FIX2INT(v_frame); if (frame < 0 || frame >= context->calced_stack_size) rb_raise(rb_eRuntimeError, "Destination frame (%d) is out of range (%d)", frame, context->calced_stack_size); context->lines = FIX2INT(lines); context->dest_frame = context->calced_stack_size - frame; return Qnil; } /* * call-seq: * context.suspend -> nil * * Suspends the thread when it is running. */ static VALUE Context_suspend(VALUE self) { VALUE status; debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); status = rb_funcall(context->thread, rb_intern("status"), 0); if (rb_str_cmp(status, rb_str_new2("run")) == 0) CTX_FL_SET(context, CTX_FL_WAS_RUNNING); else if (rb_str_cmp(status, rb_str_new2("sleep")) == 0) CTX_FL_UNSET(context, CTX_FL_WAS_RUNNING); else return Qnil; CTX_FL_SET(context, CTX_FL_SUSPEND); return Qnil; } /* * call-seq: * context.switch -> nil * * Switches execution to this context. */ static VALUE Context_switch(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); next_thread = context->thread; context->steps = 1; context->steps_out = 0; CTX_FL_SET(context, CTX_FL_STOP_ON_RET); return Qnil; } /* * call-seq: * context.suspended? -> bool * * Returns +true+ if the thread is suspended by debugger. */ static VALUE Context_is_suspended(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_SUSPEND) ? Qtrue : Qfalse; } /* * call-seq: * context.thnum -> int * * Returns the context's number. */ static inline VALUE Context_thnum(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return INT2FIX(context->thnum); } /* * call-seq: * context.thread -> thread * * Returns the thread this context is associated with. */ static inline VALUE Context_thread(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return context->thread; } /* * call-seq: * context.tracing -> bool * * Returns the tracing flag for the current context. */ static VALUE Context_tracing(VALUE self) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); return CTX_FL_TEST(context, CTX_FL_TRACING) ? Qtrue : Qfalse; } /* * call-seq: * context.tracing = bool * * Controls the tracing for this context. */ static VALUE Context_set_tracing(VALUE self, VALUE value) { debug_context_t *context; Data_Get_Struct(self, debug_context_t, context); if (RTEST(value)) CTX_FL_SET(context, CTX_FL_TRACING); else CTX_FL_UNSET(context, CTX_FL_TRACING); return value; } /* :nodoc: */ static VALUE dt_inherited(VALUE klass) { UNUSED(klass); rb_raise(rb_eRuntimeError, "Can't inherit Byebug::DebugThread class"); return Qnil; } /* * Document-class: Context * * == Summary * * Byebug keeps a single instance of this class per thread. */ void Init_byebug_context(VALUE mByebug) { cContext = rb_define_class_under(mByebug, "Context", rb_cObject); rb_define_method(cContext, "backtrace", Context_backtrace, 0); rb_define_method(cContext, "dead?", Context_dead, 0); rb_define_method(cContext, "frame_binding", Context_frame_binding, -1); rb_define_method(cContext, "frame_class", Context_frame_class, -1); rb_define_method(cContext, "frame_file", Context_frame_file, -1); rb_define_method(cContext, "frame_line", Context_frame_line, -1); rb_define_method(cContext, "frame_method", Context_frame_method, -1); rb_define_method(cContext, "frame_self", Context_frame_self, -1); rb_define_method(cContext, "ignored?", Context_ignored, 0); rb_define_method(cContext, "resume", Context_resume, 0); rb_define_method(cContext, "step_into", Context_step_into, -1); rb_define_method(cContext, "step_out", Context_step_out, -1); rb_define_method(cContext, "step_over", Context_step_over, -1); rb_define_method(cContext, "stop_reason", Context_stop_reason, 0); rb_define_method(cContext, "suspend", Context_suspend, 0); rb_define_method(cContext, "suspended?", Context_is_suspended, 0); rb_define_method(cContext, "switch", Context_switch, 0); rb_define_method(cContext, "thnum", Context_thnum, 0); rb_define_method(cContext, "thread", Context_thread, 0); rb_define_method(cContext, "tracing", Context_tracing, 0); rb_define_method(cContext, "tracing=", Context_set_tracing, 1); cDebugThread = rb_define_class_under(mByebug, "DebugThread", rb_cThread); rb_define_singleton_method(cDebugThread, "inherited", dt_inherited, 0); } byebug-11.1.1/ext/byebug/extconf.rb000066400000000000000000000004561361243113500171240ustar00rootroot00000000000000# frozen_string_literal: true require "mkmf" makefile_config = RbConfig::MAKEFILE_CONFIG makefile_config["CC"] = ENV["CC"] if ENV["CC"] makefile_config["CFLAGS"] << " -gdwarf-2 -g3 -O0" if ENV["debug"] dir_config("ruby") with_cflags(makefile_config["CFLAGS"]) { create_makefile("byebug/byebug") } byebug-11.1.1/ext/byebug/locker.c000066400000000000000000000031401361243113500165450ustar00rootroot00000000000000#include "byebug.h" /** * A simple linked list containing locked threads, FIFO style. */ typedef struct locked_thread_t { VALUE thread; struct locked_thread_t *next; } locked_thread_t; static locked_thread_t *locked_head = NULL; static locked_thread_t *locked_tail = NULL; static int is_in_locked(VALUE thread) { locked_thread_t *node; if (!locked_head) return 0; for (node = locked_head; node != locked_tail; node = node->next) if (node->thread == thread) return 1; return 0; } extern void byebug_add_to_locked(VALUE thread) { locked_thread_t *node; if (is_in_locked(thread)) return; node = ALLOC(locked_thread_t); node->thread = thread; node->next = NULL; if (locked_tail) locked_tail->next = node; locked_tail = node; if (!locked_head) locked_head = node; } extern VALUE byebug_pop_from_locked() { VALUE thread; locked_thread_t *node; if (!locked_head) return Qnil; node = locked_head; locked_head = locked_head->next; if (locked_tail == node) locked_tail = NULL; thread = node->thread; xfree(node); return thread; } extern void byebug_remove_from_locked(VALUE thread) { locked_thread_t *node; locked_thread_t *next_node; if (NIL_P(thread) || !locked_head || !is_in_locked(thread)) return; if (locked_head->thread == thread) { byebug_pop_from_locked(); return; } for (node = locked_head; node != locked_tail; node = node->next) if (node->next && node->next->thread == thread) { next_node = node->next; node->next = next_node->next; xfree(next_node); return; } } byebug-11.1.1/ext/byebug/threads.c000066400000000000000000000107471361243113500167330ustar00rootroot00000000000000#include "byebug.h" /* Threads table class */ static VALUE cThreadsTable; /* If not Qnil, holds the next thread that must be run */ VALUE next_thread = Qnil; /* To allow thread syncronization, we must stop threads when debugging */ static VALUE locker = Qnil; static int t_tbl_mark_keyvalue(st_data_t key, st_data_t value, st_data_t tbl) { UNUSED(tbl); rb_gc_mark((VALUE)key); if (!value) return ST_CONTINUE; rb_gc_mark((VALUE)value); return ST_CONTINUE; } static void t_tbl_mark(void *data) { threads_table_t *t_tbl = (threads_table_t *)data; st_table *tbl = t_tbl->tbl; st_foreach(tbl, t_tbl_mark_keyvalue, (st_data_t)tbl); } static void t_tbl_free(void *data) { threads_table_t *t_tbl = (threads_table_t *)data; st_free_table(t_tbl->tbl); xfree(t_tbl); } /* * Creates a numeric hash whose keys are the currently active threads and * whose values are their associated contexts. */ VALUE create_threads_table(void) { threads_table_t *t_tbl; t_tbl = ALLOC(threads_table_t); t_tbl->tbl = st_init_numtable(); return Data_Wrap_Struct(cThreadsTable, t_tbl_mark, t_tbl_free, t_tbl); } /* * Checks a single entry in the threads table. * * If it has no associated context or the key doesn't correspond to a living * thread, the entry is removed from the thread's list. */ static int check_thread_i(st_data_t key, st_data_t value, st_data_t data) { UNUSED(data); if (!value) return ST_DELETE; if (!is_living_thread((VALUE)key)) return ST_DELETE; return ST_CONTINUE; } /* * Checks whether a thread is either in the running or sleeping state. */ int is_living_thread(VALUE thread) { VALUE status = rb_funcall(thread, rb_intern("status"), 0); if (NIL_P(status) || status == Qfalse) return 0; if (rb_str_cmp(status, rb_str_new2("run")) == 0 || rb_str_cmp(status, rb_str_new2("sleep")) == 0) return 1; return 0; } /* * Checks threads table for dead/finished threads. */ static void cleanup_dead_threads(void) { threads_table_t *t_tbl; Data_Get_Struct(threads, threads_table_t, t_tbl); st_foreach(t_tbl->tbl, check_thread_i, 0); } /* * Looks up a context in the threads table. If not present, it creates it. */ void thread_context_lookup(VALUE thread, VALUE *context) { threads_table_t *t_tbl; Data_Get_Struct(threads, threads_table_t, t_tbl); if (!st_lookup(t_tbl->tbl, thread, context) || !*context) { *context = byebug_context_create(thread); st_insert(t_tbl->tbl, thread, *context); } } /* * Holds thread execution while another thread is active. * * Thanks to this, all threads are "frozen" while the user is typing commands. */ void acquire_lock(debug_context_t *dc) { while ((!NIL_P(locker) && locker != rb_thread_current()) || CTX_FL_TEST(dc, CTX_FL_SUSPEND)) { byebug_add_to_locked(rb_thread_current()); rb_thread_stop(); if (CTX_FL_TEST(dc, CTX_FL_SUSPEND)) CTX_FL_SET(dc, CTX_FL_WAS_RUNNING); } locker = rb_thread_current(); } /* * Releases our global lock and passes execution on to another thread, either * the thread specified by +next_thread+ or any other thread if +next_thread+ * is nil. */ void release_lock(void) { VALUE thread; cleanup_dead_threads(); locker = Qnil; if (NIL_P(next_thread)) thread = byebug_pop_from_locked(); else { byebug_remove_from_locked(next_thread); thread = next_thread; next_thread = Qnil; } if (!NIL_P(thread) && is_living_thread(thread)) rb_thread_run(thread); } /* * call-seq: * Byebug.unlock -> nil * * Unlocks global switch so other threads can run. */ static VALUE Unlock(VALUE self) { UNUSED(self); release_lock(); return locker; } /* * call-seq: * Byebug.lock -> Thread.current * * Locks global switch to reserve execution to current thread exclusively. */ static VALUE Lock(VALUE self) { debug_context_t *dc; VALUE context; UNUSED(self); if (!is_living_thread(rb_thread_current())) rb_raise(rb_eRuntimeError, "Current thread is dead!"); thread_context_lookup(rb_thread_current(), &context); Data_Get_Struct(context, debug_context_t, dc); acquire_lock(dc); return locker; } /* * * Document-class: ThreadsTable * * == Sumary * * Hash table holding currently active threads and their associated contexts */ void Init_threads_table(VALUE mByebug) { cThreadsTable = rb_define_class_under(mByebug, "ThreadsTable", rb_cObject); rb_define_module_function(mByebug, "unlock", Unlock, 0); rb_define_module_function(mByebug, "lock", Lock, 0); } byebug-11.1.1/lib/000077500000000000000000000000001361243113500136155ustar00rootroot00000000000000byebug-11.1.1/lib/byebug.rb000066400000000000000000000001021361243113500154100ustar00rootroot00000000000000# frozen_string_literal: true require_relative "byebug/attacher" byebug-11.1.1/lib/byebug/000077500000000000000000000000001361243113500150725ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/attacher.rb000066400000000000000000000015151361243113500172140ustar00rootroot00000000000000# frozen_string_literal: true # # Main Container for all of Byebug's code # module Byebug # # Starts byebug, and stops at the first line of user's code. # def self.attach unless started? self.mode = :attached start run_init_script end current_context.step_out(3, true) end def self.spawn(host = "localhost", port = nil) require_relative "core" self.wait_connection = true start_server(host, port || PORT) end end # # Adds a `byebug` method to the Kernel module. # # Dropping a `byebug` call anywhere in your code, you get a debug prompt. # module Kernel def byebug require_relative "core" Byebug.attach unless Byebug.mode == :off end def remote_byebug(host = "localhost", port = nil) Byebug.spawn(host, port) Byebug.attach end alias debugger byebug end byebug-11.1.1/lib/byebug/breakpoint.rb000066400000000000000000000053441361243113500175630ustar00rootroot00000000000000# frozen_string_literal: true module Byebug # # Implements breakpoints # class Breakpoint # # First breakpoint, in order of creation # def self.first Byebug.breakpoints.first end # # Last breakpoint, in order of creation # def self.last Byebug.breakpoints.last end # # Adds a new breakpoint # # @param [String] file # @param [Fixnum] line # @param [String] expr # def self.add(file, line, expr = nil) breakpoint = Breakpoint.new(file, line, expr) Byebug.breakpoints << breakpoint breakpoint end # # Removes a breakpoint # # @param id [integer] breakpoint number # def self.remove(id) Byebug.breakpoints.reject! { |b| b.id == id } end # # Returns an array of line numbers in file named +filename+ where # breakpoints could be set. The list will contain an entry for each # distinct line event call so it is possible (and possibly useful) for a # line number appear more than once. # # @param filename [String] File name to inspect for possible breakpoints # def self.potential_lines(filename) name = "#{Time.new.to_i}_#{rand(2**31)}" iseq = RubyVM::InstructionSequence.compile(File.read(filename), name) if iseq.respond_to?(:each_child) potential_lines_with_trace_points(iseq, {}) else potential_lines_without_trace_points(iseq, {}) end end def self.potential_lines_with_trace_points(iseq, lines) iseq.trace_points.each { |(line, _)| lines[line] = true } iseq.each_child do |child| potential_lines_with_trace_points(child, lines) end lines.keys.sort end private_class_method :potential_lines_with_trace_points def self.potential_lines_without_trace_points(iseq, lines) iseq.disasm.each_line do |line| res = /^\d+ (?\w+)\s+.+\(\s*(?\d+)\)$/.match(line) next unless res && res[:insn] == "trace" lines[res[:lineno].to_i] = true end lines.keys end private_class_method :potential_lines_without_trace_points # # Returns true if a breakpoint could be set in line number +lineno+ in file # name +filename. # def self.potential_line?(filename, lineno) potential_lines(filename).member?(lineno) end # # True if there's no breakpoints # def self.none? Byebug.breakpoints.empty? end # # Prints all information associated to the breakpoint # def inspect meths = %w[id pos source expr hit_condition hit_count hit_value enabled?] values = meths.map { |field| "#{field}: #{send(field)}" }.join(", ") "#" end end end byebug-11.1.1/lib/byebug/command.rb000066400000000000000000000043361361243113500170430ustar00rootroot00000000000000# frozen_string_literal: true require "forwardable" require_relative "helpers/string" module Byebug # # Parent class of all byebug commands. # # Subclass it and name the subclass ending with the word Command to implement # your own custom command. # # @example Define a custom command # # class MyCustomCommand < Command # def self.regexp # /custom_regexp/ # end # # def self.description # "Custom long desc" # end # # def.short_description # "Custom short desc" # end # # def execute # # My command's implementation # end # end # class Command extend Forwardable attr_reader :processor def initialize(processor, input = self.class.to_s) @processor = processor @match = match(input) end def context @context ||= processor.context end def frame @frame ||= context.frame end def arguments @match[0].split(" ").drop(1).join(" ") end def_delegators "self.class", :help, :match def_delegator "processor.printer", :print, :pr def_delegator "processor.printer", :print_collection, :prc def_delegator "processor.printer", :print_variables, :prv def_delegators "processor.interface", :errmsg, :puts, :print, :confirm class << self include Helpers::StringHelper # # Special methods to allow command filtering in processors # attr_accessor :allow_in_control, :allow_in_post_mortem attr_writer :always_run def always_run @always_run ||= 0 end # # Name of the command, as executed by the user. # def to_s name .split("::") .map { |n| n.gsub(/Command$/, "").downcase if /Command$/.match?(n) } .compact .join(" ") end def columnize(width) format( " %-#{width}s -- %s\n", name: to_s, description: short_description ) end # # Default help text for a command. # def help prettify(description) end # # Command's regexp match against an input # def match(input) regexp.match(input) end end end end byebug-11.1.1/lib/byebug/command_list.rb000066400000000000000000000010611361243113500200660ustar00rootroot00000000000000# frozen_string_literal: true require_relative "errors" module Byebug # # Holds an array of subcommands for a command # class CommandList include Enumerable def initialize(commands) @commands = commands.sort_by(&:to_s) end def match(input) find { |cmd| cmd.match(input) } end def each @commands.each { |cmd| yield(cmd) } end def to_s "\n" + map { |cmd| cmd.columnize(width) }.join + "\n" end private def width @width ||= map(&:to_s).max_by(&:size).size end end end byebug-11.1.1/lib/byebug/commands.rb000066400000000000000000000024711361243113500172240ustar00rootroot00000000000000# frozen_string_literal: true require_relative "commands/break" require_relative "commands/catch" require_relative "commands/condition" require_relative "commands/continue" require_relative "commands/debug" require_relative "commands/delete" require_relative "commands/disable" require_relative "commands/display" require_relative "commands/down" require_relative "commands/edit" require_relative "commands/enable" require_relative "commands/finish" require_relative "commands/frame" require_relative "commands/help" require_relative "commands/history" require_relative "commands/info" require_relative "commands/interrupt" require_relative "commands/irb" require_relative "commands/kill" require_relative "commands/list" require_relative "commands/method" require_relative "commands/next" require_relative "commands/pry" require_relative "commands/quit" require_relative "commands/restart" require_relative "commands/save" require_relative "commands/set" require_relative "commands/show" require_relative "commands/skip" require_relative "commands/source" require_relative "commands/step" require_relative "commands/thread" require_relative "commands/tracevar" require_relative "commands/undisplay" require_relative "commands/untracevar" require_relative "commands/up" require_relative "commands/var" require_relative "commands/where" byebug-11.1.1/lib/byebug/commands/000077500000000000000000000000001361243113500166735ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/commands/break.rb000066400000000000000000000057371361243113500203200ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/eval" require_relative "../helpers/file" require_relative "../helpers/parse" require_relative "../source_file_formatter" module Byebug # # Implements breakpoint functionality # class BreakCommand < Command include Helpers::EvalHelper include Helpers::FileHelper include Helpers::ParseHelper self.allow_in_control = true def self.regexp /^\s* b(?:reak)? (?:\s+ (.+?))? (?:\s+ if \s+(.+))? \s*$/x end def self.description <<-DESCRIPTION b[reak] [:] [if ] b[reak] [::...](.|#) [if ] They can be specified by line or method and an expression can be added for conditionally enabled breakpoints. #{short_description} DESCRIPTION end def self.short_description "Sets breakpoints in the source code" end def execute return puts(help) unless @match[1] b = line_breakpoint(@match[1]) || method_breakpoint(@match[1]) return errmsg(pr("break.errors.location")) unless b return puts(pr("break.created", id: b.id, file: b.source, line: b.pos)) if syntax_valid?(@match[2]) errmsg(pr("break.errors.expression", expr: @match[2])) b.enabled = false end private def line_breakpoint(location) line_match = location.match(/^(\d+)$/) file_line_match = location.match(/^(.+):(\d+)$/) return unless line_match || file_line_match file = line_match ? frame.file : file_line_match[1] line = line_match ? line_match[1].to_i : file_line_match[2].to_i add_line_breakpoint(file, line) end def method_breakpoint(location) location.match(/([^.#]+)[.#](.+)/) do |match| klass = target_object(match[1]) method = match[2].intern Breakpoint.add(klass, method, @match[2]) end end def target_object(str) k = error_eval(str) k&.is_a?(Module) ? k.name : str rescue StandardError errmsg("Warning: breakpoint source is not yet defined") str end def add_line_breakpoint(file, line) raise(pr("break.errors.source", file: file)) unless File.exist?(file) fullpath = File.realpath(file) raise(pr("break.errors.far_line", lines: n_lines(file), file: fullpath)) if line > n_lines(file) unless Breakpoint.potential_line?(fullpath, line) msg = pr( "break.errors.line", file: fullpath, line: line, valid_breakpoints: valid_breakpoints_for(fullpath, line) ) raise(msg) end Breakpoint.add(fullpath, line, @match[2]) end def valid_breakpoints_for(path, line) potential_lines = Breakpoint.potential_lines(path) annotator = ->(n) { potential_lines.include?(n) ? "[B]" : " " } source_file_formatter = SourceFileFormatter.new(path, annotator) source_file_formatter.lines_around(line).join.chomp end end end byebug-11.1.1/lib/byebug/commands/catch.rb000066400000000000000000000036761361243113500203160ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/eval" module Byebug # # Implements exception catching. # # Enables the user to catch unhandled assertion when they happen. # class CatchCommand < Command include Helpers::EvalHelper self.allow_in_post_mortem = true def self.regexp /^\s* cat(?:ch)? (?:\s+(\S+))? (?:\s+(off))? \s*$/x end def self.description <<-DESCRIPTION cat[ch][ (off|[ off])] #{short_description} catch -- lists catchpoints catch off -- deletes all catchpoints catch -- enables handling catch off -- disables handling DESCRIPTION end def self.short_description "Handles exception catchpoints" end def execute return info unless @match[1] return @match[1] == "off" ? clear : add(@match[1]) unless @match[2] return errmsg pr("catch.errors.off", off: cmd) unless @match[2] == "off" remove(@match[1]) end private def remove(exception) return errmsg pr("catch.errors.not_found", exception: exception) unless Byebug.catchpoints.member?(exception) puts pr("catch.removed", exception: exception) Byebug.catchpoints.delete(exception) end def add(exception) errmsg pr("catch.errors.not_class", class: exception) if warning_eval(exception.is_a?(Class).to_s) puts pr("catch.added", exception: exception) Byebug.add_catchpoint(exception) end def clear Byebug.catchpoints.clear if confirm(pr("catch.confirmations.delete_all")) end def info if Byebug.catchpoints && !Byebug.catchpoints.empty? Byebug.catchpoints.each_key do |exception| puts("#{exception}: #{exception.is_a?(Class)}") end else puts "No exceptions set to be caught." end end end end byebug-11.1.1/lib/byebug/commands/condition.rb000066400000000000000000000026661361243113500212200ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/parse" module Byebug # # Implements conditions on breakpoints. # # Adds the ability to stop on breakpoints only under certain conditions. # class ConditionCommand < Command include Helpers::ParseHelper self.allow_in_post_mortem = true def self.regexp /^\s* cond(?:ition)? (?:\s+(\d+)(?:\s+(.*))?)? \s*$/x end def self.description <<-DESCRIPTION cond[ition] [ expr] #{short_description} Specify breakpoint number to break only if is true. is an integer and is an expression to be evaluated whenever breakpoint is reached. If no expression is specified, the condition is removed. DESCRIPTION end def self.short_description "Sets conditions on breakpoints" end def execute return puts(help) unless @match[1] breakpoints = Byebug.breakpoints.sort_by(&:id) return errmsg(pr("condition.errors.no_breakpoints")) if breakpoints.empty? pos, err = get_int(@match[1], "Condition", 1) return errmsg(err) if err breakpoint = breakpoints.find { |b| b.id == pos } return errmsg(pr("break.errors.no_breakpoint")) unless breakpoint return errmsg(pr("break.errors.not_changed", expr: @match[2])) unless syntax_valid?(@match[2]) breakpoint.expr = @match[2] end end end byebug-11.1.1/lib/byebug/commands/continue.rb000066400000000000000000000032551361243113500210510ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/parse" module Byebug # # Implements the continue command. # # Allows the user to continue execution until the next stopping point, a # specific line number or until program termination. # class ContinueCommand < Command include Helpers::ParseHelper def self.regexp /^\s* c(?:ont(?:inue)?)? (?:(!|\s+unconditionally|\s+\S+))? \s*$/x end def self.description <<-DESCRIPTION c[ont[inue]][ ] #{short_description} Normally the program stops at the next breakpoint. However, if the parameter "unconditionally" is given or the command is suffixed with "!", the program will run until the end regardless of any enabled breakpoints. DESCRIPTION end def self.short_description "Runs until program ends, hits a breakpoint or reaches a line" end def execute if until_line? num, err = get_int(modifier, "Continue", 0, nil) return errmsg(err) unless num filename = File.expand_path(frame.file) return errmsg(pr("continue.errors.unstopped_line", line: num)) unless Breakpoint.potential_line?(filename, num) Breakpoint.add(filename, num) end processor.proceed! Byebug.mode = :off if unconditionally? Byebug.stop if unconditionally? || Byebug.stoppable? end private def until_line? @match[1] && !["!", "unconditionally"].include?(modifier) end def unconditionally? @match[1] && ["!", "unconditionally"].include?(modifier) end def modifier @match[1].lstrip end end end byebug-11.1.1/lib/byebug/commands/debug.rb000066400000000000000000000013621361243113500203100ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/eval" module Byebug # # Spawns a subdebugger and evaluates the given expression # class DebugCommand < Command include Helpers::EvalHelper def self.regexp /^\s* debug (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION debug #{short_description} Allows, for example, setting breakpoints on expressions evaluated from the debugger's prompt. DESCRIPTION end def self.short_description "Spawns a subdebugger" end def execute return puts(help) unless @match[1] puts safe_inspect(separate_thread_eval(@match[1])) end end end byebug-11.1.1/lib/byebug/commands/delete.rb000066400000000000000000000022361361243113500204650ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/parse" module Byebug # # Implements breakpoint deletion. # class DeleteCommand < Command include Helpers::ParseHelper self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* del(?:ete)? (?:\s+(.*))?$/x end def self.description <<-DESCRIPTION del[ete][ nnn...] #{short_description} Without and argument, deletes all breakpoints. With integer arguments, it deletes specific breakpoints. DESCRIPTION end def self.short_description "Deletes breakpoints" end def execute unless @match[1] Byebug.breakpoints.clear if confirm(pr("break.confirmations.delete_all")) return end @match[1].split(/ +/).each do |number| pos, err = get_int(number, "Delete", 1) return errmsg(err) unless pos if Breakpoint.remove(pos) puts(pr("break.messages.breakpoint_deleted", pos: pos)) else errmsg(pr("break.errors.no_breakpoint_delete", pos: pos)) end end end end end byebug-11.1.1/lib/byebug/commands/disable.rb000066400000000000000000000012411361243113500206210ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../subcommands" require_relative "../commands/disable/breakpoints" require_relative "../commands/disable/display" module Byebug # # Disabling custom display expressions or breakpoints. # class DisableCommand < Command include Subcommands self.allow_in_post_mortem = true def self.regexp /^\s* dis(?:able)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION dis[able][[ breakpoints| display)][ n1[ n2[ ...[ nn]]]]] #{short_description} DESCRIPTION end def self.short_description "Disables breakpoints or displays" end end end byebug-11.1.1/lib/byebug/commands/disable/000077500000000000000000000000001361243113500202765ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/commands/disable/breakpoints.rb000066400000000000000000000017031361243113500231450ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/toggle" module Byebug # # Reopens the +disable+ command to define the +breakpoints+ subcommand # class DisableCommand < Command # # Disables all or specific breakpoints # class BreakpointsCommand < Command include Helpers::ToggleHelper self.allow_in_post_mortem = true def self.regexp /^\s* b(?:reakpoints)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION dis[able] b[reakpoints][ .. ] #{short_description} Give breakpoint numbers (separated by spaces) as arguments or no argument at all if you want to disable every breakpoint. DESCRIPTION end def self.short_description "Disable all or specific breakpoints." end def execute enable_disable_breakpoints("disable", @match[1]) end end end end byebug-11.1.1/lib/byebug/commands/disable/display.rb000066400000000000000000000020021361243113500222620ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/toggle" module Byebug # # Reopens the +disable+ command to define the +display+ subcommand # class DisableCommand < Command # # Enables all or specific displays # class DisplayCommand < Command include Helpers::ToggleHelper self.allow_in_post_mortem = true def self.regexp /^\s* d(?:isplay)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION dis[able] d[isplay][ .. ] #{short_description} Arguments are the code numbers of the expressions to disable. Do "info display" to see the current list of code numbers. If no arguments are specified, all displays are disabled. DESCRIPTION end def self.short_description "Disables expressions to be displayed when program stops." end def execute enable_disable_display("disable", @match[1]) end end end end byebug-11.1.1/lib/byebug/commands/display.rb000066400000000000000000000027521361243113500206730ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/eval" module Byebug # # Custom expressions to be displayed every time the debugger stops. # class DisplayCommand < Command include Helpers::EvalHelper self.allow_in_post_mortem = false self.always_run = 2 def self.regexp /^\s* disp(?:lay)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION disp[lay][ ] #{short_description} If specified, adds into display expression list. Otherwise, it lists all expressions. DESCRIPTION end def self.short_description "Evaluates expressions every time the debugger stops" end def execute return print_display_expressions unless @match && @match[1] Byebug.displays.push [true, @match[1]] display_expression(@match[1]) end private def display_expression(exp) print pr("display.result", n: Byebug.displays.size, exp: exp, result: eval_expr(exp)) end def print_display_expressions result = prc("display.result", Byebug.displays) do |item, index| active, exp = item { n: index + 1, exp: exp, result: eval_expr(exp) } if active end print result end def eval_expr(expression) error_eval(expression).inspect rescue StandardError "(undefined)" end end end byebug-11.1.1/lib/byebug/commands/down.rb000066400000000000000000000016201361243113500201660ustar00rootroot00000000000000# frozen_string_literal: true require "pathname" require_relative "../command" require_relative "../helpers/frame" require_relative "../helpers/parse" module Byebug # # Move the current frame down in the backtrace. # class DownCommand < Command include Helpers::FrameHelper include Helpers::ParseHelper self.allow_in_post_mortem = true def self.regexp /^\s* down (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION down[ count] #{short_description} Use the "bt" command to find out where you want to go. DESCRIPTION end def self.short_description "Moves to a lower frame in the stack trace" end def execute pos, err = parse_steps(@match[1], "Down") return errmsg(err) unless pos jump_frames(-pos) ListCommand.new(processor).execute if Setting[:autolist] end end end byebug-11.1.1/lib/byebug/commands/edit.rb000066400000000000000000000027521361243113500201530ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" module Byebug # # Edit a file from byebug's prompt. # class EditCommand < Command self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* ed(?:it)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION edit[ file:lineno] #{short_description} With no argumnt, edits file containing most re line listed. Editing targets can also be specified to start editing at a specific line in a specific file DESCRIPTION end def self.short_description "Edits source files" end def execute file, line = location(@match[1]) return edit_error("not_exist", file) unless File.exist?(file) return edit_error("not_readable", file) unless File.readable?(file) cmd = line ? "#{editor} +#{line} #{file}" : "#{editor} #{file}" Kernel.system(cmd) end private def location(matched) if matched.nil? file = frame.file return errmsg(pr("edit.errors.state")) unless file line = frame.line elsif (@pos_match = /([^:]+)[:]([0-9]+)/.match(matched)) file, line = @pos_match.captures else file = matched line = nil end [File.expand_path(file), line] end def editor ENV["EDITOR"] || "vim" end def edit_error(type, file) errmsg(pr("edit.errors.#{type}", file: file)) end end end byebug-11.1.1/lib/byebug/commands/enable.rb000066400000000000000000000012361361243113500204500ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../subcommands" require_relative "../commands/enable/breakpoints" require_relative "../commands/enable/display" module Byebug # # Enabling custom display expressions or breakpoints. # class EnableCommand < Command include Subcommands self.allow_in_post_mortem = true def self.regexp /^\s* en(?:able)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION en[able][[ b[reakpoints]| d[isplay])][ n1[ n2[ ...[ nn]]]]] #{short_description} DESCRIPTION end def self.short_description "Enables breakpoints or displays" end end end byebug-11.1.1/lib/byebug/commands/enable/000077500000000000000000000000001361243113500201215ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/commands/enable/breakpoints.rb000066400000000000000000000016541361243113500227750ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/toggle" module Byebug # # Reopens the +enable+ command to define the +breakpoints+ subcommand # class EnableCommand < Command # # Enables all or specific breakpoints # class BreakpointsCommand < Command include Helpers::ToggleHelper self.allow_in_post_mortem = true def self.regexp /^\s* b(?:reakpoints)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION en[able] b[reakpoints][ ] #{short_description} Give breakpoint numbers (separated by spaces) as arguments or no argument at all if you want to enable every breakpoint. DESCRIPTION end def self.short_description "Enable all or specific breakpoints" end def execute enable_disable_breakpoints("enable", @match[1]) end end end end byebug-11.1.1/lib/byebug/commands/enable/display.rb000066400000000000000000000017731361243113500221230ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/toggle" module Byebug # # Reopens the +enable+ command to define the +display+ subcommand # class EnableCommand < Command # # Enables all or specific displays # class DisplayCommand < Command include Helpers::ToggleHelper self.allow_in_post_mortem = true def self.regexp /^\s* d(?:isplay)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION en[able] d[isplay][ .. ] #{short_description} Arguments are the code numbers of the expressions to enable. Do "info display" to see the current list of code numbers. If no arguments are specified, all displays are enabled. DESCRIPTION end def self.short_description "Enables expressions to be displayed when program stops." end def execute enable_disable_display("enable", @match[1]) end end end end byebug-11.1.1/lib/byebug/commands/finish.rb000066400000000000000000000024011361243113500204750ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/parse" module Byebug # # Implements the finish functionality. # # Allows the user to continue execution until certain frames are finished. # class FinishCommand < Command include Helpers::ParseHelper self.allow_in_post_mortem = false def self.regexp /^\s* fin(?:ish)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION fin[ish][ n_frames] #{short_description} If no number is given, we run until the current frame returns. If a number of frames `n_frames` is given, then we run until `n_frames` return from the current position. DESCRIPTION end def self.short_description "Runs the program until frame returns" end def execute if @match[1] n_frames, err = get_int(@match[1], "finish", 0, max_frames - 1) return errmsg(err) unless n_frames else n_frames = 1 end force = n_frames.zero? ? true : false context.step_out(context.frame.pos + n_frames, force) context.frame = 0 processor.proceed! end private def max_frames context.stack_size - context.frame.pos end end end byebug-11.1.1/lib/byebug/commands/frame.rb000066400000000000000000000027211361243113500203140ustar00rootroot00000000000000# frozen_string_literal: true require "pathname" require_relative "../command" require_relative "../helpers/frame" require_relative "../helpers/parse" module Byebug # # Move to specific frames in the backtrace. # class FrameCommand < Command include Helpers::FrameHelper include Helpers::ParseHelper self.allow_in_post_mortem = true def self.regexp /^\s* f(?:rame)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION f[rame][ frame-number] #{short_description} If a frame number has been specified, to moves to that frame. Otherwise it moves to the newest frame. A negative number indicates position from the other end, so "frame -1" moves to the oldest frame, and "frame 0" moves to the newest frame. Without an argument, the command prints the current stack frame. Since the current position is redisplayed, it may trigger a resyncronization if there is a front end also watching over things. Use the "bt" command to find out where you want to go. DESCRIPTION end def self.short_description "Moves to a frame in the call stack" end def execute return print(pr("frame.line", context.frame.to_hash)) unless @match[1] pos, err = get_int(@match[1], "Frame") return errmsg(err) unless pos switch_to_frame(pos) ListCommand.new(processor).execute if Setting[:autolist] end end end byebug-11.1.1/lib/byebug/commands/help.rb000066400000000000000000000024601361243113500201520ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../errors" module Byebug # # Ask for help from byebug's prompt. # class HelpCommand < Command self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* h(?:elp)? (?:\s+(\S+))? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION h[elp][ [ ]] #{short_description} help -- prints a summary of all commands help -- prints help on command help -- prints help on 's subcommand DESCRIPTION end def self.short_description "Helps you using byebug" end def execute return help_for_all unless @match[1] return help_for(@match[1], command) unless @match[2] help_for(@match[2], subcommand) end private def help_for_all puts(processor.command_list.to_s) end def help_for(input, cmd) raise CommandNotFound.new(input, command) unless cmd puts(cmd.help) end def command @command ||= processor.command_list.match(@match[1]) end def subcommand return unless command @subcommand ||= command.subcommand_list.match(@match[2]) end end end byebug-11.1.1/lib/byebug/commands/history.rb000066400000000000000000000013541361243113500207240ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/parse" module Byebug # # Show history of byebug commands. # class HistoryCommand < Command include Helpers::ParseHelper self.allow_in_post_mortem = true def self.regexp /^\s* hist(?:ory)? (?:\s+(?.+))? \s*$/x end def self.description <<-DESCRIPTION hist[ory][ num_cmds] #{short_description} DESCRIPTION end def self.short_description "Shows byebug's history of commands" end def execute history = processor.interface.history size, = get_int(@match[:num_cmds], "history", 1) if @match[:num_cmds] puts history.to_s(size) end end end byebug-11.1.1/lib/byebug/commands/info.rb000066400000000000000000000014471361243113500201610ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../subcommands" require_relative "../commands/info/breakpoints" require_relative "../commands/info/display" require_relative "../commands/info/file" require_relative "../commands/info/line" require_relative "../commands/info/program" module Byebug # # Shows info about different aspects of the debugger. # class InfoCommand < Command include Subcommands self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* i(?:nfo)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION info[ subcommand] #{short_description} DESCRIPTION end def self.short_description "Shows several informations about the program being debugged" end end end byebug-11.1.1/lib/byebug/commands/info/000077500000000000000000000000001361243113500176265ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/commands/info/breakpoints.rb000066400000000000000000000031501361243113500224730ustar00rootroot00000000000000# frozen_string_literal: true module Byebug # # Reopens the +info+ command to define the +breakpoints+ subcommand # class InfoCommand < Command # # Information about current breakpoints # class BreakpointsCommand < Command self.allow_in_post_mortem = true def self.regexp /^\s* b(?:reakpoints)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION inf[o] b[reakpoints] #{short_description} DESCRIPTION end def self.short_description "Status of user settable breakpoints" end def execute return puts("No breakpoints.") if Byebug.breakpoints.empty? breakpoints = Byebug.breakpoints.sort_by(&:id) if @match[1] indices = @match[1].split(/ +/).map(&:to_i) breakpoints = breakpoints.select { |b| indices.member?(b.id) } return errmsg("No breakpoints found among list given") if breakpoints.empty? end puts "Num Enb What" breakpoints.each { |b| info_breakpoint(b) } end private def info_breakpoint(brkpt) interp = format( "%-3d %-3s at %s:%s%s", id: brkpt.id, status: brkpt.enabled? ? "y" : "n", file: brkpt.source, line: brkpt.pos, expression: brkpt.expr.nil? ? "" : " if #{brkpt.expr}" ) puts interp hits = brkpt.hit_count return unless hits.positive? s = hits > 1 ? "s" : "" puts " breakpoint already hit #{hits} time#{s}" end end end end byebug-11.1.1/lib/byebug/commands/info/display.rb000066400000000000000000000021261361243113500216210ustar00rootroot00000000000000# frozen_string_literal: true module Byebug # # Reopens the +info+ command to define the +display+ subcommand # class InfoCommand < Command # # Information about display expressions # class DisplayCommand < Command self.allow_in_post_mortem = true def self.regexp /^\s* d(?:isplay)? \s*$/x end def self.description <<-DESCRIPTION inf[o] d[display] #{short_description} DESCRIPTION end def self.short_description "List of expressions to display when program stops" end def execute return puts("There are no auto-display expressions now.") unless Byebug.displays.find { |d| d[0] } puts "Auto-display expressions now in effect:" puts "Num Enb Expression" Byebug.displays.each_with_index do |d, i| interp = format( "%3d: %s %s", number: i + 1, status: d[0] ? "y" : "n", expression: d[1] ) puts(interp) end end end end end byebug-11.1.1/lib/byebug/commands/info/file.rb000066400000000000000000000035071361243113500210770ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/file" module Byebug # # Reopens the +info+ command to define the +file+ subcommand # class InfoCommand < Command # # Information about a particular source file # class FileCommand < Command include Helpers::FileHelper include Helpers::StringHelper self.allow_in_post_mortem = true def self.regexp /^\s* f(?:ile)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION inf[o] f[ile] #{short_description} It informs about file name, number of lines, possible breakpoints in the file, last modification time and sha1 digest. DESCRIPTION end def self.short_description "Information about a particular source file." end def execute file = @match[1] || frame.file return errmsg(pr("info.errors.undefined_file", file: file)) unless File.exist?(file) puts prettify <<-RUBY File #{info_file_basic(file)} Breakpoint line numbers: #{info_file_breakpoints(file)} Modification time: #{info_file_mtime(file)} Sha1 Signature: #{info_file_sha1(file)} RUBY end private def info_file_basic(file) path = File.expand_path(file) return unless File.exist?(path) s = n_lines(path) == 1 ? "" : "s" "#{path} (#{n_lines(path)} line#{s})" end def info_file_breakpoints(file) breakpoints = Breakpoint.potential_lines(file) return unless breakpoints breakpoints.to_a.sort.join(" ") end def info_file_mtime(file) File.stat(file).mtime end def info_file_sha1(file) require "digest/sha1" Digest::SHA1.hexdigest(file) end end end end byebug-11.1.1/lib/byebug/commands/info/line.rb000066400000000000000000000012611361243113500211020ustar00rootroot00000000000000# frozen_string_literal: true module Byebug # # Reopens the +info+ command to define the +line+ subcommand # class InfoCommand < Command # # Information about current location # class LineCommand < Command self.allow_in_post_mortem = true def self.regexp /^\s* l(?:ine)? \s*$/x end def self.description <<-DESCRIPTION inf[o] l[ine] #{short_description} DESCRIPTION end def self.short_description "Line number and file name of current position in source file." end def execute puts "Line #{frame.line} of \"#{frame.file}\"" end end end end byebug-11.1.1/lib/byebug/commands/info/program.rb000066400000000000000000000020671361243113500216270ustar00rootroot00000000000000# frozen_string_literal: true module Byebug # # Reopens the +info+ command to define the +args+ subcommand # class InfoCommand < Command # # Information about arguments of the current method/block # class ProgramCommand < Command self.allow_in_post_mortem = true def self.regexp /^\s* p(?:rogram)? \s*$/x end def self.description <<-DESCRIPTION inf[o] p[rogram] #{short_description} DESCRIPTION end def self.short_description "Information about the current status of the debugged program." end def execute puts "Program stopped. " format_stop_reason context.stop_reason end private def format_stop_reason(stop_reason) case stop_reason when :step puts "It stopped after stepping, next'ing or initial start." when :breakpoint puts "It stopped at a breakpoint." when :catchpoint puts "It stopped at a catchpoint." end end end end end byebug-11.1.1/lib/byebug/commands/interrupt.rb000066400000000000000000000010561361243113500212560ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" module Byebug # # Interrupting execution of current thread. # class InterruptCommand < Command self.allow_in_control = true def self.regexp /^\s*int(?:errupt)?\s*$/ end def self.description <<-DESCRIPTION int[errupt] #{short_description} DESCRIPTION end def self.short_description "Interrupts the program" end def execute Byebug.start Byebug.thread_context(Thread.main).interrupt end end end byebug-11.1.1/lib/byebug/commands/irb.rb000066400000000000000000000016561361243113500200040ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require "irb" require "English" module Byebug # # Enter IRB from byebug's prompt # class IrbCommand < Command self.allow_in_post_mortem = true def self.regexp /^\s* irb \s*$/x end def self.description <<-DESCRIPTION irb #{short_description} DESCRIPTION end def self.short_description "Starts an IRB session" end def execute return errmsg(pr("base.errors.only_local")) unless processor.interface.instance_of?(LocalInterface) # @todo IRB tries to parse $ARGV so we must clear it (see #197). Add a # test case for it so we can remove this comment. with_clean_argv { IRB.start } end private def with_clean_argv saved_argv = $ARGV.dup $ARGV.clear begin yield ensure $ARGV.concat(saved_argv) end end end end byebug-11.1.1/lib/byebug/commands/kill.rb000066400000000000000000000016301361243113500201530ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" module Byebug # # Send custom signals to the debugged program. # class KillCommand < Command self.allow_in_control = true def self.regexp /^\s* kill \s* (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION kill[ signal] #{short_description} Equivalent of Process.kill(Process.pid) DESCRIPTION end def self.short_description "Sends a signal to the current process" end def execute if @match[1] signame = @match[1] return errmsg("signal name #{signame} is not a signal I know about\n") unless Signal.list.member?(signame) else return unless confirm("Really kill? (y/n) ") signame = "KILL" end processor.interface.close if signame == "KILL" Process.kill(signame, Process.pid) end end end byebug-11.1.1/lib/byebug/commands/list.rb000066400000000000000000000073011361243113500201740ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../source_file_formatter" require_relative "../helpers/file" require_relative "../helpers/parse" module Byebug # # List parts of the source code. # class ListCommand < Command include Helpers::FileHelper include Helpers::ParseHelper self.allow_in_post_mortem = true def self.regexp /^\s* l(?:ist)? (?:\s*([-=])|\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION l[ist][[-=]][ nn-mm] #{short_description} Lists lines forward from current line or from the place where code was last listed. If "list-" is specified, lists backwards instead. If "list=" is specified, lists from current line regardless of where code was last listed. A line range can also be specified to list specific sections of code. DESCRIPTION end def self.short_description "Lists lines of source code" end def execute msg = "No sourcefile available for #{frame.file}" raise(msg) unless File.exist?(frame.file) b, e = range(@match[2]) display_lines(b, e) processor.prev_line = b end private # # Line range to be printed by `list`. # # If is set, range is parsed from it. # # Otherwise it's automatically chosen. # def range(input) return auto_range(@match[1] || "+") unless input b, e = parse_range(input) raise("Invalid line range") unless valid_range?(b, e) [b, e] end def valid_range?(first, last) first <= last && (1..max_line).cover?(first) && (1..max_line).cover?(last) end # # Set line range to be printed by list # # @return first line number to list # @return last line number to list # def auto_range(direction) prev_line = processor.prev_line if direction == "=" || prev_line.nil? source_file_formatter.range_around(frame.line) else source_file_formatter.range_from(move(prev_line, size, direction)) end end def parse_range(input) first, err = get_int(lower_bound(input), "List", 1, max_line) raise(err) unless first if upper_bound(input) last, err = get_int(upper_bound(input), "List", 1, max_line) raise(err) unless last last = amend_final(last) else first -= (size / 2) end [first, last || move(first, size - 1)] end def move(line, size, direction = "+") line.send(direction, size) end # # Show a range of lines in the current file. # # @param min [Integer] Lower bound # @param max [Integer] Upper bound # def display_lines(min, max) puts "\n[#{min}, #{max}] in #{frame.file}" puts source_file_formatter.lines(min, max).join end # # @param range [String] A string with an integer range format # # @return [String] The lower bound of the given range # def lower_bound(range) split_range(range)[0] end # # @param range [String] A string with an integer range format # # @return [String] The upper bound of the given range # def upper_bound(range) split_range(range)[1] end # # @param str [String] A string with an integer range format # # @return [Array] The upper & lower bounds of the given range # def split_range(str) str.split(/[-,]/) end extend Forwardable def_delegators :source_file_formatter, :amend_final, :size, :max_line def source_file_formatter @source_file_formatter ||= SourceFileFormatter.new( frame.file, ->(n) { n == frame.line ? "=>" : " " } ) end end end byebug-11.1.1/lib/byebug/commands/method.rb000066400000000000000000000024621361243113500205040ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/eval" module Byebug # # Show methods of specific classes/modules/objects. # class MethodCommand < Command include Helpers::EvalHelper self.allow_in_post_mortem = true def self.regexp /^\s* m(?:ethod)? \s+ (i(:?nstance)?\s+)?/x end def self.description <<-DESCRIPTION m[ethod] (i[nstance][ ]|) #{short_description} When invoked with "instance", shows instance methods of the object specified as argument or of self no object was specified. When invoked only with a class or module, shows class methods of the class or module specified as argument. DESCRIPTION end def self.short_description "Shows methods of an object, class or module" end def execute obj = warning_eval(@match.post_match) result = if @match[1] prc("method.methods", obj.methods.sort) { |item, _| { name: item } } elsif !obj.is_a?(Module) pr("variable.errors.not_module", object: @match.post_match) else prc("method.methods", obj.instance_methods(false).sort) do |item, _| { name: item } end end puts result end end end byebug-11.1.1/lib/byebug/commands/next.rb000066400000000000000000000014341361243113500202000ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/parse" module Byebug # # Implements the next functionality. # # Allows the user the continue execution until the next instruction in the # current frame. # class NextCommand < Command include Helpers::ParseHelper def self.regexp /^\s* n(?:ext)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION n[ext][ nnn] #{short_description} DESCRIPTION end def self.short_description "Runs one or more lines of code" end def execute steps, err = parse_steps(@match[1], "Next") return errmsg(err) unless steps context.step_over(steps, context.frame.pos) processor.proceed! end end end byebug-11.1.1/lib/byebug/commands/pry.rb000066400000000000000000000013701361243113500200330ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/eval" module Byebug # # Enter Pry from byebug's prompt # class PryCommand < Command self.allow_in_post_mortem = true def self.regexp /^\s* pry \s*$/x end def self.description <<-DESCRIPTION pry #{short_description} DESCRIPTION end def self.short_description "Starts a Pry session" end def execute return errmsg(pr("base.errors.only_local")) unless processor.interface.instance_of?(LocalInterface) begin require "pry" rescue LoadError return errmsg(pr("pry.errors.not_installed")) end Pry.start(context.frame._binding) end end end byebug-11.1.1/lib/byebug/commands/quit.rb000066400000000000000000000015611361243113500202050ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" module Byebug # # Exit from byebug. # class QuitCommand < Command self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* q(?:uit)? \s* (?:(!|\s+unconditionally))? \s*$/x end def self.description <<-DESCRIPTION q[uit][!| unconditionally] #{short_description} Normally we prompt before exiting. However, if the parameter "unconditionally" is given or command is suffixed with "!", we exit without asking further questions. DESCRIPTION end def self.short_description "Exits byebug" end def execute return unless @match[1] || confirm(pr("quit.confirmations.really")) processor.interface.autosave processor.interface.close Process.exit! end end end byebug-11.1.1/lib/byebug/commands/restart.rb000066400000000000000000000024701361243113500207070ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/bin" require_relative "../helpers/path" require "shellwords" require "English" require "rbconfig" module Byebug # # Restart debugged program from within byebug. # class RestartCommand < Command include Helpers::BinHelper include Helpers::PathHelper self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* restart (?:\s+(?.+))? \s*$/x end def self.description <<-DESCRIPTION restart [args] #{short_description} This is a re-exec - all byebug state is lost. If command arguments are passed those are used. DESCRIPTION end def self.short_description "Restarts the debugged program" end def execute cmd = [$PROGRAM_NAME] cmd = prepend_byebug_bin(cmd) cmd = prepend_ruby_bin(cmd) cmd += (@match[:args] ? @match[:args].shellsplit : $ARGV) puts pr("restart.success", cmd: cmd.shelljoin) Kernel.exec(*cmd) end private def prepend_byebug_bin(cmd) cmd.unshift(bin_file) if Byebug.mode == :standalone cmd end def prepend_ruby_bin(cmd) cmd.unshift(RbConfig.ruby) if which("ruby") != which(cmd.first) cmd end end end byebug-11.1.1/lib/byebug/commands/save.rb000066400000000000000000000032031361243113500201540ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" module Byebug # # Save current settings to use them in another debug session. # class SaveCommand < Command self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* sa(?:ve)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION save[ FILE] #{short_description} Byebug state is saved as a script file. This includes breakpoints, catchpoints, display expressions and some settings. If no filename is given, byebug will fabricate one. Use the "source" command in another debug session to restore the saved file. DESCRIPTION end def self.short_description "Saves current byebug session to a file" end def execute file = File.open(@match[1] || Setting[:savefile], "w") save_breakpoints(file) save_catchpoints(file) save_displays(file) save_settings(file) print pr("save.messages.done", path: file.path) file.close end private def save_breakpoints(file) Byebug.breakpoints.each do |b| file.puts "break #{b.source}:#{b.pos}#{" if #{b.expr}" if b.expr}" end end def save_catchpoints(file) Byebug.catchpoints.each_key do |c| file.puts "catch #{c}" end end def save_displays(file) Byebug.displays.each { |d| file.puts "display #{d[1]}" if d[0] } end def save_settings(file) %w[autoirb autolist basename].each do |setting| file.puts "set #{setting} #{Setting[setting.to_sym]}" end end end end byebug-11.1.1/lib/byebug/commands/set.rb000066400000000000000000000034541361243113500200210ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/parse" module Byebug # # Change byebug settings. # class SetCommand < Command include Helpers::ParseHelper self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* set (?:\s+(?\w+))? (?:\s+(?\S+))? \s*$/x end def self.description <<-DESCRIPTION set #{short_description} Boolean values take "on", "off", "true", "false", "1" or "0". If you don't specify a value, the boolean setting will be enabled. Conversely, you can use "set no" to disable them. You can see these environment settings with the "show" command. DESCRIPTION end def self.short_description "Modifies byebug settings" end def self.help super + Setting.help_all end def execute key = @match[:setting] value = @match[:value] return puts(help) if key.nil? && value.nil? setting = Setting.find(key) return errmsg(pr("set.errors.unknown_setting", key: key)) unless setting if !setting.boolean? && value.nil? err = pr("set.errors.must_specify_value", key: key) elsif setting.boolean? value, err = get_onoff(value, /^no/.match?(key) ? false : true) elsif setting.integer? value, err = get_int(value, setting.to_sym, 1) end return errmsg(err) if value.nil? setting.value = value puts setting.to_s end private def get_onoff(arg, default) return default if arg.nil? case arg when "1", "on", "true" true when "0", "off", "false" false else [nil, pr("set.errors.on_off", arg: arg)] end end end end byebug-11.1.1/lib/byebug/commands/show.rb000066400000000000000000000015351361243113500202040ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" module Byebug # # Show byebug settings. # class ShowCommand < Command self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* show (?:\s+(?\w+))? \s*$/x end def self.description <<-DESCRIPTION show #{short_description} You can change them with the "set" command. DESCRIPTION end def self.short_description "Shows byebug settings" end def self.help super + Setting.help_all end def execute key = @match[:setting] return puts(help) unless key setting = Setting.find(key) return errmsg(pr("show.errors.unknown_setting", key: key)) unless setting puts Setting.settings[setting.to_sym] end end end byebug-11.1.1/lib/byebug/commands/skip.rb000066400000000000000000000035061361243113500201720ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/parse" module Byebug # # Allows the user to continue execution until the next breakpoint, as # long as it is different from the current one # class SkipCommand < Command include Helpers::ParseHelper class << self attr_writer :file_line, :file_path attr_reader :previous_autolist def file_line @file_line ||= 0 end def file_path @file_path ||= "" end def setup_autolist(value) @previous_autolist = ListCommand.always_run ListCommand.always_run = value end def restore_autolist ListCommand.always_run = @previous_autolist @previous_autolist = nil end end def self.regexp /^\s* sk(?:ip)? \s*$/x end def self.description <<-DESCRIPTION sk[ip] #{short_description} DESCRIPTION end def self.short_description "Runs until the next breakpoint as long as it is different from the current one" end def initialize_attributes self.class.always_run = 2 self.class.setup_autolist(0) self.class.file_path = frame.file self.class.file_line = frame.line end def keep_execution [self.class.file_path, self.class.file_line] == [frame.file, frame.line] end def reset_attributes self.class.always_run = 0 ListCommand.new(processor).execute if self.class.previous_autolist == 1 self.class.restore_autolist end def auto_run return false unless self.class.always_run == 2 keep_execution ? processor.proceed! : reset_attributes true end def execute return if auto_run initialize_attributes processor.proceed! Byebug.stop if Byebug.stoppable? end end end byebug-11.1.1/lib/byebug/commands/source.rb000066400000000000000000000015221361243113500205200ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" module Byebug # # Execute a file containing byebug commands. # # It can be used to restore a previously saved debugging session. # class SourceCommand < Command self.allow_in_control = true self.allow_in_post_mortem = true def self.regexp /^\s* so(?:urce)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION source #{short_description} DESCRIPTION end def self.short_description "Restores a previously saved byebug session" end def execute return puts(help) unless @match[1] file = File.expand_path(@match[1]).strip return errmsg(pr("source.errors.not_found", file: file)) unless File.exist?(file) processor.interface.read_file(file) end end end byebug-11.1.1/lib/byebug/commands/step.rb000066400000000000000000000015501361243113500201740ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/parse" module Byebug # # Implements the step functionality. # # Allows the user the continue execution until the next instruction, possibily # in a different frame. Use step to step into method calls or blocks. # class StepCommand < Command include Helpers::ParseHelper def self.regexp /^\s* s(?:tep)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION s[tep][ times] #{short_description} DESCRIPTION end def self.short_description "Steps into blocks or methods one or more times" end def execute steps, err = parse_steps(@match[1], "Steps") return errmsg(err) unless steps context.step_into(steps, context.frame.pos) processor.proceed! end end end byebug-11.1.1/lib/byebug/commands/thread.rb000066400000000000000000000012701361243113500204670ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../subcommands" require_relative "../commands/thread/current" require_relative "../commands/thread/list" require_relative "../commands/thread/resume" require_relative "../commands/thread/stop" require_relative "../commands/thread/switch" module Byebug # # Manipulation of Ruby threads # class ThreadCommand < Command include Subcommands def self.regexp /^\s* th(?:read)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION th[read] #{short_description} DESCRIPTION end def self.short_description "Commands to manipulate threads" end end end byebug-11.1.1/lib/byebug/commands/thread/000077500000000000000000000000001361243113500201425ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/commands/thread/current.rb000066400000000000000000000012701361243113500221510ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/thread" module Byebug # # Reopens the +thread+ command to define the +current+ subcommand # class ThreadCommand < Command # # Information about the current thread # class CurrentCommand < Command include Helpers::ThreadHelper def self.regexp /^\s* c(?:urrent)? \s*$/x end def self.description <<-DESCRIPTION th[read] c[urrent] #{short_description} DESCRIPTION end def self.short_description "Shows current thread information" end def execute display_context(context) end end end end byebug-11.1.1/lib/byebug/commands/thread/list.rb000066400000000000000000000015001361243113500214360ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/thread" module Byebug # # Reopens the +thread+ command to define the +list+ subcommand # class ThreadCommand < Command # # Information about threads # class ListCommand < Command include Helpers::ThreadHelper def self.regexp /^\s* l(?:ist)? \s*$/x end def self.description <<-DESCRIPTION th[read] l[ist] #{short_description} DESCRIPTION end def self.short_description "Lists all threads" end def execute contexts = Byebug.contexts.sort_by(&:thnum) thread_list = prc("thread.context", contexts) do |context, _| thread_arguments(context) end print(thread_list) end end end end byebug-11.1.1/lib/byebug/commands/thread/resume.rb000066400000000000000000000016771361243113500220020ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/thread" module Byebug # # Reopens the +thread+ command to define the +resume+ subcommand # class ThreadCommand < Command # # Resumes the specified thread # class ResumeCommand < Command include Helpers::ThreadHelper def self.regexp /^\s* r(?:esume)? (?: \s* (\d+))? \s*$/x end def self.description <<-DESCRIPTION th[read] r[esume] #{short_description} DESCRIPTION end def self.short_description "Resumes execution of the specified thread" end def execute return puts(help) unless @match[1] context, err = context_from_thread(@match[1]) return errmsg(err) if err return errmsg(pr("thread.errors.already_running")) unless context.suspended? context.resume display_context(context) end end end end byebug-11.1.1/lib/byebug/commands/thread/stop.rb000066400000000000000000000015421361243113500214560ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/thread" module Byebug # # Reopens the +thread+ command to define the +stop+ subcommand # class ThreadCommand < Command # # Stops the specified thread # class StopCommand < Command include Helpers::ThreadHelper def self.regexp /^\s* st(?:op)? (?: \s* (\d+))? \s*$/x end def self.description <<-DESCRIPTION th[read] st[op] #{short_description} DESCRIPTION end def self.short_description "Stops the execution of the specified thread" end def execute return puts(help) unless @match[1] context, err = context_from_thread(@match[1]) return errmsg(err) if err context.suspend display_context(context) end end end end byebug-11.1.1/lib/byebug/commands/thread/switch.rb000066400000000000000000000016131361243113500217710ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/thread" module Byebug # # Reopens the +thread+ command to define the +switch+ subcommand # class ThreadCommand < Command # # Switches to the specified thread # class SwitchCommand < Command include Helpers::ThreadHelper def self.regexp /^\s* sw(?:itch)? (?: \s* (\d+))? \s*$/x end def self.description <<-DESCRIPTION th[read] sw[itch] #{short_description} DESCRIPTION end def self.short_description "Switches execution to the specified thread" end def execute return puts(help) unless @match[1] context, err = context_from_thread(@match[1]) return errmsg(err) if err display_context(context) context.switch processor.proceed! end end end end byebug-11.1.1/lib/byebug/commands/tracevar.rb000066400000000000000000000025771361243113500210420ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" module Byebug # # Show (and possibily stop) at every line that changes a global variable. # class TracevarCommand < Command def self.regexp /^\s* tr(?:acevar)? (?: \s+ (\S+))? # (variable-name)? (?: \s+ (stop|nostop))? \s*$/x end def self.description <<-DESCRIPTION tr[acevar] [[no]stop] #{short_description} If "stop" is specified, execution will stop every time the variable changes its value. If nothing or "nostop" is specified, execution won't stop, changes will just be logged in byebug's output. DESCRIPTION end def self.short_description "Enables tracing of a global variable" end def execute var = @match[1] return errmsg(pr("trace.errors.needs_global_variable")) unless var return errmsg(pr("trace.errors.var_is_not_global", name: var)) unless global_variables.include?(:"#{var}") stop = @match[2] && @match[2] !~ /nostop/ instance_eval do trace_var(:"#{var}") { |val| on_change(var, val, stop) } end puts pr("trace.messages.success", var: var) end private def on_change(name, value, stop) puts pr("trace.messages.on_change", name: name, value: value) context.step_out(1, false) if stop end end end byebug-11.1.1/lib/byebug/commands/undisplay.rb000066400000000000000000000023751361243113500212370ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" require_relative "../helpers/parse" module Byebug # # Remove expressions from display list. # class UndisplayCommand < Command include Helpers::ParseHelper self.allow_in_post_mortem = true def self.regexp /^\s* undisp(?:lay)? (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION undisp[lay][ nnn] #{short_description} Arguments are the code numbers of the expressions to stop displaying. No argument means cancel all automatic-display expressions. Type "info display" to see the current list of code numbers. DESCRIPTION end def self.short_description "Stops displaying all or some expressions when program stops" end def execute if @match[1] pos, err = get_int(@match[1], "Undisplay", 1, Byebug.displays.size) return errmsg(err) unless err.nil? last_display = Byebug.displays[pos - 1] return errmsg(pr("display.errors.undefined", expr: pos)) unless last_display last_display[0] = nil else return unless confirm(pr("display.confirmations.clear_all")) Byebug.displays.each { |d| d[0] = false } end end end end byebug-11.1.1/lib/byebug/commands/untracevar.rb000066400000000000000000000013041361243113500213700ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../command" module Byebug # # Stop tracing a global variable. # class UntracevarCommand < Command def self.regexp /^\s* untr(?:acevar)? (?:\s+ (\S+))? \s*$/x end def self.description <<-DESCRIPTION untr[acevar] #{short_description} DESCRIPTION end def self.short_description "Stops tracing a global variable" end def execute var = @match[1] if global_variables.include?(:"#{var}") untrace_var(:"#{var}") puts pr("trace.messages.undo", var: var) else errmsg pr("trace.errors.not_global", var: var) end end end end byebug-11.1.1/lib/byebug/commands/up.rb000066400000000000000000000016061361243113500176470ustar00rootroot00000000000000# frozen_string_literal: true require "pathname" require_relative "../command" require_relative "../helpers/frame" require_relative "../helpers/parse" module Byebug # # Move the current frame up in the backtrace. # class UpCommand < Command include Helpers::FrameHelper include Helpers::ParseHelper self.allow_in_post_mortem = true def self.regexp /^\s* up (?:\s+(\S+))? \s*$/x end def self.description <<-DESCRIPTION up[ count] #{short_description} Use the "bt" command to find out where you want to go. DESCRIPTION end def self.short_description "Moves to a higher frame in the stack trace" end def execute pos, err = parse_steps(@match[1], "Up") return errmsg(err) unless pos jump_frames(pos) ListCommand.new(processor).execute if Setting[:autolist] end end end byebug-11.1.1/lib/byebug/commands/var.rb000066400000000000000000000012501361243113500200060ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../subcommands" require_relative "var/all" require_relative "var/args" require_relative "var/const" require_relative "var/instance" require_relative "var/local" require_relative "var/global" module Byebug # # Shows variables and its values # class VarCommand < Command include Subcommands self.allow_in_post_mortem = true def self.regexp /^\s* v(?:ar)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION [v]ar #{short_description} DESCRIPTION end def self.short_description "Shows variables and its values" end end end byebug-11.1.1/lib/byebug/commands/var/000077500000000000000000000000001361243113500174635ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/commands/var/all.rb000066400000000000000000000013731361243113500205640ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/var" module Byebug # # Reopens the +var+ command to define the +all+ subcommand # class VarCommand < Command # # Shows global, instance and local variables # class AllCommand < Command include Helpers::VarHelper self.allow_in_post_mortem = true def self.regexp /^\s* a(?:ll)? \s*$/x end def self.description <<-DESCRIPTION v[ar] a[ll] #{short_description} DESCRIPTION end def self.short_description "Shows local, global and instance variables of self." end def execute var_global var_instance("self") var_local end end end end byebug-11.1.1/lib/byebug/commands/var/args.rb000066400000000000000000000013311361243113500207420ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/var" module Byebug # # Reopens the +var+ command to define the +args+ subcommand # class VarCommand < Command # # Information about arguments of the current method/block # class ArgsCommand < Command include Helpers::VarHelper self.allow_in_post_mortem = true def self.regexp /^\s* a(?:rgs)? \s*$/x end def self.description <<-DESCRIPTION v[ar] a[args] #{short_description} DESCRIPTION end def self.short_description "Information about arguments of the current scope" end def execute var_args end end end end byebug-11.1.1/lib/byebug/commands/var/const.rb000066400000000000000000000020151361243113500211340ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/eval" module Byebug # # Reopens the +var+ command to define the +const+ subcommand # class VarCommand < Command # # Shows constants # class ConstCommand < Command include Helpers::EvalHelper self.allow_in_post_mortem = true def self.regexp /^\s* c(?:onst)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION v[ar] c[onstant] #{short_description} DESCRIPTION end def self.short_description "Shows constants of an object." end def execute obj = warning_eval(str_obj) return errmsg(pr("variable.errors.not_module", object: str_obj)) unless obj.is_a?(Module) constants = warning_eval("#{str_obj}.constants") puts prv(constants.sort.map { |c| [c, obj.const_get(c)] }, "constant") end private def str_obj @str_obj ||= @match[1] || "self.class" end end end end byebug-11.1.1/lib/byebug/commands/var/global.rb000066400000000000000000000012021361243113500212430ustar00rootroot00000000000000# frozen_string_literal: true module Byebug # # Reopens the +var+ command to define the +global+ subcommand # class VarCommand < Command # # Shows global variables # class GlobalCommand < Command include Helpers::VarHelper self.allow_in_post_mortem = true def self.regexp /^\s* g(?:lobal)? \s*$/x end def self.description <<-DESCRIPTION v[ar] g[lobal] #{short_description} DESCRIPTION end def self.short_description "Shows global variables." end def execute var_global end end end end byebug-11.1.1/lib/byebug/commands/var/instance.rb000066400000000000000000000013671361243113500216230ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/var" module Byebug # # Reopens the +var+ command to define the +instance+ subcommand # class VarCommand < Command # # Shows instance variables # class InstanceCommand < Command include Helpers::VarHelper self.allow_in_post_mortem = true def self.regexp /^\s* i(?:nstance)? (?:\s+ (.+))? \s*$/x end def self.description <<-DESCRIPTION v[ar] i[nstance][ ] #{short_description} DESCRIPTION end def self.short_description "Shows instance variables of self or a specific object." end def execute var_instance(@match[1]) end end end end byebug-11.1.1/lib/byebug/commands/var/local.rb000066400000000000000000000013031361243113500210770ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../../helpers/var" module Byebug # # Reopens the +var+ command to define the +local+ subcommand # class VarCommand < Command # # Shows local variables in current scope # class LocalCommand < Command include Helpers::VarHelper self.allow_in_post_mortem = true def self.regexp /^\s* l(?:ocal)? \s*$/x end def self.description <<-DESCRIPTION v[ar] l[ocal] #{short_description} DESCRIPTION end def self.short_description "Shows local variables in current scope." end def execute var_local end end end end byebug-11.1.1/lib/byebug/commands/where.rb000066400000000000000000000022511361243113500203320ustar00rootroot00000000000000# frozen_string_literal: true require "pathname" require_relative "../command" require_relative "../helpers/frame" module Byebug # # Show current backtrace. # class WhereCommand < Command include Helpers::FrameHelper self.allow_in_post_mortem = true def self.regexp /^\s* (?:w(?:here)?|bt|backtrace) \s*$/x end def self.description <<-DESCRIPTION w[here]|bt|backtrace #{short_description} Print the entire stack frame. Each frame is numbered; the most recent frame is 0. A frame number can be referred to in the "frame" command. "up" and "down" add or subtract respectively to frame numbers shown. The position of the current frame is marked with -->. C-frames hang from their most immediate Ruby frame to indicate that they are not navigable. DESCRIPTION end def self.short_description "Displays the backtrace" end def execute print_backtrace end private def print_backtrace bt = prc("frame.line", (0...context.stack_size)) do |_, index| Frame.new(context, index).to_hash end print(bt) end end end byebug-11.1.1/lib/byebug/context.rb000066400000000000000000000056231361243113500171110ustar00rootroot00000000000000# frozen_string_literal: true require_relative "frame" require_relative "helpers/path" require_relative "helpers/file" require_relative "processors/command_processor" module Byebug # # Mantains context information for the debugger and it's the main # communication point between the library and the C-extension through the # at_breakpoint, at_catchpoint, at_tracing, at_line and at_return callbacks # class Context include Helpers::FileHelper class << self include Helpers::PathHelper attr_writer :ignored_files # # List of files byebug will ignore while debugging # def ignored_files @ignored_files ||= Byebug.mode == :standalone ? lib_files + [bin_file] : lib_files end attr_writer :interface def interface @interface ||= LocalInterface.new end attr_writer :processor def processor @processor ||= CommandProcessor end end # # Reader for the current frame # def frame @frame ||= Frame.new(self, 0) end # # Writer for the current frame # def frame=(pos) @frame = Frame.new(self, pos) end extend Forwardable def_delegators :frame, :file, :line # # Current file & line information # def location "#{normalize(file)}:#{line}" end # # Current file, line and source code information # def full_location return location if virtual_file?(file) "#{location} #{get_line(file, line)}" end # # Context's stack size # def stack_size return 0 unless backtrace backtrace.drop_while { |l| ignored_file?(l.first.path) } .take_while { |l| !ignored_file?(l.first.path) } .size end def interrupt step_into 1 end # # Line handler # def at_line self.frame = 0 return if ignored_file?(file) processor.at_line end # # Tracing handler # def at_tracing return if ignored_file?(file) processor.at_tracing end # # Breakpoint handler # def at_breakpoint(breakpoint) processor.at_breakpoint(breakpoint) end # # Catchpoint handler # def at_catchpoint(exception) processor.at_catchpoint(exception) end # # Return handler # def at_return(return_value) return if ignored_file?(file) processor.at_return(return_value) end # # End of class definition handler # def at_end return if ignored_file?(file) processor.at_end end private def processor @processor ||= self.class.processor.new(self, self.class.interface) end # # Tells whether a file is ignored by the debugger. # # @param path [String] filename to be checked. # def ignored_file?(path) self.class.ignored_files.include?(path) end end end byebug-11.1.1/lib/byebug/core.rb000066400000000000000000000054411361243113500163530ustar00rootroot00000000000000# frozen_string_literal: true require_relative "helpers/reflection" require "byebug/byebug" require_relative "context" require_relative "breakpoint" require_relative "interface" require_relative "processors/script_processor" require_relative "processors/post_mortem_processor" require_relative "commands" require_relative "remote" require_relative "printers/plain" # # Main debugger's container module. Everything is defined under this module # module Byebug include Helpers::ReflectionHelper extend self # # Configuration file used for startup commands. Default value is .byebugrc # attr_accessor :init_file self.init_file = ".byebugrc" # # Debugger's display expressions # attr_accessor :displays self.displays = [] # # Running mode of the debugger. Can be either: # # * :attached => Attached to a running program through the `byebug` method. # * :standalone => Started through `byebug` script. # * :off => Ignoring any `byebug` method calls. # attr_accessor :mode # # Runs normal byebug initialization scripts. # # Reads and executes the commands from init file (if any) in the current # working directory. This is only done if the current directory is different # from your home directory. Thus, you can have more than one init file, one # generic in your home directory, and another, specific to the program you # are debugging, in the directory where you invoke byebug. # def run_init_script rc_dirs.each do |dir| rc_file = File.expand_path(File.join(dir, init_file)) next unless File.exist?(rc_file) run_rc_file(rc_file) end end def self.load_settings Dir.glob(File.join(__dir__, "settings", "*.rb")).each do |file| require file end constants.grep(/[a-z]Setting/).map do |name| setting = const_get(name).new Byebug::Setting.settings[setting.to_sym] = setting end end # # Saves information about the unhandled exception and gives a byebug # prompt back to the user before program termination. # def self.handle_post_mortem return unless raised_exception context = raised_exception.__bb_context PostMortemProcessor.new(context).at_line end at_exit { Byebug.handle_post_mortem if Byebug.post_mortem? } private # # Runs a initialization script file # def run_rc_file(rc_file) interface = ScriptInterface.new(rc_file) ScriptProcessor.new(nil, interface).process_commands end # # List of folders to load rc files from # # @note Files will be loaded in the order specified here. # def rc_dirs [ENV["HOME"], Dir.pwd].compact.uniq end end Byebug.load_settings # # Extends the extension class to be able to pass information about the # debugging environment from the c-extension to the user. # class Exception attr_reader :__bb_context end byebug-11.1.1/lib/byebug/errors.rb000066400000000000000000000007721361243113500167410ustar00rootroot00000000000000# frozen_string_literal: true module Byebug # # Custom exception exception to signal "command not found" errors # class CommandNotFound < NoMethodError def initialize(input, parent = nil) @input = input @parent = parent super("Unknown command '#{name}'. Try '#{help}'") end private def name build_cmd(@parent, @input) end def help build_cmd("help", @parent) end def build_cmd(*args) args.compact.join(" ") end end end byebug-11.1.1/lib/byebug/frame.rb000066400000000000000000000067631361243113500165250ustar00rootroot00000000000000# frozen_string_literal: true require_relative "helpers/file" module Byebug # # Represents a frame in the stack trace # class Frame include Helpers::FileHelper attr_reader :pos def initialize(context, pos) @context = context @pos = pos end def file @context.frame_file(pos) end def line @context.frame_line(pos) end def _self @context.frame_self(pos) end def _binding @context.frame_binding(pos) end def _class @context.frame_class(pos) end def _method @context.frame_method(pos) end def current? @context.frame.pos == pos end # # Gets local variables for the frame. # def locals return [] unless _binding _binding.local_variables.each_with_object({}) do |e, a| a[e] = _binding.local_variable_get(e) a end end # # Gets current method arguments for the frame. # def args return c_args unless _binding ruby_args end # # Returns the current class in the frame or an empty string if the current # +callstyle+ setting is 'short' # def deco_class Setting[:callstyle] == "short" || _class.to_s.empty? ? "" : "#{_class}." end def deco_block _method[/(?:block(?: \(\d+ levels\))?|rescue) in /] || "" end def deco_method _method[/((?:block(?: \(\d+ levels\))?|rescue) in )?(.*)/] end # # Builds a string containing all available args in the frame number, in a # verbose or non verbose way according to the value of the +callstyle+ # setting # def deco_args return "" if args.empty? my_args = args.map do |arg| prefix, default = prefix_and_default(arg[0]) kls = use_short_style?(arg) ? "" : "##{locals[arg[1]].class}" "#{prefix}#{arg[1] || default}#{kls}" end "(#{my_args.join(', ')})" end # # Builds a formatted string containing information about current method call # def deco_call deco_block + deco_class + deco_method + deco_args end # # Formatted filename in frame # def deco_file Setting[:fullpath] ? File.expand_path(file) : shortpath(file) end # # Properly formatted frame number of frame # def deco_pos format("%-2d", pos: pos) end # # Formatted mark for the frame. # # --> marks the current frame # ͱ-- marks c-frames # marks regular frames # def mark return "-->" if current? return " ͱ--" if c_frame? " " end # # Checks whether the frame is a c-frame # def c_frame? _binding.nil? end def to_hash { mark: mark, pos: deco_pos, call: deco_call, file: deco_file, line: line, full_path: File.expand_path(deco_file) } end private def c_args return [] unless _self.to_s != "main" _class.instance_method(_method).parameters end def ruby_args meth_name = _binding.eval("__method__") return [] unless meth_name meth_obj = _class.instance_method(meth_name) return [] unless meth_obj meth_obj.parameters end def use_short_style?(arg) Setting[:callstyle] == "short" || arg[1].nil? || locals.empty? end def prefix_and_default(arg_type) return ["&", "block"] if arg_type == :block return ["*", "args"] if arg_type == :rest ["", nil] end end end byebug-11.1.1/lib/byebug/helpers/000077500000000000000000000000001361243113500165345ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/helpers/bin.rb000066400000000000000000000020431361243113500176300ustar00rootroot00000000000000# frozen_string_literal: true module Byebug module Helpers # # Utilities for interaction with executables # module BinHelper # # Cross-platform way of finding an executable in the $PATH. # Adapted from: https://gist.github.com/steakknife/88b6c3837a5e90a08296 # def which(cmd) return File.expand_path(cmd) if File.exist?(cmd) [nil, *search_paths].each do |path| exe = find_executable(path, cmd) return exe if exe end nil end def find_executable(path, cmd) executable_file_extensions.each do |ext| exe = File.expand_path(cmd + ext, path) return exe if real_executable?(exe) end nil end def search_paths ENV["PATH"].split(File::PATH_SEPARATOR) end def executable_file_extensions ENV["PATHEXT"] ? ENV["PATHEXT"].split(";") : [""] end def real_executable?(file) File.executable?(file) && !File.directory?(file) end end end end byebug-11.1.1/lib/byebug/helpers/eval.rb000066400000000000000000000063301361243113500200120ustar00rootroot00000000000000# frozen_string_literal: true module Byebug module Helpers # # Utilities to assist evaluation of code strings # module EvalHelper # # Evaluates an +expression+ in a separate thread. # # @param expression [String] Expression to evaluate # def separate_thread_eval(expression) allowing_other_threads do in_new_thread { warning_eval(expression) } end end # # Evaluates an +expression+ that might use or defer execution to threads # other than the current one. # # @note This is necessary because when in byebug's prompt, every thread is # "frozen" so that nothing gets run. So we need to unlock threads prior # to evaluation or we will run into a deadlock. # # @param expression [String] Expression to evaluate # def multiple_thread_eval(expression) allowing_other_threads { warning_eval(expression) } end # # Evaluates a string containing Ruby code in a specific binding, # returning nil in an error happens. # def silent_eval(str, binding = frame._binding) safe_eval(str, binding) { |_e| nil } end # # Evaluates a string containing Ruby code in a specific binding, # handling the errors at an error level. # def error_eval(str, binding = frame._binding) safe_eval(str, binding) { |e| raise(e, msg(e)) } end # # Evaluates a string containing Ruby code in a specific binding, # handling the errors at a warning level. # def warning_eval(str, binding = frame._binding) safe_eval(str, binding) { |e| errmsg(msg(e)) } end private def safe_eval(str, binding) binding.eval(str.gsub(/\Aeval /, ""), "(byebug)", 1) rescue StandardError, ScriptError => e yield(e) end def msg(exception) msg = Setting[:stack_on_error] ? error_msg(exception) : warning_msg(exception) pr("eval.exception", text_message: msg) end def error_msg(exception) at = exception.backtrace locations = ["#{at.shift}: #{warning_msg(exception)}"] locations += at.map { |path| " from #{path}" } locations.join("\n") end def warning_msg(exception) "#{exception.class} Exception: #{exception.message}" end # # Run block temporarily ignoring all TracePoint events. # # Used to evaluate stuff within Byebug's prompt. Otherwise, any code # creating new threads won't be properly evaluated because new threads # will get blocked by byebug's main thread. # def allowing_other_threads Byebug.unlock res = yield Byebug.lock res end # # Runs the given block in a new thread, waits for it to finish and # returns the new thread's result. # def in_new_thread res = nil Thread.new { res = yield }.join res end def safe_inspect(var) var.inspect rescue StandardError safe_to_s(var) end def safe_to_s(var) var.to_s rescue StandardError "*Error in evaluation*" end end end end byebug-11.1.1/lib/byebug/helpers/file.rb000066400000000000000000000027331361243113500200050ustar00rootroot00000000000000# frozen_string_literal: true module Byebug module Helpers # # Utilities for interaction with files # module FileHelper # # Reads lines of source file +filename+ into an array # def get_lines(filename) File.foreach(filename).reduce([]) { |acc, elem| acc << elem.chomp } end # # Reads line number +lineno+ from file named +filename+ # def get_line(filename, lineno) File.open(filename) do |f| f.gets until f.lineno == lineno - 1 f.gets end end # # Returns the number of lines in file +filename+ in a portable, # one-line-at-a-time way. # def n_lines(filename) File.foreach(filename).reduce(0) { |acc, _elem| acc + 1 } end # # Regularize file name. # def normalize(filename) return filename if virtual_file?(filename) return File.basename(filename) if Setting[:basename] File.exist?(filename) ? File.realpath(filename) : filename end # # A short version of a long path # def shortpath(fullpath) components = Pathname(fullpath).each_filename.to_a return fullpath if components.size <= 2 File.join("...", components[-3..-1]) end # # True for special files like -e, false otherwise # def virtual_file?(name) ["(irb)", "-e", "(byebug)", "(eval)"].include?(name) end end end end byebug-11.1.1/lib/byebug/helpers/frame.rb000066400000000000000000000034441361243113500201600ustar00rootroot00000000000000# frozen_string_literal: true module Byebug module Helpers # # Utilities to assist frame navigation # module FrameHelper def switch_to_frame(frame) new_frame = index_from_start(frame) return frame_err("c_frame") if Frame.new(context, new_frame).c_frame? adjust_frame(new_frame) end def jump_frames(steps) adjust_frame(navigate_to_frame(steps)) end private def adjust_frame(new_frame) return frame_err("too_low") if new_frame >= context.stack_size return frame_err("too_high") if new_frame.negative? context.frame = new_frame processor.prev_line = nil end def navigate_to_frame(jump_no) current_jumps = 0 current_pos = context.frame.pos loop do current_pos += direction(jump_no) break if out_of_bounds?(current_pos) next if Frame.new(context, current_pos).c_frame? current_jumps += 1 break if current_jumps == jump_no.abs end current_pos end def out_of_bounds?(pos) !(0...context.stack_size).cover?(pos) end def frame_err(msg) errmsg(pr("frame.errors.#{msg}")) end # # @param step [Integer] A positive or negative integer # # @return [Integer] +1 if step is positive / -1 if negative # def direction(step) step / step.abs end # # Convert a possibly negative index to a positive index from the start # of the callstack. -1 is the last position in the stack and so on. # # @param i [Integer] Integer to be converted in a proper positive index. # def index_from_start(index) index >= 0 ? index : context.stack_size + index end end end end byebug-11.1.1/lib/byebug/helpers/parse.rb000066400000000000000000000033151361243113500201750ustar00rootroot00000000000000# frozen_string_literal: true module Byebug module Helpers # # Utilities to assist command parsing # module ParseHelper # # Parses +str+ of command +cmd+ as an integer between +min+ and +max+. # # If either +min+ or +max+ is nil, that value has no bound. # # @todo Remove the `cmd` parameter. It has nothing to do with the method's # purpose. # def get_int(str, cmd, min = nil, max = nil) return nil, pr("parse.errors.int.not_number", cmd: cmd, str: str) unless /\A-?[0-9]+\z/.match?(str) int = str.to_i if min && int < min err = pr("parse.errors.int.too_low", cmd: cmd, str: str, min: min) return nil, err elsif max && int > max err = pr("parse.errors.int.too_high", cmd: cmd, str: str, max: max) return nil, err end int end # # @return true if code is syntactically correct for Ruby, false otherwise # def syntax_valid?(code) return true unless code without_stderr do begin RubyVM::InstructionSequence.compile(code) true rescue SyntaxError false end end end # # @return +str+ as an integer or 1 if +str+ is empty. # def parse_steps(str, cmd) return 1 unless str steps, err = get_int(str, cmd, 1) return nil, err unless steps steps end private # # Temporarily disable output to $stderr # def without_stderr old_stderr = $stderr $stderr = StringIO.new yield ensure $stderr = old_stderr end end end end byebug-11.1.1/lib/byebug/helpers/path.rb000066400000000000000000000013641361243113500200210ustar00rootroot00000000000000# frozen_string_literal: true module Byebug module Helpers # # Utilities for managing gem paths # module PathHelper def bin_file @bin_file ||= File.join(root_path, "exe", "byebug") end def root_path @root_path ||= File.expand_path(File.join("..", "..", ".."), __dir__) end def lib_files @lib_files ||= glob_for("lib") end def test_files @test_files ||= glob_for("test") end def gem_files @gem_files ||= [bin_file] + lib_files end def all_files @all_files ||= gem_files + test_files end private def glob_for(dir) Dir.glob(File.join(root_path, dir, "**", "*.rb")) end end end end byebug-11.1.1/lib/byebug/helpers/reflection.rb000066400000000000000000000006131361243113500212130ustar00rootroot00000000000000# frozen_string_literal: true module Byebug module Helpers # # Reflection utilitie # module ReflectionHelper # # List of "command" classes in the including module # def commands constants(false) .map { |const| const_get(const, false) } .select { |c| c.is_a?(Class) && c.name =~ /[a-z]Command$/ } end end end end byebug-11.1.1/lib/byebug/helpers/string.rb000066400000000000000000000013361361243113500203720ustar00rootroot00000000000000# frozen_string_literal: true module Byebug module Helpers # # Utilities for interaction with strings # module StringHelper # # Converts +str+ from an_underscored-or-dasherized_string to # ACamelizedString. # def camelize(str) str.dup.split(/[_-]/).map(&:capitalize).join("") end # # Improves indentation and spacing in +str+ for readability in Byebug's # command prompt. # def prettify(str) "\n" + deindent(str) + "\n" end # # Removes a number of leading whitespace for each input line. # def deindent(str, leading_spaces: 6) str.gsub(/^ {#{leading_spaces}}/, "") end end end end byebug-11.1.1/lib/byebug/helpers/thread.rb000066400000000000000000000031061361243113500203300ustar00rootroot00000000000000# frozen_string_literal: true module Byebug module Helpers # # Utilities for thread subcommands # module ThreadHelper def display_context(ctx) puts pr("thread.context", thread_arguments(ctx)) end def thread_arguments(ctx) { status_flag: status_flag(ctx), debug_flag: debug_flag(ctx), id: ctx.thnum, thread: ctx.thread.inspect, file_line: location(ctx), pid: Process.pid, status: ctx.thread.status, current: current_thread?(ctx) } end def current_thread?(ctx) ctx.thread == Thread.current end def context_from_thread(thnum) ctx = Byebug.contexts.find { |c| c.thnum.to_s == thnum } err = if ctx.nil? pr("thread.errors.no_thread") elsif ctx == context pr("thread.errors.current_thread") elsif ctx.ignored? pr("thread.errors.ignored", arg: thnum) end [ctx, err] end private # @todo Check whether it is Byebug.current_context or context def location(ctx) return context.location if ctx == Byebug.current_context backtrace = ctx.thread.backtrace_locations return "" unless backtrace && backtrace[0] "#{backtrace[0].path}:#{backtrace[0].lineno}" end def status_flag(ctx) return "$" if ctx.suspended? current_thread?(ctx) ? "+" : " " end def debug_flag(ctx) ctx.ignored? ? "!" : " " end end end end byebug-11.1.1/lib/byebug/helpers/toggle.rb000066400000000000000000000033251361243113500203450ustar00rootroot00000000000000# frozen_string_literal: true require_relative "parse" module Byebug module Helpers # # Utilities to assist breakpoint/display enabling/disabling. # module ToggleHelper include ParseHelper def enable_disable_breakpoints(is_enable, args) raise pr("toggle.errors.no_breakpoints") if Breakpoint.none? select_breakpoints(is_enable, args).each do |b| enabled = (is_enable == "enable") raise pr("toggle.errors.expression", expr: b.expr) if enabled && !syntax_valid?(b.expr) puts pr("toggle.messages.toggled", bpnum: b.id, endis: enabled ? "en" : "dis") b.enabled = enabled end end def enable_disable_display(is_enable, args) raise pr("toggle.errors.no_display") if n_displays.zero? selected_displays = args ? args.split(/ +/) : [1..n_displays + 1] selected_displays.each do |pos| pos, err = get_int(pos, "#{is_enable} display", 1, n_displays) raise err unless err.nil? Byebug.displays[pos - 1][0] = (is_enable == "enable") end end private def select_breakpoints(is_enable, args) all_breakpoints = Byebug.breakpoints.sort_by(&:id) return all_breakpoints if args.nil? selected_ids = [] args.split(/ +/).each do |pos| last_id = all_breakpoints.last.id pos, err = get_int(pos, "#{is_enable} breakpoints", 1, last_id) raise(ArgumentError, err) unless pos selected_ids << pos end all_breakpoints.select { |b| selected_ids.include?(b.id) } end def n_displays Byebug.displays.size end end end end byebug-11.1.1/lib/byebug/helpers/var.rb000066400000000000000000000024721361243113500176560ustar00rootroot00000000000000# frozen_string_literal: true require_relative "eval" module Byebug module Helpers # # Utilities for variable subcommands # module VarHelper include EvalHelper def var_list(ary, binding = context.frame._binding) vars = ary.sort.map do |name| [name, safe_inspect(silent_eval(name.to_s, binding))] end puts prv(vars, "instance") end def var_global globals = global_variables.reject do |v| %i[$IGNORECASE $= $KCODE $-K $binding].include?(v) end var_list(globals) end def var_instance(str) obj = warning_eval(str || "self") var_list(obj.instance_variables, obj.instance_eval { binding }) end def var_local locals = context.frame.locals cur_self = context.frame._self locals[:self] = cur_self unless cur_self.to_s == "main" puts prv(locals.keys.sort.map { |k| [k, locals[k]] }, "instance") end def var_args args = context.frame.args return if args == [[:rest]] all_locals = context.frame.locals arg_values = args.map { |arg| arg[1] } locals = all_locals.select { |k, _| arg_values.include?(k) } puts prv(locals.keys.sort.map { |k| [k, locals[k]] }, "instance") end end end end byebug-11.1.1/lib/byebug/history.rb000066400000000000000000000052121361243113500171200ustar00rootroot00000000000000# frozen_string_literal: true begin require "readline" rescue LoadError warn <<-MESSAGE Sorry, you can't use byebug without Readline. To solve this, you need to rebuild Ruby with Readline support. If using Ubuntu, try `sudo apt-get install libreadline-dev` and then reinstall your Ruby. MESSAGE raise end module Byebug # # Handles byebug's history of commands. # class History attr_accessor :size def initialize self.size = 0 end # # Array holding the list of commands in history # def buffer Readline::HISTORY.to_a end # # Restores history from disk. # def restore return unless File.exist?(Setting[:histfile]) File.readlines(Setting[:histfile]).reverse_each { |l| push(l.chomp) } end # # Saves history to disk. # def save n_cmds = Setting[:histsize] > size ? size : Setting[:histsize] File.open(Setting[:histfile], "w") do |file| n_cmds.times { file.puts(pop) } end clear end # # Discards history. # def clear size.times { pop } end # # Adds a new command to Readline's history. # def push(cmd) return if ignore?(cmd) self.size += 1 Readline::HISTORY.push(cmd) end # # Removes a command from Readline's history. # def pop self.size -= 1 Readline::HISTORY.pop end # # Prints the requested numbers of history entries. # def to_s(n_cmds) show_size = n_cmds ? specific_max_size(n_cmds) : default_max_size commands = buffer.last(show_size) last_ids(show_size).zip(commands).map do |l| format("%5d %s", position: l[0], command: l[1]) end.join("\n") + "\n" end # # Array of ids of the last +number+ commands. # def last_ids(number) (1 + size - number..size).to_a end # # Max number of commands to be displayed when no size has been specified. # # Never more than Setting[:histsize]. # def default_max_size [Setting[:histsize], self.size].min end # # Max number of commands to be displayed when a size has been specified. # # The only bound here is not showing more items than available. # def specific_max_size(number) [self.size, number].min end # # Whether a specific command should not be stored in history. # # For now, empty lines and consecutive duplicates. # def ignore?(buf) return true if /^\s*$/.match?(buf) return false if Readline::HISTORY.empty? buffer[Readline::HISTORY.length - 1] == buf end end end byebug-11.1.1/lib/byebug/interface.rb000066400000000000000000000061031361243113500173570ustar00rootroot00000000000000# frozen_string_literal: true require_relative "setting" require_relative "history" require_relative "helpers/file" # # Namespace for all of byebug's code # module Byebug # # Main Interface class # # Contains common functionality to all implemented interfaces. # class Interface include Helpers::FileHelper attr_accessor :command_queue, :history attr_reader :input, :output, :error def initialize @command_queue = [] @history = History.new @last_line = "" end def last_if_empty(input) @last_line = input.empty? ? @last_line : input end # # Pops a command from the input stream. # def read_command(prompt) return command_queue.shift unless command_queue.empty? read_input(prompt) end # # Pushes lines in +filename+ to the command queue. # def read_file(filename) command_queue.concat(get_lines(filename)) end # # Reads a new line from the interface's input stream, parses it into # commands and saves it to history. # # @return [String] Representing something to be run by the debugger. # def read_input(prompt, save_hist = true) line = prepare_input(prompt) return unless line history.push(line) if save_hist command_queue.concat(split_commands(line)) command_queue.shift end # # Reads a new line from the interface's input stream. # # @return [String] New string read or the previous string if the string # read now was empty. # def prepare_input(prompt) line = readline(prompt) return unless line last_if_empty(line) end # # Prints an error message to the error stream. # def errmsg(message) error.print("*** #{message}\n") end # # Prints an output message to the output stream. # def puts(message) output.puts(message) end # # Prints an output message to the output stream without a final "\n". # def print(message) output.print(message) end # # Confirms user introduced an affirmative response to the input stream. # def confirm(prompt) readline(prompt) == "y" end def close end # # Saves or clears history according to +autosave+ setting. # def autosave Setting[:autosave] ? history.save : history.clear end # # Restores history according to +autosave+ setting. # def autorestore history.restore if Setting[:autosave] end private # # Splits a command line of the form "cmd1 ; cmd2 ; ... ; cmdN" into an # array of commands: [cmd1, cmd2, ..., cmdN] # def split_commands(cmd_line) return [""] if cmd_line.empty? cmd_line.split(/;/).each_with_object([]) do |v, m| if m.empty? || m.last[-1] != '\\' m << v.strip next end m.last[-1, 1] = "" m.last << ";" << v end end end end require_relative "interfaces/local_interface" require_relative "interfaces/script_interface" require_relative "interfaces/remote_interface" byebug-11.1.1/lib/byebug/interfaces/000077500000000000000000000000001361243113500172155ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/interfaces/local_interface.rb000066400000000000000000000020341361243113500226530ustar00rootroot00000000000000# frozen_string_literal: true module Byebug # # Interface class for standard byebug use. # class LocalInterface < Interface EOF_ALIAS = "continue" def initialize super() @input = $stdin @output = $stdout @error = $stderr end # # Reads a single line of input using Readline. If Ctrl-D is pressed, it # returns "continue", meaning that program's execution will go on. # # @param prompt Prompt to be displayed. # def readline(prompt) with_repl_like_sigint { Readline.readline(prompt) || EOF_ALIAS } end # # Yields the block handling Ctrl-C the following way: if pressed while # waiting for input, the line is reset to only the prompt and we ask for # input again. # # @note Any external 'INT' traps are overriden during this method. # def with_repl_like_sigint orig_handler = trap("INT") { raise Interrupt } yield rescue Interrupt puts("^C") retry ensure trap("INT", orig_handler) end end end byebug-11.1.1/lib/byebug/interfaces/remote_interface.rb000066400000000000000000000016001361243113500230520ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../history" module Byebug # # Interface class for remote use of byebug. # class RemoteInterface < Interface def initialize(socket) super() @input = socket @output = socket @error = socket end def read_command(prompt) super("PROMPT #{prompt}") rescue Errno::EPIPE, Errno::ECONNABORTED "continue" end def confirm(prompt) super("CONFIRM #{prompt}") rescue Errno::EPIPE, Errno::ECONNABORTED false end def print(message) super(message) rescue Errno::EPIPE, Errno::ECONNABORTED nil end def puts(message) super(message) rescue Errno::EPIPE, Errno::ECONNABORTED nil end def close output.close end def readline(prompt) puts(prompt) (input.gets || "continue").chomp end end end byebug-11.1.1/lib/byebug/interfaces/script_interface.rb000066400000000000000000000012101361243113500230600ustar00rootroot00000000000000# frozen_string_literal: true module Byebug # # Interface class for command execution from script files. # class ScriptInterface < Interface def initialize(file, verbose = false) super() @verbose = verbose @input = File.open(file) @output = verbose ? $stdout : StringIO.new @error = $stderr end def read_command(prompt) readline(prompt, false) end def close input.close end def readline(*) while (result = input.gets) output.puts "+ #{result}" if @verbose next if /^\s*#/.match?(result) return result.chomp end end end end byebug-11.1.1/lib/byebug/interfaces/test_interface.rb000066400000000000000000000021321361243113500225370ustar00rootroot00000000000000# frozen_string_literal: true module Byebug # # Custom interface for easier assertions # class TestInterface < Interface attr_accessor :test_block def initialize super() clear end def errmsg(message) error.concat(prepare(message)) end def print(message) output.concat(prepare(message)) end def puts(message) output.concat(prepare(message)) end def read_command(prompt) cmd = super(prompt) return cmd unless cmd.nil? && test_block test_block.call self.test_block = nil end def clear @input = [] @output = [] @error = [] history.clear end def inspect [ "Input:", input.join("\n"), "Output:", output.join("\n"), "Error:", error.join("\n") ].join("\n") end def readline(prompt) puts(prompt) cmd = input.shift cmd.is_a?(Proc) ? cmd.call : cmd end private def prepare(message) return message.map(&:to_s) if message.respond_to?(:map) message.to_s.split("\n") end end end byebug-11.1.1/lib/byebug/option_setter.rb000066400000000000000000000035321361243113500203200ustar00rootroot00000000000000# frozen_string_literal: true module Byebug # # Handles byebug's command line options # class OptionSetter def initialize(runner, opts) @runner = runner @opts = opts end def setup debug include_flag post_mortem quit rc stop require_flag remote trace version help end private def debug @opts.on "-d", "--debug", "Set $DEBUG=true" do $DEBUG = true end end def include_flag @opts.on "-I", "--include list", "Add to paths to $LOAD_PATH" do |list| $LOAD_PATH.push(list.split(":")).flatten! end end def post_mortem @opts.on "-m", "--[no-]post-mortem", "Use post-mortem mode" do |v| Setting[:post_mortem] = v end end def quit @opts.on "-q", "--[no-]quit", "Quit when script finishes" do |v| @runner.quit = v end end def rc @opts.on "-x", "--[no-]rc", "Run byebug initialization file" do |v| @runner.init_script = v end end def stop @opts.on "-s", "--[no-]stop", "Stop when script is loaded" do |v| @runner.stop = v end end def require_flag @opts.on "-r", "--require file", "Require library before script" do |lib| require lib end end def remote @opts.on "-R", "--remote [host:]port", "Remote debug [host:]port" do |p| @runner.remote = p end end def trace @opts.on "-t", "--[no-]trace", "Turn on line tracing" do |v| Setting[:linetrace] = v end end def version @opts.on "-v", "--version", "Print program version" do @runner.version = Byebug::VERSION end end def help @opts.on "-h", "--help", "Display this message" do @runner.help = @opts.help end end end end byebug-11.1.1/lib/byebug/printers/000077500000000000000000000000001361243113500167405ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/printers/base.rb000066400000000000000000000032001361243113500201720ustar00rootroot00000000000000# frozen_string_literal: true require "yaml" module Byebug module Printers # # Base printer # class Base class MissedPath < StandardError; end class MissedArgument < StandardError; end SEPARATOR = "." def type self.class.name.split("::").last.downcase end private def locate(path) result = nil contents.each_value do |contents| result = parts(path).reduce(contents) do |r, part| r&.key?(part) ? r[part] : nil end break if result end raise MissedPath, "Can't find part path '#{path}'" unless result result end def translate(string, args = {}) # they may contain #{} string interpolation string.gsub(/\|\w+$/, "").gsub(/([^#]?){([^}]*)}/) do key = Regexp.last_match[2].to_s raise MissedArgument, "Missed argument #{key} for '#{string}'" unless args.key?(key.to_sym) "#{Regexp.last_match[1]}#{args[key.to_sym]}" end end def parts(path) path.split(SEPARATOR) end def contents @contents ||= contents_files.each_with_object({}) do |filename, hash| hash[filename] = YAML.load_file(filename) || {} end end def array_of_args(collection, &_block) collection_with_index = collection.each.with_index collection_with_index.each_with_object([]) do |(item, index), array| args = yield item, index array << args if args end end def contents_files [File.join(__dir__, "texts", "base.yml")] end end end end byebug-11.1.1/lib/byebug/printers/plain.rb000066400000000000000000000020521361243113500203670ustar00rootroot00000000000000# frozen_string_literal: true require_relative "base" module Byebug module Printers # # Plain text printer # class Plain < Base def print(path, args = {}) message = translate(locate(path), args) tail = parts(path).include?("confirmations") ? " (y/n) " : "\n" message << tail end def print_collection(path, collection, &block) lines = array_of_args(collection, &block).map do |args| print(path, args) end lines.join end def print_variables(variables, *_unused) print_collection("variable.variable", variables) do |(key, value), _| value = value.nil? ? "nil" : value.to_s if "#{key} = #{value}".size > Setting[:width] key_size = "#{key} = ".size value = value[0..Setting[:width] - key_size - 4] + "..." end { key: key, value: value } end end private def contents_files [File.join(__dir__, "texts", "plain.yml")] + super end end end end byebug-11.1.1/lib/byebug/printers/texts/000077500000000000000000000000001361243113500201075ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/printers/texts/base.yml000066400000000000000000000065511361243113500215530ustar00rootroot00000000000000base: errors: only_local: "Command is available only in local mode." break: errors: line: "Line {line} is not a valid breakpoint in file {file}.\n\nValid break points are:\n{valid_breakpoints}" location: "Invalid breakpoint location" state: "We are not in a state that has an associated file" class: "Unknown class {klass}" far_line: "There are only {lines} lines in file {file}" source: "No file named {file}" expression: "Incorrect expression \"{expr}\"; breakpoint disabled" no_breakpoint: "Invalid breakpoint id. Use \"info breakpoint\" to find out the correct id" no_breakpoint_delete: "No breakpoint number {pos}" not_changed: "Incorrect expression \"{expr}\", breakpoint not changed" confirmations: delete_all: "Delete all breakpoints?" messages: breakpoint_deleted: "Deleted breakpoint {pos}" catch: added: "Catching exception {exception}." removed: "Catch for exception {exception} removed" errors: off: "Off expected. Got {off}" not_class: "Warning {class} is not known to be a Class" not_found: "Catch for exception {exception} not found" confirmations: delete_all: "Delete all catchpoints? (y or n) " condition: errors: no_breakpoints: "No breakpoints have been set" continue: errors: unstopped_line: "Line {line} is not a valid stopping point in file" display: confirmations: clear_all: "Clear all expressions?" errors: undefined: "Display expression {expr} is not defined" edit: errors: state: "We are not in a state that has an associated file" file_line: "Invalid file[:line] number specification: {file_line}" not_readable: "File {file} is not readable." not_exist: "File {file} does not exist." frame: errors: too_low: "Can't navigate beyond the oldest frame" too_high: "Can't navigate beyond the newest frame" c_frame: "Can't navigate to c-frame" info: errors: undefined_file: "{file} is not a valid source file" pry: errors: not_installed: "You need to install pry in order to run this command" quit: confirmations: really: "Really quit?" save: messages: done: "Saved to '{path}'" set: errors: unknown_setting: "Unknown setting :{key}" must_specify_value: "You must specify a value for setting :{key}" on_off: "Expecting 'on', 1, true, 'off', 0, false. Got: {arg}." show: errors: unknown_setting: "Unknown setting :{key}" source: errors: not_found: "File \"{file}\" not found" thread: errors: no_thread: "No such thread" current_thread: "It's the current thread" wrong_action: "Can't {subcmd} thread {arg}" already_running: "Already running" toggle: errors: no_breakpoints: "No breakpoints have been set" no_display: "No display expressions have been set" syntax: "\"{toggle}\" must be followed by \"display\", \"breakpoints\" or breakpoint ids" expression: "Expression \"{expr}\" syntactically incorrect; breakpoint remains disabled." messages: toggled: "Breakpoint {bpnum} {endis}abled" parse: errors: int: too_low: "\"{cmd}\" argument \"{str}\" needs to be at least {min}" too_high: "\"{cmd}\" argument \"{str}\" needs to be at most {max}" not_number: "\"{cmd}\" argument \"{str}\" needs to be a number" variable: errors: not_module: "Should be Class/Module: {object}" cant_get_class_vars: "can't get class variables here.\n" byebug-11.1.1/lib/byebug/printers/texts/plain.yml000066400000000000000000000013501361243113500217340ustar00rootroot00000000000000break: created: "Created breakpoint {id} at {file}:{line}" display: result: "{n}: {exp} = {result}" eval: exception: "{text_message}" result: "{result}" frame: line: "{mark} #{pos} {call} at {file}:{line}" method: methods: "{name}|c" restart: success: "Re exec'ing:\n {cmd}" thread: context: "{status_flag}{debug_flag}{id} {thread} {file_line}" trace: messages: success: "Tracing global variable \"{var}\"." on_change: "traced global variable '{name}' has value '{value}'" undo: "Not tracing global variable \"{var}\" anymore." errors: var_is_not_global: "'{name}' is not a global variable." needs_global_variable: "tracevar needs a global variable name" variable: variable: "{key} = {value}" byebug-11.1.1/lib/byebug/processors/000077500000000000000000000000001361243113500172745ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/processors/command_processor.rb000066400000000000000000000063131361243113500233410ustar00rootroot00000000000000# frozen_string_literal: true require "forwardable" require_relative "../helpers/eval" require_relative "../errors" module Byebug # # Processes commands in regular mode. # # You can override this class to create your own command processor that, for # example, whitelists only certain commands to be executed. # # @see PostMortemProcessor for a example # class CommandProcessor include Helpers::EvalHelper attr_accessor :prev_line attr_reader :context, :interface def initialize(context, interface = LocalInterface.new) @context = context @interface = interface @proceed = false @prev_line = nil end def printer @printer ||= Printers::Plain.new end extend Forwardable def_delegators :@context, :frame def_delegator :printer, :print, :pr def_delegator :printer, :print_collection, :prc def_delegator :printer, :print_variables, :prv def_delegators :interface, :errmsg, :puts, :confirm def_delegators :Byebug, :commands # # Available commands # def command_list @command_list ||= CommandList.new(commands) end def at_line process_commands end def at_tracing puts "Tracing: #{context.full_location}" run_auto_cmds(2) end def at_breakpoint(brkpt) number = Byebug.breakpoints.index(brkpt) + 1 puts "Stopped by breakpoint #{number} at #{frame.file}:#{frame.line}" end def at_catchpoint(exception) puts "Catchpoint at #{context.location}: `#{exception}'" end def at_return(return_value) puts "Return value is: #{safe_inspect(return_value)}" process_commands end def at_end process_commands end # # Let the execution continue # def proceed! @proceed = true end # # Handle byebug commands. # def process_commands before_repl repl ensure after_repl end protected # # Prompt shown before reading a command. # def prompt "(byebug) " end def before_repl @proceed = false @prev_line = nil run_auto_cmds(1) interface.autorestore end def after_repl interface.autosave end # # Main byebug's REPL # def repl until @proceed cmd = interface.read_command(prompt) return if cmd.nil? next if cmd == "" run_cmd(cmd) end end private def auto_cmds_for(run_level) command_list.select { |cmd| cmd.always_run >= run_level } end # # Run permanent commands. # def run_auto_cmds(run_level) safely do auto_cmds_for(run_level).each { |cmd| cmd.new(self).execute } end end # # Executes the received input # # Instantiates a command matching the input and runs it. If a matching # command is not found, it evaluates the unknown input. # def run_cmd(input) safely do command = command_list.match(input) return command.new(self, input).execute if command puts safe_inspect(multiple_thread_eval(input)) end end def safely yield rescue StandardError => e errmsg(e.message) end end end byebug-11.1.1/lib/byebug/processors/control_processor.rb000066400000000000000000000006261361243113500234040ustar00rootroot00000000000000# frozen_string_literal: true require_relative "command_processor" module Byebug # # Processes commands when there's not program running # class ControlProcessor < CommandProcessor # # Available commands # def commands super.select(&:allow_in_control) end # # Prompt shown before reading a command. # def prompt "(byebug:ctrl) " end end end byebug-11.1.1/lib/byebug/processors/post_mortem_processor.rb000066400000000000000000000004711361243113500242720ustar00rootroot00000000000000# frozen_string_literal: true require_relative "command_processor" module Byebug # # Processes commands in post_mortem mode # class PostMortemProcessor < CommandProcessor def commands super.select(&:allow_in_post_mortem) end def prompt "(byebug:post_mortem) " end end end byebug-11.1.1/lib/byebug/processors/script_processor.rb000066400000000000000000000014461361243113500232310ustar00rootroot00000000000000# frozen_string_literal: true require_relative "command_processor" module Byebug # # Processes commands from a file # class ScriptProcessor < CommandProcessor # # Available commands # def commands super.select(&:allow_in_control) end def repl while (input = interface.read_command(prompt)) safely do command = command_list.match(input) raise CommandNotFound.new(input) unless command command.new(self, input).execute end end end def after_repl super interface.close end # # Prompt shown before reading a command. # def prompt "(byebug:ctrl) " end private def without_exceptions yield rescue StandardError nil end end end byebug-11.1.1/lib/byebug/remote.rb000066400000000000000000000036021361243113500167130ustar00rootroot00000000000000# frozen_string_literal: true require "socket" require_relative "processors/control_processor" require_relative "remote/server" require_relative "remote/client" # # Remote debugging functionality. # module Byebug # Port number used for remote debugging PORT = 8989 unless defined?(PORT) class << self # If in remote mode, wait for the remote connection attr_accessor :wait_connection # The actual port that the server is started at def actual_port server.actual_port end # The actual port that the control server is started at def actual_control_port control.actual_port end # # Interrupts the current thread # def interrupt current_context.interrupt end # # Starts the remote server main thread # def start_server(host = nil, port = PORT) start_control(host, port.zero? ? 0 : port + 1) server.start(host, port) end # # Starts the remote server control thread # def start_control(host = nil, port = PORT + 1) control.start(host, port) end # # Connects to the remote byebug # def start_client(host = "localhost", port = PORT) client.start(host, port) end def parse_host_and_port(host_port_spec) location = host_port_spec.split(":") location[1] ? [location[0], location[1].to_i] : ["localhost", location[0]] end private def client @client ||= Remote::Client.new(Context.interface) end def server @server ||= Remote::Server.new(wait_connection: wait_connection) do |s| Context.interface = RemoteInterface.new(s) end end def control @control ||= Remote::Server.new(wait_connection: false) do |s| context = Byebug.current_context interface = RemoteInterface.new(s) ControlProcessor.new(context, interface).process_commands end end end end byebug-11.1.1/lib/byebug/remote/000077500000000000000000000000001361243113500163655ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/remote/client.rb000066400000000000000000000022201361243113500201640ustar00rootroot00000000000000# frozen_string_literal: true require "socket" module Byebug module Remote # # Client for remote debugging # class Client attr_reader :interface, :socket def initialize(interface) @interface = interface @socket = nil end # # Connects to the remote byebug # def start(host = "localhost", port = PORT) connect_at(host, port) while (line = socket.gets) case line when /^PROMPT (.*)$/ input = interface.read_command(Regexp.last_match[1]) break unless input socket.puts input when /^CONFIRM (.*)$/ input = interface.readline(Regexp.last_match[1]) break unless input socket.puts input else interface.puts line end end socket.close end def started? !socket.nil? end private def connect_at(host, port) interface.puts "Connecting to byebug server at #{host}:#{port}..." @socket = TCPSocket.new(host, port) interface.puts "Connected." end end end end byebug-11.1.1/lib/byebug/remote/server.rb000066400000000000000000000017331361243113500202240ustar00rootroot00000000000000# frozen_string_literal: true require "socket" module Byebug module Remote # # Server for remote debugging # class Server attr_reader :actual_port, :wait_connection def initialize(wait_connection:, &block) @thread = nil @wait_connection = wait_connection @main_loop = block end # # Start the remote debugging server # def start(host, port) return if @thread if wait_connection mutex = Mutex.new proceed = ConditionVariable.new end server = TCPServer.new(host, port) @actual_port = server.addr[1] yield if block_given? @thread = DebugThread.new do while (session = server.accept) @main_loop.call(session) mutex.synchronize { proceed.signal } if wait_connection end end mutex.synchronize { proceed.wait(mutex) } if wait_connection end end end end byebug-11.1.1/lib/byebug/runner.rb000066400000000000000000000101551361243113500167320ustar00rootroot00000000000000# frozen_string_literal: true require "optparse" require "English" require_relative "core" require_relative "version" require_relative "helpers/bin" require_relative "helpers/parse" require_relative "helpers/string" require_relative "option_setter" require_relative "processors/control_processor" module Byebug # # Responsible for starting the debugger when started from the command line. # class Runner include Helpers::BinHelper include Helpers::ParseHelper include Helpers::StringHelper # # Special working modes that don't actually start the debugger. # attr_reader :help, :version, :remote # # Signals that we should exit after the debugged program is finished. # attr_accessor :quit # # Signals that we should stop before program starts # attr_accessor :stop # # Signals that we should run rc scripts before program starts # attr_writer :init_script # # @param stop [Boolean] Whether the runner should stop right before # starting the program. # # @param quit [Boolean] Whether the runner should quit right after # finishing the program. # def initialize(stop = true, quit = true) @stop = stop @quit = quit end def help=(text) @help ||= text interface.puts("#{text}\n") end def version=(number) @version ||= number interface.puts prettify <<-VERSION Running byebug #{number} VERSION end def remote=(host_and_port) @remote ||= Byebug.parse_host_and_port(host_and_port) Byebug.start_client(*@remote) end def init_script defined?(@init_script) ? @init_script : true end # # Usage banner. # def banner prettify <<-BANNER byebug #{Byebug::VERSION} Usage: byebug [options] -- BANNER end # # Starts byebug to debug a program. # def run Byebug.mode = :standalone option_parser.order!($ARGV) return if non_script_option? || error_in_script? $PROGRAM_NAME = program Byebug.run_init_script if init_script loop do debug_program break if quit ControlProcessor.new(nil, interface).process_commands end end def interface @interface ||= Context.interface end # # Processes options passed from the command line. # def option_parser @option_parser ||= OptionParser.new(banner, 25) do |opts| opts.banner = banner OptionSetter.new(self, opts).setup end end def program @program ||= begin candidate = which($ARGV.shift) if [which("ruby"), RbConfig.ruby].include?(candidate) which($ARGV.shift) else candidate end end end # # An option that doesn't need a script specified was given # def non_script_option? version || help || remote end # # There is an error with the specified script # def error_in_script? no_script? || non_existing_script? || invalid_script? end # # No script to debug specified # def no_script? return false unless $ARGV.empty? print_error("You must specify a program to debug") true end # # Extracts debugged program from command line args. # def non_existing_script? return false if program print_error("The script doesn't exist") true end # # Checks the debugged script has correct syntax # def invalid_script? return false if syntax_valid?(File.read(program)) print_error("The script has incorrect syntax") true end # # Debugs a script only if syntax checks okay. # def debug_program error = Byebug.debug_load(program, stop) puts "#{error}\n#{error.backtrace}" if error end # # Prints an error message and a help string # def print_error(msg) interface.errmsg(msg) interface.puts(option_parser.help) end end end byebug-11.1.1/lib/byebug/setting.rb000066400000000000000000000031621361243113500170760ustar00rootroot00000000000000# frozen_string_literal: true require_relative "helpers/string" module Byebug # # Parent class for all byebug settings. # class Setting attr_accessor :value DEFAULT = false def initialize @value = self.class::DEFAULT end def boolean? [true, false].include?(value) end def integer? Integer(value) ? true : false rescue ArgumentError false end def help prettify(banner) end def to_sym name = self.class.name.gsub(/^Byebug::/, "").gsub(/Setting$/, "") name.gsub(/(.)([A-Z])/, '\1_\2').downcase.to_sym end def to_s "#{to_sym} is #{value ? 'on' : 'off'}\n" end class << self def settings @settings ||= {} end def [](name) settings[name].value end def []=(name, value) settings[name].value = value end def find(shortcut) abbr = /^no/.match?(shortcut) ? shortcut[2..-1] : shortcut matches = settings.select do |key, value| key =~ (value.boolean? ? /#{abbr}/ : /#{shortcut}/) end matches.size == 1 ? matches.values.first : nil end # # @todo DRY this up. Very similar code exists in the CommandList class # def help_all output = " List of supported settings:\n\n" width = settings.keys.max_by(&:size).size settings.each_value do |sett| output += format( " %-#{width}s -- %s\n", name: sett.to_sym, description: sett.banner ) end output + "\n" end end end end byebug-11.1.1/lib/byebug/settings/000077500000000000000000000000001361243113500167325ustar00rootroot00000000000000byebug-11.1.1/lib/byebug/settings/autoirb.rb000066400000000000000000000007461361243113500207330ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" require_relative "../commands/irb" module Byebug # # Setting for automatically invoking IRB on every stop. # class AutoirbSetting < Setting DEFAULT = 0 def initialize IrbCommand.always_run = DEFAULT end def banner "Invoke IRB on every stop" end def value=(val) IrbCommand.always_run = val ? 1 : 0 end def value IrbCommand.always_run == 1 end end end byebug-11.1.1/lib/byebug/settings/autolist.rb000066400000000000000000000007731361243113500211320ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" require_relative "../commands/list" module Byebug # # Setting for automatically listing source code on every stop. # class AutolistSetting < Setting DEFAULT = 1 def initialize ListCommand.always_run = DEFAULT end def banner "Invoke list command on every stop" end def value=(val) ListCommand.always_run = val ? 1 : 0 end def value ListCommand.always_run == 1 end end end byebug-11.1.1/lib/byebug/settings/autopry.rb000066400000000000000000000007461361243113500207710ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" require_relative "../commands/pry" module Byebug # # Setting for automatically invoking Pry on every stop. # class AutoprySetting < Setting DEFAULT = 0 def initialize PryCommand.always_run = DEFAULT end def banner "Invoke Pry on every stop" end def value=(val) PryCommand.always_run = val ? 1 : 0 end def value PryCommand.always_run == 1 end end end byebug-11.1.1/lib/byebug/settings/autosave.rb000066400000000000000000000005201361243113500211030ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" module Byebug # # Setting for automatically saving previously entered commands to history # when exiting the debugger. # class AutosaveSetting < Setting DEFAULT = true def banner "Automatically save command history record on exit" end end end byebug-11.1.1/lib/byebug/settings/basename.rb000066400000000000000000000005131361243113500210310ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" module Byebug # # Command to display short paths in file names. # # For example, when displaying source code information. # class BasenameSetting < Setting def banner ": information after every stop uses short paths" end end end byebug-11.1.1/lib/byebug/settings/callstyle.rb000066400000000000000000000005611361243113500212550ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" module Byebug # # Setting to customize the verbosity level for stack frames. # class CallstyleSetting < Setting DEFAULT = "long" def banner "Set how you want method call parameters to be displayed" end def to_s "Frame display callstyle is '#{value}'" end end end byebug-11.1.1/lib/byebug/settings/fullpath.rb000066400000000000000000000004121361243113500210730ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" module Byebug # # Setting to display full paths in backtraces. # class FullpathSetting < Setting DEFAULT = true def banner "Display full file names in backtraces" end end end byebug-11.1.1/lib/byebug/settings/histfile.rb000066400000000000000000000006311361243113500210660ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" module Byebug # # Setting to customize the file where byebug's history is saved. # class HistfileSetting < Setting DEFAULT = File.expand_path(".byebug_history") def banner "File where cmd history is saved to. Default: ./.byebug_history" end def to_s "The command history file is #{value}\n" end end end byebug-11.1.1/lib/byebug/settings/histsize.rb000066400000000000000000000006241361243113500211230ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" module Byebug # # Setting to customize the number of byebug commands to be saved in history. # class HistsizeSetting < Setting DEFAULT = 256 def banner "Maximum number of commands that can be stored in byebug history" end def to_s "Maximum size of byebug's command history is #{value}" end end end byebug-11.1.1/lib/byebug/settings/linetrace.rb000066400000000000000000000005161361243113500212270ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" module Byebug # # Setting to enable/disable linetracing. # class LinetraceSetting < Setting def banner "Enable line execution tracing" end def value=(val) Byebug.tracing = val end def value Byebug.tracing? end end end byebug-11.1.1/lib/byebug/settings/listsize.rb000066400000000000000000000006411361243113500211260ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" module Byebug # # Setting to customize the number of source code lines to be displayed every # time the "list" command is invoked. # class ListsizeSetting < Setting DEFAULT = 10 def banner "Set number of source lines to list by default" end def to_s "Number of source lines to list is #{value}\n" end end end byebug-11.1.1/lib/byebug/settings/post_mortem.rb000066400000000000000000000007531361243113500216340ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" module Byebug # # Setting to enable/disable post_mortem mode, i.e., a debugger prompt after # program termination by unhandled exception. # class PostMortemSetting < Setting def initialize Byebug.post_mortem = DEFAULT end def banner "Enable/disable post-mortem mode" end def value=(val) Byebug.post_mortem = val end def value Byebug.post_mortem? end end end byebug-11.1.1/lib/byebug/settings/savefile.rb000066400000000000000000000006471361243113500210640ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" module Byebug # # Setting to customize the file where byebug's history is saved. # class SavefileSetting < Setting DEFAULT = File.expand_path("#{ENV['HOME'] || '.'}/.byebug_save") def banner "File where settings are saved to. Default: ~/.byebug_save" end def to_s "The command history file is #{value}\n" end end end byebug-11.1.1/lib/byebug/settings/stack_on_error.rb000066400000000000000000000004621361243113500222730ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" module Byebug # # Setting to enable/disable the display of backtraces when evaluations raise # errors. # class StackOnErrorSetting < Setting def banner "Display stack trace when `eval` raises an exception" end end end byebug-11.1.1/lib/byebug/settings/width.rb000066400000000000000000000005521361243113500204000ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../setting" module Byebug # # Setting to customize the maximum width of byebug's output. # class WidthSetting < Setting DEFAULT = 160 def banner "Number of characters per line in byebug's output" end def to_s "Maximum width of byebug's output is #{value}" end end end byebug-11.1.1/lib/byebug/source_file_formatter.rb000066400000000000000000000025031361243113500220010ustar00rootroot00000000000000# frozen_string_literal: true require_relative "helpers/file" require_relative "setting" module Byebug # # Formats specific line ranges in a source file # class SourceFileFormatter include Helpers::FileHelper attr_reader :file, :annotator def initialize(file, annotator) @file = file @annotator = annotator end def lines(min, max) File.foreach(file).with_index.map do |line, lineno| next unless (min..max).cover?(lineno + 1) format( "%s %#{max.to_s.size}d: %s", annotation: annotator.call(lineno + 1), lineno: lineno + 1, source: line ) end end def lines_around(center) lines(*range_around(center)) end def range_around(center) range_from(center - size / 2) end def range_from(min) first = amend_initial(min) [first, first + size - 1] end def amend_initial(line) amend(line, max_initial_line) end def amend_final(line) amend(line, max_line) end def max_initial_line max_line - size + 1 end def max_line @max_line ||= n_lines(file) end def size [Setting[:listsize], max_line].min end def amend(line, ceiling) [ceiling, [1, line].max].min end end end byebug-11.1.1/lib/byebug/subcommands.rb000066400000000000000000000021231361243113500177300ustar00rootroot00000000000000# frozen_string_literal: true require "forwardable" require_relative "helpers/reflection" require_relative "command_list" module Byebug # # Subcommand additions. # module Subcommands def self.included(command) command.extend(ClassMethods) end extend Forwardable def_delegators "self.class", :subcommand_list # # Delegates to subcommands or prints help if no subcommand specified. # def execute subcmd_name = @match[1] return puts(help) unless subcmd_name subcmd = subcommand_list.match(subcmd_name) raise CommandNotFound.new(subcmd_name, self.class) unless subcmd subcmd.new(processor, arguments).execute end # # Class methods added to subcommands # module ClassMethods include Helpers::ReflectionHelper # # Default help text for a command with subcommands # def help super + subcommand_list.to_s end # # Command's subcommands. # def subcommand_list @subcommand_list ||= CommandList.new(commands) end end end end byebug-11.1.1/lib/byebug/version.rb000066400000000000000000000001751361243113500171070ustar00rootroot00000000000000# frozen_string_literal: true # # Reopen main module to define the library version # module Byebug VERSION = "11.1.1" end byebug-11.1.1/tasks/000077500000000000000000000000001361243113500141745ustar00rootroot00000000000000byebug-11.1.1/tasks/linter.rb000066400000000000000000000040111361243113500160120ustar00rootroot00000000000000# frozen_string_literal: true # # Common stuff for a linter # module LinterMixin def run offenses = [] applicable_files.each do |file| if clean?(file) print "." else offenses << file print "F" end end print "\n" return if offenses.empty? raise failure_message_for(offenses) end private def failure_message_for(offenses) msg = "#{self.class.name} detected offenses. " msg += if respond_to?(:fixing_cmd) "Run `#{fixing_cmd(offenses)}` to fix them." else "Affected files: #{offenses.join(' ')}" end msg end end # # Lints C files # class CLangFormatLinter include LinterMixin def applicable_files Dir.glob("ext/byebug/*.[ch]") end def fixing_cmd(offenses) "#{clang_format} -i #{offenses.join(' ')} -style=file" end def clean?(file) linted, status = Open3.capture2("#{clang_format} #{file} -style=file") status.success? && linted == File.read(file) end private def clang_format "clang-format-8" end end # # Lints executability of files # class ExecutableLinter include LinterMixin def applicable_files Open3.capture2("git ls-files")[0].split end def clean?(file) in_exec_folder = !(%r{(exe|bin)/} =~ file).nil? executable = File.executable?(file) (in_exec_folder && executable) || (!in_exec_folder && !executable) end end # # Checks no tabs in source code # class TabLinter include LinterMixin def applicable_files Open3.capture2("git ls-files")[0].split end def clean?(file) relative_path = Pathname.new(__FILE__).relative_path_from(Pathname.new(File.dirname(__dir__))).to_s file == relative_path || !File.read(file, encoding: Encoding::UTF_8).include?(" ") end end # # Checks trailing whitespace # class TrailingWhitespaceLinter include LinterMixin def applicable_files Open3.capture2("git ls-files")[0].split end def clean?(file) File.read(file, encoding: Encoding::UTF_8) !~ / +$/ end end byebug-11.1.1/test/000077500000000000000000000000001361243113500140265ustar00rootroot00000000000000byebug-11.1.1/test/changelog_test.rb000066400000000000000000000010451361243113500173410ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests sanity of changelog file. # class ChangelogTest < Minitest::Test def test_has_definitions_for_all_releases changelog.scan(/\[([0-9]+\.[0-9]+\.[0-9]+)\] /).flatten.uniq.each do |release_version| assert_match %r{^\[#{release_version}\]: https://github\.com/deivid-rodriguez/byebug/compare/v[0-9]+\.[0-9]+\.[0-9]+...v#{release_version}}, changelog end end private def changelog File.read("CHANGELOG.md") end end end byebug-11.1.1/test/commands/000077500000000000000000000000001361243113500156275ustar00rootroot00000000000000byebug-11.1.1/test/commands/break_test.rb000066400000000000000000000314461361243113500203070ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests adding breakpoints to methods # class BreakAtMethodsTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test breakpoints 4: # 5: class #{example_class} 6: def self.a(n) 7: 2.times do 8: n += 1 9: end 10: end 11: 12: def b 13: 3 14: end 15: end 16: 17: module #{example_module} 18: def self.c 19: 1 20: end 21: end 22: 23: byebug 24: 25: #{example_class}.new.b 26: #{example_class}.a(1) 27: #{example_module}.c 28: end RUBY end def test_break_with_instance_method_stops_at_correct_place enter "break #{example_class}#b", "cont" if RUBY_VERSION >= "2.5.0" debug_code(program) { assert_location example_path, 13 } else debug_code(program) { assert_location example_path, 12 } end end def test_break_with_namespaced_instance_method_stops_at_correct_place enter "break Byebug::#{example_class}#b", "cont" if RUBY_VERSION >= "2.5.0" debug_code(program) { assert_location example_path, 13 } else debug_code(program) { assert_location example_path, 12 } end end def test_break_with_class_method_stops_at_correct_place enter "break #{example_class}.a", "cont" if RUBY_VERSION >= "2.5.0" debug_code(program) { assert_location example_path, 7 } else debug_code(program) { assert_location example_path, 6 } end end def test_break_with_namespaced_class_method_stops_at_correct_place enter "break Byebug::#{example_class}.a", "cont" if RUBY_VERSION >= "2.5.0" debug_code(program) { assert_location example_path, 7 } else debug_code(program) { assert_location example_path, 6 } end end def test_break_with_module_method_stops_at_correct_place enter "break #{example_module}.c", "cont" if RUBY_VERSION >= "2.5.0" debug_code(program) { assert_location(example_path, 19) } else debug_code(program) { assert_location(example_path, 18) } end end def test_break_with_namespaced_module_method_stops_at_correct_place enter "break Byebug::#{example_module}.c", "cont" if RUBY_VERSION >= "2.5.0" debug_code(program) { assert_location example_path, 19 } else debug_code(program) { assert_location example_path, 18 } end end def test_break_with_a_method_does_not_stop_at_blocks_in_the_method enter "break #{example_class}.a", "cont", "break 8", "cont" debug_code(program) { assert_location example_path, 8 } end def test_setting_breakpoint_to_an_undefined_class_creates_breakpoint enter "break B.a" debug_code(program) check_output_includes(/Created breakpoint/) end def test_setting_breakpoint_to_an_undefined_class_shows_error_message enter "break ::B.a" debug_code(program) check_error_includes "Warning: breakpoint source is not yet defined" end def test_setting_breakpoint_to_invalid_location_does_not_create_breakpoint enter "break foo" debug_code(program) { assert_empty Byebug.breakpoints } end def test_setting_breakpoint_to_invalid_location_shows_an_error enter "break foo" debug_code(program) check_error_includes "Invalid breakpoint location" end end # # Tests adding breakpoints to empty methods # class BreakAtEmptyMethodsTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test breakpoints 4: # 5: class #{example_class} 6: def self.a 7: end 8: 9: def b 10: 11: end 12: def c; end 13: end 14: 15: byebug 16: 17: #{example_class}.new.b 18: #{example_class}.a 19: #{example_class}.new.c 20: end RUBY end def test_break_with_instance_method_stops_at_correct_place # instance method #b has extra empty line intentionally # to test lineno 10 is not displayed. enter "break #{example_class}#b", "cont" debug_code(program) { assert_location example_path, 9 } end def test_break_with_oneline_instance_method_stops_at_correct_place enter "break #{example_class}#c", "cont" debug_code(program) { assert_location example_path, 12 } end def test_break_with_class_method_stops_at_correct_place enter "break #{example_class}.a", "cont" debug_code(program) { assert_location example_path, 6 } end end # # Tests adding breakpoints to lines # class BreakAtLinesTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test breakpoints 4: # 5: class #{example_class} 6: def self.a 7: y = 1 8: z = 2 9: y + z 10: end 11: end 12: 13: byebug 14: 15: #{example_class}.a 16: end RUBY end def test_setting_breakpoint_sets_correct_fields enter "break 7" debug_code(program) do b = Breakpoint.first exp = [b.pos, b.source, b.expr, b.hit_count, b.hit_value, b.enabled?] act = [7, example_path, nil, 0, 0, true] assert_equal act, exp end end def test_setting_breakpoint_using_shortcut_properly_adds_the_breakpoint enter "break 7" debug_code(program) { assert_equal 1, Byebug.breakpoints.size } end def test_setting_breakpoint_to_nonexistent_line_does_not_create_breakpoint enter "break 1000" debug_code(program) { assert_empty Byebug.breakpoints } end def test_setting_breakpoint_to_nonexistent_file_does_not_create_breakpoint enter "break asf:324" debug_code(program) { assert_empty Byebug.breakpoints } end def test_setting_breakpoint_to_nonexistent_file_shows_an_error enter "break asf:234" debug_code(program) check_error_includes "No file named asf" end def test_setting_breakpoint_with_bad_relative_path_doesnt_crash enter "break ../relative/path.rb:8" debug_code(program) check_error_includes "No file named ../relative/path.rb" end def test_setting_breakpoint_with_relative_path_adds_the_breakpoint enter "break ./test/commands/break_test.rb:3" debug_code(program) check_output_includes(/Created breakpoint/) end def test_setting_breakpoint_with_space_in_path_adds_the_breakpoint with_new_file("hello world.rb", 'puts "Hello World!"') do enter "break hello world.rb:1" debug_code(program) check_output_includes(/Created breakpoint/) end end def test_setting_breakpoint_to_nonexistent_file_with_space_shows_an_error enter "break /this path/isnt there/abc xyz:8" debug_code(program) check_error_includes "No file named /this path/isnt there/abc xyz" end def test_setting_breakpoint_to_path_with_colons_does_not_crash enter "break C:/bb.rb:1" debug_code(program) check_error_includes "No file named C:/bb.rb" end def test_setting_breakpoint_to_invalid_line_does_not_create_breakpoint enter "break 14" debug_code(program) { assert_empty Byebug.breakpoints } end def test_stops_at_correct_place_when_breakpoint_set_in_a_regular_line enter "break 7", "cont" debug_code(program) { assert_location example_path, 7 } end def test_stops_at_correct_place_when_breakpoint_set_at_method_return enter "break 10", "cont" debug_code(program) { assert_location example_path, 10 } end def test_shows_return_value_information_when_breakpoint_set_at_method_return enter "break 10", "cont" debug_code(program) check_output_includes "Return value is: 3" end def test_breaking_w_byebug_keyword_stops_at_the_next_line debug_code(program) { assert_equal 15, frame.line } end def test_conditional_breakpoint_stops_if_condition_is_true enter "break 8 if y == 1", "break 9", "cont" debug_code(program) { assert_equal 8, frame.line } end def test_conditional_breakpoint_is_ignored_if_condition_is_false enter "break 8 if y == 2", "break 9", "cont" debug_code(program) { assert_equal 9, frame.line } end def test_setting_conditional_breakpoint_using_wrong_expression_ignores_it enter "break 8 if y -=) 1", "break 9", "cont" debug_code(program) { assert_equal 9, frame.line } end def test_setting_conditional_breakpoint_using_wrong_expression_shows_error enter "break 11 if y -=) 1" debug_code(program) check_error_includes 'Incorrect expression "y -=) 1"; breakpoint disabled' end def test_shows_info_about_setting_breakpoints_when_using_just_break enter "break", "cont" debug_code(program) check_output_includes(/b\[reak\] \[:\] \[if \]/) end def test_setting_breakpoint_uses_new_source enter -> { cmd_after_replace(example_path, 7, "", "break 7") } debug_code(program) { assert_empty Byebug.breakpoints } end def test_setting_breakpoint_prints_confirmation_message enter "break 7" debug_code(program) { @id = Breakpoint.first.id } check_output_includes(/Created breakpoint #{@id}/) end def test_setting_breakpoint_to_nonexistent_line_shows_an_error enter "break 1000" debug_code(program) check_error_includes "There are only 16 lines in file #{example_path}" end def test_setting_breakpoint_to_invalid_line_shows_an_error_and_alternatives enter "break 14" debug_code(program) check_error_includes \ "Line 14 is not a valid breakpoint in file #{example_path}.", "Valid break points are:", "[B] 10: end", "[B] 11: end", "12:", "[B] 13: byebug", "14:", "[B] 15: #{example_class}.a", "[B] 16: end" end end # # Tests using the byebug keyword at the end of a method # class BreakWithByebugKeywordAtMethodEndTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test byebug at the end of a method 4: # 5: class #{example_class} 6: def a 7: byebug 8: end 9: 10: new.a 11: end 12: end RUBY end def test_stops_right_before_method_returns debug_code(program) { assert_equal 8, frame.line } end def test_shows_nil_return_value debug_code(program) check_output_includes "Return value is: nil" end end # # Tests using the byebug keyword at the end of a block # class BreakWithByebugKeywordAtBlockEndTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test byebug at the end of a block 4: # 5: class #{example_class} 6: def method_that_yields(b) 7: yield(b) 8: 0 9: end 10: 11: new.method_that_yields(0) do |n| 12: sleep n 13: byebug 14: end 15: end 16: end RUBY end def test_stops_right_before_block_returns debug_code(program) { assert_equal 14, frame.line } end def test_shows_nil_return_value debug_code(program) check_output_includes "Return value is: nil" end end # # Tests using the byebug keyword at the end of a class definition # class BreakWithByebugKeywordAtClassDefinitionEndTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test byebug at the end of a class 4: # 5: class #{example_class} 6: def a 7: 0 8: end 9: 10: byebug 11: end 12: end RUBY end def test_stops_right_before_class_definition_ends debug_code(program) { assert_equal 11, frame.line } end def test_does_not_show_return_value_information debug_code(program) check_output_doesnt_include "Return value is: nil" end end end byebug-11.1.1/test/commands/catch_test.rb000066400000000000000000000012651361243113500203010ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests exception catching # class CatchTest < TestCase def test_catch_adds_catchpoints enter "catch NoMethodError" debug_code(minimal_program) assert_equal 1, Byebug.catchpoints.size end def test_catch_removes_specific_catchpoint enter "catch NoMethodError", "catch NoMethodError off" debug_code(minimal_program) assert_empty Byebug.catchpoints end def test_catch_off_removes_all_catchpoints_after_confirmation enter "catch NoMethodError", "catch off", "y" debug_code(minimal_program) assert_empty Byebug.catchpoints end end end byebug-11.1.1/test/commands/condition_test.rb000066400000000000000000000047211361243113500212050ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests adding conditions to breakpoints. # class ConditionTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: byebug 3: 4: b = 5 5: c = b + 5 6: c + 3 7: end RUBY end def test_setting_condition_assigns_expression_to_breakpoint enter "break 5", -> { "condition #{Breakpoint.first.id} b == 5" } debug_code(program) { assert_equal "b == 5", Breakpoint.first.expr } end def test_setting_condition_w_wrong_syntax_does_not_enable_breakpoint enter "break 5", -> { "disable b #{Breakpoint.first.id}" }, -> { "cond #{Breakpoint.first.id} b ==" } debug_code(program) { assert_equal false, Breakpoint.first.enabled? } end def test_setting_condition_w_wrong_syntax_shows_error enter "break 5", -> { "disable #{Breakpoint.first.id}" }, -> { "cond #{Breakpoint.first.id} b ==" } debug_code(program) check_error_includes \ 'Incorrect expression "b ==", breakpoint not changed' end def test_execution_stops_when_condition_is_true enter "break 5", -> { "cond #{Breakpoint.first.id} b == 5" }, "cont" debug_code(program) { assert_equal 5, frame.line } end def test_execution_does_not_stop_when_condition_is_false enter "b 5", "b 6", -> { "cond #{Breakpoint.first.id} b == 3" }, "cont" debug_code(program) { assert_equal 6, frame.line } end def test_conditions_with_wrong_syntax_are_ignored enter "b 5", "b 6", -> { "cond #{Breakpoint.first.id} b ==" }, "cont" debug_code(program) { assert_equal 5, frame.line } end def test_empty_condition_means_removing_any_conditions enter "b 5 if b == 3", "b 6", -> { "cond #{Breakpoint.first.id}" }, "c" debug_code(program) do assert_nil Breakpoint.first.expr assert_equal 5, frame.line end end def test_shows_error_if_there_are_no_breakpoints enter "cond 1 true" debug_code(program) check_error_includes "No breakpoints have been set" end def test_shows_error_if_breakpoint_id_is_incorrect enter "break 5", -> { "cond #{Breakpoint.last.id + 1} b == 3" } debug_code(program) check_error_includes \ "Invalid breakpoint id. " \ 'Use "info breakpoint" to find out the correct id' end end end byebug-11.1.1/test/commands/continue_test.rb000066400000000000000000000065451361243113500210510ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests for continue command # class ContinueTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test continue command. 4: # 5: class #{example_class} 6: def add_four(num) 7: num + 4 8: end 9: end 10: 11: byebug 12: 13: b = 5 14: c = b + 5 15: #{example_class}.new.add_four(c) 16: eval("c") 17: end RUBY end def test_continues_until_the_end_if_no_line_specified_and_no_breakpoints enter "continue" debug_code(program) { assert_program_finished } end def test_continues_until_the_end_if_used_with_bang with_mode(:attached) do enter "break 14", "continue!" debug_code(program) { assert_program_finished } end end def test_continues_until_the_end_if_used_with_unconditionally with_mode(:attached) do enter "break 14", "continue unconditionally" debug_code(program) { assert_program_finished } end end def test_stops_byebug_after_continue enter "continue" debug_code(program) { assert_equal false, Byebug.started? } end def test_continues_up_to_breakpoint_if_no_line_specified enter "break 14", "continue" debug_code(program) { assert_equal 14, frame.line } end def test_works_in_abbreviated_mode_too enter "break 14", "cont" debug_code(program) { assert_equal 14, frame.line } end def test_continues_up_to_the_specified_line enter "cont 14" debug_code(program) { assert_equal 14, frame.line } end def test_ignores_the_command_if_specified_line_is_not_valid enter "cont 100" debug_code(program) { assert_equal 13, frame.line } end def test_shows_error_if_specified_line_is_not_valid enter "cont 100" debug_code(program) check_error_includes "Line 100 is not a valid stopping point in file" end def test_tracing_after_set_linetrace_and_continue with_setting :linetrace, false do enter "set linetrace", "cont" debug_code(program) check_output_includes "Tracing: #{example_path}:14 c = b + 5" end end def test_linetrace_does_not_show_a_line_in_eval_context with_setting :linetrace, true do enter "cont" debug_code(program) check_output_includes "Tracing: (eval):1" end end end class ContinueUnconditionallyWithByebugCallsTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: byebug 3: 4: b = 5 5: 6: byebug 7: 8: c = b + 5 9: 10: d = c + 5 11: end RUBY end def test_continues_until_the_end_ignoring_byebug_calls_if_used_with_bang with_mode(:attached) do enter "continue!" debug_code(program) { assert_program_finished } end end def test_continues_until_the_end_ignoring_byebug_calls_if_used_with_unconditionally with_mode(:attached) do enter "continue unconditionally" debug_code(program) { assert_program_finished } end end end end byebug-11.1.1/test/commands/debug_test.rb000066400000000000000000000024111361243113500202770ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests launching new debuggers from byebug's prompt # class SubdebuggersTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test subdebuggers inside evaluation prompt 4: # 5: class #{example_class} 6: def self.a 7: byebug 8: end 9: end 10: 11: byebug 12: 13: "Bye!" 14: end RUBY end def test_subdebugger_stops_at_correct_point_when_invoked_through_byebug_call enter "debug #{example_class}.a" debug_code(program) { assert_equal 8, frame.line } end def test_subdebugger_stops_at_correct_point_when_invoked_from_breakpoint enter "break #{example_class}.a", "debug #{example_class}.a" if RUBY_VERSION >= "2.5.0" debug_code(program) { assert_equal 7, frame.line } else debug_code(program) { assert_equal 6, frame.line } end end def test_subdebugger_goes_back_to_previous_debugger_after_continue enter "debug #{example_class}.a", "continue" debug_code(program) { assert_equal 13, frame.line } end end end byebug-11.1.1/test/commands/delete_test.rb000066400000000000000000000034221361243113500204560ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests deleting breakpoints. # class DeleteTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test breakpoints 4: # 5: class #{example_class} 6: def add_two(n) 7: byebug 8: n += 1 9: n += 1 10: n 11: end 12: end 13: 14: #{example_class}.new.add_two(0) 15: end RUBY end def test_deleting_a_breakpoint_removes_it_from_breakpoints_list enter "break 9", -> { "delete #{Breakpoint.first.id}" } debug_code(program) { assert_empty Byebug.breakpoints } end def test_deleting_a_breakpoint_shows_a_success_message enter "break 9", -> { "delete #{Breakpoint.first.id}" } debug_code(program) check_output_includes(/Deleted breakpoint/) end def test_does_not_stop_at_the_deleted_breakpoint enter "b 9", "b 10", -> { "delete #{Breakpoint.first.id}" }, "cont" debug_code(program) { assert_equal 10, frame.line } end def test_delete_by_itself_deletes_all_breakpoints_if_confirmed enter "break 9", "break 10", "delete", "y" debug_code(program) { assert_empty Byebug.breakpoints } end def test_delete_by_itself_keeps_current_breakpoints_if_not_confirmed enter "break 9", "break 10", "delete", "n" debug_code(program) { assert_equal 2, Byebug.breakpoints.size } end def test_delete_with_an_invalid_breakpoint_id_shows_error enter "break 9", -> { "delete #{Breakpoint.last.id + 1}" }, "cont" debug_code(program) check_error_includes(/No breakpoint number/) end end end byebug-11.1.1/test/commands/disable_test.rb000066400000000000000000000053631361243113500206250ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests disabling breakpoints. # class DisableTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test breakpoints 4: # 5: class #{example_class} 6: def self.a(num) 7: num + 1 8: end 9: 10: def b 11: 3 12: end 13: end 14: 15: y = 3 16: 17: byebug 18: 19: z = 5 20: 21: #{example_class}.new.b 22: #{example_class}.a(y + z) 23: end RUBY end def test_disable_all_breakpoints_sets_all_enabled_flags_to_false enter "break 21", "break 22", "disable breakpoints" debug_code(program) do assert_equal false, Breakpoint.first.enabled? assert_equal false, Breakpoint.last.enabled? end end def test_disable_all_breakpoints_shows_success_messages_for_all_breakpoints enter "break 21", "break 22", "disable breakpoints" debug_code(program) check_output_includes(/Breakpoint #{Breakpoint.first.id} disabled/, /Breakpoint #{Breakpoint.last.id} disabled/) end def test_disable_all_breakpoints_ignores_all_breakpoints enter "break 21", "break 22", "disable breakpoints", "cont" debug_code(program) check_output_doesnt_include "Stopped by breakpoint" end def test_disable_specific_breakpoints_sets_enabled_to_false enter "b 21", "b 22", -> { "disable breakpoints #{Breakpoint.first.id}" } debug_code(program) { assert_equal false, Breakpoint.first.enabled? } end def test_disable_specific_breakpoints_shows_success_message enter "break 21", "break 22", -> { "disable breakpoints #{Breakpoint.first.id}" } debug_code(program) check_output_includes(/Breakpoint #{Breakpoint.first.id} disabled/) end def test_disable_specific_breakpoints_properly_ignores_them enter "break 21", "break 22", -> { "disable breakpoints #{Breakpoint.first.id}" }, "cont" debug_code(program) { assert_equal 22, frame.line } end def test_disable_with_an_incorrect_breakpoint_number_shows_error enter "break 21", "break 22", -> { "disable breakpoints #{Breakpoint.last.id + 1}" } debug_code(program) assert_equal 1, interface.error.size check_error_includes(/"disable breakpoints" argument/) end def test_disable_without_an_argument_shows_help enter "disable" debug_code(program) check_output_includes "Disables breakpoints or displays" end end end byebug-11.1.1/test/commands/display_test.rb000066400000000000000000000021431361243113500206600ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests displaying values of expressions on every stop. # class DisplayTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: d = 0 3: 4: byebug 5: 6: d += 3 7: d + 6 8: end RUBY end def test_shows_expressions enter "display d + 1" debug_code(program) { clear_displays } check_output_includes "1: d + 1 = 1" end def test_shows_undefined_expressions enter "display e" debug_code(program) { clear_displays } check_output_includes "1: e = (undefined)" end def test_saves_displayed_expressions enter "display d + 1" debug_code(program) do assert_equal [[true, "d + 1"]], Byebug.displays clear_displays end end def test_displays_all_expressions_available enter "display d", "display d + 1", "display" debug_code(program) { clear_displays } check_output_includes "1: d = 0", "2: d + 1 = 1" end end end byebug-11.1.1/test/commands/down_test.rb000066400000000000000000000041001361243113500201550ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests commands which deal with backtraces. # class DownTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test backtraces. 4: # 5: class #{example_class} 6: def initialize(letter) 7: @letter = encode(letter) 8: end 9: 10: def encode(str) 11: integerize(str + "x") + 5 12: end 13: 14: def integerize(str) 15: byebug 16: str.ord 17: end 18: end 19: 20: frame = #{example_class}.new("f") 21: 22: frame 23: end RUBY end def test_down_moves_down_in_the_callstack enter "up", "down" debug_code(program) { assert_equal 16, frame.line } end def test_down_autolists_new_source_location_when_autolist_enabled with_setting :autolist, true do enter "up 2", "down" debug_code(program) check_output_includes '=> 11: integerize(str + "x") + 5' end end def test_down_does_not_autolist_new_source_location_when_autolist_disabled with_setting :autolist, false do enter "up 2", "down" debug_code(program) check_output_doesnt_include '=> 11: integerize(str + "x") + 5' end end def test_down_moves_down_in_the_callstack_a_specific_number_of_frames enter "up 3", "down 2" debug_code(program) { assert_equal 11, frame.line } end def test_down_skips_c_frames enter "up 3", "down", "frame" debug_code(program) check_output_includes( /--> #2 .*initialize\(letter#String\)\s* at .*#{example_path}:7/ ) end def test_down_does_not_move_if_frame_number_to_too_low enter "down" debug_code(program) { assert_equal 16, frame.line } check_error_includes "Can't navigate beyond the newest frame" end end end byebug-11.1.1/test/commands/edit_test.rb000066400000000000000000000034061361243113500201430ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests file editing from within Byebug. # class EditTest < TestCase def test_edit_opens_current_file_in_current_line_in_configured_editor with_env("EDITOR", "edi") do assert_calls(Kernel, :system, "edi +4 #{example_path}") do enter "edit" debug_code(minimal_program) end end end def test_edit_calls_vim_if_no_editor_environment_variable_is_set with_env("EDITOR", nil) do assert_calls(Kernel, :system, "vim +4 #{example_path}") do enter "edit" debug_code(minimal_program) end end end def test_edit_opens_configured_editor_at_specific_line_and_file with_env("EDITOR", "edi") do assert_calls(Kernel, :system, "edi +3 #{readme_path}") do enter "edit README.md:3" debug_code(minimal_program) end end end def test_edit_shows_an_error_if_specified_file_does_not_exist file = File.expand_path("no_such_file") enter "edit no_such_file:6" debug_code(minimal_program) check_error_includes "File #{file} does not exist." end def test_edit_shows_an_error_if_the_specified_file_is_not_readable File.stub(:readable?, false) do enter "edit README.md:6" debug_code(minimal_program) check_error_includes "File #{readme_path} is not readable." end end def test_edit_accepts_no_line_specification with_env("EDITOR", "edi") do assert_calls(Kernel, :system, "edi #{readme_path}") do enter "edit README.md" debug_code(minimal_program) end end end private def readme_path File.expand_path("README.md") end end end byebug-11.1.1/test/commands/enable_test.rb000066400000000000000000000061211361243113500204410ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests enabling breakpoints. # class EnableTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test breakpoints 4: # 5: class #{example_class} 6: def self.a(num) 7: num + 1 8: end 9: 10: def b 11: 3 12: end 13: end 14: 15: y = 3 16: 17: byebug 18: 19: z = 5 20: 21: #{example_class}.new.b 22: #{example_class}.a(y + z) 23: end RUBY end def test_enable_all_breakpoints_sets_all_enabled_flags_to_true enter "break 21", "break 22", "disable breakpoints", "enable breakpoints" debug_code(program) do assert_equal true, Breakpoint.first.enabled? assert_equal true, Breakpoint.last.enabled? end end def test_enable_all_breakpoints_shows_success_messages_for_all_breakpoints enter "break 21", "break 22", "disable breakpoints", "enable breakpoints" debug_code(program) check_output_includes(/Breakpoint #{Breakpoint.first.id} enabled/, /Breakpoint #{Breakpoint.last.id} enabled/) end def test_enable_all_breakpoints_stops_at_first_breakpoint enter "b 21", "b 22", "disable breakpoints", "enable breakpoints", "cont" debug_code(program) { assert_equal 21, frame.line } end def test_enable_all_breakpoints_stops_at_last_breakpoint enter "break 21", "break 22", "disable breakpoints", "enable breakpoints", "cont", "cont" debug_code(program) { assert_equal 22, frame.line } end def test_enable_specific_breakpoints_sets_enabled_to_true enter "break 21", "break 22", "disable breakpoints", -> { "enable breakpoints #{Breakpoint.last.id}" } debug_code(program) { assert_equal true, Breakpoint.last.enabled? } end def test_enable_specific_breakpoints_shows_success_message enter "break 21", "break 22", "disable breakpoints", -> { "enable breakpoints #{Breakpoint.last.id}" } debug_code(program) check_output_includes(/Breakpoint #{Breakpoint.last.id} enabled/) end def test_enable_specific_breakpoints_stops_at_enabled_breakpoint enter "break 21", "break 22", "disable breakpoints", -> { "enable breakpoints #{Breakpoint.last.id}" }, "cont" debug_code(program) { assert_equal 22, frame.line } end def test_enable_with_an_incorrect_breakpoint_number_shows_error enter "break 21", "break 22", "disable breakpoints", -> { "enable breakpoints #{Breakpoint.last.id + 1}" } debug_code(program) assert_equal 1, interface.error.size check_error_includes(/"enable breakpoints" argument/) end def test_enable_by_itself_shows_help enter "enable" debug_code(program) check_output_includes(/Enables breakpoints or displays/) end end end byebug-11.1.1/test/commands/finish_test.rb000066400000000000000000000103451361243113500204760ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests +finish+ functionality when it needs to stop after method return # events. # class FinishAfterReturnTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test the +finish+ command 4: # 5: class #{example_class} 6: def a 7: b 8: end 9: 10: def b 11: c 12: 2 13: end 14: 15: def c 16: d 17: 3 18: end 19: 20: def d 21: 5 22: end 23: end 24: 25: byebug 26: 27: #{example_class}.new.a 28: end RUBY end def test_finish_stops_after_current_single_line_frame_is_finished enter "break 21", "cont", "finish" debug_code(program) { assert_equal 17, frame.line } end def test_finish_stops_after_current_multiline_frame_is_finished enter "break 16", "cont", "finish" debug_code(program) { assert_equal 12, frame.line } end def test_finish_1_stops_after_current_frame_is_finished enter "break 21", "cont", "finish 1" debug_code(program) { assert_equal 17, frame.line } end def test_finish_works_for_frame_numbers_higher_than_one enter "break 21", "cont", "finish 2" debug_code(program) { assert_equal 12, frame.line } end def test_finish_behaves_consistenly_even_if_current_frame_has_been_changed enter "break 21", "cont", "up", "finish" debug_code(program) { assert_equal 12, frame.line } end def test_finish_shows_an_error_if_incorrect_frame_number_specified enter "break 21", "cont", "finish foo" debug_code(program) check_error_includes '"finish" argument "foo" needs to be a number' end def test_finish_stays_at_the_same_line_if_incorrect_frame_number_specified enter "break 21", "cont", "finish foo" debug_code(program) { assert_equal 21, frame.line } end def test_finish_does_not_stop_in_byebug_internal_frames enter "break 21", "cont", "finish 4" debug_code(program) { assert_program_finished } end end # # Tests +finish+ functionality when it needs to stop before method return # events. # class FinishBeforeReturnTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test the +finish+ command 4: # 5: class #{example_class} 6: def a 7: b 8: end 9: 10: def b 11: (1..5).map do |i| 12: i**2 13: end 14: end 15: end 16: 17: byebug 18: 19: #{example_class}.new.a 20: end RUBY end def test_finish_0_stops_right_before_frame_returns__simple_case enter "b 7", "cont", "finish 0" debug_code(program) { assert_equal 8, frame.line } end def test_finish_0_shows_information_about_the_return_value enter "b 7", "cont", "finish 0" debug_code(program) check_output_includes "Return value is: [1, 4, 9, 16, 25]" end def test_finish_0_stops_right_before_frame_returns__convoluted_case enter "b 11", "cont", "finish 0" debug_code(program) { assert_equal 14, frame.line } end end # # Tests +finish+ functionality inside autoloaded files. # class FinishInsideAutoloadedFilesTest < TestCase def program strip_line_numbers <<-RUBY 1: autoload :ByebugBar, "./byebug_bar" 2: 3: def main 4: b = ByebugBar 5: b 6: end 7: 8: main 9: 10: sleep 0 RUBY end def teardown super force_remove_const(Object, :ByebugBar) end def test_finish_inside_autoloaded_files with_new_file("byebug_bar.rb", "byebug\nmodule ByebugBar; end") do enter "finish" debug_code(program) { assert_equal 5, frame.line } end end end end byebug-11.1.1/test/commands/frame_test.rb000066400000000000000000000042041361243113500203050ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests commands which show frames # class FrameTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test backtraces. 4: # 5: class #{example_class} 6: def initialize(letter) 7: @letter = encode(letter) 8: end 9: 10: def encode(str) 11: integerize(str + "x") + 5 12: end 13: 14: def integerize(str) 15: byebug 16: str.ord 17: end 18: end 19: 20: frame = #{example_class}.new("f") 21: 22: frame 23: end RUBY end def test_frame_moves_to_a_specific_frame enter "frame 2" debug_code(program) { assert_equal 7, frame.line } end def test_frame_autolists_new_source_location_when_autolist_enabled with_setting :autolist, true do enter "frame 2" debug_code(program) check_output_includes "=> 7: @letter = encode(letter)" end end def test_frame_does_not_autolist_new_source_location_when_autolist_disabled with_setting :autolist, false do enter "frame 2" debug_code(program) check_output_doesnt_include "=> 7: @letter = encode(letter)" end end def test_frame_prints_the_callstack_when_called_without_arguments enter "up", "frame" debug_code(program) check_output_includes( /--> #1 .*encode\(str#String\)\s* at .*#{example_path}:11/ ) end def test_frame_0_sets_frame_to_the_first_one enter "up", "frame 0" debug_code(program) { assert_equal 16, frame.line } end def test_frame_minus_one_sets_frame_to_the_last_one enter "frame -1" debug_code(program) { assert_location example_path, 1 } end def test_frame_cannot_navigate_to_c_frames enter "frame 3" debug_code(program) check_error_includes "Can't navigate to c-frame" end end end byebug-11.1.1/test/commands/help_test.rb000066400000000000000000000104121361243113500201410ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests help command without arguments # class HelpAloneTest < TestCase def setup super enter "help" debug_code(minimal_program) end { break: "Sets breakpoints in the source code", catch: "Handles exception catchpoints", condition: "Sets conditions on breakpoints", continue: "Runs until program ends, hits a breakpoint or reaches a line", delete: "Deletes breakpoints", disable: "Disables breakpoints or displays", display: "Evaluates expressions every time the debugger stops", down: "Moves to a lower frame in the stack trace", edit: "Edits source files", enable: "Enables breakpoints or displays", finish: "Runs the program until frame returns", frame: "Moves to a frame in the call stack", help: "Helps you using byebug", history: "Shows byebug's history of commands", info: "Shows several informations about the program being debugged", interrupt: "Interrupts the program", irb: "Starts an IRB session", kill: "Sends a signal to the current process", list: "Lists lines of source code", method: "Shows methods of an object, class or module", next: "Runs one or more lines of code", pry: "Starts a Pry session", quit: "Exits byebug", restart: "Restarts the debugged program", save: "Saves current byebug session to a file", set: "Modifies byebug settings", show: "Shows byebug settings", source: "Restores a previously saved byebug session", step: "Steps into blocks or methods one or more times", thread: "Commands to manipulate threads", tracevar: "Enables tracing of a global variable", undisplay: "Stops displaying all or some expressions when program stops", untrace: "Stops tracing a global variable", up: "Moves to a higher frame in the stack trace", variables: "Shows variables and its values", where: "Displays the backtrace" }.each do |command, description| define_method(:"test_shows_summary_for_#{command}_command") do check_output_includes(/#{description}/) end end end # # Tests help command with arguments # class HelpWithArgsTest < TestCase def test_help_help_shows_help_for_help_command_itself with_setting :width, 50 do enter "help help" debug_code(minimal_program) expected_output = split_lines <<-TXT h[elp][ [ ]] help -- prints a summary of all commands help -- prints help on command help -- prints help on 's subcommand TXT check_output_includes(*expected_output) end end def test_help_with_specific_command_shows_help_for_it enter "help break" debug_code(minimal_program) expected_output = split_lines <<-TXT b[reak] [:] [if ] b[reak] [::...](.|#) [if ] Sets breakpoints in the source code TXT check_output_includes(*expected_output) end def test_help_with_undefined_command_shows_an_error enter "help foobar" debug_code(minimal_program) check_error_includes "Unknown command 'foobar'. Try 'help'" end def test_help_with_undefined_subcommand_shows_an_error enter "help info foobar" debug_code(minimal_program) check_error_includes "Unknown command 'info foobar'. Try 'help info'" end def test_help_with_command_and_subcommand_shows_subcommands_help enter "help info breakpoints" debug_code(minimal_program) check_output_includes("Status of user settable breakpoints") end def test_help_set_shows_help_for_set_command_and_includes_settings enter "help set" debug_code(minimal_program) check_output_includes("Modifies byebug settings", "List of supported settings:") end def test_help_show_shows_help_for_show_command_and_includes_settings enter "help show" debug_code(minimal_program) check_output_includes("Shows byebug settings", "List of supported settings:") end end end byebug-11.1.1/test/commands/history_test.rb000066400000000000000000000041641361243113500207210ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" unless ENV["LIBEDIT"] module Byebug # # Tests Byebug's command line history. # class HistoryTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: byebug 3: 4: a = 2 5: a + 3 6: end RUBY end def test_history_displays_latest_records_from_readline_history enter "show", "history" debug_code(program) check_output_includes(/\d+ show$/, /\d+ history$/) end def test_history_n_displays_whole_history_if_n_is_bigger_than_history_size enter "show", "history 3" debug_code(program) check_output_includes(/\d+ show$/, /\d+ history 3$/) end def test_history_n_displays_lastest_n_records_from_readline_history enter "show width", "show autolist", "history 2" debug_code(program) check_output_includes(/\d+ show autolist$/, /\d+ history 2$/) end def test_history_does_not_save_empty_commands enter "show", "show width", "", "history 3" debug_code(program) check_output_includes( /\d+ show$/, /\d+ show width$/, /\d+ history 3$/ ) end def test_history_does_not_save_duplicated_consecutive_commands enter "show", "show width", "show width", "history 3" debug_code(program) check_output_includes( /\d+ show$/, /\d+ show width$/, /\d+ history 3$/ ) end def test_cmds_from_previous_repls_are_remembered_if_autosave_enabled with_setting :autosave, true do enter "next", "history 2" debug_code(program) check_output_includes(/\d+ next$/, /\d+ history 2$/) end end def test_cmds_from_previous_repls_are_not_remembered_if_autosave_disabled with_setting :autosave, false do enter "next", "history" debug_code(program) check_output_includes(/\d+ history$/) check_output_doesnt_include(/\d+ next$/) end end end end end byebug-11.1.1/test/commands/info_test.rb000066400000000000000000000156541361243113500201610ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Test info command. # class InfoTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test information about files. 4: # 5: class #{example_class} 6: def initialize 7: @foo = "bar" 8: @bla = "blabla" 9: end 10: 11: def a(y, z) 12: w = "1" * 45 13: x = 2 14: w + x.to_s + y + z + @foo 15: end 16: 17: def b 18: a("a", "b") 19: e = "%.2f" 20: e 21: end 22: end 23: 24: byebug 25: i = #{example_class}.new 26: i.b 27: end RUBY end def test_info_breakpoints_shows_information_about_all_breakpoints enter "break 12", "break 13 if x == w", "info breakpoints" debug_code(program) check_output_includes "Num Enb What", /\d+ +y at #{example_path}:12/, /\d+ +y at #{example_path}:13 if x == w/ end def test_info_breakpoints_with_ids_shows_information_on_specific_breakpoints enter "b 12", "b 13", -> { "info breakpoints #{Breakpoint.first.id}" } debug_code(program) check_output_includes "Num Enb What", /\d+ +y at #{example_path}:12/ check_output_doesnt_include(/\d+ +y at #{example_path}:13/) end def test_info_breakpoints_shows_a_message_when_no_breakpoints_found enter "info breakpoints" debug_code(program) check_output_includes "No breakpoints." end def test_info_breakpoints_shows_error_if_specific_breakpoint_do_not_exist enter "break 12", "break 13", "delete 100", "info breakpoints 100" debug_code(program) check_error_includes "No breakpoints found among list given" end def test_info_breakpoints_shows_hit_counts enter "break 12", "cont", "info breakpoints" debug_code(program) check_output_includes(/\d+ +y at #{example_path}:12/, "breakpoint already hit 1 time") end def test_info_display_shows_all_display_expressions enter "display 3 + 3", "display a + b", "info display" debug_code(program) { clear_displays } check_output_includes "Auto-display expressions now in effect:", "Num Enb Expression", "1: y 3 + 3", "2: y a + b" end def test_info_display_shows_a_message_when_no_display_expressions_found enter "info display" debug_code(program) check_output_includes "There are no auto-display expressions now." end def test_info_line_shows_info_about_the_current_line enter "break 12", "cont", "info line" debug_code(program) check_output_includes "Line 12 of \"#{example_path}\"" end def test_info_program_shows_the_initial_stop_reason enter "info program" debug_code(program) check_output_includes \ "It stopped after stepping, next'ing or initial start." end def test_info_program_shows_the_step_stop_reason enter "step", "info program" debug_code(program) check_output_includes \ "Program stopped.", "It stopped after stepping, next'ing or initial start." end def test_info_program_shows_the_breakpoint_stop_reason enter "break 12", "cont", "info program" debug_code(program) check_output_includes "Program stopped.", "It stopped at a breakpoint." end def test_info_alone_shows_help enter "info", "cont" debug_code(program) check_output_includes \ "Shows several informations about the program being debugged" end end # # Tests "info file" command # class InfoFileTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: byebug 3: 3 4: end RUBY end def test_info_file_shows_basic_info_about_current_file enter "info file" debug_code(program) check_output_includes "File #{example_path} (4 lines)" end def test_info_file_with_a_file_name_shows_basic_info_about_a_specific_file with_new_tempfile("sleep 0") do |script_name| enter "info file #{script_name}" debug_code(program) check_output_includes "File #{script_name} (1 line)" end end def test_info_file_shows_mtime_of_current_file enter "info file" debug_code(program) check_output_includes \ "Modification time: #{File.stat(example_path).mtime}" end def test_info_file_w_filename_shows_mtime_of_filename with_new_tempfile("sleep 0") do |script_name| enter "info file #{script_name}" debug_code(program) check_output_includes \ "Modification time: #{File.stat(script_name).mtime}" end end def test_info_file_shows_sha1_signature_of_current_file enter "info file" debug_code(program) check_output_includes \ "Sha1 Signature: #{Digest::SHA1.hexdigest(example_path)}" end def test_info_file_w_filename_shows_sha1_signature_of_filename with_new_tempfile("sleep 0") do |script_name| enter "info file #{script_name}" debug_code(program) check_output_includes \ "Sha1 Signature: #{Digest::SHA1.hexdigest(script_name)}" end end def test_info_file_shows_potential_breakpoint_lines_in_current_file enter "info file" debug_code(minimal_program) check_output_includes "Breakpoint line numbers: 1 2 4 5" end def test_info_file_w_filename_shows_potential_breakpoint_lines_in_filename with_new_tempfile("sleep 0") do |script_name| enter "info file #{script_name}" debug_code(program) check_output_includes "Breakpoint line numbers: 1" end end def test_info_file_does_not_show_any_info_if_filename_is_invalid enter "info file blabla" debug_code(program) check_error_includes "blabla is not a valid source file" end def test_info_file_with_a_file_name_with_space_doesnt_fail enter "info file /filename/with space" debug_code(program) check_error_includes "/filename/with space is not a valid source file" end end # # Tests info command on crashed programs # class InfoCrashedTest < TestCase def program_raising strip_line_numbers <<-RUBY byebug fail "Bang" RUBY end def test_info_program_shows_the_catchpoint_stop_reason_for_crashed_programs enter "catch RuntimeError", "cont", "info program", "catch off", "y" assert_raises(RuntimeError) { debug_code(program_raising) } check_output_includes "Program stopped.", "It stopped at a catchpoint." end end end byebug-11.1.1/test/commands/interrupt_test.rb000066400000000000000000000013001361243113500212410ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests interrupt command. # class InterruptTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: byebug 3: 4: ex = 0 5: 6: 1.times do 7: ex += 1 8: end 9: end RUBY end def test_interrupt_stops_at_the_next_statement enter "interrupt", "continue" debug_code(program) { assert_equal 6, frame.line } end def test_interrupt_steps_into_blocks enter "next", "interrupt", "continue" debug_code(program) { assert_equal 7, frame.line } end end end byebug-11.1.1/test/commands/irb_test.rb000066400000000000000000000015141361243113500177700ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" require "minitest/mock" module Byebug # # Tests entering IRB from within Byebug. # class IrbTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: byebug 3: 4: a = 2 5: a + 4 6: end RUBY end def test_irb_command_starts_an_irb_session interface.stub(:instance_of?, true) do assert_calls(IRB, :start) do enter "irb" debug_code(minimal_program) end end end def test_autoirb_calls_irb_automatically_after_every_stop interface.stub(:instance_of?, true) do assert_calls(IRB, :start) do enter "set autoirb", "cont 5", "set noautoirb" debug_code(program) end end end end end byebug-11.1.1/test/commands/kill_test.rb000066400000000000000000000031101361243113500201410ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Test signal sending functionality. # class KillTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test signals 4: # 5: class #{example_class} 6: def self.kill_me 7: "dieeee" 8: end 9: end 10: 11: byebug 12: 13: #{example_class}.kill_me 14: end RUBY end def test_kill_sends_signal_to_some_pid assert_calls(Process, :kill, "TERM #{Process.pid}") do enter "kill TERM" debug_code(program) end end def test_kill_closes_interface_when_sending_kill_signal_explicitly Process.stub(:kill, nil) do assert_calls(interface, :close) do enter "kill KILL" debug_code(program) end end end def test_kill_asks_confirmation_when_sending_kill_implicitly assert_calls(Process, :kill, "KILL #{Process.pid}") do enter "kill", "y" debug_code(program) check_output_includes "Really kill? (y/n)" end end def test_kill_does_not_send_an_unknown_signal refute_calls(Process, :kill, "BLA #{Process.pid}") do enter "kill BLA" debug_code(program) end end def test_kill_shows_an_error_when_the_signal_is_unknown enter "kill BLA" debug_code(program) check_error_includes "signal name BLA is not a signal I know about" end end end byebug-11.1.1/test/commands/list_test.rb000066400000000000000000000117411361243113500201720ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests for listing source files. # class ListTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test breakpoints 4: # 5: class #{example_class} 6: def build_percentage_string 7: "%1" 8: end 9: end 10: 11: byebug 12: 13: str = #{example_class}.new.build_percentage_string 14: 15: str 16: end RUBY end def test_lists_source_code_lines with_setting :listsize, 3 do enter "list" debug_code(program) check_output_includes "[12, 14] in #{example_path}" end end def test_listsize_is_not_set_if_parameter_is_not_an_integer with_setting :listsize, 3 do enter "set listsize 5.0", "list" debug_code(program) check_output_doesnt_include "[11, 15] in #{example_path}" end end def test_does_not_list_before_the_beginning_of_file with_setting :listsize, 15 do enter "cont 7", "list" debug_code(program) check_output_includes "[1, 15] in #{example_path}" end end def test_does_not_list_after_the_end_of_file with_setting :listsize, 13 do enter "list" debug_code(program) check_output_includes "[4, 16] in #{example_path}" end end def test_lists_the_whole_file_if_number_of_lines_is_smaller_than_listsize with_setting :listsize, 17 do enter "list" debug_code(program) check_output_includes "[1, 16] in #{example_path}" end end def test_lists_forwards_after_the_second_call_to_list with_setting :listsize, 3 do enter "cont 7", "list", "list" debug_code(program) check_output_includes "[9, 11] in #{example_path}" end end def test_lists_surrounding_lines_after_the_first_call_to_list_minus with_setting :listsize, 3 do enter "list-" debug_code(program) check_output_includes "[12, 14] in #{example_path}" end end def test_lists_backwards_after_the_second_call_to_list_minus with_setting :listsize, 3 do enter "list-", "list-" debug_code(program) check_output_includes "[9, 11] in #{example_path}" end end def test_lists_backwards_from_end_of_file with_setting :listsize, 3 do enter "list 14-16", "list -" debug_code(program) check_output_includes "[11, 13] in #{example_path}" end end def test_lists_surrounding_lines_when_list_equals_is_called with_setting :listsize, 3 do enter "list =" debug_code(program) check_output_includes "[12, 14] in #{example_path}" end end def test_lists_specific_range_when_requested_in_hyphen_format enter "list 6-8" debug_code(program) check_output_includes "[6, 8] in #{example_path}" end def test_lists_specific_range_when_requested_in_comma_format enter "list 6,8" debug_code(program) check_output_includes "[6, 8] in #{example_path}" end def test_lists_nothing_if_unexistent_range_is_specified enter "list 20,25" debug_code(program) check_error_includes '"List" argument "20" needs to be at most 16' check_output_doesnt_include "[20, 25] in #{example_path}" end def test_lists_nothing_if_invalid_range_is_specified enter "list 5,4" debug_code(program) check_error_includes "Invalid line range" check_output_doesnt_include "[5, 4] in #{example_path}" end def test_list_proper_lines_when_range_around_specific_line_with_hyphen with_setting :listsize, 3 do enter "list 4-" debug_code(program) check_output_includes "[3, 5] in #{example_path}" end end def test_list_proper_lines_when_range_around_specific_line_with_comma with_setting :listsize, 3 do enter "list 4," debug_code(program) check_output_includes "[3, 5] in #{example_path}" end end def test_correctly_print_lines_containing_the_percentage_symbol enter "list 7" debug_code(program) check_output_includes(/7:\s+"%1"/) end def test_shows_error_when_invoked_with_invalid_syntax enter "list rdfe87" debug_code(program) check_error_includes(/needs to be a number/) end def test_gives_back_a_prompt_when_invoked_with_invalid_syntax enter "list rdfe87" debug_code(program) { assert_equal 13, frame.line } end def replace_build_percentage_string_line_and_list_it cmd_after_replace(example_path, 7, " \"%11\"", "list 7-7") end def test_lists_file_changes skip enter -> { replace_build_percentage_string_line_and_list_it } debug_code(program) check_output_includes(/7:\s+"%11"/) end end end byebug-11.1.1/test/commands/method_test.rb000066400000000000000000000026141361243113500204760ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests commands for listing available methods. # class MethodTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test the method command. 4: # 5: class #{example_class} 6: def initialize 7: @a = "b" 8: @c = "d" 9: end 10: 11: def self.foo 12: "asdf" 13: end 14: 15: def bla 16: "asdf" 17: end 18: end 19: 20: byebug 21: 22: a = #{example_class}.new 23: a.bla 24: end RUBY end def test_method_shows_instance_methods_of_a_class enter "cont 7", "method #{example_class}" debug_code(program) check_output_includes("bla") check_output_doesnt_include("foo") end def test_m_shows_an_error_if_specified_object_is_not_a_class_or_module enter "m a" debug_code(program) check_output_includes "Should be Class/Module: a" end def test_method_instance_shows_methods_of_object enter "cont 23", "method instance a" debug_code(program) check_output_includes("bla") check_output_doesnt_include("foo") end end end byebug-11.1.1/test/commands/next_test.rb000066400000000000000000000154071361243113500202000ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests basic stepping behaviour. # class BasicNextTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test stepping. 4: # 5: class #{example_class} 6: def self.add_four(num) 7: byebug 8: num += 4 9: num += 2 10: num 11: end 12: end 13: 14: res = #{example_class}.add_four(7) 15: 16: res + 1 17: end RUBY end def test_next_goes_to_the_next_line enter "next" debug_code(program) { assert_location example_path, 9 } end def test_n_goes_to_the_next_line enter "n" debug_code(program) { assert_location example_path, 9 } end def test_next_stays_in_current_frame_while_not_finished enter "next 2" debug_code(program) { assert_location example_path, 10 } end def test_next_goes_up_a_frame_when_current_frame_finishes enter "next 3" debug_code(program) { assert_equal 16, frame.line } end def test_next_does_not_stop_at_byebug_internal_frames enter "next 4" debug_code(program) { assert_program_finished } end end # # Test for [#103](https://github.com/deivid-rodriguez/byebug/issues/103) # class NextWhenReturnInsideLoopInsideInitializeTest < TestCase def program strip_line_numbers <<-RUBY 1: byebug 2: 3: module Byebug 4: # 5: # Toy class to test next. 6: # 7: class #{example_class} 8: def initialize 9: loop { return } 10: end 11: end 12: 13: #{example_class}.new 14: 15: "Bye!" 16: end RUBY end def test_next_works_return_inside_loop_inside_initialize enter "cont 13", "next" debug_code(program) { assert_location example_path, 15 } end end # # Test for: https://bugs.ruby-lang.org/issues/11492 # class NextAndDefineMethodTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test cases where next should not stay in frame 4: # 5: class #{example_class} 6: define_method "method1" do 7: return 1 8: end 9: 10: def foo 11: method1 12: "bye foo!" 13: end 14: end 15: 16: byebug 17: 18: #{example_class}.new.foo 19: 20: "bye!" 21: end RUBY end def test_next_works_as_expected_with_define_method enter "next" debug_code(program) { assert_equal 20, frame.line } end end # # Tests next behaviour in rescue clauses. # class NextRescueTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test stepping and rescue interaction. 4: # 5: class #{example_class} 6: def self.raise_from_c 7: unknown 8: rescue NameError 9: 1 10: end 11: 12: def self.raise_from_ruby 13: fails_badly 14: rescue 15: 1 16: end 17: 18: def self.fails_badly 19: fail "booooooooooooooom" 20: end 21: end 22: 23: byebug 24: 25: #{example_class}.raise_from_c 26: #{example_class}.raise_from_ruby 27: end RUBY end def test_next_steps_over_rescue_when_raising_from_c_method enter "break Byebug::#{example_class}.raise_from_c", "cont", "next 2" debug_code(program) { assert_equal 9, frame.line } end def test_next_steps_over_rescue_when_raising_from_ruby_method enter "break Byebug::#{example_class}.raise_from_ruby", "cont", "next 2" debug_code(program) { assert_equal 15, frame.line } end end # # Tests next behaviour in combination with backtrace commands. # class NextBacktracesTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test the combination of "up" and "next" commands. 4: # 5: class #{example_class} 6: def a 7: byebug 8: r = b(c) 9: r + 1 10: end 11: 12: def b(p) 13: r = 2 14: p + r 15: end 16: 17: def c 18: s = 3 19: s + 2 20: end 21: end 22: 23: #{example_class}.new.a 24: end RUBY end def test_step_then_up_then_next_advances_in_the_upper_frame enter "step", "up", "next" debug_code(program) { assert_equal 9, frame.line } end end # # Tests next when execution should not stop at the same "stack size level" # class NextGoingUpFramesTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test cases where next should not stay in frame 4: # 5: class #{example_class} 6: def finite_loop 7: n = 0 8: loop do 9: n = inc(n) 10: break if n == 2 11: n = inc(n) 12: end 13: end 14: 15: def inc(n) 16: if n == 2 17: n 18: else 19: n + 1 20: end 21: end 22: end 23: 24: byebug 25: 26: #{example_class}.new.finite_loop 27: end RUBY end def test_next_goes_up_a_frame_if_current_frame_finishes enter "cont 19", "next" debug_code(program) { assert_equal 10, frame.line } end def test_next_does_not_enter_other_frames_of_the_same_size enter "b 19", "cont", "cont", "next" debug_code(program) { assert_equal 9, frame.line } end end # # Test top-level block events are properly handled # class TopLevelBlockEventsTest < TestCase def program strip_line_numbers <<-RUBY 1: byebug 2: 3: 1.times {} 4: 5: sleep 0 RUBY end def test_top_level_b_call_event enter "next" debug_code(program) { assert_equal 5, frame.line } end end end byebug-11.1.1/test/commands/pry_test.rb000066400000000000000000000015521361243113500200300ustar00rootroot00000000000000# frozen_string_literal: true require "pry" require "test_helper" require "minitest/mock" module Byebug # # Tests entering Pry from within Byebug. # class PryTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: byebug 3: 4: a = 2 5: a + 4 6: end RUBY end def test_pry_command_starts_a_pry_session_if_pry_installed interface.stub(:instance_of?, true) do assert_calls(Pry, :start) do enter "pry" debug_code(minimal_program) end end end def test_autopry_calls_pry_automatically_after_every_stop interface.stub(:instance_of?, true) do assert_calls(Pry, :start) do enter "set autopry", "cont 5", "set noautopry" debug_code(program) end end end end end byebug-11.1.1/test/commands/quit_test.rb000066400000000000000000000020571361243113500202010ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" require "minitest/mock" module Byebug # # Tests exiting Byebug functionality. # class QuitTest < TestCase def faking_exit! Process.stub(:exit!, nil) { yield } end def test_quit_finishes_byebug_if_user_confirms faking_exit! do enter "quit", "y" debug_code(minimal_program) check_output_includes "Really quit? (y/n)" end end def test_quit_quits_inmediately_if_used_with_bang faking_exit! do enter "quit!" debug_code(minimal_program) check_output_doesnt_include "Really quit? (y/n)" end end def test_quit_quits_inmediately_if_used_with_unconditionally faking_exit! do enter "quit unconditionally" debug_code(minimal_program) check_output_doesnt_include "Really quit? (y/n)" end end def test_does_not_quit_if_user_did_not_confirm enter "quit", "n" debug_code(minimal_program) check_output_includes "Really quit? (y/n)" end end end byebug-11.1.1/test/commands/restart_test.rb000066400000000000000000000042671361243113500207100ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" require "rbconfig" require "minitest/mock" module Byebug # # Tests restarting functionality. # class RestartTest < TestCase def test_restart_with_no_args_original_script_with_no_args_standalone_mode with_mode(:standalone) do with_command_line(example_path) do assert_restarts(nil, "#{ruby_bin} #{byebug_bin} #{example_path}") end end end def test_restart_with_no_args_original_script_with_no_args_attached_mode with_mode(:attached) do with_command_line(example_path) do assert_restarts(nil, "#{ruby_bin} #{example_path}") end end end def test_restart_with_no_args_original_script_through_ruby_attached_mode with_mode(:attached) do with_command_line("ruby", example_path) do assert_restarts(nil, "ruby #{example_path}") end end end def test_restart_with_no_args_in_standalone_mode with_mode(:standalone) do with_command_line(example_path, "1") do assert_restarts(nil, "#{ruby_bin} #{byebug_bin} #{example_path} 1") end end end def test_restart_with_args_in_standalone_mode with_mode(:standalone) do with_command_line(example_path, "1") do assert_restarts("2", "#{ruby_bin} #{byebug_bin} #{example_path} 2") end end end def test_restart_with_no_args_in_attached_mode with_mode(:attached) do with_command_line(example_path, "1") do assert_restarts(nil, "#{ruby_bin} #{example_path} 1") end end end def test_restart_with_args_in_attached_mode with_mode(:attached) do with_command_line(example_path, "1") do assert_restarts(2, "#{ruby_bin} #{example_path} 2") end end end private def assert_restarts(arg, expected_cmd_line) assert_calls(Kernel, :exec, expected_cmd_line) do enter ["restart", arg].compact.join(" ") debug_code(minimal_program) check_output_includes "Re exec'ing:" end end def ruby_bin RbConfig.ruby end def byebug_bin Context.bin_file end end end byebug-11.1.1/test/commands/save_test.rb000066400000000000000000000043511361243113500201540ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests saving Byebug commands to a file. # class SaveTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: byebug 3: a = 2 4: a + 3 5: end RUBY end def cleanup(file) File.delete(file) end def file_contents File.read(Setting[:savefile]) end def test_save_records_regular_breakpoints enter "break 3", "save" debug_code(program) assert_includes file_contents, "break #{example_path}:3" cleanup(Setting[:savefile]) end def test_save_records_conditional_breakpoints enter "break 4 if true", "save" debug_code(program) assert_includes file_contents, "break #{example_path}:4 if true" cleanup(Setting[:savefile]) end def test_save_records_catchpoints enter "catch NoMethodError", "save", "catch NoMethodError off" debug_code(program) assert_includes file_contents, "catch NoMethodError" cleanup(Setting[:savefile]) end def test_save_records_displays enter "display 2 + 3", "save" debug_code(program) { clear_displays } assert_includes file_contents, "display 2 + 3" cleanup(Setting[:savefile]) end def test_save_records_current_state_of_settings enter "save" debug_code(program) assert_includes file_contents, "set basename false" assert_includes file_contents, "set autolist true" assert_includes file_contents, "set autoirb false" cleanup(Setting[:savefile]) end def test_save_shows_a_success_message enter "save" debug_code(program) check_output_includes "Saved to '#{Setting[:savefile]}'" cleanup(Setting[:savefile]) end def test_save_without_a_filename_uses_a_default_file enter "save output.txt" debug_code(program) assert_includes File.read("output.txt"), "set autoirb false" cleanup("output.txt") end def test_save_without_a_filename_shows_a_message_with_the_file_used enter "save output.txt" debug_code(program) check_output_includes "Saved to 'output.txt'" cleanup("output.txt") end end end byebug-11.1.1/test/commands/set_test.rb000066400000000000000000000107271361243113500200150ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Test settings functionality. # class SetTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: byebug 3: 4: z = 4 5: z += 1 6: z + 1 7: end RUBY end settings = %i[autolist autosave basename fullpath post_mortem stack_on_error] settings.each do |set| ["on", "1", "true", ""].each do |key| define_method(:"test_enable_boolean_setting_#{set}_using_#{key}") do with_setting set, false do enter "set #{set} #{key}" debug_code(program) assert_equal true, Setting[set] end end end %w[off 0 false].each do |key| define_method(:"test_disable_boolean_setting_#{set}_using_#{key}") do with_setting set, true do enter "set #{set} #{key}" debug_code(program) assert_equal false, Setting[set] end end end define_method(:"test_disable_boolean_setting_#{set}_using_no_prefix") do with_setting set, true do enter "set no#{set}" debug_code(program) assert_equal false, Setting[set] end end end def test_set_does_not_enable_a_setting_using_shorcut_when_ambiguous with_setting :autolist, false do enter "set auto" debug_code(program) assert_equal false, Setting[:autolist] end end def test_set_enables_a_setting_using_shorcut_when_not_ambiguous with_setting :autolist, false do enter "set autol" debug_code(program) assert_equal true, Setting[:autolist] end end def test_set_does_not_disable_a_setting_using_shorcut_when_ambiguous with_setting :autolist, true do enter "set noauto" debug_code(program) assert_equal true, Setting[:autolist] end end def test_set_histsize_sets_maximum_history_size with_setting :histsize, 1 do enter "set histsize 250" debug_code(program) assert_equal 250, Setting[:histsize] check_output_includes "Maximum size of byebug's command history is 250" end end def test_set_histsize_shows_an_error_message_if_no_size_is_provided enter "set histsize" debug_code(program) check_error_includes "You must specify a value for setting :histsize" end def test_set_histfile_sets_command_history_file with_setting :histfile, HistfileSetting::DEFAULT do filename = File.expand_path(".custom-byebug-hist") enter "set histfile #{filename}" debug_code(program) assert_equal filename, Setting[:histfile] check_output_includes "The command history file is #{filename}" File.delete(filename) end end def test_set_histfile_shows_an_error_message_if_no_filename_is_provided enter "set histfile" debug_code(program) check_error_includes "You must specify a value for setting :histfile" end %i[listsize width].each do |set| define_method(:"test_set_#{set}_changes_integer_setting_#{set}") do with_setting set, 100 do enter "set #{set} 50" debug_code(program) assert_equal 50, Setting[set] end end end def test_set_linetrace_enables_tracing_program_execution with_setting :linetrace, false do enter "set linetrace", "cont 5" debug_code(program) check_output_includes \ "linetrace is on", "Tracing: #{example_path}:5 z += 1" end end def test_set_nolinetrace_stops_tracing_program_execution with_setting :linetrace, true do enter "cont 5", "set nolinetrace" debug_code(program) check_output_includes "Tracing: #{example_path}:5 z += 1" check_output_doesnt_include "Tracing: #{example_path}:6 z + 1" end end def test_basename_setting_affects_tracing_output with_setting :basename, true do enter "set linetrace", "cont 5", "set nolinetrace" debug_code(program) check_output_includes \ "Tracing: #{File.basename(example_path)}:5 z += 1" end end def test_set_without_arguments_shows_help_for_set_command enter "set" debug_code(program) check_output_includes("Modifies byebug settings", "List of supported settings:") end end end byebug-11.1.1/test/commands/show_test.rb000066400000000000000000000041411361243113500201730ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Test settings display functionality. # class ShowTest < TestCase settings = %i[autolist autosave basename fullpath post_mortem stack_on_error] settings.each do |set| define_method(:"test_show_#{set}_shows_disabled_bool_setting_#{set}") do with_setting set, false do enter "show #{set}" debug_code(minimal_program) check_output_includes "#{set} is off" end end define_method(:"test_show_#{set}_shows_enabled_bool_setting_#{set}") do with_setting set, true do enter "show #{set}" debug_code(minimal_program) check_output_includes "#{set} is on" end end end def test_show_callstyle enter "show callstyle" debug_code(minimal_program) check_output_includes "Frame display callstyle is 'long'" end def test_show_listsize enter "show listsize" debug_code(minimal_program) check_output_includes "Number of source lines to list is 10" end def test_show_width width = Setting[:width] enter "show width" debug_code(minimal_program) check_output_includes "Maximum width of byebug's output is #{width}" end def test_show_unknown_setting enter "show bla" debug_code(minimal_program) check_error_includes "Unknown setting :bla" end def test_show_histfile filename = Setting[:histfile] enter "show histfile" debug_code(minimal_program) check_output_includes "The command history file is #{filename}" end def test_show_histsize max_size = Setting[:histsize] enter "show histsize" debug_code(minimal_program) check_output_includes \ "Maximum size of byebug's command history is #{max_size}" end def test_show_without_arguments_displays_help_for_the_show_command enter "show" debug_code(minimal_program) check_output_includes("Shows byebug settings", "List of supported settings:") end end end byebug-11.1.1/test/commands/skip_test.rb000066400000000000000000000032531361243113500201640ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests for continue command # class SkipTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test continue_breakpoint command. 4: # 5: class #{example_class} 6: def factor(num) 7: i = 1 8: num.times do |new_number| 9: byebug 10: i *= new_number 11: end 12: end 13: end 14: c = 5 15: 16: result = #{example_class}.new.factor(c) 17: sleep 0 18: "Result is: " + result.to_s 19: end RUBY end def test_continues_until_the_end_if_no_line_specified_and_no_breakpoints enter "break 17", "skip" debug_code(program) { assert_location example_path, 17 } end def test_works_in_abbreviated_mode_too enter "break 17", "sk" debug_code(program) { assert_location example_path, 17 } end def test_does_not_list_code_for_intermediate_breakpoints enter "break 17", "skip" debug_code(program) check_output_includes "=> 10: i *= new_number" check_output_doesnt_include "=> 10: i *= new_number", "=> 10: i *= new_number" check_output_includes "=> 17: sleep 0" end def test_restores_previous_autolisting_after_skip with_setting :autolist, false do enter "break 17", "skip", "continue 18" debug_code(program) check_output_doesnt_include '=> 18: "Result is: " + result.to_s' end end end end byebug-11.1.1/test/commands/source_test.rb000066400000000000000000000023361361243113500205170ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests reading Byebug commands from a file. # class SourceTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: byebug 3: 4: a = 2 5: a + 3 6: end RUBY end def setup File.open("source_example.txt", "w") do |f| f.puts "break 4" f.puts "break 5 if true" end super end def teardown File.delete("source_example.txt") super rescue StandardError retry end def test_source_runs_byebug_commands_from_file enter "source source_example.txt" debug_code(program) do assert_equal 4, Breakpoint.first.pos assert_equal 5, Breakpoint.last.pos assert_equal "true", Breakpoint.last.expr end end def test_source_shows_an_error_if_file_not_found enter "source blabla" debug_code(program) check_error_includes(/File ".*blabla" not found/) end def test_source_without_arguments_shows_help enter "source" debug_code(program) check_output_includes("Restores a previously saved byebug session") end end end byebug-11.1.1/test/commands/step_test.rb000066400000000000000000000053701361243113500201730ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests basic stepping behaviour. # class BasicSteppingTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test stepping. 4: # 5: class #{example_class} 6: def self.add_four(num) 7: num += 4 8: num 9: end 10: end 11: 12: byebug 13: 14: res = #{example_class}.add_four(7) 15: 16: res + 1 17: end RUBY end def test_step_goes_to_the_next_statement enter "step" debug_code(program) { assert_equal 7, frame.line } end def test_s_goes_to_the_next_statement enter "s" debug_code(program) { assert_equal 7, frame.line } end end # # Tests step/next with arguments higher than one. # class MoreThanOneStepTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test advanced stepping. 4: # 5: class #{example_class} 6: def self.add_three(num) 7: byebug 8: 2.times do 9: num += 1 10: end 11: 12: num *= 2 13: num 14: end 15: end 16: 17: res = #{example_class}.add_three(7) 18: 19: res 20: end RUBY end def step_steps_into_blocks enter "step 2" debug_code(program) { assert_equal 9, frame.line } end def step_steps_out_of_blocks_when_done enter "step 3" debug_code(program) { assert_equal 12, frame.line } end end # # Tests step/next behaviour in combination with backtrace commands. # class SteppingBacktracesTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test the combination of "up" and "next" commands. 4: # 5: class #{example_class} 6: def a 7: byebug 8: r = b(c) 9: r + 1 10: end 11: 12: def b(p) 13: r = 2 14: p + r 15: end 16: 17: def c 18: s = 3 19: s + 2 20: end 21: end 22: 23: #{example_class}.new.a 24: end RUBY end def test_step_then_up_then_steps_in_from_the_upper_frame enter "step", "up", "step" debug_code(program) { assert_equal 13, frame.line } end end end byebug-11.1.1/test/commands/thread_test.rb000066400000000000000000000115451361243113500204700ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests threading functionality. # class ThreadTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test threading 4: # 5: class #{example_class} 6: attr_accessor :lock 7: 8: def initialize 9: @lock = Queue.new 10: end 11: 12: def launch 13: t1 = Thread.new do 14: loop do 15: break unless lock.empty? 16: sleep 0.01 17: end 18: end 19: 20: @t2 = Thread.new do 21: loop { sleep 0.01 } 22: end 23: 24: t1.join 25: end 26: 27: def kill 28: @t2.kill 29: end 30: end 31: 32: byebug 33: 34: t = #{example_class}.new 35: t.launch 36: t.kill 37: end RUBY end def t1_context Byebug.contexts[-2] end def t1_thnum t1_context.thnum end def t2_context Byebug.contexts[-1] end def t2_thnum t2_context.thnum end def curr_thnum Byebug.current_context.thnum end def test_thread_list_marks_current_thread_with_a_plus_sign file = example_path enter "cont 13", "thread list", "lock << 0" debug_code(program) check_output_includes(/\+ \d+ # #{file}:13/) end def test_thread_list_shows_all_available_threads enter "cont 24", "thread list", "lock << 0" debug_code(program) check_output_includes( /(\+)?\d+ #/, /(\+)?\d+ #/, /(\+)?\d+ #/ ) end def test_thread_stop_marks_thread_as_suspended enter "cont 24", -> { "thread stop #{t2_thnum}" }, "lock << 0" debug_code(program) check_output_includes(/\$ \d+ # { "thread stop #{t2_thnum}" }, "lock << 0" debug_code(program) { Setting[:linetrace] = false } check_output_doesnt_include(/Tracing: #{example_path}:21/) end def test_thread_stop_shows_help_when_no_thread_number_specified enter "cont 13", "thread stop", "lock << 0" debug_code(program) check_output_includes "Stops the execution of the specified thread" end def test_thread_stop_shows_error_when_trying_to_stop_current_thread enter "cont 13", -> { "thread stop #{curr_thnum}" }, "lock << 0" debug_code(program) check_error_includes "It's the current thread" end def test_thread_resume_removes_threads_from_the_suspended_state ctx = nil save_second_ctx_and_stop = lambda do ctx = t2_context "thread stop #{t2_thnum}" end enter "cont 24", save_second_ctx_and_stop, -> { "thread resume #{t2_thnum}" }, "lock << 0" debug_code(program) { assert_equal false, ctx.suspended? } check_output_includes(/\$ #{ctx.thnum} # { "thread resume #{curr_thnum}" }, "lock << 0" debug_code(program) check_error_includes "It's the current thread" end def test_thread_resume_shows_error_if_thread_is_already_running enter "cont 24", -> { "thread resume #{t2_thnum}" }, "lock << 0" debug_code(program) check_error_includes "Already running" end def test_thread_switch_changes_execution_to_another_thread enter "cont 24", -> { "thread switch #{t2_thnum}" }, "lock << 0" debug_code(program) check_output_includes "=> 21: loop { sleep 0.01 }" end def test_thread_switch_shows_help_if_thread_number_not_specified enter "cont 13", "thread switch", "lock << 0" debug_code(program) check_output_includes "Switches execution to the specified thread" end def test_thread_switch_shows_error_when_trying_to_switch_current_thread enter "cont 13", -> { "thread switch #{curr_thnum}" }, "lock << 0" debug_code(program) check_error_includes "It's the current thread" end end end byebug-11.1.1/test/commands/tracevar_test.rb000066400000000000000000000037211361243113500210250ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests gloabal variable tracing functionality. # class TracevarTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test global variable tracing 4: # 5: class #{example_class} 6: def with_verbose(value) 7: previous = $VERBOSE 8: $VERBOSE = value 9: yield 10: ensure 11: $VERBOSE = previous 12: end 13: end 14: 15: #{example_class}.new.with_verbose(true) do 16: byebug 17: $VERBOSE = false 18: $VERBOSE ||= true 19: $VERBOSE &&= false 20: end 21: end RUBY end def test_tracevar_tracks_global_variables enter "tracevar $VERBOSE", "cont 19", "untracevar $VERBOSE" debug_code(program) check_output_includes \ "traced global variable '$VERBOSE' has value 'false'", "traced global variable '$VERBOSE' has value 'true'" end def test_tracevar_stop_makes_program_stop_when_global_var_changes enter "tracevar $VERBOSE stop", "cont 19", "untracevar $VERBOSE" debug_code(program) { assert_equal 18, frame.line } end def test_tracevar_nostop_does_not_stop_when_global_var_changes enter "tracevar $VERBOSE nostop", "cont 19", "untracevar $VERBOSE" debug_code(program) { assert_equal 19, frame.line } end def test_tracevar_shows_an_error_message_if_no_global_variable_is_specified enter "tracevar" debug_code(program) check_error_includes("tracevar needs a global variable name") end def test_tracevar_shows_an_error_message_if_there_is_no_such_global_var enter "tracevar $FOO" debug_code(program) check_error_includes "'$FOO' is not a global variable." end end end byebug-11.1.1/test/commands/undisplay_test.rb000066400000000000000000000053161361243113500212300ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests undisplay command used to stop periodically displaying expressions. # class UndisplayTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: d = 0 3: 4: byebug 5: 6: d += 3 7: d + 6 8: end RUBY end def test_asks_for_confirmation enter "display d", "display d + 1", "undisplay" debug_code(program) { clear_displays } check_output_includes "Clear all expressions? (y/n)" end def test_removes_all_expressions_from_list_if_confirmed enter "display d", "display d + 1", "undisplay", "y", "next" debug_code(program) do assert_equal [[false, "d"], [false, "d + 1"]], Byebug.displays clear_displays end check_output_doesnt_include "1: d = 3", "2: d + 1 = 4" end def test_does_not_remove_all_expressions_from_list_unless_confirmed enter "display d", "display d + 1", "undisplay", "n", "display" debug_code(program) do assert_equal [[true, "d"], [true, "d + 1"]], Byebug.displays clear_displays end check_output_includes "1: d = 0", "2: d + 1 = 1" end def test_marks_specific_expression_from_list_as_inactive enter "display d", "display d + 1", "undisplay 1" debug_code(program) do assert_equal [[nil, "d"], [true, "d + 1"]], Byebug.displays clear_displays end end def test_displays_only_the_active_position enter "display d", "display d + 1", "undisplay 1", "next" debug_code(program) { clear_displays } check_output_includes "2: d + 1 = 4" check_output_doesnt_include "1: d = 3" end def test_disable_display_removes_the_expression_from_display_list enter "display d", "disable display 1" debug_code(program) do assert_equal [[false, "d"]], Byebug.displays clear_displays end end def test_disable_display_shows_an_error_if_no_displays_are_set enter "disable display 1" debug_code(program) check_error_includes "No display expressions have been set" end def test_disable_display_shows_an_error_if_theres_no_such_display_position enter "display d", "disable display 4" debug_code(program) { clear_displays } check_error_includes \ '"disable display" argument "4" needs to be at most 1' end def test_enable_display_set_display_flag_to_true_in_display_list enter "display d", "disable display 1", "enable display 1" debug_code(program) do assert_equal [[true, "d"]], Byebug.displays clear_displays end end end end byebug-11.1.1/test/commands/up_test.rb000066400000000000000000000041531361243113500176420ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests the +up+ command. # class UpTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test backtraces. 4: # 5: class #{example_class} 6: def initialize(letter) 7: @letter = encode(letter) 8: end 9: 10: def encode(str) 11: integerize(str + "x") + 5 12: end 13: 14: def integerize(str) 15: byebug 16: str.ord 17: end 18: end 19: 20: frame = #{example_class}.new("f") 21: 22: frame 23: end RUBY end def test_up_moves_up_in_the_callstack enter "up" debug_code(program) { assert_equal 11, frame.line } end def test_up_autolists_new_source_location_when_autolist_enabled with_setting :autolist, true do enter "up" debug_code(program) check_output_includes '=> 11: integerize(str + "x") + 5' end end def test_up_does_not_autolist_new_source_location_when_autolist_disabled with_setting :autolist, false do enter "up" debug_code(program) check_output_doesnt_include '=> 11: integerize(str + "x") + 5' end end def test_up_moves_up_in_the_callstack_a_specific_number_of_frames enter "up 2" debug_code(program) { assert_equal 7, frame.line } end def test_up_does_not_move_if_frame_number_to_too_high enter "up 100" debug_code(program) { assert_equal 16, frame.line } check_error_includes "Can't navigate beyond the oldest frame" end def test_up_skips_c_frames enter "up 3", "frame" debug_code(program) check_output_includes(/--> #4 at #{example_path}:20/) end def test_up_plays_well_with_evaluation enter "str", "up", "str", "up" debug_code(program) check_output_includes '"fx"', '"f"' end end end byebug-11.1.1/test/commands/var_test.rb000066400000000000000000000055531361243113500200130ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests variable evaluation. # class VarTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test variable evaluation. 4: # 5: class #{example_class} 6: SOMECONST = "foo" unless defined?(SOMECONST) 7: 8: def initialize 9: @instance_variable = "1" * 20 10: byebug 11: end 12: 13: def run(level) 14: [1, 2, 3].map do |i| 15: level * i 16: end 17: end 18: end 19: 20: v = #{example_class}.new 21: v.run(2) 22: end RUBY end def test_var_args_shows_information_about_current_frame_arguments enter "break 14", "cont", "var args" debug_code(program) check_output_includes "level = 2" end def test_var_const_shows_constants_in_class_or_module enter "var const Byebug::#{example_class}" debug_code(program) check_output_includes "SOMECONST = foo" end def test_var_const_shows_constants_in_current_scope_when_without_argument enter "var const" debug_code(program) check_output_includes "SOMECONST = foo" end def test_var_const_shows_error_if_given_object_is_not_a_class_or_module enter "break 21", "cont", "var const v" debug_code(program) check_error_includes "Should be Class/Module: v" end def test_var_instance_shows_instance_vars_of_an_object enter "break 21", "cont", "var instance v" debug_code(program) check_output_includes '@instance_variable = "11111111111111111111"' end def test_var_global_shows_global_variables enter "var global" debug_code(program) check_output_includes "$ERROR_INFO = nil" end def test_var_instance_shows_instance_variables_of_self_if_no_object_given enter "var instance" debug_code(program) check_output_includes '@instance_variable = "11111111111111111111"' end def test_var_instance_cuts_long_variable_values_according_to_width_setting with_setting :width, 40 do enter "var instance" debug_code(program) check_output_includes '@instance_variable = "111111111111111...' end end def test_var_local_shows_local_variables enter "break 15", "cont", "var local" debug_code(program) check_output_includes "i = 1", "level = 2" end def test_var_all_shows_all_variables enter "break 15", "cont", "var all" debug_code(program) check_output_includes "$ERROR_INFO = nil", '@instance_variable = "11111111111111111111"', "level = 2" end end end byebug-11.1.1/test/commands/where_test.rb000066400000000000000000000131421361243113500203260ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests commands which deal with backtraces. # class WhereStandardTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test backtraces. 4: # 5: class #{example_class} 6: def initialize(l) 7: @letter = encode(l) 8: end 9: 10: def encode(str) 11: to_int(str + "x") + 5 12: end 13: 14: def to_int(str) 15: byebug 16: str.ord 17: end 18: end 19: 20: frame = #{example_class}.new("f") 21: 22: frame 23: end RUBY end def test_where_displays_current_backtrace_with_fullpaths_by_default enter "where" debug_code(program) expected_output = prepare_for_regexp <<-TXT --> #0 #{example_full_class}.to_int(str#String) at #{example_path}:16 #1 #{example_full_class}.encode(str#String) at #{example_path}:11 #2 #{example_full_class}.initialize(l#String) at #{example_path}:7 ͱ-- #3 Class.new(*args) at #{example_path}:20 #4 at #{example_path}:20 #5 at #{example_path}:1 TXT check_output_includes(*expected_output) end def test_where_displays_backtraces_using_long_callstyle_by_default enter "where" debug_code(program) expected_output = prepare_for_regexp <<-TXT --> #0 #{example_full_class}.to_int(str#String) at #{example_path}:16 #1 #{example_full_class}.encode(str#String) at #{example_path}:11 #2 #{example_full_class}.initialize(l#String) at #{example_path}:7 ͱ-- #3 Class.new\(*args) at #{example_path}:20 #4 at #{example_path}:20 #5 at #{example_path}:1 TXT check_output_includes(*expected_output) end def test_where_displays_backtraces_using_short_callstyle enter "set callstyle short", "where", "set callstyle long" debug_code(program) expected_output = prepare_for_regexp <<-TXT --> #0 to_int(str) at #{example_path}:16 #1 encode(str) at #{example_path}:11 #2 initialize(l) at #{example_path}:7 ͱ-- #3 new(*args) at #{example_path}:20 #4 at #{example_path}:20 #5 at #{example_path}:1 TXT check_output_includes(*expected_output) end def test_where_displays_instance_exec_block_frames enter "where" program = strip_line_numbers <<-RUBY 1: module Byebug 2: class #{example_full_class} 3: def foo 4: Object.new.instance_exec do 5: byebug 6: end 7: end 8: end 9: 10: #{example_full_class}.new.foo 11: end RUBY debug_code(program) expected_output = prepare_for_regexp <<-TXT --> #0 block in #{example_full_class}.block in foo at #{example_path}:6 #1 BasicObject.instance_exec(*args) at #{example_path}:4 #2 #{example_full_class}.foo at #{example_path}:4 #3 at #{example_path}:10 #4 at #{example_path}:1 TXT check_output_includes(*expected_output) end end # # Tests dealing with backtraces when the path being debugged is not deeply # nested. # # @note We skip this tests in Windows since the paths in this CI environment # are usually very deeply nested and on OS X where tmp path is always # deeply nested. # unless /cygwin|mswin|mingw|darwin/.match?(RUBY_PLATFORM) class WhereWithNotDeeplyNestedPathsTest < WhereStandardTest def test_where_displays_current_backtrace_w_shorpaths_if_fullpath_disabled enter "set nofullpath", "where", "set fullpath" debug_code(program) expected_output = prepare_for_regexp <<-TXT --> #0 #{example_full_class}.to_int(str#String) at #{example_path}:16 #1 #{example_full_class}.encode(str#String) at #{example_path}:11 #2 #{example_full_class}.initialize(l#String) at #{example_path}:7 ͱ-- #3 Class.new(*args) at #{example_path}:20 #4 at #{example_path}:20 #5 at #{example_path}:1 TXT check_output_includes(*expected_output) end end end # # Tests dealing with backtraces when the path being debugged is deeply nested. # class WhereWithDeeplyNestedPathsTest < WhereStandardTest def setup @example_parent_folder = Dir.mktmpdir(nil) @example_folder = File.realpath(Dir.mktmpdir(nil, @example_parent_folder)) super end def teardown super FileUtils.remove_dir(@example_parent_folder, true) end def test_where_displays_current_backtrace_w_shorpaths_if_fullpath_disabled enter "set nofullpath", "where", "set fullpath" debug_code(program) expected_output = prepare_for_regexp <<-TXT --> #0 #{example_full_class}.to_int(str#String) at ... #1 #{example_full_class}.encode(str#String) at ... #2 #{example_full_class}.initialize(l#String) at ... ͱ-- #3 Class.new(*args) at ... #4 at ... #5 at ... TXT check_output_includes(*expected_output) end end end byebug-11.1.1/test/debugger_alias_test.rb000066400000000000000000000005251361243113500203510ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests that the old "debugger" kernel method still works. It will be # eventually removed. # class DebuggerAliasTest < Minitest::Test def test_aliases_debugger_to_byebug assert_equal Kernel.method(:byebug), Kernel.method(:debugger) end end end byebug-11.1.1/test/helpers/000077500000000000000000000000001361243113500154705ustar00rootroot00000000000000byebug-11.1.1/test/helpers/bin_helper_test.rb000066400000000000000000000004141361243113500211620ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug module Helpers class BinHelperTest < Minitest::Test include BinHelper def test_which_resolves_ruby assert_equal true, File.exist?(which("ruby")) end end end end byebug-11.1.1/test/interface_test.rb000066400000000000000000000026411361243113500173550ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # A subclass of Interface to test the base class functionality # class SpecificInterface < Interface attr_accessor :fake_input_queue def readline(_prompt) @fake_input_queue.pop end end # # Tests the Interface class # class InterfaceTest < Minitest::Test def setup @interface = SpecificInterface.new end def teardown @interface.history.clear end def test_reads_simple_commands @interface.fake_input_queue = ["a_command"] assert_equal "a_command", @interface.read_command("byebug") end def test_reads_multiple_commands_in_same_line_separated_by_semicolon @interface.fake_input_queue = ["a_command; another"] assert_equal "a_command", @interface.read_command("byebug") assert_equal "another", @interface.read_command("byebug") end def test_understands_ruby_commands_using_semicolon_if_escaped @interface.fake_input_queue = ['a_command \; another'] assert_equal "a_command ; another", @interface.read_command("byebug") end def test_keeps_an_internal_command_buffer @interface.fake_input_queue = ["a_command"] @interface.command_queue = ["a_buffered_command"] assert_equal "a_buffered_command", @interface.read_command("byebug") assert_equal "a_command", @interface.read_command("byebug") end end end byebug-11.1.1/test/interfaces/000077500000000000000000000000001361243113500161515ustar00rootroot00000000000000byebug-11.1.1/test/interfaces/local_interface_test.rb000066400000000000000000000006541361243113500226540ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" require "minitest/mock" module Byebug # # Tests the local interface # class LocalInterfaceTest < TestCase def test_continues_by_control_d # `Readline.readline` returns nil for Control-D Readline.stub(:readline, nil) do interface = LocalInterface.new assert_equal "continue", interface.readline("(byebug)") end end end end byebug-11.1.1/test/interfaces/script_interface_test.rb000066400000000000000000000020471361243113500230640ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests the script interface (batch execution of byebug commands from a file) # class ScriptInterfaceTest < TestCase def test_initialize_wires_up_dependencies with_new_tempfile("show") do |path| interface = ScriptInterface.new(path) assert_instance_of File, interface.input assert_instance_of StringIO, interface.output assert_equal $stderr, interface.error end end def test_initialize_verbose_writes_to_stdout_and_stderr with_new_tempfile("show") do |path| interface = ScriptInterface.new(path, true) assert_instance_of File, interface.input assert_equal $stdout, interface.output assert_equal $stderr, interface.error end end def test_readline_reads_input_until_first_non_comment with_new_tempfile("# Run the show command\nshow\n") do |path| interface = ScriptInterface.new(path) assert_equal "show", interface.readline end end end end byebug-11.1.1/test/minitest_runner.rb000066400000000000000000000042661361243113500176100ustar00rootroot00000000000000# frozen_string_literal: true $LOAD_PATH.unshift(File.expand_path(File.join("..", "lib"), __dir__)) $LOAD_PATH.unshift(__dir__) require "minitest" require "English" require "shellwords" require "timeout" module Byebug # # Helper class to aid running minitest # class MinitestRunner def initialize @test_suites = extract_from_argv { |cmd_arg| test_suite?(cmd_arg) } end def run test_suites.each { |f| require File.expand_path(f) } flags = if ENV["TESTOPTS"] ENV["TESTOPTS"].split(" ") else ["--name=/#{filtered_methods.join('|')}/"] end run_with_timeout(flags) end private def max_running_time 300 end def run_with_timeout(flags) Timeout.timeout(max_running_time) { Minitest.run(flags + $ARGV) } rescue Timeout::Error warn "Test suite timed out after #{max_running_time} seconds" false end def runnables Minitest::Runnable.runnables end def test_suite?(str) all_test_suites.include?(str) end def test_suites return all_test_suites if @test_suites.empty? @test_suites end def test_methods(str) if /test_.*/.match?(str) filter_runnables_by_method(str) else filter_runnables_by_class(str) end end def filter_runnables_by_method(str) filter_runnables do |runnable| "#{runnable}##{str}" if runnable.runnable_methods.include?(str) end end def filter_runnables_by_class(str) filter_runnables do |runnable| runnable.runnable_methods if runnable.name == "Byebug::#{str}" end end def filter_runnables selected = runnables.flat_map do |runnable| yield(runnable) end.compact return unless selected.any? selected end def filtered_methods @filtered_methods ||= extract_from_argv do |cmd_arg| test_methods(cmd_arg) end end def all_test_suites Dir.glob("test/**/*_test.rb") end def extract_from_argv matching, non_matching = $ARGV.partition { |arg| yield(arg) } $ARGV.replace(non_matching) matching end end end byebug-11.1.1/test/minitest_runner_test.rb000066400000000000000000000032301361243113500206350ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug class MinitestRunnerTest < TestCase def test_runs output = run_minitest_runner("test/debugger_alias_test.rb") assert_includes output, "\n.\n" end def test_per_test_class output = run_minitest_runner("DebuggerAliasTest") assert_includes output, "\n.\n" end def test_per_test output = run_minitest_runner("test_aliases_debugger_to_byebug") assert_includes output, "\n.\n" end def test_combinations output = run_minitest_runner( "DebuggerAliasTest", "test_script_processor_clears_history" ) assert_includes output, "\n..\n" end def test_with_verbose_option output = run_minitest_runner("DebuggerAliasTest", "--verbose") assert_includes \ output, "Byebug::DebuggerAliasTest#test_aliases_debugger_to_byebug = 0.00 s = ." assert_includes \ output, "Run options: --name=/DebuggerAliasTest/ --verbose" end def test_with_seed_option output = run_minitest_runner("DebuggerAliasTest", "--seed=37") assert_includes output, "\n.\n" assert_includes \ output, "Run options: --name=/DebuggerAliasTest/ --seed=37" end private def run_minitest_runner(*args) Bundler.with_original_env do output, status = Open3.capture2e(shell_out_env(simplecov: false), *binstub, *args) assert_equal true, status.success?, output output end end def binstub cmd = "bin/minitest" return [cmd] unless Gem.win_platform? %W[#{RbConfig.ruby} #{cmd}] end end end byebug-11.1.1/test/post_mortem_test.rb000066400000000000000000000043531361243113500177670ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests post mortem functionality. # class PostMortemTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test post mortem functionality 4: # 5: class #{example_class} 6: def a 7: fail "blabla" 8: end 9: end 10: 11: byebug 12: 13: c = #{example_class}.new 14: c.a 15: end RUBY end def test_rises_before_exit_in_post_mortem_mode with_setting :post_mortem, true do enter "cont" assert_raises(RuntimeError) { debug_code(program) } end end def test_post_mortem_mode_sets_post_mortem_flag_to_true with_setting :post_mortem, true do enter "cont" begin debug_code(program) rescue RuntimeError assert_equal true, Byebug.post_mortem? end end end def test_execution_is_stopped_at_the_correct_line_after_exception with_setting :post_mortem, true do enter "cont" begin debug_code(program) rescue StandardError assert_equal 7, Byebug.raised_exception.__bb_context.frame.line end end end def test_command_forbidden_in_post_mortem_mode with_post_mortem_processor do with_setting :post_mortem, true do enter "help next" begin debug_code(program) rescue RuntimeError check_error_includes "Unknown command 'next'. Try 'help'" end end end end def test_command_permitted_in_post_mortem_mode with_post_mortem_processor do with_setting :post_mortem, true do enter "help where" begin debug_code(program) rescue RuntimeError check_output_includes "Displays the backtrace" end end end end private def with_post_mortem_processor old_processor = Context.processor Context.processor = PostMortemProcessor yield ensure Context.processor = old_processor end end end byebug-11.1.1/test/printers/000077500000000000000000000000001361243113500156745ustar00rootroot00000000000000byebug-11.1.1/test/printers/plain_test.rb000066400000000000000000000047271361243113500203750ustar00rootroot00000000000000# frozen_string_literal: true require_relative "../test_helper" require "minitest/mock" require_relative "../../lib/byebug/helpers/string" module Byebug # # Tests the plain text printer # class PrintersPlainTest < TestCase include Helpers::StringHelper def klass @klass ||= Printers::Plain end def printer @printer ||= klass.new end def yaml_plain deindent <<-YAML, leading_spaces: 8 foo: bar: "plain {zee}, {uga} gaa" confirmations: okay: "Okay?" variable: variable: "{key}: {value}" YAML end def yaml_base deindent <<-YAML, leading_spaces: 8 foo: bar: "base {zee}, {uga} gaa" boo: "{zee}, gau" YAML end def test_returns_correctly_translated_string with_dummy_yaml do assert_equal \ "plain zuu, aga gaa\n", printer.print("foo.bar", zee: "zuu", uga: "aga") end end def test_add_yn_to_the_confirmation_strings with_dummy_yaml do assert_equal("Okay? (y/n) ", printer.print("foo.confirmations.okay")) end end def test_strings_inherited_from_base with_new_tempfile(yaml_plain) do |path_plain| with_new_tempfile(yaml_base) do |path_base| printer.stub(:contents_files, [path_plain, path_base]) do assert_equal("zuu, gau\n", printer.print("foo.boo", zee: "zuu")) end end end end def test_error_if_there_is_no_specified_path with_dummy_yaml do assert_raises(klass::MissedPath) { printer.print("foo.bla") } end end def test_error_if_there_is_no_specified_argument with_dummy_yaml do assert_raises(klass::MissedArgument) do printer.print("foo.bar", zee: "zuu") end end end def test_print_collection with_dummy_yaml do assert_equal( "plain 0, a gaa\nplain 1, b gaa\n", printer.print_collection( "foo.bar", [{ uga: "a" }, { uga: "b" }] ) do |item, index| item.merge(zee: index) end ) end end def test_print_variables with_dummy_yaml do assert_equal \ "a: b\nc: d\n", printer.print_variables([%w[a b], %w[c d]]) end end def with_dummy_yaml with_new_tempfile(yaml_plain) do |path| printer.stub(:contents_files, [path]) { yield } end end end end byebug-11.1.1/test/processors/000077500000000000000000000000001361243113500162305ustar00rootroot00000000000000byebug-11.1.1/test/processors/command_processor_test.rb000066400000000000000000000161061361243113500233350ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" require "timeout" module Byebug # # Tests generic input evaluation # class ProcessorBaseTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: byebug 3: 4: d = 1 5: d += 1 6: d 7: end RUBY end def test_syntax_error_gives_a_prompt_back enter "d." debug_code(program) { assert_equal 4, frame.line } end def test_empty_command_repeats_last_command enter "n", "" debug_code(program) { assert_equal 6, frame.line } end def test_multiple_commands_are_executed_sequentially enter "n ; n" debug_code(program) { assert_equal 6, frame.line } end def test_semicolon_can_be_escaped_to_prevent_multiple_command_behaviour enter 'n \; n' debug_code(program) { assert_equal 4, frame.line } end def test_shows_an_error_for_unknown_subcommands_by_default enter "info unknown_subcmd" debug_code(minimal_program) check_error_includes \ "Unknown command 'info unknown_subcmd'. Try 'help info'" end end # # Test evaluation of unknown input introduced by the user. Basically, the # REPL behavior. # class ProcessorUnknownInputTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test evaluation of unknown input 4: # 5: class #{example_class} 6: def inspect 7: "A very cool string representation" 8: end 9: 10: def to_s 11: "A not so cool string representation" 12: end 13: end 14: 15: byebug 16: 17: "Bye!" 18: end RUBY end def test_arithmetic_expressions_are_evaluated_on_unknown_input enter "3 + 2" debug_code(minimal_program) check_output_includes "5" end def test_ruby_code_is_evaluated_on_unknown_input enter "[5, 6, 7].inject(&:+)" debug_code(minimal_program) check_output_includes "18" end def test_arrays_are_properly_printed_after_evaluation_of_unknown_input enter "(1..3).to_a" debug_code(minimal_program) check_output_includes "[1, 2, 3]" end def test_eval_evaluates_just_like_without_it enter 's = "something"', 'eval "s is #{s}"' debug_code(minimal_program) check_output_includes '"s is something"' end def test_evaluation_results_on_unknown_input_prefer_inspect_over_to_s enter "#{example_class}.new" debug_code(program) check_output_includes "A very cool string representation" end def test_shows_backtrace_on_error_if_stack_on_error_enabled enter "set stack_on_error", "2 / 0" debug_code(minimal_program) check_error_includes(/\s*from \S+:in \`eval\'/) check_error_doesnt_include "ZeroDivisionError Exception: divided by 0" end def test_shows_only_exception_if_stack_on_error_disabled enter "set stack_on_error off", "2 / 0" debug_code(minimal_program) check_error_includes "ZeroDivisionError Exception: divided by 0" check_error_doesnt_include(/\S+:\d+:in `eval':divided by 0/) end end # # Tests processor evaluation and breakpoints working together # class ProcessorEvaluationAndBreakpointsTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test subdebuggers inside evaluation prompt 4: # 5: class #{example_class} 6: def self.m1 7: m2 8: end 9: 10: def self.m2 11: "m2" 12: end 13: end 14: 15: byebug 16: 17: #{example_class}.m1 18: 19: "Bye!" 20: end RUBY end def test_does_not_show_incorrect_info_about_having_stopped_at_breakpoint enter "b 7", "cont", "m2" debug_code(program) # Regular breakpoint: OK check_output_includes(/Stopped by breakpoint \d/) # Incorrect info when evaluating something from command prompt check_output_doesnt_include(/Stopped by breakpoint \d/, /Stopped by breakpoint \d/) end end # # Tests commands automatically run when control is returned back to user # class ProcessorAutocommandsTest < TestCase def program strip_line_numbers <<-RUBY 1: module Byebug 2: # 3: # Toy class to test subdebuggers inside evaluation prompt 4: # 5: class #{example_class} 6: class_eval "def self.a; 1 end" 7: end 8: 9: byebug 10: 11: #{example_class}.a 12: 13: "Bye!" 14: end RUBY end def test_autolists_lists_source_before_stopping debug_code(program) check_output_includes "[5, 14] in #{example_path}" end def test_shows_error_when_current_source_location_is_unknown enter "step" debug_code(program) { assert_equal "(eval)", frame.file } check_error_includes "No sourcefile available for (eval)" end end # # Tests evaluation in threaded programs. # class ProcessorEvaluationAndThreadsTest < TestCase def program <<-RUBY module Byebug # # Toy class to test evaluation in Byebug's prompt # class #{example_class} attr_accessor :thread def initialize @thread = Thread.new do loop do sleep 0.01 next if numbers.empty? squares << (numbers.pop)**2 end end end def numbers @numbers ||= Queue.new end def squares @squares ||= [] end def calc(number) numbers.push(number) loop do next if squares.empty? return squares.pop end end end worker = #{example_class}.new byebug worker.thread.kill end RUBY end def test_properly_evaluates_expressions_using_threads enter "Timeout::timeout(60) { 1 }" debug_code(minimal_program) check_output_includes "1" end def test_does_not_hang_when_evaluating_expressions_using_new_threads enter "Thread.new {}.join" debug_code(minimal_program) check_output_includes(/#/) end def test_does_not_hang_when_evaluating_expressions_using_old_threads enter "worker.calc(10)" debug_code(program) check_output_includes "100" end def test_thread_context_is_kept enter 'Thread.current[:greeting] = "hi!"', "Thread.current[:greeting]" debug_code(minimal_program) check_output_includes '"hi!"', # After set '"hi!"' # After get end end end byebug-11.1.1/test/processors/script_processor_test.rb000066400000000000000000000016201361243113500232160ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" module Byebug # # Tests script processor in isolation # class ScriptProcessorTest < Minitest::Test include TestUtils def test_script_processor_clears_history previous_history = Readline::HISTORY.to_a process_rc_file("set callstyle long") assert_equal previous_history, Readline::HISTORY.to_a end def test_script_processor_closes_files process_rc_file("set callstyle long") assert_equal 0, dangling_descriptors.count end private def dangling_descriptors ObjectSpace.each_object(File).select do |f| f.path == Byebug.init_file && !f.closed? end end def process_rc_file(content) with_init_file(content) do interface = ScriptInterface.new(Byebug.init_file) ScriptProcessor.new(nil, interface).process_commands end end end end byebug-11.1.1/test/rc_test.rb000066400000000000000000000027711361243113500160250ustar00rootroot00000000000000# frozen_string_literal: true require_relative "test_helper" require_relative "../lib/byebug/runner" module Byebug class RcTest < TestCase def setup super example_file.write("sleep 0") example_file.close end def test_run_with_no_rc_option with_command_line("exe/byebug", "--no-rc", example_path) do refute_calls(Byebug, :run_init_script) { non_stop_runner.run } end end def test_rc_file_commands_are_properly_run_by_default rc_positive_test(nil) end def test_rc_file_commands_are_properly_run_by_explicit_option rc_positive_test("--rc") end def test_rc_file_commands_are_properly_run_when_home_folder_not_known with_env("HOME", nil) { rc_positive_test(nil) } end def test_rc_file_with_invalid_commands with_init_file("seta callstyle long") do with_command_line("exe/byebug", "--rc", example_path) do assert_output(nil, /Unknown command 'seta callstyle long'/) do non_stop_runner.run end end end end private def rc_positive_test(flag) args = [flag, example_path].compact with_setting :callstyle, "short" do with_init_file("set callstyle long") do with_command_line("exe/byebug", *args) do non_stop_runner.run assert_equal "long", Setting[:callstyle] end end end end def non_stop_runner @non_stop_runner ||= Byebug::Runner.new(false) end end end byebug-11.1.1/test/remote_debugging_test.rb000066400000000000000000000034071361243113500207240ustar00rootroot00000000000000# frozen_string_literal: true require_relative "test_helper" require_relative "support/remote_debugging_tests" module Byebug # # Tests remote debugging functionality. # class RemoteDebuggingTest < TestCase include RemoteDebuggingTests def program strip_line_numbers <<-RUBY 1: require "byebug" 2: 3: module Byebug 4: # 5: # Toy class to test remote debugging 6: # 7: class #{example_class} 8: def a 9: 3 10: end 11: end 12: 13: require "byebug/core" 14: self.wait_connection = true 15: self.start_server("127.0.0.1") 16: 17: byebug 18: 19: #{example_class}.new.a 20: end RUBY end def program_with_two_breakpoints strip_line_numbers <<-RUBY 1: require "byebug" 2: require "byebug/core" 3: 4: module Byebug 5: # 6: # Toy class to test remote debugging 7: # 8: class #{example_class} 9: def a 10: 3 11: end 12: end 13: 14: self.wait_connection = true 15: self.start_server("127.0.0.1") 16: 17: byebug 18: thingy = #{example_class}.new 19: thingy.a 20: sleep 3 21: thingy.a 22: byebug 23: thingy.a 24: end RUBY end def test_interrupting_client_doesnt_abort_server_after_a_second_breakpoint write_program(program_with_two_breakpoints) status = remote_debug_connect_and_interrupt("cont") assert_equal true, status.success? end end end byebug-11.1.1/test/remote_shortcut_test.rb000066400000000000000000000013241361243113500206400ustar00rootroot00000000000000# frozen_string_literal: true require_relative "test_helper" require_relative "support/remote_debugging_tests" module Byebug # # Tests the remote debugging shortcut. # class RemoteShortcutTest < TestCase include RemoteDebuggingTests def program strip_line_numbers <<-RUBY 1: require "byebug" 2: 3: module Byebug 4: # 5: # Toy class to test remote debugging 6: # 7: class #{example_class} 8: def a 9: 3 10: end 11: end 12: 13: remote_byebug("127.0.0.1") 14: 15: #{example_class}.new.a 16: end RUBY end end end byebug-11.1.1/test/runner_against_program_with_byebug_call_test.rb000066400000000000000000000010471361243113500255450ustar00rootroot00000000000000# frozen_string_literal: true require_relative "test_helper" module Byebug # # Tests standalone byebug when debugging a target program # class RunnerAgainstProgramWithByebugCallTest < TestCase def setup super example_file.write("require 'byebug'\nbyebug\nsleep 0") example_file.close end def test_run_with_a_script_to_debug stdout = run_program( ["ruby", example_path], 'puts "Program: #{$PROGRAM_NAME}"' ) assert_match(/Program: #{example_path}/, stdout) end end end byebug-11.1.1/test/runner_against_valid_program_test.rb000066400000000000000000000065521361243113500233470ustar00rootroot00000000000000# frozen_string_literal: true require_relative "test_helper" require_relative "../lib/byebug/version" module Byebug # # Tests standalone byebug when debugging a target program # class RunnerAgainstValidProgramTest < TestCase def setup super example_file.write("sleep 0") example_file.close end def test_run_with_a_script_to_debug stdout = run_byebug( example_path, input: 'puts "Program: #{$PROGRAM_NAME}"' ) assert_match(/Program: #{example_path}/, stdout) end def test_run_with_a_script_and_params_does_not_consume_script_params stdout = run_byebug( "--", example_path, "-opt", "value", input: 'puts "Args: #{$ARGV.join(\', \')}"' ) assert_match(/Args: -opt, value/, stdout) end def test_run_with_ruby_is_ignored_and_script_passed_instead stdout = run_byebug( "--", "ruby", example_path, input: 'puts "Program: #{$0}"' ) assert_match(/Program: #{example_path}/, stdout) end def test_run_with_fullpath_ruby_is_ignored_and_script_passed_instead stdout = run_byebug( "--", RbConfig.ruby, example_path, input: 'puts "Program: #{$0}"' ) assert_match(/Program: #{example_path}/, stdout) end def test_run_with_post_mortem_mode_flag stdout = run_byebug( "-m", example_path, input: "show post_mortem\nset post_mortem off" ) assert_match(/post_mortem is on/, stdout) end def test_run_with_linetracing_flag stdout = run_byebug( "-t", example_path, input: "show linetrace\nset linetrace off" ) assert_match(/linetrace is on/, stdout) end def test_run_with_no_quit_flag skip stdout = run_byebug( "--no-quit", example_path, input: "cont\nquit!" ) assert_match(/\(byebug:ctrl\)/, stdout) end def test_run_with_require_flag stdout = run_byebug( "-r", example_path, example_path, input: \ "puts \"Abbrev loaded? \#{$LOADED_FEATURES.last == '#{example_path}'}\"" ) assert_match(/Abbrev loaded\? true/, stdout) end def test_run_with_a_single_include_flag stdout = run_byebug( "-I", "dir1", example_path, input: 'puts "dir1 in LOAD_PATH? #{$LOAD_PATH.include?(\'dir1\')}"' ) assert_match(/dir1 in LOAD_PATH\? true/, stdout) end def test_run_with_several_include_flags stdout = run_byebug( "-I", "d1:d2", example_path, input: \ 'puts "d1 and d2 in LOAD_PATH? #{(%w(d1 d2) - $LOAD_PATH).empty?}"' ) assert_match(/d1 and d2 in LOAD_PATH\? true/, stdout) end def test_run_with_debug_flag stdout = run_byebug( "-d", example_path, input: 'puts "Debug flag is #{$DEBUG}"' ) assert_match(/Debug flag is true/, stdout) end def test_run_stops_at_the_first_line_by_default stdout = run_byebug(example_path) assert_match(/=> 1: sleep 0/, stdout) end def test_run_with_no_stop_flag_does_not_stop_at_the_first_line stdout = run_byebug("--no-stop", example_path) refute_match(/=> 1: sleep 0/, stdout) end def test_run_with_stop_flag_stops_at_the_first_line stdout = run_byebug("--stop", example_path) assert_match(/=> 1: sleep 0/, stdout) end end end byebug-11.1.1/test/runner_without_target_program_test.rb000066400000000000000000000047361361243113500236150ustar00rootroot00000000000000# frozen_string_literal: true require_relative "test_helper" require_relative "../lib/byebug/version" module Byebug # # Tests standalone byebug when flags that require no target program are used # class RunnerWithoutTargetProgramTest < TestCase def test_run_with_version_flag stdout = run_byebug("--version") assert_match full_version, stdout end def test_run_with_help_flag stdout = run_byebug("--help") assert_match full_help, stdout end def test_run_with_remote_option_only_with_a_port_number stdout = run_byebug("--remote", "9999") assert_match( /Connecting to byebug server at localhost:9999\.\.\./, stdout ) end def test_run_with_remote_option_with_host_and_port_specification stdout = run_byebug("--remote", "myhost:9999") assert_match(/Connecting to byebug server at myhost:9999\.\.\./, stdout) end def test_run_without_a_script_to_debug stdout = run_byebug assert_match_error("You must specify a program to debug", stdout) end def test_run_with_an_nonexistent_script stdout = run_byebug("non_existent_script.rb") assert_match_error("The script doesn't exist", stdout) end def test_run_with_an_invalid_script example_file.write("[1,2,") example_file.close stdout = run_byebug(example_path) assert_match_error("The script has incorrect syntax", stdout) end private def assert_match_error(message, output) assert_match(/\*\*\* #{message}/, output) end def full_version deindent <<-HELP Running byebug #{Byebug::VERSION} HELP end def full_help deindent <<-HELP byebug #{Byebug::VERSION} Usage: byebug [options] -- -d, --debug Set $DEBUG=true -I, --include list Add to paths to $LOAD_PATH -m, --[no-]post-mortem Use post-mortem mode -q, --[no-]quit Quit when script finishes -x, --[no-]rc Run byebug initialization file -s, --[no-]stop Stop when script is loaded -r, --require file Require library before script -R, --remote [host:]port Remote debug [host:]port -t, --[no-]trace Turn on line tracing -v, --version Print program version -h, --help Display this message HELP end end end byebug-11.1.1/test/support/000077500000000000000000000000001361243113500155425ustar00rootroot00000000000000byebug-11.1.1/test/support/assertions.rb000066400000000000000000000060341361243113500202640ustar00rootroot00000000000000# frozen_string_literal: true require "minitest/mock" module Minitest # # Custom Minitest assertions # module Assertions # # Checks that a given collection is included in another collection # and in correct order. It accepts both strings and regexps as elements of # the arrays. # # @param given [Array] Collection to be checked for inclusion. # @param original [Array] Collection +given+ is checked against. # # @example Passing assertion with simple array # assert_includes_in_order(%w(1 2 3 4 5), %w(1 3 5)) # # @example Failing assertion with simple array # assert_includes_in_order(%w(1 2 3 4 5), %w(1 5 3)) # # @example Passing assertion with array and regexp elements # assert_includes_in_order(%w(1 2 3 4 5), ["1", /\d+/, "5"]) # # @example Failing assertion with array and regexp elements # assert_includes_in_order(%w(1 2 3 4 5), ["1", /\[a-z]+/, "5"]) # def assert_includes_in_order(given, original, msg = nil) msg = message(msg) do "Expected #{mu_pp(original)} to include #{mu_pp(given)} in order" end assert includes_in_order(given, original), msg end def refute_includes_in_order(given, original, msg = nil) msg = message(msg) do "Expected #{mu_pp(original)} to not include #{mu_pp(given)} in order" end refute includes_in_order(given, original), msg end def assert_location(file, line) expected = "#{file}:#{line}" actual = "#{frame.file}:#{frame.line}" msg = "Expected location to be #{expected}, but was #{actual}" assert file == frame.file && line == frame.line, msg end def assert_program_finished assert_nil context.backtrace, "Expected program to have finished" end def assert_calls(object, method, *expected_args, &block) check_calls(:includes, object, method, *expected_args, &block) end def refute_calls(object, method, *expected_args, &block) check_calls(:doesnt_include, object, method, *expected_args, &block) end private def check_calls(check_method, object, method, *expected_args) object.public_send(:stub, method, printer_stub(method)) do yield expected = Regexp.new(Regexp.escape(signature(method, *expected_args))) send(:"check_output_#{check_method}", expected) end end def signature(method, *args) return method.to_s unless args.any? [method, *args].join(" ") end def includes_in_order(collection, original_collection) collection.reduce(0) do |index, item| current_collection = original_collection[index..-1] i = case item when String then current_collection.index(item) when Regexp then current_collection.index { |it| it =~ item } end return false unless i i + 1 end true end def printer_stub(method_name) lambda do |*actual_args| Byebug::Context.interface.puts signature(method_name, *actual_args) end end end end byebug-11.1.1/test/support/matchers.rb000066400000000000000000000013751361243113500177030ustar00rootroot00000000000000# frozen_string_literal: true require "support/assertions" module Byebug # # Some custom matches for byebug's output # module TestMatchers def check_output_includes(*args) check_stream(:assert_includes_in_order, interface.output, *args) end def check_error_includes(*args) check_stream(:assert_includes_in_order, interface.error, *args) end def check_output_doesnt_include(*args) check_stream(:refute_includes_in_order, interface.output, *args) end def check_error_doesnt_include(*args) check_stream(:refute_includes_in_order, interface.error, *args) end private def check_stream(check_method, stream, *args) send(check_method, Array(args), stream.map(&:strip)) end end end byebug-11.1.1/test/support/remote_debugging_tests.rb000066400000000000000000000043721361243113500226250ustar00rootroot00000000000000# frozen_string_literal: true require "test_helper" require "open3" module Byebug # # Tests remote debugging functionality. # module RemoteDebuggingTests def test_connecting_to_remote_debugger write_program(program) remote_debug_and_connect("quit!") check_output_includes \ "Connecting to byebug server at 127.0.0.1:8989...", "Connected." end def test_interacting_with_remote_debugger write_program(program) remote_debug_and_connect("cont 9", "cont") check_output_includes \ "7: class ByebugExampleClass", "8: def a", "=> 9: 3", "10: end" end def test_interrupting_client_doesnt_abort_server write_program(program) status = remote_debug_connect_and_interrupt("cont") assert_equal true, status.success? end def test_ignoring_main_server_and_control_threads write_program(program) remote_debug_and_connect("thread list", "cont") check_output_includes \ %r{!.*/byebug/remote/server.rb}, %r{!.*/byebug/remote/server.rb} end private def write_program(code) example_file.write(code) example_file.close end def remote_debug_and_connect(*commands) remote_debug(*commands) do |wait_th| launch_client wait_for_client_startup wait_th.value end end def remote_debug_connect_and_interrupt(*commands) remote_debug(*commands) do |wait_th, err_th| th = Thread.new { launch_client } wait_for_client_startup th.kill err_th.join wait_th.value end end def remote_debug(*commands) enter(*commands) Open3.popen3(shell_out_env, "ruby #{example_path}") do |_i, _o, e, wait_thr| err_thr = Thread.new { print e.read } yield(wait_thr, err_thr) end end def wait_for_client_startup sleep 0.1 until mutex.synchronize { client.started? } end def launch_client mutex.synchronize { client.start("127.0.0.1") } rescue Errno::ECONNREFUSED sleep 0.1 retry end def mutex @mutex ||= Mutex.new end def client @client ||= Remote::Client.new(Context.interface) end end end byebug-11.1.1/test/support/temporary.rb000066400000000000000000000050341361243113500201130ustar00rootroot00000000000000# frozen_string_literal: true module Byebug # # Some custom matches for changing stuff temporarily during tests # module TestTemporary # # Yields a block using temporary values for command line program name and # command line arguments. # # @param program_name [String] New value for the program name # @param *args [Array] New value for the program arguments # def with_command_line(program_name, *args) original_program_name = $PROGRAM_NAME original_argv = $ARGV $PROGRAM_NAME = program_name $ARGV.replace(args) yield ensure $PROGRAM_NAME = original_program_name $ARGV.replace(original_argv) end # # Yields a block using a specific mode of the debugger # def with_mode(mode) old_mode = Byebug.mode Byebug.mode = mode yield ensure Byebug.mode = old_mode end # # Yields a block using a temporary value for a setting # # @param key [Symbol] Setting key # @param value [Object] Temporary value for the setting # def with_setting(key, value) original_value = Setting[key] Setting[key] = value yield ensure Setting[key] = original_value end # # Temporary creates a new file a yields it to the passed block # def with_new_tempfile(content) file = Tempfile.new("foo") file.write(content) file.close yield(file.path) ensure file.close file.unlink end # # Changes global rc file to have specific contents, runs the block and # restores the old config afterwards. # def with_init_file(content) old_init_file = Byebug.init_file Byebug.init_file = ".byebug_test_rc" with_new_file(File.expand_path(".byebug_test_rc"), content) do yield end ensure Byebug.init_file = old_init_file end # # Creates a file, yields the block and deletes the file afterwards # # @param name [String] Name for the file # @param content [String] Content for the file # def with_new_file(name, content) File.open(name, "w") { |f| f.write(content) } yield ensure File.delete(name) end # # Runs the block with a temporary value for an ENV variable # # @param key [String] Name for the key in the ENV hash # @param vlaue [String] Value of the key in the ENV hash # def with_env(key, value) old_value = ENV[key] ENV[key] = value yield ensure ENV[key] = old_value end end end byebug-11.1.1/test/support/test_case.rb000066400000000000000000000046661361243113500200550ustar00rootroot00000000000000# frozen_string_literal: true require "minitest" require_relative "../../lib/byebug" require_relative "../../lib/byebug/core" require_relative "../../lib/byebug/interfaces/test_interface" require_relative "utils" module Byebug # # Extends Minitest's base test case and provides defaults for all tests. # class TestCase < Minitest::Test make_my_diffs_pretty! include TestUtils include Helpers::StringHelper def self.before_suite Byebug.init_file = ".byebug_test_rc" Context.interface = TestInterface.new Context.ignored_files = Context.all_files end # # Reset to default state before each test # def setup Byebug.start interface.clear end # # Cleanup temp files, and dummy classes/modules. # def teardown cleanup_namespace clear_example_file Byebug.breakpoints&.clear Byebug.catchpoints&.clear Byebug.stop end # # Removes test example file and its memoization # def clear_example_file example_file.close delete_example_file @example_file = nil end # # Cleanup main Byebug namespace from dummy test classes and modules # def cleanup_namespace force_remove_const(Byebug, example_class) force_remove_const(Byebug, example_module) end # # Temporary file where code for each test is saved # def example_file @example_file ||= File.new(example_path, "w+") end # # Path to file where test code is saved # def example_path File.join(example_folder, "byebug_example.rb") end # # Fully namespaced example class # def example_full_class "Byebug::#{example_class}" end # # Name of the temporary test class # def example_class "#{camelized_path}Class" end # # Name of the temporary test module # def example_module "#{camelized_path}Module" end # # Temporary folder where the test file lives # def example_folder @example_folder ||= File.realpath(Dir.tmpdir) end private def camelized_path camelize(File.basename(example_path, ".rb")) end def delete_example_file File.unlink(example_file) rescue StandardError # On windows we need the file closed before deleting it, and sometimes it # didn't have time to close yet. So retry until we can delete it. retry end end end byebug-11.1.1/test/support/utils.rb000066400000000000000000000157161361243113500172410ustar00rootroot00000000000000# frozen_string_literal: true require "support/matchers" require "support/temporary" require "open3" module Byebug # # Misc tools for the test suite # module TestUtils include TestMatchers include TestTemporary # # Adds commands to the input queue, so they will be later retrieved by # Processor, i.e., it emulates user's input. # # If a command is a Proc object, it will be executed before being retrieved # by Processor. May be handy when you need build a command depending on the # current context. # # @example # # enter "b 12", "cont" # enter "b 12", ->{ "disable #{breakpoint.id}" }, "cont" # def enter(*messages) interface.input.concat(messages) end # # Runs the code block passed as a string. # # The string is copied to a new file and then that file is run. This is # done, instead of using `instance_eval` or similar techniques, because # it's useful to load a real file in order to make assertions on backtraces # or file names. # # @param program String containing Ruby code to be run. This string could # be any valid Ruby code, but in order to avoid redefinition warnings in # the test suite, it should define at most one class inside the Byebug # namespace. The name of this class is defined by the +example_class+ # method. # # @param &block Optional proc which will be executed when Processor # extracts all the commands from the input queue. You can use that for # making assertions on the current test. If you specified the block and it # was never executed, the test will fail. # # @example # # enter "next" # prog <<-RUBY # byebug # puts "hello" # RUBY # # debug_code(prog) { assert_equal 3, frame.line } # def debug_code(program, &block) interface.test_block = block debug_in_temp_file(program) interface.test_block&.call interface.test_block = nil end # # Writes a string containing Ruby code to a file and then debugs that file. # # @param program [String] Ruby code to be debugged # def debug_in_temp_file(program) example_file.write(program) example_file.close load(example_path) end # # Strips line numbers from a here doc containing ruby code. # # @param str_with_ruby_code A here doc containing lines of ruby code, each # one labeled with a line number # # @example # # strip_line_numbers <<-EOF # 1: puts "hello" # 2: puts "bye" # EOF # # returns # # puts "hello" # puts "bye" # def strip_line_numbers(str_with_ruby_code) str_with_ruby_code.gsub(/ *\d+: ? ?/, "") end # # Split a string (normally a here doc containing byebug's output) into # stripped lines # # @param output_str [String] # # @example # # split_lines <<-EOF # Sample command # # It does an amazing thing. # EOF # # returns # # ["Sample command", "It does an amazing thing."] # def split_lines(output_str) output_str.split("\n").map(&:strip) end # # Prepares a string to get feed to an assertion accepting arrays of # Regexp's. The string is split into lines and each of them is converted to # a regexp, properly escaping it and ignoring whitespace. # # @param output_str [String] # def prepare_for_regexp(output_str) split_lines(output_str).map do |str| Regexp.new(Regexp.escape(str), Regexp::EXTENDED) end end # # Shortcut to Byebug's interface # def interface Context.interface end # # Shortcut to Byebug's context # def context Byebug.current_context end # # Shortcut to current frame # def frame context.frame end # # Removes all (both enabled and disabled) displays # def clear_displays loop do break if Byebug.displays.empty? Byebug.displays.pop end end # # Remove +const+ from +klass+ without a warning # def force_remove_const(klass, const) klass.send(:remove_const, const) if klass.const_defined?(const) end # # Modifies a line number in a file with new content. # # @param filename File to be changed # @param lineno Line number to be changed # @param new_line New line content # def change_line(file, lineno, new_line) lines = File.readlines(file).tap { |c| c[lineno - 1] = "#{new_line}\n" } File.open(file, "w") { |f| f.write(lines.join) } end # # Replaces line number in file with content # # @param lineno Line number of line to be replaced. # @param file File containing the line to be replaced. # @param content New content for the line. # @param cmd Command to be run right after changing the line. # def cmd_after_replace(file, lineno, content, cmd) change_line(file, lineno, content) cmd end # # A minimal program that gives you a byebug's prompt # def minimal_program <<-RUBY module Byebug byebug "Hello world" end RUBY end # # Runs program in a subprocess feeding it with some input and # returns the output of the program. # # @param cmd [Array] Command line to be run. # @param input [String] Input string to feed to the program. # # @return Program's output # def run_program(cmd, input = "") stdout, = Open3.capture2e(shell_out_env, *cmd, stdin_data: input) stdout end # # Runs byebug in a subprocess feeding it with some input and with # environment . # # @param env [Hash] Environment to be passed to the subprocess. # @param *args [Array] Args to be passed to byebug. # @param input [String] Input string to feed to byebug. # # @return Byebug's output # def run_byebug(*args, input: "") run_program([*binstub, *args], input) end # # Common environment shared by specs that shell out. It needs to: # # * Adds byebug to the LOAD_PATH. # * (Optionally) Setup coverage tracking so that coverage in the subprocess # is tracked. # def shell_out_env(simplecov: true) minitest_test = Thread.current.backtrace_locations.find do |location| location.label.start_with?("test_") end byebug_dir = File.absolute_path(File.join("..", "..", "lib"), __dir__) base = { "MINITEST_TEST" => "#{self.class}##{minitest_test.label}", "RUBYOPT" => "-I #{byebug_dir}" } base["RUBYOPT"] += " -r simplecov" if simplecov base end # # Binstub command used to run byebug in standalone mode during tests # def binstub cmd = "exe/byebug" return [cmd] unless Gem.win_platform? [RbConfig.ruby, cmd] end end end byebug-11.1.1/test/test_helper.rb000066400000000000000000000002031361243113500166640ustar00rootroot00000000000000# frozen_string_literal: true require "simplecov" if ENV["NOCOV"].nil? require "support/test_case" Byebug::TestCase.before_suite