UNPKG

wasm-metering

Version:

injects metering into webassembly binaries

732 lines (731 loc) 20.9 kB
<!doctype html> <html lang="en"> <head> <title>Code coverage report for index.js</title> <meta charset="utf-8" /> <link rel="stylesheet" href="prettify.css" /> <link rel="stylesheet" href="base.css" /> <meta name="viewport" content="width=device-width, initial-scale=1"> <style type='text/css'> .coverage-summary .sorter { background-image: url(sort-arrow-sprite.png); } </style> </head> <body> <div class='wrapper'> <div class='pad1'> <h1> <a href="index.html">All files</a> index.js </h1> <div class='clearfix'> <div class='fl pad1y space-right2'> <span class="strong">100% </span> <span class="quiet">Statements</span> <span class='fraction'>111/111</span> </div> <div class='fl pad1y space-right2'> <span class="strong">98.28% </span> <span class="quiet">Branches</span> <span class='fraction'>57/58</span> </div> <div class='fl pad1y space-right2'> <span class="strong">100% </span> <span class="quiet">Functions</span> <span class='fraction'>13/13</span> </div> <div class='fl pad1y space-right2'> <span class="strong">100% </span> <span class="quiet">Lines</span> <span class='fraction'>102/102</span> </div> </div> </div> <div class='status-line high'></div> <pre><table class="coverage"> <tr><td class="line-count quiet">1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223</td><td class="line-coverage quiet"><span class="cline-any cline-yes">2x</span> <span class="cline-any cline-yes">2x</span> <span class="cline-any cline-yes">2x</span> <span class="cline-any cline-yes">2x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">272x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">272x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">272x</span> <span class="cline-any cline-yes">48x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">224x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">64x</span> <span class="cline-any cline-yes">64x</span> <span class="cline-any cline-yes">40x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">200x</span> <span class="cline-any cline-yes">190x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">10x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">272x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">58x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">112x</span> <span class="cline-any cline-yes">4x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">48x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">36x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">36x</span> <span class="cline-any cline-yes">112x</span> <span class="cline-any cline-yes">112x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">112x</span> <span class="cline-any cline-yes">112x</span> <span class="cline-any cline-yes">36x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">36x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">34x</span> <span class="cline-any cline-yes">34x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">36x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">36x</span> <span class="cline-any cline-yes">36x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">2x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">148x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">20x</span> <span class="cline-any cline-yes">20x</span> <span class="cline-any cline-yes">54x</span> <span class="cline-any cline-yes">54x</span> <span class="cline-any cline-yes">54x</span> <span class="cline-any cline-yes">34x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">20x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">20x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">152x</span> <span class="cline-any cline-yes">152x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">16x</span> <span class="cline-any cline-yes">16x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">14x</span> <span class="cline-any cline-yes">2x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">12x</span> <span class="cline-any cline-yes">10x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">22x</span> <span class="cline-any cline-yes">22x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">16x</span> <span class="cline-any cline-yes">20x</span> <span class="cline-any cline-yes">16x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">16x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">2x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">10x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">2x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">4x</span> <span class="cline-any cline-yes">4x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">16x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">24x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">16x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">22x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-yes">2x</span> <span class="cline-any cline-yes">2x</span> <span class="cline-any cline-yes">2x</span> <span class="cline-any cline-yes">2x</span> <span class="cline-any cline-neutral">&nbsp;</span> <span class="cline-any cline-neutral">&nbsp;</span></td><td class="text"><pre class="prettyprint lang-js">const toolkit = require('wasm-json-toolkit') const text2json = toolkit.text2json const SECTION_IDS = require('wasm-json-toolkit/json2wasm').SECTION_IDS const defaultCostTable = require('./defaultCostTable.json') &nbsp; // gets the cost of an operation for entry in a section from the cost table function getCost (json, costTable = {}, defaultCost = 0) { let cost = 0 // finds the default cost defaultCost = costTable['DEFAULT'] !== undefined ? costTable['DEFAULT'] : 0 &nbsp; if (Array.isArray(json)) { json.forEach(el =&gt; { cost += getCost(el, costTable) }) } else if (typeof json === 'object') { for (const propName in json) { const propCost = costTable[propName] if (propCost) { cost += getCost(json[propName], propCost, defaultCost) } } } else if (costTable[json] === undefined) { cost = defaultCost } else { cost = costTable[json] } return cost } &nbsp; // meters a single code entrie function meterCodeEntry (entry, costTable, meterFuncIndex, meterType, cost) { function meteringStatement (cost, meteringImportIndex) { return text2json(`${meterType}.const ${cost} call ${meteringImportIndex}`) } function remapOp (op, funcIndex) { if (op.name === 'call' &amp;&amp; op.immediates &gt;= funcIndex) { op.immediates = (++op.immediates).toString() } } function meterTheMeteringStatement () { const code = meteringStatement(0, 0) // sum the operations cost return code.reduce( (sum, op) =&gt; sum + getCost(op.name, costTable.code) , 0) } &nbsp; // operations that can possible cause a branch const branchingOps = new Set(['grow_memory', 'end', 'br', 'br_table', 'br_if', 'if', 'else', 'return', 'loop']) const meteringOverHead = meterTheMeteringStatement() let code = entry.code.slice() let meteredCode = [] &nbsp; cost += getCost(entry.locals, costTable.local) &nbsp; while (code.length) { let i = 0 &nbsp; // meters a segment of wasm code while (true) { const op = code[i++] remapOp(op, meterFuncIndex) &nbsp; cost += getCost(op.name, costTable.code) if (branchingOps.has(op.name)) { break } } &nbsp; // add the metering statement if (cost !== 0) { // add the cost of metering cost += meteringOverHead meteredCode = meteredCode .concat(meteringStatement(cost, meterFuncIndex)) } &nbsp; // start a new segment meteredCode = meteredCode .concat(code.slice(0, i)) code = code.slice(i) cost = 0 } &nbsp; entry.code = meteredCode return entry } &nbsp; /** * Injects metering into a JSON output of [wasm2json](https://github.com/ewasm/wasm-json-toolkit#wasm2json) * @param {Object} json the json tobe metered * @param {Object} opts * @param {Object} [opts.costTable=defaultTable] the cost table to meter with. See these notes about the default. * @param {String} [opts.moduleStr='metering'] the import string for the metering function * @param {String} [opts.fieldStr='usegas'] the field string for the metering function * @param {String} [opts.meterType='i64'] the regerster type that is used to meter. Can be `i64`, `i32`, `f64`, `f32` * @return {Object} the metered json */ exports.meterJSON = (json, opts) =&gt; { function findSection (module, sectionName) { return module.find(section =&gt; section.name === sectionName) } &nbsp; function createSection (module, name) { const newSectionId = SECTION_IDS[name] for (let index in module) { const section = module[index] const sectionId = SECTION_IDS[section.name] if (sectionId) { if (newSectionId &lt; sectionId) { // inject a new section module.splice(index, 0, { name, entries: [] }) return } } } } &nbsp; let funcIndex = 0 let functionModule, typeModule &nbsp; let {costTable, moduleStr, fieldStr, meterType} = opts &nbsp; // set defaults if (!costTable) costTable = defaultCostTable if (!moduleStr) moduleStr = 'metering' if (!fieldStr) fieldStr = 'usegas' if (!meterType) meterType = 'i32' &nbsp; // add nessicarry sections iff they don't exist if (!findSection(json, 'type')) createSection(json, 'type') if (!findSection(json, 'import')) createSection(json, 'import') &nbsp; const importJson = { 'moduleStr': moduleStr, 'fieldStr': fieldStr, 'kind': 'function' } const importType = { 'form': 'func', 'params': [meterType] } &nbsp; json = json.slice(0) &nbsp; for (let section of json) { section = Object.assign(section) switch (section.name) { case 'type': // mark the import index importJson.type = section.entries.push(importType) - 1 // save for use for the code section typeModule = section break case 'function': // save for use for the code section functionModule = section break case 'import': for (const entry of section.entries) { if (entry.moduleStr === moduleStr &amp;&amp; entry.fieldStr === fieldStr) { throw new Error('importing meteing function is not allowed') } if (entry.kind === 'function') { funcIndex++ } } // append the metering import section.entries.push(importJson) break case 'export': for (const entry of section.entries) { if (entry.kind === 'function' &amp;&amp; entry.index &gt;= funcIndex) { entry.index++ } } break case 'element': for (const entry of section.entries) { // remap elements indices entry.elements = entry.elements.map(el =&gt; el &gt;= funcIndex ? ++el : el) } break case 'start': // remap start index if (section.index &gt;= funcIndex) section.index++ break case 'code': for (const i in section.entries) { const entry = section.entries[i] const typeIndex = functionModule.entries[i] const type = typeModule.entries[typeIndex] const cost = getCost(type, costTable.type) &nbsp; meterCodeEntry(entry, costTable.code, funcIndex, meterType, cost) } break } } &nbsp; return json } &nbsp; /** * Injects metering into a webassembly binary * @param {Object} json the json tobe metered * @param {Object} opts * @param {Object} [opts.costTable=defaultTable] the cost table to meter with. See these notes about the default. * @param {String} [opts.moduleStr='metering'] the import string for the metering function * @param {String} [opts.fieldStr='usegas'] the field string for the metering function * @param {String} [opts.meterType='i64'] the regerster type that is used to meter. Can be `i64`, `i32`, `f64`, `f32` * @return {Buffer} */ exports.meterWASM = (wasm, opts = <span class="branch-0 cbranch-no" title="branch not covered" >{})</span> =&gt; { let json = toolkit.wasm2json(wasm) json = exports.meterJSON(json, opts) return toolkit.json2wasm(json) } &nbsp;</pre></td></tr> </table></pre> <div class='push'></div><!-- for sticky footer --> </div><!-- /wrapper --> <div class='footer quiet pad2 space-top1 center small'> Code coverage generated by <a href="https://istanbul.js.org/" target="_blank">istanbul</a> at Thu Apr 05 2018 17:48:46 GMT-0400 (EDT) </div> </div> <script src="prettify.js"></script> <script> window.onload = function () { if (typeof prettyPrint === 'function') { prettyPrint(); } }; </script> <script src="sorter.js"></script> </body> </html>