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 }