| 1 | /* |
| 2 | * Copyright 2017 WebAssembly Community Group participants |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | // |
| 18 | // Emit a JavaScript wrapper to run a wasm module with some test |
| 19 | // values, useful for fuzzing. |
| 20 | // |
| 21 | |
| 22 | #include "wasm-type.h" |
| 23 | #include "wasm.h" |
| 24 | #include <string> |
| 25 | |
| 26 | namespace wasm { |
| 27 | |
| 28 | inline std::string generateJSWrapper(Module& wasm) { |
| 29 | std::string ret; |
| 30 | ret += "if (typeof console === 'undefined') {\n" |
| 31 | " console = { log: print };\n" |
| 32 | "}\n" |
| 33 | "var tempRet0;\n" |
| 34 | "var binary;\n" |
| 35 | "if (typeof process === 'object' && typeof require === 'function' /* " |
| 36 | "node.js detection */) {\n" |
| 37 | " var args = process.argv.slice(2);\n" |
| 38 | " binary = require('fs').readFileSync(args[0]);\n" |
| 39 | " if (!binary.buffer) binary = new Uint8Array(binary);\n" |
| 40 | "} else {\n" |
| 41 | " var args;\n" |
| 42 | " if (typeof scriptArgs != 'undefined') {\n" |
| 43 | " args = scriptArgs;\n" |
| 44 | " } else if (typeof arguments != 'undefined') {\n" |
| 45 | " args = arguments;\n" |
| 46 | " }\n" |
| 47 | " if (typeof readbuffer === 'function') {\n" |
| 48 | " binary = new Uint8Array(readbuffer(args[0]));\n" |
| 49 | " } else {\n" |
| 50 | " binary = read(args[0], 'binary');\n" |
| 51 | " }\n" |
| 52 | "}\n" |
| 53 | "function literal(x, type) {\n" |
| 54 | " var ret = '';\n" |
| 55 | " switch (type) {\n" |
| 56 | " case 'i32': ret += (x | 0); break;\n" |
| 57 | " case 'f32':\n" |
| 58 | " case 'f64': {\n" |
| 59 | " if (x == 0 && (1 / x) < 0) ret += '-';\n" |
| 60 | " ret += Number(x).toString();\n" |
| 61 | " break;\n" |
| 62 | " }\n" |
| 63 | " // For anything else, just print the type.\n" |
| 64 | " default: ret += type; break;\n" |
| 65 | " }\n" |
| 66 | " return ret;\n" |
| 67 | "}\n" |
| 68 | "var instance = new WebAssembly.Instance(new " |
| 69 | "WebAssembly.Module(binary), {\n" |
| 70 | " 'fuzzing-support': {\n" |
| 71 | " 'log-i32': function(x) { " |
| 72 | "console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') " |
| 73 | "+ ']') },\n" |
| 74 | " 'log-i64': function(x, y) { " |
| 75 | "console.log('[LoggingExternalInterface logging ' + literal(x, 'i32') " |
| 76 | "+ ' ' + literal(y, 'i32') + ']') },\n" // legalization: two i32s |
| 77 | " 'log-f32': function(x) { " |
| 78 | "console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') " |
| 79 | "+ ']') },\n" // legalization: an f64 |
| 80 | " 'log-f64': function(x) { " |
| 81 | "console.log('[LoggingExternalInterface logging ' + literal(x, 'f64') " |
| 82 | "+ ']') },\n" |
| 83 | " },\n" |
| 84 | " 'env': {\n" |
| 85 | " 'setTempRet0': function(x) { tempRet0 = x },\n" |
| 86 | " 'getTempRet0': function() { return tempRet0 },\n" |
| 87 | " },\n" |
| 88 | "});\n" ; |
| 89 | for (auto& exp : wasm.exports) { |
| 90 | if (exp->kind != ExternalKind::Function) { |
| 91 | continue; // something exported other than a function |
| 92 | } |
| 93 | auto* func = wasm.getFunction(exp->value); |
| 94 | ret += "try {\n" ; |
| 95 | ret += std::string(" console.log('[fuzz-exec] calling " ) + |
| 96 | exp->name.toString() + "');\n" ; |
| 97 | if (func->getResults() != Type::none) { |
| 98 | ret += std::string(" console.log('[fuzz-exec] note result: " ) + |
| 99 | exp->name.toString() + " => ' + literal(" ; |
| 100 | } else { |
| 101 | ret += " " ; |
| 102 | } |
| 103 | ret += std::string("instance.exports." ) + exp->name.toString() + "(" ; |
| 104 | bool first = true; |
| 105 | for (auto param : func->getParams()) { |
| 106 | // zeros in arguments TODO more? |
| 107 | if (first) { |
| 108 | first = false; |
| 109 | } else { |
| 110 | ret += ", " ; |
| 111 | } |
| 112 | if (param.isRef()) { |
| 113 | ret += "null" ; |
| 114 | } else { |
| 115 | ret += "0" ; |
| 116 | if (param == Type::i64) { |
| 117 | ret += ", 0" ; |
| 118 | } |
| 119 | } |
| 120 | } |
| 121 | ret += ")" ; |
| 122 | if (func->getResults() != Type::none) { |
| 123 | ret += ", '" + func->getResults().toString() + "'))" ; |
| 124 | // TODO: getTempRet |
| 125 | } |
| 126 | ret += ";\n" ; |
| 127 | ret += "} catch (e) {\n" ; |
| 128 | ret += " console.log('exception!' /* + e */);\n" ; |
| 129 | ret += "}\n" ; |
| 130 | } |
| 131 | return ret; |
| 132 | } |
| 133 | |
| 134 | } // namespace wasm |
| 135 | |