1 /** 2 This module implements the Chunk 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.chunk; 21 22 import std.typecons; 23 24 import mildew.util.encode; 25 import mildew.util.stack; 26 import mildew.vm.consttable; 27 import mildew.vm.debuginfo; 28 29 /** 30 * This is the compiled form of a program. It includes a table of constants that all functions under the 31 * same compilation share. 32 */ 33 class Chunk 34 { 35 /// const table 36 ConstTable constTable = new ConstTable(); 37 /// raw byte code 38 ubyte[] bytecode; 39 /** 40 * Optional debug map. Each function under the same chunk compilation can be added to this table. 41 */ 42 DebugMap debugMap; 43 44 /// serialize to raw bytes that can be written and read to files 45 ubyte[] serialize() 46 { 47 // write an indicator that the file is binary 48 ubyte[] data = [0x01]; 49 data ~= encode(MAGIC); 50 data ~= VERSION; 51 data ~= encode!ubyte(size_t.sizeof); 52 // length of meta-data, 0 for now 53 data ~= encode!size_t(0); 54 data ~= constTable.serialize(); 55 data ~= encode(bytecode); 56 return data; 57 } 58 59 /// deserialize chunk from ubyte stream 60 static Chunk deserialize(ref ubyte[] stream) 61 { 62 if(stream[0] != 0x01) 63 throw new ChunkDecodeException("Invalid file format, not a chunk file"); 64 stream = stream[1..$]; 65 immutable magic = decode!uint(stream); 66 stream = stream[uint.sizeof..$]; 67 if(magic != MAGIC) 68 { 69 if(magic == MAGIC_REVERSE) 70 throw new ChunkDecodeException("This chunk was compiled on a machine with different CPU " 71 ~ "architecture. You must recompile the script for this machine."); 72 else 73 throw new ChunkDecodeException("This is not a chunk file"); 74 } 75 immutable version_ = decode!ubyte(stream); 76 stream = stream[1..$]; 77 if(version_ != VERSION) 78 throw new ChunkDecodeException("The version of the file is incompatible"); 79 immutable sizeOfSizeT = decode!ubyte(stream); 80 stream = stream[1..$]; 81 if(sizeOfSizeT != size_t.sizeof) 82 throw new ChunkDecodeException("Different CPU width, must recompile script for this machine"); 83 immutable sizeOfMD = decode!size_t(stream); 84 stream = stream[size_t.sizeof..$]; 85 86 Chunk chunk = new Chunk(); 87 chunk.constTable = ConstTable.deserialize(stream); 88 chunk.bytecode = decode!(ubyte[])(stream); 89 stream = stream[size_t.sizeof..$]; 90 stream = stream[chunk.bytecode.length * ubyte.sizeof .. $]; 91 chunk.constTable.seal(); // there is no hashmap so it's not possible to add anymore values 92 return chunk; 93 } 94 95 /// enums used when serializing to and from file in the future 96 static const uint MAGIC = 0xB00BA911; 97 /// see above 98 static const uint MAGIC_REVERSE = 0x11A90BB0; 99 /// binary file format version 100 static const ubyte VERSION = 0x01; // file format version 101 } 102 103 /// Thrown when decoding binary chunk fails 104 class ChunkDecodeException : Exception 105 { 106 /// ctor 107 this(string msg, string file = __FILE__, size_t line = __LINE__) 108 { 109 super(msg, file, line); 110 } 111 }