用户定义类型的编译时边界检查的制作方法

文档序号:6435313阅读:231来源:国知局
专利名称:用户定义类型的编译时边界检查的制作方法
技术领域
本发明涉及计算机可读介质、过程及计算机系统,尤其涉及用户定义类型的编译时边界检查。
背景技术
可在计算机程序中进行边界检查以检测变量是否在某些指定边界内。例如,在用一个值作为到阵列中的索引之前,可以检查该值以确定该值是否位于该阵列的边界内,这种边界检查有时候被称为索引检查或范围检查。失败的边界检查可以生成运行时错误,比如异常信号。试图访问在其边界之外的阵列或其他变量经常指示编程错误。然而,不总是在每次使用有边界变量之前执行边界检查,因为边界检查增加程序执行时间。编译器有时候会自动消除被认为不必要的边界检查。作为示例,考虑从阵列内的一位置读取一值,然后将另一值(或同一值)存储回那个相同位置的程序代码。在没有任何优化的情况下,这个代码会包括从阵列读取阵列位置时的第一边界检查和写入阵列中的同一位置时的第二边界检查。但是编译器或其他优化工具在确定阵列大小没有被改变而且阵列中的同一位置正在被读取然后被写入之后,可能自动消除第二边界检查。更一般地,在编译器和其他工具中使用多种优化以自动减少或消除冗余的边界检查。

发明内容
对阵列访问的自动化的边界检查不总被使用,这是由于它对程序的性能有实际的 (或感觉得到的)影响。但是自动化的边界检查对于选择使用它来更安全地访问阵列的开发者来说仍然是方便可用的。然而,当开发者使用具有更复杂结构的定制数据类型时,或者以其他方式背离简单阵列时,针对非法访问的检查有时候涉及人工插入边界检查代码。不幸的是,当开发者显式编写边界检查时,该边界检查代码的目的对于编译器来说并非显见, 所以编译器缺乏允许它移除冗余的边界检查的知识。此处所描述的一些实施例提供用户定义类型的自动编译时边界检查,部分通过在源代码中标识用户定义类以安全访问显式分配的存储器。该用户定义类具有由开发者用用户定义边界检查注释(例如,向编译器指示该代码访问存储器映射的缓冲区或其他显式分配的存储器的注释)注释的存储器访问代码成员。该用户定义类还具有由开发者注释以向该编译器指示它提供用于生成对存储器访问代码的边界检查的边界信息的边界提供成员。 该边界提供成员可以是例如含有边界的整数字段,或者是在被调用时返回边界的方法。该用户定义边界的检查可在编程语言没有边界检查时提供边界检查,或者可以补充现有的边界检查,例如,通过包装内建阵列类型或垃圾收集器托管类型。边界检查可以扩展到阵列和布局由编译器控制的其他类型之外;该用户定义类可被定义为不使用任何多元素阵列成员作为组成类型。该用户定义边界检查注释的边界检查表示由该编译器插入中间语言代码中。然后优化自动减少否则会在该可执行代码中出现的重复的边界检查。该优化可被应用于该中间语言边界检查表示、应用于插入的边界检查代码或应用于两者。所给出的示例只是说明性的。本发明内容并不旨在标识所要求保护主题的关键特征或必要特征,也不旨在用于限制所要求保护主题的范围。相反地,提供本概述是为了以简化的形式介绍将在以下详细描述中进一步描述的一些概念。本发明由权利要求书限定,在本发明内容与权利要求书有冲突的情况下,应该以权利要求书为准。


将参考附图给出更具体的描述。这些附图只示出了选定的方面,且因此不完全确定覆盖或范围。图1是示出一计算机系统的框图,该计算机系统具有至少一个处理器、至少一个存储器(memory)、至少一个程序源代码、以及可存在于多个网络节点上的操作环境中的其他项目,并且还示出了配置的存储介质实施例;图2是示出在一示例体系结构中,任意复杂的用户定义数据类型的编译时边界检查的框图;以及图3是示出某一过程的步骤和配置的存储介质实施例的流程图。
具体实施例方式概览今天的托管代码系统(managed code system),像微软NET和Java 环境(分别是微软公司和Oracle美国公司的标记)中的那些,经常假定被托管的代码程序所使用的所有存储器(memory)被自动分配和管理。然而,在系统编程中,尤其是在设备驱动器编程中, 这种假定可能失败。在某些情况下,设备驱动器使用的存储器被物理设备在位置上固定,如当该设备上的缓冲区(buffer)被存储器映射到地址的特定集合中时。在其他情况下,显式管理存储器以避免数据的不必要复制或在特定时限内重用缓冲区。在一些语言(比如C#)中,当程序使用显式分配的存储器时,程序员将频繁使用指向非托管(unmanaged)数据结构的不安全的指针。在访问该指针或作指针算术时需要非常小心以避免错误。不安全的指针可能否定托管代码的正确度益处,因为不正确的指针算术可能导致存储器破坏或程序崩溃。在存在垃圾收集的情况下,这些错误特别难以调试。当程序员编写定制数据结构并希望执行边界检查以策安全时,他们可以将数据检查显式地编写为源代码行。编译器将这种代码的用途与其他代码行的用途相区分,在这种情况下,编译器缺少允许它移除显式边界检查的信息。本文描述的某些实施例允许托管代码安全且高效地访问显式分配的存储器。因此可高效地使用托管代码来进行系统编程。通过用如本文所述的编译时优化边界检查来使用托管代码进行系统编程,程序员可以用托管代码写出高效的设备驱动器并消除设备驱动器中的作为操作系统崩溃的常见诱因的常见错误。本文描述的某些实施例可以在更宽泛的环境中查看。例如,诸如存储器访问、变量边界、源代码注释、和编译之类的概念可与特定实施例相关。然而,可以使用宽泛的环境并不使得本文对抽象概念寻求专有权;它们并不是专有的。相反,本发明注重提供适当具体的实施例。例如,涉及存储器访问、边界检查、编译和/或源注释的其他介质、系统和方法在本范围之外。因此,在对本发明的正确理解下,也避免了模糊性和附带的证据问题。现在将参考诸如附图中所示出的那些示例性实施例,并使用特定语言来对其进行描述。但是,相关技术领域的且拥有本公开内容的技术人员将想到的对此处所示出的特征的更改和进一步的修改以及对本文所示的原理的其他应用,都应该被视为在权利要求的范围内。在本公开中阐明了各术语的含义,因此应该在仔细关注这些阐明的情况下阅读权利要求书。给出了具体示例,但是相关领域的技术人员将理解其他示例也可落在所使用的术语的含义范围内以及落在一个或多个权利要求的范围内。各术语不必在此具有与其一般使用中、特定行业的使用中、或特定字典或字典集的使用中相同的含义。附图标记可以与各种措词一起使用,以帮助示出术语的广度。从给定文本片段中省略附图标记不一定意味着没有通过文本讨论附图的内容。发明人声称并行使其对于其自己的词典编纂的权利。这里可在具体实施方式
中和/或在申请文件的别处显式地或隐式地定义术语。如本文所使用的,“计算机系统”可包括例如一个或多个服务器、主板、处理节点、 个人计算机(便携式或非便携式)、个人数字助理、蜂窝或移动电话、和/或提供至少部分地由指令控制的一个或多个处理器的其它设备。指令可以采取存储器中的软件和/或专门电路的形式。具体而言,虽然可发生许多实施例在工作站或膝上型计算机上运行,但是其他实施例也可以在其他计算设备上运行,并且任何一个或多个这样的设备都可以是给定实施例的一部分。“多线程化”计算机系统是支持多个执行线程的计算机系统。术语线程应被理解为包括能够或经历同步的任何代码,并且可用另一名称来称呼,如“任务”、“进程”或“协同例程”。线程可以并行地、按顺序、或以并行执行(例如,多处理)和顺序执行(例如,时间分片)的组合来运行。在各种配置中都已设计了多线程化环境。执行线程可以并行地运行, 或者线程可以被组织为并行执行,但是实际上轮流按顺序执行。例如,多线程化可以通过在多处理环境中在不同核上运行不同线程、通过对单个处理器核上的不同线程进行时间分片、或者通过时间分片和多处理器线程化的某种组合来实现。线程上下文切换可以例如由内核的线程调度器、由用户空间信号、或由用户空间和内核操作的组合来发起。线程可以轮流对共享数据进行操作,或者例如每一线程都可以对其自己的数据进行操作。“逻辑处理器”或“处理器”是单个独立的硬件线程处理单元。例如,每一个核运行两个线程的超线程化四核芯片具有8个逻辑处理器。处理器可以是通用的,或者它们可以针对诸如图形处理、信号处理、浮点算术处理、加密、I/O处理等特定用途来定制。“多处理器”计算机系统是具有多个逻辑处理器的计算机系统。多处理器环境存在于各种配置中。在一给定配置中,所有处理器可在功能上相同,而在另一配置中,由于具有不同的硬件能力、不同的软件分配或两者,某些处理器可与其他处理器不同。取决于配置, 处理器可在单个总线上紧密地彼此耦合,或它们可松散地耦合。在某些配置中,处理器共享一中央存储器,在某些配置中,它们各自具有自己的本地存储器,在某些配置中,存在共享和本地存储器两者。“内核”包括操作系统、系统管理程序、虚拟机、以及类似的硬件接口软件。“代码”指的是处理器指令、数据(包括常量、变量、以及数据结构)、或指令和数据两者。
“程序”在本文中被广泛地使用以包括应用、内核、驱动程序、中断处理程序、库、以及程序员(也被成为开发者)编写的其他代码。“自动地”指的是使用自动化(例如,由用于这里所讨论的特定操作的软件配置的通用计算硬件),与没有自动化相对。特别地,“自动地”执行的步骤不是由手在纸上或在人的头脑中执行的,它们是用机器执行的。然而,“自动地”不必意味着“立即地”。贯穿本文,对可选的复数的使用意味着存在一个或多个所指示的特征。例如, “(诸)注释”意味着“一个或多个注释”或等效于“至少一个注释”。贯穿本文,除非另外明确表明,否则对过程中的某一步骤的任何引用都假定该步骤可直接由所关注的一方执行和/或由该方通过介入机制和/或介入实体而间接地执行, 且仍然在该步骤的范围内。也就是说,除非直接执行是被明确表明的要求,否则并不要求由关注方对步骤的直接执行。例如,涉及由相关方执行的动作的步骤,如“传输”、“发送”、“传送”、“应用”、“插入”、“注释”、“标示”、“指定”或以其他方式确定目的地可能涉及居间动作, 如某个另一方执行的转发、复制、上传、下载、编码、解码、压缩、解压缩、加密、解密等等,仍被理解为直接由该相关方直接执行。在参考数据或指令时,要理解这些项目配置了计算机可读存储器,从而将其变换为特定物品,而非简单地存在于纸张上、人的头脑中、或作为例如线路上的瞬时信号。操作环境参考图1,用于一实施例的操作环境100可包括计算机系统102。计算机系统102 可以是多处理器计算机系统,或者也可以不是。操作环境可包括给定计算机系统中的一个或多个机器,它们可以是群集的、客户机-服务器联网的、和/或对等联网的。人类用户104可以通过使用显示器、键盘、及其他外围设备106与计算机系统102 进行交互。系统管理员、开发者、工程技术人员、以及最终用户各自都是特定类型的用户 104。代表一个或多个人操作的自动化代理也可以是用户104。在某些实施例中,存储设备和/或联网设备可以被认为是外围设备。图1中未示出的其他计算机系统可以与计算机系统102进行交互,或者例如通过网络接口设备使用到网络108的一个或多个连接与另一系统实施例进行交互。计算机系统102包括至少一个逻辑处理器110。计算机系统102与其他合适的系统一样,还包括一个或多个计算机可读非瞬态存储介质112。介质112可以是不同的物理类型。介质112可以是易失性存储器、非易失性存储器、被安装就位的介质、可移动介质、磁介质、光学介质、和/或其他类型的非瞬态介质(而不是诸如只传播信号的线路之类的瞬态介质)。具体而言,诸如⑶、DVD、记忆棒、或其他可移动非易失性存储器介质之类的配置的介质114在被插入或以其他方式安装时可以在功能上变为计算机系统的一部分,从而使其内容可被访问以供处理器110使用。可移动的配置的介质114是计算机可读存储介质112的示例。计算机可读存储介质112的某些其他示例包括内置RAM、R0M、硬盘、以及其他不能被用户104轻松地移走的存储设备。介质114用可由处理器110执行的指令116来配置;“可执行”在此以宽泛的意义被使用以包括机器代码、可解释代码、以及在例如虚拟机上运行的代码。介质114还被配置有数据118,该数据通过指令116的执行被创建、修改、引用和/或以别的方式使用。指令 116和数据118配置它们所驻留于的介质114 ;当该存储器是给定计算机系统的功能部分时,指令116和数据118还配置该计算机系统。在某些实施例中,数据118的一部分代表了诸如产品特征、清单、物理测量值、设定、图像、读数、目标、卷等等之类的现实世界的项。如本文讨论的,通过灵活的编译时优化边界检查来转换这样的数据,例如通过插入、应用、指定、注释、标示、绑定、部署、执行、修改、显示、创建、加载、和/或其它操作。程序120(例如,其具有源代码122、中间语言代码IM和可执行代码1 )、调试器、编译器和其他开发工具136、其他软件和附图中所示的其他项目可部分或全部驻留于一个或多个介质112内,由此配置那些介质。中间语言代码1 有时候被称为中间表示。例如,程序120可包括内建类型1 和垃圾收集器托管类型(gartage-collector-managed type)130。在许多开发配置中,阵列类型132可以是既内建又托管的。除了(诸)处理器 110之外,操作环境还可包括其他硬件,如显示器、存储器映射设备134、总线、电源和加速
,寸寸。给定操作环境100可包括向开发者提供一组协调的软件开发工具的集成开发环境(IDE) 138。具体而言,对于一些实施例,合适的操作环境中的一些包括或帮助创建被配置成支持程序开发的Microsoft Visual Studio 开发环境(微软公司的标记)。一些合适的操作环境包括Java 环境(Oracle America有限公司的标记),并且一些操作环境包括利用诸如C++或C#( “C-Sharp")等语言的环境,但本文的教导适用于各种各样的编程语言、 编程模型、以及程序。在图1中以轮廓形式示出了一个或多个项目来强调它们不一定是所示操作环境的一部分,但是可以与此处讨论的操作环境中的项目互操作。未采用轮廓形式的项目在任何附图或任何实施例中也不一定是必需的。系统图2示出了适合与一些实施例一起使用的架构。用户定义类型204(比如用户定义的类20 具有注释206,注释206用于以允许编译器不仅提供边界检查而且还移除冗余的边界检查的方式将开发者的边界检查意图传递给编译器224。例如,注释可标识用户定义类型的存储器访问208代码和边界提供210代码。例如,存储器访问代码可以是内联声明 (inline statement)和/或区别方法(distinct method)。边界提供代码可以是含有边界 218的字段212和/或在被调用时返回边界218的方法214。尽管阵列索引检查可能非常有用,然而本文的关注点在于其他类型的边界检查, 即,用于(不仅仅是阵列的)用户定义结构的边界检查。与常见的阵列类型132不同,类202 或其他用户定义类型204不是内建的,因此可具有不由编译器2M控制的数据布局216。在一些实施例中,编译器224响应于注释206而在中间语言代码124中插入边界检查表示220。边界检查代码222随后响应于边界检查表示220而被放入可执行代码1 中。边界检查表示220可以尊重针对中间语言代码124的常见的约定,而所生成的边界检查代码222可包括常见的条件跳转指令之类的。然而,为了呈现之目的,这些常见的约定和指令的上下文是用户定义类型,所述用户定义类型不仅仅是阵列,而且在一些实施例中甚至不使用阵列作为组成类型。在一些实施例中,优化器226向边界检查表示220、向边界检查代码222或向两者应用优化228,以除去否则会出现在可执行代码126中的冗余边界检查。优化器1 可集成于编译器2M中,或它可以是由编译器2M或由开发者调用的独立的工具,取决于具体实施例。与阵列边界检查一起使用的优化可被适合并改为应用于用户定义类型。在具有类型 204上的注释206的益处的情况下,例如,优化器可确定循环(loop)中对那个用户定义类型204的变量的所有的访问都在该变量的可允许存储器地址边界内,且因此该优化器可以除去否则会因为执行该循环而出现的多个边界检查。如图2中暗示的,用户定义类型和优化的编译时边界检查在作为程序120开发设备驱动器230代码时特别有用。托管的代码可用于系统编程,且设备驱动器可使用IDE 138 开发以在提供没有显式分配的存储器的垃圾收集的系统中执行。被显式分配且因此没有被垃圾收集的存储器,可以由开发者的代码管理而不牺牲边界检查且不会带来极为低效的边界检查。例如,类202可被定义为包括用于设备134的显式分配的、存储器映射的缓冲器 232,其具有一个或多个经注释的缓冲区访问208方法以读取/写入该缓冲区。该缓冲区的大小可动态确定,然后通过注释206和边界提供210机制(比如bufferBound字段212或 getBufferBoundO方法)提供给边界检查代码。参考图1和2,某些实施例提供了具有逻辑处理器110和存储器介质112的计算机系统102,该存储器介质由电路、固件和/或软件配置以在支持如此处所描述的优化的编译时边界检查的情况下转换代码122、124、126。存储器可与逻辑处理器进行可操作地通信。 驻留于存储器中的源代码122具有用户定义类型204。该用户定义类型具有存储器访问208 方法,该存储器访问208方法是经用户定义边界检查注释206注释的。该用户定义类型还具有至少一个边界指定符(bound specifier),比如边界提供210字段或方法。驻留于存储器中的编译器2M被配置成将用户定义边界检查注释的边界检查表示220插入到中间语言代码124中。驻留于存储器中的优化器2 被配置成向中间语言代码应用优化228以减少重复的边界检查。在一些实施例中,经注释的源代码包括设备驱动器230源代码122,而用户定义类型204对应于存储器映射的缓冲区232。存储器映射的缓冲区仅仅是一个示例,在一些实施例中,经注释的设备驱动器代码访问其他显式分配的存储器112。在一些实施例中,经注释的源代码包括垃圾收集数据类型130,而用户定义类型对应于显式分配的存储器。在一些实施例中,用户定义类型204具有不由编译器2M控制的数据布局216。在一些实施例中,用户定义类型204被定义为没有任何多元素阵列类型作为组成类型。在其他实施例中,用户定义类型204具有一个或多个阵列作为组成类型,但是比阵列更加复杂。在一些实施例中,用户定义类型204包装阵列类型并提供补充性的边界检查, 例如以检查阵列访问不仅位于被分配到该阵列的空间内,而且还位于保留更新后的元素的空间内,或例如在开发者想要保留指定的值集合的阵列的子部分内。在一些实施例中,边界指定器(又称边界提供210机制)包括以下中的至少一项指示用户定义数据类型204的字段212中含有用于存储器访问208方法的边界218的字段含边界注释(field-contains-bound annotation) 206 ;指示用户定义数据类型204 中的边界获取方法214返回用于存储器访问208方法的边界218的边界获取器方法注释 (bound-getter-method annotation)206。在一些实施例中,系统102包括驻留于存储器中的中间语言代码124,而代码IM 经用户定义边界检查注释206的边界检查表示220来注释。在一些实施例中,编译器224 被配置成不仅为用户定义边界检查注释206还为内建类型1 插入边界检查代码222。在一些实施例中,用户定义边界检查注释206指示补充性的边界检查,因为用户定义类型204 包装内建类型128,其中编译器2M被配置为已经边界检查了该内建类型128,而不管是否存在任何用户定义边界检查注释206。在某些实施例中,诸如人类用户I/O设备之类的外围设备106(屏幕、键盘、鼠标、 图形输入板、话筒、扬声器、运动传感器等等)将可操作地与一个或多个处理器110和存储器进行通信。然而,一实施例也可以深嵌入在系统中,以便没有人类用户104直接与该实施例进行交互。软件进程可以是用户104。在一些实施例中,该系统包括通过网络连接的多个计算机。网络接口设备可以使用例如诸如分组交换网络接口卡、无线收发机或电话网络接口之类的组件提供对网络108 的接入,并将存在于计算机系统中。然而,一实施例也可以通过直接存储器访问、可移动非易失性介质、或其他信息存储-检索和/或传输方法进行通信,或者,计算机系统中的一实施例可以在不与其他计算机系统进行通信的情况下操作。过程图3以流程图300示出了某些过程实施例。附图中所示的过程在一些实施例中可自动执行,例如在需要很少用户输入或不需要用户输入的脚本控制下由编译器2M和优化器226自动执行,或由从用户提供的说明书生成用户定义类型204的自动化源代码122生成器自动执行。除非另外指明,否则过程也可以部分自动地且部分手动地执行。在一给定实施例中,可以重复过程的零个或更多个所示出的步骤,有可能利用不同的参数或数据来操作。一实施例中的步骤也可以按照与图3中展示的自顶向下次序不同的次序来执行。步骤可以串行地、以部分重叠的方式、或完全并行地执行。遍历流程图300以指出在过程中执行的步骤的次序可以在过程的一次执行与该过程的另一次执行之间不同。流程图遍历次序也可以在一个过程实施例与另一过程实施例之间不同。各步骤还可以被省略、组合、重命名、 重组、或以其他方式偏离所示出的流程,只要所执行的过程是可操作的,并符合至少一个权利要求。此处提供了帮助示出该技术的各方面的示例,但是在本文内给出的示例并未描述所有可能的实施例。实施例不仅限于此处所提供的具体实现、排列、显示、特征、方法或情形。给定实施例可包括例如附加的或不同的特征、机制、和/或数据结构,并可以以别的方式偏离此处所提供的示例。在用户定义类型标识步骤302过程中,一实施例标识源代码中的用户定义类型 204。步骤302可以使用词法分析器(lexical analyzer)、解析器(parser)和/或(例如被适合为适于标识如本文所述的用户定义类型204的)其他机制完成。具体而言,用于识别常见的源代码注释的机制可被适合为通过关键词识别注释206。在边界检查表示插入步骤304过程中,一实施例在编译相应的经注释源代码的过程中将边界检查表示220插入到中间语言代码124中。步骤304可使用解析树、抽象句法树、属性、概况的信息向量(generalized dope vector)和/或(例如被适合为适于表示如本文所述的边界检查注释206的)的其他机制完成。在优化应用步骤306过程中,一实施例应用(诸)优化228以减少或消除冗余的边界检查。优化可应用于源代码、应用于中间代码、和/或应用于可执行代码,以减少否则可能在可执行代码中出现的重复的边界检查。步骤306可以通过经分析确定在执行过程中经受边界检查的存储器访问不能采取这样一个值而完成,其中该值会导致在被允许边界外的存储器访问。例如,如果已在代码中的第一点边界检查了一指针,且如果该边界和该指针值已在代码执行过程中随后的第二点不可能已改变,则在第二点不需要边界检查。作为另一示例,如果已在代码中的第一点边界检查了一指针,且如果在代码执行过程中随后的第二点、该指针值在给定方向的改变不可能比边界在那个方向的改变更多,则在第二点不需要边界检查。作为又一示例,如果存储器访问是代码执行过程中任何控制流均无法触及的, 则对那个存储器访问不需要边界检查。在边界检查代码插入步骤308过程中,一实施例在编译相应的经注释源代码的过程中将边界检查代码222插入可执行代码126。一些实施例使中间语言代码IM和可执行代码1 保持分开,例如,在分开的文件中,而其他实施例将中间语言代码IM和可执行代码1 混合。因此,可能发生如下情形步骤308将边界检查代码222作为中间语言代码 124插入在同一文件中或在同一工作存储器块中出现的可执行代码126中。步骤308可使用解析树、抽象语法树、指令选择、指令调度(instruction scheduling)、寄存器分配和/ 或(例如,被适合为适用于插入边界检查代码222的)其他机制完成。在边界检查补充步骤310过程中,一实施例补充已提供的边界检查,比如内建类型的边界检查,或例如简单阵列类型的边界检查。步骤310可通过定义例如具有边界检查组成类型的类型204或通过编译这样的类型完成。因此,如果该插入补充之前提供边界检查的话,补充步骤310可在边界检查表示插入步骤304过程中和/或边界检查代码插入步骤308过程中进行。补充步骤310也可由开发者执行,其中该开发者定义被注释为向之前指示的边界检查增加更多边界检查的类型204。在特定类型定义步骤312过程中,用户定义不具有阵列类型132的类型204,即,没有阵列类型作为组成类型的类型204。类202被认为是用户定义类型204的一个示例。单值变量(single-valued variable)(比如整数变量)不被认为是阵列的特例,为步骤312 之目的,阵列具有至少两个元素。步骤312所限定的类型中不存在阵列是为了强调与常见的针对阵列的边界检查相比,如本文所述的编译时边界检查的更好的灵活性。开发者可使用常见的源代码编辑工具和开发环境138来接收在步骤312过程中定义的类型204。在源代码获得步骤314过程中,开发者或代表开发者行动的实施例获得源代码 122。步骤314可以使用文件系统、网络、IDE 138、和/或其他常见机制来完成。在类型指定步骤316过程中,开发者或代表开发者行动的实施例在源代码122中指定用户定义类型204(例如,其可以是用户定义类20 。在步骤316过程中,开发者可使用常见的源代码编辑工具和开发环境138来指定类型204。在特定实施例中,步骤316可包括定义步骤312和/或补充步骤310。在方法定位步骤318过程中,开发者或代表开发者行动的实施例定位由用户定义类型204定义(例如,指定316)的存储器访问方法320。这样的方法320 —般而言是存储器访问代码的示例。步骤318可以使用常见的源代码编辑工具和开发环境138完成,具体而言是通过该源代码编辑工具和开发环境138的关键词搜索能力完成。在(诸)注释步骤322过程中,开发者或代表开发者行动的实施例注释源代码以向编译器2M提供针对为保留用户定义类型204的对象或其他变量而将被显式分配的存储器的边界检查信息。例如,存储器访问代码可以用用户定义边界检查3M注释206来注释,其指示该代码访问(或可能访问)被显式分配或以其他方式经历在没有注释206的情况下由语言环境所提供的任何检查之外的边界检查的存储器。访问(或可能访问)被显式分配的存储器的代码可由访问显式分配存储器3 注释206向编译器2M标识。可置入边界提供210注释206以注释322指示边界的机制,比如通过用字段含边界3 注释206来注释 322字段212,或通过用边界获取器方法330注释206来注释322方法214。在包装步骤332过程中,开发者或代表开发者行动的实施例在用户定义类型204 中包装现有类型。即,用户定义(指定)具有现有类型作为组成类型的类型204,从而补充 310现有类型的边界检查(如果它原来有任何边界检查的话)。步骤332可使用常见的源代码编辑工具和开发环境138完成。在第二边界标示步骤334中,开发者或代表开发者行动的实施例为用户定义类型 204标示第二边界218条件。即,用户通过指定不同的边界来补充310该用户定义类型的边界检查。例如,一类型可能具有反映总的分配的存储器的第一边界,还具有反映分配的存储器的实际使用的第二边界,例如,被标记为“废弃”的记录可以被认为是在边界之外的,即便它们驻留于为保留记录而被分配的存储器中。步骤334可使用常见的源代码编辑工具和环境138完成。在重复的边界检查减少步骤336过程中,一实施例减少重复的边界检查,例如,通过应用306(诸)优化228,其成功地定位并移除至少一个冗余的边界检查。在编译步骤338过程中,一实施例编译经注释332的源代码。步骤338可以使用被适合为适于提供如本文所述的用户定义类型的编译时优化边界检查的常见的编译工具和技术实现。在存储器配置步骤340过程中,存储器介质112被用户定义类型204、优化编译器 224,226配置和/或以其他方式与如本文讨论的用户定义类型的编译时优化边界检查相联系。下面将联系各实施例更加详细地讨论前面的步骤和它们的相互关系。一些实施例提供用于用户定义类型的编译时边界检查的过程。该过程包括从源代码到中间语言代码到可执行代码的程序编译过程中执行的步骤。用户定义类202或源代码中的其他类型被标识302。例如,该类可以是用于安全地访问显式分配的存储器的。在一些实施例中,该用户定义的类被定义312为没有任何多元素阵列类型作为组成类型。用户定义的类可具有由用户定义边界检查3M注释206来注释322的存储器访问208代码作为经注释成员。类202也可具有边界提供210成员作为经注释成员,其中该边界提供210成员被注释322为指示它提供用于生成存储器访问代码上的边界检查的边界218信息。响应于经注释类型204,用户定义边界检查注释的边界检查表示220被插入304到中间语言代码中, 且在一些情况下,优化2 被应用306,以试图减少否则会在可执行代码中出现的重复的边界检查。在一些实施例中,该过程包括响应于边界检查表示而将边界检查代码插入308到中间语言代码中,且该应用步骤向插入的边界检查代码应用306该优化而不是向(诸)边界检查表示应用该优化。在一些实施例中,该应用步骤向(诸)边界检查表示220应用306 该优化而不是向边界检查代码222应用该优化。在一些实施例中,该标识步骤标识302已经用访问显式分配存储器3 注释来注释的存储器访问方法320。在一些实施例中,该标识步骤标识302指示用户定义边界检查的注释206,其中该用户定义边界检查用于补充310内建类型128的系统定义的边界检查。 在一些实施例中,该标识步骤标识302指示用户定义边界检查的注释206,其中该用户定义边界检查用于补充310垃圾收集器托管类型130的系统定义的边界检查。一些实施例提供使程序开发者管理用户定义类型(即,非内建的类型)的编译时边界检查的过程。该过程包括获得314计算机程序的源代码,并指定316该源代码中的用户定义数据类型(例如,通过编写类型204或通过接受一个之前写好的类型)。该过程还包括定位318由用户定义数据类型定义的存储器访问方法320,并例如用访问显式分配存储器3 或其他注释206来注释322该存储器访问方法。此外,该过程包括用以下中至少一项注释322源代码指示用户定义数据类型204定义的字段212中含有用于存储器访问方法的边界218值的字段含边界3 注释206,指示用户定义数据类型定义的边界获取方法 214返回用于存储器访问方法的边界218值的边界获取器方法330注释206。在一些实施例中,开发者用标示334用于存储器访问方法的第二边界的注释206 来注释322该源代码。在一些实施例中,用户定义类型204包装332内建阵列类型128、132。 在一些实施例中,用户定义类型204包装332内建托管类型128、130。配置的介质一些实施例包括配置的计算机可读存储介质112。介质112可包括盘(磁盘、光盘或其他)、RAM、EEPROM或其他ROM、和/或其他可配置存储器,具体包括非瞬态计算机可读介质(与电线和其他传播信号介质相对)。被配置的存储介质可以特别地是诸如CD、DVD或闪存之类的可移动存储介质114。可以是可移动或不可移动并且可以是易失性或非易失性的通用存储器可被配置成使用数据118和指令116形式的从可移动介质114和/或诸如网络连接等另一源读取的诸如用户定义类型204(包括它们的注释206)和/或优化器226(其适于处理注释206)等项目来形成配置的介质的实施例。配置的介质112能够使计算机系统执行用于通过此处讨论的注释和编译时的优化的灵活的边界检查来转换源代码和其他代码的过程步骤。如此,图1到3帮助示出了配置的存储介质实施例和过程实施例,以及系统和过程实施例。具体而言,图3中所示出的,或此处以其他方式教导的进程步骤中的任一个可以被用来帮助配置存储介质以形成已配置的介质实施例。附加示例下面提供了额外的细节和设计考虑。如同此处的其他示例,在给定实施例中,所描述的特征可以单独地使用和/或组合地使用,或根本不使用。本领域的技术人员将理解,实现细节可以涉及诸如特定API和特定样本程序之类的特定代码,且因此不必出现在每个实施例中。本领域的技术人员还将理解,在讨论细节时所使用的程序标识符和某些其他术语是针对具体实现的,且如此不必涉及每个实施例。尽管如此,虽然它们不一定需要出现在这里,但是提供了这些细节,因为它们通过提供上下文可以帮助一些读者,和/或可以示出此处所讨论的技术的许多可能的实现中的一些。此处描述的一些实施例提供如下方面。第一,一种允许程序员定义用于以安全方式访问显式分配的存储器的数据类型 204(例如,类20 的方式。程序员可以使用注释206的集合,其中该程序员将该注释206 集合放到该数据类型定义的方法上。一种注释206指示经注释的方法访问显式分配的存储器并应受边界检查的保护。另一种注释206指示该数据类型中的字段212含有该存储器访问上的边界218。第三种注释206指示该数据类型中的方法214返回该存储器访问上的边界。第二种或第三种注释中的任一种(或这两种)可与第一种注释的特定实例一起使用。第二,编译器2 在其中间表示中(即,在中间语言代码中)表示这些注释206。第三,基于该中间表示中的注释,编译器2M在对访问显式分配的存储器的方法 320的调用之前插入边界检查。第四,在插入304/308边界检查后,编译器进行优化,该优化减少336 (并因此有可能消除)不必要的边界检查。通过扩展阵列的优化以理解经注释的中间表示并消除在对访问显式分配的存储器(其比仅仅是阵列更加复杂)的方法的调用之前插入的边界检查,这些优化2 可以从该文献中常见的优化中适合而在本上下文中使用。编译器2M或其他优化器2 标识与含有边界访问的字段或返回边界访问的方法的比较、以及在函数调用之前插入的检查,然后象征性地(symbolically)确定(经由从用于消除阵列上的不必要的边界检查的常见技术改编而来的方案)该检查是否可被安全地消除。用这种方式,程序员可以以相对安全的方式使用显式分配的存储器。这允许高效、 安全地访问托管代码中的显式分配的存储器。在一些实施例中,编译器2M提供可被应用于任何数据结构(包括并非仅仅是阵列的数据结构)的属性的集合。用这种方式,这些实施例概况(generalize) 了对边界检查的早先工作并增加了对边界检查的早先工作的灵活性,例如,该早先工作专用于作为内建语言类型的阵列。这些实施例允许程序员将边界检查应用于由程序员定义的替代数据结构,具体说来,允许在编译器和语言系统不控制数据布局216或数据布局216是随意的的情况下进行边界检查。一些实施例从“信息向量”(其是在编程语言中的阵列的实现中使用的)这一思想中得到了灵感,并在实现一实施例的过程中改造了这一概念,在该实施例中,程序员可以定义边界检查的数据结构,而不是该数据结构由语言实现来限定。常见的信息向量含有指向含有阵列元素、阵列边界以及可能地其他信息的存储器块的指针。一些实施例与或可以与对阵列边界检查的常见工作集成,以便使得程序在常见的阵列边界检查和在本文所教导的灵活的用户定义类型204边界检查两方面都更高效。在一些实施例中,注释206描述了必要时可在运行时实施的正确度检查,其不是库操作的高级语义属性。优化器式图消除不必要的检查。相对而言,在其他工作中,优化器使用注释来描述库的语义属性并优化该库的使用,而不是如此处所述的减少336不必要的安全检查。一些实施例具有含有Microsoft 通用语言运行时(CLR)的操作环境100,CRL 是一个相对较大的运行时,包括诸如即时(JIT)编译、垃圾收集(GC)、运行时反映、以及更多的服务和特征。一些实施例具有密切遵循具有传统编译(现在有时候称为提前 (ahead-of-time)编译)的C模型的环境100,尽管可以为了类型安全的目的而提供一些 GC。在一些实施例中,C#允许人们注释返回值,从而可以用于允许方法的属性[return SomeAttribute]int SomeMethod () {. . . }
在一些实施例中,编译器生成的和编译器移除的边界检查可用于但不限于具有非托管资源的索引池(典型地是存储器(memory))的数据结构。程序员可以注释他们的数据结构从而编译器2M将生成边界检查,所述边界检查在运行时以类似于阵列边界检查的方式行动(例如,通过在违反边界时抛出异常),并且可以由从常见的按需阵列边界检查 (Array Bounds Checks on Demand,ABCD)方案或从常见的代替ABCD优化改编而来的方案移除。在一些实施例中,提供三个定制属性。BoundsChecking属性被应用于经注释的方法320。响应之,编译器2M将在对被标记为BoundsChecking的方法的调用点插入边界检查。在一个实施例中,编译器2M需要 BoundsChecking方法具有至少一个自变量(argument),并需要第一个自变量是Int32型的。该边界检查将检查第一个自变量在零和标记为Boimd(IT)的字段之间。在此实施例中,所有具有BoundsChecking方法的类型具有正好一个标记为Bound的Int32字段。由于增加BoundsChecking而移除安全性检查应当被认为是重大的改变。Bound属性被应用于经注释的字段212。在一个实施例中,该字段是ht32并由同一类型204中的BoundsChecking方法所生成的边界检查使用。BoundsGetter属性被应用于经注释的方法214。在一个实施例中,如果返回Bound 的方法不是内联的(inlined),它就可以被标记为BoimdGetter,且对它的调用将被作为访问该Bound来对待。在一些实施例中,编译器2 将检查上面所描述的要求,但是确保Bound只应用于有意义的字段且BoimdGetter只应用于返回该Boimd(或比该Bound小的值)的(诸)方法却是程序员的责任。在一个具有用改编自基于阵列的优化以除去边界检查的实施例中, 如果Bound字段被改变则该检查可能被不安全地移除。一些实施例采用基于类型的方案并预计用户将编写形状与阵列类似的类型,并请求用户向编译器描述那个形状(哪里是长度,哪里是访问器)。但是并不是在每个实施例中都要求结构化的类型。在一些实施例中,所涉及的特性包括以下事实,即用户代码中的位置需要对某个用户变量的检查,且指导编译器如何构造那个检查。作为一种实现选择,一些实施例改变可用检查的形状以看上去像阵列检查
放到方法320上。一些实施例还将它们直接放
在其他存储器访问208代码的源中,比如在如下实施例中
void Foo(int i) { byte* ρ =... (or [BoundsChecking(i)] ...*(p + i)...
}在实践中,这种注释可能被某些源语言标准排除。它在微软中间语言(MSIL)中没有被遵循,但是可能在其他语言中被遵循(MSIL是ECMA 335标准的一种实现)。一些实施例不被约束为保护显式分配的存储器。例如,人们可能这样封装332托管阵列
class List { int[] arr 二 new int[20]; [Bound] int count = 0;
void Add(int i) {
arr[count]= i; count = count + 1 ; void Get(int i) { return arr[i];在此示例中,该语言提供对arr的现有阵列边界检查,但是开发者还想要更强的补充性检查,以确保不仅i小于20,而且它小于已被添加到列表中的项的数量。两个检查都可以是通过阵列边界检查和/或其他优化来移除的候选。排除为了进一步描绘并澄清此处描述的灵活的编译时边界检查和现有方案之间的不同,提供常见阵列边界检查的以下讨论。本讨论中描述的概念和技术在实践中可以与本文教导的实施例兼容,因为两者均不阻止另一个的使用,但是它们在此处所寻求保护的实施例的范围之外。在边界属性收敛的上下文中,一种方案注记可能出现在字段、参数、或指针或C# 阵列或阵列类型的返回值上的属性[StaticBound (η)],其中η是某个文字型整数。[BoundedBy (ident)],其中ident是标识符,该标识符是作为同一直接包含结构的成员的某个整数类型的其他字段,或同一过程/方法的某个其他形式参数,或在返回值的情况下,它实际上附加于该方法上。如果允许该自变量被定义为{string! int},那么这些可以折叠为单一属性,我们可以不需要第二个属性名称。如果携带这些属性的字段/参数是指针,那么该属性的存在携带如下合约通过该指针的索引操作必须被检查。从社会角度看,可以注意到,兼容地后退比采用其他方案更加容易。可能出现在充当索引(其必须是某种整数类型的)的参数、字段、或返回值上的属性[Range (begin, end)],其中begin和end可以是文字型整数或者是被编码为串 (string)的标识符,而正常的预期是begin将是文字型常量零。当此属性出现在形式参数上时,它指示调用者必须范围检查/卸下(discharge) 实际的参数。当此属性出现在字段上时,它指示赋值(assignment)或初始化 (initialization)的RHS必须被范围检查或等同地卸下。当此属性出现在方法(method)上时,它指示在返回前该方法必须检查/卸下的返回值的要求。至于版本泡沫,通过打碎并包装,如果需要的话使其与不在意调用者(oblivious caller)向下兼容是有可能的。范围可被更精确地捕捉为[Range(inclusiveBase, ExclusiveBound)]至于独占边界(exclusive bound)的优点和缺点,一个不足是不能够为int编码 (例如)MAXINT。替代的[Range (inclusiveBase,InclusiveBound)]将几乎不变地转化为形式[Range (0,boundldent-l)]的用例,这看上去很奇怪,并且在属性中带来了表达的问题。在最大可表示值必须包括在如下范围中的情况下,使用不同的属性可以直接解决那个问题[AtLeast (IowerBound)]或[GreaterThanOrEqualTo(IowerBound)]留给底层参数类型来利用如下事实隐式地指定上边界每个C#类型因为其类型的关系而固有地具有范围边界。BoundedBy 禾口 Range 属性可以分离(decoupled)。一属性可被指定为在给定参数位置采用“要么整数要么串”。人们可以为定制属性编写多个构造器(constructor),这样人们可以在同一位置采用不同的类型,或者使用命名参数(named parameter)来实现。这总结了对排除的讨论。结语虽然具体实施例在此处被明确示出并描述为进程、已配置的介质、或系统,但是可以理解,对一种类型的实施例的讨论也一般性地延伸到其他实施例类型。例如,结合图3对过程的描述也帮助描述已配置的介质,并帮助描述类似于结合其他附图所讨论的那些的系统和产品的操作。对一个实施例的限制也不一定适用于另一个实施例。具体而言,进程不一定仅限于在讨论诸如已配置的存储器之类的系统或产品时呈现的数据结构和方案。不是图中所示出的每一项都需要存在于每个实施例中。相反,实施例可以包含图中未显式地示出的项。虽然一些可能性在此处通过具体示例在文本和附图中示出,但是各实施例可以偏离这些示例。例如,一示例的具体特征可以被省略、重命名、以不同的方式分组、重复、不同地以硬件和/或软件实例化,或是在两个或更多示例中出现的特征的混合。 在某些实施例中,在一个位置处示出的功能也可以在不同的位置处提供。通过附图标记参考了附图。在附图或文本中与给定附图标记相关联的措词中的任何显而易见的不一致性应该被理解为仅仅时拓宽该标记所引用的内容的范围。如此处所使用的,诸如“一”和“该”等术语包括了所指示的项目或步骤中的一个或多个。具体而言,在权利要求书中,对一个项的引用一般表示至少一个这样的项存在,并且对一个步骤的引用表示执行该步骤的至少一个实例。标题是仅出于方便起见的;关于给定话题的信息可在其标题指示该话题的节之外找到。所提出的所有权利要求都是本说明书的一部分。尽管在附图中示出并在上文中描述了示例性实施例,但本领域普通技术人员将明白,可作出多种修改而不脱离权利要求书中阐明的原理和概念,且这些修改不需要涵盖整个抽象概念。尽管用结构特征和/或过程动作专用的语言描述了本主题,但可以理解,所附权利要求书中定义的主题不必限于权利要求书上面所描述的具体特征或动作。不一定在给定定义或示例中标识的每一个手段或方面都在每个实施例中存在或使用。相反,所描述的具体特征和动作是作为供当实现权利要求书时考虑的示例来公开的。无法包围整个抽象概念但落入权利要求的等效技术方案的意义和范围内的所有改变都在法律所准许的最大程度内被包含在其范围内。
权利要求
1.一种配置有数据且配置有指令的计算机可读非瞬态存储介质,其中所述指令在由至少一个处理器执行时使得所述处理器执行用于用户定义类型的编译时边界检查的过程,所述过程包括在程序从源代码经历中间语言代码到可执行代码的编译过程中执行的如下步骤在源代码中标识(30 用于安全地访问存储器的用户定义类,即,具有存储器访问代码作为经注释成员并具有边界提供成员作为另一经注释成员的用户定义类,其中所述存储器访问代码经用户定义边界检查注释来注释,而所述边界提供成员被注释为指示它提供用于生成所述存储器访问代码上的边界检查的边界信息;在所述中间语言代码中插入(304)所述用户定义边界检查注释的边界检查表示;以及应用(306)优化,其中所述优化减少否则会在所述可执行代码中出现的重复的边界检查。
2.根据权利要求1所述的配置的介质,其特征在于,还包括响应于边界检查表示而将边界检查代码插入(308)到所述中间语言代码中,且所述应用步骤向插入的边界检查代码应用所述优化而不是向边界检查表示应用所述优化。
3.根据权利要求1所述的配置的介质,其特征在于,所述应用步骤向边界检查表示 (220)应用(306)所述优化而不是向所述中间语言代码中的边界检查代码(22 应用所述优化。
4.根据权利要求1所述的配置的介质,其特征在于,所述标识步骤标识(30 经访问显式分配存储器注释来注释的存储器访问(208)方法。
5.根据权利要求1所述的配置的介质,其特征在于,所述标识步骤标识(30 指示用户定义边界检查的注释以补充(310)内建类型(128)的系统定义的边界检查。
6.根据权利要求1所述的配置的介质,其特征在于,所述标识步骤标识(30 指示用户定义边界检查的注释以补充(310)垃圾收集器托管类型(130)的系统定义的边界检查。
7.一种用于程序开发者管理用户定义类型的编译时边界检查的过程,所述用户定义类型即非内建的类型,所述过程包括如下步骤获得(314)计算机程序的源代码;在所述源代码中指定(316)用户定义的数据类型;定位(318)由所述用户定义的数据类型定义的存储器访问方法;用访问显式分配存储器(326)注释来注释(32 所述存储器访问方法;以及用以下中的至少一项注释(32 所述源代码指示由所述用户定义的数据类型定义的字段含有用于所述存储器访问方法的边界的字段含边界(328)注释,指示由所述用户定义的数据类型定义的边界获取方法返回用于所述存储器访问方法的边界的边界获取器方法(330)注释。
8.根据权利要求7所述的过程,其特征在于,所述用户定义类型包装(33 以下中的至少一项内建阵列类型,内建托管类型。
9.一种计算机系统,包括 逻辑处理器(110);与所述逻辑处理器可操作地通信的存储器(112);驻留于所述存储器中并具有用户定义类型的源代码(122),所述用户定义类型具有存储器访问方法,所述存储器访问方法是经用户定义边界检查注释来注释的,所述用户定义类型还具有至少一个边界指定器;驻留于所述存储器中并被配置成在中间语言代码中插入所述用户定义边界检查注释的边界检查表的编译器0 );以及驻留于所述存储器中并被配置成向中间语言代码应用优化以减少重复的边界检查的优化器(226)。
10.根据权利要求9所述的系统,其特征在于,经注释的源代码包括设备驱动器(230) 源代码122,而所述用户定义类型对应于存储器映射的缓冲区032)。
11.根据权利要求9所述的系统,其特征在于,经注释的源代码包括垃圾收集数据类型 (130),而所述用户定义类型对应于显式分配的存储器。
12.根据权利要求9所述的系统,其特征在于,所述用户定义类型被定义为没有任何多元素阵列类型(132)作为组成类型。
13.根据权利要求9所述的系统,其特征在于,所述边界指定器包括以下中的至少一项指示所述用户定义的数据类型中的字段含有用于所述存储器访问方法的边界的字段含边界(328)注释(206);指示所述用户定义的数据类型中的边界获取方法返回用于所述存储器访问方法的边界的边界获取器方法(330)注释006)。
14.根据权利要求9所述的系统,其特征在于,还包括驻留于所述存储器中并且经所述用户定义边界检查注释O06)的边界检查表示(220)来注释的中间语言代码(IM)。
15.根据权利要求9所述的系统,其特征在于,所述用户定义类型具有不由所述编译器控制的数据布局016)。
全文摘要
本发明涉及用户定义类型的编译时边界检查。提供用户定义类型的编译时优化的边界检查。用户定义类具有注释的存储器访问方法,以及注释的边界提供成员,比如含有边界或在调用时返回边界的方法的整数字段。该用户定义边界的检查可在编程语言没有边界检查时提供边界检查,或者可以补充现有的边界检查,例如,通过包装内建阵列类型或垃圾收集器托管类型。边界检查可以扩展到阵列和布局被编译器控制的其他类型之外,从而允许在托管代码环境中的高效系统编程。边界检查表示由编译器插入到中间语言代码中。然后优化减少重复的边界检查。
文档编号G06F9/45GK102402451SQ20111030686
公开日2012年4月4日 申请日期2011年9月26日 优先权日2010年9月28日
发明者D·S·哈维 申请人:微软公司
网友询问留言 已有0条留言
  • 还没有人留言评论。精彩留言会获得点赞!
1