Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 青草久久精品亚洲综合专区,久草视频精品在线,欧美大片aaa

          整合營銷服務(wù)商

          電腦端+手機端+微信端=數(shù)據(jù)同步管理

          免費咨詢熱線:

          JS中的重載-如何實現(xiàn)一個類似這樣的功能,我也想玩玩

          JS中的重載-如何實現(xiàn)一個類似這樣的功能,我也想玩玩

          數(shù)重載

          在其他語言中,我們一般都聽說過重載的概念,對于一個方法,如果我們期望傳入不同的參數(shù)類型,或者傳入不同的參數(shù)個數(shù),甚至傳入?yún)?shù)的順序不一樣,而去執(zhí)行不同的處理代碼,以返回相應(yīng)的結(jié)果,那么他們的方法名是可以相同的,而且它們不會產(chǎn)生沖突,這就是所謂的重載。

          這是因為像java等這樣的強類型語言,他們具有類型約束和方法簽名,這樣就使得他們能夠根據(jù)調(diào)用時傳入?yún)?shù)的情況來決定使用哪一個同名方法來處理需要的操作。

          是在說我嗎?

          但是js屬于弱類型語言,也就是說它不會規(guī)定參數(shù)必須傳入哪種類型,定義的方法也不具有簽名的功能,而且在定義多個同名方法時,類似于css里面的層疊樣式表的效果,后定義的同名方法會覆蓋前面的方法,還有一個就是函數(shù)具有提升的特性,這也就使得它無法實現(xiàn)重載的功能。

          怎么辦呢?

          其實js中也確實不需要重載的功能,因為沒有類型的約束,在一個方法里面就可以做很多自由的發(fā)揮,不但能滿足需要重載的需求,而且還能玩一些花樣。

          不過話又說回來,沒有了約束,就容易犯錯,都用一個方法體來處理所有情況,就會容易出亂子,使得我們需要使用一堆的類似if-else的語句來做到這一點。

          那么我們能不能在現(xiàn)有js運行方式的基礎(chǔ)上,借鑒其他語言對于重載的運用,來繞個彎子變量來實現(xiàn)一下屬于js自己的重載方式呢?

          試一試

          我們今天只討論在js中變相實現(xiàn)重載的方式,而不討論它的意義與實際應(yīng)用場景,我們通過一個簡單的例子,來支撐這個討論,其他的交由你們來自由發(fā)揮。

          讓我們開始吧

          js重載的方式

          在js中主要有以下幾種實現(xiàn)重載的方式:

          1. 使用剩余參數(shù)的形式來接收可變的參數(shù),并通過一些判斷手段來處理不同情況的邏輯。
          2. 使用arguments的形式,來動態(tài)判斷需要執(zhí)行的操作
          3. 使用proxy的形式來攔截函數(shù)的行為,以達到控制不同的邏輯分支。

          前兩種方式比較相似,思路一樣,只是實現(xiàn)手段有所不同,用戶需要自己寫判斷邏輯。第三種方式結(jié)合proxy來隱藏實現(xiàn)細節(jié),讓用戶只關(guān)注各自的分工。但它們都是在參數(shù)動態(tài)判斷方面做文章。

          前兩種方式的優(yōu)缺點:

          優(yōu)點:可以直接定義函數(shù)或使用表達式

          缺點:函數(shù)名不能相同,需要寫較多的判斷分支

          第三種方式的優(yōu)缺點:

          優(yōu)點:可以不用寫各種參數(shù)形式的分支

          缺點:只能使用表達式定義函數(shù)

          由于前兩種網(wǎng)上已經(jīng)有很多的實現(xiàn)思路與方案,因此這里不再進行探討,其中有很多奇妙的實現(xiàn),可以做到在js中使用重載的思想。

          所以在此我們只討論第三種方案,我們下面來看一下它的思路是什么,是否滿足重載的需求,它是如何實現(xiàn)的,以及它能滿足我們什么樣的需求?

          這是什么呢?

          需求假設(shè)

          我們現(xiàn)在有這樣一個場景和需求:

          自己開了一家服裝店,由于生意火爆,我們想要答謝新老顧客,現(xiàn)在推出了一個活動,全場無論任何服裝,只要買一件就直接打95折,只要買兩件就全部打9折,只要買三件就全部打85折,只要買四件及以上,就全部打8折。

          如果用代碼來實現(xiàn),其實就是給方法中傳入一個兩個三個四個參數(shù)的問題,因此我們自然而然的就想到了使用重載來實現(xiàn)這個需求。

          接下來我們就試著自己實現(xiàn)一個這樣的功能,看看可不可以創(chuàng)建一個賦能方法來使某個業(yè)務(wù)處理函數(shù)具有重載的能力。

          思路分析

          要生成這樣一個賦能方法,我們需要有對函數(shù)改造的能力,在創(chuàng)建業(yè)務(wù)處理函數(shù)的時候,最好能夠改變它的默認行為,在執(zhí)行的時候也能夠?qū)λM行攔截以做一些額外的操作。

          那么我們很自然而然的就想到了使用Proxy,先生成一個Proxy函數(shù),然后在給它設(shè)置屬性的時候,我們進行攔截,把賦值操作中的value緩存起來,以備將來調(diào)用的時候做分支處理,根據(jù)參數(shù)的個數(shù)與類型來控制需要執(zhí)行的業(yè)務(wù)邏輯代碼。它真的能做到嗎?我們來看一下下面的一步步代碼實現(xiàn)。

          實現(xiàn)需求

          function Overload(defaultCall) {
            let func=defaultCall || new Function()
            func.overloadCached=[]
            return new Proxy(func, {
              set(target, prop, value) {
                if(prop==='load') {
                  target.overloadCached.push(value)
                }
              },
              apply(target, thisArg, argumentsList) {
                for(let i=target.overloadCached.length - 1; i > -1; i--) {
                  if(argumentsList.length===target.overloadCached[i].length || (argumentsList.length > target.overloadCached[i].length)) {
                    return target.overloadCached[i].apply(thisArg, argumentsList)
                  }
                }
                return target.apply(thisArg, argumentsList)
              }
            })
          }
          let sum=Overload()
          sum.load=function (a) {
            return a * 0.95;
          }
          sum.load=function (a, b) {
            return (a + b) * 0.9;
          }
          sum.load=function (a, b, c) {
            return (a + b + c) * 0.85;
          }
          sum.load=function (a, b, c, d, ...arg) {
            return (arg.concat(a,b,c,d).reduce((total, cur)=> {return total + cur},0)) * 0.8;
          }
          console.log(sum(200));
          console.log(sum(200, 300));
          console.log(sum(180, 280, 190));
          console.log(sum(270, 260, 310, 240));
          console.log(sum(180, 220, 240, 210, 190));
          //輸出:190,450,552.5,864,832

          可以看到,我們實現(xiàn)了一個Overload函數(shù),用來返回一個Proxy,通過它去load不同的方法來實現(xiàn)對同名方法的重載,調(diào)用的時候只需要一個方法名即可,Proxy中我們對set(即設(shè)置該Proxy的值的操作)和apply(即執(zhí)行該Proxy的操作)兩種操作進行了攔截,用到了一個叫做overloadCached的屬性來緩存我們的處理函數(shù),在調(diào)用函數(shù)的時候,我們使用從后往前遍歷的方式,來達到后定義優(yōu)先生效的原則。

          通過打印結(jié)果我們知道,它已經(jīng)滿足了我們的需求假設(shè)。

          默認處理

          從上面的代碼中我們發(fā)現(xiàn),Overload函數(shù)可以傳入一個叫做defaultCall的參數(shù),它是用來處理默認操作的,也就是說如果后面定義的所有方法都不能夠處理的時候,將使用該默認函數(shù)進行處理,如果沒有定義該函數(shù),那么調(diào)用sum時如果沒有滿足的執(zhí)行函數(shù),將會返回undefined。

          現(xiàn)在我們給它傳入一個默認的處理函數(shù),那么上面的需求將可以寫成這樣:

          function Overload(defaultCall) {
            let func=defaultCall || new Function()
            func.overloadCached=[]
            return new Proxy(func, {
              set(target, prop, value) {
                if(prop==='load') {
                  target.overloadCached.push(value)
                }
              },
              apply(target, thisArg, argumentsList) {
                for(let i=target.overloadCached.length - 1; i > -1; i--) {
                  //注意這里的變化
                  if(argumentsList.length===target.overloadCached[i].length) {
                    return target.overloadCached[i].apply(thisArg, argumentsList)
                  }
                }
                return target.apply(thisArg, argumentsList)
              }
            })
          }
          let sum=Overload(function () {
            return ([].__proto__.reduce.call(arguments, (total, cur)=> {return total + cur},0)) * 0.8;
          })
          sum.load=function (a) {
            return a * 0.95;
          }
          sum.load=function (a, b) {
            return (a + b) * 0.9;
          }
          sum.load=function (a, b, c) {
            return (a + b + c) * 0.85;
          }
          console.log(sum(200));
          console.log(sum(200, 300));
          console.log(sum(180, 280, 190));
          console.log(sum(270, 260, 310, 240));
          console.log(sum(180, 220, 240, 210, 190));
          //輸出:190,450,552.5,864,832

          我們注意Overload函數(shù)的變化,現(xiàn)在依然滿足上面的需求。

          處理兼容

          由于我們把四個參數(shù)即以上的處理函數(shù)改為通過傳入默認函數(shù)的方式來實現(xiàn),因此我們修改了Overload方法,這顯然是不合理的,因為這樣不設(shè)置默認函數(shù)的時候會出問題,因此我們做一個兼容處理,修改之后就變成了這樣:

          function Overload(defaultCall) {
            let func=defaultCall || new Function()
            func.overloadCached=[]
            return new Proxy(func, {
              set(target, prop, value) {
                if(prop==='load') {
                  let str=value.toString()
                  let m1=str.match(/\(.+?\)/)
                  if(m1 && m1[0].indexOf("...") !=-1) {
                    value.rest=true
                  }
                  target.overloadCached.push(value)
                }
              },
              apply(target, thisArg, argumentsList) {
                for(let i=target.overloadCached.length - 1; i > -1; i--) {
                  if((argumentsList.length===target.overloadCached[i].length) || (target.overloadCached[i].rest && argumentsList.length > target.overloadCached[i].length)) {
                    return target.overloadCached[i].apply(thisArg, argumentsList)
                  }
                }
                return target.apply(thisArg, argumentsList)
              }
            })
          }
          //輸出:190,450,552.5,864,832

          現(xiàn)在使用這個Overload函數(shù)就已經(jīng)能夠處理上面的這兩種情況了。我們設(shè)定了一個rest屬性來給方法打上了一個標識。

          需求延伸

          如果我們現(xiàn)在在上面的需求基礎(chǔ)上,又想要對金額做一些處理,比如希望能夠加上$、¥、等前綴,來區(qū)分不同的幣種。

          這個時候我們需要增加新的重載函數(shù),而加了幣種之后的函數(shù)可能與現(xiàn)有的函數(shù)參數(shù)個數(shù)相同(比如sum('$', 220, 240)和sum(270, 260, 310)),這就造成了誤判,那么我們能不能再做一個類型區(qū)分呢?

          應(yīng)該是可以的,但是我們必須約定一種格式,比如下面這種形式,我們需要在獲取Proxy屬性的時候(這里就用到了攔截獲取Proxy屬性的操作),將類型進行緩存,以便將來時候的時候來做類型的判斷:

          //我們約定了10種類型
          //n→number
          //s→string
          //b→boolean
          //o→object
          //a→array
          //d→date
          //S→Symbol
          //r→regexp
          //B→bigint
          //f→function
          function Overload(defaultCall) {
            let func=defaultCall || new Function()
            func.overloadCached=[]
            func.modifier=[]
            return new Proxy(func, {
              get(target, property, receiver) {
                if(property !=='load') {
                  target.modifier.push(property)
                }
                return receiver
              },
              set(target, prop, value) {
                if(['n','s','b','o','a','d','S','r','B','f'].includes(prop)) {
                  target.modifier.push(prop)
                }
                if(prop==='load' || target.modifier.length !==0) {
                  let str=value.toString()
                  let m1=str.match(/\(.+?\)/)
                  if(m1 && m1[0].indexOf("...") !=-1) {
                    value.rest=true
                  }
                  value.modifier=target.modifier
                  target.overloadCached.push(value)
                  target.modifier=[]
                }
              },
              apply(target, thisArg, argumentsList) {
                for(let i=target.overloadCached.length - 1; i > -1; i--) {
                  if((argumentsList.length===target.overloadCached[i].length) || (target.overloadCached[i].rest && argumentsList.length > target.overloadCached[i].length)) {
                    if(target.overloadCached[i].modifier.length !==0){
                      let ty={
                        '[object Number]': ['n'],
                        '[object String]': ['s'],
                        '[object Boolean]': ['b'],
                        '[object Object]': ['o'],
                        '[object Array]': ['a'],
                        '[object Date]': ['d'],
                        '[object Symbol]': ['S'],
                        '[object Regexp]': ['r'],
                        '[object BigInt]': ['B'],
                        '[object Function]': ['f'],
                      }
                      if(target.overloadCached[i].modifier.some((m, j)=> {
                        return !ty[({}).__proto__.toString.call(argumentsList[j])].includes(m)
                      })) {
                        continue
                      }
                    }
                    return target.overloadCached[i].apply(thisArg, argumentsList)
                  }
                }
                return target.apply(thisArg, argumentsList)
              }
            })
          }
          let sum=Overload()
          sum.load.n=function (a) {
            return a * 0.95;
          }
          sum.load.n.n=function (a, b) {
            return (a + b) * 0.9;
          }
          sum.load.n.n.n=function (a, b, c) {
            return (a + b + c) * 0.85;
          }
          sum.load.s.n.n=function (a, b, c) {
            return a + (b + c) * 0.85;
          }
          sum.load.n.n.n.n=function (a, b, c, d, ...arg) {
            return (arg.concat(a,b,c,d).reduce((total, cur)=> {return total + cur},0)) * 0.8;
          }
          sum.load.s.n.n.n=function (a, b, c, d, ...arg) {
            return a + (arg.concat(b,c,d).reduce((total, cur)=> {return total + cur},0)) * 0.8;
          }
          console.log(sum(200));
          console.log(sum(200, 300));
          console.log(sum(260, 310, 240));
          console.log(sum('', 280, 190));
          console.log(sum(180, 220, 240, 210, 190));
          console.log(sum('$', 220, 240, 210, 190));
          //輸出:190,450,688.5,399.5,832,$688

          我們現(xiàn)在已經(jīng)加上了類型判斷,通過傳入的參數(shù)類型與個數(shù)的不同,能夠相應(yīng)的去執(zhí)行對應(yīng)的函數(shù),其實參數(shù)的順序一個道理,也是支持的。

          類型擴展

          上面的類型約定我們可能看起來怪怪的,而且比較難以理解,因此我們可以擴展一下類型約定的表示方式,改造后的Overload函數(shù)如下:

          function Overload(defaultCall) {
            let func=defaultCall || new Function()
            func.overloadCached=[]
            func.modifier=[]
            return new Proxy(func, {
              get(target, property, receiver) {
                if(property !=='load') {
                  if(property.indexOf(',') !==-1) {
                    property.split(',').map(item=> {
                      target.modifier.push(item)
                    })
                  }else{
                    property.split('').map(item=> {
                      target.modifier.push(item)
                    })
                  }
                }
                return receiver
              },
              set(target, prop, value) {
                let modi=null
                if(prop.indexOf(',') !==-1) {
                  modi=prop.split(',')
                }else{
                  modi=prop.split('')
                }
                if(modi.every(p=> {
                  return ['n','s','b','o','a','d','S','r','B','f','number','string','boolean','object','array','date','Symbol','regexp','bigint','function'].includes(p)
                })) {
                  modi.map(item=> {
                    target.modifier.push(item)
                  })
                }
                if(prop==='load' || target.modifier.length !==0) {
                  let str=value.toString()
                  let m1=str.match(/\(.+?\)/)
                  if(m1 && m1[0].indexOf("...") !=-1) {
                    value.rest=true
                  }
                  value.modifier=target.modifier
                  target.overloadCached.push(value)
                  target.modifier=[]
                }
              },
              apply(target, thisArg, argumentsList) {
                for(let i=target.overloadCached.length - 1; i > -1; i--) {
                  if((argumentsList.length===target.overloadCached[i].length) || (target.overloadCached[i].rest && argumentsList.length > target.overloadCached[i].length)) {
                    if(target.overloadCached[i].modifier.length !==0){
                      let ty={
                        '[object Number]': ['n','number'],
                        '[object String]': ['s','string'],
                        '[object Boolean]': ['b','boolean'],
                        '[object Object]': ['o','object'],
                        '[object Array]': ['a','array'],
                        '[object Date]': ['d','date'],
                        '[object Symbol]': ['S','Symbol'],
                        '[object Regexp]': ['r','regexp'],
                        '[object BigInt]': ['B','bigint'],
                        '[object Function]': ['f','function'],
                      }
                      if(target.overloadCached[i].modifier.some((m, j)=> {
                        return !ty[({}).__proto__.toString.call(argumentsList[j])].includes(m)
                      })) {
                        continue
                      }
                    }
                    return target.overloadCached[i].apply(thisArg, argumentsList)
                  }
                }
                return target.apply(thisArg, argumentsList)
              }
            })
          }

          這樣我們就可以支持一下幾種類型約定的書寫形式:

          sum.load.s.n.n=function (a, b, c) {
            return a + (b + c) * 0.85;
          }
          sum.load['snn']=function (a, b, c) {
            return a + (b + c) * 0.85;
          }
          sum.load.snn=function (a, b, c) {
            return a + (b + c) * 0.85;
          }
          //對于全稱不能夠?qū)懗?(點)的形式
          sum.load['string,number,number']=function (a, b, c) {
            return a + (b + c) * 0.85;
          }
          //這四種形式的任意一種對于console.log(sum('$', 280, 190));
          //都會輸出:$399.5

          到此為止,我們已經(jīng)能夠支持參數(shù)的個數(shù)、類型、順序的不同,會執(zhí)行不同的處理函數(shù),滿足了重載的基本需求,完成了我們在最開始的需求假設(shè)的實現(xiàn)。

          結(jié)語

          目前這種方式只能支持函數(shù)表達式的方式來進行重載,這里只是給大家提供一個自定義實現(xiàn)重載的方式,結(jié)合自己的業(yè)務(wù)場景,小伙伴們可以自由發(fā)揮,其實目前js的既有方式能滿足我們需要重載的場景,而不需要額外設(shè)計重載的代碼。

          具體這種方式的優(yōu)劣,大家可以自行判斷,并且可以根據(jù)這種思路重新設(shè)計一下實現(xiàn)的手段。

          謝謝

          、JavaScript

          1.JavaScript語言

          JavaScript是ECMAScript的實現(xiàn),由ECMA 39(歐洲計算機制造商協(xié)會39號技術(shù)委員會)負責(zé)制定ECMAScript標準。

          ECMAScript發(fā)展史:

          時間

          版本

          說明

          1997年7月

          ES1.0 發(fā)布

          當年7月,ECMA262 標準出臺

          1998年6月

          ES2.0 發(fā)布

          該版本修改完全符合ISO/IEC 16262國際標準。

          1999年12月

          ES3.0 發(fā)布

          成為 JavaScript 的通行標準,得到了廣泛支持

          2007年10月

          ES4.0草案發(fā)布

          各大廠商意見分歧,該方案未通過

          2008年7月

          發(fā)布ES3.1,并改名為ECMAScript 5

          廢除ECMAScript 4.0,所以4.0版本不存在

          2009年12月

          ESt 5.0 正式發(fā)布


          2011年6月

          ES5.1 發(fā)布

          該版本成為了 ISO 國際標準(ISO/IEC 16262:2011)

          2013年12月

          ES6 草案發(fā)布


          2015年6月

          ES6 正式發(fā)布,并且更名為“ECMAScript 2015”

          TC39委員會決定每年發(fā)布一個ECMAScript 的版本

          2.JavaScript引擎

          JavaScript引擎是指用于處理以及執(zhí)行JavaScript腳本的虛擬機。

          常見的JavaScript引擎:


          ??

          我們描述了一個名為 Jalangi 的工具框架,用于 JavaScript 程序的動態(tài)分析和符號執(zhí)行測試。該框架是用 JavaScript 編寫的,允許對 JavaScript 進行各種重載動態(tài)分析。Jalangi 包含兩個關(guān)鍵技術(shù):1)選擇性錄制回放,這是一種能夠錄制并準確地回放用戶選擇的程序部分的技術(shù);2)陰影值和陰影執(zhí)行,可以輕松實現(xiàn)重量級動態(tài)分析,例如,condicolic 測試和污點跟蹤。Jalangi 通過對源代碼注入進行檢測,這使得它可以實現(xiàn)跨平臺移植。根據(jù) Apache 2.0 許可,可以從https://github.com/SRA-SiliconValley/jalangi獲得Jalangi。我們在SunSpider基準套件和五個Web應(yīng)用程序上對Jalangi的評估表明,Jalangi在錄制過程中的平均速度降低了26倍,在重放和分析過程中的速度平均降低了30倍。與類似工具(如PIN和針對x86二進制文件的Valgrind)報告的類似情況相比,速度的降低是可比的。

          關(guān)鍵詞

          JavaScript;動態(tài)分析;符號執(zhí)行測試

          一 導(dǎo)言

          JavaScript 是編寫客戶端 web 應(yīng)用程序的首選語言,并且在編寫移動應(yīng)用程序(例如用于 Tizen OS 和 iOS)、桌面應(yīng)用程序(如 Windows 8 和 Gnome 桌面應(yīng)用程序)和服務(wù)器端應(yīng)用程序(如 node.js)時越來越流行。但是,針對 JavaScript 應(yīng)用程序的分析、測試和調(diào)試的工具較少。我們已經(jīng)開發(fā)了一個簡單而強大的框架,稱為 Jalangi,用于為 JavaScript 編寫重載動態(tài)分析。本文簡要介紹了該框架及其使用場景。該框架提供了一些有用的抽象和 API,大大簡化了 JavaScript 動態(tài)分析的實現(xiàn)。關(guān)于 Jalangi 背后的技術(shù)的詳細描述可以在[6]中找到。

          Jalangi 可以在任何瀏覽器或 node.js 上工作。我們通過選擇性的源代碼檢測來實現(xiàn)瀏覽器的獨立性。即使某些源文件沒有檢測,Jalangi 也可以運行。對 Jalangi 的分析分兩個階段進行。在第一階段中,在用戶選擇的平臺(如 Android 上運行的 mobile chrome)上執(zhí)行并錄制一個插入指令的 JavaScript 應(yīng)用程序。在第二階段中,錄制的數(shù)據(jù)用于在桌面環(huán)境中執(zhí)行用戶指定的動態(tài)分析。

          Jalangi 允許通過支持陰影值和陰影執(zhí)行輕松實現(xiàn)動態(tài)分析。陰影值使我們能夠?qū)㈥幱爸蹬c程序中使用的任何值相關(guān)聯(lián)。在 Jalangi 中,我們使用陰影值和執(zhí)行實現(xiàn)了幾個動態(tài)分析:1)共同語言測試,2)純符號執(zhí)行,3)跟蹤 null 和 undefined 的來源,4)檢測可能的類型不一致,5)簡單的對象分配分析器,6)簡單的動態(tài)污染分析。

          二 技術(shù)細節(jié)

          我們提供了 Jalangi 的技術(shù)細節(jié)概要。有關(guān)更詳細的技術(shù)討論,請參閱[6]。用戶標識由 Jalangi 檢測的 web 應(yīng)用程序的一個子集,以便錄制和回放。在錄制階段,將在用戶選擇的平臺上執(zhí)行生成的檢測代碼。即使使用了用戶代碼的子集,整個應(yīng)用程序也會在錄制階段執(zhí)行,包括已插入和未插入 JavaScript 代碼以及本機代碼。但是,在回放階段,Jalangi 僅回放已檢測的代碼。Jalangi 具有在用戶平臺上完整執(zhí)行 JavaScript 應(yīng)用程序的功能,使錄制的執(zhí)行可以在開發(fā)筆記本電腦/臺式機 JavaScript 引擎上進行調(diào)試,以進行調(diào)試,包括移動瀏覽器和基于 node.js 的系統(tǒng),或具有嵌入式 JavaScript 引擎的集成開發(fā)系統(tǒng)。這種方法還支持使用支持陰影執(zhí)行的底層陰影值實現(xiàn)動態(tài)分析。

          通過錄制執(zhí)行期間從內(nèi)存加載的每個值,并在回放期間在相關(guān)內(nèi)存加載期間重新使用它們,可以有效地提供錄制和回放。這種方法雖然聽起來不錯,但也存在一些挑戰(zhàn),例如:(1)如何有效地錄制函數(shù)和對象?(2) 如何在未檢測本機函數(shù)(例如,JavaScript 事件 dispather)調(diào)用檢測函數(shù)時提供回放?通過提供間接記錄(其中唯一的數(shù)字標識符與每個對象和功能相關(guān)聯(lián))以及記錄這些標識符的值來解決第一個問題。第二個問題通過顯式錄制和調(diào)用已檢測功能來解決,這些功能又從未插入代碼中調(diào)用,或者由于 JavaScript 事件處理程序調(diào)度而執(zhí)行。

          此外,我們還觀察到,回放期間的內(nèi)存負載值可以通過執(zhí)行檢測代碼來計算,而無需錄制所有內(nèi)存負載的值。通過僅記錄必要的內(nèi)存負載,這用于提高 Jalangi 的效率。為了確定是否必須記錄內(nèi)存負載的值,Jalangi 在記錄階段跟蹤影子內(nèi)存,該影子內(nèi)存將在執(zhí)行實際代碼時隨實際內(nèi)存一起更新。 執(zhí)行本機和未執(zhí)行的代碼不會對影子內(nèi)存進行更新。為了確保在重放階段可以使用正確的值,只有在記錄階段在內(nèi)存位置存儲的值存在差異時(例如,如果在內(nèi)存位置的值之間存在差異),Jalangi 才會存儲內(nèi)存負載的值。 實際加載的內(nèi)存及其關(guān)聯(lián)值存儲在影子內(nèi)存中)。

          在 Jalangi 中,在回放階段執(zhí)行中使用的任何值都可以替換為帶注釋的值,該值可以為實際使用的值提供附加信息。例如,支持污染分析所需的額外污染信息,或者可能是與符號執(zhí)行中的實際值相關(guān)的信息,這些信息可以以符號表達式的形式提供。Jalangi 使用 concolvalue 類型的對象來表示帶注釋的值。

          三 動態(tài)分析

          在 Jalangi,我們進行了以下動態(tài)分析:

          • 符號執(zhí)行測試:符號執(zhí)行測試沿著具體的執(zhí)行路徑執(zhí)行符號執(zhí)行,生成表示輸入值約束的邏輯公式,并求解約束以生成新的測試輸入,這些新的測試輸入將沿著以前未探索的路徑執(zhí)行程序。Jalangi 中的符號執(zhí)行測試支持對整數(shù),字符串和對象類型的約束以及新穎的類型約束。我們引入類型約束來處理 JavaScript 的動態(tài)特性,對于程序的不同可行執(zhí)行路徑,輸入變量的類型可以不同。
          • 純符號執(zhí)行:純符號執(zhí)行象征性地執(zhí)行程序,從不為了回溯而重新啟動程序。它在執(zhí)行分支語句之前檢查狀態(tài),執(zhí)行一個分支,然后使用檢查點狀態(tài)回溯以探索另一個分支。對于小程序,純符號執(zhí)行避免了由于重復(fù)重新啟動而造成的時間浪費。
          • 跟蹤空值和未定義值的來源:此分析錄制空值和未定義值來源的源代碼位置,并在由于空值或未定義值而發(fā)生錯誤時報告位置。每當由于這些文本(例如訪問空值的字段)而出現(xiàn)錯誤時,就會向用戶報告文本的陰影值。這樣的報告有助于程序員輕松識別空值的來源。
          • 檢測可能的類型不一致:動態(tài)分析檢查在給定程序位置創(chuàng)建的對象是否可以采用多個不一致的類型。它計算在程序中的每個定義位置創(chuàng)建的對象和函數(shù)值的類型。如果在程序位置定義的對象或函數(shù)值在執(zhí)行期間被觀察到具有多個類型,則分析報告程序位置和觀察到的類型。有時,這種類型的不一致可能會指向程序中的潛在錯誤。我們已經(jīng)在兩個 SunSpider 基準測試程序中注意到了這樣的問題。
          • 簡單對象分配探查器:此動態(tài)分析錄制在給定分配站點創(chuàng)建的對象數(shù)量以及訪問對象的頻率。它報告在給定分配站點創(chuàng)建的對象是只讀的還是常量。它還報告對象創(chuàng)建時間和對象的最新訪問時間之間的最大和平均差異。如果分配站點創(chuàng)建的常量對象太多,則可能導(dǎo)致內(nèi)存效率低下。我們在基準測試套件中的一個 web 應(yīng)用程序中發(fā)現(xiàn)了這樣一個問題。
          • 動態(tài)污染分析:動態(tài)污染分析是一種信息流分析形式,它檢查信息是否可以從一組特定的內(nèi)存位置(稱為源)流向另一組內(nèi)存位置(稱為匯)。我們在 Jalangi 中實現(xiàn)了一種簡單的動態(tài)污染分析形式。在分析中,我們將任何對象的任何字段的讀取(以前未由檢測源編寫)視為污染源。我們將任何可能改變程序控制流的內(nèi)存位置的讀取都視為接收器。我們附加污染信息與實際值的陰影值。

          四 實施

          Jalangi 可在https://github.com/SRA SiliconValley/Jalangi 上獲得。我們已經(jīng)用 JavaScript 實現(xiàn)了 Jalangi。

          Jalangi 通過檢測 JavaScript 代碼進行操作。表 3 顯示了在表 1 中插入代碼后獲得的代碼。在檢測期間,Jalangi 從 Jalangi 庫插入各種回調(diào)函數(shù)。回調(diào)函數(shù)列在表 2 中。這些函數(shù)在 JavaScript 中包裝了各種操作。Jalangi 的選擇性錄制重放引擎是通過定義這些回調(diào)函數(shù)來實現(xiàn)的。

          Jalangi 將工具庫公開為函數(shù)工具代碼。這也使我們能夠動態(tài)地測試在運行時創(chuàng)建和計算的任何代碼。例如,我們將對 eval(s)的任何調(diào)用修改為 eval(instrumentCode(s))。

          五 Jalangi 的表現(xiàn)

          我們在 JavaScript-SunSpider(http://www.webkit.org/perf/SunSpider/SunSpider.html)基準套件中的26個程序和使用HTML5/JavaScript為Tizen操作系統(tǒng)編寫的5個web應(yīng)用程序上運行了Jalangi的錄制回放。(https://developer.tizen.org/下載/示例web應(yīng)用程序)。表4顯示了與錄制階段和三個動態(tài)分析相關(guān)的開銷:無分析(用null表示)、跟蹤空和未定義的來源(用track表示)和污染分析(用taint表示)。我們還報告為每個基準程序錄制的值的數(shù)量。實驗是在配備2.3 GHz Intel Core i7 和 8 GB RAM 的筆記本電腦上進行的,并在 Chrome 25 上運行了網(wǎng)絡(luò)應(yīng)用,并在 node.js 0.8.14 上執(zhí)行了重放。

          我們沒有衡量 web 應(yīng)用程序的增長速度,因為它們大多是交互式應(yīng)用程序。對于 SunSpider 基準套件,在記錄階段,我們觀察到平均速度降低了 26 倍,最低為 1.5 倍,最高為 93 倍。 在重播階段進行空分析時,我們觀察到平均速度降低了 30 倍,最小值為 1.5 倍,最大值為 93 倍。 跟蹤分析顯示,平均速度降低了 32.75 倍,最小值為 1.5 倍,最大值為 96 倍。

          5.1 JALANGI 動態(tài)分析檢測到的問題

          Jalangi 可能的類型不一致檢查器發(fā)現(xiàn),SunSpider 基準測試套件的 3d-cube.js 中的 CreateP 函數(shù)主要用作構(gòu)造函數(shù),但在某個位置它被稱為函數(shù)。作為函數(shù)調(diào)用的結(jié)果,程序在全局對象中創(chuàng)建一個不必要的 V 字段。我們認為此調(diào)用可能是編程錯誤。

          Jalangi 的對象分配分析器注意到附件游戲 webapp 中的 getValue(place,_board)方法創(chuàng)建了一個常量對象數(shù)千次。我們相信,通過在方法之外提升常數(shù)對象,可以避免這種不必要的常數(shù)對象的創(chuàng)建。

          六 相關(guān)工作

          據(jù)我們所知,Jalangi 是第一個 JavaScript 動態(tài)分析框架。很少有工具可以執(zhí)行 JavaScript 程序的錄制回放。JSBench 使用錄制回放機制創(chuàng)建 JavaScript 基準。Mugshot 捕獲所有事件,以確定地重放 web 應(yīng)用程序的執(zhí)行。Ripley 在服務(wù)器端副本上復(fù)制客戶端 JavaScript 程序的執(zhí)行。

          七 結(jié)論

          Jalangi 已經(jīng)處理了 JavaScript 的各種具有挑戰(zhàn)性的細節(jié)。由于可以處理 JavaScript 的所有令人擔(dān)憂的問題,因此可以輕松地在 Jalangi 框架中實施動態(tài)分析。我們期望 Jalangi 將有助于未來 JavaScript 動態(tài)分析的研究。

          致謝

          本文由南京大學(xué)軟件學(xué)院 2020 級碩士李彤宇轉(zhuǎn)述翻譯 感謝國家重點研發(fā)計劃(2018YFB1003900)和國家自然科學(xué)基金(61832009,61932012)支持!


          主站蜘蛛池模板: 国产日韩AV免费无码一区二区| 国产一区高清视频| 视频一区二区在线观看| 国产婷婷色一区二区三区| 亚洲综合色一区二区三区| 一区二区精品在线| 狠狠色成人一区二区三区| 久久精品无码一区二区三区| 精品国产乱码一区二区三区| 国产波霸爆乳一区二区| 波多野结衣一区在线| 亚洲AV无码国产一区二区三区| 无码人妻一区二区三区在线视频| 一区国严二区亚洲三区| 一区二区三区精密机械| 亚洲国产精品一区二区久| 国产一区视频在线| 国产伦理一区二区| 国产内射在线激情一区| 亚洲线精品一区二区三区| 亚洲国产精品一区二区三区久久| 成人精品一区久久久久| 国产在线观看精品一区二区三区91 | 国产日韩AV免费无码一区二区| 亚洲国产精品一区二区三区久久| 色欲精品国产一区二区三区AV| 中文字幕一区二区人妻性色 | 国产91精品一区二区麻豆网站 | 欧亚精品一区三区免费| 色欲精品国产一区二区三区AV| 亚洲一区免费观看| 亚洲宅男精品一区在线观看| 亚洲区精品久久一区二区三区| 久久er99热精品一区二区| 亚洲福利秒拍一区二区| 亚洲精品无码一区二区| 日韩视频一区二区在线观看| 国产伦精品一区二区三区视频小说| 国产免费一区二区三区VR| 国产精品毛片一区二区| 亚洲色无码一区二区三区|