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