1 /** 2 This module implements the Program class. 3 4 ──────────────────────────────────────────────────────────────────────────────── 5 6 Copyright (C) 2021 pillager86.rf.gd 7 8 This program is free software: you can redistribute it and/or modify it under 9 the terms of the GNU General Public License as published by the Free Software 10 Foundation, either version 3 of the License, or (at your option) any later 11 version. 12 13 This program is distributed in the hope that it will be useful, but WITHOUT ANY 14 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 15 PARTICULAR PURPOSE. See the GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License along with 18 this program. If not, see <https://www.gnu.org/licenses/>. 19 */ 20 module mildew.vm.program; 21 22 import std.typecons; 23 24 import mildew.types.func; 25 import mildew.util.encode; 26 import mildew.util.stack; 27 import mildew.vm.consttable; 28 import mildew.vm.debuginfo; 29 30 /** 31 * This is the compiled form of a program. It includes a table of constants that all functions under the 32 * same compilation share. 33 */ 34 class Program 35 { 36 /// constructor 37 this(ConstTable ct, ScriptFunction mainFunc, DebugMap debugMap=null) 38 { 39 _constTable = ct; 40 _mainFunction = mainFunc; 41 _debugMap = debugMap; 42 } 43 44 /// consttable property 45 ConstTable constTable() { return _constTable; } 46 /// mainfunction property 47 ScriptFunction mainFunction() { return _mainFunction; } 48 /// debugmap property 49 DebugMap debugMap() { return _debugMap; } 50 51 /// serialize to raw bytes that can be written and read to files 52 ubyte[] serialize() 53 { 54 // write an indicator that the file is binary 55 ubyte[] data = [0x01]; 56 data ~= encode(MAGIC); 57 data ~= VERSION; 58 data ~= encode!ubyte(size_t.sizeof); 59 // length of meta-data, 0 for now 60 data ~= encode!size_t(0); 61 data ~= constTable.serialize(); 62 data ~= encode(_mainFunction.compiled); 63 return data; 64 } 65 66 /// deserialize chunk from ubyte stream 67 static Program deserialize(ref ubyte[] stream, in string name="<program>") 68 { 69 if(stream[0] != 0x01) 70 throw new Exception("Invalid file format, not a Mildew program file"); 71 stream = stream[1..$]; 72 73 immutable magic = decode!uint(stream); 74 stream = stream[uint.sizeof..$]; 75 if(magic != MAGIC) 76 { 77 if(magic == MAGIC_REVERSE) 78 throw new Exception("This program was compiled on a machine with different CPU " 79 ~ "architecture. You must recompile the script for this machine."); 80 else 81 throw new Exception("This is not a Mildew program file"); 82 } 83 84 immutable version_ = decode!ubyte(stream); 85 stream = stream[1..$]; 86 if(version_ != VERSION) 87 throw new Exception("The version of the file is incompatible"); 88 89 immutable sizeOfSizeT = decode!ubyte(stream); 90 stream = stream[1..$]; 91 if(sizeOfSizeT != size_t.sizeof) 92 throw new Exception("Different CPU width, must recompile script for this machine"); 93 94 immutable sizeOfMD = decode!size_t(stream); 95 stream = stream[size_t.sizeof..$]; 96 97 auto constTable = ConstTable.deserialize(stream); 98 99 ubyte[] bytecode = decode!(ubyte[])(stream); 100 stream = stream[size_t.sizeof..$]; 101 stream = stream[bytecode.length * ubyte.sizeof .. $]; 102 auto mainFunc = new ScriptFunction(name, ["module", "exports"], bytecode, false); 103 104 // constTable.seal(); // there is no hashmap so it's not possible to add anymore values 105 // actually compiled scripts may call uncompiled scripts. const table deserialization rebuilds the hash map 106 107 return new Program(constTable, mainFunc); 108 } 109 110 /// enums used when serializing to and from file in the future 111 static const uint MAGIC = 0xB00BA911; 112 /// see above 113 static const uint MAGIC_REVERSE = 0x11A90BB0; 114 /// binary file format version 115 static const ubyte VERSION = 0x01; // file format version 116 117 private: 118 119 /// const table 120 ConstTable _constTable; 121 /// main function 122 ScriptFunction _mainFunction; 123 /** 124 * Optional debug map. Each function under the same chunk compilation can be added to this table. 125 */ 126 DebugMap _debugMap; 127 }