精品国产人成在线_亚洲高清无码在线观看_国产在线视频国产永久2021_国产AV综合第一页一个的一区免费影院黑人_最近中文字幕MV高清在线视频

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

Python的C外部函數的靜態類型推斷

jf_glM2sZ6i ? 來源:編程語言Lab ? 2023-07-14 17:42 ? 次閱讀

Python 已經成為最流行的編程語言之一,大量 Python 包采用 Python/C 的多語言架構,其中宿主語言 Python 和外部語言 C/C++ 的結合兼具開發效率與性能,被包括 NumPy、Pillow、TensorFlow 和 PyTorch 等在內的諸多主流軟件系統所采用。但是 Python 和 C/C++ 之間語言特性的差異也使得基于 Python/C API 的跨語言接口代碼容易出錯,類型誤用就是常見的錯誤之一。

# Python 程序的靜態類型推斷

學界和業界都在 Python 的靜態類型推斷這一問題上做了大量嘗試。這些工作可以分為以下三類:

通過擴展語法支持類型標注,并基于類型標注進行類型推斷。該方法的弊端在于需要修改源碼且影響了 Python 開發的敏捷性。

基于機器學習的方法。一方面該方法的數據集往往來自傳統方法的推斷結果,另一方面該方法是非確定性的,只能得到概率的結果。

基于數據流分析、抽象解釋、SMT 求解等傳統程序分析方法。這些工作對外部對象的處理往往采用直接忽略(如視為 object 類型)或預置類型存根(type stubs)等方法,類型推斷精度表現不佳或需要人工輔助。

# 研究方法與定義#

# 外部接口

Python 的外部接口 Python/C API 是橋接 Python 和 C/C++ 的中間層。如圖 1 所示,外部函數 ext.foo 通過 Python/C API PyMethodDef 映射到 C 實現 _foo,再通過 Python/C API PyModuleDef 關聯到模塊 ext。在 C 實現內部,Python/C API PyArg_ParseTuple 是一類常見的參數解析方法,它通過格式化串指明由 Python 到 C 的類型轉換。Python/C API PyLong_FromLong 是一類常見的返回類型轉換,它把一個 C 整型變量轉換到 Python 整型變量。

23b85e20-222a-11ee-962d-dac502259ad0.png

圖 1:Python 的 C 擴展模塊的一個例子

對于靜態類型語言,外部函數在聲明時帶有顯式類型,動態類型語言雖然沒有這一信息(如圖 1(b)第 2 行),但在接口層仍需給出包含跨語言的類型轉換等信息的調用接口描述。我們的核心思路就是建模并分析這些調用接口描述中的隱式信息,推斷作用于外部函數的類型約束。

23e1849e-222a-11ee-962d-dac502259ad0.png

圖 2:多語言與類型系統視角下的外部函數調用

圖 2 是類型系統視角下的外部函數調用。如果僅僅從單語言視角來看(藍色虛線),外部函數的參數類型和返回類型都是不可知的(灰色框);但在多語言的視角下(紅色實線),結合跨語言接口層得到的調用接口描述,外部函數的類型實際是可推導的。我們把這些隱式信息分成三個部分:

外部函數聲明(D)建立 Python 側外部函數的調用名和 C 側外部函數實現之間的映射關系。

參數類型轉換(P)刻畫 Python 側傳入外部函數的實參到 C 變量的類型轉換。

返回類型轉換(R)刻畫 C 側返回值返回 Python 側時的類型轉換。

# 抽象語法

我們形式化地把 Python/C 多語言軟件系統的抽象語法表示為圖 3,其中上標 p 和 c 標記不同的語言側。

2402ec38-222a-11ee-962d-dac502259ad0.png

圖 3:Python/C 多語言系統的抽象語法

不同于本地函數在 Python 側聲明與定義,并在 Python 側應用,外部函數的應用在 Python 側,但其聲明和定義都在 C 側。

# 類型

作為動態類型語言,如圖 4 所示,Python 側的類型是 Python 變量在運行時被綁定的類型值,包括 str、int、object 等內置類型;同時我們引入函數類型 pFunc 表示函數,引入積類型 pProduct 表示 list、tuple、dict 等類型,引入和類型 pUnion 支持共用體這一 C 側常見的語言特性。一些類型如 module、iterator 等不在類型集合中,因為它們在傳遞給外部函數時會被作為 object 類型對象處理。

244363ee-222a-11ee-962d-dac502259ad0.png

圖 4:Python 側類型

Python/C 跨語言接口層的調用接口描述能夠給出更嚴格的類型和值約束。比如圖 1 中,Python/C API 函數 PyArg_ParseTuple 的第 2 個參數給出的格式化串 II 中的格式化單元I要求傳入對應的外部函數的實參是一個 Python 整型并且非負。我們利用子類型來刻畫這類規則。Python 側類型的子定型規則如圖 5 所示。

246e5ed2-222a-11ee-962d-dac502259ad0.png

圖 5:Python 側類型的子定型規則

如圖 6 所示,C 側除了常見的內置類型外,還包括一些在 CPython 解釋器內部的、與 Python 側類型實現相對應的結構體。作為 Python/C API 的一部分,它們也被用于在接口層接收 Python 側傳遞并轉換的對象。

248d5e36-222a-11ee-962d-dac502259ad0.png

圖 6:C 側類型

# 類型推斷#

在以上核心思路和文法定義的基礎上,我們把類型推斷規則形式化地表示為如下形式:

24b075e2-222a-11ee-962d-dac502259ad0.png

外部函數聲明(D)、參數類型轉換(P)、返回類型轉換(R)共同構成推理前提,從而推導出包含在 Python/C 跨語言接口層調用接口描述中的外部函數的類型簽名,函數類型的參數類型和返回類型由 D、P、R 的具體組合確定。

# 外部函數聲明 D

24d296b8-222a-11ee-962d-dac502259ad0.png

表示如上,它描述了 Python 側外部函數調用名和 C 側實現的映射關系。其中flag給出調用慣例,在 CPython 中典型的如METH_VARARGS,它表示外部函數接收一個或多個 Python 對象作為參數,它們被打包成一個對象并傳遞到 C 側,跨語言接口層需要給出把這一打包對象解析到多個 C 變量的規則。

# 參數類型轉換 P

24e8999a-222a-11ee-962d-dac502259ad0.png

表示如上,它刻畫了基于程序性質 P 在 C 側(包含跨語言接口代碼)的上下文中描述的 Python 類型到 C 類型的轉換。這種隱式信息的分析包含了以下兩類常見的規則:

## 調用慣例分析

例如,當調用慣例flag為METH_NOARGS時,外部函數被聲明為無參的,表示為如下的無參分析(Parameter-Free Analysis),

25027bda-222a-11ee-962d-dac502259ad0.png

假設判斷 表示只有當關于程序性質 P 的門限語義謂詞 為真時,判斷 J 成立。

然而,當對應的 C 實現根本不解析并使用傳入的參數時,外部函數實際也是無參的?;谝粋€未使用形參的分析(Unused Parameter Analysis)可以類似地表示如下,

251c27c4-222a-11ee-962d-dac502259ad0.png

動態類型語言(Python)和靜態類型語言(C/C++)之間類型系統的差異,以及 Python 外部接口的設計導致了這種聲明上的冗余,并且留下了聲明不一致的隱患。即只有當上述兩個門限語義謂詞都為真時,才可以確定外部函數是無參的,用合取范式表示如下:

25409780-222a-11ee-962d-dac502259ad0.png

## 參數解析分析

當上述兩個門限語義謂詞都為假時,外部函數至少接收一個 Python 對象。如上所述,這些 Python 側對象被打包為跨語言接口層的一個中間參數,該參數被解析并恢復到若干個 C 變量。最常見的,這一跨語言的類型轉換是由參數解析族 Python/C API 完成。這些 Python/C API 用一個格式化串指明轉換規則,一個格式化串包含零或多個格式化單元,每個格式化單元(特殊含義字符除外)對應一個 Python 類型到 C 類型的轉換,表示如下:

255ab7be-222a-11ee-962d-dac502259ad0.png

其中 是某個參數解析族的 Python/C API(常見的如 PyArg_ParseTuple), 是其第 i 個格式化單元。例如,格式化單元 對應 Python 非負整型到 C 無符號 int 類型的轉換, ,完整的格式化單元轉換規則表見論文表 1。

# 返回類型轉換 R

256f7c58-222a-11ee-962d-dac502259ad0.png

表示如上,它刻畫了基于程序性質 P 在 C 側(包含跨語言接口)的上下文中描述的 C 類型到 Python 類型的轉換。作為 Python 的一部分,其外部函數也支持多返回(multiple returns)的語言特性,而 C 本身是不支持的。這部分隱式信息的分析包含四類常見的規則。

##值構建分析

同樣基于格式化串,但是方向與參數類型轉換的參數解析分析相反,即:

2584ad3a-222a-11ee-962d-dac502259ad0.png

是某個值構建族的 Python/C API(常見的如 Py_BuildValue), 是其第 j 個格式化單元,m 個 C 變量根據對應的格式化單元轉換到 Python 對象并共同構成一個 tuple 對象作為多返回的值。

## 顯性轉換分析

一些 Python/C API 支持直接以單一對象作為外部函數的返回。

25a6b54c-222a-11ee-962d-dac502259ad0.png

顯式轉換的 Python/C API 形如:(1)PyPT_FromCT 把一個 CT 類型的 C 變量轉換到一個 PT 類型的 Python 變量,(2)PyPT_New 創建并返回一個 PT 類型的 Python 變量,(3)Py_PT 本身直接作為一個 PT 類型的對象被返回到 Python 側。

## 類型轉換(type cast)分析

C 程序支持作為右結合算子的顯式類型轉換,對于一個形如 的返回表達式,可以推斷:

25c3db36-222a-11ee-962d-dac502259ad0.png

作為外部函數的 C 實現向 Python 側的返回,C 類型 是與 Python 內置類型一一對應的結構體(圖 6 中 Py 開頭的 C 類型)。

## 可達定義分析

考慮如下圖所示的更復雜的返回情形,

25dbdde4-222a-11ee-962d-dac502259ad0.png

圖 7:一個復雜的返回類型轉換的例子

變量 result 被聲明為 T1 類型(一般為 PyObject*),其可能通過調用前述的一些 Python/C API 被賦值為更精化的類型(T2,T3)。我們通過一個過程內的可達定義分析 來分析這樣的類型傳播。 內部會調用前面三類的返回類型轉換分析?;诳蛇_定義分析的返回類型轉換表示如下:

25f6f71e-222a-11ee-962d-dac502259ad0.png

# 小結

對于類型推斷(TInfer)的三個前提,外部函數聲明(D)只有一種形式,參數類型轉換(P)包含形式(Pcc)和(Pap),返回類型轉換(R)包含形式(Rvb),(Rec),(Rtc)和(Rrd)。這樣,對于使用參數解析分析進行參數類型轉換、使用值構建分析進行返回類型轉換的一個外部函數典型模式,其類型推斷規則如下:

26132506-222a-11ee-962d-dac502259ad0.png

類似地,帶有顯式返回的無參外部函數可以推斷如下:

263083e4-222a-11ee-962d-dac502259ad0.png

# 實驗結果#

我們的靜態類型推斷系統 PyCType 的原型結構如圖 8 所示。

264a1264-222a-11ee-962d-dac502259ad0.png

圖 8:PyCType 架構概覽

接口分離器從 Python/C 多語言項目中分離出跨語言接口代碼。預處理配置器配置解析文件所需的依賴。AST 解析器基于 Python 實現的 C99 解析器 pycparser。在得到接口代碼的 AST 后,多數分析基于訪問 AST 實現,當某個分析需要其他中間表示如 CFG 時,AST 變換模塊對對應的 AST 片段進行變換。其他主要處理模塊(圓角矩形)與前文對應。

# 外部函數聲明與其實現不一致的漏洞

如調用慣例分析小節所述,同一個外部函數其無參分析(PFA)和未使用參數分析(UPA)可能不能同時成立,這會導致一個無參外部函數可以接受任意類型的參數。

# 可靠性

類型推斷的可靠性是構建以上嚴格的推理系統的主要目的之一,即類型推斷的結果沒有錯誤(但可能不夠精確,如把 int 推斷為 object)。我們通過人工檢查驗證了該靜態類型推斷系統的可靠性。同時漏洞發現也是沒有誤報的,所有錯誤都可以構造出對應的觸發代碼。(這里的可靠性是對類型推斷而言的。在漏洞檢查的研究中,可靠一般指沒有漏報。如果將一致性漏洞檢查作為一個獨立的系統,其應表示為類型推斷系統調用慣例分析的謂詞條件的否定命題。)

# 完備性

在可靠的基礎上,完備性成為衡量類型推斷系統有效性的一個重要指標,即推斷率。如表 1 所示是 CPython、NumPy 和 Pillow 中外部函數的參數類型的推斷率??梢钥吹?,對于參數類型轉換,規則(Pcc)和(Pap)能夠覆蓋大多數的情形。同時在規則中描述更多符合條件的 Python/C API 并不困難。

表 1:參數類型推斷的完備性

2675cc56-222a-11ee-962d-dac502259ad0.png

一方面,參數個數往往多于返回值,一方面,外部函數調用作為 Python 程序的一部分,其返回值可能是其他(外部或本地)函數的參數。因此,整個系統的有效性需要和已有的單語言的類型推斷工具結合起來進行評估。

# 有效性

由于可靠并不等于精確,因此我們選擇上表中推斷率最高的 Pillow 來構建類型增強實驗以進一步衡量有效性。來自 Google 的 Pytype 是 state-of-the-art 的 Python 靜態類型推斷工具,其不支持外部函數的自動推斷,而是通過類型存根預置了一些外部函數的類型簽名,如常見的標準庫函數。我們把對 Pillow 中外部函數類型推斷的結果編碼成 Pytype 的類型存根,然后比較這一類型增強前后的推斷率。實驗目標我們選擇 GitHub 中使用了 Pillow 且星標多于 3 萬的 Python 倉庫,實驗結果如表 2 所示,可以看到,PyCType 對 Pytype 有 7% 到 80% 的提升(平均 27.5%)。

表 2:類型推斷增強實驗

269c8210-222a-11ee-962d-dac502259ad0.png

26bc57d4-222a-11ee-962d-dac502259ad0.png

# 總結#

我們提出了 Python 外部函數的靜態類型推斷系統 PyCType。其類型推斷規則包括三部分可組合的推理前提,分別建模和分析 Python/C 跨語言接口層中類型轉換相關的隱式信息。在主流軟件系統上的實驗表明,PyCType 能夠可靠地推斷多數外部函數的類型簽名。其作為單語言 Python 靜態類型推斷工具的增強,使其能夠推斷含有外部函數調用的程序。同時能夠檢查使得無參外部函數可以接受任意類型參數的聲明不一致的漏洞,部分發現漏洞已被確認和修復。





審核編輯:劉清

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • smt
    smt
    +關注

    關注

    40

    文章

    2883

    瀏覽量

    69061
  • 機器學習
    +關注

    關注

    66

    文章

    8378

    瀏覽量

    132425
  • python
    +關注

    關注

    56

    文章

    4782

    瀏覽量

    84463

原文標題:技術干貨 | Python 的 C 外部函數的靜態類型推斷

文章出處:【微信號:編程語言Lab,微信公眾號:編程語言Lab】歡迎添加關注!文章轉載請注明出處。

收藏 人收藏

    評論

    相關推薦

    PythonC#對比

    Python可以實現類型之間的快速切換,而C#卻需要通過相應的類型轉換才能實現;5. Python無法做
    發表于 05-14 17:14

    Xilinx是否具有用于推斷RAM的算法類型和原語類型

    找到任何關于算法類型和基元類型的約束。如果有,我將使用更少的BRAM資源。Xilinx是否具有用于推斷RAM的算法類型和原語類型?我使用的是
    發表于 03-31 07:45

    python的數據類型有哪些?

    python的數據類型有哪些?Python定義函數一般格式是什么?
    發表于 12-27 06:26

    python靜態方法與類方法

    python靜態方法與類方法1. 寫法上的差異類的方法可以分為:靜態方法:有 staticmethod 裝飾的函數類方法:有 classmethod 裝飾的
    發表于 03-07 16:56

    函數及變量存貯類型

    如題,C語言編程中函數以及變量存儲類型的理解與知識總結
    發表于 03-14 16:11 ?0次下載

    Python函數使用基礎教程

    Functional Programming(函數式編程)的概念最早起源于LISP,由約翰·麥卡錫在1958年創立,最早提出了自動垃圾回收的理念,這一理念現在也被Python/Java/Ruby等
    發表于 11-15 19:05 ?1105次閱讀

    Python并不是弱類型語言

    Python是弱類型?Python并不是弱類型,Python是動態型強類型語言。
    的頭像 發表于 04-21 17:45 ?4849次閱讀
    <b class='flag-5'>Python</b>并不是弱<b class='flag-5'>類型</b>語言

    python靜態方法與類方法

    python靜態方法與類方法 1. 寫法上的差異 類的方法可以分為: 靜態方法:有 staticmethod 裝飾的函數 類方法:有 classmethod 裝飾的
    的頭像 發表于 03-07 16:56 ?1555次閱讀

    Python入門之什么是函數

    Python函數比我們想象的更為靈活。由于Python函數是對象,所以函數對象可以賦值給其他的名字、傳遞給其他
    的頭像 發表于 02-21 14:24 ?482次閱讀

    淺析python的變量類型

    python不需要事先聲明變量。 python的變量類型是在運行過程中自動決定的,不需要代碼聲明類型。
    的頭像 發表于 03-10 10:11 ?781次閱讀
    淺析<b class='flag-5'>python</b>的變量<b class='flag-5'>類型</b>

    為什么Python沒有main函數?

    今天的文章中,我們來討論一下為什么有的編程語言有main函數,而Python為什么沒有main函數。
    發表于 08-17 11:47 ?307次閱讀

    函數類型函數指針類型的區別

    平時用的更多的是函數指針類型,比如作為函數參數傳入回調函數等等。實際上函數類型也是可以作為
    發表于 10-24 14:27 ?352次閱讀

    python函數返回多個參數

    。 在Python中,可以使用關鍵字 return 來返回一個或多個值。在函數定義的末尾,可以使用 return 語句來指定函數要返回的值。這些返回的值可以是一個變量、一個常量、一個列表、一個元組或其他任意
    的頭像 發表于 11-21 16:37 ?1196次閱讀

    python中各種函數的用法

    Python中有很多種不同類型函數,它們在解決各種問題和完成不同任務時起著重要的作用。下面我將詳細介紹幾種常用的函數,包括內置函數、自定義
    的頭像 發表于 11-23 15:48 ?714次閱讀

    不屬于python的內置函數

    Python是一種高級編程語言,它提供了許多內置函數,可以幫助開發人員更輕松地處理各種任務。但是,在Python中并非所有的函數都是內置函數
    的頭像 發表于 11-29 14:27 ?1401次閱讀