-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
wanderer
committed
Dec 21, 2016
0 parents
commit e054637
Showing
59 changed files
with
3,259 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
[submodule "test/wabt"] | ||
path = test/wabt | ||
url = git@github.com:WebAssembly/wabt.git |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
language: node_js | ||
node_js: | ||
- "7" | ||
env: | ||
matrix: | ||
- TEST_SUITE=test | ||
matrix: | ||
fast_finish: true | ||
include: | ||
- os: linux | ||
node_js: "7" | ||
env: TEST_SUITE=coveralls | ||
- os: linux | ||
node_js: "7" | ||
env: TEST_SUITE=lint | ||
script: npm run $TEST_SUITE |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
{ | ||
"start": 1, | ||
"type": { | ||
"params": { | ||
"DEFAULT": 1 | ||
}, | ||
"return_type": { | ||
"DEFAULT": 1 | ||
} | ||
}, | ||
"import": 1, | ||
"code": { | ||
"locals": { | ||
"DEFAULT": 1 | ||
}, | ||
"code": { | ||
"DEFAULT": 1 | ||
} | ||
}, | ||
"data": 1 | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,196 @@ | ||
const text2json = require('wasm-json-toolkit').text2json | ||
const SECTION_IDS = require('wasm-json-toolkit/json2wasm').SECTION_IDS | ||
|
||
function getCost (json, costTable = {}, defaultCost = 0) { | ||
let cost = 0 | ||
defaultCost = costTable['DEFAULT'] !== undefined ? costTable['DEFAULT'] : 0 | ||
|
||
if (typeof costTable === 'function') { | ||
cost = costTable(json) | ||
} else if (Array.isArray(json)) { | ||
json.forEach(el => { | ||
cost += getCost(el, costTable) | ||
}) | ||
} else if (typeof costTable === 'number') { | ||
cost = 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 | ||
} | ||
|
||
exports.meter = function meter (json, costTable) { | ||
function findSection (module, sectionName) { | ||
return module.find(section => section.name === sectionName) | ||
} | ||
|
||
function createSection (module, sectionName) { | ||
const newSectionId = SECTION_IDS[sectionName] | ||
for (let index in module) { | ||
const section = module[index] | ||
const sectionId = SECTION_IDS[section.name] | ||
if (sectionId) { | ||
if (newSectionId < sectionId) { | ||
// inject a new section | ||
module.splice(index, 0, { | ||
name: sectionName, | ||
entries: [] | ||
}) | ||
return | ||
} | ||
} | ||
} | ||
} | ||
|
||
const importJson = { | ||
'moduleStr': 'metering', | ||
'fieldStr': 'usegas', | ||
'kind': 'function' | ||
} | ||
const importType = { | ||
'form': 'func', | ||
'params': ['i64'] | ||
} | ||
|
||
let funcIndex = 0 | ||
let initialCost = 0 | ||
let functionModule, typeModule | ||
|
||
json = json.slice(0) | ||
|
||
// add nessicarry sections iff they don't exist | ||
if (!findSection(json, 'type')) createSection(json, 'type') | ||
if (!findSection(json, 'import')) createSection(json, 'import') | ||
|
||
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) { | ||
initialCost += getCost(entry, costTable.import) | ||
if (entry.kind === 'function') { | ||
funcIndex++ | ||
} | ||
} | ||
// append the metering import | ||
section.entries.push(importJson) | ||
break | ||
case 'export': | ||
for (const entry of section.entries) { | ||
initialCost += getCost(entry, costTable.global) | ||
if (entry.kind === 'function' && entry.index >= funcIndex) { | ||
entry.index++ | ||
} | ||
} | ||
break | ||
case 'element': | ||
for (const entry of section.entries) { | ||
initialCost += getCost(entry, costTable.element) | ||
// remap elements indices | ||
entry.elements = entry.elements.map(el => el >= funcIndex ? ++el : el) | ||
} | ||
break | ||
case 'start': | ||
initialCost += getCost(section, costTable.start) | ||
// remap start index | ||
if (section.index >= 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) | ||
|
||
exports.meterCodeEntry(entry, costTable.code, funcIndex, cost) | ||
} | ||
break | ||
default: | ||
if (section.entries) { | ||
for (const entry of section.entries) { | ||
initialCost += getCost(entry, costTable[section.name]) | ||
} | ||
} | ||
} | ||
} | ||
return { | ||
initialCost: initialCost, | ||
module: json | ||
} | ||
} | ||
|
||
exports.meterCodeEntry = (entry, costTable, meterFuncIndex, cost = 0) => { | ||
function meteringStatement (cost, meteringImportIndex) { | ||
return text2json(`i64.const ${cost} call ${meteringImportIndex}`) | ||
} | ||
function remapOp (op, funcIndex) { | ||
if (op.name === 'call' && op.immediates >= funcIndex) { | ||
op.immediates = (++op.immediates).toString() | ||
} | ||
} | ||
function meterTheMeteringStatement () { | ||
const code = meteringStatement(0, 0) | ||
// sum the operations cost | ||
return code.reduce( | ||
(sum, op) => sum + getCost(op.name, costTable.code) | ||
, 0) | ||
} | ||
|
||
const branchingOps = new Set(['end', 'br', 'br_table', 'br_if', 'if', 'else', 'return', 'loop']) | ||
const meteringOverHead = meterTheMeteringStatement() | ||
let code = entry.code.slice() | ||
let meteredCode = [] | ||
|
||
cost += getCost(entry.locals, costTable.local) | ||
|
||
while (code.length) { | ||
let i = 0 | ||
|
||
// meters a segment of wasm code | ||
while (true) { | ||
const op = code[i++] | ||
remapOp(op, meterFuncIndex) | ||
|
||
cost += getCost(op.name, costTable.code) | ||
if (branchingOps.has(op.name)) { | ||
break | ||
} | ||
} | ||
|
||
// add the metering statement | ||
if (cost !== 0) { | ||
// add the cost of metering | ||
cost += meteringOverHead | ||
meteredCode = meteredCode | ||
.concat(meteringStatement(cost, meterFuncIndex)) | ||
} | ||
|
||
// start a new segment | ||
meteredCode = meteredCode | ||
.concat(code.slice(0, i)) | ||
code = code.slice(i) | ||
cost = 0 | ||
} | ||
|
||
entry.code = meteredCode | ||
return entry | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
{ | ||
"name": "wasm-metering", | ||
"version": "0.1.0", | ||
"description": "injects metering into a webassembly binary", | ||
"main": "index.js", | ||
"scripts": { | ||
"coverage": "node --harmony ./node_modules/istanbul/lib/cli.js cover ./test/index.js", | ||
"coveralls": "npm run coverage && coveralls <coverage/lcov.info", | ||
"lint": "standard", | ||
"test": "node --harmony ./test/index.js" | ||
}, | ||
"author": "mjbecze <mjbecze@gmail.com>", | ||
"license": "MPL-2.0", | ||
"dependencies": { | ||
"leb128": "0.0.0", | ||
"wasm-json-toolkit": "0.0.2" | ||
}, | ||
"devDependencies": { | ||
"coveralls": "^2.11.4", | ||
"istanbul": "^0.4.1", | ||
"standard": "^8.6.0", | ||
"tape": "^4.6.3" | ||
}, | ||
"keywords": [ | ||
"wasm", | ||
"metering", | ||
"webassembly" | ||
], | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/ewasm/wasm-metering.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/ewasm/wasm-metering/issues" | ||
}, | ||
"homepage": "https://github.com/ewasm/wasm-metering", | ||
"standard": { | ||
"ignore": [ | ||
"test/wabt" | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
const fs = require('fs') | ||
const cp = require('child_process') | ||
const path = require('path') | ||
const wasm2json = require('wasm-json-toolkit/wasm2json') | ||
|
||
function processFiles (path, out) { | ||
const binPath = `${__dirname}/wabt/out/wast2wasm` | ||
const files = fs.readdirSync(path) | ||
|
||
for (let file of files) { | ||
console.log(file) | ||
if (file.split('.')[1] === 'wast') { | ||
// compile to wasm | ||
const str = `${binPath} ${path}/${file} -o /tmp/temp.wasm` | ||
cp.execSync(str) | ||
const wasm = fs.readFileSync('/tmp/temp.wasm') | ||
// compile to json | ||
const json = wasm2json(wasm) | ||
fs.writeFileSync(`${out}/${file}.json`, JSON.stringify(json, null, 2)) | ||
} | ||
} | ||
} | ||
|
||
processFiles(path.join(__dirname, './in/wast'), path.join(__dirname, './in/json')) | ||
processFiles(path.join(__dirname, './expected-out/wast'), path.join(__dirname, './expected-out/json')) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
const tape = require('tape') | ||
const metering = require('../').meter | ||
const json = require('./memory.json') | ||
|
||
tape('different cost tables', t => { | ||
let costTable = require('./generativeCostTable.js') | ||
let meteredJson = metering(json, costTable) | ||
// let expectedJson = require(`${__dirname}/ | ||
t.deepEquals(meteredJson.initialCost, 20, 'should generat costs for memory') | ||
|
||
costTable.data = 5 | ||
meteredJson = metering(json, costTable) | ||
|
||
t.deepEquals(meteredJson.initialCost, 30, 'should use interger for section type') | ||
|
||
costTable.data = { | ||
'offset': { | ||
'return_type': { | ||
'i32': 3 | ||
} | ||
} | ||
} | ||
|
||
meteredJson = metering(json, costTable) | ||
t.deepEquals(meteredJson.initialCost, 26, 'should use nested keys for metering') | ||
t.end() | ||
}) | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"basic+import.wast.json": 1, | ||
"basic.wast.json": 0, | ||
"element.wast.json": 2, | ||
"fac.wast.json": 0, | ||
"memory0.wast.json": 20, | ||
"memory1.wast.json": 30, | ||
"memory2.wast.json": 26, | ||
"mixedImports.wast.json": 2, | ||
"start.wast.json": 4, | ||
"zeroCostOps.wast.json": 0, | ||
"stuff.wast.json": 3 | ||
} |
Oops, something went wrong.