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 }