Skip to content

Commit

Permalink
[inferpython] generate qualified Textual procdesc
Browse files Browse the repository at this point in the history
Summary: instead of generating Textual toplevel procedures, we use the filepath+module name to qualify them. Now we can analyze multiple files in a package, even if they have same names.

Reviewed By: skcho

Differential Revision:
D65819620

Privacy Context Container: L1208441

fbshipit-source-id: 962d86e922a456be078944ecf89a62c920983232
  • Loading branch information
davidpichardie authored and facebook-github-bot committed Nov 13, 2024
1 parent 63d2c01 commit e18394a
Show file tree
Hide file tree
Showing 15 changed files with 206 additions and 43 deletions.
6 changes: 4 additions & 2 deletions infer/src/pulse/PulseModelsPython.ml
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,13 @@ let import_name name _fromlist _level : model =
start_model
@@ fun () ->
let* opt_str = as_constant_string name in
let function_name =
let module_name =
Option.value_or_thunk opt_str ~default:(fun () ->
L.die InternalError "frontend should always give a string here" )
in
let proc_name = Procname.make_python ~class_name:None ~function_name in
let class_name = PythonClassName.make module_name in
let function_name = "__module_body__" in
let proc_name = Procname.make_python ~class_name:(Some class_name) ~function_name in
let* globals = Dict.make [] [] in
(* TODO: call it only once! *)
let* _ = python_call proc_name [("globals", globals)] in
Expand Down
2 changes: 1 addition & 1 deletion infer/src/python/PyIR.mli
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ module ScopedIdent : sig
end

module QualName : sig
type t
type t = {module_name: Ident.t; function_name: Ident.t}

val pp : Format.formatter -> t -> unit

Expand Down
27 changes: 13 additions & 14 deletions infer/src/python/PyIR2Textual.ml
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,23 @@ module Typ = struct
let value = Textual.(Typ.Ptr (Typ.Struct (TypeName.of_string "PyObject")))
end

let str_module_body name = F.asprintf "%a.__module_body__" Ident.pp name
let str_module_body = "__module_body__"

type proc_kind = ModuleBody of Ident.t | RegularFunction of QualName.t

let is_module_body = function ModuleBody _ -> true | _ -> false

let mk_qualified_proc_name ?loc kind =
let qual_name_str =
match kind with
| ModuleBody name ->
str_module_body name
| RegularFunction qual_name ->
F.asprintf "%a" QualName.pp qual_name
in
{ Textual.QualifiedProcName.enclosing_class= TopLevel
; name= Textual.ProcName.of_string ?loc qual_name_str }
let typename_of_ident ?loc ident = Textual.TypeName.of_string ?loc (F.asprintf "%a" Ident.pp ident)

let mk_qualified_proc_name ?loc kind : Textual.QualifiedProcName.t =
let procname_of_ident ident = Textual.ProcName.of_string ?loc (F.asprintf "%a" Ident.pp ident) in
match kind with
| ModuleBody name ->
{ enclosing_class= Enclosing (typename_of_ident name)
; name= Textual.ProcName.of_string ?loc str_module_body }
| RegularFunction {module_name; function_name} ->
{ enclosing_class= Enclosing (typename_of_ident ?loc module_name)
; name= procname_of_ident function_name }


let mk_procdecl_attributes {CodeInfo.co_argcount; co_varnames} =
Expand Down Expand Up @@ -128,9 +129,7 @@ let rec of_exp exp : Textual.Exp.t =
| LoadClassDeref {name; slot= _} ->
call_builtin "py_load_class_deref" [exp_of_ident_str name] (* TODO: more arg needed *)
| ImportName {name; fromlist; level} ->
let str =
str_module_body name |> Textual.ProcName.of_string |> F.asprintf "%a" Textual.ProcName.pp
in
let str = typename_of_ident name |> F.asprintf "%a" Textual.TypeName.pp in
call_builtin "py_import_name" [Textual.Exp.Const (Str str); of_exp fromlist; of_exp level]
| ImportFrom {name; exp} ->
call_builtin "py_import_from" [exp_of_ident_str name; of_exp exp]
Expand Down
30 changes: 15 additions & 15 deletions infer/src/python/unit/PyIR2TextualTest.ml
Original file line number Diff line number Diff line change
Expand Up @@ -59,20 +59,20 @@ def g():
TRANSFORMATION PyIR -> Textual
.source_language = "python"

define dummy::__module_body__(globals: *PyGlobals) : *PyObject {
define dummy.__module_body__(globals: *PyGlobals) : *PyObject {
local locals: *PyLocals
#b0:
store &locals <- globals
_ = $builtins.py_store_name("x", locals, globals, $builtins.py_make_int(0))
n0 = $builtins.py_make_function(fun (globals, locals) -> dummy::f(globals, locals), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none())
n0 = $builtins.py_make_function(fun (globals, locals) -> dummy.f(globals, locals), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none())
_ = $builtins.py_store_name("f", locals, globals, n0)
n1 = $builtins.py_make_function(fun (globals, locals) -> dummy::g(globals, locals), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none())
n1 = $builtins.py_make_function(fun (globals, locals) -> dummy.g(globals, locals), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none())
_ = $builtins.py_store_name("g", locals, globals, n1)
ret $builtins.py_make_none()

}

define .args = "y,l" dummy::f(globals: *PyGlobals, locals: *PyLocals) : *PyObject {
define .args = "y,l" dummy.f(globals: *PyGlobals, locals: *PyLocals) : *PyObject {
#b0:
n0 = $builtins.py_load_fast("y", locals)
if n0 then jmp b1 else jmp b2
Expand Down Expand Up @@ -107,7 +107,7 @@ def g():

}

define dummy::g(globals: *PyGlobals, locals: *PyLocals) : *PyObject {
define dummy.g(globals: *PyGlobals, locals: *PyLocals) : *PyObject {
#b0:
n0 = $builtins.py_load_global("print", globals)
n1 = $builtins.py_load_global("x", globals)
Expand All @@ -119,20 +119,20 @@ def g():
TYPE INFERENCE
.source_language = "python"

define dummy::__module_body__(globals: *PyGlobals) : *PyObject {
define dummy.__module_body__(globals: *PyGlobals) : *PyObject {
local locals: *PyLocals
#b0:
store &locals <- [&globals:*PyGlobals]:*PyGlobals
_ = $builtins.py_store_name("x", [&locals:*PyLocals], [&globals:*PyGlobals], $builtins.py_make_int(0))
n0 = $builtins.py_make_function(fun (globals, locals) -> dummy::f(globals, locals), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none())
n0 = $builtins.py_make_function(fun (globals, locals) -> dummy.f(globals, locals), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none())
_ = $builtins.py_store_name("f", [&locals:*PyLocals], [&globals:*PyGlobals], n0)
n1 = $builtins.py_make_function(fun (globals, locals) -> dummy::g(globals, locals), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none())
n1 = $builtins.py_make_function(fun (globals, locals) -> dummy.g(globals, locals), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none(), $builtins.py_make_none())
_ = $builtins.py_store_name("g", [&locals:*PyLocals], [&globals:*PyGlobals], n1)
ret $builtins.py_make_none()

}

define .args = "y,l" dummy::f(globals: *PyGlobals, locals: *PyLocals) : *PyObject {
define .args = "y,l" dummy.f(globals: *PyGlobals, locals: *PyLocals) : *PyObject {
#b0:
n0 = $builtins.py_load_fast("y", [&locals:*PyLocals])
if n0 then jmp b1 else jmp b2
Expand Down Expand Up @@ -167,7 +167,7 @@ def g():

}

define dummy::g(globals: *PyGlobals, locals: *PyLocals) : *PyObject {
define dummy.g(globals: *PyGlobals, locals: *PyLocals) : *PyObject {
#b0:
n0 = $builtins.py_load_global("print", [&globals:*PyGlobals])
n1 = $builtins.py_load_global("x", [&globals:*PyGlobals])
Expand All @@ -185,7 +185,7 @@ def g():
#entry:
n0:*PyGlobals = load &globals
n1:*PyLocals = load &locals
n2 = dummy::g(n0, n1)
n2 = dummy.g(n0, n1)
ret n2

}
Expand All @@ -196,12 +196,12 @@ def g():
#entry:
n0:*PyGlobals = load &globals
n1:*PyLocals = load &locals
n2 = dummy::f(n0, n1)
n2 = dummy.f(n0, n1)
ret n2

}

define dummy::__module_body__(globals: *PyGlobals) : *PyObject {
define dummy.__module_body__(globals: *PyGlobals) : *PyObject {
local locals: *PyLocals
#b0:
n2:*PyGlobals = load &globals
Expand Down Expand Up @@ -233,7 +233,7 @@ def g():

}

define .args = "y,l" dummy::f(globals: *PyGlobals, locals: *PyLocals) : *PyObject {
define .args = "y,l" dummy.f(globals: *PyGlobals, locals: *PyLocals) : *PyObject {
#b0:
n13:*PyLocals = load &locals
n0 = $builtins.py_load_fast("y", n13)
Expand Down Expand Up @@ -292,7 +292,7 @@ def g():

}

define dummy::g(globals: *PyGlobals, locals: *PyLocals) : *PyObject {
define dummy.g(globals: *PyGlobals, locals: *PyLocals) : *PyObject {
#b0:
n3:*PyGlobals = load &globals
n0 = $builtins.py_load_global("print", n3)
Expand Down
4 changes: 2 additions & 2 deletions infer/tests/codetoanalyze/python/pulse/.inferconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"pulse-specialization-partial": true,
"pulse-taint-short-traces": true,
"pulse-taint-sources": [
{ "procedure": "taint::source" }
{ "procedure": "taint.source" }
],
"pulse-taint-sinks": [
{ "procedure": "taint::sink",
{ "procedure": "taint.sink",
"taint_target": ["ArgumentPositions", [1]]
}
]
Expand Down
2 changes: 1 addition & 1 deletion infer/tests/codetoanalyze/python/pulse/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ INFERPRINT_OPTIONS = --issues-tests

include $(TESTS_DIR)/infer.make

SOURCES = $(sort $(wildcard *.py))
SOURCES = $(sort $(wildcard *.py) $(wildcard */*.py) $(wildcard */*/*.py))

default: test

Expand Down
Empty file.
10 changes: 10 additions & 0 deletions infer/tests/codetoanalyze/python/pulse/dir1/dir3/testmod.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

import random
import taint

tainted_global3 = taint.source()
untainted_global3 = random.random()
10 changes: 10 additions & 0 deletions infer/tests/codetoanalyze/python/pulse/dir1/dir4/testmod.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

import random
import taint

tainted_global4 = taint.source()
untainted_global4 = random.random()
10 changes: 10 additions & 0 deletions infer/tests/codetoanalyze/python/pulse/dir1/testmod.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

import random
import taint

tainted_global1 = taint.source()
untainted_global1 = random.random()
10 changes: 10 additions & 0 deletions infer/tests/codetoanalyze/python/pulse/dir2/dir5/testmod.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

import random
import taint

tainted_global5 = taint.source()
untainted_global5 = random.random()
10 changes: 10 additions & 0 deletions infer/tests/codetoanalyze/python/pulse/dir2/dir6/testmod.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

import random
import taint

tainted_global6 = taint.source()
untainted_global6 = random.random()
10 changes: 10 additions & 0 deletions infer/tests/codetoanalyze/python/pulse/dir2/testmod.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Copyright (c) Facebook, Inc. and its affiliates.
#
# This source code is licensed under the MIT license found in the
# LICENSE file in the root directory of this source tree.

import random
import taint

tainted_global2 = taint.source()
untainted_global2 = random.random()
Loading

0 comments on commit e18394a

Please sign in to comment.