aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs
diff options
context:
space:
mode:
authorUbitUmarov2018-02-02 12:49:40 +0000
committerUbitUmarov2018-02-02 12:49:40 +0000
commit83e2fee71be695b78438e0c9dc50b649a539d0e3 (patch)
treeaf213fea5ebfa4e773af6942c753e7f3bba0d83f /OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs
parentMerge branch 'master' into httptests (diff)
downloadopensim-SC-83e2fee71be695b78438e0c9dc50b649a539d0e3.zip
opensim-SC-83e2fee71be695b78438e0c9dc50b649a539d0e3.tar.gz
opensim-SC-83e2fee71be695b78438e0c9dc50b649a539d0e3.tar.bz2
opensim-SC-83e2fee71be695b78438e0c9dc50b649a539d0e3.tar.xz
add experimental script engine XMRengine donated by mrieker (DreamNation) And our Melanie. ***DANGER*** ***TESTONLY*** ***disable HG*** dont leave running when not looking... tp/crossing to Xengine will reset scripts. i do see a few issues but should be testable, so we can decide if we should invest more on it.
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs6262
1 files changed, 6262 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs
new file mode 100644
index 0000000..5219fa8
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/XMREngine/MMRScriptCodeGen.cs
@@ -0,0 +1,6262 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using OpenSim.Region.ScriptEngine.Shared.ScriptBase;
29using OpenSim.Region.ScriptEngine.XMREngine;
30using System;
31using System.Collections.Generic;
32using System.IO;
33using System.Reflection;
34using System.Reflection.Emit;
35using System.Runtime.Serialization;
36using System.Text;
37using System.Threading;
38
39using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
40using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
41using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
42using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
43using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
44using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
45using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
46
47/**
48 * @brief translate a reduced script token into corresponding CIL code.
49 * The single script token contains a tokenized and textured version of the whole script file.
50 */
51
52namespace OpenSim.Region.ScriptEngine.XMREngine
53{
54 public interface IScriptCodeGen
55 {
56 ScriptMyILGen ilGen { get; } // the output instruction stream
57 void ErrorMsg (Token token, string message);
58 void PushDefaultValue (TokenType type);
59 void PushXMRInst ();
60 }
61
62 public class ScriptCodeGen : IScriptCodeGen
63 {
64 private static readonly bool DEBUG_STACKCAPRES = false;
65 private static readonly bool DEBUG_TRYSTMT = false;
66
67 public static readonly string OBJECT_CODE_MAGIC = "XMRObjectCode";
68 public static int COMPILED_VERSION_VALUE = 20; // incremented when compiler changes for compatibility testing
69
70 public static readonly int CALL_FRAME_MEMUSE = 64;
71 public static readonly int STRING_LEN_TO_MEMUSE = 2;
72
73 public static Type xmrInstSuperType = null; // typeof whatever is actually malloc'd for script instances
74 // - must inherit from XMRInstAbstract
75
76 /*
77 * Static tables that there only needs to be one copy of for all.
78 */
79 private static VarDict legalEventHandlers = CreateLegalEventHandlers ();
80 private static CompValu[] zeroCompValus = new CompValu[0];
81 private static TokenType[] zeroArgs = new TokenType[0];
82 private static TokenTypeBool tokenTypeBool = new TokenTypeBool (null);
83 private static TokenTypeExc tokenTypeExc = new TokenTypeExc (null);
84 private static TokenTypeFloat tokenTypeFlt = new TokenTypeFloat (null);
85 private static TokenTypeInt tokenTypeInt = new TokenTypeInt (null);
86 private static TokenTypeObject tokenTypeObj = new TokenTypeObject (null);
87 private static TokenTypeRot tokenTypeRot = new TokenTypeRot (null);
88 private static TokenTypeStr tokenTypeStr = new TokenTypeStr (null);
89 private static TokenTypeVec tokenTypeVec = new TokenTypeVec (null);
90 private static Type[] instanceTypeArg = new Type[] { typeof (XMRInstAbstract) };
91 private static string[] instanceNameArg = new string[] { "$xmrthis" };
92
93 private static ConstructorInfo lslFloatConstructorInfo = typeof (LSL_Float).GetConstructor (new Type[] { typeof (double) });
94 private static ConstructorInfo lslIntegerConstructorInfo = typeof (LSL_Integer).GetConstructor (new Type[] { typeof (int) });
95 private static ConstructorInfo lslListConstructorInfo = typeof (LSL_List).GetConstructor (new Type[] { typeof (object[]) });
96 public static ConstructorInfo lslRotationConstructorInfo = typeof (LSL_Rotation).GetConstructor (new Type[] { typeof (double), typeof (double), typeof (double), typeof (double) });
97 private static ConstructorInfo lslStringConstructorInfo = typeof (LSL_String).GetConstructor (new Type[] { typeof (string) });
98 public static ConstructorInfo lslVectorConstructorInfo = typeof (LSL_Vector).GetConstructor (new Type[] { typeof (double), typeof (double), typeof (double) });
99 private static ConstructorInfo scriptBadCallNoExceptionConstructorInfo = typeof (ScriptBadCallNoException).GetConstructor (new Type[] { typeof (int) });
100 private static ConstructorInfo scriptChangeStateExceptionConstructorInfo = typeof (ScriptChangeStateException).GetConstructor (new Type[] { typeof (int) });
101 private static ConstructorInfo scriptRestoreCatchExceptionConstructorInfo = typeof (ScriptRestoreCatchException).GetConstructor (new Type[] { typeof (Exception) });
102 private static ConstructorInfo scriptUndefinedStateExceptionConstructorInfo = typeof (ScriptUndefinedStateException).GetConstructor (new Type[] { typeof (string) });
103 private static ConstructorInfo sdtClassConstructorInfo = typeof (XMRSDTypeClObj).GetConstructor (new Type[] { typeof (XMRInstAbstract), typeof (int) });
104 private static ConstructorInfo xmrArrayConstructorInfo = typeof (XMR_Array).GetConstructor (new Type[] { typeof (XMRInstAbstract) });
105 private static FieldInfo callModeFieldInfo = typeof (XMRInstAbstract).GetField ("callMode");
106 private static FieldInfo doGblInitFieldInfo = typeof (XMRInstAbstract).GetField ("doGblInit");
107 private static FieldInfo ehArgsFieldInfo = typeof (XMRInstAbstract).GetField ("ehArgs");
108 private static FieldInfo rotationXFieldInfo = typeof (LSL_Rotation).GetField ("x");
109 private static FieldInfo rotationYFieldInfo = typeof (LSL_Rotation).GetField ("y");
110 private static FieldInfo rotationZFieldInfo = typeof (LSL_Rotation).GetField ("z");
111 private static FieldInfo rotationSFieldInfo = typeof (LSL_Rotation).GetField ("s");
112 private static FieldInfo sdtXMRInstFieldInfo = typeof (XMRSDTypeClObj).GetField ("xmrInst");
113 private static FieldInfo vectorXFieldInfo = typeof (LSL_Vector).GetField ("x");
114 private static FieldInfo vectorYFieldInfo = typeof (LSL_Vector).GetField ("y");
115 private static FieldInfo vectorZFieldInfo = typeof (LSL_Vector).GetField ("z");
116
117 private static MethodInfo arrayClearMethodInfo = typeof (XMR_Array).GetMethod ("__pub_clear", new Type[] { });
118 private static MethodInfo arrayCountMethodInfo = typeof (XMR_Array).GetMethod ("__pub_count", new Type[] { });
119 private static MethodInfo arrayIndexMethodInfo = typeof (XMR_Array).GetMethod ("__pub_index", new Type[] { typeof (int) });
120 private static MethodInfo arrayValueMethodInfo = typeof (XMR_Array).GetMethod ("__pub_value", new Type[] { typeof (int) });
121 private static MethodInfo checkRunStackMethInfo = typeof (XMRInstAbstract).GetMethod ("CheckRunStack", new Type[] { });
122 private static MethodInfo checkRunQuickMethInfo = typeof (XMRInstAbstract).GetMethod ("CheckRunQuick", new Type[] { });
123 private static MethodInfo ehArgUnwrapFloat = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapFloat", new Type[] { typeof (object) });
124 private static MethodInfo ehArgUnwrapInteger = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapInteger", new Type[] { typeof (object) });
125 private static MethodInfo ehArgUnwrapRotation = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapRotation", new Type[] { typeof (object) });
126 private static MethodInfo ehArgUnwrapString = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapString", new Type[] { typeof (object) });
127 private static MethodInfo ehArgUnwrapVector = GetStaticMethod (typeof (TypeCast), "EHArgUnwrapVector", new Type[] { typeof (object) });
128 private static MethodInfo xmrArrPubIndexMethod = typeof (XMR_Array).GetMethod ("__pub_index", new Type[] { typeof (int) });
129 private static MethodInfo xmrArrPubValueMethod = typeof (XMR_Array).GetMethod ("__pub_value", new Type[] { typeof (int) });
130 private static MethodInfo captureStackFrameMethodInfo = typeof (XMRInstAbstract).GetMethod ("CaptureStackFrame", new Type[] { typeof (string), typeof (int), typeof (int) });
131 private static MethodInfo restoreStackFrameMethodInfo = typeof (XMRInstAbstract).GetMethod ("RestoreStackFrame", new Type[] { typeof (string), typeof (int).MakeByRefType () });
132 private static MethodInfo stringCompareMethodInfo = GetStaticMethod (typeof (String), "Compare", new Type[] { typeof (string), typeof (string), typeof (StringComparison) });
133 private static MethodInfo stringConcat2MethodInfo = GetStaticMethod (typeof (String), "Concat", new Type[] { typeof (string), typeof (string) });
134 private static MethodInfo stringConcat3MethodInfo = GetStaticMethod (typeof (String), "Concat", new Type[] { typeof (string), typeof (string), typeof (string) });
135 private static MethodInfo stringConcat4MethodInfo = GetStaticMethod (typeof (String), "Concat", new Type[] { typeof (string), typeof (string), typeof (string), typeof (string) });
136 private static MethodInfo lslRotationNegateMethodInfo = GetStaticMethod (typeof (ScriptCodeGen),
137 "LSLRotationNegate",
138 new Type[] { typeof (LSL_Rotation) });
139 private static MethodInfo lslVectorNegateMethodInfo = GetStaticMethod (typeof (ScriptCodeGen),
140 "LSLVectorNegate",
141 new Type[] { typeof (LSL_Vector) });
142 private static MethodInfo scriptRestoreCatchExceptionUnwrap = GetStaticMethod (typeof (ScriptRestoreCatchException), "Unwrap", new Type[] { typeof (Exception) });
143 private static MethodInfo thrownExceptionWrapMethodInfo = GetStaticMethod (typeof (ScriptThrownException), "Wrap", new Type[] { typeof (object) });
144 private static MethodInfo heapTrackerListPush = typeof (HeapTrackerList). GetMethod ("Push", new Type[0]);
145 private static MethodInfo heapTrackerObjectPush = typeof (HeapTrackerObject).GetMethod ("Push", new Type[0]);
146 private static MethodInfo heapTrackerStringPush = typeof (HeapTrackerString).GetMethod ("Push", new Type[0]);
147
148 private static MethodInfo catchExcToStrMethodInfo = GetStaticMethod (typeof (ScriptCodeGen),
149 "CatchExcToStr",
150 new Type[] { typeof (Exception) });
151
152 private static MethodInfo consoleWriteMethodInfo = GetStaticMethod (typeof (ScriptCodeGen), "ConsoleWrite", new Type[] { typeof (object) });
153 public static void ConsoleWrite (object o)
154 {
155 if (o == null) o = "<<null>>";
156 Console.Write (o.ToString ());
157 }
158
159 public static bool CodeGen (TokenScript tokenScript, BinaryWriter objFileWriter, string sourceHash)
160 {
161 /*
162 * Run compiler such that it has a 'this' context for convenience.
163 */
164 ScriptCodeGen scg = new ScriptCodeGen (tokenScript, objFileWriter, sourceHash);
165
166 /*
167 * Return pointer to resultant script object code.
168 */
169 return !scg.youveAnError;
170 }
171
172 /*
173 * There is one set of these variables for each script being compiled.
174 */
175 private bool mightGetHere = false;
176 private bool youveAnError = false;
177 private BreakContTarg curBreakTarg = null;
178 private BreakContTarg curContTarg = null;
179 private int lastErrorLine = 0;
180 private int nStates = 0;
181 private string sourceHash;
182 private string lastErrorFile = "";
183 private string[] stateNames;
184 private XMRInstArSizes glblSizes = new XMRInstArSizes ();
185 private Token errorMessageToken = null;
186 private TokenDeclVar curDeclFunc = null;
187 private TokenStmtBlock curStmtBlock = null;
188 private BinaryWriter objFileWriter = null;
189 private TokenScript tokenScript = null;
190 public int tempCompValuNum = 0;
191 private TokenDeclSDTypeClass currentSDTClass = null;
192
193 private Dictionary<string, int> stateIndices = null;
194
195 // These get cleared at beginning of every function definition
196 private ScriptMyLocal instancePointer; // holds XMRInstanceSuperType pointer
197 private ScriptMyLabel retLabel = null; // where to jump to exit function
198 private ScriptMyLocal retValue = null;
199 private ScriptMyLocal actCallNo = null; // for the active try/catch/finally stack or the big one outside them all
200 private LinkedList<CallLabel> actCallLabels = new LinkedList<CallLabel> (); // for the active try/catch/finally stack or the big one outside them all
201 private LinkedList<CallLabel> allCallLabels = new LinkedList<CallLabel> (); // this holds each and every one for all stacks in total
202 public CallLabel openCallLabel = null; // only one call label can be open at a time
203 // - the call label is open from the time of CallPre() until corresponding CallPost()
204 // - so no non-trivial pushes/pops etc allowed between a CallPre() and a CallPost()
205
206 private ScriptMyILGen _ilGen;
207 public ScriptMyILGen ilGen { get { return _ilGen; } }
208
209 private ScriptCodeGen (TokenScript tokenScript, BinaryWriter objFileWriter, string sourceHash)
210 {
211 this.tokenScript = tokenScript;
212 this.objFileWriter = objFileWriter;
213 this.sourceHash = sourceHash;
214
215 try {
216 PerformCompilation ();
217 } catch {
218 // if we've an error, just punt on any exception
219 // it's probably just a null reference from something
220 // not being filled in etc.
221 if (!youveAnError) throw;
222 } finally {
223 objFileWriter = null;
224 }
225 }
226
227 /**
228 * @brief Convert 'tokenScript' to 'objFileWriter' format.
229 * 'tokenScript' is a parsed/reduced abstract syntax tree of the script source file
230 * 'objFileWriter' is a serialized form of the CIL code that we generate
231 */
232 private void PerformCompilation ()
233 {
234 /*
235 * errorMessageToken is used only when the given token doesn't have a
236 * output delegate associated with it such as for backend API functions
237 * that only have one copy for the whole system. It is kept up-to-date
238 * approximately but is rarely needed so going to assume it doesn't have
239 * to be exact.
240 */
241 errorMessageToken = tokenScript;
242
243 /*
244 * Set up dictionary to translate state names to their index number.
245 */
246 stateIndices = new Dictionary<string, int> ();
247
248 /*
249 * Assign each state its own unique index.
250 * The default state gets 0.
251 */
252 nStates = 0;
253 tokenScript.defaultState.body.index = nStates ++;
254 stateIndices.Add ("default", 0);
255 foreach (KeyValuePair<string, TokenDeclState> kvp in tokenScript.states) {
256 TokenDeclState declState = kvp.Value;
257 declState.body.index = nStates ++;
258 stateIndices.Add (declState.name.val, declState.body.index);
259 }
260
261 /*
262 * Make up an array that translates state indices to state name strings.
263 */
264 stateNames = new string[nStates];
265 stateNames[0] = "default";
266 foreach (KeyValuePair<string, TokenDeclState> kvp in tokenScript.states) {
267 TokenDeclState declState = kvp.Value;
268 stateNames[declState.body.index] = declState.name.val;
269 }
270
271 /*
272 * Make sure we have delegates for all script-defined functions and methods,
273 * creating anonymous ones if needed. Note that this includes all property
274 * getter and setter methods.
275 */
276 foreach (TokenDeclVar declFunc in tokenScript.variablesStack) {
277 if (declFunc.retType != null) {
278 declFunc.GetDelType ();
279 }
280 }
281 while (true) {
282 bool itIsAGoodDayToDie = true;
283 try {
284 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
285 itIsAGoodDayToDie = false;
286 if (sdType is TokenDeclSDTypeClass) {
287 TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
288 foreach (TokenDeclVar declFunc in sdtClass.members) {
289 if (declFunc.retType != null) {
290 declFunc.GetDelType ();
291 if (declFunc.funcNameSig.val.StartsWith ("$ctor(")) {
292 // this is for the "$new()" static method that we create below.
293 // See GenerateStmtNewobj() etc.
294 new TokenTypeSDTypeDelegate (declFunc, sdtClass.MakeRefToken (declFunc),
295 declFunc.argDecl.types, tokenScript);
296 }
297 }
298 }
299 }
300 if (sdType is TokenDeclSDTypeInterface) {
301 TokenDeclSDTypeInterface sdtIFace = (TokenDeclSDTypeInterface)sdType;
302 foreach (TokenDeclVar declFunc in sdtIFace.methsNProps) {
303 if (declFunc.retType != null) {
304 declFunc.GetDelType ();
305 }
306 }
307 }
308 itIsAGoodDayToDie = true;
309 }
310 break;
311 } catch (InvalidOperationException) {
312 if (!itIsAGoodDayToDie) throw;
313 // fetching the delegate created an anonymous entry in tokenScript.sdSrcTypesValues
314 // which made the foreach statement puque, so start over...
315 }
316 }
317
318 /*
319 * No more types can be defined or we won't be able to write them to the object file.
320 */
321 tokenScript.sdSrcTypesSeal ();
322
323 /*
324 * Assign all global variables a slot in its corresponding XMRInstance.gbl<Type>s[] array.
325 * Global variables are simply elements of those arrays at runtime, thus we don't need to create
326 * an unique class for each script, we can just use XMRInstance as is for all.
327 */
328 foreach (TokenDeclVar declVar in tokenScript.variablesStack) {
329
330 /*
331 * Omit 'constant' variables as they are coded inline so don't need a slot.
332 */
333 if (declVar.constant) continue;
334
335 /*
336 * Do functions later.
337 */
338 if (declVar.retType != null) continue;
339
340 /*
341 * Create entry in the value array for the variable or property.
342 */
343 declVar.location = new CompValuGlobalVar (declVar, glblSizes);
344 }
345
346 /*
347 * Likewise for any static fields in script-defined classes.
348 * They can be referenced anywhere by <typename>.<fieldname>, see
349 * GenerateFromLValSField().
350 */
351 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
352 if (!(sdType is TokenDeclSDTypeClass)) continue;
353 TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
354
355 foreach (TokenDeclVar declVar in sdtClass.members) {
356
357 /*
358 * Omit 'constant' variables as they are coded inline so don't need a slot.
359 */
360 if (declVar.constant) continue;
361
362 /*
363 * Do methods later.
364 */
365 if (declVar.retType != null) continue;
366
367 /*
368 * Ignore non-static fields for now.
369 * They get assigned below.
370 */
371 if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) == 0) continue;
372
373 /*
374 * Create entry in the value array for the static field or static property.
375 */
376 declVar.location = new CompValuGlobalVar (declVar, glblSizes);
377 }
378 }
379
380 /*
381 * Assign slots for all interface method prototypes.
382 * These indices are used to index the array of delegates that holds a class' implementation of an
383 * interface.
384 * Properties do not get a slot because they aren't called as such. But their corresponding
385 * <name>$get() and <name>$set(<type>) methods are in the table and they each get a slot.
386 */
387 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
388 if (!(sdType is TokenDeclSDTypeInterface)) continue;
389 TokenDeclSDTypeInterface sdtIFace = (TokenDeclSDTypeInterface)sdType;
390 int vti = 0;
391 foreach (TokenDeclVar im in sdtIFace.methsNProps) {
392 if ((im.getProp == null) && (im.setProp == null)) {
393 im.vTableIndex = vti ++;
394 }
395 }
396 }
397
398 /*
399 * Assign slots for all instance fields and virtual methods of script-defined classes.
400 */
401 int maxExtends = tokenScript.sdSrcTypesCount;
402 bool didOne;
403 do {
404 didOne = false;
405 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
406 if (!(sdType is TokenDeclSDTypeClass)) continue;
407 TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
408 if (sdtClass.slotsAssigned) continue;
409
410 /*
411 * If this class extends another, the extended class has to already
412 * be set up, because our slots add on to the end of the extended class.
413 */
414 TokenDeclSDTypeClass extends = sdtClass.extends;
415 if (extends != null) {
416 if (!extends.slotsAssigned) continue;
417 sdtClass.instSizes = extends.instSizes;
418 sdtClass.numVirtFuncs = extends.numVirtFuncs;
419 sdtClass.numInterfaces = extends.numInterfaces;
420
421 int n = maxExtends;
422 for (TokenDeclSDTypeClass ex = extends; ex != null; ex = ex.extends) {
423 if (-- n < 0) break;
424 }
425 if (n < 0) {
426 ErrorMsg (sdtClass, "loop in extended classes");
427 sdtClass.slotsAssigned = true;
428 continue;
429 }
430 }
431
432 /*
433 * Extended class's slots all assigned, assign our instance fields
434 * slots in the XMRSDTypeClObj arrays.
435 */
436 foreach (TokenDeclVar declVar in sdtClass.members) {
437 if (declVar.retType != null) continue;
438 if (declVar.constant) continue;
439 if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) continue;
440 if ((declVar.getProp == null) && (declVar.setProp == null)) {
441 declVar.type.AssignVarSlot (declVar, sdtClass.instSizes);
442 }
443 }
444
445 /*
446 * ... and assign virtual method vtable slots.
447 *
448 * - : error if any overridden method, doesn't need a slot
449 * abstract : error if any overridden method, alloc new slot but leave it empty
450 * new : ignore any overridden method, doesn't need a slot
451 * new abstract : ignore any overridden method, alloc new slot but leave it empty
452 * override : must have overridden abstract/virtual, use old slot
453 * override abstract : must have overridden abstract, use old slot but it is still empty
454 * static : error if any overridden method, doesn't need a slot
455 * static new : ignore any overridden method, doesn't need a slot
456 * virtual : error if any overridden method, alloc new slot and fill it in
457 * virtual new : ignore any overridden method, alloc new slot and fill it in
458 */
459 foreach (TokenDeclVar declFunc in sdtClass.members) {
460 if (declFunc.retType == null) continue;
461 curDeclFunc = declFunc;
462
463 /*
464 * See if there is a method in an extended class that this method overshadows.
465 * If so, check for various conflicts.
466 * In any case, SDT_NEW on our method means to ignore any overshadowed method.
467 */
468 string declLongName = sdtClass.longName.val + "." + declFunc.funcNameSig.val;
469 uint declFlags = declFunc.sdtFlags;
470 TokenDeclVar overridden = null;
471 if ((declFlags & ScriptReduce.SDT_NEW) == 0) {
472 for (TokenDeclSDTypeClass sdtd = extends; sdtd != null; sdtd = sdtd.extends) {
473 overridden = FindExactWithRet (sdtd.members, declFunc.name, declFunc.retType, declFunc.argDecl.types);
474 if (overridden != null) break;
475 }
476 }
477 if (overridden != null) do {
478 string overLongName = overridden.sdtClass.longName.val;
479 uint overFlags = overridden.sdtFlags;
480
481 /*
482 * See if overridden method allows itself to be overridden.
483 */
484 if ((overFlags & ScriptReduce.SDT_ABSTRACT) != 0) {
485 if ((declFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_OVERRIDE)) == 0) {
486 ErrorMsg (declFunc, declLongName + " overshadows abstract " + overLongName + " but is not marked abstract, new or override");
487 break;
488 }
489 } else if ((overFlags & ScriptReduce.SDT_FINAL) != 0) {
490 ErrorMsg (declFunc, declLongName + " overshadows final " + overLongName + " but is not marked new");
491 } else if ((overFlags & (ScriptReduce.SDT_OVERRIDE | ScriptReduce.SDT_VIRTUAL)) != 0) {
492 if ((declFlags & (ScriptReduce.SDT_NEW | ScriptReduce.SDT_OVERRIDE)) == 0) {
493 ErrorMsg (declFunc, declLongName + " overshadows virtual " + overLongName + " but is not marked new or override");
494 break;
495 }
496 } else {
497 ErrorMsg (declFunc, declLongName + " overshadows non-virtual " + overLongName + " but is not marked new");
498 break;
499 }
500
501 /*
502 * See if our method is capable of overriding the other method.
503 */
504 if ((declFlags & ScriptReduce.SDT_ABSTRACT) != 0) {
505 if ((overFlags & ScriptReduce.SDT_ABSTRACT) == 0) {
506 ErrorMsg (declFunc, declLongName + " abstract overshadows non-abstract " + overLongName + " but is not marked new");
507 break;
508 }
509 } else if ((declFlags & ScriptReduce.SDT_OVERRIDE) != 0) {
510 if ((overFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_OVERRIDE | ScriptReduce.SDT_VIRTUAL)) == 0) {
511 ErrorMsg (declFunc, declLongName + " override overshadows non-abstract/non-virtual " + overLongName);
512 break;
513 }
514 } else {
515 ErrorMsg (declFunc, declLongName + " overshadows " + overLongName + " but is not marked new");
516 break;
517 }
518 } while (false);
519
520 /*
521 * Now we can assign it a vtable slot if it needs one (ie, it is virtual).
522 */
523 declFunc.vTableIndex = -1;
524 if (overridden != null) {
525 declFunc.vTableIndex = overridden.vTableIndex;
526 } else if ((declFlags & ScriptReduce.SDT_OVERRIDE) != 0) {
527 ErrorMsg (declFunc, declLongName + " marked override but nothing matching found that it overrides");
528 }
529 if ((declFlags & (ScriptReduce.SDT_ABSTRACT | ScriptReduce.SDT_VIRTUAL)) != 0) {
530 declFunc.vTableIndex = sdtClass.numVirtFuncs ++;
531 }
532 }
533 curDeclFunc = null;
534
535 /*
536 * ... and assign implemented interface slots.
537 * Note that our implementations of a given interface is completely independent of any
538 * rootward class's implementation of that same interface.
539 */
540 int nIFaces = sdtClass.numInterfaces + sdtClass.implements.Count;
541 sdtClass.iFaces = new TokenDeclSDTypeInterface[nIFaces];
542 sdtClass.iImplFunc = new TokenDeclVar[nIFaces][];
543 for (int i = 0; i < sdtClass.numInterfaces; i ++) {
544 sdtClass.iFaces[i] = extends.iFaces[i];
545 sdtClass.iImplFunc[i] = extends.iImplFunc[i];
546 }
547
548 foreach (TokenDeclSDTypeInterface intf in sdtClass.implements) {
549 int i = sdtClass.numInterfaces ++;
550 sdtClass.iFaces[i] = intf;
551 sdtClass.intfIndices.Add (intf.longName.val, i);
552 int nMeths = 0;
553 foreach (TokenDeclVar m in intf.methsNProps) {
554 if ((m.getProp == null) && (m.setProp == null)) nMeths ++;
555 }
556 sdtClass.iImplFunc[i] = new TokenDeclVar[nMeths];
557 }
558
559 foreach (TokenDeclVar classMeth in sdtClass.members) {
560 if (classMeth.retType == null) continue;
561 curDeclFunc = classMeth;
562 for (TokenIntfImpl intfImpl = classMeth.implements; intfImpl != null; intfImpl = (TokenIntfImpl)intfImpl.nextToken) {
563
564 /*
565 * One of the class methods implements an interface method.
566 * Try to find the interface method that is implemented and verify its signature.
567 */
568 TokenDeclSDTypeInterface intfType = intfImpl.intfType.decl;
569 TokenDeclVar intfMeth = FindExactWithRet (intfType.methsNProps, intfImpl.methName, classMeth.retType, classMeth.argDecl.types);
570 if (intfMeth == null) {
571 ErrorMsg (intfImpl, "interface does not define method " + intfImpl.methName.val + classMeth.argDecl.GetArgSig ());
572 continue;
573 }
574
575 /*
576 * See if this class was declared to implement that interface.
577 */
578 bool found = false;
579 foreach (TokenDeclSDTypeInterface intf in sdtClass.implements) {
580 if (intf == intfType) {
581 found = true;
582 break;
583 }
584 }
585 if (!found) {
586 ErrorMsg (intfImpl, "class not declared to implement " + intfType.longName.val);
587 continue;
588 }
589
590 /*
591 * Get index in iFaces[] and iImplFunc[] arrays.
592 * Start scanning from the end in case one of our rootward classes also implements the interface.
593 * We should always be successful because we know by now that this class implements the interface.
594 */
595 int i;
596 for (i = sdtClass.numInterfaces; -- i >= 0;) {
597 if (sdtClass.iFaces[i] == intfType) break;
598 }
599
600 /*
601 * Now remember which of the class methods implements that interface method.
602 */
603 int j = intfMeth.vTableIndex;
604 if (sdtClass.iImplFunc[i][j] != null) {
605 ErrorMsg (intfImpl, "also implemented by " + sdtClass.iImplFunc[i][j].funcNameSig.val);
606 continue;
607 }
608 sdtClass.iImplFunc[i][j] = classMeth;
609 }
610 }
611 curDeclFunc = null;
612
613 /*
614 * Now make sure this class implements all methods for all declared interfaces.
615 */
616 for (int i = sdtClass.numInterfaces - sdtClass.implements.Count; i < sdtClass.numInterfaces; i ++) {
617 TokenDeclVar[] implementations = sdtClass.iImplFunc[i];
618 for (int j = implementations.Length; -- j >= 0;) {
619 if (implementations[j] == null) {
620 TokenDeclSDTypeInterface intf = sdtClass.iFaces[i];
621 TokenDeclVar meth = null;
622 foreach (TokenDeclVar im in intf.methsNProps) {
623 if (im.vTableIndex == j) {
624 meth = im;
625 break;
626 }
627 }
628 ErrorMsg (sdtClass, "does not implement " + intf.longName.val + "." + meth.funcNameSig.val);
629 }
630 }
631 }
632
633 /*
634 * All slots for this class have been assigned.
635 */
636 sdtClass.slotsAssigned = true;
637 didOne = true;
638 }
639 } while (didOne);
640
641 /*
642 * Compute final values for all variables/fields declared as 'constant'.
643 * Note that there may be forward references.
644 */
645 do {
646 didOne = false;
647 foreach (TokenDeclVar tdv in tokenScript.variablesStack) {
648 if (tdv.constant && !(tdv.init is TokenRValConst)) {
649 tdv.init = tdv.init.TryComputeConstant (LookupInitConstants, ref didOne);
650 }
651 }
652 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
653 if (!(sdType is TokenDeclSDTypeClass)) continue;
654 currentSDTClass = (TokenDeclSDTypeClass)sdType;
655 foreach (TokenDeclVar tdv in currentSDTClass.members) {
656 if (tdv.constant && !(tdv.init is TokenRValConst)) {
657 tdv.init = tdv.init.TryComputeConstant (LookupInitConstants, ref didOne);
658 }
659 }
660 }
661 currentSDTClass = null;
662 } while (didOne);
663
664 /*
665 * Now we should be able to assign all those constants their type and location.
666 */
667 foreach (TokenDeclVar tdv in tokenScript.variablesStack) {
668 if (tdv.constant) {
669 if (tdv.init is TokenRValConst) {
670 TokenRValConst rvc = (TokenRValConst)tdv.init;
671 tdv.type = rvc.tokType;
672 tdv.location = rvc.GetCompValu ();
673 } else {
674 ErrorMsg (tdv, "value is not constant");
675 }
676 }
677 }
678 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
679 if (!(sdType is TokenDeclSDTypeClass)) continue;
680 currentSDTClass = (TokenDeclSDTypeClass)sdType;
681 foreach (TokenDeclVar tdv in currentSDTClass.members) {
682 if (tdv.constant) {
683 if (tdv.init is TokenRValConst) {
684 TokenRValConst rvc = (TokenRValConst)tdv.init;
685 tdv.type = rvc.tokType;
686 tdv.location = rvc.GetCompValu ();
687 } else {
688 ErrorMsg (tdv, "value is not constant");
689 }
690 }
691 }
692 }
693 currentSDTClass = null;
694
695 /*
696 * For all classes that define all the methods needed for the class, ie, they aren't abstract,
697 * define a static class.$new() method with same args as the $ctor(s). This will allow the
698 * class to be instantiated via the new operator.
699 */
700 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
701 if (!(sdType is TokenDeclSDTypeClass)) continue;
702 TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
703
704 /*
705 * See if the class as it stands would be able to fill every slot of its vtable.
706 */
707 bool[] filled = new bool[sdtClass.numVirtFuncs];
708 int numFilled = 0;
709 for (TokenDeclSDTypeClass sdtc = sdtClass; sdtc != null; sdtc = sdtc.extends) {
710 foreach (TokenDeclVar tdf in sdtc.members) {
711 if ((tdf.retType != null) && (tdf.vTableIndex >= 0) && ((tdf.sdtFlags & ScriptReduce.SDT_ABSTRACT) == 0)) {
712 if (!filled[tdf.vTableIndex]) {
713 filled[tdf.vTableIndex] = true;
714 numFilled ++;
715 }
716 }
717 }
718 }
719
720 /*
721 * If so, define a static class.$new() method for every constructor defined for the class.
722 * Give it the same access (private/protected/public) as the script declared for the constructor.
723 * Note that the reducer made sure there is at least a default constructor for every class.
724 */
725 if (numFilled >= sdtClass.numVirtFuncs) {
726 List<TokenDeclVar> newobjDeclFuncs = new List<TokenDeclVar> ();
727 foreach (TokenDeclVar ctorDeclFunc in sdtClass.members) {
728 if ((ctorDeclFunc.funcNameSig != null) && ctorDeclFunc.funcNameSig.val.StartsWith ("$ctor(")) {
729 TokenDeclVar newobjDeclFunc = DefineNewobjFunc (ctorDeclFunc);
730 newobjDeclFuncs.Add (newobjDeclFunc);
731 }
732 }
733 foreach (TokenDeclVar newobjDeclFunc in newobjDeclFuncs) {
734 sdtClass.members.AddEntry (newobjDeclFunc);
735 }
736 }
737 }
738
739 /*
740 * Write fixed portion of object file.
741 */
742 objFileWriter.Write (OBJECT_CODE_MAGIC.ToCharArray ());
743 objFileWriter.Write (COMPILED_VERSION_VALUE);
744 objFileWriter.Write (sourceHash);
745 objFileWriter.Write (tokenScript.expiryDays);
746 glblSizes.WriteToFile (objFileWriter);
747
748 objFileWriter.Write (nStates);
749 for (int i = 0; i < nStates; i ++) {
750 objFileWriter.Write (stateNames[i]);
751 }
752
753 /*
754 * For debugging, we also write out global variable array slot assignments.
755 */
756 foreach (TokenDeclVar declVar in tokenScript.variablesStack) {
757 if (declVar.retType == null) {
758 WriteOutGblAssignment ("", declVar);
759 }
760 }
761 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
762 if (!(sdType is TokenDeclSDTypeClass)) continue;
763 TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
764 foreach (TokenDeclVar declVar in sdtClass.members) {
765 if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) {
766 WriteOutGblAssignment (sdtClass.longName.val + ".", declVar);
767 }
768 }
769 }
770 objFileWriter.Write ("");
771
772 /*
773 * Write out script-defined types.
774 */
775 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
776 objFileWriter.Write (sdType.longName.val);
777 sdType.WriteToFile (objFileWriter);
778 }
779 objFileWriter.Write ("");
780
781 /*
782 * Output function headers then bodies.
783 * Do all headers first in case bodies do forward references.
784 * Do both global functions, script-defined class static methods and
785 * script-defined instance methods, as we handle the differences
786 * during compilation of the functions/methods themselves.
787 */
788 for (int pass = 0; pass < 2; pass ++) {
789 foreach (TokenDeclVar declFunc in tokenScript.variablesStack) {
790 if (declFunc.retType != null) {
791 if (pass == 0) GenerateMethodHeader (declFunc);
792 else GenerateMethodBody (declFunc);
793 }
794 }
795 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
796 if (sdType is TokenDeclSDTypeClass) {
797 TokenDeclSDTypeClass sdtClass = (TokenDeclSDTypeClass)sdType;
798 foreach (TokenDeclVar declFunc in sdtClass.members) {
799 if ((declFunc.retType != null) && ((declFunc.sdtFlags & ScriptReduce.SDT_ABSTRACT) == 0)) {
800 if (pass == 0) GenerateMethodHeader (declFunc);
801 else GenerateMethodBody (declFunc);
802 }
803 }
804 }
805 }
806 }
807
808 /*
809 * Output default state event handler functions.
810 * Each event handler is a private static method named 'default <eventname>'.
811 * Splice in a default state_entry() handler if none defined so we can init global vars.
812 */
813 TokenDeclVar defaultStateEntry = null;
814 for (defaultStateEntry = tokenScript.defaultState.body.eventFuncs;
815 defaultStateEntry != null;
816 defaultStateEntry = (TokenDeclVar)defaultStateEntry.nextToken) {
817 if (defaultStateEntry.funcNameSig.val == "state_entry()") break;
818 }
819 if (defaultStateEntry == null) {
820 defaultStateEntry = new TokenDeclVar (tokenScript.defaultState.body, null, tokenScript);
821 defaultStateEntry.name = new TokenName (tokenScript.defaultState.body, "state_entry");
822 defaultStateEntry.retType = new TokenTypeVoid (tokenScript.defaultState.body);
823 defaultStateEntry.argDecl = new TokenArgDecl (tokenScript.defaultState.body);
824 defaultStateEntry.body = new TokenStmtBlock (tokenScript.defaultState.body);
825 defaultStateEntry.body.function = defaultStateEntry;
826
827 defaultStateEntry.nextToken = tokenScript.defaultState.body.eventFuncs;
828 tokenScript.defaultState.body.eventFuncs = defaultStateEntry;
829 }
830 GenerateStateEventHandlers ("default", tokenScript.defaultState.body);
831
832 /*
833 * Output script-defined state event handler methods.
834 * Each event handler is a private static method named <statename> <eventname>
835 */
836 foreach (KeyValuePair<string, TokenDeclState> kvp in tokenScript.states) {
837 TokenDeclState declState = kvp.Value;
838 GenerateStateEventHandlers (declState.name.val, declState.body);
839 }
840
841 ScriptObjWriter.TheEnd (objFileWriter);
842 }
843
844 /**
845 * @brief Write out what slot was assigned for a global or sdtclass static variable.
846 * Constants, functions, instance fields, methods, properties do not have slots in the global variables arrays.
847 */
848 private void WriteOutGblAssignment (string pfx, TokenDeclVar declVar)
849 {
850 if (!declVar.constant && (declVar.retType == null) && (declVar.getProp == null) && (declVar.setProp == null)) {
851 objFileWriter.Write (pfx + declVar.name.val); // string
852 objFileWriter.Write (declVar.vTableArray.Name); // string
853 objFileWriter.Write (declVar.vTableIndex); // int
854 }
855 }
856
857 /**
858 * @brief generate event handler code
859 * Writes out a function definition for each state handler
860 * named <statename> <eventname>
861 *
862 * However, each has just 'XMRInstance __sw' as its single argument
863 * and each of its user-visible argments is extracted from __sw.ehArgs[].
864 *
865 * So we end up generating something like this:
866 *
867 * private static void <statename> <eventname>(XMRInstance __sw)
868 * {
869 * <typeArg0> <nameArg0> = (<typeArg0>)__sw.ehArgs[0];
870 * <typeArg1> <nameArg1> = (<typeArg1>)__sw.ehArgs[1];
871 *
872 * ... script code ...
873 * }
874 *
875 * The continuations code assumes there will be no references to ehArgs[]
876 * after the first call to CheckRun() as CheckRun() makes no attempt to
877 * serialize the ehArgs[] array, as doing so would be redundant. Any values
878 * from ehArgs[] that are being used will be in local stack variables and
879 * thus preserved that way.
880 */
881 private void GenerateStateEventHandlers (string statename, TokenStateBody body)
882 {
883 Dictionary<string,TokenDeclVar> statehandlers = new Dictionary<string,TokenDeclVar> ();
884 for (Token t = body.eventFuncs; t != null; t = t.nextToken) {
885 TokenDeclVar tdv = (TokenDeclVar)t;
886 string eventname = tdv.GetSimpleName ();
887 if (statehandlers.ContainsKey (eventname)) {
888 ErrorMsg (tdv, "event handler " + eventname + " already defined for state " + statename);
889 } else {
890 statehandlers.Add (eventname, tdv);
891 GenerateEventHandler (statename, tdv);
892 }
893 }
894 }
895
896 private void GenerateEventHandler (string statename, TokenDeclVar declFunc)
897 {
898 string eventname = declFunc.GetSimpleName ();
899 TokenArgDecl argDecl = declFunc.argDecl;
900
901 /*
902 * Make sure event handler name is valid and that number and type of arguments is correct.
903 * Apparently some scripts exist with fewer than correct number of args in their declaration
904 * so allow for that. It is ok because the handlers are called with the arguments in an
905 * object[] array, and we just won't access the missing argments in the vector. But the
906 * specified types must match one of the prototypes in legalEventHandlers.
907 */
908 TokenDeclVar protoDeclFunc = legalEventHandlers.FindExact (eventname, argDecl.types);
909 if (protoDeclFunc == null) {
910 ErrorMsg (declFunc, "unknown event handler " + eventname + argDecl.GetArgSig ());
911 return;
912 }
913
914 /*
915 * Output function header.
916 * They just have the XMRInstAbstract pointer as the one argument.
917 */
918 string functionName = statename + " " + eventname;
919 _ilGen = new ScriptObjWriter (tokenScript,
920 functionName,
921 typeof (void),
922 instanceTypeArg,
923 instanceNameArg,
924 objFileWriter);
925 StartFunctionBody (declFunc);
926
927 /*
928 * Create a temp to hold XMRInstanceSuperType version of arg 0.
929 */
930 instancePointer = ilGen.DeclareLocal (xmrInstSuperType, "__xmrinst");
931 ilGen.Emit (declFunc, OpCodes.Ldarg_0);
932 ilGen.Emit (declFunc, OpCodes.Castclass, xmrInstSuperType);
933 ilGen.Emit (declFunc, OpCodes.Stloc, instancePointer);
934
935 /*
936 * Output args as variable definitions and initialize each from __sw.ehArgs[].
937 * If the script writer goofed, the typecast will complain.
938 */
939 int nArgs = argDecl.vars.Length;
940 for (int i = 0; i < nArgs; i ++) {
941
942 /*
943 * Say that the argument variable is going to be located in a local var.
944 */
945 TokenDeclVar argVar = argDecl.vars[i];
946 TokenType argTokType = argVar.type;
947 CompValuLocalVar local = new CompValuLocalVar (argTokType, argVar.name.val, this);
948 argVar.location = local;
949
950 /*
951 * Copy from the ehArgs[i] element to the temp var.
952 * Cast as needed, there is a lot of craziness like OpenMetaverse.Quaternion.
953 */
954 local.PopPre (this, argVar.name);
955 PushXMRInst (); // instance
956 ilGen.Emit (declFunc, OpCodes.Ldfld, ehArgsFieldInfo); // instance.ehArgs (array of objects)
957 ilGen.Emit (declFunc, OpCodes.Ldc_I4, i); // array index = i
958 ilGen.Emit (declFunc, OpCodes.Ldelem, typeof (object)); // select the argument we want
959 TokenType stkTokType = tokenTypeObj; // stack has a type 'object' on it now
960 Type argSysType = argTokType.ToSysType (); // this is the type the script expects
961 if (argSysType == typeof (double)) { // LSL_Float/double -> double
962 ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapFloat);
963 stkTokType = tokenTypeFlt; // stack has a type 'double' on it now
964 }
965 if (argSysType == typeof (int)) { // LSL_Integer/int -> int
966 ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapInteger);
967 stkTokType = tokenTypeInt; // stack has a type 'int' on it now
968 }
969 if (argSysType == typeof (LSL_List)) { // LSL_List -> LSL_List
970 TypeCast.CastTopOfStack (this, argVar.name, stkTokType, argTokType, true);
971 stkTokType = argTokType; // stack has a type 'LSL_List' on it now
972 }
973 if (argSysType == typeof (LSL_Rotation)) { // OpenMetaverse.Quaternion/LSL_Rotation -> LSL_Rotation
974 ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapRotation);
975 stkTokType = tokenTypeRot; // stack has a type 'LSL_Rotation' on it now
976 }
977 if (argSysType == typeof (string)) { // LSL_Key/LSL_String/string -> string
978 ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapString);
979 stkTokType = tokenTypeStr; // stack has a type 'string' on it now
980 }
981 if (argSysType == typeof (LSL_Vector)) { // OpenMetaverse.Vector3/LSL_Vector -> LSL_Vector
982 ilGen.Emit (declFunc, OpCodes.Call, ehArgUnwrapVector);
983 stkTokType = tokenTypeVec; // stack has a type 'LSL_Vector' on it now
984 }
985 local.PopPost (this, argVar.name, stkTokType); // pop stack type into argtype
986 }
987
988 /*
989 * Output code for the statements and clean up.
990 */
991 GenerateFuncBody ();
992 }
993
994 /**
995 * @brief generate header for an arbitrary script-defined global function.
996 * @param declFunc = function being defined
997 */
998 private void GenerateMethodHeader (TokenDeclVar declFunc)
999 {
1000 curDeclFunc = declFunc;
1001
1002 /*
1003 * Make up array of all argument types as seen by the code generator.
1004 * We splice in XMRInstanceSuperType or XMRSDTypeClObj for the first
1005 * arg as the function itself is static, followed by script-visible
1006 * arg types.
1007 */
1008 TokenArgDecl argDecl = declFunc.argDecl;
1009 int nArgs = argDecl.vars.Length;
1010 Type[] argTypes = new Type[nArgs+1];
1011 string[] argNames = new string[nArgs+1];
1012 if (IsSDTInstMethod ()) {
1013 argTypes[0] = typeof (XMRSDTypeClObj);
1014 argNames[0] = "$sdtthis";
1015 } else {
1016 argTypes[0] = xmrInstSuperType;
1017 argNames[0] = "$xmrthis";
1018 }
1019 for (int i = 0; i < nArgs; i ++) {
1020 argTypes[i+1] = argDecl.vars[i].type.ToSysType ();
1021 argNames[i+1] = argDecl.vars[i].name.val;
1022 }
1023
1024 /*
1025 * Set up entrypoint.
1026 */
1027 string objCodeName = declFunc.GetObjCodeName ();
1028 declFunc.ilGen = new ScriptObjWriter (tokenScript,
1029 objCodeName,
1030 declFunc.retType.ToSysType (),
1031 argTypes,
1032 argNames,
1033 objFileWriter);
1034
1035 /*
1036 * This says how to generate a call to the function and to get a delegate.
1037 */
1038 declFunc.location = new CompValuGlobalMeth (declFunc);
1039
1040 curDeclFunc = null;
1041 }
1042
1043 /**
1044 * @brief generate code for an arbitrary script-defined function.
1045 * @param name = name of the function
1046 * @param argDecl = argument declarations
1047 * @param body = function's code body
1048 */
1049 private void GenerateMethodBody (TokenDeclVar declFunc)
1050 {
1051 /*
1052 * Set up code generator for the function's contents.
1053 */
1054 _ilGen = declFunc.ilGen;
1055 StartFunctionBody (declFunc);
1056
1057 /*
1058 * Create a temp to hold XMRInstanceSuperType version of arg 0.
1059 * For most functions, arg 0 is already XMRInstanceSuperType.
1060 * But for script-defined class instance methods, arg 0 holds
1061 * the XMRSDTypeClObj pointer and so we read the XMRInstAbstract
1062 * pointer from its XMRSDTypeClObj.xmrInst field then cast it to
1063 * XMRInstanceSuperType.
1064 */
1065 if (IsSDTInstMethod ()) {
1066 instancePointer = ilGen.DeclareLocal (xmrInstSuperType, "__xmrinst");
1067 ilGen.Emit (declFunc, OpCodes.Ldarg_0);
1068 ilGen.Emit (declFunc, OpCodes.Ldfld, sdtXMRInstFieldInfo);
1069 ilGen.Emit (declFunc, OpCodes.Castclass, xmrInstSuperType);
1070 ilGen.Emit (declFunc, OpCodes.Stloc, instancePointer);
1071 }
1072
1073 /*
1074 * Define location of all script-level arguments so script body can access them.
1075 * The argument indices need to have +1 added to them because XMRInstance or
1076 * XMRSDTypeClObj is spliced in at arg 0.
1077 */
1078 TokenArgDecl argDecl = declFunc.argDecl;
1079 int nArgs = argDecl.vars.Length;
1080 for (int i = 0; i < nArgs; i ++) {
1081 TokenDeclVar argVar = argDecl.vars[i];
1082 argVar.location = new CompValuArg (argVar.type, i + 1);
1083 }
1084
1085 /*
1086 * Output code for the statements and clean up.
1087 */
1088 GenerateFuncBody ();
1089 }
1090
1091 private void StartFunctionBody (TokenDeclVar declFunc)
1092 {
1093 /*
1094 * Start current function being processed.
1095 * Set 'mightGetHere' as the code at the top is always executed.
1096 */
1097 instancePointer = null;
1098 mightGetHere = true;
1099 curBreakTarg = null;
1100 curContTarg = null;
1101 curDeclFunc = declFunc;
1102
1103 /*
1104 * Start generating code.
1105 */
1106 ((ScriptObjWriter)ilGen).BegMethod ();
1107 }
1108
1109 /**
1110 * @brief Define function for a script-defined type's <typename>.$new(<argsig>) method.
1111 * See GenerateStmtNewobj() for more info.
1112 */
1113 private TokenDeclVar DefineNewobjFunc (TokenDeclVar ctorDeclFunc)
1114 {
1115 /*
1116 * Set up 'static classname $new(params-same-as-ctor) { }'.
1117 */
1118 TokenDeclVar newobjDeclFunc = new TokenDeclVar (ctorDeclFunc, null, tokenScript);
1119 newobjDeclFunc.name = new TokenName (newobjDeclFunc, "$new");
1120 newobjDeclFunc.retType = ctorDeclFunc.sdtClass.MakeRefToken (newobjDeclFunc);
1121 newobjDeclFunc.argDecl = ctorDeclFunc.argDecl;
1122 newobjDeclFunc.sdtClass = ctorDeclFunc.sdtClass;
1123 newobjDeclFunc.sdtFlags = ScriptReduce.SDT_STATIC | ctorDeclFunc.sdtFlags;
1124
1125 /*
1126 * Declare local variable named '$objptr' in a frame just under
1127 * what the '$new(...)' function's arguments are declared in.
1128 */
1129 TokenDeclVar objptrVar = new TokenDeclVar (newobjDeclFunc, newobjDeclFunc, tokenScript);
1130 objptrVar.type = newobjDeclFunc.retType;
1131 objptrVar.name = new TokenName (newobjDeclFunc, "$objptr");
1132 VarDict newFrame = new VarDict (false);
1133 newFrame.outerVarDict = ctorDeclFunc.argDecl.varDict;
1134 newFrame.AddEntry (objptrVar);
1135
1136 /*
1137 * Set up '$objptr.$ctor'
1138 */
1139 TokenLValName objptrLValName = new TokenLValName (objptrVar.name, newFrame);
1140 // ref a var by giving its name
1141 TokenLValIField objptrDotCtor = new TokenLValIField (newobjDeclFunc); // an instance member reference
1142 objptrDotCtor.baseRVal = objptrLValName; // '$objptr'
1143 objptrDotCtor.fieldName = ctorDeclFunc.name; // '.' '$ctor'
1144
1145 /*
1146 * Set up '$objptr.$ctor(arglist)' call for use in the '$new(...)' body.
1147 * Copy the arglist from the constructor declaration so triviality
1148 * processing will pick the correct overloaded constructor.
1149 */
1150 TokenRValCall callCtorRVal = new TokenRValCall (newobjDeclFunc); // doing a call of some sort
1151 callCtorRVal.meth = objptrDotCtor; // calling $objptr.$ctor()
1152 TokenDeclVar[] argList = newobjDeclFunc.argDecl.vars; // get args $new() was declared with
1153 callCtorRVal.nArgs = argList.Length; // ...that is nArgs we are passing to $objptr.$ctor()
1154 for (int i = argList.Length; -- i >= 0;) {
1155 TokenDeclVar arg = argList[i]; // find out about one of the args
1156 TokenLValName argLValName = new TokenLValName (arg.name, ctorDeclFunc.argDecl.varDict);
1157 // pass arg of that name to $objptr.$ctor()
1158 argLValName.nextToken = callCtorRVal.args; // link to list of args passed to $objptr.$ctor()
1159 callCtorRVal.args = argLValName;
1160 }
1161
1162 /*
1163 * Set up a funky call to the constructor for the code body.
1164 * This will let code generator know there is some craziness.
1165 * See GenerateStmtNewobj().
1166 *
1167 * This is in essence:
1168 * {
1169 * classname $objptr = newobj (classname);
1170 * $objptr.$ctor (...);
1171 * return $objptr;
1172 * }
1173 */
1174 TokenStmtNewobj newobjStmtBody = new TokenStmtNewobj (ctorDeclFunc);
1175 newobjStmtBody.objptrVar = objptrVar;
1176 newobjStmtBody.rValCall = callCtorRVal;
1177 TokenStmtBlock newobjBody = new TokenStmtBlock (ctorDeclFunc);
1178 newobjBody.statements = newobjStmtBody;
1179
1180 /*
1181 * Link that code as the body of the function.
1182 */
1183 newobjDeclFunc.body = newobjBody;
1184
1185 /*
1186 * Say the function calls '$objptr.$ctor(arglist)' so we will inherit ctor's triviality.
1187 */
1188 newobjDeclFunc.unknownTrivialityCalls.AddLast (callCtorRVal);
1189 return newobjDeclFunc;
1190 }
1191
1192 private class TokenStmtNewobj : TokenStmt {
1193 public TokenDeclVar objptrVar;
1194 public TokenRValCall rValCall;
1195 public TokenStmtNewobj (Token original) : base (original) { }
1196 }
1197
1198 /**
1199 * @brief Output function body (either event handler or script-defined method).
1200 */
1201 private void GenerateFuncBody ()
1202 {
1203 /*
1204 * We want to know if the function's code is trivial, ie,
1205 * if it doesn't have anything that might be an infinite
1206 * loop and that is doesn't call anything that might have
1207 * an infinite loop. If it is, we don't need any CheckRun()
1208 * stuff or any of the frame save/restore stuff.
1209 */
1210 bool isTrivial = curDeclFunc.IsFuncTrivial (this);
1211
1212 /*
1213 * Clear list of all call labels.
1214 * A call label is inserted just before every call that can possibly
1215 * call CheckRun(), including any direct calls to CheckRun().
1216 * Then, when restoring stack, we can just switch to this label to
1217 * resume at the correct spot.
1218 */
1219 actCallLabels.Clear ();
1220 allCallLabels.Clear ();
1221 openCallLabel = null;
1222
1223 /*
1224 * Alloc stack space for local vars.
1225 */
1226 AllocLocalVarStackSpace ();
1227
1228 /*
1229 * Any return statements inside function body jump to this label
1230 * after putting return value in __retval.
1231 */
1232 retLabel = ilGen.DefineLabel ("__retlbl");
1233 retValue = null;
1234 if (!(curDeclFunc.retType is TokenTypeVoid)) {
1235 retValue = ilGen.DeclareLocal (curDeclFunc.retType.ToSysType (), "__retval");
1236 }
1237
1238 /*
1239 * Output:
1240 * int __mainCallNo = -1;
1241 * try {
1242 * if (instance.callMode != CallMode_NORMAL) goto __cmRestore;
1243 */
1244 actCallNo = null;
1245 ScriptMyLabel cmRestore = null;
1246 if (!isTrivial) {
1247 actCallNo = ilGen.DeclareLocal (typeof (int), "__mainCallNo");
1248 SetCallNo (curDeclFunc, actCallNo, -1);
1249 cmRestore = ilGen.DefineLabel ("__cmRestore");
1250 ilGen.BeginExceptionBlock ();
1251 PushXMRInst ();
1252 ilGen.Emit (curDeclFunc, OpCodes.Ldfld, ScriptCodeGen.callModeFieldInfo);
1253 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, XMRInstAbstract.CallMode_NORMAL);
1254 ilGen.Emit (curDeclFunc, OpCodes.Bne_Un, cmRestore);
1255 }
1256
1257 /*
1258 * Splice in the code optimizer for the body of the function.
1259 */
1260 ScriptCollector collector = new ScriptCollector ((ScriptObjWriter)ilGen);
1261 _ilGen = collector;
1262
1263 /*
1264 * If this is the default state_entry() handler, output code to set all global
1265 * variables to their initial values. Note that every script must have a
1266 * default state_entry() handler, we provide one if the script doesn't explicitly
1267 * define one.
1268 */
1269 string methname = ilGen.methName;
1270 if (methname == "default state_entry") {
1271
1272 // if (!doGblInit) goto skipGblInit;
1273 ScriptMyLabel skipGblInitLabel = ilGen.DefineLabel ("__skipGblInit");
1274 PushXMRInst (); // instance
1275 ilGen.Emit (curDeclFunc, OpCodes.Ldfld, doGblInitFieldInfo); // instance.doGblInit
1276 ilGen.Emit (curDeclFunc, OpCodes.Brfalse, skipGblInitLabel);
1277
1278 // $globalvarinit();
1279 TokenDeclVar gviFunc = tokenScript.globalVarInit;
1280 if (gviFunc.body.statements != null) {
1281 gviFunc.location.CallPre (this, gviFunc);
1282 gviFunc.location.CallPost (this, gviFunc);
1283 }
1284
1285 // various $staticfieldinit();
1286 foreach (TokenDeclSDType sdType in tokenScript.sdSrcTypesValues) {
1287 if (sdType is TokenDeclSDTypeClass) {
1288 TokenDeclVar sfiFunc = ((TokenDeclSDTypeClass)sdType).staticFieldInit;
1289 if ((sfiFunc != null) && (sfiFunc.body.statements != null)) {
1290 sfiFunc.location.CallPre (this, sfiFunc);
1291 sfiFunc.location.CallPost (this, sfiFunc);
1292 }
1293 }
1294 }
1295
1296 // doGblInit = 0;
1297 PushXMRInst (); // instance
1298 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4_0);
1299 ilGen.Emit (curDeclFunc, OpCodes.Stfld, doGblInitFieldInfo); // instance.doGblInit
1300
1301 //skipGblInit:
1302 ilGen.MarkLabel (skipGblInitLabel);
1303 }
1304
1305 /*
1306 * If this is a script-defined type constructor, call the base constructor and call
1307 * this class's $instfieldinit() method to initialize instance fields.
1308 */
1309 if ((curDeclFunc.sdtClass != null) && curDeclFunc.funcNameSig.val.StartsWith ("$ctor(")) {
1310 if (curDeclFunc.baseCtorCall != null) {
1311 GenerateFromRValCall (curDeclFunc.baseCtorCall);
1312 }
1313 TokenDeclVar ifiFunc = ((TokenDeclSDTypeClass)curDeclFunc.sdtClass).instFieldInit;
1314 if (ifiFunc.body.statements != null) {
1315 CompValu thisCompValu = new CompValuArg (ifiFunc.sdtClass.MakeRefToken (ifiFunc), 0);
1316 CompValu ifiFuncLocn = new CompValuInstMember (ifiFunc, thisCompValu, true);
1317 ifiFuncLocn.CallPre (this, ifiFunc);
1318 ifiFuncLocn.CallPost (this, ifiFunc);
1319 }
1320 }
1321
1322 /*
1323 * See if time to suspend in case they are doing a loop with recursion.
1324 */
1325 if (!isTrivial) EmitCallCheckRun (curDeclFunc, true);
1326
1327 /*
1328 * Output code body.
1329 */
1330 GenerateStmtBlock (curDeclFunc.body);
1331
1332 /*
1333 * If code falls through to this point, means they are missing
1334 * a return statement. And that is legal only if the function
1335 * returns 'void'.
1336 */
1337 if (mightGetHere) {
1338 if (!(curDeclFunc.retType is TokenTypeVoid)) {
1339 ErrorMsg (curDeclFunc.body, "missing final return statement");
1340 }
1341 ilGen.Emit (curDeclFunc, OpCodes.Leave, retLabel);
1342 }
1343
1344 /*
1345 * End of the code to be optimized.
1346 * Do optimizations then write it all out to object file.
1347 * After this, all code gets written directly to object file.
1348 * Optimization must be completed before we scan the allCallLabels
1349 * list below to look for active locals and temps.
1350 */
1351 collector.Optimize ();
1352 _ilGen = collector.WriteOutAll ();
1353 collector = null;
1354
1355 /*
1356 * Output code to restore stack frame from stream.
1357 * It jumps back to the call labels within the function body.
1358 */
1359 List<ScriptMyLocal> activeTemps = null;
1360 if (!isTrivial) {
1361
1362 /*
1363 * Build list of locals and temps active at all the call labels.
1364 */
1365 activeTemps = new List<ScriptMyLocal> ();
1366 foreach (CallLabel cl in allCallLabels) {
1367 foreach (ScriptMyLocal lcl in cl.callLabel.whereAmI.localsReadBeforeWritten) {
1368 if (!activeTemps.Contains (lcl)) {
1369 activeTemps.Add (lcl);
1370 }
1371 }
1372 }
1373
1374 /*
1375 * Output code to restore the args, locals and temps then jump to
1376 * the call label that we were interrupted at.
1377 */
1378 ilGen.MarkLabel (cmRestore);
1379 GenerateFrameRestoreCode (activeTemps);
1380 }
1381
1382 /*
1383 * Output epilog that saves stack frame state if CallMode_SAVE.
1384 *
1385 * finally {
1386 * if (instance.callMode != CallMode_SAVE) goto __endFin;
1387 * GenerateFrameCaptureCode();
1388 * __endFin:
1389 * }
1390 */
1391 ScriptMyLabel endFin = null;
1392 if (!isTrivial) {
1393 ilGen.BeginFinallyBlock ();
1394 endFin = ilGen.DefineLabel ("__endFin");
1395 PushXMRInst ();
1396 ilGen.Emit (curDeclFunc, OpCodes.Ldfld, callModeFieldInfo);
1397 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, XMRInstAbstract.CallMode_SAVE);
1398 ilGen.Emit (curDeclFunc, OpCodes.Bne_Un, endFin);
1399 GenerateFrameCaptureCode (activeTemps);
1400 ilGen.MarkLabel (endFin);
1401 ilGen.Emit (curDeclFunc, OpCodes.Endfinally);
1402 ilGen.EndExceptionBlock ();
1403 }
1404
1405 /*
1406 * Output the 'real' return opcode.
1407 */
1408 ilGen.MarkLabel (retLabel);
1409 if (!(curDeclFunc.retType is TokenTypeVoid)) {
1410 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, retValue);
1411 }
1412 ilGen.Emit (curDeclFunc, OpCodes.Ret);
1413 retLabel = null;
1414 retValue = null;
1415
1416 /*
1417 * No more instructions for this method.
1418 */
1419 ((ScriptObjWriter)ilGen).EndMethod ();
1420 _ilGen = null;
1421
1422 /*
1423 * Not generating function code any more.
1424 */
1425 curBreakTarg = null;
1426 curContTarg = null;
1427 curDeclFunc = null;
1428 }
1429
1430 /**
1431 * @brief Allocate stack space for all local variables, regardless of
1432 * which { } statement block they are actually defined in.
1433 */
1434 private void AllocLocalVarStackSpace ()
1435 {
1436 foreach (TokenDeclVar localVar in curDeclFunc.localVars) {
1437
1438 /*
1439 * Skip all 'constant' vars as they were handled by the reducer.
1440 */
1441 if (localVar.constant) continue;
1442
1443 /*
1444 * Get a stack location for the local variable.
1445 */
1446 localVar.location = new CompValuLocalVar (localVar.type, localVar.name.val, this);
1447 }
1448 }
1449
1450 /**
1451 * @brief Generate code to write all arguments and locals to the capture stack frame.
1452 * This includes temp variables.
1453 * We only need to save what is active at the point of callLabels through because
1454 * those are the only points we will jump to on restore. This saves us from saving
1455 * all the little temp vars we create.
1456 * @param activeTemps = list of locals and temps that we care about, ie, which
1457 * ones get restored by GenerateFrameRestoreCode().
1458 */
1459 private void GenerateFrameCaptureCode (List<ScriptMyLocal> activeTemps)
1460 {
1461 /*
1462 * Compute total number of slots we need to save stuff.
1463 * Assume we need to save all call arguments.
1464 */
1465 int nSaves = curDeclFunc.argDecl.vars.Length + activeTemps.Count;
1466
1467 /*
1468 * Output code to allocate a stack frame object with an object array.
1469 * This also pushes the stack frame object on the instance.stackFrames list.
1470 * It returns a pointer to the object array it allocated.
1471 */
1472 PushXMRInst ();
1473 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, ilGen.methName);
1474 GetCallNo (curDeclFunc, actCallNo);
1475 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, nSaves);
1476 ilGen.Emit (curDeclFunc, OpCodes.Call, captureStackFrameMethodInfo);
1477
1478 if (DEBUG_STACKCAPRES) {
1479 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, ilGen.methName + "*: capture mainCallNo=");
1480 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1481 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, actCallNo);
1482 ilGen.Emit (curDeclFunc, OpCodes.Box, typeof (int));
1483 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1484 }
1485
1486 /*
1487 * Copy arg values to object array, boxing as needed.
1488 */
1489 int i = 0;
1490 foreach (TokenDeclVar argVar in curDeclFunc.argDecl.varDict) {
1491 ilGen.Emit (curDeclFunc, OpCodes.Dup);
1492 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, i);
1493 argVar.location.PushVal (this, argVar.name, tokenTypeObj);
1494 if (DEBUG_STACKCAPRES) {
1495 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n arg:" + argVar.name.val + "=");
1496 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1497 ilGen.Emit (curDeclFunc, OpCodes.Dup);
1498 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1499 }
1500 ilGen.Emit (curDeclFunc, OpCodes.Stelem_Ref);
1501 i ++;
1502 }
1503
1504 /*
1505 * Copy local and temp values to object array, boxing as needed.
1506 */
1507 foreach (ScriptMyLocal lcl in activeTemps) {
1508 ilGen.Emit (curDeclFunc, OpCodes.Dup);
1509 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, i ++);
1510 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, lcl);
1511 Type t = lcl.type;
1512 if (t == typeof (HeapTrackerList)) {
1513 ilGen.Emit (curDeclFunc, OpCodes.Call, heapTrackerListPush);
1514 t = typeof (LSL_List);
1515 }
1516 if (t == typeof (HeapTrackerObject)) {
1517 ilGen.Emit (curDeclFunc, OpCodes.Call, heapTrackerObjectPush);
1518 t = typeof (object);
1519 }
1520 if (t == typeof (HeapTrackerString)) {
1521 ilGen.Emit (curDeclFunc, OpCodes.Call, heapTrackerStringPush);
1522 t = typeof (string);
1523 }
1524 if (t.IsValueType) {
1525 ilGen.Emit (curDeclFunc, OpCodes.Box, t);
1526 }
1527 if (DEBUG_STACKCAPRES) {
1528 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n lcl:" + lcl.name + "=");
1529 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1530 ilGen.Emit (curDeclFunc, OpCodes.Dup);
1531 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1532 }
1533 ilGen.Emit (curDeclFunc, OpCodes.Stelem_Ref);
1534 }
1535 if (DEBUG_STACKCAPRES) {
1536 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n");
1537 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1538 }
1539
1540 ilGen.Emit (curDeclFunc, OpCodes.Pop);
1541 }
1542
1543 /**
1544 * @brief Generate code to restore all arguments and locals from the restore stack frame.
1545 * This includes temp variables.
1546 */
1547 private void GenerateFrameRestoreCode (List<ScriptMyLocal> activeTemps)
1548 {
1549 ScriptMyLocal objArray = ilGen.DeclareLocal (typeof (object[]), "__restObjArray");
1550
1551 /*
1552 * Output code to pop stack frame from instance.stackFrames.
1553 * It returns a pointer to the object array that contains values to be restored.
1554 */
1555 PushXMRInst ();
1556 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, ilGen.methName);
1557 ilGen.Emit (curDeclFunc, OpCodes.Ldloca, actCallNo); // __mainCallNo
1558 ilGen.Emit (curDeclFunc, OpCodes.Call, restoreStackFrameMethodInfo);
1559 ilGen.Emit (curDeclFunc, OpCodes.Stloc, objArray);
1560 if (DEBUG_STACKCAPRES) {
1561 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, ilGen.methName + "*: restore mainCallNo=");
1562 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1563 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, actCallNo);
1564 ilGen.Emit (curDeclFunc, OpCodes.Box, typeof (int));
1565 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1566 }
1567
1568 /*
1569 * Restore argument values from object array, unboxing as needed.
1570 * Although the caller has restored them to what it called us with, it's possible that this
1571 * function has modified them since, so we need to do our own restore.
1572 */
1573 int i = 0;
1574 foreach (TokenDeclVar argVar in curDeclFunc.argDecl.varDict) {
1575 CompValu argLoc = argVar.location;
1576 argLoc.PopPre (this, argVar.name);
1577 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, objArray);
1578 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, i);
1579 ilGen.Emit (curDeclFunc, OpCodes.Ldelem_Ref);
1580 if (DEBUG_STACKCAPRES) {
1581 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n arg:" + argVar.name.val + "=");
1582 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1583 ilGen.Emit (curDeclFunc, OpCodes.Dup);
1584 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1585 }
1586 TypeCast.CastTopOfStack (this, argVar.name, tokenTypeObj, argLoc.type, true);
1587 argLoc.PopPost (this, argVar.name);
1588 i ++;
1589 }
1590
1591 /*
1592 * Restore local and temp values from object array, unboxing as needed.
1593 */
1594 foreach (ScriptMyLocal lcl in activeTemps) {
1595 Type t = lcl.type;
1596 Type u = t;
1597 if (t == typeof (HeapTrackerList)) u = typeof (LSL_List);
1598 if (t == typeof (HeapTrackerObject)) u = typeof (object);
1599 if (t == typeof (HeapTrackerString)) u = typeof (string);
1600 if (u != t) {
1601 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, lcl);
1602 }
1603 ilGen.Emit (curDeclFunc, OpCodes.Ldloc, objArray);
1604 ilGen.Emit (curDeclFunc, OpCodes.Ldc_I4, i ++);
1605 ilGen.Emit (curDeclFunc, OpCodes.Ldelem_Ref);
1606 if (DEBUG_STACKCAPRES) {
1607 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n lcl:" + lcl.name + "=");
1608 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1609 ilGen.Emit (curDeclFunc, OpCodes.Dup);
1610 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1611 }
1612 if (u.IsValueType) {
1613 ilGen.Emit (curDeclFunc, OpCodes.Unbox_Any, u);
1614 } else if (u != typeof (object)) {
1615 ilGen.Emit (curDeclFunc, OpCodes.Castclass, u);
1616 }
1617 if (u != t) {
1618 ilGen.Emit (curDeclFunc, OpCodes.Call, t.GetMethod ("Pop", new Type[] { u }));
1619 } else {
1620 ilGen.Emit (curDeclFunc, OpCodes.Stloc, lcl);
1621 }
1622 }
1623 if (DEBUG_STACKCAPRES) {
1624 ilGen.Emit (curDeclFunc, OpCodes.Ldstr, "\n");
1625 ilGen.Emit (curDeclFunc, OpCodes.Call, consoleWriteMethodInfo);
1626 }
1627
1628 OutputCallNoSwitchStmt ();
1629 }
1630
1631 /**
1632 * @brief Output a switch statement with a case for each possible
1633 * value of whatever callNo is currently active, either
1634 * __mainCallNo or one of the try/catch/finally's callNos.
1635 *
1636 * switch (callNo) {
1637 * case 0: goto __call_0;
1638 * case 1: goto __call_1;
1639 * ...
1640 * }
1641 * throw new ScriptBadCallNoException (callNo);
1642 */
1643 private void OutputCallNoSwitchStmt ()
1644 {
1645 ScriptMyLabel[] callLabels = new ScriptMyLabel[actCallLabels.Count];
1646 foreach (CallLabel cl in actCallLabels) {
1647 callLabels[cl.index] = cl.callLabel;
1648 }
1649 GetCallNo (curDeclFunc, actCallNo);
1650 ilGen.Emit (curDeclFunc, OpCodes.Switch, callLabels);
1651
1652 GetCallNo (curDeclFunc, actCallNo);
1653 ilGen.Emit (curDeclFunc, OpCodes.Newobj, scriptBadCallNoExceptionConstructorInfo);
1654 ilGen.Emit (curDeclFunc, OpCodes.Throw);
1655 }
1656
1657 /**
1658 * @brief There is one of these per call that can possibly call CheckRun(),
1659 * including direct calls to CheckRun().
1660 * They mark points that the stack capture/restore code will save & restore to.
1661 * All object-code level local vars active at the call label's point will
1662 * be saved & restored.
1663 *
1664 * callNo = 5;
1665 * __call_5:
1666 * push call arguments from temps
1667 * call SomethingThatCallsCheckRun()
1668 *
1669 * If SomethingThatCallsCheckRun() actually calls CheckRun(), our restore code
1670 * will restore our args, locals & temps, then jump to __call_5, which will then
1671 * call SomethingThatCallsCheckRun() again, which will restore its stuff likewise.
1672 * When eventually the actual CheckRun() call is restored, it will turn off restore
1673 * mode (by changing callMode from CallMode_RESTORE to CallMode_NORMAL) and return,
1674 * allowing the code to run normally from that point.
1675 */
1676 public class CallLabel {
1677 public int index; // sequential integer, starting at 0, within actCallLabels
1678 // - used for the switch statement
1679 public ScriptMyLabel callLabel; // the actual label token
1680
1681 public CallLabel (ScriptCodeGen scg, Token errorAt)
1682 {
1683 if (scg.openCallLabel != null) throw new Exception ("call label already open");
1684
1685 if (!scg.curDeclFunc.IsFuncTrivial (scg)) {
1686 this.index = scg.actCallLabels.Count;
1687 string name = "__call_" + index + "_" + scg.allCallLabels.Count;
1688
1689 /*
1690 * Make sure eval stack is empty because the frame capture/restore
1691 * code expects such (restore switch stmt has an empty stack).
1692 */
1693 int depth = ((ScriptCollector)scg.ilGen).stackDepth.Count;
1694 if (depth > 0) {
1695 // maybe need to call Trivialize()
1696 throw new Exception ("call label stack depth " + depth + " at " + errorAt.SrcLoc);
1697 }
1698
1699 /*
1700 * Eval stack is empty so the restore code can handle it.
1701 */
1702 this.index = scg.actCallLabels.Count;
1703 scg.actCallLabels.AddLast (this);
1704 scg.allCallLabels.AddLast (this);
1705 this.callLabel = scg.ilGen.DefineLabel (name);
1706 scg.SetCallNo (errorAt, scg.actCallNo, this.index);
1707 scg.ilGen.MarkLabel (this.callLabel);
1708 }
1709
1710 scg.openCallLabel = this;
1711 }
1712 };
1713
1714 /**
1715 * @brief generate code for an arbitrary statement.
1716 */
1717 private void GenerateStmt (TokenStmt stmt)
1718 {
1719 errorMessageToken = stmt;
1720 if (stmt is TokenDeclVar) { GenerateDeclVar ((TokenDeclVar)stmt); return; }
1721 if (stmt is TokenStmtBlock) { GenerateStmtBlock ((TokenStmtBlock)stmt); return; }
1722 if (stmt is TokenStmtBreak) { GenerateStmtBreak ((TokenStmtBreak)stmt); return; }
1723 if (stmt is TokenStmtCont) { GenerateStmtCont ((TokenStmtCont)stmt); return; }
1724 if (stmt is TokenStmtDo) { GenerateStmtDo ((TokenStmtDo)stmt); return; }
1725 if (stmt is TokenStmtFor) { GenerateStmtFor ((TokenStmtFor)stmt); return; }
1726 if (stmt is TokenStmtForEach) { GenerateStmtForEach ((TokenStmtForEach)stmt); return; }
1727 if (stmt is TokenStmtIf) { GenerateStmtIf ((TokenStmtIf)stmt); return; }
1728 if (stmt is TokenStmtJump) { GenerateStmtJump ((TokenStmtJump)stmt); return; }
1729 if (stmt is TokenStmtLabel) { GenerateStmtLabel ((TokenStmtLabel)stmt); return; }
1730 if (stmt is TokenStmtNewobj) { GenerateStmtNewobj ((TokenStmtNewobj)stmt); return; }
1731 if (stmt is TokenStmtNull) { return; }
1732 if (stmt is TokenStmtRet) { GenerateStmtRet ((TokenStmtRet)stmt); return; }
1733 if (stmt is TokenStmtRVal) { GenerateStmtRVal ((TokenStmtRVal)stmt); return; }
1734 if (stmt is TokenStmtState) { GenerateStmtState ((TokenStmtState)stmt); return; }
1735 if (stmt is TokenStmtSwitch) { GenerateStmtSwitch ((TokenStmtSwitch)stmt); return; }
1736 if (stmt is TokenStmtThrow) { GenerateStmtThrow ((TokenStmtThrow)stmt); return; }
1737 if (stmt is TokenStmtTry) { GenerateStmtTry ((TokenStmtTry)stmt); return; }
1738 if (stmt is TokenStmtVarIniDef) { GenerateStmtVarIniDef ((TokenStmtVarIniDef)stmt); return; }
1739 if (stmt is TokenStmtWhile) { GenerateStmtWhile ((TokenStmtWhile)stmt); return; }
1740 throw new Exception ("unknown TokenStmt type " + stmt.GetType ().ToString ());
1741 }
1742
1743 /**
1744 * @brief generate statement block (ie, with braces)
1745 */
1746 private void GenerateStmtBlock (TokenStmtBlock stmtBlock)
1747 {
1748 if (!mightGetHere) return;
1749
1750 /*
1751 * Push new current statement block pointer for anyone who cares.
1752 */
1753 TokenStmtBlock oldStmtBlock = curStmtBlock;
1754 curStmtBlock = stmtBlock;
1755
1756 /*
1757 * Output the statements that make up the block.
1758 */
1759 for (Token t = stmtBlock.statements; t != null; t = t.nextToken) {
1760 GenerateStmt ((TokenStmt)t);
1761 }
1762
1763 /*
1764 * Pop the current statement block.
1765 */
1766 curStmtBlock = oldStmtBlock;
1767 }
1768
1769 /**
1770 * @brief output code for a 'break' statement
1771 */
1772 private void GenerateStmtBreak (TokenStmtBreak breakStmt)
1773 {
1774 if (!mightGetHere) return;
1775
1776 /*
1777 * Make sure we are in a breakable situation.
1778 */
1779 if (curBreakTarg == null) {
1780 ErrorMsg (breakStmt, "not in a breakable situation");
1781 return;
1782 }
1783
1784 /*
1785 * Tell anyone who cares that the break target was actually used.
1786 */
1787 curBreakTarg.used = true;
1788
1789 /*
1790 * Output the instructions.
1791 */
1792 EmitJumpCode (curBreakTarg.label, curBreakTarg.block, breakStmt);
1793 }
1794
1795 /**
1796 * @brief output code for a 'continue' statement
1797 */
1798 private void GenerateStmtCont (TokenStmtCont contStmt)
1799 {
1800 if (!mightGetHere) return;
1801
1802 /*
1803 * Make sure we are in a contable situation.
1804 */
1805 if (curContTarg == null) {
1806 ErrorMsg (contStmt, "not in a continueable situation");
1807 return;
1808 }
1809
1810 /*
1811 * Tell anyone who cares that the continue target was actually used.
1812 */
1813 curContTarg.used = true;
1814
1815 /*
1816 * Output the instructions.
1817 */
1818 EmitJumpCode (curContTarg.label, curContTarg.block, contStmt);
1819 }
1820
1821 /**
1822 * @brief output code for a 'do' statement
1823 */
1824 private void GenerateStmtDo (TokenStmtDo doStmt)
1825 {
1826 if (!mightGetHere) return;
1827
1828 BreakContTarg oldBreakTarg = curBreakTarg;
1829 BreakContTarg oldContTarg = curContTarg;
1830 ScriptMyLabel loopLabel = ilGen.DefineLabel ("doloop_" + doStmt.Unique);
1831
1832 curBreakTarg = new BreakContTarg (this, "dobreak_" + doStmt.Unique);
1833 curContTarg = new BreakContTarg (this, "docont_" + doStmt.Unique);
1834
1835 ilGen.MarkLabel (loopLabel);
1836 GenerateStmt (doStmt.bodyStmt);
1837 if (curContTarg.used) {
1838 ilGen.MarkLabel (curContTarg.label);
1839 mightGetHere = true;
1840 }
1841
1842 if (mightGetHere) {
1843 EmitCallCheckRun (doStmt, false);
1844 CompValu testRVal = GenerateFromRVal (doStmt.testRVal);
1845 if (IsConstBoolExprTrue (testRVal)) {
1846
1847 /*
1848 * Unconditional looping, unconditional branch and
1849 * say we never fall through to next statement.
1850 */
1851 ilGen.Emit (doStmt, OpCodes.Br, loopLabel);
1852 mightGetHere = false;
1853 } else {
1854
1855 /*
1856 * Conditional looping, test and brach back to top of loop.
1857 */
1858 testRVal.PushVal (this, doStmt.testRVal, tokenTypeBool);
1859 ilGen.Emit (doStmt, OpCodes.Brtrue, loopLabel);
1860 }
1861 }
1862
1863 /*
1864 * If 'break' statement was used, output target label.
1865 * And assume that since a 'break' statement was used, it's possible for the code to get here.
1866 */
1867 if (curBreakTarg.used) {
1868 ilGen.MarkLabel (curBreakTarg.label);
1869 mightGetHere = true;
1870 }
1871
1872 curBreakTarg = oldBreakTarg;
1873 curContTarg = oldContTarg;
1874 }
1875
1876 /**
1877 * @brief output code for a 'for' statement
1878 */
1879 private void GenerateStmtFor (TokenStmtFor forStmt)
1880 {
1881 if (!mightGetHere) return;
1882
1883 BreakContTarg oldBreakTarg = curBreakTarg;
1884 BreakContTarg oldContTarg = curContTarg;
1885 ScriptMyLabel loopLabel = ilGen.DefineLabel ("forloop_" + forStmt.Unique);
1886
1887 curBreakTarg = new BreakContTarg (this, "forbreak_" + forStmt.Unique);
1888 curContTarg = new BreakContTarg (this, "forcont_" + forStmt.Unique);
1889
1890 if (forStmt.initStmt != null) {
1891 GenerateStmt (forStmt.initStmt);
1892 }
1893 ilGen.MarkLabel (loopLabel);
1894
1895 /*
1896 * See if we have a test expression that is other than a constant TRUE.
1897 * If so, test it and conditionally branch to end if false.
1898 */
1899 if (forStmt.testRVal != null) {
1900 CompValu testRVal = GenerateFromRVal (forStmt.testRVal);
1901 if (!IsConstBoolExprTrue (testRVal)) {
1902 testRVal.PushVal (this, forStmt.testRVal, tokenTypeBool);
1903 ilGen.Emit (forStmt, OpCodes.Brfalse, curBreakTarg.label);
1904 curBreakTarg.used = true;
1905 }
1906 }
1907
1908 /*
1909 * Output loop body.
1910 */
1911 GenerateStmt (forStmt.bodyStmt);
1912
1913 /*
1914 * Here's where a 'continue' statement jumps to.
1915 */
1916 if (curContTarg.used) {
1917 ilGen.MarkLabel (curContTarg.label);
1918 mightGetHere = true;
1919 }
1920
1921 if (mightGetHere) {
1922
1923 /*
1924 * After checking for excessive CPU time, output increment statement, if any.
1925 */
1926 EmitCallCheckRun (forStmt, false);
1927 if (forStmt.incrRVal != null) {
1928 GenerateFromRVal (forStmt.incrRVal);
1929 }
1930
1931 /*
1932 * Unconditional branch back to beginning of loop.
1933 */
1934 ilGen.Emit (forStmt, OpCodes.Br, loopLabel);
1935 }
1936
1937 /*
1938 * If test needs label, output label for it to jump to.
1939 * Otherwise, clear mightGetHere as we know loop never
1940 * falls out the bottom.
1941 */
1942 mightGetHere = curBreakTarg.used;
1943 if (mightGetHere) {
1944 ilGen.MarkLabel (curBreakTarg.label);
1945 }
1946
1947 curBreakTarg = oldBreakTarg;
1948 curContTarg = oldContTarg;
1949 }
1950
1951 private void GenerateStmtForEach (TokenStmtForEach forEachStmt)
1952 {
1953 if (!mightGetHere) return;
1954
1955 BreakContTarg oldBreakTarg = curBreakTarg;
1956 BreakContTarg oldContTarg = curContTarg;
1957 CompValu keyLVal = null;
1958 CompValu valLVal = null;
1959 CompValu arrayRVal = GenerateFromRVal (forEachStmt.arrayRVal);
1960
1961 if (forEachStmt.keyLVal != null) {
1962 keyLVal = GenerateFromLVal (forEachStmt.keyLVal);
1963 if (!(keyLVal.type is TokenTypeObject)) {
1964 ErrorMsg (forEachStmt.arrayRVal, "must be object");
1965 }
1966 }
1967 if (forEachStmt.valLVal != null) {
1968 valLVal = GenerateFromLVal (forEachStmt.valLVal);
1969 if (!(valLVal.type is TokenTypeObject)) {
1970 ErrorMsg (forEachStmt.arrayRVal, "must be object");
1971 }
1972 }
1973 if (!(arrayRVal.type is TokenTypeArray)) {
1974 ErrorMsg (forEachStmt.arrayRVal, "must be an array");
1975 }
1976
1977 curBreakTarg = new BreakContTarg (this, "foreachbreak_" + forEachStmt.Unique);
1978 curContTarg = new BreakContTarg (this, "foreachcont_" + forEachStmt.Unique);
1979
1980 CompValuTemp indexVar = new CompValuTemp (new TokenTypeInt (forEachStmt), this);
1981 ScriptMyLabel loopLabel = ilGen.DefineLabel ("foreachloop_" + forEachStmt.Unique);
1982
1983 // indexVar = 0
1984 ilGen.Emit (forEachStmt, OpCodes.Ldc_I4_0);
1985 indexVar.Pop (this, forEachStmt);
1986
1987 ilGen.MarkLabel (loopLabel);
1988
1989 // key = array.__pub_index (indexVar);
1990 // if (key == null) goto curBreakTarg;
1991 if (keyLVal != null) {
1992 keyLVal.PopPre (this, forEachStmt.keyLVal);
1993 arrayRVal.PushVal (this, forEachStmt.arrayRVal);
1994 indexVar.PushVal (this, forEachStmt);
1995 ilGen.Emit (forEachStmt, OpCodes.Call, xmrArrPubIndexMethod);
1996 keyLVal.PopPost (this, forEachStmt.keyLVal);
1997 keyLVal.PushVal (this, forEachStmt.keyLVal);
1998 ilGen.Emit (forEachStmt, OpCodes.Brfalse, curBreakTarg.label);
1999 curBreakTarg.used = true;
2000 }
2001
2002 // val = array._pub_value (indexVar);
2003 // if (val == null) goto curBreakTarg;
2004 if (valLVal != null) {
2005 valLVal.PopPre (this, forEachStmt.valLVal);
2006 arrayRVal.PushVal (this, forEachStmt.arrayRVal);
2007 indexVar.PushVal (this, forEachStmt);
2008 ilGen.Emit (forEachStmt, OpCodes.Call, xmrArrPubValueMethod);
2009 valLVal.PopPost (this, forEachStmt.valLVal);
2010 if (keyLVal == null) {
2011 valLVal.PushVal (this, forEachStmt.valLVal);
2012 ilGen.Emit (forEachStmt, OpCodes.Brfalse, curBreakTarg.label);
2013 curBreakTarg.used = true;
2014 }
2015 }
2016
2017 // indexVar ++;
2018 indexVar.PushVal (this, forEachStmt);
2019 ilGen.Emit (forEachStmt, OpCodes.Ldc_I4_1);
2020 ilGen.Emit (forEachStmt, OpCodes.Add);
2021 indexVar.Pop (this, forEachStmt);
2022
2023 // body statement
2024 GenerateStmt (forEachStmt.bodyStmt);
2025
2026 // continue label
2027 if (curContTarg.used) {
2028 ilGen.MarkLabel (curContTarg.label);
2029 mightGetHere = true;
2030 }
2031
2032 // call CheckRun()
2033 if (mightGetHere) {
2034 EmitCallCheckRun (forEachStmt, false);
2035 ilGen.Emit (forEachStmt, OpCodes.Br, loopLabel);
2036 }
2037
2038 // break label
2039 ilGen.MarkLabel (curBreakTarg.label);
2040 mightGetHere = true;
2041
2042 curBreakTarg = oldBreakTarg;
2043 curContTarg = oldContTarg;
2044 }
2045
2046 /**
2047 * @brief output code for an 'if' statement
2048 * Braces are necessary because what may be one statement for trueStmt or elseStmt in
2049 * the script may translate to more than one statement in the resultant C# code.
2050 */
2051 private void GenerateStmtIf (TokenStmtIf ifStmt)
2052 {
2053 if (!mightGetHere) return;
2054
2055 bool constVal;
2056
2057 /*
2058 * Test condition and see if constant test expression.
2059 */
2060 CompValu testRVal = GenerateFromRVal (ifStmt.testRVal);
2061 if (IsConstBoolExpr (testRVal, out constVal)) {
2062
2063 /*
2064 * Constant, output just either the true or else part.
2065 */
2066 if (constVal) {
2067 GenerateStmt (ifStmt.trueStmt);
2068 } else if (ifStmt.elseStmt != null) {
2069 GenerateStmt (ifStmt.elseStmt);
2070 }
2071 } else if (ifStmt.elseStmt == null) {
2072
2073 /*
2074 * This is an 'if' statement without an 'else' clause.
2075 */
2076 testRVal.PushVal (this, ifStmt.testRVal, tokenTypeBool);
2077 ScriptMyLabel doneLabel = ilGen.DefineLabel ("ifdone_" + ifStmt.Unique);
2078 ilGen.Emit (ifStmt, OpCodes.Brfalse, doneLabel); // brfalse doneLabel
2079 GenerateStmt (ifStmt.trueStmt); // generate true body code
2080 ilGen.MarkLabel (doneLabel);
2081 mightGetHere = true; // there's always a possibility of getting here
2082 } else {
2083
2084 /*
2085 * This is an 'if' statement with an 'else' clause.
2086 */
2087 testRVal.PushVal (this, ifStmt.testRVal, tokenTypeBool);
2088 ScriptMyLabel elseLabel = ilGen.DefineLabel ("ifelse_" + ifStmt.Unique);
2089 ilGen.Emit (ifStmt, OpCodes.Brfalse, elseLabel); // brfalse elseLabel
2090 GenerateStmt (ifStmt.trueStmt); // generate true body code
2091 bool trueMightGetHere = mightGetHere; // save whether or not true falls through
2092 ScriptMyLabel doneLabel = ilGen.DefineLabel ("ifdone_" + ifStmt.Unique);
2093 ilGen.Emit (ifStmt, OpCodes.Br, doneLabel); // branch to done
2094 ilGen.MarkLabel (elseLabel); // beginning of else code
2095 mightGetHere = true; // the top of the else might be executed
2096 GenerateStmt (ifStmt.elseStmt); // output else code
2097 ilGen.MarkLabel (doneLabel); // where end of true clause code branches to
2098 mightGetHere |= trueMightGetHere; // gets this far if either true or else falls through
2099 }
2100 }
2101
2102 /**
2103 * @brief output code for a 'jump' statement
2104 */
2105 private void GenerateStmtJump (TokenStmtJump jumpStmt)
2106 {
2107 if (!mightGetHere) return;
2108
2109 /*
2110 * Make sure the target label is defined somewhere in the function.
2111 */
2112 TokenStmtLabel stmtLabel;
2113 if (!curDeclFunc.labels.TryGetValue (jumpStmt.label.val, out stmtLabel)) {
2114 ErrorMsg (jumpStmt, "undefined label " + jumpStmt.label.val);
2115 return;
2116 }
2117 if (!stmtLabel.labelTagged) {
2118 stmtLabel.labelStruct = ilGen.DefineLabel ("jump_" + stmtLabel.name.val);
2119 stmtLabel.labelTagged = true;
2120 }
2121
2122 /*
2123 * Emit instructions to do the jump.
2124 */
2125 EmitJumpCode (stmtLabel.labelStruct, stmtLabel.block, jumpStmt);
2126 }
2127
2128 /**
2129 * @brief Emit code to jump to a label
2130 * @param target = label being jumped to
2131 * @param targetsBlock = { ... } the label is defined in
2132 */
2133 private void EmitJumpCode (ScriptMyLabel target, TokenStmtBlock targetsBlock, Token errorAt)
2134 {
2135 /*
2136 * Jumps never fall through.
2137 */
2138 mightGetHere = false;
2139
2140 /*
2141 * Find which block the target label is in. Must be in this or an outer block,
2142 * no laterals allowed. And if we exit a try/catch block, use Leave instead of Br.
2143 *
2144 * jump lateral;
2145 * {
2146 * @lateral;
2147 * }
2148 */
2149 bool useLeave = false;
2150 TokenStmtBlock stmtBlock;
2151 Stack<TokenStmtTry> finallyBlocksCalled = new Stack<TokenStmtTry> ();
2152 for (stmtBlock = curStmtBlock; stmtBlock != targetsBlock; stmtBlock = stmtBlock.outerStmtBlock) {
2153 if (stmtBlock == null) {
2154 ErrorMsg (errorAt, "no lateral jumps allowed");
2155 return;
2156 }
2157 if (stmtBlock.isFinally) {
2158 ErrorMsg (errorAt, "cannot jump out of finally");
2159 return;
2160 }
2161 if (stmtBlock.isTry || stmtBlock.isCatch) useLeave = true;
2162 if ((stmtBlock.tryStmt != null) && (stmtBlock.tryStmt.finallyStmt != null)) {
2163 finallyBlocksCalled.Push (stmtBlock.tryStmt);
2164 }
2165 }
2166
2167 /*
2168 * If popping through more than one finally block, we have to break it down for the stack
2169 * capture and restore code, one finally block at a time.
2170 *
2171 * try {
2172 * try {
2173 * try {
2174 * jump exit;
2175 * } finally {
2176 * llOwnerSay ("exiting inner");
2177 * }
2178 * } finally {
2179 * llOwnerSay ("exiting middle");
2180 * }
2181 * } finally {
2182 * llOwnerSay ("exiting outer");
2183 * }
2184 * @exit;
2185 *
2186 * try {
2187 * try {
2188 * try {
2189 * jump intr2_exit; <<< gets its own tryNo call label so inner try knows where to restore to
2190 * } finally {
2191 * llOwnerSay ("exiting inner");
2192 * }
2193 * jump outtry2;
2194 * @intr2_exit; jump intr1_exit; <<< gets its own tryNo call label so middle try knows where to restore to
2195 * @outtry2;
2196 * } finally {
2197 * llOwnerSay ("exiting middle");
2198 * }
2199 * jump outtry1;
2200 * @intr1_exit: jump exit; <<< gets its own tryNo call label so outer try knows where to restore to
2201 * @outtry1;
2202 * } finally {
2203 * llOwnerSay ("exiting outer");
2204 * }
2205 * @exit;
2206 */
2207 int level = 0;
2208 while (finallyBlocksCalled.Count > 1) {
2209 TokenStmtTry finallyBlock = finallyBlocksCalled.Pop ();
2210 string intername = "intr" + (++ level) + "_" + target.name;
2211 IntermediateLeave iLeave;
2212 if (!finallyBlock.iLeaves.TryGetValue (intername, out iLeave)) {
2213 iLeave = new IntermediateLeave ();
2214 iLeave.jumpIntoLabel = ilGen.DefineLabel (intername);
2215 iLeave.jumpAwayLabel = target;
2216 finallyBlock.iLeaves.Add (intername, iLeave);
2217 }
2218 target = iLeave.jumpIntoLabel;
2219 }
2220
2221 /*
2222 * Finally output the branch/leave opcode.
2223 * If using Leave, prefix with a call label in case the corresponding finally block
2224 * calls CheckRun() and that CheckRun() captures the stack, it will have a point to
2225 * restore to that will properly jump back into the finally block.
2226 */
2227 if (useLeave) {
2228 new CallLabel (this, errorAt);
2229 ilGen.Emit (errorAt, OpCodes.Leave, target);
2230 openCallLabel = null;
2231 } else {
2232 ilGen.Emit (errorAt, OpCodes.Br, target);
2233 }
2234 }
2235
2236 /**
2237 * @brief output code for a jump target label statement.
2238 * If there are any backward jumps to the label, do a CheckRun() also.
2239 */
2240 private void GenerateStmtLabel (TokenStmtLabel labelStmt)
2241 {
2242 if (!labelStmt.labelTagged) {
2243 labelStmt.labelStruct = ilGen.DefineLabel ("jump_" + labelStmt.name.val);
2244 labelStmt.labelTagged = true;
2245 }
2246 ilGen.MarkLabel (labelStmt.labelStruct);
2247 if (labelStmt.hasBkwdRefs) {
2248 EmitCallCheckRun (labelStmt, false);
2249 }
2250
2251 /*
2252 * We are going to say that the label falls through.
2253 * It would be nice if we could analyze all referencing
2254 * goto's to see if all of them are not used but we are
2255 * going to assume that if the script writer put a label
2256 * somewhere, it is probably going to be used.
2257 */
2258 mightGetHere = true;
2259 }
2260
2261 /**
2262 * @brief Generate code for a script-defined type's <typename>.$new(<argsig>) method.
2263 * It is used to malloc the object and initialize it.
2264 * It is defined as a script-defined type static method, so the object level
2265 * method gets the XMRInstance pointer passed as arg 0, and the method is
2266 * supposed to return the allocated and constructed XMRSDTypeClObj
2267 * object pointer.
2268 */
2269 private void GenerateStmtNewobj (TokenStmtNewobj newobjStmt)
2270 {
2271 /*
2272 * First off, malloc a new empty XMRSDTypeClObj object
2273 * then call the XMRSDTypeClObj()-level constructor.
2274 * Store the result in local var $objptr.
2275 */
2276 newobjStmt.objptrVar.location.PopPre (this, newobjStmt);
2277 ilGen.Emit (newobjStmt, OpCodes.Ldarg_0);
2278 ilGen.Emit (newobjStmt, OpCodes.Ldc_I4, curDeclFunc.sdtClass.sdTypeIndex);
2279 ilGen.Emit (newobjStmt, OpCodes.Newobj, sdtClassConstructorInfo);
2280 newobjStmt.objptrVar.location.PopPost (this, newobjStmt);
2281
2282 /*
2283 * Now call the script-level constructor.
2284 * Pass the object pointer in $objptr as it's 'this' argument.
2285 * The rest of the args are the script-visible args and are just copied from $new() call.
2286 */
2287 GenerateFromRValCall (newobjStmt.rValCall);
2288
2289 /*
2290 * Put object pointer in retval so it gets returned to caller.
2291 */
2292 newobjStmt.objptrVar.location.PushVal (this, newobjStmt);
2293 ilGen.Emit (newobjStmt, OpCodes.Stloc, retValue);
2294
2295 /*
2296 * Exit the function like a return statement.
2297 * And thus we don't fall through.
2298 */
2299 ilGen.Emit (newobjStmt, OpCodes.Leave, retLabel);
2300 mightGetHere = false;
2301 }
2302
2303 /**
2304 * @brief output code for a return statement.
2305 * @param retStmt = return statement token, including return value if any
2306 */
2307 private void GenerateStmtRet (TokenStmtRet retStmt)
2308 {
2309 if (!mightGetHere) return;
2310
2311 for (TokenStmtBlock stmtBlock = curStmtBlock; stmtBlock != null; stmtBlock = stmtBlock.outerStmtBlock) {
2312 if (stmtBlock.isFinally) {
2313 ErrorMsg (retStmt, "cannot return out of finally");
2314 return;
2315 }
2316 }
2317
2318 if (curDeclFunc.retType is TokenTypeVoid) {
2319 if (retStmt.rVal != null) {
2320 ErrorMsg (retStmt, "function returns void, no value allowed");
2321 return;
2322 }
2323 } else {
2324 if (retStmt.rVal == null) {
2325 ErrorMsg (retStmt, "function requires return value type " + curDeclFunc.retType.ToString ());
2326 return;
2327 }
2328 CompValu rVal = GenerateFromRVal (retStmt.rVal);
2329 rVal.PushVal (this, retStmt.rVal, curDeclFunc.retType);
2330 ilGen.Emit (retStmt, OpCodes.Stloc, retValue);
2331 }
2332
2333 /*
2334 * Use a OpCodes.Leave instruction to break out of any try { } blocks.
2335 * All Leave's inside script-defined try { } need call labels (see GenerateStmtTry()).
2336 */
2337 bool brokeOutOfTry = false;
2338 for (TokenStmtBlock stmtBlock = curStmtBlock; stmtBlock != null; stmtBlock = stmtBlock.outerStmtBlock) {
2339 if (stmtBlock.isTry) {
2340 brokeOutOfTry = true;
2341 break;
2342 }
2343 }
2344 if (brokeOutOfTry) new CallLabel (this, retStmt);
2345 ilGen.Emit (retStmt, OpCodes.Leave, retLabel);
2346 if (brokeOutOfTry) openCallLabel = null;
2347
2348 /*
2349 * 'return' statements never fall through.
2350 */
2351 mightGetHere = false;
2352 }
2353
2354 /**
2355 * @brief the statement is just an expression, most likely an assignment or a ++ or -- thing.
2356 */
2357 private void GenerateStmtRVal (TokenStmtRVal rValStmt)
2358 {
2359 if (!mightGetHere) return;
2360
2361 GenerateFromRVal (rValStmt.rVal);
2362 }
2363
2364 /**
2365 * @brief generate code for a 'state' statement that transitions state.
2366 * It sets the new state by throwing a ScriptChangeStateException.
2367 */
2368 private void GenerateStmtState (TokenStmtState stateStmt)
2369 {
2370 if (!mightGetHere) return;
2371
2372 int index = 0; // 'default' state
2373
2374 /*
2375 * Set new state value by throwing an exception.
2376 * These exceptions aren't catchable by script-level try { } catch { }.
2377 */
2378 if ((stateStmt.state != null) && !stateIndices.TryGetValue (stateStmt.state.val, out index)) {
2379 // The moron XEngine compiles scripts that reference undefined states.
2380 // So rather than produce a compile-time error, we'll throw an exception at runtime.
2381 // ErrorMsg (stateStmt, "undefined state " + stateStmt.state.val);
2382
2383 // throw new UndefinedStateException (stateStmt.state.val);
2384 ilGen.Emit (stateStmt, OpCodes.Ldstr, stateStmt.state.val);
2385 ilGen.Emit (stateStmt, OpCodes.Newobj, scriptUndefinedStateExceptionConstructorInfo);
2386 } else {
2387 ilGen.Emit (stateStmt, OpCodes.Ldc_I4, index); // new state's index
2388 ilGen.Emit (stateStmt, OpCodes.Newobj, scriptChangeStateExceptionConstructorInfo);
2389 }
2390 ilGen.Emit (stateStmt, OpCodes.Throw);
2391
2392 /*
2393 * 'state' statements never fall through.
2394 */
2395 mightGetHere = false;
2396 }
2397
2398 /**
2399 * @brief output code for a 'switch' statement
2400 */
2401 private void GenerateStmtSwitch (TokenStmtSwitch switchStmt)
2402 {
2403 if (!mightGetHere) return;
2404
2405 /*
2406 * Output code to calculate index.
2407 */
2408 CompValu testRVal = GenerateFromRVal (switchStmt.testRVal);
2409
2410 /*
2411 * Generate code based on string or integer index.
2412 */
2413 if ((testRVal.type is TokenTypeKey) || (testRVal.type is TokenTypeStr)) {
2414 GenerateStmtSwitchStr (testRVal, switchStmt);
2415 } else {
2416 GenerateStmtSwitchInt (testRVal, switchStmt);
2417 }
2418 }
2419
2420 private void GenerateStmtSwitchInt (CompValu testRVal, TokenStmtSwitch switchStmt)
2421 {
2422 testRVal.PushVal (this, switchStmt.testRVal, tokenTypeInt);
2423
2424 BreakContTarg oldBreakTarg = curBreakTarg;
2425 ScriptMyLabel defaultLabel = null;
2426 TokenSwitchCase sortedCases = null;
2427 TokenSwitchCase defaultCase = null;
2428
2429 curBreakTarg = new BreakContTarg (this, "switchbreak_" + switchStmt.Unique);
2430
2431 /*
2432 * Build list of cases sorted by ascending values.
2433 * There should not be any overlapping of values.
2434 */
2435 for (TokenSwitchCase thisCase = switchStmt.cases; thisCase != null; thisCase = thisCase.nextCase) {
2436 thisCase.label = ilGen.DefineLabel ("case_" + thisCase.Unique);
2437
2438 /*
2439 * The default case if any, goes in its own separate slot.
2440 */
2441 if (thisCase.rVal1 == null) {
2442 if (defaultCase != null) {
2443 ErrorMsg (thisCase, "only one default case allowed");
2444 ErrorMsg (defaultCase, "...prior default case");
2445 return;
2446 }
2447 defaultCase = thisCase;
2448 defaultLabel = thisCase.label;
2449 continue;
2450 }
2451
2452 /*
2453 * Evaluate case operands, they must be compile-time integer constants.
2454 */
2455 CompValu rVal = GenerateFromRVal (thisCase.rVal1);
2456 if (!IsConstIntExpr (rVal, out thisCase.val1)) {
2457 ErrorMsg (thisCase.rVal1, "must be compile-time char or integer constant");
2458 return;
2459 }
2460 thisCase.val2 = thisCase.val1;
2461 if (thisCase.rVal2 != null) {
2462 rVal = GenerateFromRVal (thisCase.rVal2);
2463 if (!IsConstIntExpr (rVal, out thisCase.val2)) {
2464 ErrorMsg (thisCase.rVal2, "must be compile-time char or integer constant");
2465 return;
2466 }
2467 }
2468 if (thisCase.val2 < thisCase.val1) {
2469 ErrorMsg (thisCase.rVal2, "must be .ge. first value for the case");
2470 return;
2471 }
2472
2473 /*
2474 * Insert into list, sorted by value.
2475 * Note that both limits are inclusive.
2476 */
2477 TokenSwitchCase lastCase = null;
2478 TokenSwitchCase nextCase;
2479 for (nextCase = sortedCases; nextCase != null; nextCase = nextCase.nextSortedCase) {
2480 if (nextCase.val1 > thisCase.val2) break;
2481 if (nextCase.val2 >= thisCase.val1) {
2482 ErrorMsg (thisCase, "value used by previous case");
2483 ErrorMsg (nextCase, "...previous case");
2484 return;
2485 }
2486 lastCase = nextCase;
2487 }
2488 thisCase.nextSortedCase = nextCase;
2489 if (lastCase == null) {
2490 sortedCases = thisCase;
2491 } else {
2492 lastCase.nextSortedCase = thisCase;
2493 }
2494 }
2495
2496 if (defaultLabel == null) {
2497 defaultLabel = ilGen.DefineLabel ("default_" + switchStmt.Unique);
2498 }
2499
2500 /*
2501 * Output code to jump to the case statement's labels based on integer index on stack.
2502 * Note that each case still has the integer index on stack when jumped to.
2503 */
2504 int offset = 0;
2505 for (TokenSwitchCase thisCase = sortedCases; thisCase != null;) {
2506
2507 /*
2508 * Scan through list of cases to find the maximum number of cases who's numvalues-to-case ratio
2509 * is from 0.5 to 2.0. If such a group is found, use a CIL switch for them. If not, just use a
2510 * compare-and-branch for the current case.
2511 */
2512 int numCases = 0;
2513 int numFound = 0;
2514 int lowValue = thisCase.val1;
2515 int numValues = 0;
2516 for (TokenSwitchCase scanCase = thisCase; scanCase != null; scanCase = scanCase.nextSortedCase) {
2517 int nVals = scanCase.val2 - thisCase.val1 + 1;
2518 double ratio = (double)nVals / (double)(++ numCases);
2519 if ((ratio >= 0.5) && (ratio <= 2.0)) {
2520 numFound = numCases;
2521 numValues = nVals;
2522 }
2523 }
2524 if (numFound > 1) {
2525
2526 /*
2527 * There is a group of case's, starting with thisCase, that fall within our criteria, ie,
2528 * that have a nice density of meaningful jumps.
2529 *
2530 * So first generate an array of jumps to the default label (explicit or implicit).
2531 */
2532 ScriptMyLabel[] labels = new ScriptMyLabel[numValues];
2533 for (int i = 0; i < numValues; i ++) {
2534 labels[i] = defaultLabel;
2535 }
2536
2537 /*
2538 * Next, for each case in that group, fill in the corresponding array entries to jump to
2539 * that case's label.
2540 */
2541 do {
2542 for (int i = thisCase.val1; i <= thisCase.val2; i ++) {
2543 labels[i-lowValue] = thisCase.label;
2544 }
2545 thisCase = thisCase.nextSortedCase;
2546 } while (-- numFound > 0);
2547
2548 /*
2549 * Subtract the low value and do the computed jump.
2550 * The OpCodes.Switch falls through if out of range (unsigned compare).
2551 */
2552 if (offset != lowValue) {
2553 ilGen.Emit (switchStmt, OpCodes.Ldc_I4, lowValue - offset);
2554 ilGen.Emit (switchStmt, OpCodes.Sub);
2555 offset = lowValue;
2556 }
2557 ilGen.Emit (switchStmt, OpCodes.Dup);
2558 ilGen.Emit (switchStmt, OpCodes.Switch, labels);
2559 } else {
2560
2561 /*
2562 * It's not economical to do with a computed jump, so output a subtract/compare/branch
2563 * for thisCase.
2564 */
2565 if (lowValue == thisCase.val2) {
2566 ilGen.Emit (switchStmt, OpCodes.Dup);
2567 ilGen.Emit (switchStmt, OpCodes.Ldc_I4, lowValue - offset);
2568 ilGen.Emit (switchStmt, OpCodes.Beq, thisCase.label);
2569 } else {
2570 if (offset != lowValue) {
2571 ilGen.Emit (switchStmt, OpCodes.Ldc_I4, lowValue - offset);
2572 ilGen.Emit (switchStmt, OpCodes.Sub);
2573 offset = lowValue;
2574 }
2575 ilGen.Emit (switchStmt, OpCodes.Dup);
2576 ilGen.Emit (switchStmt, OpCodes.Ldc_I4, thisCase.val2 - offset);
2577 ilGen.Emit (switchStmt, OpCodes.Ble_Un, thisCase.label);
2578 }
2579 thisCase = thisCase.nextSortedCase;
2580 }
2581 }
2582 ilGen.Emit (switchStmt, OpCodes.Br, defaultLabel);
2583
2584 /*
2585 * Output code for the cases themselves, in the order given by the programmer,
2586 * so they fall through as programmer wants. This includes the default case, if any.
2587 *
2588 * Each label is jumped to with the index still on the stack. So pop it off in case
2589 * the case body does a goto outside the switch or a return. If the case body might
2590 * fall through to the next case or the bottom of the switch, push a zero so the stack
2591 * matches in all cases.
2592 */
2593 for (TokenSwitchCase thisCase = switchStmt.cases; thisCase != null; thisCase = thisCase.nextCase) {
2594 ilGen.MarkLabel (thisCase.label); // the branch comes here
2595 ilGen.Emit (thisCase, OpCodes.Pop); // pop the integer index off stack
2596 mightGetHere = true; // it's possible to get here
2597 for (TokenStmt stmt = thisCase.stmts; stmt != null; stmt = (TokenStmt)(stmt.nextToken)) {
2598 GenerateStmt (stmt); // output the case/explicit default body
2599 }
2600 if (mightGetHere) {
2601 ilGen.Emit (thisCase, OpCodes.Ldc_I4_0);
2602 // in case we fall through, push a dummy integer index
2603 }
2604 }
2605
2606 /*
2607 * If no explicit default case, output the default label here.
2608 */
2609 if (defaultCase == null) {
2610 ilGen.MarkLabel (defaultLabel);
2611 mightGetHere = true;
2612 }
2613
2614 /*
2615 * If the last case of the switch falls through out the bottom,
2616 * we have to pop the index still on the stack.
2617 */
2618 if (mightGetHere) {
2619 ilGen.Emit (switchStmt, OpCodes.Pop);
2620 }
2621
2622 /*
2623 * Output the 'break' statement target label.
2624 * Note that the integer index is not on the stack at this point.
2625 */
2626 if (curBreakTarg.used) {
2627 ilGen.MarkLabel (curBreakTarg.label);
2628 mightGetHere = true;
2629 }
2630
2631 curBreakTarg = oldBreakTarg;
2632 }
2633
2634 private void GenerateStmtSwitchStr (CompValu testRVal, TokenStmtSwitch switchStmt)
2635 {
2636 BreakContTarg oldBreakTarg = curBreakTarg;
2637 ScriptMyLabel defaultLabel = null;
2638 TokenSwitchCase caseTreeTop = null;
2639 TokenSwitchCase defaultCase = null;
2640
2641 curBreakTarg = new BreakContTarg (this, "switchbreak_" + switchStmt.Unique);
2642
2643 /*
2644 * Make sure value is in a temp so we don't compute it more than once.
2645 */
2646 if (!(testRVal is CompValuTemp)) {
2647 CompValuTemp temp = new CompValuTemp (testRVal.type, this);
2648 testRVal.PushVal (this, switchStmt);
2649 temp.Pop (this, switchStmt);
2650 testRVal = temp;
2651 }
2652
2653 /*
2654 * Build tree of cases.
2655 * There should not be any overlapping of values.
2656 */
2657 for (TokenSwitchCase thisCase = switchStmt.cases; thisCase != null; thisCase = thisCase.nextCase) {
2658 thisCase.label = ilGen.DefineLabel ("case");
2659
2660 /*
2661 * The default case if any, goes in its own separate slot.
2662 */
2663 if (thisCase.rVal1 == null) {
2664 if (defaultCase != null) {
2665 ErrorMsg (thisCase, "only one default case allowed");
2666 ErrorMsg (defaultCase, "...prior default case");
2667 return;
2668 }
2669 defaultCase = thisCase;
2670 defaultLabel = thisCase.label;
2671 continue;
2672 }
2673
2674 /*
2675 * Evaluate case operands, they must be compile-time string constants.
2676 */
2677 CompValu rVal = GenerateFromRVal (thisCase.rVal1);
2678 if (!IsConstStrExpr (rVal, out thisCase.str1)) {
2679 ErrorMsg (thisCase.rVal1, "must be compile-time string constant");
2680 continue;
2681 }
2682 thisCase.str2 = thisCase.str1;
2683 if (thisCase.rVal2 != null) {
2684 rVal = GenerateFromRVal (thisCase.rVal2);
2685 if (!IsConstStrExpr (rVal, out thisCase.str2)) {
2686 ErrorMsg (thisCase.rVal2, "must be compile-time string constant");
2687 continue;
2688 }
2689 }
2690 if (String.Compare (thisCase.str2, thisCase.str1, StringComparison.Ordinal) < 0) {
2691 ErrorMsg (thisCase.rVal2, "must be .ge. first value for the case");
2692 continue;
2693 }
2694
2695 /*
2696 * Insert into list, sorted by value.
2697 * Note that both limits are inclusive.
2698 */
2699 caseTreeTop = InsertCaseInTree (caseTreeTop, thisCase);
2700 }
2701
2702 /*
2703 * Balance tree so we end up generating code that does O(log2 n) comparisons.
2704 */
2705 caseTreeTop = BalanceTree (caseTreeTop);
2706
2707 /*
2708 * Output compare and branch instructions in a tree-like fashion so we do O(log2 n) comparisons.
2709 */
2710 if (defaultLabel == null) {
2711 defaultLabel = ilGen.DefineLabel ("default");
2712 }
2713 OutputStrCase (testRVal, caseTreeTop, defaultLabel);
2714
2715 /*
2716 * Output code for the cases themselves, in the order given by the programmer,
2717 * so they fall through as programmer wants. This includes the default case, if any.
2718 */
2719 for (TokenSwitchCase thisCase = switchStmt.cases; thisCase != null; thisCase = thisCase.nextCase) {
2720 ilGen.MarkLabel (thisCase.label); // the branch comes here
2721 mightGetHere = true; // it's possible to get here
2722 for (TokenStmt stmt = thisCase.stmts; stmt != null; stmt = (TokenStmt)(stmt.nextToken)) {
2723 GenerateStmt (stmt); // output the case/explicit default body
2724 }
2725 }
2726
2727 /*
2728 * If no explicit default case, output the default label here.
2729 */
2730 if (defaultCase == null) {
2731 ilGen.MarkLabel (defaultLabel);
2732 mightGetHere = true;
2733 }
2734
2735 /*
2736 * Output the 'break' statement target label.
2737 */
2738 if (curBreakTarg.used) {
2739 ilGen.MarkLabel (curBreakTarg.label);
2740 mightGetHere = true;
2741 }
2742
2743 curBreakTarg = oldBreakTarg;
2744 }
2745
2746 /**
2747 * @brief Insert a case in a tree of cases
2748 * @param r = root of existing cases to insert into
2749 * @param n = new case being inserted
2750 * @returns new root with new case inserted
2751 */
2752 private TokenSwitchCase InsertCaseInTree (TokenSwitchCase r, TokenSwitchCase n)
2753 {
2754 if (r == null) return n;
2755
2756 TokenSwitchCase t = r;
2757 while (true) {
2758 if (String.Compare (n.str2, t.str1, StringComparison.Ordinal) < 0) {
2759 if (t.lowerCase == null) {
2760 t.lowerCase = n;
2761 break;
2762 }
2763 t = t.lowerCase;
2764 continue;
2765 }
2766 if (String.Compare (n.str1, t.str2, StringComparison.Ordinal) > 0) {
2767 if (t.higherCase == null) {
2768 t.higherCase = n;
2769 break;
2770 }
2771 t = t.higherCase;
2772 continue;
2773 }
2774 ErrorMsg (n, "duplicate case");
2775 ErrorMsg (r, "...duplicate of");
2776 break;
2777 }
2778 return r;
2779 }
2780
2781 /**
2782 * @brief Balance a tree so left & right halves contain same number within +-1
2783 * @param r = root of tree to balance
2784 * @returns new root
2785 */
2786 private static TokenSwitchCase BalanceTree (TokenSwitchCase r)
2787 {
2788 if (r == null) return r;
2789
2790 int lc = CountTree (r.lowerCase);
2791 int hc = CountTree (r.higherCase);
2792 TokenSwitchCase n, x;
2793
2794 /*
2795 * If lower side is heavy, move highest nodes from lower side to
2796 * higher side until balanced.
2797 */
2798 while (lc > hc + 1) {
2799 x = ExtractHighest (r.lowerCase, out n);
2800 n.lowerCase = x;
2801 n.higherCase = r;
2802 r.lowerCase = null;
2803 r = n;
2804 lc --;
2805 hc ++;
2806 }
2807
2808 /*
2809 * If higher side is heavy, move lowest nodes from higher side to
2810 * lower side until balanced.
2811 */
2812 while (hc > lc + 1) {
2813 x = ExtractLowest (r.higherCase, out n);
2814 n.higherCase = x;
2815 n.lowerCase = r;
2816 r.higherCase = null;
2817 r = n;
2818 lc ++;
2819 hc --;
2820 }
2821
2822 /*
2823 * Now balance each side because they can be lopsided individually.
2824 */
2825 r.lowerCase = BalanceTree (r.lowerCase);
2826 r.higherCase = BalanceTree (r.higherCase);
2827 return r;
2828 }
2829
2830 /**
2831 * @brief Get number of nodes in a tree
2832 * @param n = root of tree to count
2833 * @returns number of nodes including root
2834 */
2835 private static int CountTree (TokenSwitchCase n)
2836 {
2837 if (n == null) return 0;
2838 return 1 + CountTree (n.lowerCase) + CountTree (n.higherCase);
2839 }
2840
2841 // Extract highest node from a tree
2842 // @param r = root of tree to extract highest from
2843 // @returns new root after node has been extracted
2844 // n = node that was extracted from tree
2845 private static TokenSwitchCase ExtractHighest (TokenSwitchCase r, out TokenSwitchCase n)
2846 {
2847 if (r.higherCase == null) {
2848 n = r;
2849 return r.lowerCase;
2850 }
2851 r.higherCase = ExtractHighest (r.higherCase, out n);
2852 return r;
2853 }
2854
2855 // Extract lowest node from a tree
2856 // @param r = root of tree to extract lowest from
2857 // @returns new root after node has been extracted
2858 // n = node that was extracted from tree
2859 private static TokenSwitchCase ExtractLowest (TokenSwitchCase r, out TokenSwitchCase n)
2860 {
2861 if (r.lowerCase == null) {
2862 n = r;
2863 return r.higherCase;
2864 }
2865 r.lowerCase = ExtractLowest (r.lowerCase, out n);
2866 return r;
2867 }
2868
2869 /**
2870 * Output code for string-style case of a switch/case to jump to the script code associated with the case.
2871 * @param testRVal = value being switched on
2872 * @param thisCase = case that the code is being output for
2873 * @param defaultLabel = where the default clause is (or past all cases if none)
2874 * Note:
2875 * Outputs code for this case and the lowerCase and higherCases if any.
2876 * If no lowerCase or higherCase, outputs a br to defaultLabel so this code never falls through.
2877 */
2878 private void OutputStrCase (CompValu testRVal, TokenSwitchCase thisCase, ScriptMyLabel defaultLabel)
2879 {
2880 /*
2881 * If nothing lower on tree and there is a single case value,
2882 * just do one compare for equality.
2883 */
2884 if ((thisCase.lowerCase == null) && (thisCase.higherCase == null) && (thisCase.str1 == thisCase.str2)) {
2885 testRVal.PushVal (this, thisCase, tokenTypeStr);
2886 ilGen.Emit (thisCase, OpCodes.Ldstr, thisCase.str1);
2887 ilGen.Emit (thisCase, OpCodes.Ldc_I4, (int)StringComparison.Ordinal);
2888 ilGen.Emit (thisCase, OpCodes.Call, stringCompareMethodInfo);
2889 ilGen.Emit (thisCase, OpCodes.Brfalse, thisCase.label);
2890 ilGen.Emit (thisCase, OpCodes.Br, defaultLabel);
2891 return;
2892 }
2893
2894 /*
2895 * Determine where to jump if switch value is lower than lower case value.
2896 */
2897 ScriptMyLabel lowerLabel = defaultLabel;
2898 if (thisCase.lowerCase != null) {
2899 lowerLabel = ilGen.DefineLabel ("lower");
2900 }
2901
2902 /*
2903 * If single case value, put comparison result in this temp.
2904 */
2905 CompValuTemp cmpv1 = null;
2906 if (thisCase.str1 == thisCase.str2) {
2907 cmpv1 = new CompValuTemp (tokenTypeInt, this);
2908 }
2909
2910 /*
2911 * If switch value .lt. lower case value, jump to lower label.
2912 * Maybe save comparison result in a temp.
2913 */
2914 testRVal.PushVal (this, thisCase, tokenTypeStr);
2915 ilGen.Emit (thisCase, OpCodes.Ldstr, thisCase.str1);
2916 ilGen.Emit (thisCase, OpCodes.Ldc_I4, (int)StringComparison.Ordinal);
2917 ilGen.Emit (thisCase, OpCodes.Call, stringCompareMethodInfo);
2918 if (cmpv1 != null) {
2919 ilGen.Emit (thisCase, OpCodes.Dup);
2920 cmpv1.Pop (this, thisCase);
2921 }
2922 ilGen.Emit (thisCase, OpCodes.Ldc_I4_0);
2923 ilGen.Emit (thisCase, OpCodes.Blt, lowerLabel);
2924
2925 /*
2926 * If switch value .le. higher case value, jump to case code.
2927 * Maybe get comparison from the temp.
2928 */
2929 if (cmpv1 == null) {
2930 testRVal.PushVal (this, thisCase, tokenTypeStr);
2931 ilGen.Emit (thisCase, OpCodes.Ldstr, thisCase.str2);
2932 ilGen.Emit (thisCase, OpCodes.Ldc_I4, (int)StringComparison.Ordinal);
2933 ilGen.Emit (thisCase, OpCodes.Call, stringCompareMethodInfo);
2934 } else {
2935 cmpv1.PushVal (this, thisCase);
2936 }
2937 ilGen.Emit (thisCase, OpCodes.Ldc_I4_0);
2938 ilGen.Emit (thisCase, OpCodes.Ble, thisCase.label);
2939
2940 /*
2941 * Output code for higher comparison if any.
2942 */
2943 if (thisCase.higherCase == null) {
2944 ilGen.Emit (thisCase, OpCodes.Br, defaultLabel);
2945 } else {
2946 OutputStrCase (testRVal, thisCase.higherCase, defaultLabel);
2947 }
2948
2949 /*
2950 * Output code for lower comparison if any.
2951 */
2952 if (thisCase.lowerCase != null) {
2953 ilGen.MarkLabel (lowerLabel);
2954 OutputStrCase (testRVal, thisCase.lowerCase, defaultLabel);
2955 }
2956 }
2957
2958 /**
2959 * @brief output code for a throw statement.
2960 * @param throwStmt = throw statement token, including value to be thrown
2961 */
2962 private void GenerateStmtThrow (TokenStmtThrow throwStmt)
2963 {
2964 if (!mightGetHere) return;
2965
2966 /*
2967 * 'throw' statements never fall through.
2968 */
2969 mightGetHere = false;
2970
2971 /*
2972 * Output code for either a throw or a rethrow.
2973 */
2974 if (throwStmt.rVal == null) {
2975 for (TokenStmtBlock blk = curStmtBlock; blk != null; blk = blk.outerStmtBlock) {
2976 if (curStmtBlock.isCatch) {
2977 ilGen.Emit (throwStmt, OpCodes.Rethrow);
2978 return;
2979 }
2980 }
2981 ErrorMsg (throwStmt, "rethrow allowed only in catch clause");
2982 } else {
2983 CompValu rVal = GenerateFromRVal (throwStmt.rVal);
2984 rVal.PushVal (this, throwStmt.rVal, tokenTypeObj);
2985 ilGen.Emit (throwStmt, OpCodes.Call, thrownExceptionWrapMethodInfo);
2986 ilGen.Emit (throwStmt, OpCodes.Throw);
2987 }
2988 }
2989
2990 /**
2991 * @brief output code for a try/catch/finally block
2992 */
2993 private void GenerateStmtTry (TokenStmtTry tryStmt)
2994 {
2995 if (!mightGetHere) return;
2996
2997 /*
2998 * Reducer should make sure we have exactly one of catch or finally.
2999 */
3000 if ((tryStmt.catchStmt == null) && (tryStmt.finallyStmt == null)) {
3001 throw new Exception ("must have a catch or a finally on try");
3002 }
3003 if ((tryStmt.catchStmt != null) && (tryStmt.finallyStmt != null)) {
3004 throw new Exception ("can't have both catch and finally on same try");
3005 }
3006
3007 /*
3008 * Stack the call labels.
3009 * Try blocks have their own series of call labels.
3010 */
3011 ScriptMyLocal saveCallNo = actCallNo;
3012 LinkedList<CallLabel> saveCallLabels = actCallLabels;
3013
3014 /*
3015 * Generate code for either try { } catch { } or try { } finally { }.
3016 */
3017 if (tryStmt.catchStmt != null) GenerateStmtTryCatch (tryStmt);
3018 if (tryStmt.finallyStmt != null) GenerateStmtTryFinally (tryStmt);
3019
3020 /*
3021 * Restore call labels.
3022 */
3023 actCallNo = saveCallNo;
3024 actCallLabels = saveCallLabels;
3025 }
3026
3027
3028 /**
3029 * @brief output code for a try/catch block
3030 *
3031 * int __tryCallNo = -1; // call number within try { } subblock
3032 * int __catCallNo = -1; // call number within catch { } subblock
3033 * Exception __catThrown = null; // caught exception
3034 * <oldCallLabel>: // the outside world jumps here to restore us no matter ...
3035 * try { // ... where we actually were inside of try/catch
3036 * if (__tryCallNo >= 0) goto tryCallSw; // maybe go do restore
3037 * <try body using __tryCallNo> // execute script-defined code
3038 * // ...stack capture WILL run catch { } subblock
3039 * leave tryEnd; // exits
3040 * tryThrow:<tryCallLabel>:
3041 * throw new ScriptRestoreCatchException(__catThrown); // catch { } was running, jump to its beginning
3042 * tryCallSw: // restoring...
3043 * switch (__tryCallNo) back up into <try body> // not catching, jump back inside try
3044 * } catch (Exception exc) {
3045 * exc = ScriptRestoreCatchException.Unwrap(exc); // unwrap possible ScriptRestoreCatchException
3046 * if (exc == null) goto catchRetro; // rethrow if IXMRUncatchable (eg, StackCaptureException)
3047 * __catThrown = exc; // save what was thrown so restoring try { } will throw it again
3048 * catchVar = exc; // set up script-visible variable
3049 * __tryCallNo = tryThrow:<tryCallLabel>
3050 * if (__catCallNo >= 0) goto catchCallSw; // if restoring, go check below
3051 * <catch body using __catCallNo> // normal, execute script-defined code
3052 * leave tryEnd; // all done, exit catch { }
3053 * catchRetro:
3054 * rethrow;
3055 * catchCallSw:
3056 * switch (__catCallNo) back up into <catch body> // restart catch { } code wherever it was
3057 * }
3058 * tryEnd:
3059 */
3060 private void GenerateStmtTryCatch (TokenStmtTry tryStmt)
3061 {
3062 CompValuTemp tryCallNo = new CompValuTemp (tokenTypeInt, this);
3063 CompValuTemp catCallNo = new CompValuTemp (tokenTypeInt, this);
3064 CompValuTemp catThrown = new CompValuTemp (tokenTypeExc, this);
3065
3066 ScriptMyLabel tryCallSw = ilGen.DefineLabel ("__tryCallSw_" + tryStmt.Unique);
3067 ScriptMyLabel catchRetro = ilGen.DefineLabel ("__catchRetro_" + tryStmt.Unique);
3068 ScriptMyLabel catchCallSw = ilGen.DefineLabel ("__catchCallSw_" + tryStmt.Unique);
3069 ScriptMyLabel tryEnd = ilGen.DefineLabel ("__tryEnd_" + tryStmt.Unique);
3070
3071 SetCallNo (tryStmt, tryCallNo, -1);
3072 SetCallNo (tryStmt, catCallNo, -1);
3073 ilGen.Emit (tryStmt, OpCodes.Ldnull);
3074 catThrown.Pop (this, tryStmt);
3075
3076 new CallLabel (this, tryStmt); // <oldcalllabel>:
3077 ilGen.BeginExceptionBlock (); // try {
3078 openCallLabel = null;
3079 if (DEBUG_TRYSTMT) {
3080 ilGen.Emit (tryStmt, OpCodes.Ldstr, "enter try*: " + tryStmt.line + " callMode=");
3081 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3082 PushXMRInst ();
3083 ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
3084 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3085 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3086 ilGen.Emit (tryStmt, OpCodes.Ldstr, " tryCallNo=");
3087 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3088 tryCallNo.PushVal (this, tryStmt);
3089 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3090 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3091 ilGen.Emit (tryStmt, OpCodes.Ldstr, " catThrown.IsNull=");
3092 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3093 catThrown.PushVal (this, tryStmt);
3094 ilGen.Emit (tryStmt, OpCodes.Ldnull);
3095 ilGen.Emit (tryStmt, OpCodes.Ceq);
3096 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3097 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3098 ilGen.Emit (tryStmt, OpCodes.Ldstr, " catCallNo=");
3099 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3100 catCallNo.PushVal (this, tryStmt);
3101 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3102 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3103 ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
3104 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3105 }
3106
3107 GetCallNo (tryStmt, tryCallNo); // if (__tryCallNo >= 0) goto tryCallSw;
3108 ilGen.Emit (tryStmt, OpCodes.Ldc_I4_0);
3109 ilGen.Emit (tryStmt, OpCodes.Bge, tryCallSw);
3110
3111 actCallNo = tryCallNo.localBuilder; // set up __tryCallNo for call labels
3112 actCallLabels = new LinkedList<CallLabel> ();
3113
3114 GenerateStmtBlock (tryStmt.tryStmt); // output the try block statement subblock
3115
3116 bool tryBlockFallsOutBottom = mightGetHere;
3117 if (tryBlockFallsOutBottom) {
3118 new CallLabel (this, tryStmt); // <tryCallLabel>:
3119 ilGen.Emit (tryStmt, OpCodes.Leave, tryEnd); // leave tryEnd;
3120 openCallLabel = null;
3121 }
3122
3123 CallLabel tryThrow = new CallLabel (this, tryStmt); // tryThrow:<tryCallLabel>:
3124 if (DEBUG_TRYSTMT) {
3125 ilGen.Emit (tryStmt, OpCodes.Ldstr, "tryThrow*: " + tryStmt.line + " catThrown=");
3126 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3127 catThrown.PushVal (this, tryStmt);
3128 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3129 ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
3130 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3131 }
3132 catThrown.PushVal (this, tryStmt); // throw new ScriptRestoreCatchException (__catThrown);
3133 ilGen.Emit (tryStmt, OpCodes.Newobj, scriptRestoreCatchExceptionConstructorInfo);
3134 ilGen.Emit (tryStmt, OpCodes.Throw);
3135 openCallLabel = null;
3136
3137 ilGen.MarkLabel (tryCallSw); // tryCallSw:
3138 if (DEBUG_TRYSTMT) {
3139 ilGen.Emit (tryStmt, OpCodes.Ldstr, "tryCallSw*: " + tryStmt.line + " tryCallNo=");
3140 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3141 tryCallNo.PushVal (this, tryStmt);
3142 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3143 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3144 ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
3145 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3146 }
3147 OutputCallNoSwitchStmt (); // switch (tryCallNo) ...
3148
3149 CompValuLocalVar catchVarLocExc = null;
3150 CompValuTemp catchVarLocStr = null;
3151
3152 if (tryStmt.catchVar.type.ToSysType () == typeof (Exception)) {
3153 catchVarLocExc = new CompValuLocalVar (tryStmt.catchVar.type, tryStmt.catchVar.name.val, this);
3154 } else if (tryStmt.catchVar.type.ToSysType () == typeof (String)) {
3155 catchVarLocStr = new CompValuTemp (tryStmt.catchVar.type, this);
3156 }
3157
3158 ScriptMyLocal excLocal = ilGen.DeclareLocal (typeof (String), "catchstr_" + tryStmt.Unique);
3159
3160 ilGen.BeginCatchBlock (typeof (Exception)); // start of the catch block that can catch any exception
3161 if (DEBUG_TRYSTMT) {
3162 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldstr, "enter catch*: " + tryStmt.line + " callMode=");
3163 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3164 PushXMRInst ();
3165 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldfld, callModeFieldInfo);
3166 ilGen.Emit (tryStmt.catchStmt, OpCodes.Box, typeof (int));
3167 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3168 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldstr, " catCallNo=");
3169 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3170 catCallNo.PushVal (this, tryStmt);
3171 ilGen.Emit (tryStmt.catchStmt, OpCodes.Box, typeof (int));
3172 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3173 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldstr, " exc=");
3174 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3175 ilGen.Emit (tryStmt.catchStmt, OpCodes.Dup);
3176 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3177 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldstr, "\n");
3178 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, consoleWriteMethodInfo);
3179 }
3180 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, scriptRestoreCatchExceptionUnwrap);
3181 // exc = ScriptRestoreCatchException.Unwrap (exc);
3182 ilGen.Emit (tryStmt.catchStmt, OpCodes.Dup); // rethrow if IXMRUncatchable (eg, StackCaptureException)
3183 ilGen.Emit (tryStmt.catchStmt, OpCodes.Brfalse, catchRetro);
3184 if (tryStmt.catchVar.type.ToSysType () == typeof (Exception)) {
3185 tryStmt.catchVar.location = catchVarLocExc;
3186 ilGen.Emit (tryStmt.catchStmt, OpCodes.Dup);
3187 catThrown.Pop (this, tryStmt); // store exception object in catThrown
3188 catchVarLocExc.Pop (this, tryStmt.catchVar.name); // also store in script-visible variable
3189 } else if (tryStmt.catchVar.type.ToSysType () == typeof (String)) {
3190 tryStmt.catchVar.location = catchVarLocStr;
3191 ilGen.Emit (tryStmt.catchStmt, OpCodes.Dup);
3192 catThrown.Pop (this, tryStmt); // store exception object in catThrown
3193 ilGen.Emit (tryStmt.catchStmt, OpCodes.Call, catchExcToStrMethodInfo);
3194
3195 ilGen.Emit (tryStmt.catchStmt, OpCodes.Stloc, excLocal);
3196 catchVarLocStr.PopPre (this, tryStmt.catchVar.name);
3197 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldloc, excLocal);
3198 catchVarLocStr.PopPost (this, tryStmt.catchVar.name, tokenTypeStr);
3199 } else {
3200 throw new Exception ("bad catch var type " + tryStmt.catchVar.type.ToString ());
3201 }
3202
3203 SetCallNo (tryStmt, tryCallNo, tryThrow.index); // __tryCallNo = tryThrow so it knows to do 'throw catThrown' on restore
3204
3205 GetCallNo (tryStmt, catCallNo); // if (__catCallNo >= 0) goto catchCallSw;
3206 ilGen.Emit (tryStmt.catchStmt, OpCodes.Ldc_I4_0);
3207 ilGen.Emit (tryStmt.catchStmt, OpCodes.Bge, catchCallSw);
3208
3209 actCallNo = catCallNo.localBuilder; // set up __catCallNo for call labels
3210 actCallLabels.Clear ();
3211 mightGetHere = true; // if we can get to the 'try' assume we can get to the 'catch'
3212 GenerateStmtBlock (tryStmt.catchStmt); // output catch clause statement subblock
3213
3214 if (mightGetHere) {
3215 new CallLabel (this, tryStmt.catchStmt);
3216 ilGen.Emit (tryStmt.catchStmt, OpCodes.Leave, tryEnd);
3217 openCallLabel = null;
3218 }
3219
3220 ilGen.MarkLabel (catchRetro); // not a script-visible exception, rethrow it
3221 ilGen.Emit (tryStmt.catchStmt, OpCodes.Pop);
3222 ilGen.Emit (tryStmt.catchStmt, OpCodes.Rethrow);
3223
3224 ilGen.MarkLabel (catchCallSw);
3225 OutputCallNoSwitchStmt (); // restoring, jump back inside script-defined body
3226
3227 ilGen.EndExceptionBlock ();
3228 ilGen.MarkLabel (tryEnd);
3229
3230 mightGetHere |= tryBlockFallsOutBottom; // also get here if try body falls out bottom
3231 }
3232
3233 /**
3234 * @brief output code for a try/finally block
3235 *
3236 * This is such a mess because there is hidden state for the finally { } that we have to recreate.
3237 * The finally { } can be entered either via an exception being thrown in the try { } or a leave
3238 * being executed in the try { } whose target is outside the try { } finally { }.
3239 *
3240 * For the thrown exception case, we slip in a try { } catch { } wrapper around the original try { }
3241 * body. This will sense any thrown exception that would execute the finally { }. Then we have our
3242 * try { } throw the exception on restore which gets the finally { } called and on its way again.
3243 *
3244 * For the leave case, we prefix all leave instructions with a call label and we explicitly chain
3245 * all leaves through each try { } that has an associated finally { } that the leave would unwind
3246 * through. This gets each try { } to simply jump to the correct leave instruction which immediately
3247 * invokes the corresponding finally { } and then chains to the next leave instruction on out until
3248 * it gets to its target.
3249 *
3250 * int __finCallNo = -1; // call number within finally { } subblock
3251 * int __tryCallNo = -1; // call number within try { } subblock
3252 * Exception __catThrown = null; // caught exception
3253 * <oldCallLabel>: // the outside world jumps here to restore us no matter ...
3254 * try { // ... where we actually were inside of try/finally
3255 * try {
3256 * if (__tryCallNo >= 0) goto tryCallSw; // maybe go do restore
3257 * <try body using __tryCallNo> // execute script-defined code
3258 * // ...stack capture WILL run catch/finally { } subblock
3259 * leave tryEnd; // executes finally { } subblock and exits
3260 * tryThrow:<tryCallLabel>:
3261 * throw new ScriptRestoreCatchException(__catThrown); // catch { } was running, jump to its beginning
3262 * tryCallSw: // restoring...
3263 * switch (__tryCallNo) back up into <try body> // jump back inside try, ...
3264 * // ... maybe to a leave if we were doing finally { } subblock
3265 * } catch (Exception exc) { // in case we're getting to finally { } via a thrown exception:
3266 * exc = ScriptRestoreCatchException.Unwrap(exc); // unwrap possible ScriptRestoreCatchException
3267 * if (callMode == CallMode_SAVE) goto catchRetro; // don't touch anything if capturing stack
3268 * __catThrown = exc; // save exception so try { } can throw it on restore
3269 * __tryCallNo = tryThrow:<tryCallLabel>; // tell try { } to throw it on restore
3270 * catchRetro:
3271 * rethrow; // in any case, go on to finally { } subblock now
3272 * }
3273 * } finally {
3274 * if (callMode == CallMode_SAVE) goto finEnd; // don't touch anything if capturing stack
3275 * if (__finCallNo >= 0) goto finCallSw; // maybe go do restore
3276 * <finally body using __finCallNo> // normal, execute script-defined code
3277 * finEnd:
3278 * endfinally // jump to leave/throw target or next outer finally { }
3279 * finCallSw:
3280 * switch (__finCallNo) back up into <finally body> // restoring, restart finally { } code wherever it was
3281 * }
3282 * tryEnd:
3283 */
3284 private void GenerateStmtTryFinally (TokenStmtTry tryStmt)
3285 {
3286 CompValuTemp finCallNo = new CompValuTemp (tokenTypeInt, this);
3287 CompValuTemp tryCallNo = new CompValuTemp (tokenTypeInt, this);
3288 CompValuTemp catThrown = new CompValuTemp (tokenTypeExc, this);
3289
3290 ScriptMyLabel tryCallSw = ilGen.DefineLabel ( "__tryCallSw_" + tryStmt.Unique);
3291 ScriptMyLabel catchRetro = ilGen.DefineLabel ( "__catchRetro_" + tryStmt.Unique);
3292 ScriptMyLabel finCallSw = ilGen.DefineLabel ( "__finCallSw_" + tryStmt.Unique);
3293 BreakContTarg finEnd = new BreakContTarg (this, "__finEnd_" + tryStmt.Unique);
3294 ScriptMyLabel tryEnd = ilGen.DefineLabel ( "__tryEnd_" + tryStmt.Unique);
3295
3296 SetCallNo (tryStmt, finCallNo, -1);
3297 SetCallNo (tryStmt, tryCallNo, -1);
3298 ilGen.Emit (tryStmt, OpCodes.Ldnull);
3299 catThrown.Pop (this, tryStmt);
3300
3301 new CallLabel (this, tryStmt); // <oldcalllabel>:
3302 ilGen.BeginExceptionBlock (); // try {
3303 ilGen.BeginExceptionBlock (); // try {
3304 openCallLabel = null;
3305 if (DEBUG_TRYSTMT) {
3306 ilGen.Emit (tryStmt, OpCodes.Ldstr, "enter try*: " + tryStmt.line + " callMode=");
3307 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3308 PushXMRInst ();
3309 ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
3310 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3311 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3312 ilGen.Emit (tryStmt, OpCodes.Ldstr, " tryCallNo=");
3313 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3314 tryCallNo.PushVal (this, tryStmt);
3315 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3316 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3317 ilGen.Emit (tryStmt, OpCodes.Ldstr, " finCallNo=");
3318 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3319 finCallNo.PushVal (this, tryStmt);
3320 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3321 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3322 ilGen.Emit (tryStmt, OpCodes.Ldstr, " catThrown.IsNull=");
3323 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3324 catThrown.PushVal (this, tryStmt);
3325 ilGen.Emit (tryStmt, OpCodes.Ldnull);
3326 ilGen.Emit (tryStmt, OpCodes.Ceq);
3327 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3328 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3329 ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
3330 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3331 }
3332
3333 GetCallNo (tryStmt, tryCallNo); // if (__tryCallNo >= 0) goto tryCallSw;
3334 ilGen.Emit (tryStmt, OpCodes.Ldc_I4_0);
3335 ilGen.Emit (tryStmt, OpCodes.Bge, tryCallSw);
3336
3337 actCallNo = tryCallNo.localBuilder; // set up __tryCallNo for call labels
3338 actCallLabels = new LinkedList<CallLabel> ();
3339
3340 GenerateStmtBlock (tryStmt.tryStmt); // output the try block statement subblock
3341
3342 if (mightGetHere) {
3343 new CallLabel (this, tryStmt); // <newCallLabel>:
3344 ilGen.Emit (tryStmt, OpCodes.Leave, tryEnd); // leave tryEnd;
3345 openCallLabel = null;
3346 }
3347
3348 foreach (IntermediateLeave iLeave in tryStmt.iLeaves.Values) {
3349 ilGen.MarkLabel (iLeave.jumpIntoLabel); // intr2_exit:
3350 new CallLabel (this, tryStmt); // tryCallNo = n;
3351 ilGen.Emit (tryStmt, OpCodes.Leave, iLeave.jumpAwayLabel); // __callNo_n_: leave int1_exit;
3352 openCallLabel = null;
3353 }
3354
3355 CallLabel tryThrow = new CallLabel (this, tryStmt); // tryThrow:<tryCallLabel>:
3356 if (DEBUG_TRYSTMT) {
3357 ilGen.Emit (tryStmt, OpCodes.Ldstr, "tryThrow*: " + tryStmt.line + " catThrown=");
3358 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3359 catThrown.PushVal (this, tryStmt);
3360 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3361 ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
3362 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3363 }
3364 catThrown.PushVal (this, tryStmt); // throw new ScriptRestoreCatchException (__catThrown);
3365 ilGen.Emit (tryStmt, OpCodes.Newobj, scriptRestoreCatchExceptionConstructorInfo);
3366 ilGen.Emit (tryStmt, OpCodes.Throw);
3367 openCallLabel = null;
3368
3369 ilGen.MarkLabel (tryCallSw); // tryCallSw:
3370 OutputCallNoSwitchStmt (); // switch (tryCallNo) ...
3371 // }
3372
3373 ilGen.BeginCatchBlock (typeof (Exception)); // start of the catch block that can catch any exception
3374 if (DEBUG_TRYSTMT) {
3375 ilGen.Emit (tryStmt, OpCodes.Ldstr, "enter catch*: " + tryStmt.line + " callMode=");
3376 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3377 PushXMRInst ();
3378 ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
3379 ilGen.Emit (tryStmt, OpCodes.Box, typeof (int));
3380 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3381 ilGen.Emit (tryStmt, OpCodes.Ldstr, " exc=");
3382 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3383 ilGen.Emit (tryStmt, OpCodes.Dup);
3384 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3385 ilGen.Emit (tryStmt, OpCodes.Ldstr, "\n");
3386 ilGen.Emit (tryStmt, OpCodes.Call, consoleWriteMethodInfo);
3387 }
3388 ilGen.Emit (tryStmt, OpCodes.Call, scriptRestoreCatchExceptionUnwrap); // exc = ScriptRestoreCatchException.Unwrap (exc);
3389 PushXMRInst (); // if (callMode == CallMode_SAVE) goto catchRetro;
3390 ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
3391 ilGen.Emit (tryStmt, OpCodes.Ldc_I4, XMRInstAbstract.CallMode_SAVE);
3392 ilGen.Emit (tryStmt, OpCodes.Beq, catchRetro);
3393
3394 catThrown.Pop (this, tryStmt); // __catThrown = exc;
3395 SetCallNo (tryStmt, tryCallNo, tryThrow.index); // __tryCallNo = tryThrow:<tryCallLabel>;
3396 ilGen.Emit (tryStmt, OpCodes.Rethrow);
3397
3398 ilGen.MarkLabel (catchRetro); // catchRetro:
3399 ilGen.Emit (tryStmt, OpCodes.Pop);
3400 ilGen.Emit (tryStmt, OpCodes.Rethrow); // rethrow;
3401
3402 ilGen.EndExceptionBlock (); // }
3403
3404 ilGen.BeginFinallyBlock (); // start of the finally block
3405
3406 PushXMRInst (); // if (callMode == CallMode_SAVE) goto finEnd;
3407 ilGen.Emit (tryStmt, OpCodes.Ldfld, callModeFieldInfo);
3408 ilGen.Emit (tryStmt, OpCodes.Ldc_I4, XMRInstAbstract.CallMode_SAVE);
3409 ilGen.Emit (tryStmt, OpCodes.Beq, finEnd.label);
3410
3411 GetCallNo (tryStmt, finCallNo); // if (__finCallNo >= 0) goto finCallSw;
3412 ilGen.Emit (tryStmt, OpCodes.Ldc_I4_0);
3413 ilGen.Emit (tryStmt, OpCodes.Bge, finCallSw);
3414
3415 actCallNo = finCallNo.localBuilder; // set up __finCallNo for call labels
3416 actCallLabels.Clear ();
3417 mightGetHere = true; // if we can get to the 'try' assume we can get to the 'finally'
3418 GenerateStmtBlock (tryStmt.finallyStmt); // output finally clause statement subblock
3419
3420 ilGen.MarkLabel (finEnd.label); // finEnd:
3421 ilGen.Emit (tryStmt, OpCodes.Endfinally); // return out to next finally { } or catch { } or leave target
3422
3423 ilGen.MarkLabel (finCallSw); // restore mode, switch (finCallNo) ...
3424 OutputCallNoSwitchStmt ();
3425
3426 ilGen.EndExceptionBlock ();
3427 ilGen.MarkLabel (tryEnd);
3428
3429 mightGetHere |= finEnd.used; // get here if finally body falls through or has a break statement
3430 }
3431
3432 /**
3433 * @brief Generate code to initialize a variable to its default value.
3434 */
3435 private void GenerateStmtVarIniDef (TokenStmtVarIniDef varIniDefStmt)
3436 {
3437 if (!mightGetHere) return;
3438
3439 CompValu left = GenerateFromLVal (varIniDefStmt.var);
3440 left.PopPre (this, varIniDefStmt);
3441 PushDefaultValue (left.type);
3442 left.PopPost (this, varIniDefStmt);
3443 }
3444
3445 /**
3446 * @brief generate code for a 'while' statement including the loop body.
3447 */
3448 private void GenerateStmtWhile (TokenStmtWhile whileStmt)
3449 {
3450 if (!mightGetHere) return;
3451
3452 BreakContTarg oldBreakTarg = curBreakTarg;
3453 BreakContTarg oldContTarg = curContTarg;
3454 ScriptMyLabel loopLabel = ilGen.DefineLabel ("whileloop_" + whileStmt.Unique);
3455
3456 curBreakTarg = new BreakContTarg (this, "whilebreak_" + whileStmt.Unique);
3457 curContTarg = new BreakContTarg (this, "whilecont_" + whileStmt.Unique);
3458
3459 ilGen.MarkLabel (loopLabel); // loop:
3460 CompValu testRVal = GenerateFromRVal (whileStmt.testRVal); // testRVal = while test expression
3461 if (!IsConstBoolExprTrue (testRVal)) {
3462 testRVal.PushVal (this, whileStmt.testRVal, tokenTypeBool); // if (!testRVal)
3463 ilGen.Emit (whileStmt, OpCodes.Brfalse, curBreakTarg.label); // goto break
3464 curBreakTarg.used = true;
3465 }
3466 GenerateStmt (whileStmt.bodyStmt); // while body statement
3467 if (curContTarg.used) {
3468 ilGen.MarkLabel (curContTarg.label); // cont:
3469 mightGetHere = true;
3470 }
3471 if (mightGetHere) {
3472 EmitCallCheckRun (whileStmt, false); // __sw.CheckRun()
3473 ilGen.Emit (whileStmt, OpCodes.Br, loopLabel); // goto loop
3474 }
3475 mightGetHere = curBreakTarg.used;
3476 if (mightGetHere) {
3477 ilGen.MarkLabel (curBreakTarg.label); // done:
3478 }
3479
3480 curBreakTarg = oldBreakTarg;
3481 curContTarg = oldContTarg;
3482 }
3483
3484 /**
3485 * @brief process a local variable declaration statement, possibly with initialization expression.
3486 * Note that the function header processing allocated stack space (CompValuTemp) for the
3487 * variable and now all we do is write its initialization value.
3488 */
3489 private void GenerateDeclVar (TokenDeclVar declVar)
3490 {
3491 /*
3492 * Script gave us an initialization value, so just store init value in var like an assignment statement.
3493 * If no init given, set it to its default value.
3494 */
3495 CompValu local = declVar.location;
3496 if (declVar.init != null) {
3497 CompValu rVal = GenerateFromRVal (declVar.init, local.GetArgTypes ());
3498 local.PopPre (this, declVar);
3499 rVal.PushVal (this, declVar.init, declVar.type);
3500 local.PopPost (this, declVar);
3501 } else {
3502 local.PopPre (this, declVar);
3503 PushDefaultValue (declVar.type);
3504 local.PopPost (this, declVar);
3505 }
3506 }
3507
3508 /**
3509 * @brief Get the type and location of an L-value (eg, variable)
3510 * @param lVal = L-value expression to evaluate
3511 * @param argsig = null: it's a field/property
3512 * else: select overload method that fits these arg types
3513 */
3514 private CompValu GenerateFromLVal (TokenLVal lVal)
3515 {
3516 return GenerateFromLVal (lVal, null);
3517 }
3518 private CompValu GenerateFromLVal (TokenLVal lVal, TokenType[] argsig)
3519 {
3520 if (lVal is TokenLValArEle) return GenerateFromLValArEle ((TokenLValArEle)lVal);
3521 if (lVal is TokenLValBaseField) return GenerateFromLValBaseField ((TokenLValBaseField)lVal, argsig);
3522 if (lVal is TokenLValIField) return GenerateFromLValIField ((TokenLValIField)lVal, argsig);
3523 if (lVal is TokenLValName) return GenerateFromLValName ((TokenLValName)lVal, argsig);
3524 if (lVal is TokenLValSField) return GenerateFromLValSField ((TokenLValSField)lVal, argsig);
3525 throw new Exception ("bad lval class");
3526 }
3527
3528 /**
3529 * @brief we have an L-value token that is an element within an array.
3530 * @returns a CompValu giving the type and location of the element of the array.
3531 */
3532 private CompValu GenerateFromLValArEle (TokenLValArEle lVal)
3533 {
3534 CompValu subCompValu;
3535
3536 /*
3537 * Compute location of array itself.
3538 */
3539 CompValu baseCompValu = GenerateFromRVal (lVal.baseRVal);
3540
3541 /*
3542 * Maybe it is a fixed array access.
3543 */
3544 string basetypestring = baseCompValu.type.ToString ();
3545 if (basetypestring.EndsWith ("]")) {
3546 TokenRVal subRVal = lVal.subRVal;
3547 int nSubs = 1;
3548 if (subRVal is TokenRValList) {
3549 nSubs = ((TokenRValList)subRVal).nItems;
3550 subRVal = ((TokenRValList)subRVal).rVal;
3551 }
3552
3553 int rank = basetypestring.IndexOf (']') - basetypestring.IndexOf ('[');
3554 if (nSubs != rank) {
3555 ErrorMsg (lVal.baseRVal, "expect " + rank + " subscript" + ((rank == 1) ? "" : "s") + " but have " + nSubs);
3556 }
3557 CompValu[] subCompValus = new CompValu[rank];
3558 int i;
3559 for (i = 0; (subRVal != null) && (i < rank); i ++) {
3560 subCompValus[i] = GenerateFromRVal (subRVal);
3561 subRVal = (TokenRVal)subRVal.nextToken;
3562 }
3563 while (i < rank) subCompValus[i++] = new CompValuInteger (new TokenTypeInt (lVal.subRVal), 0);
3564 return new CompValuFixArEl (this, baseCompValu, subCompValus);
3565 }
3566
3567 /*
3568 * Maybe it is accessing the $idxprop property of a script-defined class.
3569 */
3570 if (baseCompValu.type is TokenTypeSDTypeClass) {
3571 TokenName name = new TokenName (lVal, "$idxprop");
3572 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)baseCompValu.type;
3573 TokenDeclSDTypeClass sdtDecl = sdtType.decl;
3574 TokenDeclVar idxProp = FindThisMember (sdtDecl, name, null);
3575 if (idxProp == null) {
3576 ErrorMsg (lVal, "no index property in class " + sdtDecl.longName.val);
3577 return new CompValuVoid (lVal);
3578 }
3579 if ((idxProp.sdtFlags & ScriptReduce.SDT_STATIC) != 0) {
3580 ErrorMsg (lVal, "non-static reference to static member " + idxProp.name.val);
3581 return new CompValuVoid (idxProp);
3582 }
3583 CheckAccess (idxProp, name);
3584
3585 TokenType[] argTypes = IdxPropArgTypes (idxProp);
3586 CompValu[] compValus = IdxPropCompValus (lVal, argTypes.Length);
3587 return new CompValuIdxProp (idxProp, baseCompValu, argTypes, compValus);
3588
3589 }
3590
3591 /*
3592 * Maybe they are accessing $idxprop property of a script-defined interface.
3593 */
3594 if (baseCompValu.type is TokenTypeSDTypeInterface) {
3595 TokenName name = new TokenName (lVal, "$idxprop");
3596 TokenTypeSDTypeInterface sdtType = (TokenTypeSDTypeInterface)baseCompValu.type;
3597 TokenDeclVar idxProp = FindInterfaceMember (sdtType, name, null, ref baseCompValu);
3598 if (idxProp == null) {
3599 ErrorMsg (lVal, "no index property defined for interface " + sdtType.decl.longName.val);
3600 return baseCompValu;
3601 }
3602
3603 TokenType[] argTypes = IdxPropArgTypes (idxProp);
3604 CompValu[] compValus = IdxPropCompValus (lVal, argTypes.Length);
3605 return new CompValuIdxProp (idxProp, baseCompValu, argTypes, compValus);
3606 }
3607
3608 /*
3609 * Maybe it is extracting a character from a string.
3610 */
3611 if ((baseCompValu.type is TokenTypeKey) || (baseCompValu.type is TokenTypeStr)) {
3612 subCompValu = GenerateFromRVal (lVal.subRVal);
3613 return new CompValuStrChr (new TokenTypeChar (lVal), baseCompValu, subCompValu);
3614 }
3615
3616 /*
3617 * Maybe it is extracting an element from a list.
3618 */
3619 if (baseCompValu.type is TokenTypeList) {
3620 subCompValu = GenerateFromRVal (lVal.subRVal);
3621 return new CompValuListEl (new TokenTypeObject (lVal), baseCompValu, subCompValu);
3622 }
3623
3624 /*
3625 * Access should be to XMR_Array otherwise.
3626 */
3627 if (!(baseCompValu.type is TokenTypeArray)) {
3628 ErrorMsg (lVal, "taking subscript of non-array");
3629 return baseCompValu;
3630 }
3631 subCompValu = GenerateFromRVal (lVal.subRVal);
3632 return new CompValuArEle (new TokenTypeObject (lVal), baseCompValu, subCompValu);
3633 }
3634
3635 /**
3636 * @brief Get number and type of arguments required by an index property.
3637 */
3638 private static TokenType[] IdxPropArgTypes (TokenDeclVar idxProp)
3639 {
3640 TokenType[] argTypes;
3641 if (idxProp.getProp != null) {
3642 int nArgs = idxProp.getProp.argDecl.varDict.Count;
3643 argTypes = new TokenType[nArgs];
3644 foreach (TokenDeclVar var in idxProp.getProp.argDecl.varDict) {
3645 argTypes[var.vTableIndex] = var.type;
3646 }
3647 } else {
3648 int nArgs = idxProp.setProp.argDecl.varDict.Count - 1;
3649 argTypes = new TokenType[nArgs];
3650 foreach (TokenDeclVar var in idxProp.setProp.argDecl.varDict) {
3651 if (var.vTableIndex < nArgs) {
3652 argTypes[var.vTableIndex] = var.type;
3653 }
3654 }
3655 }
3656 return argTypes;
3657 }
3658
3659 /**
3660 * @brief Get number and computed value of index property arguments.
3661 * @param lVal = list of arguments
3662 * @param nArgs = number of arguments required
3663 * @returns null: argument count mismatch
3664 * else: array of index property argument values
3665 */
3666 private CompValu[] IdxPropCompValus (TokenLValArEle lVal, int nArgs)
3667 {
3668 TokenRVal subRVal = lVal.subRVal;
3669 int nSubs = 1;
3670 if (subRVal is TokenRValList) {
3671 nSubs = ((TokenRValList)subRVal).nItems;
3672 subRVal = ((TokenRValList)subRVal).rVal;
3673 }
3674
3675 if (nSubs != nArgs) {
3676 ErrorMsg (lVal, "index property requires " + nArgs + " subscript(s)");
3677 return null;
3678 }
3679
3680 CompValu[] subCompValus = new CompValu[nArgs];
3681 for (int i = 0; i < nArgs; i ++) {
3682 subCompValus[i] = GenerateFromRVal (subRVal);
3683 subRVal = (TokenRVal)subRVal.nextToken;
3684 }
3685 return subCompValus;
3686 }
3687
3688 /**
3689 * @brief using 'base' within a script-defined instance method to refer to an instance field/method
3690 * of the class being extended.
3691 */
3692 private CompValu GenerateFromLValBaseField (TokenLValBaseField baseField, TokenType[] argsig)
3693 {
3694 string fieldName = baseField.fieldName.val;
3695
3696 TokenDeclSDType sdtDecl = curDeclFunc.sdtClass;
3697 if ((sdtDecl == null) || ((curDeclFunc.sdtFlags & ScriptReduce.SDT_STATIC) != 0)) {
3698 ErrorMsg (baseField, "cannot use 'base' outside instance method body");
3699 return new CompValuVoid (baseField);
3700 }
3701 if (!IsSDTInstMethod ()) {
3702 ErrorMsg (baseField, "cannot access instance member of base class from static method");
3703 return new CompValuVoid (baseField);
3704 }
3705
3706 TokenDeclVar declVar = FindThisMember (sdtDecl.extends, baseField.fieldName, argsig);
3707 if (declVar != null) {
3708 CheckAccess (declVar, baseField.fieldName);
3709 TokenType baseType = declVar.sdtClass.MakeRefToken (baseField);
3710 CompValu basePtr = new CompValuArg (baseType, 0);
3711 return AccessInstanceMember (declVar, basePtr, baseField, true);
3712 }
3713
3714 ErrorMsg (baseField, "no member " + fieldName + ArgSigString (argsig) + " rootward of " + sdtDecl.longName.val);
3715 return new CompValuVoid (baseField);
3716 }
3717
3718 /**
3719 * @brief We have an L-value token that is an instance field/method within a struct.
3720 * @returns a CompValu giving the type and location of the field/method in the struct.
3721 */
3722 private CompValu GenerateFromLValIField (TokenLValIField lVal, TokenType[] argsig)
3723 {
3724 CompValu baseRVal = GenerateFromRVal (lVal.baseRVal);
3725 string fieldName = lVal.fieldName.val + ArgSigString (argsig);
3726
3727 /*
3728 * Maybe they are accessing an instance field, method or property of a script-defined class.
3729 */
3730 if (baseRVal.type is TokenTypeSDTypeClass) {
3731 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)baseRVal.type;
3732 TokenDeclSDTypeClass sdtDecl = sdtType.decl;
3733 TokenDeclVar declVar = FindThisMember (sdtDecl, lVal.fieldName, argsig);
3734 if (declVar != null) {
3735 CheckAccess (declVar, lVal.fieldName);
3736 return AccessInstanceMember (declVar, baseRVal, lVal, false);
3737 }
3738 ErrorMsg (lVal.fieldName, "no member " + fieldName + " in class " + sdtDecl.longName.val);
3739 return new CompValuVoid (lVal.fieldName);
3740 }
3741
3742 /*
3743 * Maybe they are accessing a method or property of a script-defined interface.
3744 */
3745 if (baseRVal.type is TokenTypeSDTypeInterface) {
3746 TokenTypeSDTypeInterface sdtType = (TokenTypeSDTypeInterface)baseRVal.type;
3747 TokenDeclVar declVar = FindInterfaceMember (sdtType, lVal.fieldName, argsig, ref baseRVal);
3748 if (declVar != null) {
3749 return new CompValuIntfMember (declVar, baseRVal);
3750 }
3751 ErrorMsg (lVal.fieldName, "no member " + fieldName + " in interface " + sdtType.decl.longName.val);
3752 return new CompValuVoid (lVal.fieldName);
3753 }
3754
3755 /*
3756 * Since we only have a few built-in types with fields, just pound them out.
3757 */
3758 if (baseRVal.type is TokenTypeArray) {
3759
3760 // no arguments, no parentheses, just the field name, returning integer
3761 // but internally, it is a call to a method()
3762 if (fieldName == "count") {
3763 return new CompValuIntInstROProp (tokenTypeInt, baseRVal, arrayCountMethodInfo);
3764 }
3765
3766 // no arguments but with the parentheses, returning void
3767 if (fieldName == "clear()") {
3768 return new CompValuIntInstMeth (XMR_Array.clearDelegate, baseRVal, arrayClearMethodInfo);
3769 }
3770
3771 // single integer argument, returning an object
3772 if (fieldName == "index(integer)") {
3773 return new CompValuIntInstMeth (XMR_Array.indexDelegate, baseRVal, arrayIndexMethodInfo);
3774 }
3775 if (fieldName == "value(integer)") {
3776 return new CompValuIntInstMeth (XMR_Array.valueDelegate, baseRVal, arrayValueMethodInfo);
3777 }
3778 }
3779 if (baseRVal.type is TokenTypeRot) {
3780 FieldInfo fi = null;
3781 if (fieldName == "x") fi = rotationXFieldInfo;
3782 if (fieldName == "y") fi = rotationYFieldInfo;
3783 if (fieldName == "z") fi = rotationZFieldInfo;
3784 if (fieldName == "s") fi = rotationSFieldInfo;
3785 if (fi != null) {
3786 return new CompValuField (new TokenTypeFloat (lVal), baseRVal, fi);
3787 }
3788 }
3789 if (baseRVal.type is TokenTypeVec) {
3790 FieldInfo fi = null;
3791 if (fieldName == "x") fi = vectorXFieldInfo;
3792 if (fieldName == "y") fi = vectorYFieldInfo;
3793 if (fieldName == "z") fi = vectorZFieldInfo;
3794 if (fi != null) {
3795 return new CompValuField (new TokenTypeFloat (lVal), baseRVal, fi);
3796 }
3797 }
3798
3799 ErrorMsg (lVal, "type " + baseRVal.type.ToString () + " does not define member " + fieldName);
3800 return baseRVal;
3801 }
3802
3803 /**
3804 * @brief We have an L-value token that is a function, method or variable name.
3805 * @param lVal = name we are looking for
3806 * @param argsig = null: just look for name as a variable
3807 * else: look for name as a function/method being called with the given argument types
3808 * eg, "(string,integer,list)"
3809 * @returns a CompValu giving the type and location of the function, method or variable.
3810 */
3811 private CompValu GenerateFromLValName (TokenLValName lVal, TokenType[] argsig)
3812 {
3813 /*
3814 * Look in variable stack then look for built-in constants and functions.
3815 */
3816 TokenDeclVar var = FindNamedVar (lVal, argsig);
3817 if (var == null) {
3818 ErrorMsg (lVal, "undefined constant/function/variable " + lVal.name.val + ArgSigString (argsig));
3819 return new CompValuVoid (lVal);
3820 }
3821
3822 /*
3823 * Maybe it has an implied 'this.' on the front.
3824 */
3825 if ((var.sdtClass != null) && ((var.sdtFlags & ScriptReduce.SDT_STATIC) == 0)) {
3826
3827 if (!IsSDTInstMethod ()) {
3828 ErrorMsg (lVal, "cannot access instance member of class from static method");
3829 return new CompValuVoid (lVal);
3830 }
3831
3832 /*
3833 * Don't allow something such as:
3834 *
3835 * class A {
3836 * integer I;
3837 * class B {
3838 * Print ()
3839 * {
3840 * llOwnerSay ("I=" + (string)I); <- access to I not allowed inside class B.
3841 * explicit reference required as we don't
3842 * have a valid reference to class A.
3843 * }
3844 * }
3845 * }
3846 *
3847 * But do allow something such as:
3848 *
3849 * class A {
3850 * integer I;
3851 * }
3852 * class B : A {
3853 * Print ()
3854 * {
3855 * llOwnerSay ("I=" + (string)I);
3856 * }
3857 * }
3858 */
3859 for (TokenDeclSDType c = curDeclFunc.sdtClass; c != var.sdtClass; c = c.extends) {
3860 if (c == null) {
3861 // our arg0 points to an instance of curDeclFunc.sdtClass, not var.sdtClass
3862 ErrorMsg (lVal, "cannot access instance member of outer class with implied 'this'");
3863 break;
3864 }
3865 }
3866
3867 CompValu thisCompValu = new CompValuArg (var.sdtClass.MakeRefToken (lVal), 0);
3868 return AccessInstanceMember (var, thisCompValu, lVal, false);
3869 }
3870
3871 /*
3872 * It's a local variable, static field, global, constant, etc.
3873 */
3874 return var.location;
3875 }
3876
3877 /**
3878 * @brief Access a script-defined type's instance member
3879 * @param declVar = which member (field,method,property) to access
3880 * @param basePtr = points to particular object instance
3881 * @param ignoreVirt = true: access declVar's method directly; else: maybe use vTable
3882 * @returns where the field/method/property is located
3883 */
3884 private CompValu AccessInstanceMember (TokenDeclVar declVar, CompValu basePtr, Token errorAt, bool ignoreVirt)
3885 {
3886 if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0) {
3887 ErrorMsg (errorAt, "non-static reference to static member " + declVar.name.val);
3888 return new CompValuVoid (declVar);
3889 }
3890 return new CompValuInstMember (declVar, basePtr, ignoreVirt);
3891 }
3892
3893 /**
3894 * @brief we have an L-value token that is a static member within a struct.
3895 * @returns a CompValu giving the type and location of the member in the struct.
3896 */
3897 private CompValu GenerateFromLValSField (TokenLValSField lVal, TokenType[] argsig)
3898 {
3899 TokenType stType = lVal.baseType;
3900 string fieldName = lVal.fieldName.val + ArgSigString (argsig);
3901
3902 /*
3903 * Maybe they are accessing a static member of a script-defined class.
3904 */
3905 if (stType is TokenTypeSDTypeClass) {
3906 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)stType;
3907 TokenDeclVar declVar = FindThisMember (sdtType.decl, lVal.fieldName, argsig);
3908 if (declVar != null) {
3909 CheckAccess (declVar, lVal.fieldName);
3910 if ((declVar.sdtFlags & ScriptReduce.SDT_STATIC) == 0) {
3911 ErrorMsg (lVal.fieldName, "static reference to non-static member " + fieldName);
3912 return new CompValuVoid (lVal.fieldName);
3913 }
3914 return declVar.location;
3915 }
3916 }
3917
3918 ErrorMsg (lVal.fieldName, "no member " + fieldName + " in " + stType.ToString ());
3919 return new CompValuVoid (lVal.fieldName);
3920 }
3921
3922 /**
3923 * @brief generate code from an RVal expression and return its type and where the result is stored.
3924 * For anything that has side-effects, statements are generated that perform the computation then
3925 * the result it put in a temp var and the temp var name is returned.
3926 * For anything without side-effects, they are returned as an equivalent sequence of Emits.
3927 * @param rVal = rVal token to be evaluated
3928 * @param argsig = null: not being used in an function/method context
3929 * else: string giving argument types, eg, "(string,integer,list,vector)"
3930 * that can be used to select among overloaded methods
3931 * @returns resultant type and location
3932 */
3933 private CompValu GenerateFromRVal (TokenRVal rVal)
3934 {
3935 return GenerateFromRVal (rVal, null);
3936 }
3937 private CompValu GenerateFromRVal (TokenRVal rVal, TokenType[] argsig)
3938 {
3939 errorMessageToken = rVal;
3940
3941 /*
3942 * Maybe the expression can be converted to a constant.
3943 */
3944 bool didOne;
3945 do {
3946 didOne = false;
3947 rVal = rVal.TryComputeConstant (LookupBodyConstants, ref didOne);
3948 } while (didOne);
3949
3950 /*
3951 * Generate code for the computation and return resulting type and location.
3952 */
3953 CompValu cVal = null;
3954 if (rVal is TokenRValAsnPost) cVal = GenerateFromRValAsnPost ((TokenRValAsnPost)rVal);
3955 if (rVal is TokenRValAsnPre) cVal = GenerateFromRValAsnPre ((TokenRValAsnPre)rVal);
3956 if (rVal is TokenRValCall) cVal = GenerateFromRValCall ((TokenRValCall)rVal);
3957 if (rVal is TokenRValCast) cVal = GenerateFromRValCast ((TokenRValCast)rVal);
3958 if (rVal is TokenRValCondExpr) cVal = GenerateFromRValCondExpr ((TokenRValCondExpr)rVal);
3959 if (rVal is TokenRValConst) cVal = GenerateFromRValConst ((TokenRValConst)rVal);
3960 if (rVal is TokenRValInitDef) cVal = GenerateFromRValInitDef ((TokenRValInitDef)rVal);
3961 if (rVal is TokenRValIsType) cVal = GenerateFromRValIsType ((TokenRValIsType)rVal);
3962 if (rVal is TokenRValList) cVal = GenerateFromRValList ((TokenRValList)rVal);
3963 if (rVal is TokenRValNewArIni) cVal = GenerateFromRValNewArIni ((TokenRValNewArIni)rVal);
3964 if (rVal is TokenRValOpBin) cVal = GenerateFromRValOpBin ((TokenRValOpBin)rVal);
3965 if (rVal is TokenRValOpUn) cVal = GenerateFromRValOpUn ((TokenRValOpUn)rVal);
3966 if (rVal is TokenRValParen) cVal = GenerateFromRValParen ((TokenRValParen)rVal);
3967 if (rVal is TokenRValRot) cVal = GenerateFromRValRot ((TokenRValRot)rVal);
3968 if (rVal is TokenRValThis) cVal = GenerateFromRValThis ((TokenRValThis)rVal);
3969 if (rVal is TokenRValUndef) cVal = GenerateFromRValUndef ((TokenRValUndef)rVal);
3970 if (rVal is TokenRValVec) cVal = GenerateFromRValVec ((TokenRValVec)rVal);
3971 if (rVal is TokenLVal) cVal = GenerateFromLVal ((TokenLVal)rVal, argsig);
3972
3973 if (cVal == null) throw new Exception ("bad rval class " + rVal.GetType ().ToString ());
3974
3975 /*
3976 * Sanity check.
3977 */
3978 if (!youveAnError) {
3979 if (cVal.type == null) throw new Exception ("cVal has no type " + cVal.GetType ());
3980 string cValType = cVal.type.ToString ();
3981 string rValType = rVal.GetRValType (this, argsig).ToString ();
3982 if (cValType == "bool") cValType = "integer";
3983 if (rValType == "bool") rValType = "integer";
3984 if (cValType != rValType) {
3985 throw new Exception ("cVal.type " + cValType + " != rVal.type " + rValType +
3986 " (" + rVal.GetType ().Name + " " + rVal.SrcLoc + ")");
3987 }
3988 }
3989
3990 return cVal;
3991 }
3992
3993 /**
3994 * @brief compute the result of a binary operator (eg, add, subtract, multiply, lessthan)
3995 * @param token = binary operator token, includes the left and right operands
3996 * @returns where the resultant R-value is as something that doesn't have side effects
3997 */
3998 private CompValu GenerateFromRValOpBin (TokenRValOpBin token)
3999 {
4000 CompValu left, right;
4001 string opcodeIndex = token.opcode.ToString ();
4002
4003 /*
4004 * Comma operators are special, as they say to compute the left-hand value and
4005 * discard it, then compute the right-hand argument and that is the result.
4006 */
4007 if (opcodeIndex == ",") {
4008
4009 /*
4010 * Compute left-hand operand but throw away result.
4011 */
4012 GenerateFromRVal (token.rValLeft);
4013
4014 /*
4015 * Compute right-hand operand and that is the value of the expression.
4016 */
4017 return GenerateFromRVal (token.rValRight);
4018 }
4019
4020 /*
4021 * Simple overwriting assignments are their own special case,
4022 * as we want to cast the R-value to the type of the L-value.
4023 * And in the case of delegates, we want to use the arg signature
4024 * of the delegate to select which overloaded method to use.
4025 */
4026 if (opcodeIndex == "=") {
4027 if (!(token.rValLeft is TokenLVal)) {
4028 ErrorMsg (token, "invalid L-value for =");
4029 return GenerateFromRVal (token.rValLeft);
4030 }
4031 left = GenerateFromLVal ((TokenLVal)token.rValLeft);
4032 right = Trivialize (GenerateFromRVal (token.rValRight, left.GetArgTypes ()), token.rValRight);
4033 left.PopPre (this, token.rValLeft);
4034 right.PushVal (this, token.rValRight, left.type); // push (left.type)right
4035 left.PopPost (this, token.rValLeft); // pop to left
4036 return left;
4037 }
4038
4039 /*
4040 * There are String.Concat() methods available for 2, 3 and 4 operands.
4041 * So see if we have a string concat op and optimize if so.
4042 */
4043 if ((opcodeIndex == "+") ||
4044 ((opcodeIndex == "+=") &&
4045 (token.rValLeft is TokenLVal) &&
4046 (token.rValLeft.GetRValType (this, null) is TokenTypeStr))) {
4047
4048 /*
4049 * We are adding something. Maybe it's a bunch of strings together.
4050 */
4051 List<TokenRVal> scorvs = new List<TokenRVal> ();
4052 if (StringConcatOperands (token.rValLeft, token.rValRight, scorvs, token.opcode)) {
4053
4054 /*
4055 * Evaluate all the operands, right-to-left on purpose per LSL scripting.
4056 */
4057 int i;
4058 int n = scorvs.Count;
4059 CompValu[] scocvs = new CompValu[n];
4060 for (i = n; -- i >= 0;) {
4061 scocvs[i] = GenerateFromRVal (scorvs[i]);
4062 if (i > 0) scocvs[i] = Trivialize (scocvs[i], scorvs[i]);
4063 }
4064
4065 /*
4066 * Figure out where to put the result.
4067 * A temp if '+', or back in original L-value if '+='.
4068 */
4069 CompValu retcv;
4070 if (opcodeIndex == "+") {
4071 retcv = new CompValuTemp (new TokenTypeStr (token.opcode), this);
4072 } else {
4073 retcv = GenerateFromLVal ((TokenLVal)token.rValLeft);
4074 }
4075 retcv.PopPre (this, token);
4076
4077 /*
4078 * Call the String.Concat() methods, passing operands in left-to-right order.
4079 * Force a cast to string (retcv.type) for each operand.
4080 */
4081 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4082 while (i + 3 < n) {
4083 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4084 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4085 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4086 ilGen.Emit (scorvs[i], OpCodes.Call, stringConcat4MethodInfo);
4087 }
4088 if (i + 2 < n) {
4089 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4090 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4091 ilGen.Emit (scorvs[i], OpCodes.Call, stringConcat3MethodInfo);
4092 }
4093 if (i + 1 < n) {
4094 ++ i; scocvs[i].PushVal (this, scorvs[i], retcv.type);
4095 ilGen.Emit (scorvs[i], OpCodes.Call, stringConcat2MethodInfo);
4096 }
4097
4098 /*
4099 * Put the result where we want it and return where we put it.
4100 */
4101 retcv.PopPost (this, token);
4102 return retcv;
4103 }
4104 }
4105
4106 /*
4107 * If "&&&", it is a short-circuiting AND.
4108 * Compute left-hand operand and if true, compute right-hand operand.
4109 */
4110 if (opcodeIndex == "&&&") {
4111 bool leftVal, rightVal;
4112 left = GenerateFromRVal (token.rValLeft);
4113 if (!IsConstBoolExpr (left, out leftVal)) {
4114 ScriptMyLabel falseLabel = ilGen.DefineLabel ("ssandfalse");
4115 left.PushVal (this, tokenTypeBool);
4116 ilGen.Emit (token, OpCodes.Brfalse, falseLabel);
4117 right = GenerateFromRVal (token.rValRight);
4118 if (!IsConstBoolExpr (right, out rightVal)) {
4119 right.PushVal (this, tokenTypeBool);
4120 goto donessand;
4121 }
4122 if (!rightVal) {
4123 ilGen.MarkLabel (falseLabel);
4124 return new CompValuInteger (new TokenTypeInt (token.rValLeft), 0);
4125 }
4126 ilGen.Emit (token, OpCodes.Ldc_I4_1);
4127 donessand:
4128 ScriptMyLabel doneLabel = ilGen.DefineLabel ("ssanddone");
4129 ilGen.Emit (token, OpCodes.Br, doneLabel);
4130 ilGen.MarkLabel (falseLabel);
4131 ilGen.Emit (token, OpCodes.Ldc_I4_0);
4132 ilGen.MarkLabel (doneLabel);
4133 CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
4134 retRVal.Pop (this, token);
4135 return retRVal;
4136 }
4137
4138 if (!leftVal) {
4139 return new CompValuInteger (new TokenTypeInt (token.rValLeft), 0);
4140 }
4141
4142 right = GenerateFromRVal (token.rValRight);
4143 if (!IsConstBoolExpr (right, out rightVal)) {
4144 right.PushVal (this, tokenTypeBool);
4145 CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
4146 retRVal.Pop (this, token);
4147 return retRVal;
4148 }
4149 return new CompValuInteger (new TokenTypeInt (token), rightVal ? 1 : 0);
4150 }
4151
4152 /*
4153 * If "|||", it is a short-circuiting OR.
4154 * Compute left-hand operand and if false, compute right-hand operand.
4155 */
4156 if (opcodeIndex == "|||") {
4157 bool leftVal, rightVal;
4158 left = GenerateFromRVal (token.rValLeft);
4159 if (!IsConstBoolExpr (left, out leftVal)) {
4160 ScriptMyLabel trueLabel = ilGen.DefineLabel ("ssortrue");
4161 left.PushVal (this, tokenTypeBool);
4162 ilGen.Emit (token, OpCodes.Brtrue, trueLabel);
4163 right = GenerateFromRVal (token.rValRight);
4164 if (!IsConstBoolExpr (right, out rightVal)) {
4165 right.PushVal (this, tokenTypeBool);
4166 goto donessor;
4167 }
4168 if (rightVal) {
4169 ilGen.MarkLabel (trueLabel);
4170 return new CompValuInteger (new TokenTypeInt (token.rValLeft), 1);
4171 }
4172 ilGen.Emit (token, OpCodes.Ldc_I4_0);
4173 donessor:
4174 ScriptMyLabel doneLabel = ilGen.DefineLabel ("ssanddone");
4175 ilGen.Emit (token, OpCodes.Br, doneLabel);
4176 ilGen.MarkLabel (trueLabel);
4177 ilGen.Emit (token, OpCodes.Ldc_I4_1);
4178 ilGen.MarkLabel (doneLabel);
4179 CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
4180 retRVal.Pop (this, token);
4181 return retRVal;
4182 }
4183
4184 if (leftVal) {
4185 return new CompValuInteger (new TokenTypeInt (token.rValLeft), 1);
4186 }
4187
4188 right = GenerateFromRVal (token.rValRight);
4189 if (!IsConstBoolExpr (right, out rightVal)) {
4190 right.PushVal (this, tokenTypeBool);
4191 CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
4192 retRVal.Pop (this, token);
4193 return retRVal;
4194 }
4195 return new CompValuInteger (new TokenTypeInt (token), rightVal ? 1 : 0);
4196 }
4197
4198 /*
4199 * Computation of some sort, compute right-hand operand value then left-hand value
4200 * because LSL is supposed to be right-to-left evaluation.
4201 */
4202 right = Trivialize (GenerateFromRVal (token.rValRight), token.rValRight);
4203
4204 /*
4205 * If left is a script-defined class and there is a method with the operator's name,
4206 * convert this to a call to that method with the right value as its single parameter.
4207 * Except don't if the right value is 'undef' so they can always compare to undef.
4208 */
4209 TokenType leftType = token.rValLeft.GetRValType (this, null);
4210 if ((leftType is TokenTypeSDTypeClass) && !(right.type is TokenTypeUndef)) {
4211 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)leftType;
4212 TokenDeclSDTypeClass sdtDecl = sdtType.decl;
4213 TokenType[] argsig = new TokenType[] { right.type };
4214 TokenName funcName = new TokenName (token.opcode, "$op" + opcodeIndex);
4215 TokenDeclVar declFunc = FindThisMember (sdtDecl, funcName, argsig);
4216 if (declFunc != null) {
4217 CheckAccess (declFunc, funcName);
4218 left = GenerateFromRVal (token.rValLeft);
4219 CompValu method = AccessInstanceMember (declFunc, left, token, false);
4220 CompValu[] argRVals = new CompValu[] { right };
4221 return GenerateACall (method, argRVals, token);
4222 }
4223 }
4224
4225 /*
4226 * Formulate key string for binOpStrings = (lefttype)(operator)(righttype)
4227 */
4228 string leftIndex = leftType.ToString ();
4229 string rightIndex = right.type.ToString ();
4230 string key = leftIndex + opcodeIndex + rightIndex;
4231
4232 /*
4233 * If that key exists in table, then the operation is defined between those types
4234 * ... and it produces an R-value of type as given in the table.
4235 */
4236 BinOpStr binOpStr;
4237 if (BinOpStr.defined.TryGetValue (key, out binOpStr)) {
4238
4239 /*
4240 * If table contained an explicit assignment type like +=, output the statement without
4241 * casting the L-value, then return the L-value as the resultant value.
4242 *
4243 * Make sure we don't include comparisons (such as ==, >=, etc).
4244 * Nothing like +=, -=, %=, etc, generate a boolean, only the comparisons.
4245 */
4246 if ((binOpStr.outtype != typeof (bool)) && opcodeIndex.EndsWith ("=") && (opcodeIndex != "!=")) {
4247 if (!(token.rValLeft is TokenLVal)) {
4248 ErrorMsg (token.rValLeft, "invalid L-value");
4249 return GenerateFromRVal (token.rValLeft);
4250 }
4251 left = GenerateFromLVal ((TokenLVal)token.rValLeft);
4252 binOpStr.emitBO (this, token, left, right, left);
4253 return left;
4254 }
4255
4256 /*
4257 * It's of the form left binop right.
4258 * Compute left, perform operation then put result in a temp.
4259 */
4260 left = GenerateFromRVal (token.rValLeft);
4261 CompValu retRVal = new CompValuTemp (TokenType.FromSysType (token.opcode, binOpStr.outtype), this);
4262 binOpStr.emitBO (this, token, left, right, retRVal);
4263 return retRVal;
4264 }
4265
4266 /*
4267 * Nothing in the table, check for comparing object pointers because of the myriad of types possible.
4268 * This will compare list pointers, null pointers, script-defined type pointers, array pointers, etc.
4269 * It will show equal iff the memory addresses are equal and that is good enough.
4270 */
4271 if (!leftType.ToSysType().IsValueType && !right.type.ToSysType().IsValueType && ((opcodeIndex == "==") || (opcodeIndex == "!="))) {
4272 CompValuTemp retRVal = new CompValuTemp (new TokenTypeInt (token), this);
4273 left = GenerateFromRVal (token.rValLeft);
4274 left.PushVal (this, token.rValLeft);
4275 right.PushVal (this, token.rValRight);
4276 ilGen.Emit (token, OpCodes.Ceq);
4277 if (opcodeIndex == "!=") {
4278 ilGen.Emit (token, OpCodes.Ldc_I4_1);
4279 ilGen.Emit (token, OpCodes.Xor);
4280 }
4281 retRVal.Pop (this, token);
4282 return retRVal;
4283 }
4284
4285 /*
4286 * If the opcode ends with "=", it may be something like "+=".
4287 * So look up the key as if we didn't have the "=" to tell us if the operation is legal.
4288 * Also, the binary operation's output type must be the same as the L-value type.
4289 * Likewise, integer += float not allowed because result is float, but float += integer is ok.
4290 */
4291 if (opcodeIndex.EndsWith ("=")) {
4292 key = leftIndex + opcodeIndex.Substring (0, opcodeIndex.Length - 1) + rightIndex;
4293 if (BinOpStr.defined.TryGetValue (key, out binOpStr)) {
4294 if (!(token.rValLeft is TokenLVal)) {
4295 ErrorMsg (token, "invalid L-value for <op>=");
4296 return GenerateFromRVal (token.rValLeft);
4297 }
4298 if (!binOpStr.rmwOK) {
4299 ErrorMsg (token, "<op>= not allowed: " + leftIndex + " " + opcodeIndex + " " + rightIndex);
4300 return new CompValuVoid (token);
4301 }
4302
4303 /*
4304 * Now we know for something like %= that left%right is legal for the types given.
4305 */
4306 left = GenerateFromLVal ((TokenLVal)token.rValLeft);
4307 if (binOpStr.outtype == leftType.ToSysType ()) {
4308 binOpStr.emitBO (this, token, left, right, left);
4309 } else {
4310 CompValu temp = new CompValuTemp (TokenType.FromSysType (token, binOpStr.outtype), this);
4311 binOpStr.emitBO (this, token, left, right, temp);
4312 left.PopPre (this, token);
4313 temp.PushVal (this, token, leftType);
4314 left.PopPost (this, token);
4315 }
4316 return left;
4317 }
4318 }
4319
4320 /*
4321 * Can't find it, oh well.
4322 */
4323 ErrorMsg (token, "op not defined: " + leftIndex + " " + opcodeIndex + " " + rightIndex);
4324 return new CompValuVoid (token);
4325 }
4326
4327 /**
4328 * @brief Queue the given operands to the end of the scos list.
4329 * If it can be broken down into more string concat operands, do so.
4330 * Otherwise, just push it as one operand.
4331 * @param leftRVal = left-hand operand of a '+' operation
4332 * @param rightRVal = right-hand operand of a '+' operation
4333 * @param scos = left-to-right list of operands for the string concat so far
4334 * @param addop = the add operator token (either '+' or '+=')
4335 * @returns false: neither operand is a string, nothing added to scos
4336 * true: scos = updated with leftRVal then rightRVal added onto the end, possibly broken down further
4337 */
4338 private bool StringConcatOperands (TokenRVal leftRVal, TokenRVal rightRVal, List<TokenRVal> scos, TokenKw addop)
4339 {
4340 /*
4341 * If neither operand is a string (eg, float+integer), then the result isn't going to be a string.
4342 */
4343 TokenType leftType = leftRVal.GetRValType (this, null);
4344 TokenType rightType = rightRVal.GetRValType (this, null);
4345 if (!(leftType is TokenTypeStr) && !(rightType is TokenTypeStr)) return false;
4346
4347 /*
4348 * Also, list+string => list so reject that too.
4349 * Also, string+list => list so reject that too.
4350 */
4351 if (leftType is TokenTypeList) return false;
4352 if (rightType is TokenTypeList) return false;
4353
4354 /*
4355 * Append values to the end of the list in left-to-right order.
4356 * If value is formed from a something+something => string,
4357 * push them as separate values, otherwise push as one value.
4358 */
4359 StringConcatOperand (leftType, leftRVal, scos);
4360 StringConcatOperand (rightType, rightRVal, scos);
4361
4362 /*
4363 * Maybe constant strings can be concatted.
4364 */
4365 try {
4366 int len;
4367 while (((len = scos.Count) >= 2) &&
4368 ((leftRVal = scos[len-2]) is TokenRValConst) &&
4369 ((rightRVal = scos[len-1]) is TokenRValConst)) {
4370 object sum = addop.binOpConst (((TokenRValConst)leftRVal).val,
4371 ((TokenRValConst)rightRVal).val);
4372 scos[len-2] = new TokenRValConst (addop, sum);
4373 scos.RemoveAt (len - 1);
4374 }
4375 } catch {
4376 }
4377
4378 /*
4379 * We pushed some string stuff.
4380 */
4381 return true;
4382 }
4383
4384 /**
4385 * @brief Queue the given operand to the end of the scos list.
4386 * If it can be broken down into more string concat operands, do so.
4387 * Otherwise, just push it as one operand.
4388 * @param type = rVal's resultant type
4389 * @param rVal = operand to examine
4390 * @param scos = left-to-right list of operands for the string concat so far
4391 * @returns with scos = updated with rVal added onto the end, possibly broken down further
4392 */
4393 private void StringConcatOperand (TokenType type, TokenRVal rVal, List<TokenRVal> scos)
4394 {
4395 bool didOne;
4396 do {
4397 didOne = false;
4398 rVal = rVal.TryComputeConstant (LookupBodyConstants, ref didOne);
4399 } while (didOne);
4400
4401 if (!(type is TokenTypeStr)) goto pushasis;
4402 if (!(rVal is TokenRValOpBin)) goto pushasis;
4403 TokenRValOpBin rValOpBin = (TokenRValOpBin)rVal;
4404 if (!(rValOpBin.opcode is TokenKwAdd)) goto pushasis;
4405 if (StringConcatOperands (rValOpBin.rValLeft, rValOpBin.rValRight, scos, rValOpBin.opcode)) return;
4406 pushasis:
4407 scos.Add (rVal);
4408 }
4409
4410 /**
4411 * @brief compute the result of an unary operator
4412 * @param token = unary operator token, includes the operand
4413 * @returns where the resultant R-value is
4414 */
4415 private CompValu GenerateFromRValOpUn (TokenRValOpUn token)
4416 {
4417 CompValu inRVal = GenerateFromRVal (token.rVal);
4418
4419 /*
4420 * Script-defined types can define their own methods to handle unary operators.
4421 */
4422 if (inRVal.type is TokenTypeSDTypeClass) {
4423 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)inRVal.type;
4424 TokenDeclSDTypeClass sdtDecl = sdtType.decl;
4425 TokenName funcName = new TokenName (token.opcode, "$op" + token.opcode.ToString ());
4426 TokenDeclVar declFunc = FindThisMember (sdtDecl, funcName, zeroArgs);
4427 if (declFunc != null) {
4428 CheckAccess (declFunc, funcName);
4429 CompValu method = AccessInstanceMember (declFunc, inRVal, token, false);
4430 return GenerateACall (method, zeroCompValus, token);
4431 }
4432 }
4433
4434 /*
4435 * Otherwise use the default.
4436 */
4437 return UnOpGenerate (inRVal, token.opcode);
4438 }
4439
4440 /**
4441 * @brief postfix operator -- this returns the type and location of the resultant value
4442 */
4443 private CompValu GenerateFromRValAsnPost (TokenRValAsnPost asnPost)
4444 {
4445 CompValu lVal = GenerateFromLVal (asnPost.lVal);
4446
4447 /*
4448 * Make up a temp to save original value in.
4449 */
4450 CompValuTemp result = new CompValuTemp (lVal.type, this);
4451
4452 /*
4453 * Prepare to pop incremented value back into variable being incremented.
4454 */
4455 lVal.PopPre (this, asnPost.lVal);
4456
4457 /*
4458 * Copy original value to temp and leave value on stack.
4459 */
4460 lVal.PushVal (this, asnPost.lVal);
4461 ilGen.Emit (asnPost.lVal, OpCodes.Dup);
4462 result.Pop (this, asnPost.lVal);
4463
4464 /*
4465 * Perform the ++/--.
4466 */
4467 if ((lVal.type is TokenTypeChar) || (lVal.type is TokenTypeInt)) {
4468 ilGen.Emit (asnPost, OpCodes.Ldc_I4_1);
4469 } else if (lVal.type is TokenTypeFloat) {
4470 ilGen.Emit (asnPost, OpCodes.Ldc_R4, 1.0f);
4471 } else {
4472 lVal.PopPost (this, asnPost.lVal);
4473 ErrorMsg (asnPost, "invalid type for " + asnPost.postfix.ToString ());
4474 return lVal;
4475 }
4476 switch (asnPost.postfix.ToString ()) {
4477 case "++": {
4478 ilGen.Emit (asnPost, OpCodes.Add);
4479 break;
4480 }
4481 case "--": {
4482 ilGen.Emit (asnPost, OpCodes.Sub);
4483 break;
4484 }
4485 default: throw new Exception ("unknown asnPost op");
4486 }
4487
4488 /*
4489 * Store new value in original variable.
4490 */
4491 lVal.PopPost (this, asnPost.lVal);
4492
4493 return result;
4494 }
4495
4496 /**
4497 * @brief prefix operator -- this returns the type and location of the resultant value
4498 */
4499 private CompValu GenerateFromRValAsnPre (TokenRValAsnPre asnPre)
4500 {
4501 CompValu lVal = GenerateFromLVal (asnPre.lVal);
4502
4503 /*
4504 * Make up a temp to put result in.
4505 */
4506 CompValuTemp result = new CompValuTemp (lVal.type, this);
4507
4508 /*
4509 * Prepare to pop incremented value back into variable being incremented.
4510 */
4511 lVal.PopPre (this, asnPre.lVal);
4512
4513 /*
4514 * Push original value.
4515 */
4516 lVal.PushVal (this, asnPre.lVal);
4517
4518 /*
4519 * Perform the ++/--.
4520 */
4521 if ((lVal.type is TokenTypeChar) || (lVal.type is TokenTypeInt)) {
4522 ilGen.Emit (asnPre, OpCodes.Ldc_I4_1);
4523 } else if (lVal.type is TokenTypeFloat) {
4524 ilGen.Emit (asnPre, OpCodes.Ldc_R4, 1.0f);
4525 } else {
4526 lVal.PopPost (this, asnPre.lVal);
4527 ErrorMsg (asnPre, "invalid type for " + asnPre.prefix.ToString ());
4528 return lVal;
4529 }
4530 switch (asnPre.prefix.ToString ()) {
4531 case "++": {
4532 ilGen.Emit (asnPre, OpCodes.Add);
4533 break;
4534 }
4535 case "--": {
4536 ilGen.Emit (asnPre, OpCodes.Sub);
4537 break;
4538 }
4539 default: throw new Exception ("unknown asnPre op");
4540 }
4541
4542 /*
4543 * Store new value in temp variable, keeping new value on stack.
4544 */
4545 ilGen.Emit (asnPre.lVal, OpCodes.Dup);
4546 result.Pop (this, asnPre.lVal);
4547
4548 /*
4549 * Store new value in original variable.
4550 */
4551 lVal.PopPost (this, asnPre.lVal);
4552
4553 return result;
4554 }
4555
4556 /**
4557 * @brief Generate code that calls a function or object's method.
4558 * @returns where the call's return value is stored (a TokenTypeVoid if void)
4559 */
4560 private CompValu GenerateFromRValCall (TokenRValCall call)
4561 {
4562 CompValu method;
4563 CompValu[] argRVals;
4564 int i, nargs;
4565 TokenRVal arg;
4566 TokenType[] argTypes;
4567
4568 /*
4569 * Compute the values of all the function's call arguments.
4570 * Save where the computation results are in the argRVals[] array.
4571 * Might as well build the argument signature from the argument types, too.
4572 */
4573 nargs = call.nArgs;
4574 argRVals = new CompValu[nargs];
4575 argTypes = new TokenType[nargs];
4576 if (nargs > 0) {
4577 i = 0;
4578 for (arg = call.args; arg != null; arg = (TokenRVal)arg.nextToken) {
4579 argRVals[i] = GenerateFromRVal (arg);
4580 argTypes[i] = argRVals[i].type;
4581 i ++;
4582 }
4583 }
4584
4585 /*
4586 * Get function/method's entrypoint that matches the call argument types.
4587 */
4588 method = GenerateFromRVal (call.meth, argTypes);
4589 if (method == null) return null;
4590
4591 return GenerateACall (method, argRVals, call);
4592 }
4593
4594 /**
4595 * @brief Generate call to a function/method.
4596 * @param method = function/method being called
4597 * @param argVRVals = its call parameters (zero length if none)
4598 * @param call = where in source code call is being made from (for error messages)
4599 * @returns type and location of return value (CompValuVoid if none)
4600 */
4601 private CompValu GenerateACall (CompValu method, CompValu[] argRVals, Token call)
4602 {
4603 CompValuTemp result;
4604 int i, nArgs;
4605 TokenType retType;
4606 TokenType[] argTypes;
4607
4608 /*
4609 * Must be some kind of callable.
4610 */
4611 retType = method.GetRetType (); // TokenTypeVoid if void; null means a variable
4612 if (retType == null) {
4613 ErrorMsg (call, "must be a delegate, function or method");
4614 return new CompValuVoid (call);
4615 }
4616
4617 /*
4618 * Get a location for return value.
4619 */
4620 if (retType is TokenTypeVoid) {
4621 result = new CompValuVoid (call);
4622 } else {
4623 result = new CompValuTemp (retType, this);
4624 }
4625
4626 /*
4627 * Make sure all arguments are trivial, ie, don't involve their own call labels.
4628 * For any that aren't, output code to calculate the arg and put in a temporary.
4629 */
4630 nArgs = argRVals.Length;
4631 for (i = 0; i < nArgs; i ++) {
4632 if (!argRVals[i].IsReadTrivial (this, call)) {
4633 argRVals[i] = Trivialize (argRVals[i], call);
4634 }
4635 }
4636
4637 /*
4638 * Inline functions know how to generate their own call.
4639 */
4640 if (method is CompValuInline) {
4641 CompValuInline inline = (CompValuInline)method;
4642 inline.declInline.CodeGen (this, call, result, argRVals);
4643 return result;
4644 }
4645
4646 /*
4647 * Push whatever the function/method needs as a this argument, if anything.
4648 */
4649 method.CallPre (this, call);
4650
4651 /*
4652 * Push the script-visible args, left-to-right.
4653 */
4654 argTypes = method.GetArgTypes ();
4655 for (i = 0; i < nArgs; i ++) {
4656 if (argTypes == null) {
4657 argRVals[i].PushVal (this, call);
4658 } else {
4659 argRVals[i].PushVal (this, call, argTypes[i]);
4660 }
4661 }
4662
4663 /*
4664 * Now output call instruction.
4665 */
4666 method.CallPost (this, call);
4667
4668 /*
4669 * Deal with the return value (if any), by putting it in 'result'.
4670 */
4671 result.Pop (this, call, retType);
4672 return result;
4673 }
4674
4675 /**
4676 * @brief This is needed to avoid nesting call labels around non-trivial properties.
4677 * It should be used for the second (and later) operands.
4678 * Note that a 'call' is considered an operator, so all arguments of a call
4679 * should be trivialized, but the method itself does not need to be.
4680 */
4681 public CompValu Trivialize (CompValu operand, Token errorAt)
4682 {
4683 if (operand.IsReadTrivial (this, errorAt)) return operand;
4684 CompValuTemp temp = new CompValuTemp (operand.type, this);
4685 operand.PushVal (this, errorAt);
4686 temp.Pop (this, errorAt);
4687 return temp;
4688 }
4689
4690 /**
4691 * @brief Generate code that casts a value to a particular type.
4692 * @returns where the result of the conversion is stored.
4693 */
4694 private CompValu GenerateFromRValCast (TokenRValCast cast)
4695 {
4696 /*
4697 * If casting to a delegate type, use the argment signature
4698 * of the delegate to help select the function/method, eg,
4699 * '(delegate string(integer))ToString'
4700 * will select 'string ToString(integer x)'
4701 * instaead of 'string ToString(float x)' or anything else
4702 */
4703 TokenType[] argsig = null;
4704 TokenType outType = cast.castTo;
4705 if (outType is TokenTypeSDTypeDelegate) {
4706 argsig = ((TokenTypeSDTypeDelegate)outType).decl.GetArgTypes ();
4707 }
4708
4709 /*
4710 * Generate the value that is being cast.
4711 * If the value is already the requested type, just use it as is.
4712 */
4713 CompValu inRVal = GenerateFromRVal (cast.rVal, argsig);
4714 if (inRVal.type == outType) return inRVal;
4715
4716 /*
4717 * Different type, generate casting code, putting the result in a temp of the output type.
4718 */
4719 CompValu outRVal = new CompValuTemp (outType, this);
4720 outRVal.PopPre (this, cast);
4721 inRVal.PushVal (this, cast, outType, true);
4722 outRVal.PopPost (this, cast);
4723 return outRVal;
4724 }
4725
4726 /**
4727 * @brief Compute conditional expression value.
4728 * @returns type and location of computed value.
4729 */
4730 private CompValu GenerateFromRValCondExpr (TokenRValCondExpr rValCondExpr)
4731 {
4732 bool condVal;
4733 CompValu condValu = GenerateFromRVal (rValCondExpr.condExpr);
4734 if (IsConstBoolExpr (condValu, out condVal)) {
4735 return GenerateFromRVal (condVal ? rValCondExpr.trueExpr : rValCondExpr.falseExpr);
4736 }
4737
4738 ScriptMyLabel falseLabel = ilGen.DefineLabel ("condexfalse");
4739 ScriptMyLabel doneLabel = ilGen.DefineLabel ("condexdone");
4740
4741 condValu.PushVal (this, rValCondExpr.condExpr, tokenTypeBool);
4742 ilGen.Emit (rValCondExpr, OpCodes.Brfalse, falseLabel);
4743
4744 CompValu trueValu = GenerateFromRVal (rValCondExpr.trueExpr);
4745 trueValu.PushVal (this, rValCondExpr.trueExpr);
4746 ilGen.Emit (rValCondExpr, OpCodes.Br, doneLabel);
4747
4748 ilGen.MarkLabel (falseLabel);
4749 CompValu falseValu = GenerateFromRVal (rValCondExpr.falseExpr);
4750 falseValu.PushVal (this, rValCondExpr.falseExpr);
4751
4752 if (trueValu.type.GetType () != falseValu.type.GetType ()) {
4753 ErrorMsg (rValCondExpr, "? operands " + trueValu.type.ToString () + " : " +
4754 falseValu.type.ToString () + " must be of same type");
4755 }
4756
4757 ilGen.MarkLabel (doneLabel);
4758 CompValuTemp retRVal = new CompValuTemp (trueValu.type, this);
4759 retRVal.Pop (this, rValCondExpr);
4760 return retRVal;
4761 }
4762
4763 /**
4764 * @brief Constant in the script somewhere
4765 * @returns where the constants value is stored
4766 */
4767 private CompValu GenerateFromRValConst (TokenRValConst rValConst)
4768 {
4769 switch (rValConst.type) {
4770 case TokenRValConstType.CHAR: {
4771 return new CompValuChar (new TokenTypeChar (rValConst), (char)(rValConst.val));
4772 }
4773 case TokenRValConstType.FLOAT: {
4774 return new CompValuFloat (new TokenTypeFloat (rValConst), (double)(rValConst.val));
4775 }
4776 case TokenRValConstType.INT: {
4777 return new CompValuInteger (new TokenTypeInt (rValConst), (int)(rValConst.val));
4778 }
4779 case TokenRValConstType.KEY: {
4780 return new CompValuString (new TokenTypeKey (rValConst), (string)(rValConst.val));
4781 }
4782 case TokenRValConstType.STRING: {
4783 return new CompValuString (new TokenTypeStr (rValConst), (string)(rValConst.val));
4784 }
4785 }
4786 throw new Exception ("unknown constant type " + rValConst.val.GetType ());
4787 }
4788
4789 /**
4790 * @brief generate a new list object
4791 * @param rValList = an rVal to create it from
4792 */
4793 private CompValu GenerateFromRValList (TokenRValList rValList)
4794 {
4795 /*
4796 * Compute all element values and remember where we put them.
4797 * Do it right-to-left as customary for LSL scripts.
4798 */
4799 int i = 0;
4800 TokenRVal lastRVal = null;
4801 for (TokenRVal val = rValList.rVal; val != null; val = (TokenRVal)val.nextToken) {
4802 i ++;
4803 val.prevToken = lastRVal;
4804 lastRVal = val;
4805 }
4806 CompValu[] vals = new CompValu[i];
4807 for (TokenRVal val = lastRVal; val != null; val = (TokenRVal)val.prevToken) {
4808 vals[--i] = GenerateFromRVal (val);
4809 }
4810
4811 /*
4812 * This is the temp that will hold the created list.
4813 */
4814 CompValuTemp newList = new CompValuTemp (new TokenTypeList (rValList.rVal), this);
4815
4816 /*
4817 * Create a temp object[] array to hold all the initial values.
4818 */
4819 ilGen.Emit (rValList, OpCodes.Ldc_I4, rValList.nItems);
4820 ilGen.Emit (rValList, OpCodes.Newarr, typeof (object));
4821
4822 /*
4823 * Populate the array.
4824 */
4825 i = 0;
4826 for (TokenRVal val = rValList.rVal; val != null; val = (TokenRVal)val.nextToken) {
4827
4828 /*
4829 * Get pointer to temp array object.
4830 */
4831 ilGen.Emit (rValList, OpCodes.Dup);
4832
4833 /*
4834 * Get index in that array.
4835 */
4836 ilGen.Emit (rValList, OpCodes.Ldc_I4, i);
4837
4838 /*
4839 * Store initialization value in array location.
4840 * However, floats and ints need to be converted to LSL_Float and LSL_Integer,
4841 * or things like llSetPayPrice() will puque when they try to cast the elements
4842 * to LSL_Float or LSL_Integer. Likewise with string/LSL_String.
4843 *
4844 * Maybe it's already LSL-boxed so we don't do anything with it except make sure
4845 * it is an object, not a struct.
4846 */
4847 CompValu eRVal = vals[i++];
4848 eRVal.PushVal (this, val);
4849 if (eRVal.type.ToLSLWrapType () == null) {
4850 if (eRVal.type is TokenTypeFloat) {
4851 ilGen.Emit (val, OpCodes.Newobj, lslFloatConstructorInfo);
4852 ilGen.Emit (val, OpCodes.Box, typeof (LSL_Float));
4853 } else if (eRVal.type is TokenTypeInt) {
4854 ilGen.Emit (val, OpCodes.Newobj, lslIntegerConstructorInfo);
4855 ilGen.Emit (val, OpCodes.Box, typeof (LSL_Integer));
4856 } else if ((eRVal.type is TokenTypeKey) || (eRVal.type is TokenTypeStr)) {
4857 ilGen.Emit (val, OpCodes.Newobj, lslStringConstructorInfo);
4858 ilGen.Emit (val, OpCodes.Box, typeof (LSL_String));
4859 } else if (eRVal.type.ToSysType ().IsValueType) {
4860 ilGen.Emit (val, OpCodes.Box, eRVal.type.ToSysType ());
4861 }
4862 } else if (eRVal.type.ToLSLWrapType ().IsValueType) {
4863
4864 // Convert the LSL value structs to an object of the LSL-boxed type
4865 ilGen.Emit (val, OpCodes.Box, eRVal.type.ToLSLWrapType ());
4866 }
4867 ilGen.Emit (val, OpCodes.Stelem, typeof (object));
4868 }
4869
4870 /*
4871 * Create new list object from temp initial value array (whose ref is still on the stack).
4872 */
4873 ilGen.Emit (rValList, OpCodes.Newobj, lslListConstructorInfo);
4874 newList.Pop (this, rValList);
4875 return newList;
4876 }
4877
4878 /**
4879 * @brief New array allocation with initializer expressions.
4880 */
4881 private CompValu GenerateFromRValNewArIni (TokenRValNewArIni rValNewArIni)
4882 {
4883 return MallocAndInitArray (rValNewArIni.arrayType, rValNewArIni.valueList);
4884 }
4885
4886 /**
4887 * @brief Mallocate and initialize an array from its initialization list.
4888 * @param arrayType = type of the array to be allocated and initialized
4889 * @param values = initialization value list used to size and initialize the array.
4890 * @returns memory location of the resultant initialized array.
4891 */
4892 private CompValu MallocAndInitArray (TokenType arrayType, TokenList values)
4893 {
4894 TokenDeclSDTypeClass arrayDecl = ((TokenTypeSDTypeClass)arrayType).decl;
4895 TokenType eleType = arrayDecl.arrayOfType;
4896 int rank = arrayDecl.arrayOfRank;
4897
4898 // Get size of each of the dimensions by scanning the initialization value list
4899 int[] dimSizes = new int[rank];
4900 FillInDimSizes (dimSizes, 0, rank, values);
4901
4902 // Figure out where the array's $new() method is
4903 TokenType[] newargsig = new TokenType[rank];
4904 for (int k = 0; k < rank; k ++) {
4905 newargsig[k] = tokenTypeInt;
4906 }
4907 TokenDeclVar newMeth = FindThisMember (arrayDecl, new TokenName (null, "$new"), newargsig);
4908
4909 // Output a call to malloc the array with all default values
4910 // array = ArrayType.$new (dimSizes[0], dimSizes[1], ...)
4911 CompValuTemp array = new CompValuTemp (arrayType, this);
4912 PushXMRInst ();
4913 for (int k = 0; k < rank; k ++) {
4914 ilGen.Emit (values, OpCodes.Ldc_I4, dimSizes[k]);
4915 }
4916 ilGen.Emit (values, OpCodes.Call, newMeth.ilGen);
4917 array.Pop (this, arrayType);
4918
4919 // Figure out where the array's Set() method is
4920 TokenType[] setargsig = new TokenType[rank+1];
4921 for (int k = 0; k < rank; k ++) {
4922 setargsig[k] = tokenTypeInt;
4923 }
4924 setargsig[rank] = eleType;
4925 TokenDeclVar setMeth = FindThisMember (arrayDecl, new TokenName (null, "Set"), setargsig);
4926
4927 // Fill in the array with the initializer values
4928 FillInInitVals (array, setMeth, dimSizes, 0, rank, values, eleType);
4929
4930 // The array is our resultant value
4931 return array;
4932 }
4933
4934 /**
4935 * @brief Compute an array's dimensions given its initialization value list
4936 * @param dimSizes = filled in with array's dimensions
4937 * @param dimNo = what dimension the 'values' list applies to
4938 * @param rank = total number of dimensions of the array
4939 * @param values = list of values to initialize the array's 'dimNo' dimension with
4940 * @returns with dimSizes[dimNo..rank-1] filled in
4941 */
4942 private static void FillInDimSizes (int[] dimSizes, int dimNo, int rank, TokenList values)
4943 {
4944 // the size of a dimension is the largest number of initializer elements at this level
4945 // for dimNo 0, this is the number of elements in the top-level list
4946 if (dimSizes[dimNo] < values.tl.Count) dimSizes[dimNo] = values.tl.Count;
4947
4948 // see if there is another dimension to calculate
4949 if (++ dimNo < rank) {
4950
4951 // its size is the size of the largest initializer list at the next inner level
4952 foreach (Token val in values.tl) {
4953 if (val is TokenList) {
4954 TokenList subvals = (TokenList)val;
4955 FillInDimSizes (dimSizes, dimNo, rank, subvals);
4956 }
4957 }
4958 }
4959 }
4960
4961 /**
4962 * @brief Output code to fill in array's initialization values
4963 * @param array = array to be filled in
4964 * @param setMeth = the array's Set() method
4965 * @param subscripts = holds subscripts being built
4966 * @param dimNo = which dimension the 'values' are for
4967 * @param values = list of initialization values for dimension 'dimNo'
4968 * @param rank = number of dimensions of 'array'
4969 * @param values = list of values to initialize the array's 'dimNo' dimension with
4970 * @param eleType = the element's type
4971 * @returns with code emitted to initialize array's [subscripts[0], ..., subscripts[dimNo-1], *, *, ...]
4972 * dimNo and up completely filled ---^
4973 */
4974 private void FillInInitVals (CompValu array, TokenDeclVar setMeth, int[] subscripts, int dimNo, int rank, TokenList values, TokenType eleType)
4975 {
4976 subscripts[dimNo] = 0;
4977 foreach (Token val in values.tl) {
4978 CompValu initValue = null;
4979
4980 /*
4981 * If it is a sublist, process it.
4982 * If we don't have enough subscripts yet, hopefully that sublist will have enough.
4983 * If we already have enough subscripts, then that sublist can be for an element of this supposedly jagged array.
4984 */
4985 if (val is TokenList) {
4986 TokenList sublist = (TokenList)val;
4987 if (dimNo + 1 < rank) {
4988
4989 /*
4990 * We don't have enough subscripts yet, hopefully the sublist has the rest.
4991 */
4992 FillInInitVals (array, setMeth, subscripts, dimNo + 1, rank, sublist, eleType);
4993 } else if ((eleType is TokenTypeSDTypeClass) && (((TokenTypeSDTypeClass)eleType).decl.arrayOfType == null)) {
4994
4995 /*
4996 * If we aren't a jagged array either, we can't do anything with the sublist.
4997 */
4998 ErrorMsg (val, "too many brace levels");
4999 } else {
5000
5001 /*
5002 * We are a jagged array, so malloc a subarray and initialize it with the sublist.
5003 * Then we can use that subarray to fill this array's element.
5004 */
5005 initValue = MallocAndInitArray (eleType, sublist);
5006 }
5007 }
5008
5009 /*
5010 * If it is a value expression, then output code to compute the value.
5011 */
5012 if (val is TokenRVal) {
5013 if (dimNo + 1 < rank) {
5014 ErrorMsg ((Token)val, "not enough brace levels");
5015 } else {
5016 initValue = GenerateFromRVal ((TokenRVal)val);
5017 }
5018 }
5019
5020 /*
5021 * If there is an initValue, output "array.Set (subscript[0], subscript[1], ..., initValue)"
5022 */
5023 if (initValue != null) {
5024 array.PushVal (this, val);
5025 for (int i = 0; i <= dimNo; i ++) {
5026 ilGen.Emit (val, OpCodes.Ldc_I4, subscripts[i]);
5027 }
5028 initValue.PushVal (this, val, eleType);
5029 ilGen.Emit (val, OpCodes.Call, setMeth.ilGen);
5030 }
5031
5032 /*
5033 * That subscript is processed one way or another, on to the next.
5034 */
5035 subscripts[dimNo] ++;
5036 }
5037 }
5038
5039 /**
5040 * @brief parenthesized expression
5041 * @returns type and location of the result of the computation.
5042 */
5043 private CompValu GenerateFromRValParen (TokenRValParen rValParen)
5044 {
5045 return GenerateFromRVal (rValParen.rVal);
5046 }
5047
5048 /**
5049 * @brief create a rotation object from the x,y,z,w value expressions.
5050 */
5051 private CompValu GenerateFromRValRot (TokenRValRot rValRot)
5052 {
5053 CompValu xRVal, yRVal, zRVal, wRVal;
5054
5055 xRVal = Trivialize (GenerateFromRVal (rValRot.xRVal), rValRot);
5056 yRVal = Trivialize (GenerateFromRVal (rValRot.yRVal), rValRot);
5057 zRVal = Trivialize (GenerateFromRVal (rValRot.zRVal), rValRot);
5058 wRVal = Trivialize (GenerateFromRVal (rValRot.wRVal), rValRot);
5059 return new CompValuRot (new TokenTypeRot (rValRot), xRVal, yRVal, zRVal, wRVal);
5060 }
5061
5062 /**
5063 * @brief Using 'this' as a pointer to the current script-defined instance object.
5064 * The value is located in arg #0 of the current instance method.
5065 */
5066 private CompValu GenerateFromRValThis (TokenRValThis zhis)
5067 {
5068 if (!IsSDTInstMethod ()) {
5069 ErrorMsg (zhis, "cannot access instance member of class from static method");
5070 return new CompValuVoid (zhis);
5071 }
5072 return new CompValuArg (curDeclFunc.sdtClass.MakeRefToken (zhis), 0);
5073 }
5074
5075 /**
5076 * @brief 'undefined' constant.
5077 * If this constant gets written to an array element, it will delete that element from the array.
5078 * If the script retrieves an element by key that is not defined, it will get this value.
5079 * This value can be stored in and retrieved from variables of type 'object' or script-defined classes.
5080 * It is a runtime error to cast this value to any other type, eg,
5081 * we don't allow list or string variables to be null pointers.
5082 */
5083 private CompValu GenerateFromRValUndef (TokenRValUndef rValUndef)
5084 {
5085 return new CompValuNull (new TokenTypeUndef (rValUndef));
5086 }
5087
5088 /**
5089 * @brief create a vector object from the x,y,z value expressions.
5090 */
5091 private CompValu GenerateFromRValVec (TokenRValVec rValVec)
5092 {
5093 CompValu xRVal, yRVal, zRVal;
5094
5095 xRVal = Trivialize (GenerateFromRVal (rValVec.xRVal), rValVec);
5096 yRVal = Trivialize (GenerateFromRVal (rValVec.yRVal), rValVec);
5097 zRVal = Trivialize (GenerateFromRVal (rValVec.zRVal), rValVec);
5098 return new CompValuVec (new TokenTypeVec (rValVec), xRVal, yRVal, zRVal);
5099 }
5100
5101 /**
5102 * @brief Generate code to get the default initialization value for a variable.
5103 */
5104 private CompValu GenerateFromRValInitDef (TokenRValInitDef rValInitDef)
5105 {
5106 TokenType type = rValInitDef.type;
5107
5108 if (type is TokenTypeChar) {
5109 return new CompValuChar (type, (char)0);
5110 }
5111 if (type is TokenTypeRot) {
5112 CompValuFloat x = new CompValuFloat (type, ScriptBaseClass.ZERO_ROTATION.x);
5113 CompValuFloat y = new CompValuFloat (type, ScriptBaseClass.ZERO_ROTATION.y);
5114 CompValuFloat z = new CompValuFloat (type, ScriptBaseClass.ZERO_ROTATION.z);
5115 CompValuFloat s = new CompValuFloat (type, ScriptBaseClass.ZERO_ROTATION.s);
5116 return new CompValuRot (type, x, y, z, s);
5117 }
5118 if ((type is TokenTypeKey) || (type is TokenTypeStr)) {
5119 return new CompValuString (type, "");
5120 }
5121 if (type is TokenTypeVec) {
5122 CompValuFloat x = new CompValuFloat (type, ScriptBaseClass.ZERO_VECTOR.x);
5123 CompValuFloat y = new CompValuFloat (type, ScriptBaseClass.ZERO_VECTOR.y);
5124 CompValuFloat z = new CompValuFloat (type, ScriptBaseClass.ZERO_VECTOR.z);
5125 return new CompValuVec (type, x, y, z);
5126 }
5127 if (type is TokenTypeInt) {
5128 return new CompValuInteger (type, 0);
5129 }
5130 if (type is TokenTypeFloat) {
5131 return new CompValuFloat (type, 0);
5132 }
5133 if (type is TokenTypeVoid) {
5134 return new CompValuVoid (type);
5135 }
5136
5137 /*
5138 * Default for 'object' type is 'undef'.
5139 * Likewise for script-defined classes and interfaces.
5140 */
5141 if ((type is TokenTypeObject) || (type is TokenTypeSDTypeClass) || (type is TokenTypeSDTypeDelegate) ||
5142 (type is TokenTypeSDTypeInterface) || (type is TokenTypeExc)) {
5143 return new CompValuNull (type);
5144 }
5145
5146 /*
5147 * array and list
5148 */
5149 CompValuTemp temp = new CompValuTemp (type, this);
5150 PushDefaultValue (type);
5151 temp.Pop (this, rValInitDef, type);
5152 return temp;
5153 }
5154
5155 /**
5156 * @brief Generate code to process an <rVal> is <type> expression, and produce a boolean value.
5157 */
5158 private CompValu GenerateFromRValIsType (TokenRValIsType rValIsType)
5159 {
5160 /*
5161 * Expression we want to know the type of.
5162 */
5163 CompValu val = GenerateFromRVal (rValIsType.rValExp);
5164
5165 /*
5166 * Pass it in to top-level type expression decoder.
5167 */
5168 return GenerateFromTypeExp (val, rValIsType.typeExp);
5169 }
5170
5171 /**
5172 * @brief See if the type of the given value matches the type expression.
5173 * @param val = where the value to be evaluated is stored
5174 * @param typeExp = script tokens representing type expression
5175 * @returns location where the boolean result is stored
5176 */
5177 private CompValu GenerateFromTypeExp (CompValu val, TokenTypeExp typeExp)
5178 {
5179 if (typeExp is TokenTypeExpBinOp) {
5180 CompValu left = GenerateFromTypeExp (val, ((TokenTypeExpBinOp)typeExp).leftOp);
5181 CompValu right = GenerateFromTypeExp (val, ((TokenTypeExpBinOp)typeExp).rightOp);
5182 CompValuTemp result = new CompValuTemp (tokenTypeBool, this);
5183 Token op = ((TokenTypeExpBinOp)typeExp).binOp;
5184 left.PushVal (this, ((TokenTypeExpBinOp)typeExp).leftOp);
5185 right.PushVal (this, ((TokenTypeExpBinOp)typeExp).rightOp);
5186 if (op is TokenKwAnd) {
5187 ilGen.Emit (typeExp, OpCodes.And);
5188 } else if (op is TokenKwOr) {
5189 ilGen.Emit (typeExp, OpCodes.Or);
5190 } else {
5191 throw new Exception ("unknown TokenTypeExpBinOp " + op.GetType ());
5192 }
5193 result.Pop (this, typeExp);
5194 return result;
5195 }
5196 if (typeExp is TokenTypeExpNot) {
5197 CompValu interm = GenerateFromTypeExp (val, ((TokenTypeExpNot)typeExp).typeExp);
5198 CompValuTemp result = new CompValuTemp (tokenTypeBool, this);
5199 interm.PushVal (this, ((TokenTypeExpNot)typeExp).typeExp, tokenTypeBool);
5200 ilGen.Emit (typeExp, OpCodes.Ldc_I4_1);
5201 ilGen.Emit (typeExp, OpCodes.Xor);
5202 result.Pop (this, typeExp);
5203 return result;
5204 }
5205 if (typeExp is TokenTypeExpPar) {
5206 return GenerateFromTypeExp (val, ((TokenTypeExpPar)typeExp).typeExp);
5207 }
5208 if (typeExp is TokenTypeExpType) {
5209 CompValuTemp result = new CompValuTemp (tokenTypeBool, this);
5210 val.PushVal (this, typeExp);
5211 ilGen.Emit (typeExp, OpCodes.Isinst, ((TokenTypeExpType)typeExp).typeToken.ToSysType ());
5212 ilGen.Emit (typeExp, OpCodes.Ldnull);
5213 ilGen.Emit (typeExp, OpCodes.Ceq);
5214 ilGen.Emit (typeExp, OpCodes.Ldc_I4_1);
5215 ilGen.Emit (typeExp, OpCodes.Xor);
5216 result.Pop (this, typeExp);
5217 return result;
5218 }
5219 if (typeExp is TokenTypeExpUndef) {
5220 CompValuTemp result = new CompValuTemp (tokenTypeBool, this);
5221 val.PushVal (this, typeExp);
5222 ilGen.Emit (typeExp, OpCodes.Ldnull);
5223 ilGen.Emit (typeExp, OpCodes.Ceq);
5224 result.Pop (this, typeExp);
5225 return result;
5226 }
5227 throw new Exception ("unknown TokenTypeExp type " + typeExp.GetType ());
5228 }
5229
5230 /**
5231 * @brief Push the default (null) value for a particular variable
5232 * @param var = variable to get the default value for
5233 * @returns with value pushed on stack
5234 */
5235 public void PushVarDefaultValue (TokenDeclVar var)
5236 {
5237 PushDefaultValue (var.type);
5238 }
5239 public void PushDefaultValue (TokenType type)
5240 {
5241 if (type is TokenTypeArray) {
5242 PushXMRInst (); // instance
5243 ilGen.Emit (type, OpCodes.Newobj, xmrArrayConstructorInfo);
5244 return;
5245 }
5246 if (type is TokenTypeChar) {
5247 ilGen.Emit (type, OpCodes.Ldc_I4_0);
5248 return;
5249 }
5250 if (type is TokenTypeList) {
5251 ilGen.Emit (type, OpCodes.Ldc_I4_0);
5252 ilGen.Emit (type, OpCodes.Newarr, typeof (object));
5253 ilGen.Emit (type, OpCodes.Newobj, lslListConstructorInfo);
5254 return;
5255 }
5256 if (type is TokenTypeRot) {
5257 // Mono is tOO stOOpid to allow: ilGen.Emit (OpCodes.Ldsfld, zeroRotationFieldInfo);
5258 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_ROTATION.x);
5259 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_ROTATION.y);
5260 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_ROTATION.z);
5261 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_ROTATION.s);
5262 ilGen.Emit (type, OpCodes.Newobj, lslRotationConstructorInfo);
5263 return;
5264 }
5265 if ((type is TokenTypeKey) || (type is TokenTypeStr)) {
5266 ilGen.Emit (type, OpCodes.Ldstr, "");
5267 return;
5268 }
5269 if (type is TokenTypeVec) {
5270 // Mono is tOO stOOpid to allow: ilGen.Emit (OpCodes.Ldsfld, zeroVectorFieldInfo);
5271 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_VECTOR.x);
5272 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_VECTOR.y);
5273 ilGen.Emit (type, OpCodes.Ldc_R8, ScriptBaseClass.ZERO_VECTOR.z);
5274 ilGen.Emit (type, OpCodes.Newobj, lslVectorConstructorInfo);
5275 return;
5276 }
5277 if (type is TokenTypeInt) {
5278 ilGen.Emit (type, OpCodes.Ldc_I4_0);
5279 return;
5280 }
5281 if (type is TokenTypeFloat) {
5282 ilGen.Emit (type, OpCodes.Ldc_R4, 0.0f);
5283 return;
5284 }
5285
5286 /*
5287 * Default for 'object' type is 'undef'.
5288 * Likewise for script-defined classes and interfaces.
5289 */
5290 if ((type is TokenTypeObject) || (type is TokenTypeSDTypeClass) || (type is TokenTypeSDTypeInterface) || (type is TokenTypeExc)) {
5291 ilGen.Emit (type, OpCodes.Ldnull);
5292 return;
5293 }
5294
5295 /*
5296 * Void is pushed as the default return value of a void function.
5297 * So just push nothing as expected of void functions.
5298 */
5299 if (type is TokenTypeVoid) {
5300 return;
5301 }
5302
5303 /*
5304 * Default for 'delegate' type is 'undef'.
5305 */
5306 if (type is TokenTypeSDTypeDelegate) {
5307 ilGen.Emit (type, OpCodes.Ldnull);
5308 return;
5309 }
5310
5311 throw new Exception ("unknown type " + type.GetType ().ToString ());
5312 }
5313
5314 /**
5315 * @brief Determine if the expression has a constant boolean value
5316 * and if so, if the value is true or false.
5317 * @param expr = expression to evaluate
5318 * @returns true: expression is contant and has boolean value true
5319 * false: otherwise
5320 */
5321 private bool IsConstBoolExprTrue (CompValu expr)
5322 {
5323 bool constVal;
5324 return IsConstBoolExpr (expr, out constVal) && constVal;
5325 }
5326
5327 private bool IsConstBoolExpr (CompValu expr, out bool constVal)
5328 {
5329 if (expr is CompValuChar) {
5330 constVal = ((CompValuChar)expr).x != 0;
5331 return true;
5332 }
5333 if (expr is CompValuFloat) {
5334 constVal = ((CompValuFloat)expr).x != (double)0;
5335 return true;
5336 }
5337 if (expr is CompValuInteger) {
5338 constVal = ((CompValuInteger)expr).x != 0;
5339 return true;
5340 }
5341 if (expr is CompValuString) {
5342 string s = ((CompValuString)expr).x;
5343 constVal = s != "";
5344 if (constVal && (expr.type is TokenTypeKey)) {
5345 constVal = s != ScriptBaseClass.NULL_KEY;
5346 }
5347 return true;
5348 }
5349
5350 constVal = false;
5351 return false;
5352 }
5353
5354 /**
5355 * @brief Determine if the expression has a constant integer value
5356 * and if so, return the integer value.
5357 * @param expr = expression to evaluate
5358 * @returns true: expression is contant and has integer value
5359 * false: otherwise
5360 */
5361 private bool IsConstIntExpr (CompValu expr, out int constVal)
5362 {
5363 if (expr is CompValuChar) {
5364 constVal = (int)((CompValuChar)expr).x;
5365 return true;
5366 }
5367 if (expr is CompValuInteger) {
5368 constVal = ((CompValuInteger)expr).x;
5369 return true;
5370 }
5371
5372 constVal = 0;
5373 return false;
5374 }
5375
5376 /**
5377 * @brief Determine if the expression has a constant string value
5378 * and if so, return the string value.
5379 * @param expr = expression to evaluate
5380 * @returns true: expression is contant and has string value
5381 * false: otherwise
5382 */
5383 private bool IsConstStrExpr (CompValu expr, out string constVal)
5384 {
5385 if (expr is CompValuString) {
5386 constVal = ((CompValuString)expr).x;
5387 return true;
5388 }
5389 constVal = "";
5390 return false;
5391 }
5392
5393 /**
5394 * @brief create table of legal event handler prototypes.
5395 * This is used to make sure script's event handler declrations are valid.
5396 */
5397 private static VarDict CreateLegalEventHandlers ()
5398 {
5399 /*
5400 * Get handler prototypes with full argument lists.
5401 */
5402 VarDict leh = new InternalFuncDict (typeof (IEventHandlers), false);
5403
5404 /*
5405 * We want the scripts to be able to declare their handlers with
5406 * fewer arguments than the full argument lists. So define additional
5407 * prototypes with fewer arguments.
5408 */
5409 TokenDeclVar[] fullArgProtos = new TokenDeclVar[leh.Count];
5410 int i = 0;
5411 foreach (TokenDeclVar fap in leh) fullArgProtos[i++] = fap;
5412
5413 foreach (TokenDeclVar fap in fullArgProtos) {
5414 TokenArgDecl fal = fap.argDecl;
5415 int fullArgCount = fal.vars.Length;
5416 for (i = 0; i < fullArgCount; i ++) {
5417 TokenArgDecl shortArgList = new TokenArgDecl (null);
5418 for (int j = 0; j < i; j ++) {
5419 TokenDeclVar var = fal.vars[j];
5420 shortArgList.AddArg (var.type, var.name);
5421 }
5422 TokenDeclVar shortArgProto = new TokenDeclVar (null, null, null);
5423 shortArgProto.name = new TokenName (null, fap.GetSimpleName ());
5424 shortArgProto.retType = fap.retType;
5425 shortArgProto.argDecl = shortArgList;
5426 leh.AddEntry (shortArgProto);
5427 }
5428 }
5429
5430 return leh;
5431 }
5432
5433 /**
5434 * @brief Emit a call to CheckRun(), (voluntary multitasking switch)
5435 */
5436 public void EmitCallCheckRun (Token errorAt, bool stack)
5437 {
5438 if (curDeclFunc.IsFuncTrivial (this)) throw new Exception (curDeclFunc.fullName + " is supposed to be trivial");
5439 new CallLabel (this, errorAt); // jump here when stack restored
5440 PushXMRInst (); // instance
5441 ilGen.Emit (errorAt, OpCodes.Call, stack ? checkRunStackMethInfo : checkRunQuickMethInfo);
5442 openCallLabel = null;
5443 }
5444
5445 /**
5446 * @brief Emit code to push a callNo var on the stack.
5447 */
5448 public void GetCallNo (Token errorAt, ScriptMyLocal callNoVar)
5449 {
5450 ilGen.Emit (errorAt, OpCodes.Ldloc, callNoVar);
5451 //ilGen.Emit (errorAt, OpCodes.Ldloca, callNoVar);
5452 //ilGen.Emit (errorAt, OpCodes.Volatile);
5453 //ilGen.Emit (errorAt, OpCodes.Ldind_I4);
5454 }
5455 public void GetCallNo (Token errorAt, CompValu callNoVar)
5456 {
5457 callNoVar.PushVal (this, errorAt);
5458 //callNoVar.PushRef (this, errorAt);
5459 //ilGen.Emit (errorAt, OpCodes.Volatile);
5460 //ilGen.Emit (errorAt, OpCodes.Ldind_I4);
5461 }
5462
5463 /**
5464 * @brief Emit code to set a callNo var to a given constant.
5465 */
5466 public void SetCallNo (Token errorAt, ScriptMyLocal callNoVar, int val)
5467 {
5468 ilGen.Emit (errorAt, OpCodes.Ldc_I4, val);
5469 ilGen.Emit (errorAt, OpCodes.Stloc, callNoVar);
5470 //ilGen.Emit (errorAt, OpCodes.Ldloca, callNoVar);
5471 //ilGen.Emit (errorAt, OpCodes.Ldc_I4, val);
5472 //ilGen.Emit (errorAt, OpCodes.Volatile);
5473 //ilGen.Emit (errorAt, OpCodes.Stind_I4);
5474 }
5475 public void SetCallNo (Token errorAt, CompValu callNoVar, int val)
5476 {
5477 callNoVar.PopPre (this, errorAt);
5478 ilGen.Emit (errorAt, OpCodes.Ldc_I4, val);
5479 callNoVar.PopPost (this, errorAt);
5480 //callNoVar.PushRef (this, errorAt);
5481 //ilGen.Emit (errorAt, OpCodes.Ldc_I4, val);
5482 //ilGen.Emit (errorAt, OpCodes.Volatile);
5483 //ilGen.Emit (errorAt, OpCodes.Stind_I4);
5484 }
5485
5486 /**
5487 * @brief handle a unary operator, such as -x.
5488 */
5489 private CompValu UnOpGenerate (CompValu inRVal, Token opcode)
5490 {
5491 /*
5492 * - Negate
5493 */
5494 if (opcode is TokenKwSub) {
5495 if (inRVal.type is TokenTypeFloat) {
5496 CompValuTemp outRVal = new CompValuTemp (new TokenTypeFloat (opcode), this);
5497 inRVal.PushVal (this, opcode, outRVal.type); // push value to negate, make sure not LSL-boxed
5498 ilGen.Emit (opcode, OpCodes.Neg); // compute the negative
5499 outRVal.Pop (this, opcode); // pop into result
5500 return outRVal; // tell caller where we put it
5501 }
5502 if (inRVal.type is TokenTypeInt) {
5503 CompValuTemp outRVal = new CompValuTemp (new TokenTypeInt (opcode), this);
5504 inRVal.PushVal (this, opcode, outRVal.type); // push value to negate, make sure not LSL-boxed
5505 ilGen.Emit (opcode, OpCodes.Neg); // compute the negative
5506 outRVal.Pop (this, opcode); // pop into result
5507 return outRVal; // tell caller where we put it
5508 }
5509 if (inRVal.type is TokenTypeRot) {
5510 CompValuTemp outRVal = new CompValuTemp (inRVal.type, this);
5511 inRVal.PushVal (this, opcode); // push rotation, then call negate routine
5512 ilGen.Emit (opcode, OpCodes.Call, lslRotationNegateMethodInfo);
5513 outRVal.Pop (this, opcode); // pop into result
5514 return outRVal; // tell caller where we put it
5515 }
5516 if (inRVal.type is TokenTypeVec) {
5517 CompValuTemp outRVal = new CompValuTemp (inRVal.type, this);
5518 inRVal.PushVal (this, opcode); // push vector, then call negate routine
5519 ilGen.Emit (opcode, OpCodes.Call, lslVectorNegateMethodInfo);
5520 outRVal.Pop (this, opcode); // pop into result
5521 return outRVal; // tell caller where we put it
5522 }
5523 ErrorMsg (opcode, "can't negate a " + inRVal.type.ToString ());
5524 return inRVal;
5525 }
5526
5527 /*
5528 * ~ Complement (bitwise integer)
5529 */
5530 if (opcode is TokenKwTilde) {
5531 if (inRVal.type is TokenTypeInt) {
5532 CompValuTemp outRVal = new CompValuTemp (new TokenTypeInt (opcode), this);
5533 inRVal.PushVal (this, opcode, outRVal.type); // push value to negate, make sure not LSL-boxed
5534 ilGen.Emit (opcode, OpCodes.Not); // compute the complement
5535 outRVal.Pop (this, opcode); // pop into result
5536 return outRVal; // tell caller where we put it
5537 }
5538 ErrorMsg (opcode, "can't complement a " + inRVal.type.ToString ());
5539 return inRVal;
5540 }
5541
5542 /*
5543 * ! Not (boolean)
5544 *
5545 * We stuff the 0/1 result in an int because I've seen x+!y in scripts
5546 * and we don't want to have to create tables to handle int+bool and
5547 * everything like that.
5548 */
5549 if (opcode is TokenKwExclam) {
5550 CompValuTemp outRVal = new CompValuTemp (new TokenTypeInt (opcode), this);
5551 inRVal.PushVal (this, opcode, tokenTypeBool); // anything converts to boolean
5552 ilGen.Emit (opcode, OpCodes.Ldc_I4_1); // then XOR with 1 to flip it
5553 ilGen.Emit (opcode, OpCodes.Xor);
5554 outRVal.Pop (this, opcode); // pop into result
5555 return outRVal; // tell caller where we put it
5556 }
5557
5558 throw new Exception ("unhandled opcode " + opcode.ToString ());
5559 }
5560
5561 /**
5562 * @brief This is called while trying to compute the value of constant initializers.
5563 * It is passed a name and that name is looked up in the constant tables.
5564 */
5565 private TokenRVal LookupInitConstants (TokenRVal rVal, ref bool didOne)
5566 {
5567 /*
5568 * If it is a static field of a script-defined type, look it up and hopefully we find a constant there.
5569 */
5570 TokenDeclVar gblVar;
5571 if (rVal is TokenLValSField) {
5572 TokenLValSField lvsf = (TokenLValSField)rVal;
5573 if (lvsf.baseType is TokenTypeSDTypeClass) {
5574 TokenDeclSDTypeClass sdtClass = ((TokenTypeSDTypeClass)lvsf.baseType).decl;
5575 gblVar = sdtClass.members.FindExact (lvsf.fieldName.val, null);
5576 if (gblVar != null) {
5577 if (gblVar.constant && (gblVar.init is TokenRValConst)) {
5578 didOne = true;
5579 return gblVar.init;
5580 }
5581 }
5582 }
5583 return rVal;
5584 }
5585
5586 /*
5587 * Only other thing we handle is stand-alone names.
5588 */
5589 if (!(rVal is TokenLValName)) return rVal;
5590 string name = ((TokenLValName)rVal).name.val;
5591
5592 /*
5593 * If we are doing the initializations for a script-defined type,
5594 * look for the constant among the fields for that type.
5595 */
5596 if (currentSDTClass != null) {
5597 gblVar = currentSDTClass.members.FindExact (name, null);
5598 if (gblVar != null) {
5599 if (gblVar.constant && (gblVar.init is TokenRValConst)) {
5600 didOne = true;
5601 return gblVar.init;
5602 }
5603 return rVal;
5604 }
5605 }
5606
5607 /*
5608 * Look it up as a script-defined global variable.
5609 * Then if the variable is defined as a constant and has a constant value,
5610 * we are successful. If it is defined as something else, return failure.
5611 */
5612 gblVar = tokenScript.variablesStack.FindExact (name, null);
5613 if (gblVar != null) {
5614 if (gblVar.constant && (gblVar.init is TokenRValConst)) {
5615 didOne = true;
5616 return gblVar.init;
5617 }
5618 return rVal;
5619 }
5620
5621 /*
5622 * Maybe it is a built-in symbolic constant.
5623 */
5624 ScriptConst scriptConst = ScriptConst.Lookup (name);
5625 if (scriptConst != null) {
5626 rVal = CompValuConst2RValConst (scriptConst.rVal, rVal);
5627 if (rVal is TokenRValConst) {
5628 didOne = true;
5629 return rVal;
5630 }
5631 }
5632
5633 /*
5634 * Don't know what it is, return failure.
5635 */
5636 return rVal;
5637 }
5638
5639 /**
5640 * @brief This is called while trying to compute the value of constant expressions.
5641 * It is passed a name and that name is looked up in the constant tables.
5642 */
5643 private TokenRVal LookupBodyConstants (TokenRVal rVal, ref bool didOne)
5644 {
5645 /*
5646 * If it is a static field of a script-defined type, look it up and hopefully we find a constant there.
5647 */
5648 TokenDeclVar gblVar;
5649 if (rVal is TokenLValSField) {
5650 TokenLValSField lvsf = (TokenLValSField)rVal;
5651 if (lvsf.baseType is TokenTypeSDTypeClass) {
5652 TokenDeclSDTypeClass sdtClass = ((TokenTypeSDTypeClass)lvsf.baseType).decl;
5653 gblVar = sdtClass.members.FindExact (lvsf.fieldName.val, null);
5654 if ((gblVar != null) && gblVar.constant && (gblVar.init is TokenRValConst)) {
5655 didOne = true;
5656 return gblVar.init;
5657 }
5658 }
5659 return rVal;
5660 }
5661
5662 /*
5663 * Only other thing we handle is stand-alone names.
5664 */
5665 if (!(rVal is TokenLValName)) return rVal;
5666 string name = ((TokenLValName)rVal).name.val;
5667
5668 /*
5669 * Scan through the variable stack and hopefully we find a constant there.
5670 * But we stop as soon as we get a match because that's what the script is referring to.
5671 */
5672 CompValu val;
5673 for (VarDict vars = ((TokenLValName)rVal).stack; vars != null; vars = vars.outerVarDict) {
5674 TokenDeclVar var = vars.FindExact (name, null);
5675 if (var != null) {
5676 val = var.location;
5677 goto foundit;
5678 }
5679
5680 TokenDeclSDTypeClass baseClass = vars.thisClass;
5681 if (baseClass != null) {
5682 while ((baseClass = baseClass.extends) != null) {
5683 var = baseClass.members.FindExact (name, null);
5684 if (var != null) {
5685 val = var.location;
5686 goto foundit;
5687 }
5688 }
5689 }
5690 }
5691
5692 /*
5693 * Maybe it is a built-in symbolic constant.
5694 */
5695 ScriptConst scriptConst = ScriptConst.Lookup (name);
5696 if (scriptConst != null) {
5697 val = scriptConst.rVal;
5698 goto foundit;
5699 }
5700
5701 /*
5702 * Don't know what it is, return failure.
5703 */
5704 return rVal;
5705
5706 /*
5707 * Found a CompValu. If it's a simple constant, then use it.
5708 * Otherwise tell caller we failed to simplify.
5709 */
5710 foundit:
5711 rVal = CompValuConst2RValConst (val, rVal);
5712 if (rVal is TokenRValConst) {
5713 didOne = true;
5714 }
5715 return rVal;
5716 }
5717
5718 private static TokenRVal CompValuConst2RValConst (CompValu val, TokenRVal rVal)
5719 {
5720 if (val is CompValuChar) rVal = new TokenRValConst (rVal, ((CompValuChar)val).x);
5721 if (val is CompValuFloat) rVal = new TokenRValConst (rVal, ((CompValuFloat)val).x);
5722 if (val is CompValuInteger) rVal = new TokenRValConst (rVal, ((CompValuInteger)val).x);
5723 if (val is CompValuString) rVal = new TokenRValConst (rVal, ((CompValuString)val).x);
5724 return rVal;
5725 }
5726
5727 /**
5728 * @brief Generate code to push XMRInstanceSuperType pointer on stack.
5729 */
5730 public void PushXMRInst ()
5731 {
5732 if (instancePointer == null) {
5733 ilGen.Emit (null, OpCodes.Ldarg_0);
5734 } else {
5735 ilGen.Emit (null, OpCodes.Ldloc, instancePointer);
5736 }
5737 }
5738
5739 /**
5740 * @returns true: Ldarg_0 gives XMRSDTypeClObj pointer
5741 * - this is the case for instance methods
5742 * false: Ldarg_0 gives XMR_Instance pointer
5743 * - this is the case for both global functions and static methods
5744 */
5745 public bool IsSDTInstMethod ()
5746 {
5747 return (curDeclFunc.sdtClass != null) &&
5748 ((curDeclFunc.sdtFlags & ScriptReduce.SDT_STATIC) == 0);
5749 }
5750
5751 /**
5752 * @brief Look for a simply named function or variable (not a field or method)
5753 */
5754 public TokenDeclVar FindNamedVar (TokenLValName lValName, TokenType[] argsig)
5755 {
5756 /*
5757 * Look in variable stack for the given name.
5758 */
5759 for (VarDict vars = lValName.stack; vars != null; vars = vars.outerVarDict) {
5760
5761 // first look for it possibly with an argument signature
5762 // so we pick the correct overloaded method
5763 TokenDeclVar var = FindSingleMember (vars, lValName.name, argsig);
5764 if (var != null) return var;
5765
5766 // if that fails, try it without the argument signature.
5767 // delegates get entered like any other variable, ie,
5768 // no signature on their name.
5769 if (argsig != null) {
5770 var = FindSingleMember (vars, lValName.name, null);
5771 if (var != null) return var;
5772 }
5773
5774 // if this is the frame for some class members, try searching base class members too
5775 TokenDeclSDTypeClass baseClass = vars.thisClass;
5776 if (baseClass != null) {
5777 while ((baseClass = baseClass.extends) != null) {
5778 var = FindSingleMember (baseClass.members, lValName.name, argsig);
5779 if (var != null) return var;
5780 if (argsig != null) {
5781 var = FindSingleMember (baseClass.members, lValName.name, null);
5782 if (var != null) return var;
5783 }
5784 }
5785 }
5786 }
5787
5788 /*
5789 * If not found, try one of the built-in constants or functions.
5790 */
5791 if (argsig == null) {
5792 ScriptConst scriptConst = ScriptConst.Lookup (lValName.name.val);
5793 if (scriptConst != null) {
5794 TokenDeclVar var = new TokenDeclVar (lValName.name, null, tokenScript);
5795 var.name = lValName.name;
5796 var.type = scriptConst.rVal.type;
5797 var.location = scriptConst.rVal;
5798 return var;
5799 }
5800 } else {
5801 TokenDeclVar inline = FindSingleMember (TokenDeclInline.inlineFunctions, lValName.name, argsig);
5802 if (inline != null) return inline;
5803 }
5804
5805 return null;
5806 }
5807
5808
5809 /**
5810 * @brief Find a member of an interface.
5811 * @param sdType = interface type
5812 * @param name = name of member to find
5813 * @param argsig = null: field/property; else: script-visible method argument types
5814 * @param baseRVal = pointer to interface object
5815 * @returns null: no such member
5816 * else: pointer to member
5817 * baseRVal = possibly modified to point to type-casted interface object
5818 */
5819 private TokenDeclVar FindInterfaceMember (TokenTypeSDTypeInterface sdtType, TokenName name, TokenType[] argsig, ref CompValu baseRVal)
5820 {
5821 TokenDeclSDTypeInterface sdtDecl = sdtType.decl;
5822 TokenDeclSDTypeInterface impl;
5823 TokenDeclVar declVar = sdtDecl.FindIFaceMember (this, name, argsig, out impl);
5824 if ((declVar != null) && (impl != sdtDecl)) {
5825
5826 /*
5827 * Accessing a method or propterty of another interface that the primary interface says it implements.
5828 * In this case, we have to cast from the primary interface to that secondary interface.
5829 *
5830 * interface IEnumerable {
5831 * IEnumerator GetEnumerator ();
5832 * }
5833 * interface ICountable : IEnumerable {
5834 * integer GetCount ();
5835 * }
5836 * class List : ICountable {
5837 * public GetCount () : ICountable { ... }
5838 * public GetEnumerator () : IEnumerable { ... }
5839 * }
5840 *
5841 * ICountable aList = new List ();
5842 * IEnumerator anEnumer = aList.GetEnumerator (); << we are here
5843 * << baseRVal = aList
5844 * << sdtDecl = ICountable
5845 * << impl = IEnumerable
5846 * << name = GetEnumerator
5847 * << argsig = ()
5848 * So we have to cast aList from ICountable to IEnumerable.
5849 */
5850
5851 // make type token for the secondary interface type
5852 TokenType subIntfType = impl.MakeRefToken (name);
5853
5854 // make a temp variable of the secondary interface type
5855 CompValuTemp castBase = new CompValuTemp (subIntfType, this);
5856
5857 // output code to cast from the primary interface to the secondary interface
5858 // this is 2 basic steps:
5859 // 1) cast from primary interface object -> class object
5860 // ...gets it from interfaceObject.delegateArray[0].Target
5861 // 2) cast from class object -> secondary interface object
5862 // ...gets it from classObject.sdtcITable[interfaceIndex]
5863 baseRVal.PushVal (this, name, subIntfType);
5864
5865 // save result of casting in temp
5866 castBase.Pop (this, name);
5867
5868 // return temp reference
5869 baseRVal = castBase;
5870 }
5871
5872 return declVar;
5873 }
5874
5875 /**
5876 * @brief Find a member of a script-defined type class.
5877 * @param sdtType = reference to class declaration
5878 * @param name = name of member to find
5879 * @param argsig = argument signature used to select among overloaded members
5880 * @returns null: no such member found
5881 * else: the member found
5882 */
5883 public TokenDeclVar FindThisMember (TokenTypeSDTypeClass sdtType, TokenName name, TokenType[] argsig)
5884 {
5885 return FindThisMember (sdtType.decl, name, argsig);
5886 }
5887 public TokenDeclVar FindThisMember (TokenDeclSDTypeClass sdtDecl, TokenName name, TokenType[] argsig)
5888 {
5889 for (TokenDeclSDTypeClass sdtd = sdtDecl; sdtd != null; sdtd = sdtd.extends) {
5890 TokenDeclVar declVar = FindSingleMember (sdtd.members, name, argsig);
5891 if (declVar != null) return declVar;
5892 }
5893 return null;
5894 }
5895
5896 /**
5897 * @brief Look for a single member that matches the given name and argument signature
5898 * @param where = which dictionary to look in
5899 * @param name = basic name of the field or method, eg, "Printable"
5900 * @param argsig = argument types the method is being called with, eg, "(string)"
5901 * or null to find a field
5902 * @returns null: no member found
5903 * else: the member found
5904 */
5905 public TokenDeclVar FindSingleMember (VarDict where, TokenName name, TokenType[] argsig)
5906 {
5907 TokenDeclVar[] members = where.FindCallables (name.val, argsig);
5908 if (members == null) return null;
5909 if (members.Length > 1) {
5910 ErrorMsg (name, "more than one matching member");
5911 for (int i = 0; i < members.Length; i ++) {
5912 ErrorMsg (members[i], " " + members[i].argDecl.GetArgSig ());
5913 }
5914 }
5915 return members[0];
5916 }
5917
5918 /**
5919 * @brief Find an exact function name and argument signature match.
5920 * Also verify that the return value type is an exact match.
5921 * @param where = which method dictionary to look in
5922 * @param name = basic name of the method, eg, "Printable"
5923 * @param ret = expected return value type
5924 * @param argsig = argument types the method is being called with, eg, "(string)"
5925 * @returns null: no exact match found
5926 * else: the matching function
5927 */
5928 private TokenDeclVar FindExactWithRet (VarDict where, TokenName name, TokenType ret, TokenType[] argsig)
5929 {
5930 TokenDeclVar func = where.FindExact (name.val, argsig);
5931 if ((func != null) && (func.retType.ToString () != ret.ToString ())) {
5932 ErrorMsg (name, "return type mismatch, have " + func.retType.ToString () + ", expect " + ret.ToString ());
5933 }
5934 if (func != null) CheckAccess (func, name);
5935 return func;
5936 }
5937
5938 /**
5939 * @brief Check the private/protected/public access flags of a member.
5940 */
5941 private void CheckAccess (TokenDeclVar var, Token errorAt)
5942 {
5943 TokenDeclSDType nested;
5944 TokenDeclSDType definedBy = var.sdtClass;
5945 TokenDeclSDType accessedBy = curDeclFunc.sdtClass;
5946
5947 /*******************************\
5948 * Check member-level access *
5949 \*******************************/
5950
5951 /*
5952 * Note that if accessedBy is null, ie, accessing from global function (or event handlers),
5953 * anything tagged as SDT_PRIVATE or SDT_PROTECTED will fail.
5954 */
5955
5956 /*
5957 * Private means accessed by the class that defined the member or accessed by a nested class
5958 * of the class that defined the member.
5959 */
5960 if ((var.sdtFlags & ScriptReduce.SDT_PRIVATE) != 0) {
5961 for (nested = accessedBy; nested != null; nested = nested.outerSDType) {
5962 if (nested == definedBy) goto acc1ok;
5963 }
5964 ErrorMsg (errorAt, "private member " + var.fullName + " cannot be accessed by " + curDeclFunc.fullName);
5965 return;
5966 }
5967
5968 /*
5969 * Protected means:
5970 * If being accessed by an inner class, the inner class has access to it if the inner class derives
5971 * from the declaring class. It also has access to it if an outer class derives from the declaring
5972 * class.
5973 */
5974 if ((var.sdtFlags & ScriptReduce.SDT_PROTECTED) != 0) {
5975 for (nested = accessedBy; nested != null; nested = nested.outerSDType) {
5976 for (TokenDeclSDType rootward = nested; rootward != null; rootward = rootward.extends) {
5977 if (rootward == definedBy) goto acc1ok;
5978 }
5979 }
5980 ErrorMsg (errorAt, "protected member " + var.fullName + " cannot be accessed by " + curDeclFunc.fullName);
5981 return;
5982 }
5983 acc1ok:
5984
5985 /******************************\
5986 * Check class-level access *
5987 \******************************/
5988
5989 /*
5990 * If being accessed by same or inner class than where defined, it is ok.
5991 *
5992 * class DefiningClass {
5993 * varBeingAccessed;
5994 * .
5995 * .
5996 * .
5997 * class AccessingClass {
5998 * functionDoingAccess() { }
5999 * }
6000 * .
6001 * .
6002 * .
6003 * }
6004 */
6005 nested = accessedBy;
6006 while (true) {
6007 if (nested == definedBy) return;
6008 if (nested == null) break;
6009 nested = (TokenDeclSDTypeClass)nested.outerSDType;
6010 }
6011
6012 /*
6013 * It is being accessed by an outer class than where defined,
6014 * check for a 'private' or 'protected' class tag that blocks.
6015 */
6016 do {
6017
6018 /*
6019 * If the field's class is defined directly inside the accessing class,
6020 * access is allowed regardless of class-level private or protected tags.
6021 *
6022 * class AccessingClass {
6023 * functionDoingAccess() { }
6024 * class DefiningClass {
6025 * varBeingAccessed;
6026 * }
6027 * }
6028 */
6029 if (definedBy.outerSDType == accessedBy) return;
6030
6031 /*
6032 * If the field's class is defined two or more levels inside the accessing class,
6033 * access is denied if the defining class is tagged private.
6034 *
6035 * class AccessingClass {
6036 * functionDoingAccess() { }
6037 * .
6038 * .
6039 * .
6040 * class IntermediateClass {
6041 * private class DefiningClass {
6042 * varBeingAccessed;
6043 * }
6044 * }
6045 * .
6046 * .
6047 * .
6048 * }
6049 */
6050 if ((definedBy.accessLevel & ScriptReduce.SDT_PRIVATE) != 0) {
6051 ErrorMsg (errorAt, "member " + var.fullName + " cannot be accessed by " + curDeclFunc.fullName +
6052 " because of private class " + definedBy.longName.val);
6053 return;
6054 }
6055
6056 /*
6057 * Likewise, if DefiningClass is tagged protected, the AccessingClass must derive from the
6058 * IntermediateClass or access is denied.
6059 */
6060 if ((definedBy.accessLevel & ScriptReduce.SDT_PROTECTED) != 0) {
6061 for (TokenDeclSDType extends = accessedBy; extends != definedBy.outerSDType; extends = extends.extends) {
6062 if (extends == null) {
6063 ErrorMsg (errorAt, "member " + var.fullName + " cannot be accessed by " + curDeclFunc.fullName +
6064 " because of protected class " + definedBy.longName.val);
6065 return;
6066 }
6067 }
6068 }
6069
6070 /*
6071 * Check next outer level.
6072 */
6073 definedBy = definedBy.outerSDType;
6074 } while (definedBy != null);
6075 }
6076
6077 /**
6078 * @brief Convert a list of argument types to printable string, eg, "(list,string,float,integer)"
6079 * If given a null, return "" indicating it is a field not a method
6080 */
6081 public static string ArgSigString (TokenType[] argsig)
6082 {
6083 if (argsig == null) return "";
6084 StringBuilder sb = new StringBuilder ("(");
6085 for (int i = 0; i < argsig.Length; i ++) {
6086 if (i > 0) sb.Append (",");
6087 sb.Append (argsig[i].ToString ());
6088 }
6089 sb.Append (")");
6090 return sb.ToString ();
6091 }
6092
6093 /**
6094 * @brief output error message and remember that we did
6095 */
6096 public void ErrorMsg (Token token, string message)
6097 {
6098 if ((token == null) || (token.emsg == null)) token = errorMessageToken;
6099 if (!youveAnError || (token.file != lastErrorFile) || (token.line > lastErrorLine)) {
6100 token.ErrorMsg (message);
6101 youveAnError = true;
6102 lastErrorFile = token.file;
6103 lastErrorLine = token.line;
6104 }
6105 }
6106
6107 /**
6108 * @brief Find a private static method.
6109 * @param owner = class the method is part of
6110 * @param name = name of method to find
6111 * @param args = array of argument types
6112 * @returns pointer to method
6113 */
6114 public static MethodInfo GetStaticMethod (Type owner, string name, Type[] args)
6115 {
6116 MethodInfo mi = owner.GetMethod (name, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, args, null);
6117 if (mi == null) {
6118 throw new Exception ("undefined method " + owner.ToString () + "." + name);
6119 }
6120 return mi;
6121 }
6122
6123 // http://wiki.secondlife.com/wiki/Rotation 'negate a rotation' says just negate .s component
6124 // but http://wiki.secondlife.com/wiki/LSL_Language_Test (lslangtest1.lsl) says negate all 4 values
6125 public static LSL_Rotation LSLRotationNegate (LSL_Rotation r) { return new LSL_Rotation (-r.x,-r.y,-r.z,-r.s); }
6126 public static LSL_Vector LSLVectorNegate (LSL_Vector v) { return -v; }
6127 public static string CatchExcToStr (Exception exc) { return exc.ToString(); }
6128 //public static void ConsoleWrite (string str) { Console.Write(str); }
6129
6130 /**
6131 * @brief Defines an internal label that is used as a target for 'break' and 'continue' statements.
6132 */
6133 private class BreakContTarg {
6134 public bool used;
6135 public ScriptMyLabel label;
6136 public TokenStmtBlock block;
6137
6138 public BreakContTarg (ScriptCodeGen scg, string name) {
6139 used = false; // assume it isn't referenced at all
6140 label = scg.ilGen.DefineLabel (name); // label that the break/continue jumps to
6141 block = scg.curStmtBlock; // { ... } that the break/continue label is in
6142 }
6143 }
6144 }
6145
6146 /**
6147 * @brief Marker interface indicates an exception that can't be caught by a script-level try/catch.
6148 */
6149 public interface IXMRUncatchable { }
6150
6151 /**
6152 * @brief Thrown by a script when it attempts to change to an undefined state.
6153 * These can be detected at compile time but the moron XEngine compiles
6154 * such things, so we compile them as runtime errors.
6155 */
6156 [SerializableAttribute]
6157 public class ScriptUndefinedStateException : Exception, ISerializable {
6158 public string stateName;
6159 public ScriptUndefinedStateException (string stateName) : base ("undefined state " + stateName) {
6160 this.stateName = stateName;
6161 }
6162 protected ScriptUndefinedStateException (SerializationInfo info, StreamingContext context) : base (info, context)
6163 { }
6164 }
6165
6166 /**
6167 * @brief Created by a throw statement.
6168 */
6169 [SerializableAttribute]
6170 public class ScriptThrownException : Exception, ISerializable {
6171 public object thrown;
6172
6173 /**
6174 * @brief Called by a throw statement to wrap the object in a unique
6175 * tag that capable of capturing a stack trace. Script can
6176 * unwrap it by calling xmrExceptionThrownValue().
6177 */
6178 public static Exception Wrap (object thrown)
6179 {
6180 return new ScriptThrownException (thrown);
6181 }
6182 private ScriptThrownException (object thrown) : base (thrown.ToString ())
6183 {
6184 this.thrown = thrown;
6185 }
6186
6187 /**
6188 * @brief Used by serialization/deserialization.
6189 */
6190 protected ScriptThrownException (SerializationInfo info, StreamingContext context) : base (info, context)
6191 { }
6192 }
6193
6194 /**
6195 * @brief Thrown by a script when it attempts to change to a defined state.
6196 */
6197 [SerializableAttribute]
6198 public class ScriptChangeStateException : Exception, ISerializable, IXMRUncatchable {
6199 public int newState;
6200 public ScriptChangeStateException (int newState) {
6201 this.newState = newState;
6202 }
6203 protected ScriptChangeStateException (SerializationInfo info, StreamingContext context) : base (info, context)
6204 { }
6205 }
6206
6207 /**
6208 * @brief We are restoring to the body of a catch { } so we need to
6209 * wrap the original exception in an outer exception, so the
6210 * system won't try to refill the stack trace.
6211 *
6212 * We don't mark this one serializable as it should never get
6213 * serialized out. It only lives from the throw to the very
6214 * beginning of the catch handler where it is promptly unwrapped.
6215 * No CheckRun() call can possibly intervene.
6216 */
6217 public class ScriptRestoreCatchException : Exception {
6218
6219 // old code uses these
6220 private object e;
6221 public ScriptRestoreCatchException (object e) {
6222 this.e = e;
6223 }
6224 public static object Unwrap (object o)
6225 {
6226 if (o is IXMRUncatchable) return null;
6227 if (o is ScriptRestoreCatchException) return ((ScriptRestoreCatchException)o).e;
6228 return o;
6229 }
6230
6231 // new code uses these
6232 private Exception ee;
6233 public ScriptRestoreCatchException (Exception ee) {
6234 this.ee = ee;
6235 }
6236 public static Exception Unwrap (Exception oo)
6237 {
6238 if (oo is IXMRUncatchable) return null;
6239 if (oo is ScriptRestoreCatchException) return ((ScriptRestoreCatchException)oo).ee;
6240 return oo;
6241 }
6242 }
6243
6244 [SerializableAttribute]
6245 public class ScriptBadCallNoException : Exception {
6246 public ScriptBadCallNoException (int callNo) : base ("bad callNo " + callNo) { }
6247 protected ScriptBadCallNoException (SerializationInfo info, StreamingContext context) : base (info, context)
6248 { }
6249 }
6250
6251 public class CVVMismatchException : Exception {
6252 public int oldcvv;
6253 public int newcvv;
6254
6255 public CVVMismatchException (int oldcvv, int newcvv) : base ("object version is " + oldcvv.ToString () +
6256 " but accept only " + newcvv.ToString ())
6257 {
6258 this.oldcvv = oldcvv;
6259 this.newcvv = newcvv;
6260 }
6261 }
6262}