From: Tom Tromey Date: Thu, 25 May 2023 18:25:43 +0000 (-0600) Subject: Implement type checking for DAP breakpoint requests X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=5ad513ae6288b186b982017bc20e7d492220a2ba;p=binutils-gdb.git Implement type checking for DAP breakpoint requests I realized that with a small refactoring, it is possible to type-check the parameters to the various DAP breakpoint requests. This would have caught the earlier bug involving hitCondition. --- diff --git a/gdb/python/lib/gdb/dap/breakpoint.py b/gdb/python/lib/gdb/dap/breakpoint.py index 3f40cfb7f63..227085ad73e 100644 --- a/gdb/python/lib/gdb/dap/breakpoint.py +++ b/gdb/python/lib/gdb/dap/breakpoint.py @@ -143,19 +143,28 @@ def _set_breakpoints(kind, specs): return _set_breakpoints_callback(kind, specs, gdb.Breakpoint) -# Turn a DAP SourceBreakpoint, FunctionBreakpoint, or -# InstructionBreakpoint into a "spec" that is used by -# _set_breakpoints. SPEC is a dictionary of parameters that is used -# as the base of the result; it is modified in place. -def _basic_spec(bp_info, spec): - for name in ("condition", "hitCondition"): - if name in bp_info: - spec[name] = bp_info[name] - return spec +# A helper function that rewrites a SourceBreakpoint into the internal +# form passed to the creator. This function also allows for +# type-checking of each SourceBreakpoint. +@type_check +def _rewrite_src_breakpoint( + *, + # This is a Source but we don't type-check it. + source, + line: int, + condition: Optional[str] = None, + hitCondition: Optional[str] = None, + **args, +): + return { + "source": source["path"], + "line": line, + "condition": condition, + "hitCondition": hitCondition, + } # FIXME we do not specify a type for 'source'. -# FIXME 'breakpoints' is really a list[SourceBreakpoint]. @request("setBreakpoints") @capability("supportsHitConditionalBreakpoints") @capability("supportsConditionalBreakpoints") @@ -163,17 +172,7 @@ def set_breakpoint(*, source, breakpoints: Sequence = (), **args): if "path" not in source: result = [] else: - specs = [] - for obj in breakpoints: - specs.append( - _basic_spec( - obj, - { - "source": source["path"], - "line": obj["line"], - }, - ) - ) + specs = [_rewrite_src_breakpoint(source=source, **bp) for bp in breakpoints] # Be sure to include the path in the key, so that we only # clear out breakpoints coming from this same source. key = "source:" + source["path"] @@ -183,45 +182,64 @@ def set_breakpoint(*, source, breakpoints: Sequence = (), **args): } +# A helper function that rewrites a FunctionBreakpoint into the +# internal form passed to the creator. This function also allows for +# type-checking of each FunctionBreakpoint. +@type_check +def _rewrite_fn_breakpoint( + *, + name: str, + condition: Optional[str] = None, + hitCondition: Optional[str] = None, + **args, +): + return { + "function": name, + "condition": condition, + "hitCondition": hitCondition, + } + + @request("setFunctionBreakpoints") @capability("supportsFunctionBreakpoints") def set_fn_breakpoint(*, breakpoints: Sequence, **args): - specs = [] - for bp in breakpoints: - specs.append( - _basic_spec( - bp, - { - "function": bp["name"], - }, - ) - ) + specs = [_rewrite_fn_breakpoint(**bp) for bp in breakpoints] result = send_gdb_with_response(lambda: _set_breakpoints("function", specs)) return { "breakpoints": result, } +# A helper function that rewrites an InstructionBreakpoint into the +# internal form passed to the creator. This function also allows for +# type-checking of each InstructionBreakpoint. +@type_check +def _rewrite_insn_breakpoint( + *, + instructionReference: str, + offset: Optional[int] = None, + condition: Optional[str] = None, + hitCondition: Optional[str] = None, + **args, +): + # There's no way to set an explicit address breakpoint from + # Python, so we rely on "spec" instead. + val = "*" + instructionReference + if offset is not None: + val = val + " + " + str(offset) + return { + "spec": val, + "condition": condition, + "hitCondition": hitCondition, + } + + @request("setInstructionBreakpoints") @capability("supportsInstructionBreakpoints") def set_insn_breakpoints( *, breakpoints: Sequence, offset: Optional[int] = None, **args ): - specs = [] - for bp in breakpoints: - # There's no way to set an explicit address breakpoint - # from Python, so we rely on "spec" instead. - val = "*" + bp["instructionReference"] - if offset is not None: - val = val + " + " + str(offset) - specs.append( - _basic_spec( - bp, - { - "spec": val, - }, - ) - ) + specs = [_rewrite_insn_breakpoint(**bp) for bp in breakpoints] result = send_gdb_with_response(lambda: _set_breakpoints("instruction", specs)) return { "breakpoints": result, @@ -249,6 +267,23 @@ def _set_exception_catchpoints(filter_options): return _set_breakpoints_callback("exception", filter_options, _catch_exception) +# A helper function that rewrites an ExceptionFilterOptions into the +# internal form passed to the creator. This function also allows for +# type-checking of each ExceptionFilterOptions. +@type_check +def _rewrite_exception_breakpoint( + *, + filterId: str, + condition: Optional[str] = None, + # Note that exception breakpoints do not support a hit count. + **args, +): + return { + "filterId": filterId, + "condition": condition, + } + + @request("setExceptionBreakpoints") @capability("supportsExceptionFilterOptions") @capability( @@ -272,6 +307,7 @@ def set_exception_breakpoints( # Convert the 'filters' to the filter-options style. options = [{"filterId": filter} for filter in filters] options.extend(filterOptions) + options = [_rewrite_exception_breakpoint(**bp) for bp in options] result = send_gdb_with_response(lambda: _set_exception_catchpoints(options)) return { "breakpoints": result,