1 module tagion.tools.wasmutil.wasmutil; 2 3 import std.exception : assumeUnique; 4 import std.file : exists, fread = read, readText, fwrite = write; 5 import std.format; 6 import std.getopt; 7 import std.json; 8 import std.meta; 9 import std.path : baseName, extension, setExtension; 10 import std.range : empty, only; 11 import std.stdio; 12 import std.traits : EnumMembers; 13 import tagion.basic.Types : Buffer, FileExtension; 14 import tagion.basic.basic : basename; 15 import tagion.hibon.Document : Document; 16 import tagion.hibon.HiBON : HiBON; 17 import tagion.hibon.HiBONJSON; 18 import tagion.wasm.WasmBase; 19 import tagion.wasm.WasmBetterC : wasmBetterC; 20 import tagion.wasm.WasmException; 21 import tagion.wasm.WasmGas; 22 import tagion.wasm.WasmReader; 23 import tagion.wasm.WasmWat : wat; 24 import tagion.wasm.WasmWriter; 25 26 //import tagion.script.StandardRecords; 27 import std.array : join; 28 import tagion.tools.Basic : Main; 29 import tagion.tools.revision; 30 31 template Produce(FileExtension ext) { 32 static if (ext == FileExtension.wat) { 33 import tagion.wasm.WasmWat; 34 35 alias Produce = wat; 36 } 37 else static if (ext == FileExtension.dsrc) { 38 import tagion.wasm.WasmBetterC; 39 40 alias Produce = wasmBetterC; 41 } 42 else { 43 static assert(0, format("Can not produce a %s format not supported", ext)); 44 } 45 } 46 47 auto produce(FileExtension ext)(WasmReader wasm_reader, File fout) { 48 return Produce!(ext)(wasm_reader, fout); 49 } 50 51 enum OutputType { 52 wat, /// WASM text output type in wat format (FileExtension.wat) 53 wasm, /// WASM binary output type (FileExtension.wasm) 54 betterc, /// BetterC source file (FileExtension.dsrc) 55 } 56 57 FileExtension typeExtension(const OutputType type) pure nothrow @nogc { 58 final switch (type) { 59 case OutputType.wat: 60 return FileExtension.wat; 61 case OutputType.wasm: 62 return FileExtension.wasm; 63 case OutputType.betterc: 64 return FileExtension.dsrc; 65 } 66 } 67 68 mixin Main!_main; 69 70 int _main(string[] args) { 71 immutable program = args[0]; 72 bool version_switch; 73 74 string inputfilename; 75 string outputfilename; 76 //bool print; 77 //bool betterc; 78 string module_name; 79 string[] imports; 80 string[] attributes; 81 bool inject_gas; 82 bool verbose_switch; 83 84 string[] modify_from; 85 string[] modify_to; 86 87 OutputType type; 88 try { 89 auto main_args = getopt(args, 90 std.getopt.config.caseSensitive, 91 std.getopt.config.bundling, 92 "version", "display the version", &version_switch, //"inputfile|i", "Sets the HiBON input file name", &inputfilename, 93 //"outputfile|o", "Sets the output file name", &outputfilename, // "bin|b", "Use HiBON or else use JSON", &binary, 94 // "value|V", format("Bill value : default: %d", value), &value, 95 "gas|g", format("Inject gas countes: %s", inject_gas), &inject_gas, 96 "verbose|v", format("Verbose %s", verbose_switch), &verbose_switch, 97 "mod|m", "Modify import module name from ", &modify_from, 98 "to", "Modify import module name from ", &modify_to, 99 "imports|i", "Import list", &imports, 100 "name", "Import list", &module_name, // "print|p", format("Print the wasm as wat: %s", print), &print, 101 102 // "betterc|d", format("Print the wasm as wat: %s", betterc), &betterc, 103 104 "type|t", format("Sets stdout file type (%-(%s %))", [EnumMembers!OutputType]), &type, 105 "global-attribute", "Sets the global attribute for the D transpiling", &attributes, 106 ); 107 108 void help() { 109 defaultGetoptPrinter( 110 [ 111 // format("%s version %s", program, REVNO), 112 "Documentation: https://tagion.org/", 113 "", 114 "Usage:", 115 format("%s [<option>...] <in-file> <out-file>", program), 116 format("%s [<option>...] <in-file>", program), 117 "", 118 "Where:", 119 format("<in-file> Is an input file in (%-(%s -%)) format", 120 only(FileExtension.wasm, FileExtension.wat)), 121 format("<out-file> Is an output file in (%-(%s -%)) format", 122 only(FileExtension.wat, FileExtension.dsrc)), 123 " stdout is used of the output is not specifed the", 124 "", 125 126 "<option>:", 127 128 ].join("\n"), 129 main_args.options); 130 } 131 132 if (version_switch) { 133 revision_text.writeln; 134 return 0; 135 } 136 137 version (none) 138 if (verbose_switch && (!print || outputfilename.length is 0)) { 139 verbose.mode = VerboseMode.STANDARD; 140 } 141 142 if (main_args.helpWanted) { 143 help; 144 return 0; 145 } 146 147 foreach (file; args[1 .. $]) { 148 with (FileExtension) { 149 switch (file.extension) { 150 case wasm: 151 case wat: 152 if (inputfilename.empty) { 153 inputfilename = file; 154 break; 155 } 156 check(outputfilename is null, 157 format("Only one outputfile allowed (both %s and %s has been specifiled)", 158 inputfilename, file)); 159 outputfilename = file; 160 break; 161 case dsrc: 162 check(outputfilename is null, 163 format("Only one outputfile allowed (both %s and %s has been specifiled)", 164 inputfilename, file)); 165 outputfilename = file; 166 break; 167 case wast: 168 169 if (inputfilename.empty) { 170 inputfilename = file; 171 type = OutputType.wasm; 172 173 } 174 175 if (module_name.empty) { 176 module_name = inputfilename.baseName(FileExtension.wast); 177 } 178 break; 179 default: 180 check(0, format("File %s is not supported", file)); 181 } 182 } 183 } 184 if (modify_from.length !is modify_to.length) { 185 stderr.writefln("Modify set must be set in pair"); 186 stderr.writefln("mod=%s", modify_from); 187 stderr.writefln("to=%s", modify_to); 188 help; 189 return 4; 190 } 191 192 immutable standard_output = (outputfilename.length == 0); 193 194 WasmReader wasm_reader; 195 WasmWriter wasm_writer; 196 with (FileExtension) { 197 switch (inputfilename.extension) { 198 case wasm, wo: 199 immutable read_data = assumeUnique(cast(ubyte[]) fread(inputfilename)); 200 wasm_reader = WasmReader(read_data); 201 verbose.hex(0, read_data); 202 wasm_writer = WasmWriter(wasm_reader); 203 break; 204 case wast: 205 import tagion.wasm.WastParser; 206 import tagion.wasm.WastTokenizer; 207 208 immutable wast_text = inputfilename.readText; 209 auto tokenizer = WastTokenizer(wast_text); 210 wasm_writer = new WasmWriter; 211 auto wast_parser = WastParser(wasm_writer); 212 wast_parser.parse(tokenizer); 213 writefln("Before wasmwrite"); 214 writefln("wasm_writer=%(%02X %)", wasm_writer.serialize); 215 break; 216 default: 217 check(0, format("File extensions %s not valid for input file (only %-(%s, %))", 218 inputfilename.extension, only(FileExtension.wasm, FileExtension.wo))); 219 } 220 } 221 222 //WasmWriter wasm_writer = WasmWriter(wasm_reader); 223 224 if (inject_gas) { 225 auto wasmgas = WasmGas(wasm_writer); 226 wasmgas.modify; 227 } 228 immutable data_out = wasm_writer.serialize; 229 writefln("after data_out"); 230 if (verbose_switch) { 231 verbose.mode = VerboseMode.STANDARD; 232 } 233 234 const output_extension = (outputfilename.empty) ? type.typeExtension : outputfilename.extension; 235 with (FileExtension) { 236 WasmOutCase: 237 switch (output_extension) { 238 static foreach (WasmOut; AliasSeq!(wat, dsrc)) { 239 case WasmOut: 240 File fout = stdout; 241 if (!outputfilename.empty) { 242 fout = File(outputfilename, "w"); 243 } 244 scope (exit) { 245 if (fout !is stdout) { 246 fout.close; 247 } 248 } 249 auto prod = produce!WasmOut(WasmReader(data_out), fout); 250 static if (__traits(hasMember, prod, "module_name")) { 251 writefln("BetterC"); 252 prod.module_name = module_name; 253 prod.imports = imports; 254 prod.attributes = attributes; 255 } 256 // produce!(FileExtension.wat)(WasmReader(data_out), outputfilename); 257 // import _wast=tagion.wasm.Wat; 258 // _wast.wat(WasmReader(data_out), stdout).serialize; 259 prod.serialize; 260 break WasmOutCase; 261 } 262 case wasm: 263 if (outputfilename.empty) { 264 outputfilename = inputfilename.setExtension(FileExtension.wasm); 265 } 266 outputfilename.fwrite(data_out); 267 break; 268 default: 269 check(outputfilename is null, 270 format("File extensions %s not valid output file (only %s)", 271 outputfilename.extension, 272 only(FileExtension.wasm, FileExtension.wat))); 273 version (none) 274 if (print) { 275 Wat(wasm_reader, stdout).serialize(); 276 } 277 } 278 } 279 } 280 catch (Exception e) { 281 //verbose(e); 282 stderr.writefln("Error: %s", e.msg); 283 } 284 return 0; 285 }