APP下载

开源软件开发中的代码评审

2020-04-11赵海燕陈庆奎

小型微型计算机系统 2020年4期
关键词:开发者开源代码

赵海燕,李 敏,陈庆奎,曹 健

1(上海市现代光学系统重点实验室,光学仪器与系统教育部工程研究中心,上海理工大学光电信息与计算机工程学院,上海 200093)2(上海交通大学 计算机科学与技术系,上海 200030)

1 引 言

软件代码评审作为一项软件开发中的有效方法已有35年的历史[1,2].随着软件工程的发展,代码评审已经被各个软件开发组织广泛采用,并被证明是软件开发项目中质量保证的有效手段,成为开源和专有软件项目中提倡的最佳实践[3,4].

在代码评审中,要求由独立的开发人员检查新的代码或者代码的更改,以在代码集成到系统之前能够识别和修复代码中的缺陷.虽然传统的代码评审已经被证明可以提高软件产品的整体质量[5-7],然而传统代码评审通常需要举行面对面的会议,有时一位评审员评审之后还需交给下一位评审员评审,整个流程严格按照规定的制度执行,导致整个过程比较繁琐、费时,一定程度上也阻碍了它在实践中的应用[8,9].

现代代码评审[10,11]作为一种非正式的、轻量级的(不需要面对面正式会议及文件收集工作)、基于工具的代码评审方法,已经在专有软件和开源软件的开发中广泛使用.相对于正式代码评审,轻量级代码评审所耗费的各种成本明显下降,如果正确实施,它可以起到更加积极的效果[6,11].

开源软件开发依赖于开发者的志愿参与,因此更适合采用轻量级代码评审.例如,GitHub提供了一个在线审查机制,以确保来自贡献者的代码质量,该审查机制是GitHub基于Pull软件开发模型[12-14]的重要组成部分:项目的贡献者可以通过添加新特性或修复某些bug对源代码进行改进,改进的代码不能直接提交到项目存储库,而是以Pull 请求的形式提交给社区,此请求将受到整个社区的审查.每个用户都可以是一个潜在的评审员,他根据代码风格、新特性的必要性、代码逻辑的正确性等对请求进行评估.最后,集成人员会综合考虑所有评审者的意见做出决定.

在评审过程中,评审者必须综合考虑各方面的因素对代码进行严格的审查.对评审者来说,此过程需要充分去理解代码并对此作出评论,所以代码评审是一个耗费脑力乃至体力的工作,也是一个知识密集型的工作.因此,找到合适的评审人员对最终的评审效果具有极大的意义[5,15].然而,在数百名潜在的评审员中筛选合适的评审人员已成为现代代码评审的一项挑战.特别是在开源项目中找到积极、有能力还有相关背景的评审者更为困难.

由于代码评审的价值,它是目前软件工程领域的一个重要研究主题,在许多国际会议,例如ICSE、ICSME、SANER、ICSM、SIGSOFT上都有论文对此进行研讨.一方面,研究者们探讨了影响开源代码评审质量的各种因素,另一方面,如何提高开源代码评审质量特别是推荐合适的评审者也成为了重要的研究内容.本论文对此领域的研究进行了系统的总结,并对未来的研究方向进行了讨论.

2 影响代码评审的因素

一方面,代码评审作为开源软件开发过程的一个重要部分,其目的是客观、及时、独立地评估代码的质量.另一方面,对于贡献者而言,补丁代码能否被接受、代码是不是在最短时间内被接受等都是非常关心的.

原则上,代码评审是一个透明的过程,评审者进行独立的评估,然而在实践中,该过程受诸多因素的影响.因此,近年来,研究者人员基于对大型开源项目的实际数据进行了实证研究.本文对近年来研究者们分析的各种影响因素进行了总结,并分为与开发者相关的因素、与评审者相关的因素、与项目相关的因素,如表1所示.

表1 代码评审的影响因素
Table 1 Influencing factors of code review

因素类别影响因素影 响参考文献请求提交次数提交次数越少请求被接受几率越大,对代码评审结果有正面影响[16]补丁的大小提交较小的补丁,分析复杂度低,对代码评审过程有正面影响[12,16,17][18,20,21][22]是否首次提交Pull请求初次提交请求被拒绝的几率大,对代码评审结果有负面影响[16,18]与开发者相关的因素开发者身份开发者是核心团队成员,对评审结果有正面影响[16,17,18][22,23]开发者与评审者之间的关系关系密切,对代码评审速度有正面影响[16,22,23]Pull请求的位置对某目录文件操作次数多,该目录下文件被接受的几率增大,对代码评审结果有正面影响[12,16]开发者的经验经验丰富的开发者对代码评审结果有正面影响[14,17,19]与评审员相关的因素评审人的活跃度活跃的评审员对代码评审响应时间有正面影响[17]评审队列长度评审员需要处理评审的任务队列越短,对代码评审速度有正面影响[17]项目涉及的编程语言某些编程语言如C、TypeScript等对代码评审有正面影响[14,18]文件优先级项目中的文件等级越高对代码评审结果有正面影响[17,20]与项目属性相关的因素软件组件不同的软件组件对代码评审时间有一定的影响[17]组织Pull请求所属的组织机构对代码评审速度有一定的影响[17]应用领域项目所属领域对代码评审有一定影响[14]项目的热度项目的热度会对代码评审有一定的影响[12,14]

2.1 与开发者相关的因素

在开源软件开发过程中,开发者通过提交Pull 请求为项目做出贡献.一些研究者使用各种算法包括关联规则挖掘算法对代码评审影响因素进行了梳理,揭示了代码评审过程受多方面因素的影响[12-23].与开发者相关的因素如下:

2.1.1 请求提交次数因素

Pull请求提交次数会影响评审[16].开发者提交请求次数变多,导致分析的复杂度增加,会增加评审员的工作量,所以请求被接受的几率变小.

2.1.2 补丁的大小因素

开发者提交更小的补丁对代码评审有正面影响[12,16-18,20-22].较小的补丁只需要经过几轮修订,而较大的补丁需要做更多的工作才能成功的并入项目.当开发者对项目中的文件进行添加处理,会增加分析的复杂性,降低Pull请求接受的几率;反之,删除文件,会使得分析的复杂性降低,增大其接受的几率.

2.1.3 是否首次提交Pull请求

开发者对Pull请求有过历史提交记录,导致各方面修改比较完善,则在提交新Pull请求时,该请求被接受的几率增大[16,18].

2.1.4 开发者的身份

开发者身份对代码评审过程有的一定影响[16-18,22,23].通过开发者的概要信息,可知开发者是否为项目核心团队成员.如果是核心团队成员,其提交的请求被接受的几率增大.

2.1.5 开发者与评审者之间的关系

在实际项目中,开发者和评审者之间可能具有追随关系,而这种关系会对代码评审带来正面影响[16,22,23].在这种关系下,当开发者提交Pull请求时,评审者愿意评审该请求的几率会增大.其次,当开发者与评审者之间拥有共同的专业技能和兴趣时,评审者会对该请求更感兴趣而使评审意愿增大.最后,当开发者与评审者社会关系密切时,评审者也会更倾向于评审此请求.

2.1.6 Pull请求的位置

不同的开发者可能会修改不同的文件,开发者可能对项目下的某些文件操作多于其他文件.研究发现,Pull请求的位置会影响代码评审过程[12,16].从评审人分配角度,某些开发者在近期时间内,反复对某些文件进行修改,从而增加了该开发者成为这类文件的评审者的机会.从评审机会的角度,软件项目中同一目录下的文件具有一定的相似性,因此当Pull请求对应的文件属于相同目录时,需要的评审者是类似的.同样,目录的不同导致合适的评审者有差别.

2.1.7 开发者的经验

开发者经验在代码评审中扮演重要角色.由经验丰富的活跃开发者提出的请求,修改的内容较完善,评审时间会更短,导致接受机会增加.

2.2 与评审员相关的因素

研究者通过对项目的实证研究,发现评审人的某些因素对代码评审有影响:

2.2.1 评审人的活跃度

评审人员的活跃度对项目评审的时间有一定影响[17].较为活跃的评审人员更愿意花更多的时间和精力去参与评审,导致评审响应时间更短.

2.2.2 评审任务队列长度

评审人需要审核的请求队列的大小与评审响应时间存在直接联系,待评审队列越短,评审员的工作量越少,则相应的评审员的评审速度越快[17].

2.3 与项目相关的因素

开发者及评审者的工作都在某项目下进行,因此项目的某些属性对代码评审有着重要影响.近年来研究者们从若干方面进行分析,总结出相关因素如下:

2.3.1 项目涉及的编程语言

拉赫曼的研究表明在某些编程语言中,请求被接受的几率更大[14].实验证明,使用Java、CSS、JavaScript和C++等语言编写的请求被接受的几率相对降低,而使用C#、 C、TypeScript、Scala和Go等会增大Pull请求被接受的几率[14,18].

2.3.2 文件优先级

在某些项目中,评审者对优先级高的项目表现更为积极.例如:在WebKit项目中,发现项目优先级和评审积极性之间存在统计学上显著的相关性,优先级越高的补丁被接受的几率越大[17,20].

2.3.3 软件组件

不同的项目组件对代码评审的时间有一定影响[17].例如,WebKit项目包括bindings,bridge,css,dom,editing,html,inspector,page,platform,rendering等组件,其中,rendering组件需要101分钟的时间来进行评审,bindings需要72分钟,inspector需要58分钟.

2.3.4 组织因素

代码Pull请求所属的组织机构对其评审有一定的影响[17].代码评审过程不仅和技术价值有关,而且和商业利益有关.当Pull请求的拥有者和评审者所在的组织不同时,代码审核速度会降低.例如,苹果对自身补丁的审核速度非常快,但对谷歌补丁的审核速度相对较慢.谷歌公司虽然对不同组织的补丁审核速度差异较小,但是,依然能体现出谷歌公司对待不同组织补丁的区别.

2.3.5 应用领域因素

面向开源项目的应用领域比较广泛,不同领域接受到的请求数量不等,所以,不同领域评审请求被通过的几率也不一样.例如,某些项目包含以下几个领域:Networking、数据库、IDE、Statistics、Framework、库和客户端应用程序.研究者发现Framework和IDE领域接受到的请求数量为最多,其中,被通过的成功几率也相对高于其他项目[14].

2.3.6 项目的热度

项目的热度由项目的存在的时间和被fork的数量来衡量[12,14].随着时间推移,项目热度会发生变化,从而,导致项目的Pull请求评审通过的几率不同.

3 评审员的自动推荐

有效地执行代码评审可以提高软件的质量并减少缺陷的发生.但是,这需要具有经验和对系统的代码有深刻理解的审阅人员.对于具有一定规模的开源项目,涉及到的人员数量众多,手动选择审阅人员是一项昂贵且耗时的任务.面对这一挑战,自动化推荐是解决该问题的一个有力的工具.因而,最近研究者们通过对不同大型项目研究分析,提出了各种各样的推荐算法,为Pull请求寻求一个合适的评审人员.

3.1 自动推荐技术

如今,随着信息技术和互联网的发展,人们逐渐从信息匮乏的时代走入了信息过载[24].自动推荐系统是建立在海量数据挖掘基础上的一种高级商务智能平台[25],它可以为用户提供个性化的决策支持和信息服务,帮助用户发现对自己有价值的信息,从而实现信息消费者和信息生产者的双赢[26].自动推荐技术和系统在许多领域都得到了应用[27],例如,电子商务、电影和视频网站、个性化音乐网络电台、社交网络、个性化邮件、个性化阅读等[28,29].推荐算法的本质是通过一定的方式将用户与物品联系起来,目前大致有三种方式:基于内容的推荐[30-32]、基于协同过滤的推荐[33-35]和社会化推荐[36-38].由于一个Pull请求并不需要反复被评审,因此,协同过滤的推荐方法无法使用[39].因此,目前采用的评审员推荐方法主要分为基于内容的推荐和社会化推荐两类.

3.2 基于代码内容的推荐

基于内容的推荐是在推荐引擎出现之初应用最为广泛的推荐方法,它的核心思想是根据推荐对象的属性,发现“物品”或者内容的相关性,然后基于用户以往的偏好记录,推荐给用户相似的“物品”[40,42,44].围绕审阅者推荐,研究者利用了基于文件路径的相似性、基于信息检索的方法进行推荐和基于经验的推荐.

3.2.1 基于文件路径相似度算法

文献[1,8,43,44,45]中都采用了文件路径相似度的算法,该算法的目的是推荐那些曾经评审过相关代码的评审人员.代码的相关性可以采用文件路径的相似性来衡量.该算法的核心思想是建立在一个假设基础之上:位于类似的文件路径中的文件是相关的,因此可以有同样的专家进行代码评审[8].这个假设背后的理由是:在大多数大型系统中,具有类似功能的文件通常位于相同或相接近的目录中[46],例如Linux内核项目中,文件目录结构反映了系统体系结构.

该算法的一般步骤如下:

首先,当目标Pull请求到达时,首先确定Pull请求的相关文件目录,即提交者将文件提交到的文件目录.这些文件目录被视为Pull请求的相关模块目录.

其次,在选择目标Pull请求的候选评审员后,计算该Pull请求与各候选评审员之间的关系.在提交目标Pull请求之前,每个候选人都已经提交或审核过这个模块相同或相似的文件,因此,通过总结每个相似文件之间的关系来得到Pull请求和评审者之间的关系,如公式(1)所示:

relation(d,pr)=∑pr′:PRd∑f′:Fpr′∑f:Fprsimilarity(f,f′)

(1)

其中,其中,Fpr′表示历史Pull请求更改的相关文件,Fpr表示为目标Pull请求更改的相关文件,similarity(f,f′)表示两个文件之间相似度,d代表评审者;PRd指评审者d之前评审过的Pull请求集.可以采用字符串比较具体计算文件目录的相似性.

最后,在得到每个候选人与目标Pull请求的关系后,得到排名前n的候选人作为推荐结果.

具体而言,文献[8]中使用了四种字符串比较方法求取文件路径相似性,这四种方法分别为:最长公共前缀,最长公共后缀、最长公共子串和最长公共子序列.文献[44]提出求取文件目录相似性公式,如公式(2)所示:

(2)

上式公式fn,fp分别表示在Pull请求pr所在的同目录下的两个文件,max(Length(fn),Length(fp))取两个文件的长度的最大值,其中commonPath(fn,fp)计算两个文件路径中的公共目录的数量,具体计算方法如下:首先,根据文件路径,将路径字符串其目录分隔符进行切分,得到该文件所处位置的目录层次列表;然后比较两文件的目录前缀,取重合的公共目录数为得到的结果.例如,对于某安卓项目,有以下两个文件,分别为/src/camera/photo/a.java和/src/camera/video/a.java,则可得两者公共祖先目录为/src/camera文件夹,因而,两路径的公共目录数量为2.

文献[45]中得出结论,使用该算法准确率高达77.97%,明显优于基于代码行对比的Review Bot算法,使得相似性的对比不局限于具体的代码行,所以泛化能力更强,对不同的项目更为通用.

3.2.2 基于信息检索推荐算法

文献[8,49,50-52]中提出了基于信息检索的推荐算法,该算法的核心步骤如下:

首先,根据项目的描述及标题,为每个项目的Pull 请求生成技术术语集合,而这个术语集合是依据构建的技术术语语料库删除之前定义的停止词来生成的.

其次,根据TF-IDF算法生成每个Pull 请求的向量,其中不同的Pull 请求具有不同的值,如公式(3)所示:

(3)

上式中,t是某个从Pull请求pr中提取的技术术语,freq(t,pr)是t在每个pr中出现的次数,Tpr是在每个Pull请求中所有的技术术语集合,PR表示整个项目的所有Pull请求.

接着,当出现目标Pull请求时,可通过余弦相似度计算Pull请求之间的关系.利用开发人员之前评审过的Pull请求与目标Pull请求之间的关系,得到开发人员与目标Pull请求之间的关系,如公式(4)所示:

(4)

上式中,d是评审者,PRd是评审者d以前审核过的Pull请求集,similarity(pr,pr′)是目标Pull请求pr与历史Pull请求pr′之间的相似度.因此当目标Pull请求出现,通过计算评审者与目标Pull请求之间的关系,然后按降序排列这些值,并推荐最前面的几个结果.

文献[53]中也指出该推荐算法比较简单,比较适合活跃的评审人员,不适合不活跃的评审人员,总体性能精确度高于Rails Robot(一种自动分配代码评审人的机器).

3.2.3 基于经验的推荐

基于文件路径或检索的推荐算法均是利用了Pull请求的粗粒度信息.而基于审阅者经验的推荐算法可以对评审者进行更深入的刻画.该方法利用代码的历史更改来衡量评审者的专业知识,充分考虑评审者历史的兴趣点,从而得出Pull请求与评审者的联系程度,进而进行评审者推荐[41,54-57].利用遗忘曲线的特性,记忆的强度如公式(5)所示:

(5)

上式中,t表示创建了记忆后经过的时间,s表示记忆强度.在评审者推荐问题中,当一个评审者审阅或提交了一段代码,则可以认为该评审者拥有了这部分记忆.随着时间的推移,可以认为该评审者对它的记忆逐渐减弱.

当开发者评审或提交Pull请求时,认为该开发者对这块领域的相关技术具备一定的知识储备.然而,不同的Pull请求对应不同文件中的代码行修改,同时对应的提交和评审时间不一样.因此,为了计算开发人员对不同Pull请求的知识经验,需要涉及修改的代码行及相关的时间.开发者对源代码文件的经验知识值如公式(6)所示:

E(d,f)=

(6)

上式中,kT(d,pr)表示由开发者d提交或是评审Pull请求的时间,PRd表示由开发者评审过的Pull请求集合,PRf表示修改文件f中的目标Pull请求集合,loc(pr,f)表示Pull请求pr的相关文件f中更改的代码行,这里设置的记忆强度为1,忽略不同开发者之间的差异.

开发者和目标Pull请求之间的关系,总结为候选者对目标Pull请求中各个相关文件的经验知识,如公式(7)所示:

(7)

式中Fpr表示目标Pull请求pr目录下的文件集合.

该算法考虑到评审者兴趣点的历史变化,对评审者近期的活跃点进行着重考量,使结果更为可信.较以往模型,可以有效的处理评审者兴趣转换的问题,具有积极意义.

3.3 基于社会化关系的推荐

社会化推荐是通过用户自己的社交网络关系进行信息推荐[61-63],物以类聚,人以群分,好友中越多的人对某条信息感兴趣,则可以预测用户对该条信息的兴趣度越大.从而可以利用社会化推荐的方法,来进行评审者推荐.

基于社会化推荐的思想,文献[12,47,50,54-56]中提出了基于评论网络的推荐算法,该算法的核心思想是通过寻求共同兴趣的开发人员,使之作为一位合适的评审人员.其中,开发者之间的共同兴趣可以通过开发者和评审者之间对Pull请求的评论互动来衡量.基于评论网络,可以预测出与Pull请求高度相关的评审者,从而进行评审者推荐.

评论网络[54]由有向图Gcn=来定义,其中,顶点v表示开发人员的集合,边e表示结点之间的关系,权值w集合表示边的重要程度,eiJ表示结点Vi到结点VJ的一条边,且结点VJ至少评审过结点Vi的一个Pull请求,eij的Wij权值如公式(8)所示:

(8)

其中k是结点开发者Vi提交Pull请求的总数,m是VJ在同一个Pull请求中提交的评论的总和,Pc是经过实证的默认值(设置为1),其含义是保留评估每条评论对Pull请求的影响,λ是Pull请求的衰减系数,其值设置为0.8,t(i,j,r,n)为时间敏感因子函数,如公式(9)所示:

(9)

其中timestamp(i,j,r,n)是评审者VJ对贡献者Vi在第r条Pull请求提交第n条评论的创建时间.其中baseline与deadline与训练集的选择高度相关.例如,使用2012年01月01日至2013年05月31日的数据来学习权重,则baseline和deadline分别设置为2011年12月31日和2013年05月31日.

当贡献者提出新的Pull请求时,使用Pull请求的文本来计算各个候选评审者的专业评分.然后,在评论网络中,由贡献者开始,计算各个候选评审者的兴趣评分.同时,考虑到共同兴趣的影响和专业性的影响都很重要,因而将兴趣分数进行归一化,最后将两种评分加和起来得到各个评审者的最终评分,并以此选取最大分数的K个评审者进行推荐.

4 开源项目代码评审者推荐研究的数据集和评价指标

4.1 数据集的选取

近年来,随着分布式软件开发流行,越来越多的开源软件项目托管在Github管理平台.GitHub为这些公开的数据提供了REST API[58,60,64],通过调用API接口,研究人员能够获取各大型项目的原始数据,如项目开发者信息、代码库信息、提交信息、合并信息、Bug 信息等,从而构造高质量且互相关联的数据.可以从GitHub上获取详细的Pull请求的评审信息,此外,还可以使用REST API收集一些流行的大型开源项目数据,如Rails、Angular、Netty、Saltstack等上面的代码评审信息.

4.2 评价指标

对于评审者推荐结果的评价,研究者们主要使用精确率(Precision)、召回率(Recall)、MMR(Mean Reciprocal Rank)等[48,59,61,65].当模型返回的是排序的K个推荐结果,然后可以根据Top1到TopK的精确率和召回率,以及MMR来得到对模型的评价.

1)精确率:又称查准率,正确推荐的评审者数目占模型预测的评审者总数的比例.计算如公式(10)所示:

(10)

上式中,Recom_Reviewers(i)代表给第i个Pull请求推荐的评审者,Actual_Reviewer(i)是第i个Pull请求的实际评审员,m是每个项目中测试Pull请求的数量.

2)召回率:又称查全率,正确推荐的评审者数目占所有评审者数目的比例.其计算如公式(11)所示:

Recall=

(11)

上式中的各变量含义同上.

3)MMR:考察返回的结果集合的优劣情况,较优的结果越靠前,评分越高.其计算如公式(12)所示:

(12)

上式中,K是推荐系统返回结果的个数,rank(i)是第一个相关的结果所在的排列位置.

5 研究展望

尽管对开源软件中代码评审的研究已经取得了不少成果,但是这些研究还存在一定的局限性,未来需要(但不限于)从以下方面展开研究:

1)尽管研究者已经分析了众多因素对代码评审的影响,这些方面的研究还不够全面和深入.一方面,还有许多因素没有考虑,例如,评审者的情感、性别等因素没有研究;第二,各种因素具有复杂的交互作用,目前的研究并没有考虑因素的交叉影响;第三,目前的许多研究采用的是定性方法,并基于小规模的调研,下一步可以基于更大的数据规模进行定量化的研究.

2)尽管有关代码评审的影响因素已经有不少研究并取得了相应的成果,但是在评审者推荐中这些因素并没有得到完整的考虑.例如,研究中表明评审者的工作负荷对Pull请求的处理及时性有影响,如果一位评审者目前有很多请求需要去评审,那么新的评审任务很可能被他拒绝,因此成为一个失败的推荐.在未来,评审者推荐需要进一步结合多种影响因素.

3)目前的推荐算法都是基于历史数据来确定评审员是否适合某个Pull请求[13,47,55,56],并基于历史数据来评价该推荐算法的效果.然而,实际中的代码评审并不总是成功的,因而,需要结合过去评审者的实际表现进行推荐.

4)随着开源项目流行,开发者和项目的数量日益俱增,不同的项目具有不同的特点.然而,现有的评审者推荐算法普遍还不具有很强的泛化能力且不能很好的处理概念漂移的问题.甚至不同的项目可能需要不同的推荐算法.因而,评审者推荐算法的适应性也是一个重要的研究方向.

6 总 结

近年来,开源软件开发得到了迅速发展并涌现出一批成功的开源项目.许多个人乃至企业都参与到开源软件开发中.然而,开源软件开发中如何保证软件开发质量是一个挑战.其中Pull请求的代码评审是保证软件开发质量的重要手段.

围绕代码评审的研究已成为一个热点.研究者们针对影响代码评审质量的因素、评审者的自动推荐等问题进行了研究并取得了一定的成果.研究表明,请求提交次数、补丁代码的大小、是否首次提交Pull请求、开发者的身份、开发者与审阅者之间的关系、Pull请求中代码的位置、开发者的经验、评审人的活跃度、项目涉及的编程语言、评审队列长度、文件优先级、组织因素、应用领域因素、项目的热度等都对评审质量或者评审的及时性和响应性有一定的影响.同时,为了进行代码评审者推荐,目前的推荐方法中考虑了Pull请求之间的相似性、项目之间技术相关度、开发者之间的社交关联度等信息.下一步我们需要对更多的影响因素进行研究并应用于评审者推荐算法中.

猜你喜欢

开发者开源代码
校园武术“学、练、赛”一体化实践探索
传播开源精神 共迎美好未来
五毛钱能买多少头牛
2019开源杰出贡献奖
神秘的代码
“85后”高学历男性成为APP开发新生主力军
一周机构净增(减)仓股前20名
一行代码玩完19亿元卫星
16%游戏开发者看好VR
近期连续上涨7天以上的股