Skip to content

Commit

Permalink
[infer/py] initial support for RAISE_VARARGS
Browse files Browse the repository at this point in the history
Summary: Limited support for RAISE_VARARGS, the `throw` instruction in Python. I'll improve support as I learn more about exceptions in Python

Reviewed By: ngorogiannis

Differential Revision: D49417753

fbshipit-source-id: d81128f67b098d74c41d7e74ae18a2f37294c708
  • Loading branch information
Vincent Siles authored and facebook-github-bot committed Sep 26, 2023
1 parent 807a04a commit c5385c4
Show file tree
Hide file tree
Showing 2 changed files with 125 additions and 0 deletions.
74 changes: 74 additions & 0 deletions infer/src/python/PyTrans.ml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ module Error = struct
| CallKwMissing of string
| CallKwMissingId of Ident.t
| CallKwInvalidFunction of DataStack.cell
| RaiseException of int
| RaiseExceptionSource of DataStack.cell

let pp_todo fmt = function
| UnsupportedOpcode opname ->
Expand Down Expand Up @@ -69,6 +71,10 @@ module Error = struct
Ident.pp id
| CallKwInvalidFunction cell ->
F.fprintf fmt "Unsupported CALL_FUNCTION_KW with %a" DataStack.pp_cell cell
| RaiseException n ->
F.fprintf fmt "Unsupported RAISE_VARARGS mode %d" n
| RaiseExceptionSource cell ->
F.fprintf fmt "Unsupported source of exception: %a" DataStack.pp_cell cell


type kind =
Expand Down Expand Up @@ -113,6 +119,8 @@ module Error = struct
| CallKeywordNotString0 of FFI.Constant.t
| CallKeywordNotString1 of DataStack.cell
| CallKeywordBuildClass
| RaiseExceptionInvalid of int
| RaiseExceptionUnknown of DataStack.cell

type t = L.error * kind

Expand Down Expand Up @@ -205,6 +213,10 @@ module Error = struct
F.fprintf fmt "CALL_FUNCTION_KW: keyword is not a string: %a" DataStack.pp_cell cell
| CallKeywordBuildClass ->
F.pp_print_string fmt "CALL_FUNCTION_KW cannot be used with LOAD_BUILD_CLASS"
| RaiseExceptionInvalid n ->
F.fprintf fmt "RAISE_VARARGS invalid mode %d" n
| RaiseExceptionUnknown cell ->
F.fprintf fmt "RAISE_VARARGS unknown construct %a" DataStack.pp_cell cell


let class_decl (err, kind) = (err, ClassDecl kind)
Expand Down Expand Up @@ -1600,6 +1612,7 @@ module JUMP = struct
| Return of T.Terminator.t
| TwoWay of {condition: T.BoolExp.t; next_info: jump_info; other_info: jump_info}
| Absolute of jump_info
| Throw of T.Exp.t

module POP_IF = struct
(** {v POP_JUMP_IF_TRUE(target) v}
Expand Down Expand Up @@ -2254,6 +2267,52 @@ module UNPACK = struct
end
end

module RAISE_VARARGS = struct
(** RAISE_VARARGS(argc)
Raises an exception using one of the 3 forms of the raise statement, depending on the value of
[argc]:
0: raise (re-raise previous exception)
1: raise top-of-stack (raise exception instance or type at TOS)
2: raise top-of-stack1 from top-of-stack (raise exception instance or type at top-of-stack1
with [__cause__] set to top-of-stack) *)
let run env {FFI.Code.co_names} {FFI.Instruction.opname; arg= argc} =
let open IResult.Let_syntax in
Debug.p "[%s] argc= %d\n" opname argc ;
let* () =
match argc with
| 1 ->
Ok ()
| 0 | 2 ->
Error (L.InternalError, Error.TODO (RaiseException argc))
| _ ->
Error (L.ExternalError, Error.RaiseExceptionInvalid argc)
in
let loc = Env.loc env in
let* env, tos = pop_datastack opname env in
match (tos : DataStack.cell) with
| Name {global; ndx} -> (
let name = co_names.(ndx) in
let key = mk_key global loc name in
let opt_sym = Env.lookup_symbol env key in
match opt_sym with
| Some {Symbol.kind= Class; id} ->
(* TODO: check this heuristic. Not sure what can be raised yet *)
let s = Ident.to_string ~sep:"::" id in
let exp = T.Exp.Const (Str s) in
let throw = JUMP.Throw exp in
Ok (env, Some throw)
| Some {Symbol.kind; id} ->
Error (L.InternalError, Error.LoadInvalid (kind, id))
| None ->
Error (L.InternalError, Error.RaiseExceptionUnknown tos) )
| _ ->
Error (L.InternalError, Error.TODO (RaiseExceptionSource tos))
end

(** Main opcode dispatch function. *)
let run_instruction env code ({FFI.Instruction.opname; starts_line} as instr) next_offset_opt =
Debug.p "Dump Stack:\n%a\n" (Pp.seq ~sep:"\n" DataStack.pp_cell) (Env.stack env) ;
Expand Down Expand Up @@ -2410,6 +2469,8 @@ let run_instruction env code ({FFI.Instruction.opname; starts_line} as instr) ne
ROT.FOUR.run env instr
| "UNPACK_SEQUENCE" ->
UNPACK.SEQUENCE.run env code instr
| "RAISE_VARARGS" ->
RAISE_VARARGS.run env code instr
| _ ->
Error (L.InternalError, Error.TODO (UnsupportedOpcode opname))

Expand Down Expand Up @@ -2575,6 +2636,19 @@ let until_terminator env label_info code instructions =
let env, node = unconditional_jump env ssa_args label_info offset in
Debug.p " Absolute: register %s at %d\n" (Env.Label.name label_info) offset ;
Ok (env, rest, node)
| Some (Throw exp) ->
Debug.p " Throwing exception %a\n" T.Exp.pp exp ;
let node =
T.Node.
{ label
; ssa_parameters
; exn_succs= [] (* TODO ? *)
; last= Throw exp
; instrs= Env.instructions env
; last_loc
; label_loc }
in
Ok (env, rest, node)


(** Process a sequence of instructions until there is no more left to process. *)
Expand Down
51 changes: 51 additions & 0 deletions infer/src/python/unit/PyTransTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -3265,3 +3265,54 @@ f(0, y=2, x=1)
declare $builtins.python_int(int) : *PyInt |}]
end )
let%test_module "exception" =
( module struct
let%expect_test _ =
let source = {|
class C(Exception):
pass
raise C
|} in
test source ;
[%expect
{|
.source_language = "python"
define dummy.$toplevel() : *PyNone {
#b0:
n0 = $builtins.python_class("dummy::C")
throw "dummy::C"
}
declare dummy::C(...) : *dummy::C
declare dummy::C.__init__(...) : *PyNone
global dummy::C$static: *PyObject
type .static dummy::C$static extends Exception$static = {}
type dummy::C extends Exception = {}
global $python_implicit_names::__name__: *PyString
global $python_implicit_names::__file__: *PyString
declare $builtins.python_class(*String) : *PyClass
declare $builtins.python_tuple(...) : *PyObject
declare $builtins.python_bytes(*Bytes) : *PyBytes
declare $builtins.python_string(*String) : *PyString
declare $builtins.python_bool(int) : *PyBool
declare $builtins.python_float(float) : *PyFloat
declare $builtins.python_int(int) : *PyInt |}]
end )

0 comments on commit c5385c4

Please sign in to comment.