1 /** 
2  * Copyright: Enalye
3  * License: Zlib
4  * Authors: Enalye
5  */
6 module grimoire.runtime.context;
7 
8 import grimoire.assembly;
9 import grimoire.runtime.engine, grimoire.runtime.array,
10     grimoire.runtime.channel, grimoire.runtime.object;
11 
12 /**
13 Represents a single function context in the callStack.
14 */
15 struct GrStackFrame {
16     /// Size of the locals in the calling function.
17     uint ilocalStackSize, flocalStackSize, slocalStackSize, olocalStackSize;
18     /// PC to jumps back to.
19     uint retPosition;
20     /// All current function deferred blocks.
21     uint[] deferStack;
22     /// All current function exception handling blocks.
23     uint[] exceptionHandlers;
24 }
25 
26 /**
27 Snapshot of the context's state. \
28 Used when we need to restore the context to a previous state.
29 */
30 struct GrContextState {
31     /// Current expression stack top
32     int istackPos, /// Ditto
33         fstackPos, /// Ditto
34         sstackPos, /// Ditto
35         ostackPos;
36 
37     /// Callstack
38     GrStackFrame stackFrame;
39 
40     /// Stack frame pointer for the current function.
41     /// Each function takes 2 integer: the return pc, and the local variable size.
42     uint stackPos;
43 
44     /// Local variables: Access with Xlocals[XlocalsPos + variableIndex]
45     uint ilocalsPos, flocalsPos, slocalsPos, olocalsPos;
46 }
47 
48 /**
49 Pause the associated context.
50 */
51 abstract class GrBlocker {
52     /// Update the state, returns true if the context is still paused.
53     bool run();
54 }
55 
56 /**
57 Coroutines are contexts that hold local data.
58 */
59 final class GrContext {
60     /// Default ctor.
61     this(GrEngine engine_) {
62         engine = engine_;
63         setupCallStack(4);
64         setupStack(8);
65         setupLocals(2, 2, 2, 2);
66     }
67 
68     /// Parent engine where the context is running.
69     GrEngine engine;
70 
71     /// Local variables
72     GrInt[] ilocals;
73     /// Ditto
74     GrFloat[] flocals;
75     /// Ditto
76     GrString[] slocals;
77     /// Ditto
78     GrPtr[] olocals;
79 
80     /// Callstack
81     GrStackFrame[] callStack;
82 
83     /// Expression stack.
84     GrInt[] istack;
85     /// Ditto
86     GrFloat[] fstack;
87     /// Ditto
88     GrString[] sstack;
89     /// Ditto
90     GrPtr[] ostack;
91 
92     /// Operation pointer.
93     uint pc;
94     /// Local variables: Access with Xlocals[XlocalsPos + variableIndex]
95     uint ilocalsPos, flocalsPos, slocalsPos, olocalsPos;
96     /// Stack frame pointer for the current function.
97     /// Each function takes 2 integer: the return pc, and the local variable size.
98     uint stackPos;
99 
100     /// Current expression stack top
101     int istackPos = -1, /// Ditto
102         fstackPos = -1, /// Ditto
103         sstackPos = -1, /// Ditto
104         ostackPos = -1;
105 
106     /// Kill state, unwind the call stack and call all registered deferred statements.
107     bool isKilled;
108 
109     /// An exception has been raised an is not caught.
110     bool isPanicking;
111 
112     /// Set when the context is in a select/case statement.
113     /// Then, the context is not stopped by a blocking channel.
114     bool isEvaluatingChannel;
115 
116     /// Set when the context is forced to yield by a blocking channel.
117     /// Release only when the channel is ready.
118     bool isLocked;
119 
120     /// When evaluating, a blocking jump to this position will occur instead of blocking.
121     uint selectPositionJump;
122 
123     /// The context will block until the blocker is cleared.
124     GrBlocker blocker;
125 
126     /// Backup to restore stack state after select evaluation.
127     GrContextState[] states;
128 
129     /// Current callstack max depth.
130     uint callStackLimit;
131     /// Current max local variable available.
132     uint ilocalsLimit, flocalsLimit, slocalsLimit, olocalsLimit;
133 
134     /// Initialize the call stacks.
135     void setupCallStack(uint size) {
136         callStackLimit = size;
137         callStack = new GrStackFrame[callStackLimit];
138     }
139 
140     /// Initialize the expression stacks.
141     void setupStack(uint size) {
142         istack = new GrInt[size];
143         fstack = new GrFloat[size];
144         sstack = new GrString[size];
145         ostack = new GrPtr[size];
146     }
147 
148     /// Initialize the local variable stacks.
149     void setupLocals(uint isize, uint fsize, uint ssize, uint osize) {
150         ilocalsLimit = isize;
151         flocalsLimit = fsize;
152         slocalsLimit = ssize;
153         olocalsLimit = osize;
154         ilocals = new GrInt[ilocalsLimit];
155         flocals = new GrFloat[flocalsLimit];
156         slocals = new GrString[slocalsLimit];
157         olocals = new GrPtr[olocalsLimit];
158     }
159 
160     /// Double the current callstack size.
161     void doubleCallStackSize() {
162         callStackLimit <<= 1;
163         callStack.length = callStackLimit;
164     }
165 
166     /// Double the current integer locals stacks' size.
167     void doubleIntLocalsStackSize(uint localsStackSize) {
168         while (localsStackSize >= ilocalsLimit)
169             ilocalsLimit <<= 1;
170         ilocals.length = ilocalsLimit;
171     }
172 
173     /// Double the current float locals stacks' size.
174     void doubleFloatLocalsStackSize(uint localsStackSize) {
175         while (localsStackSize >= flocalsLimit)
176             flocalsLimit <<= 1;
177         flocals.length = flocalsLimit;
178     }
179 
180     /// Double the current string locals stacks' size.
181     void doubleStringLocalsStackSize(uint localsStackSize) {
182         while (localsStackSize >= slocalsLimit)
183             slocalsLimit <<= 1;
184         slocals.length = slocalsLimit;
185     }
186 
187     /// Double the current object locals stacks' size.
188     void doubleObjectLocalsStackSize(uint localsStackSize) {
189         while (localsStackSize >= olocalsLimit)
190             olocalsLimit <<= 1;
191         olocals.length = olocalsLimit;
192     }
193 
194     alias setBool = setValue!GrBool;
195     alias setInt = setValue!GrInt;
196     alias setFloat = setValue!GrFloat;
197     alias setString = setValue!GrString;
198     alias setPtr = setValue!GrPtr;
199 
200     void setInt32(int value) {
201         setValue!GrInt(cast(GrInt) value);
202     }
203 
204     void setInt64(long value) {
205         setValue!GrInt(cast(GrInt) value);
206     }
207 
208     void setFloat32(float value) {
209         setValue!GrFloat(cast(GrFloat) value);
210     }
211 
212     void setFloat64(double value) {
213         setValue!GrFloat(cast(GrFloat) value);
214     }
215 
216     void setObject(GrObject value) {
217         setValue!GrPtr(cast(GrPtr) value);
218     }
219 
220     void setArray(T)(GrArray!T value) {
221         setValue!GrPtr(cast(GrPtr) value);
222     }
223 
224     void setIntArray(GrIntArray value) {
225         setValue!GrPtr(cast(GrPtr) value);
226     }
227 
228     void setFloatArray(GrFloatArray value) {
229         setValue!GrPtr(cast(GrPtr) value);
230     }
231 
232     void setStringArray(GrStringArray value) {
233         setValue!GrPtr(cast(GrPtr) value);
234     }
235 
236     void setObjectArray(GrObjectArray value) {
237         setValue!GrPtr(cast(GrPtr) value);
238     }
239 
240     void setIntChannel(GrIntChannel value) {
241         setValue!GrPtr(cast(GrPtr) value);
242     }
243 
244     void setFloatChannel(GrFloatChannel value) {
245         setValue!GrPtr(cast(GrPtr) value);
246     }
247 
248     void setStringChannel(GrStringChannel value) {
249         setValue!GrPtr(cast(GrPtr) value);
250     }
251 
252     void setObjectChannel(GrObjectChannel value) {
253         setValue!GrPtr(cast(GrPtr) value);
254     }
255 
256     void setEnum(T)(T value) {
257         setValue!GrInt(cast(GrInt) value);
258     }
259 
260     void setForeign(T)(T value) {
261         setValue!GrPtr(cast(GrPtr) value);
262     }
263 
264     private void setValue(T)(T value) {
265         static if (is(T == GrInt)) {
266             istackPos++;
267             istack[istackPos] = value;
268         }
269         else static if (is(T == GrBool)) {
270             istackPos++;
271             istack[istackPos] = value;
272         }
273         else static if (is(T == GrFloat)) {
274             fstackPos++;
275             fstack[fstackPos] = value;
276         }
277         else static if (is(T == GrString)) {
278             sstackPos++;
279             sstack[sstackPos] = value;
280         }
281         else static if (is(T == GrPtr)) {
282             ostackPos++;
283             ostack[ostackPos] = value;
284         }
285     }
286 
287     /// Register the current state of the context
288     void pushState() {
289         GrContextState state;
290         state.istackPos = istackPos;
291         state.fstackPos = fstackPos;
292         state.sstackPos = sstackPos;
293         state.ostackPos = ostackPos;
294         state.stackPos = stackPos;
295         state.stackFrame = callStack[stackPos];
296         state.ilocalsPos = ilocalsPos;
297         state.flocalsPos = flocalsPos;
298         state.slocalsPos = slocalsPos;
299         state.olocalsPos = olocalsPos;
300         states ~= state;
301     }
302 
303     /// Restore the last state of the context
304     void restoreState() {
305         if (!states.length)
306             throw new Exception("Fatal error: pop context state");
307         GrContextState state = states[$ - 1];
308         istackPos = state.istackPos;
309         fstackPos = state.fstackPos;
310         sstackPos = state.sstackPos;
311         ostackPos = state.ostackPos;
312         stackPos = state.stackPos;
313         ilocalsPos = state.ilocalsPos;
314         flocalsPos = state.flocalsPos;
315         slocalsPos = state.slocalsPos;
316         olocalsPos = state.olocalsPos;
317         callStack[stackPos] = state.stackFrame;
318     }
319 
320     /// Remove last state of the context
321     void popState() {
322         states.length--;
323     }
324 
325     /// Lock the context until the blocker is cleared
326     void block(GrBlocker blocker_) {
327         blocker = blocker_;
328     }
329 
330     /// Unlock the context from the blocker
331     void unblock() {
332         blocker = null;
333     }
334 
335     /// Dump stacks info
336     string dump() {
337         import std.conv : to;
338 
339         string result = "Context Dump:";
340         result ~= "\nfstack: " ~ to!string(fstack[0 .. (fstackPos + 1)]);
341         result ~= "\nistack: " ~ to!string(istack[0 .. (istackPos + 1)]);
342         result ~= "\nsstack: " ~ to!string(sstack[0 .. (sstackPos + 1)]);
343         result ~= "\nostack: " ~ to!string(ostack[0 .. (ostackPos + 1)]);
344         return result;
345     }
346 }