aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompValu.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompValu.cs')
-rw-r--r--OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompValu.cs1897
1 files changed, 1897 insertions, 0 deletions
diff --git a/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompValu.cs b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompValu.cs
new file mode 100644
index 0000000..675ab9a
--- /dev/null
+++ b/OpenSim/Region/ScriptEngine/YEngine/MMRScriptCompValu.cs
@@ -0,0 +1,1897 @@
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;
35
36using LSL_Float = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLFloat;
37using LSL_Integer = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLInteger;
38using LSL_Key = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
39using LSL_List = OpenSim.Region.ScriptEngine.Shared.LSL_Types.list;
40using LSL_Rotation = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Quaternion;
41using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
42using LSL_Vector = OpenSim.Region.ScriptEngine.Shared.LSL_Types.Vector3;
43
44/**
45 * @brief Compute values used during code generation to keep track of where computed values are stored.
46 *
47 * Conceptually holds the memory address and type of the value
48 * such as that used for a local variable, global variable, temporary variable.
49 * Also used for things like constants and function/method entrypoints,
50 * they are basically treated as read-only variables.
51 *
52 * cv.type - type of the value
53 *
54 * cv.PushVal() - pushes the value on the CIL stack
55 * cv.PushRef() - pushes address of the value on the CIL stack
56 *
57 * cv.PopPre() - gets ready to pop from the CIL stack
58 * ...by possibly pushing something
59 * <push value to be popped>
60 * cv.PushPre() - pops value from the CIL stack
61 *
62 * If the type is a TokenTypeSDTypeDelegate, the location is callable,
63 * so you get these additional functions:
64 *
65 * cv.GetRetType() - gets function/method's return value type
66 * TokenTypeVoid if void
67 * null if not a delegate
68 * cv.GetArgTypes() - gets array of argument types
69 * as seen by script level, ie,
70 * does not include any hidden 'this' type
71 * cv.GetArgSig() - gets argument signature eg, "(integer,list)"
72 * null if not a delegate
73 *
74 * cv.CallPre() - gets ready to call the function/method
75 * ...by possibly pushing something
76 * such as a 'this' pointer
77 * <push call args left-to-right>
78 * cv.CallPost() - calls the function/method
79 */
80
81namespace OpenSim.Region.ScriptEngine.Yengine
82{
83
84 /**
85 * @brief Location of a value
86 * Includes constants, expressions and temp variables.
87 */
88 public abstract class CompValu
89 {
90 protected static readonly MethodInfo gsmdMethodInfo =
91 typeof(XMRInstAbstract).GetMethod("GetScriptMethodDelegate",
92 new Type[] { typeof(string), typeof(string), typeof(object) });
93
94 private static readonly MethodInfo avpmListMethInfo = typeof(XMRInstArrays).GetMethod("PopList", new Type[] { typeof(int), typeof(LSL_List) });
95 private static readonly MethodInfo avpmObjectMethInfo = typeof(XMRInstArrays).GetMethod("PopObject", new Type[] { typeof(int), typeof(object) });
96 private static readonly MethodInfo avpmStringMethInfo = typeof(XMRInstArrays).GetMethod("PopString", new Type[] { typeof(int), typeof(string) });
97
98 public TokenType type; // type of the value and where in the source it was used
99
100 public CompValu(TokenType type)
101 {
102 this.type = type;
103 }
104
105 public Type ToSysType()
106 {
107 return (type.ToLSLWrapType() != null) ? type.ToLSLWrapType() : type.ToSysType();
108 }
109
110 /*
111 * if a field of an XMRInstArrays array cannot be directly written,
112 * get the method that can write it
113 */
114 private static MethodInfo ArrVarPopMeth(FieldInfo fi)
115 {
116 if(fi.Name == "iarLists")
117 return avpmListMethInfo;
118 if(fi.Name == "iarObjects")
119 return avpmObjectMethInfo;
120 if(fi.Name == "iarStrings")
121 return avpmStringMethInfo;
122 return null;
123 }
124
125 /*
126 * emit code to push value onto stack
127 */
128 public void PushVal(ScriptCodeGen scg, Token errorAt, TokenType stackType)
129 {
130 this.PushVal(scg, errorAt, stackType, false);
131 }
132 public void PushVal(ScriptCodeGen scg, Token errorAt, TokenType stackType, bool explicitAllowed)
133 {
134 this.PushVal(scg, errorAt);
135 TypeCast.CastTopOfStack(scg, errorAt, this.type, stackType, explicitAllowed);
136 }
137 public abstract void PushVal(ScriptCodeGen scg, Token errorAt);
138 public abstract void PushRef(ScriptCodeGen scg, Token errorAt);
139
140 /*
141 * emit code to pop value from stack
142 */
143 public void PopPost(ScriptCodeGen scg, Token errorAt, TokenType stackType)
144 {
145 TypeCast.CastTopOfStack(scg, errorAt, stackType, this.type, false);
146 this.PopPost(scg, errorAt);
147 }
148 public virtual void PopPre(ScriptCodeGen scg, Token errorAt)
149 {
150 }
151
152 /*
153 * call this before pushing value to be popped
154 */
155 public abstract void PopPost(ScriptCodeGen scg, Token errorAt); // call this after pushing value to be popped
156
157
158 /*
159 * return true: doing a PushVal() does not involve CheckRun()
160 * false: otherwise
161 */
162 public virtual bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
163 {
164 return true;
165 }
166
167 /*
168 * These additional functions are available if the type is a delegate
169 */
170 public TokenType GetRetType()
171 {
172 if(!(type is TokenTypeSDTypeDelegate))
173 return null;
174 return ((TokenTypeSDTypeDelegate)type).decl.GetRetType();
175 }
176 public TokenType[] GetArgTypes()
177 {
178 if(!(type is TokenTypeSDTypeDelegate))
179 return null;
180 return ((TokenTypeSDTypeDelegate)type).decl.GetArgTypes();
181 }
182 public string GetArgSig()
183 {
184 if(!(type is TokenTypeSDTypeDelegate))
185 return null;
186 return ((TokenTypeSDTypeDelegate)type).decl.GetArgSig();
187 }
188
189 /*
190 * These are used only if type is a delegate too
191 * - but it is a real delegate pointer in a global or local variable or a field, etc
192 * - ie, PushVal() pushes a delegate pointer
193 * - so we must have CallPre() push the delegate pointer as a 'this' for this.Invoke(...)
194 * - and CallPost() call the delegate's Invoke() method
195 * - we assume the target function is non-trivial so we always use a call label
196 */
197 public virtual void CallPre(ScriptCodeGen scg, Token errorAt) // call this before pushing arguments
198 {
199 new ScriptCodeGen.CallLabel(scg, errorAt);
200 this.PushVal(scg, errorAt);
201 }
202 public virtual void CallPost(ScriptCodeGen scg, Token errorAt) // call this after pushing arguments
203 {
204 TokenTypeSDTypeDelegate ttd = (TokenTypeSDTypeDelegate)type;
205 MethodInfo invokeMethodInfo = ttd.decl.GetInvokerInfo();
206 scg.ilGen.Emit(errorAt, OpCodes.Callvirt, invokeMethodInfo);
207 scg.openCallLabel = null;
208 }
209
210 /*
211 * Utilities used by CompValuGlobalVar and CompValuInstField
212 * where the value is located in a type-dependent array.
213 */
214 protected void EmitFieldPushVal(ScriptCodeGen scg, Token errorAt, TokenDeclVar var)
215 {
216 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, var.vTableArray); // which array
217 scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, var.vTableIndex); // which array element
218 if(type is TokenTypeFloat)
219 {
220 scg.ilGen.Emit(errorAt, OpCodes.Ldelem_R8);
221 }
222 else if(type is TokenTypeInt)
223 {
224 scg.ilGen.Emit(errorAt, OpCodes.Ldelem_I4);
225 }
226 else if(type is TokenTypeSDTypeDelegate)
227 {
228 scg.ilGen.Emit(errorAt, OpCodes.Ldelem, typeof(object));
229 scg.ilGen.Emit(errorAt, OpCodes.Castclass, ToSysType());
230 }
231 else
232 {
233 scg.ilGen.Emit(errorAt, OpCodes.Ldelem, ToSysType());
234 }
235 }
236
237 protected void EmitFieldPushRef(ScriptCodeGen scg, Token errorAt, TokenDeclVar var)
238 {
239 if(ArrVarPopMeth(var.vTableArray) != null)
240 {
241 scg.ErrorMsg(errorAt, "can't take address of this variable");
242 }
243 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, var.vTableArray);
244 scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, var.vTableIndex);
245 scg.ilGen.Emit(errorAt, OpCodes.Ldelema, ToSysType());
246 }
247
248 protected void EmitFieldPopPre(ScriptCodeGen scg, Token errorAt, TokenDeclVar var)
249 {
250 if(ArrVarPopMeth(var.vTableArray) != null)
251 {
252 scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, var.vTableIndex);
253 }
254 else
255 {
256 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, var.vTableArray);
257 scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, var.vTableIndex);
258 }
259 }
260
261 protected void EmitFieldPopPost(ScriptCodeGen scg, Token errorAt, TokenDeclVar var)
262 {
263 if(ArrVarPopMeth(var.vTableArray) != null)
264 {
265 scg.ilGen.Emit(errorAt, OpCodes.Call, ArrVarPopMeth(var.vTableArray));
266 }
267 else if(type is TokenTypeFloat)
268 {
269 scg.ilGen.Emit(errorAt, OpCodes.Stelem_R8);
270 }
271 else if(type is TokenTypeInt)
272 {
273 scg.ilGen.Emit(errorAt, OpCodes.Stelem_I4);
274 }
275 else if(type is TokenTypeSDTypeDelegate)
276 {
277 scg.ilGen.Emit(errorAt, OpCodes.Stelem, typeof(object));
278 }
279 else
280 {
281 scg.ilGen.Emit(errorAt, OpCodes.Stelem, ToSysType());
282 }
283 }
284
285 /**
286 * @brief With value pushed on stack, emit code to set a property by calling its setter() method.
287 * @param scg = which script is being compiled
288 * @param errorAt = for error messages
289 * @param type = property type
290 * @param setProp = setter() method
291 */
292 protected void EmitPopPostProp(ScriptCodeGen scg, Token errorAt, TokenType type, CompValu setProp)
293 {
294 ScriptMyLocal temp = scg.ilGen.DeclareLocal(type.ToSysType(), "__spr_" + errorAt.Unique);
295 scg.ilGen.Emit(errorAt, OpCodes.Stloc, temp);
296 setProp.CallPre(scg, errorAt);
297 scg.ilGen.Emit(errorAt, OpCodes.Ldloc, temp);
298 setProp.CallPost(scg, errorAt);
299 }
300 }
301
302 // The value is kept in an (XMR_Array) array element
303 public class CompValuArEle: CompValu
304 {
305 public CompValu arr;
306 private CompValu idx;
307 private TokenTypeObject tto;
308
309 private static readonly MethodInfo getByKeyMethodInfo = typeof(XMR_Array).GetMethod("GetByKey",
310 new Type[] { typeof(object) });
311 private static readonly MethodInfo setByKeyMethodInfo = typeof(XMR_Array).GetMethod("SetByKey",
312 new Type[] { typeof (object),
313 typeof (object) });
314
315 // type = TokenTypeObject always, as our array elements are always of type 'object'
316 // arr = where the array object itself is stored
317 // idx = where the index value is stored
318 public CompValuArEle(TokenType type, CompValu arr, CompValu idx) : base(type)
319 {
320 this.arr = arr;
321 this.idx = idx;
322 this.tto = new TokenTypeObject(this.type);
323 }
324 public override void PushVal(ScriptCodeGen scg, Token errorAt)
325 {
326 arr.PushVal(scg, errorAt); // array
327 idx.PushVal(scg, errorAt, this.tto); // key
328 scg.ilGen.Emit(errorAt, OpCodes.Call, getByKeyMethodInfo);
329 }
330 public override void PushRef(ScriptCodeGen scg, Token errorAt)
331 {
332 scg.ErrorMsg(errorAt, "array element not allowed here");
333 scg.ilGen.Emit(errorAt, OpCodes.Ldnull);
334 }
335 public override void PopPre(ScriptCodeGen scg, Token errorAt)
336 {
337 arr.PushVal(scg, errorAt); // array
338 idx.PushVal(scg, errorAt, this.tto); // key
339 }
340 public override void PopPost(ScriptCodeGen scg, Token errorAt)
341 {
342 scg.ilGen.Emit(errorAt, OpCodes.Call, setByKeyMethodInfo);
343 }
344
345 // non-trivial because it needs to be copied into a temp
346 // in case the idiot does dumb-ass side effects tricks
347 // eg, (x = 0) + x + 2
348 // should read old value of x not 0
349 // but if 'xmroption norighttoleft;' in effect,
350 // we can read it in any order so reading an
351 // XMR_Array element is trivial
352 public override bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
353 {
354 return readAt.nr2l;
355 }
356 }
357
358 // The value is kept in the current function's argument list
359 public class CompValuArg: CompValu
360 {
361 public int index;
362 public bool readOnly;
363
364 private static OpCode[] ldargs = { OpCodes.Ldarg_0, OpCodes.Ldarg_1,
365 OpCodes.Ldarg_2, OpCodes.Ldarg_3 };
366
367 public CompValuArg(TokenType type, int index) : base(type)
368 {
369 this.index = index;
370 }
371 public CompValuArg(TokenType type, int index, bool ro) : base(type)
372 {
373 this.index = index;
374 this.readOnly = ro;
375 }
376 public override void PushVal(ScriptCodeGen scg, Token errorAt)
377 {
378 if(index < ldargs.Length)
379 scg.ilGen.Emit(errorAt, ldargs[index]);
380 else if(index <= 255)
381 scg.ilGen.Emit(errorAt, OpCodes.Ldarg_S, index);
382 else
383 scg.ilGen.Emit(errorAt, OpCodes.Ldarg, index);
384 }
385 public override void PushRef(ScriptCodeGen scg, Token errorAt)
386 {
387 if(readOnly)
388 {
389 scg.ErrorMsg(errorAt, "location cannot be written to");
390 }
391 if(index <= 255)
392 scg.ilGen.Emit(errorAt, OpCodes.Ldarga_S, index);
393 else
394 scg.ilGen.Emit(errorAt, OpCodes.Ldarga, index);
395 }
396 public override void PopPost(ScriptCodeGen scg, Token errorAt)
397 {
398 if(readOnly)
399 {
400 scg.ErrorMsg(errorAt, "location cannot be written to");
401 }
402 scg.ilGen.Emit(errorAt, OpCodes.Starg, index);
403 }
404
405 // non-trivial because it needs to be copied into a temp
406 // in case the idiot does dumb-ass side effects tricks
407 // eg, (x = 0) + x + 2
408 // should read old value of x not 0
409 // but if 'xmroption norighttoleft;' in effect,
410 // we can read it in any order so reading an
411 // argument is trivial
412 public override bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
413 {
414 return readAt.nr2l;
415 }
416 }
417
418 // The value is a character constant
419 public class CompValuChar: CompValu
420 {
421 public char x;
422
423 public CompValuChar(TokenType type, char x) : base(type)
424 {
425 if(!(this.type is TokenTypeChar))
426 {
427 this.type = new TokenTypeChar(this.type);
428 }
429 this.x = x;
430 }
431 public override void PushVal(ScriptCodeGen scg, Token errorAt)
432 {
433 scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, (int)x);
434 }
435 public override void PushRef(ScriptCodeGen scg, Token errorAt)
436 {
437 throw new Exception("cannot get constant's address");
438 }
439 public override void PopPost(ScriptCodeGen scg, Token errorAt)
440 {
441 throw new Exception("cannot store into contant");
442 }
443 }
444
445 // The value is kept in a struct/class field of an internal struct/class
446 public class CompValuField: CompValu
447 {
448 CompValu obj;
449 FieldInfo field;
450
451 public CompValuField(TokenType type, CompValu obj, FieldInfo field) : base(type)
452 {
453 this.obj = obj;
454 this.field = field;
455 }
456 public override void PushVal(ScriptCodeGen scg, Token errorAt)
457 {
458 if(field.ReflectedType.IsValueType)
459 {
460 obj.PushRef(scg, errorAt);
461 }
462 else
463 {
464 obj.PushVal(scg, errorAt);
465 }
466 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, field);
467 }
468 public override void PushRef(ScriptCodeGen scg, Token errorAt)
469 {
470 if(field.ReflectedType.IsValueType)
471 {
472 obj.PushRef(scg, errorAt);
473 }
474 else
475 {
476 obj.PushVal(scg, errorAt);
477 }
478 scg.ilGen.Emit(errorAt, OpCodes.Ldflda, field);
479 }
480 public override void PopPre(ScriptCodeGen scg, Token errorAt)
481 {
482 if(field.ReflectedType.IsValueType)
483 {
484 obj.PushRef(scg, errorAt);
485 }
486 else
487 {
488 obj.PushVal(scg, errorAt);
489 }
490 }
491 public override void PopPost(ScriptCodeGen scg, Token errorAt)
492 {
493 scg.ilGen.Emit(errorAt, OpCodes.Stfld, field);
494 }
495
496 // non-trivial because it needs to be copied into a temp
497 // in case the idiot does dumb-ass side effects tricks
498 // eg, (x = 0) + x + 2
499 // should read old value of x not 0
500 // but if 'xmroption norighttoleft;' in effect,
501 // we can read it in any order so reading an
502 // field of a class/struct is trivial
503 public override bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
504 {
505 return readAt.nr2l;
506 }
507 }
508
509 // Accessing an element of a fixed-dimension array
510 public class CompValuFixArEl: CompValu
511 {
512 private CompValu baseRVal;
513 private CompValu[] subRVals;
514
515 private int nSubs;
516 private TokenDeclVar getFunc;
517 private TokenDeclVar setFunc;
518 private TokenTypeInt tokenTypeInt;
519
520 /**
521 * @brief Set up to access an element of an array.
522 * @param scg = what script we are compiling
523 * @param baseRVal = what array we are accessing
524 * @param subRVals = the subscripts being applied
525 */
526 public CompValuFixArEl(ScriptCodeGen scg, CompValu baseRVal, CompValu[] subRVals) : base(GetElementType(scg, baseRVal, subRVals))
527 {
528 this.baseRVal = baseRVal; // location of the array itself
529 this.subRVals = subRVals; // subscript values
530 this.nSubs = subRVals.Length;
531
532 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)baseRVal.type;
533 TokenDeclSDTypeClass sdtDecl = sdtType.decl;
534 tokenTypeInt = new TokenTypeInt(sdtType);
535
536 TokenName name = new TokenName(sdtType, "Get");
537 TokenType[] argsig = new TokenType[nSubs];
538 for(int i = 0; i < nSubs; i++)
539 {
540 argsig[i] = tokenTypeInt;
541 }
542 getFunc = scg.FindThisMember(sdtDecl, name, argsig);
543
544 name = new TokenName(sdtType, "Set");
545 argsig = new TokenType[nSubs + 1];
546 for(int i = 0; i < nSubs; i++)
547 {
548 argsig[i] = tokenTypeInt;
549 }
550 argsig[nSubs] = getFunc.retType;
551 setFunc = scg.FindThisMember(sdtDecl, name, argsig);
552 }
553
554 /**
555 * @brief Read array element and push value on stack.
556 */
557 public override void PushVal(ScriptCodeGen scg, Token errorAt)
558 {
559 // call script-defined class' Get() method to fetch the value
560 baseRVal.PushVal(scg, errorAt);
561 for(int i = 0; i < nSubs; i++)
562 {
563 subRVals[i].PushVal(scg, errorAt, tokenTypeInt);
564 }
565 scg.ilGen.Emit(errorAt, OpCodes.Call, getFunc.ilGen);
566 }
567
568 /**
569 * @brief Push address of array element on stack.
570 */
571 public override void PushRef(ScriptCodeGen scg, Token errorAt)
572 {
573 throw new Exception("tu stOOpid to get array element address");
574 }
575
576 /**
577 * @brief Prepare to write array element.
578 */
579 public override void PopPre(ScriptCodeGen scg, Token errorAt)
580 {
581 // set up call to script-defined class' Set() method to write the value
582 baseRVal.PushVal(scg, errorAt);
583 for(int i = 0; i < nSubs; i++)
584 {
585 subRVals[i].PushVal(scg, errorAt, tokenTypeInt);
586 }
587 }
588
589 /**
590 * @brief Pop value from stack and write array element.
591 */
592 public override void PopPost(ScriptCodeGen scg, Token errorAt)
593 {
594 // call script-defined class' Set() method to write the value
595 scg.ilGen.Emit(errorAt, OpCodes.Call, setFunc.ilGen);
596 }
597
598 /**
599 * @brief Get the array element type by getting the Get() functions return type.
600 * Crude but effective.
601 * @param scg = what script we are compiling
602 * @param baseRVal = what array we are accessing
603 * @param subRVals = the subscripts being applied
604 * @returns array element type
605 */
606 private static TokenType GetElementType(ScriptCodeGen scg, CompValu baseRVal, CompValu[] subRVals)
607 {
608 TokenTypeSDTypeClass sdtType = (TokenTypeSDTypeClass)baseRVal.type;
609 TokenDeclSDTypeClass sdtDecl = sdtType.decl;
610 TokenName name = new TokenName(sdtType, "Get");
611 int nSubs = subRVals.Length;
612 TokenType[] argsig = new TokenType[nSubs];
613 argsig[0] = new TokenTypeInt(sdtType);
614 for(int i = 0; ++i < nSubs;)
615 {
616 argsig[i] = argsig[0];
617 }
618 TokenDeclVar getFunc = scg.FindThisMember(sdtDecl, name, argsig);
619 return getFunc.retType;
620 }
621
622 // non-trivial because it needs to be copied into a temp
623 // in case the idiot does dumb-ass side effects tricks
624 // eg, (x = 0) + x + 2
625 // should read old value of x not 0
626 // but if 'xmroption norighttoleft;' in effect,
627 // we can read it in any order so reading an
628 // fixed-dimension array element is trivial
629 public override bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
630 {
631 return readAt.nr2l;
632 }
633 }
634
635 // The value is a float constant
636 public class CompValuFloat: CompValu
637 {
638 public double x;
639
640 public CompValuFloat(TokenType type, double x) : base(type)
641 {
642 if(!(this.type is TokenTypeFloat))
643 {
644 this.type = new TokenTypeFloat(this.type);
645 }
646 this.x = x;
647 }
648 public override void PushVal(ScriptCodeGen scg, Token errorAt)
649 {
650 scg.ilGen.Emit(errorAt, OpCodes.Ldc_R8, x);
651 }
652 public override void PushRef(ScriptCodeGen scg, Token errorAt)
653 {
654 throw new Exception("cannot get constant's address");
655 }
656 public override void PopPost(ScriptCodeGen scg, Token errorAt)
657 {
658 throw new Exception("cannot store into constant");
659 }
660 }
661
662 // The value is the entrypoint of a script-defined global function.
663 // These are also used for script-defined type static methods as the calling convention is the same,
664 // ie, the XMRInstance pointer is a hidden first argument.
665 // There is just one of these created when the function is being compiled as there is only one value
666 // of the function.
667 public class CompValuGlobalMeth: CompValu
668 {
669 private TokenDeclVar func;
670
671 public CompValuGlobalMeth(TokenDeclVar declFunc) : base(declFunc.GetDelType())
672 {
673 this.func = declFunc;
674 }
675
676 /**
677 * @brief PushVal for a function/method means push a delegate on the stack.
678 * We build a call to the DynamicMethod's CreateDelegate() function
679 * to create the delegate. Slip the scriptinstance pointer as the
680 * function's arg 0 so it will get passed to the function when called.
681 */
682 public override void PushVal(ScriptCodeGen scg, Token errorAt)
683 {
684 string dtn = type.ToString();
685 if(dtn.StartsWith("delegate "))
686 dtn = dtn.Substring(9);
687
688 // delegateinstance = (signature)scriptinstance.GetScriptMethodDelegate (methName, signature, arg0);
689 // where methName = [<sdtclass>.]<methname>(<argtypes>)
690 // signature = <rettype>(<argtypes>)
691 // arg0 = scriptinstance (XMRInstance)
692 scg.PushXMRInst(); // [0] scriptinstance
693 scg.ilGen.Emit(errorAt, OpCodes.Ldstr, func.ilGen.methName); // [1] method name
694 scg.ilGen.Emit(errorAt, OpCodes.Ldstr, dtn); // [2] delegate type name
695 scg.PushXMRInst(); // [3] scriptinstance
696 scg.ilGen.Emit(errorAt, OpCodes.Callvirt, gsmdMethodInfo); // [0] delegate instance
697 scg.ilGen.Emit(errorAt, OpCodes.Castclass, type.ToSysType()); // [0] cast to correct delegate class
698 }
699 public override void PushRef(ScriptCodeGen scg, Token errorAt)
700 {
701 throw new Exception("cannot get ref to global method");
702 }
703 public override void PopPost(ScriptCodeGen scg, Token errorAt)
704 {
705 throw new Exception("cannot store into global method");
706 }
707
708 /**
709 * @brief A direct call is much simpler than pushing a delegate.
710 * Just push the XMRInstance pointer, push the args and finally call the function.
711 */
712 public override void CallPre(ScriptCodeGen scg, Token errorAt)
713 {
714 if(!this.func.IsFuncTrivial(scg))
715 new ScriptCodeGen.CallLabel(scg, errorAt);
716
717 // all script-defined global functions are static methods created by DynamicMethod()
718 // and the first argument is always the XMR_Instance pointer
719 scg.PushXMRInst();
720 }
721 public override void CallPost(ScriptCodeGen scg, Token errorAt)
722 {
723 scg.ilGen.Emit(errorAt, OpCodes.Call, func.ilGen);
724 if(!this.func.IsFuncTrivial(scg))
725 scg.openCallLabel = null;
726 }
727 }
728
729 // The value is in a script-global variable = ScriptModule instance variable
730 // It could also be a script-global property
731 public class CompValuGlobalVar: CompValu
732 {
733 private static readonly FieldInfo glblVarsFieldInfo = typeof(XMRInstAbstract).GetField("glblVars");
734
735 private TokenDeclVar declVar;
736
737 public CompValuGlobalVar(TokenDeclVar declVar, XMRInstArSizes glblSizes) : base(declVar.type)
738 {
739 this.declVar = declVar;
740 if((declVar.getProp == null) && (declVar.setProp == null))
741 {
742 declVar.type.AssignVarSlot(declVar, glblSizes);
743 }
744 }
745 public override void PushVal(ScriptCodeGen scg, Token errorAt)
746 {
747 if((declVar.getProp == null) && (declVar.setProp == null))
748 {
749 scg.PushXMRInst();
750 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, glblVarsFieldInfo);
751 EmitFieldPushVal(scg, errorAt, declVar);
752 }
753 else if(declVar.getProp != null)
754 {
755 declVar.getProp.location.CallPre(scg, errorAt);
756 declVar.getProp.location.CallPost(scg, errorAt);
757 }
758 else
759 {
760 scg.ErrorMsg(errorAt, "property not readable");
761 scg.PushDefaultValue(declVar.type);
762 }
763 }
764 public override void PushRef(ScriptCodeGen scg, Token errorAt)
765 {
766 if((declVar.getProp == null) && (declVar.setProp == null))
767 {
768 scg.PushXMRInst();
769 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, glblVarsFieldInfo);
770 EmitFieldPushRef(scg, errorAt, declVar);
771 }
772 else
773 {
774 scg.ErrorMsg(errorAt, "cannot get address of property");
775 }
776 }
777 public override void PopPre(ScriptCodeGen scg, Token errorAt)
778 {
779 if((declVar.getProp == null) && (declVar.setProp == null))
780 {
781 scg.PushXMRInst();
782 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, glblVarsFieldInfo);
783 EmitFieldPopPre(scg, errorAt, declVar);
784 }
785 else if(declVar.setProp == null)
786 {
787 scg.ErrorMsg(errorAt, "property not writable");
788 }
789 }
790 public override void PopPost(ScriptCodeGen scg, Token errorAt)
791 {
792 if((declVar.getProp == null) && (declVar.setProp == null))
793 {
794 EmitFieldPopPost(scg, errorAt, declVar);
795 }
796 else if(declVar.setProp != null)
797 {
798 EmitPopPostProp(scg, errorAt, declVar.type, declVar.setProp.location);
799 }
800 else
801 {
802 scg.ilGen.Emit(errorAt, OpCodes.Pop);
803 }
804 }
805
806 // non-trivial because it needs to be copied into a temp
807 // in case the idiot does dumb-ass side effects tricks
808 // eg, (x = 0) + x + 2
809 // should read old value of x not 0
810 // but if 'xmroption norighttoleft;' in effect,
811 // we can read it in any order so reading an
812 // global variable is trivial provided it is
813 // not a property or the property function is
814 // trivial.
815 public override bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
816 {
817 return readAt.nr2l && ((declVar.getProp == null) || declVar.getProp.IsFuncTrivial(scg));
818 }
819 }
820
821 // The value is in an $idxprop property of a script-defined type class or interface instance.
822 // Reading and writing is via a method call.
823 public class CompValuIdxProp: CompValu
824 {
825 private TokenDeclVar idxProp; // $idxprop property within baseRVal
826 private CompValu baseRVal; // pointer to class or interface object containing property
827 private TokenType[] argTypes; // argument types as required by $idxprop declaration
828 private CompValu[] indices; // actual index values to pass to getter/setter method
829 private CompValu setProp; // location of setter method
830
831 public CompValuIdxProp(TokenDeclVar idxProp, CompValu baseRVal, TokenType[] argTypes, CompValu[] indices) : base(idxProp.type)
832 {
833 this.idxProp = idxProp;
834 this.baseRVal = baseRVal;
835 this.argTypes = argTypes;
836 this.indices = indices;
837 }
838
839 /**
840 * @brief Pushing the property's value is a matter of calling the getter method
841 * with the supplied argument list as is.
842 */
843 public override void PushVal(ScriptCodeGen scg, Token errorAt)
844 {
845 if(idxProp.getProp != null)
846 {
847 if(!idxProp.getProp.IsFuncTrivial(scg))
848 {
849 for(int i = indices.Length; --i >= 0;)
850 {
851 indices[i] = scg.Trivialize(indices[i], errorAt);
852 }
853 }
854 CompValu getProp = GetIdxPropMeth(idxProp.getProp);
855 getProp.CallPre(scg, errorAt);
856 for(int i = 0; i < indices.Length; i++)
857 {
858 indices[i].PushVal(scg, errorAt, argTypes[i]);
859 }
860 getProp.CallPost(scg, errorAt);
861 }
862 else
863 {
864 // write-only property
865 scg.ErrorMsg(errorAt, "member not readable");
866 scg.PushDefaultValue(idxProp.type);
867 }
868 }
869
870 /**
871 * @brief A property does not have a memory address.
872 */
873 public override void PushRef(ScriptCodeGen scg, Token errorAt)
874 {
875 scg.ErrorMsg(errorAt, "member has no address");
876 scg.ilGen.Emit(errorAt, OpCodes.Ldnull);
877 }
878
879 /**
880 * @brief Preparing to write a property consists of preparing to call the setter method
881 * then pushing the index arguments.
882 */
883 public override void PopPre(ScriptCodeGen scg, Token errorAt)
884 {
885 if(idxProp.setProp != null)
886 {
887 if(!idxProp.setProp.IsFuncTrivial(scg))
888 {
889 for(int i = indices.Length; --i >= 0;)
890 {
891 indices[i] = scg.Trivialize(indices[i], errorAt);
892 }
893 }
894 this.setProp = GetIdxPropMeth(idxProp.setProp);
895 this.setProp.CallPre(scg, errorAt);
896 for(int i = 0; i < indices.Length; i++)
897 {
898 indices[i].PushVal(scg, errorAt, argTypes[i]);
899 }
900 }
901 else
902 {
903 // read-only property
904 scg.ErrorMsg(errorAt, "member not writable");
905 }
906 }
907
908 /**
909 * @brief Finishing writing a property consists of finishing the call to the setter method
910 * now that the value to be written has been pushed by our caller.
911 */
912 public override void PopPost(ScriptCodeGen scg, Token errorAt)
913 {
914 if(idxProp.setProp != null)
915 {
916 this.setProp.CallPost(scg, errorAt);
917 }
918 else
919 {
920 scg.ilGen.Emit(errorAt, OpCodes.Pop);
921 }
922 }
923
924 public override bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
925 {
926 // if no getter, reading would throw an error, so doesn't really matter what we say
927 if(idxProp.getProp == null)
928 return true;
929
930 // assume interface methods are always non-trivial because we don't know anything about the actual implementation
931 if(baseRVal.type is TokenTypeSDTypeInterface)
932 return false;
933
934 // accessing it in any way can't be trivial if reading the pointer isn't trivial
935 if(!baseRVal.IsReadTrivial(scg, readAt))
936 return false;
937
938 // likewise with the indices
939 foreach(CompValu idx in indices)
940 {
941 if(!idx.IsReadTrivial(scg, readAt))
942 return false;
943 }
944
945 // now the only way it can be non-trivial to read is if the getter() method itself is non-trivial.
946 return idxProp.getProp.IsFuncTrivial(scg);
947 }
948
949 /**
950 * @brief Get how to call the getter or setter method.
951 */
952 private CompValu GetIdxPropMeth(TokenDeclVar meth)
953 {
954 if(baseRVal.type is TokenTypeSDTypeClass)
955 {
956 return new CompValuInstMember(meth, baseRVal, false);
957 }
958 return new CompValuIntfMember(meth, baseRVal);
959 }
960 }
961
962 // This represents the type and location of an internally-defined function
963 // that a script can call
964 public class CompValuInline: CompValu
965 {
966 public TokenDeclInline declInline;
967
968 public CompValuInline(TokenDeclInline declInline) : base(declInline.GetDelType())
969 {
970 this.declInline = declInline;
971 }
972
973 public override void PushVal(ScriptCodeGen scg, Token errorAt)
974 {
975 scg.ErrorMsg(errorAt, "cannot use built-in for delegate, wrap it");
976 scg.ilGen.Emit(errorAt, OpCodes.Ldnull);
977 }
978 public override void PushRef(ScriptCodeGen scg, Token errorAt)
979 {
980 scg.ErrorMsg(errorAt, "cannot use built-in for delegate, wrap it");
981 scg.ilGen.Emit(errorAt, OpCodes.Ldnull);
982 }
983 public override void PopPre(ScriptCodeGen scg, Token errorAt)
984 {
985 scg.ErrorMsg(errorAt, "cannot use built-in for delegate, wrap it");
986 }
987 public override void PopPost(ScriptCodeGen scg, Token errorAt)
988 {
989 scg.ErrorMsg(errorAt, "cannot use built-in for delegate, wrap it");
990 scg.ilGen.Emit(errorAt, OpCodes.Pop);
991 }
992 }
993
994 // The value is the entrypoint of a script-defined type's interface method combined with
995 // the pointer used to access the method. Thus there is one of these per call site.
996 // They also handle accessing interface properties.
997 public class CompValuIntfMember: CompValu
998 {
999 private TokenDeclVar declVar;
1000 private CompValu baseRVal;
1001
1002 public CompValuIntfMember(TokenDeclVar declVar, CompValu baseRVal) : base(declVar.type)
1003 {
1004 if(this.type == null)
1005 throw new Exception("interface member type is null");
1006 this.declVar = declVar; // which element of the baseRVal vector to be accessed
1007 this.baseRVal = baseRVal; // the vector of delegates implementing the interface
1008 }
1009
1010 /**
1011 * @brief Reading a method's value means getting a delegate to that method.
1012 * Reading a property's value means calling the getter method for that property.
1013 */
1014 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1015 {
1016 if(declVar.retType != null)
1017 {
1018 baseRVal.PushVal(scg, errorAt); // push pointer to delegate array on stack
1019 scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, declVar.vTableIndex); // select which delegate to access
1020 scg.ilGen.Emit(errorAt, OpCodes.Ldelem, typeof(Delegate)); // push delegate on stack
1021 scg.ilGen.Emit(errorAt, OpCodes.Castclass, type.ToSysType()); // cast to correct delegate class
1022 }
1023 else if(declVar.getProp != null)
1024 {
1025 CompValu getProp = new CompValuIntfMember(declVar.getProp, baseRVal);
1026 getProp.CallPre(scg, errorAt); // reading property, call its getter
1027 getProp.CallPost(scg, errorAt); // ... with no arguments
1028 }
1029 else
1030 {
1031 scg.ErrorMsg(errorAt, "member not readable");
1032 scg.PushDefaultValue(declVar.type);
1033 }
1034 }
1035
1036 /**
1037 * @brief Can't get the address of either a method or a property.
1038 */
1039 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1040 {
1041 scg.ErrorMsg(errorAt, "member has no address");
1042 scg.ilGen.Emit(errorAt, OpCodes.Ldnull);
1043 }
1044
1045 /**
1046 * @brief Can't write a method.
1047 * For property, it means calling the setter method for that property.
1048 */
1049 public override void PopPre(ScriptCodeGen scg, Token errorAt)
1050 {
1051 if(declVar.setProp == null)
1052 {
1053 // read-only property
1054 scg.ErrorMsg(errorAt, "member not writable");
1055 }
1056 }
1057 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1058 {
1059 if(declVar.setProp != null)
1060 {
1061 CompValu setProp = new CompValuIntfMember(declVar.setProp, baseRVal);
1062 EmitPopPostProp(scg, errorAt, declVar.type, setProp);
1063 }
1064 else
1065 {
1066 scg.ilGen.Emit(errorAt, OpCodes.Pop);
1067 }
1068 }
1069
1070 /**
1071 * @brief Reading a method (ie, it's delegate) is always trivial, it's just retrieving
1072 * an element from the delegate array that make up the interface object.
1073 *
1074 * Reading a property is always non-trivial because we don't know which implementation
1075 * the interface is pointing to, so we don't know if it's trivial or not, so assume
1076 * the worst, ie, that it is non-trivial and might call CheckRun().
1077 *
1078 * But all that assumes that locating the interface object in the first place is
1079 * trivial, ie, baseRVal.PushVal() must not call CheckRun() either.
1080 */
1081 public override bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
1082 {
1083 return baseRVal.IsReadTrivial(scg, readAt) && (declVar.getProp == null);
1084 }
1085
1086 /**
1087 * @brief We just defer to the default CallPre() and CallPost() methods.
1088 * They expect this.PushVal() to push a delegate to the method to be called.
1089 * If this member is a method, our PushVal() will read the correct element
1090 * of the iTable array and push it on the stack, ready for Invoke() to be
1091 * called. If this member is a property, the only way it can be called is
1092 * if the property is a delegate, in which case PushVal() will retrieve the
1093 * delegate by calling the property's getter method.
1094 */
1095 }
1096
1097 // The value is the entrypoint of an internal instance method
1098 // such as XMR_Array.index()
1099 public class CompValuIntInstMeth: CompValu
1100 {
1101 private TokenTypeSDTypeDelegate delType;
1102 private CompValu baseRVal;
1103 private MethodInfo methInfo;
1104
1105 public CompValuIntInstMeth(TokenTypeSDTypeDelegate delType, CompValu baseRVal, MethodInfo methInfo) : base(delType)
1106 {
1107 this.delType = delType;
1108 this.baseRVal = baseRVal;
1109 this.methInfo = methInfo;
1110 }
1111
1112 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1113 {
1114 // its value, ie, without applying the (arglist), is a delegate...
1115 baseRVal.PushVal(scg, errorAt);
1116 scg.ilGen.Emit(errorAt, OpCodes.Ldftn, methInfo);
1117 scg.ilGen.Emit(errorAt, OpCodes.Newobj, delType.decl.GetConstructorInfo());
1118 }
1119 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1120 {
1121 throw new Exception("cannot get ref to instance method");
1122 }
1123 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1124 {
1125 throw new Exception("cannot store into instance method");
1126 }
1127
1128 public override void CallPre(ScriptCodeGen scg, Token errorAt)
1129 {
1130 // internal instance methods are always trivial so never need a CallLabel.
1131 baseRVal.PushVal(scg, errorAt);
1132 }
1133 public override void CallPost(ScriptCodeGen scg, Token errorAt)
1134 {
1135 scg.ilGen.Emit(errorAt, OpCodes.Call, methInfo);
1136 }
1137 }
1138
1139 // The value is fetched by calling an internal instance method
1140 // such as XMR_Array.count
1141 public class CompValuIntInstROProp: CompValu
1142 {
1143 private CompValu baseRVal;
1144 private MethodInfo methInfo;
1145
1146 public CompValuIntInstROProp(TokenType valType, CompValu baseRVal, MethodInfo methInfo) : base(valType)
1147 {
1148 this.baseRVal = baseRVal;
1149 this.methInfo = methInfo;
1150 }
1151
1152 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1153 {
1154 baseRVal.PushVal(scg, errorAt);
1155 scg.ilGen.Emit(errorAt, OpCodes.Call, methInfo);
1156 }
1157 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1158 {
1159 scg.ErrorMsg(errorAt, "cannot get ref to read-only property");
1160 scg.ilGen.Emit(errorAt, OpCodes.Ldnull);
1161 }
1162 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1163 {
1164 scg.ErrorMsg(errorAt, "cannot store into read-only property");
1165 scg.ilGen.Emit(errorAt, OpCodes.Pop);
1166 }
1167 }
1168
1169 // The value is in a member of a script-defined type class instance.
1170 // field: value is in one of the arrays contained within XMRSDTypeClObj.instVars
1171 // method: value is a delegate; can be called
1172 // property: reading and writing is via a method call
1173 public class CompValuInstMember: CompValu
1174 {
1175 private static readonly FieldInfo instVarsFieldInfo = typeof(XMRSDTypeClObj).GetField("instVars");
1176 private static readonly FieldInfo vTableFieldInfo = typeof(XMRSDTypeClObj).GetField("sdtcVTable");
1177
1178 private TokenDeclVar declVar; // member being accessed
1179 private CompValu baseRVal; // pointer to particular object instance
1180 private bool ignoreVirt; // ignore virtual attribute; use declVar's non-virtual method/property
1181
1182 public CompValuInstMember(TokenDeclVar declVar, CompValu baseRVal, bool ignoreVirt) : base(declVar.type)
1183 {
1184 this.declVar = declVar;
1185 this.baseRVal = baseRVal;
1186 this.ignoreVirt = ignoreVirt;
1187 }
1188
1189 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1190 {
1191 if(declVar.retType != null)
1192 {
1193 // a method's value, ie, without applying the (arglist), is a delegate...
1194 PushValMethod(scg, errorAt);
1195 }
1196 else if(declVar.vTableArray != null)
1197 {
1198 // a field's value is its XMRSDTypeClObj.instVars array element
1199 baseRVal.PushVal(scg, errorAt);
1200 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, instVarsFieldInfo);
1201 EmitFieldPushVal(scg, errorAt, declVar);
1202 }
1203 else if(declVar.getProp != null)
1204 {
1205 // a property's value is calling its get method with no arguments
1206 CompValu getProp = new CompValuInstMember(declVar.getProp, baseRVal, ignoreVirt);
1207 getProp.CallPre(scg, errorAt);
1208 getProp.CallPost(scg, errorAt);
1209 }
1210 else
1211 {
1212 // write-only property
1213 scg.ErrorMsg(errorAt, "member not readable");
1214 scg.PushDefaultValue(declVar.type);
1215 }
1216 }
1217 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1218 {
1219 if(declVar.vTableArray != null)
1220 {
1221 // a field's value is its XMRSDTypeClObj.instVars array element
1222 baseRVal.PushVal(scg, errorAt);
1223 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, instVarsFieldInfo);
1224 EmitFieldPushRef(scg, errorAt, declVar);
1225 }
1226 else
1227 {
1228 scg.ErrorMsg(errorAt, "member has no address");
1229 scg.ilGen.Emit(errorAt, OpCodes.Ldnull);
1230 }
1231 }
1232 public override void PopPre(ScriptCodeGen scg, Token errorAt)
1233 {
1234 if(declVar.vTableArray != null)
1235 {
1236 // a field's value is its XMRSDTypeClObj.instVars array element
1237 baseRVal.PushVal(scg, errorAt);
1238 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, instVarsFieldInfo);
1239 EmitFieldPopPre(scg, errorAt, declVar);
1240 }
1241 else if(declVar.setProp == null)
1242 {
1243 // read-only property
1244 scg.ErrorMsg(errorAt, "member not writable");
1245 }
1246 }
1247 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1248 {
1249 if(declVar.vTableArray != null)
1250 {
1251 EmitFieldPopPost(scg, errorAt, declVar);
1252 }
1253 else if(declVar.setProp != null)
1254 {
1255 CompValu setProp = new CompValuInstMember(declVar.setProp, baseRVal, ignoreVirt);
1256 EmitPopPostProp(scg, errorAt, declVar.type, setProp);
1257 }
1258 else
1259 {
1260 scg.ilGen.Emit(errorAt, OpCodes.Pop);
1261 }
1262 }
1263
1264 public override bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
1265 {
1266 // accessing it in any way can't be trivial if reading the pointer isn't trivial.
1267 // this also handles strict right-to-left mode detection as the side-effect can
1268 // only apply to the pointer (it can't change which field or method we access).
1269 if(!baseRVal.IsReadTrivial(scg, readAt))
1270 return false;
1271
1272 // now the only way it can be non-trivial to read is if it is a property and the
1273 // getter() method is non-trivial. reading a method means getting a delegate
1274 // which is always trivial, and reading a simple field is always trivial, ie, no
1275 // CheckRun() call can possibly be involved.
1276 if(declVar.retType != null)
1277 {
1278 // a method's value, ie, without applying the (arglist), is a delegate...
1279 return true;
1280 }
1281 if(declVar.vTableArray != null)
1282 {
1283 // a field's value is its XMRSDTypeClObj.instVars array element
1284 return true;
1285 }
1286 if(declVar.getProp != null)
1287 {
1288 // a property's value is calling its get method with no arguments
1289 return declVar.getProp.IsFuncTrivial(scg);
1290 }
1291
1292 // write-only property
1293 return true;
1294 }
1295
1296 public override void CallPre(ScriptCodeGen scg, Token errorAt)
1297 {
1298 if(declVar.retType != null)
1299 {
1300 CallPreMethod(scg, errorAt);
1301 }
1302 else
1303 {
1304 base.CallPre(scg, errorAt);
1305 }
1306 }
1307 public override void CallPost(ScriptCodeGen scg, Token errorAt)
1308 {
1309 if(declVar.retType != null)
1310 {
1311 CallPostMethod(scg, errorAt);
1312 }
1313 else
1314 {
1315 base.CallPost(scg, errorAt);
1316 }
1317 }
1318
1319 /**
1320 * @brief A PushVal() for a method means to push a delegate for the method on the stack.
1321 */
1322 private void PushValMethod(ScriptCodeGen scg, Token errorAt)
1323 {
1324 if((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0)
1325 throw new Exception("dont use for statics");
1326
1327 if(ignoreVirt || (declVar.vTableIndex < 0))
1328 {
1329
1330 /*
1331 * Non-virtual instance method, create a delegate that references the method.
1332 */
1333 string dtn = type.ToString();
1334
1335 // delegateinstance = (signature)scriptinstance.GetScriptMethodDelegate (methName, signature, arg0);
1336 // where methName = <sdtclass>.<methname>(<argtypes>)
1337 // signature = <rettype>(<argtypes>)
1338 // arg0 = sdt istance (XMRSDTypeClObj) 'this' value
1339 scg.PushXMRInst(); // [0] scriptinstance
1340 scg.ilGen.Emit(errorAt, OpCodes.Ldstr, declVar.ilGen.methName); // [1] method name
1341 scg.ilGen.Emit(errorAt, OpCodes.Ldstr, dtn); // [2] delegate type name
1342 baseRVal.PushVal(scg, errorAt); // [3] sdtinstance
1343 scg.ilGen.Emit(errorAt, OpCodes.Callvirt, gsmdMethodInfo); // [0] delegate instance
1344 scg.ilGen.Emit(errorAt, OpCodes.Castclass, type.ToSysType()); // [0] cast to correct delegate class
1345 }
1346 else
1347 {
1348
1349 /*
1350 * Virtual instance method, get the delegate from the vtable.
1351 */
1352 baseRVal.PushVal(scg, errorAt); // 'this' selecting the instance
1353 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, vTableFieldInfo); // get pointer to instance's vtable array
1354 scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, declVar.vTableIndex); // select vtable element
1355 scg.ilGen.Emit(errorAt, OpCodes.Ldelem, typeof(Delegate)); // get delegate pointer = 'this' for 'Invoke()'
1356 scg.ilGen.Emit(errorAt, OpCodes.Castclass, type.ToSysType()); // cast to correct delegate class
1357 }
1358 }
1359
1360 private void CallPreMethod(ScriptCodeGen scg, Token errorAt)
1361 {
1362 if((declVar.sdtFlags & ScriptReduce.SDT_STATIC) != 0)
1363 throw new Exception("dont use for statics");
1364
1365 if(!this.declVar.IsFuncTrivial(scg))
1366 new ScriptCodeGen.CallLabel(scg, errorAt);
1367
1368 if(ignoreVirt || (declVar.vTableIndex < 0))
1369 {
1370 baseRVal.PushVal(scg, errorAt); // 'this' being passed directly to method
1371 }
1372 else
1373 {
1374 baseRVal.PushVal(scg, errorAt); // 'this' selecting the instance
1375 scg.ilGen.Emit(errorAt, OpCodes.Ldfld, vTableFieldInfo); // get pointer to instance's vtable array
1376 scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, declVar.vTableIndex); // select vtable element
1377 scg.ilGen.Emit(errorAt, OpCodes.Ldelem, typeof(Delegate)); // get delegate pointer = 'this' for 'Invoke()'
1378 scg.ilGen.Emit(errorAt, OpCodes.Castclass, type.ToSysType()); // cast to correct delegate class
1379 }
1380 }
1381 private void CallPostMethod(ScriptCodeGen scg, Token errorAt)
1382 {
1383 if(ignoreVirt || (declVar.vTableIndex < 0))
1384 {
1385 // non-virt instance, just call function directly
1386 scg.ilGen.Emit(errorAt, OpCodes.Call, declVar.ilGen);
1387 }
1388 else
1389 {
1390 // virtual, call via delegate Invoke(...) method
1391 TokenTypeSDTypeDelegate ttd = (TokenTypeSDTypeDelegate)type;
1392 MethodInfo invokeMethodInfo = ttd.decl.GetInvokerInfo();
1393 scg.ilGen.Emit(errorAt, OpCodes.Callvirt, invokeMethodInfo);
1394 }
1395
1396 if(!this.declVar.IsFuncTrivial(scg))
1397 scg.openCallLabel = null;
1398 }
1399 }
1400
1401 // The value is an integer constant
1402 public class CompValuInteger: CompValu
1403 {
1404 public int x;
1405
1406 public CompValuInteger(TokenType type, int x) : base(type)
1407 {
1408 if(!(this.type is TokenTypeInt))
1409 {
1410 this.type = new TokenTypeInt(this.type);
1411 }
1412 this.x = x;
1413 }
1414 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1415 {
1416 scg.ilGen.Emit(errorAt, OpCodes.Ldc_I4, x);
1417 }
1418 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1419 {
1420 throw new Exception("cannot get constant's address");
1421 }
1422 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1423 {
1424 throw new Exception("cannot store into constant");
1425 }
1426 }
1427
1428 // The value is an element of a list
1429 public class CompValuListEl: CompValu
1430 {
1431 private static readonly MethodInfo getElementFromListMethodInfo =
1432 typeof(CompValuListEl).GetMethod("GetElementFromList", new Type[] { typeof(LSL_List), typeof(int) });
1433
1434 private CompValu theList;
1435 private CompValu subscript;
1436
1437 public CompValuListEl(TokenType type, CompValu theList, CompValu subscript) : base(type)
1438 {
1439 this.theList = theList;
1440 this.subscript = subscript;
1441 }
1442 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1443 {
1444 theList.PushVal(scg, errorAt, new TokenTypeList(type));
1445 subscript.PushVal(scg, errorAt, new TokenTypeInt(type));
1446 scg.ilGen.Emit(errorAt, OpCodes.Call, getElementFromListMethodInfo);
1447 }
1448 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1449 {
1450 throw new Exception("cannot get list element's address");
1451 }
1452 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1453 {
1454 scg.ErrorMsg(errorAt, "cannot store into list element");
1455 scg.ilGen.Emit(errorAt, OpCodes.Pop);
1456 }
1457
1458 public static object GetElementFromList(LSL_List lis, int idx)
1459 {
1460 object element = lis.Data[idx];
1461 if(element is LSL_Float)
1462 return TypeCast.EHArgUnwrapFloat(element);
1463 if(element is LSL_Integer)
1464 return TypeCast.EHArgUnwrapInteger(element);
1465 if(element is LSL_String)
1466 return TypeCast.EHArgUnwrapString(element);
1467 if(element is OpenMetaverse.Quaternion)
1468 return TypeCast.EHArgUnwrapRotation(element);
1469 if(element is OpenMetaverse.Vector3)
1470 return TypeCast.EHArgUnwrapVector(element);
1471 return element;
1472 }
1473 }
1474
1475 // The value is kept in a script-addressable local variable
1476 public class CompValuLocalVar: CompValu
1477 {
1478 private static int htpopseq = 0;
1479
1480 private ScriptMyLocal localBuilder;
1481
1482 public CompValuLocalVar(TokenType type, string name, ScriptCodeGen scg) : base(type)
1483 {
1484 if(type.ToHeapTrackerType() != null)
1485 {
1486 this.localBuilder = scg.ilGen.DeclareLocal(type.ToHeapTrackerType(), name);
1487 scg.PushXMRInst();
1488 scg.ilGen.Emit(type, OpCodes.Newobj, type.GetHeapTrackerCtor());
1489 scg.ilGen.Emit(type, OpCodes.Stloc, localBuilder);
1490 }
1491 else
1492 {
1493 this.localBuilder = scg.ilGen.DeclareLocal(ToSysType(), name);
1494 }
1495 }
1496
1497 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1498 {
1499 scg.ilGen.Emit(errorAt, OpCodes.Ldloc, localBuilder);
1500 if(type.ToHeapTrackerType() != null)
1501 {
1502 type.CallHeapTrackerPushMeth(errorAt, scg.ilGen);
1503 }
1504 }
1505 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1506 {
1507 if(type.ToHeapTrackerType() != null)
1508 {
1509 scg.ErrorMsg(errorAt, "can't take ref of heap-tracked type " + type.ToString());
1510 scg.ilGen.Emit(errorAt, OpCodes.Ldnull);
1511 }
1512 else
1513 {
1514 scg.ilGen.Emit(errorAt, OpCodes.Ldloca, localBuilder);
1515 }
1516 }
1517
1518 public override void PopPre(ScriptCodeGen scg, Token errorAt)
1519 {
1520 if(type.ToHeapTrackerType() != null)
1521 {
1522 scg.ilGen.Emit(errorAt, OpCodes.Ldloc, localBuilder);
1523 }
1524 }
1525 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1526 {
1527 if(type.ToHeapTrackerType() != null)
1528 {
1529 type.CallHeapTrackerPopMeth(errorAt, scg.ilGen);
1530 }
1531 else
1532 {
1533 scg.ilGen.Emit(errorAt, OpCodes.Stloc, localBuilder);
1534 }
1535 }
1536
1537 public void Pop(ScriptCodeGen scg, Token errorAt)
1538 {
1539 if(type.ToHeapTrackerType() != null)
1540 {
1541 /*
1542 * Popping into a heap tracker wrapped local variable.
1543 * First pop value into a temp var, then call the heap tracker's pop method.
1544 */
1545 ScriptMyLocal htpop = scg.ilGen.DeclareLocal(type.ToSysType(), "htpop$" + (++htpopseq).ToString());
1546 scg.ilGen.Emit(errorAt, OpCodes.Stloc, htpop);
1547 scg.ilGen.Emit(errorAt, OpCodes.Ldloc, localBuilder);
1548 scg.ilGen.Emit(errorAt, OpCodes.Ldloc, htpop);
1549 type.CallHeapTrackerPopMeth(errorAt, scg.ilGen);
1550 }
1551 else
1552 {
1553
1554 /*
1555 * Not a heap-tracked local var, just pop directly into it.
1556 */
1557 scg.ilGen.Emit(errorAt, OpCodes.Stloc, localBuilder);
1558 }
1559 }
1560
1561 // non-trivial because it needs to be copied into a temp
1562 // in case the idiot does dumb-ass side effects tricks
1563 // eg, (x = 0) + x + 2
1564 // should read old value of x not 0
1565 // but if 'xmroption norighttoleft;' in effect,
1566 // we can read it in any order so reading a
1567 // local variable is trivial.
1568 public override bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
1569 {
1570 return readAt.nr2l;
1571 }
1572 }
1573
1574 // The value is a null
1575 public class CompValuNull: CompValu
1576 {
1577 public CompValuNull(TokenType type) : base(type) { }
1578 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1579 {
1580 scg.ilGen.Emit(errorAt, OpCodes.Ldnull);
1581 }
1582 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1583 {
1584 throw new Exception("cannot get null's address");
1585 }
1586 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1587 {
1588 throw new Exception("cannot store into null");
1589 }
1590 }
1591
1592 // The value is a rotation
1593 public class CompValuRot: CompValu
1594 {
1595 public CompValu x;
1596 public CompValu y;
1597 public CompValu z;
1598 public CompValu w;
1599
1600 private static readonly ConstructorInfo lslRotConstructorInfo =
1601 typeof(LSL_Rotation).GetConstructor(new Type[] { typeof (double),
1602 typeof (double),
1603 typeof (double),
1604 typeof (double) });
1605
1606 public CompValuRot(TokenType type, CompValu x, CompValu y, CompValu z, CompValu w) :
1607 base(type)
1608 {
1609 if(!(type is TokenTypeRot))
1610 {
1611 this.type = new TokenTypeRot(type);
1612 }
1613 this.x = x;
1614 this.y = y;
1615 this.z = z;
1616 this.w = w;
1617 }
1618 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1619 {
1620 this.x.PushVal(scg, errorAt, new TokenTypeFloat(this.x.type));
1621 this.y.PushVal(scg, errorAt, new TokenTypeFloat(this.y.type));
1622 this.z.PushVal(scg, errorAt, new TokenTypeFloat(this.z.type));
1623 this.w.PushVal(scg, errorAt, new TokenTypeFloat(this.w.type));
1624 scg.ilGen.Emit(errorAt, OpCodes.Newobj, lslRotConstructorInfo);
1625 }
1626 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1627 {
1628 throw new Exception("cannot get constant's address");
1629 }
1630 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1631 {
1632 throw new Exception("cannot store into constant");
1633 }
1634
1635 public override bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
1636 {
1637 // the supplied values must be trivial because when we call their PushVal()s
1638 // there will be stuff on the stack for all but the first PushVal() and so
1639 // they would have a non-empty stack at their call label.
1640 if(!this.w.IsReadTrivial(scg, readAt) ||
1641 !this.x.IsReadTrivial(scg, readAt) ||
1642 !this.y.IsReadTrivial(scg, readAt) ||
1643 !this.z.IsReadTrivial(scg, readAt))
1644 {
1645 throw new Exception("rotation values must be trivial");
1646 }
1647
1648 return true;
1649 }
1650 }
1651
1652 // The value is in a static field of an internally defined struct/class
1653 public class CompValuSField: CompValu
1654 {
1655 public FieldInfo field;
1656
1657 public CompValuSField(TokenType type, FieldInfo field) : base(type)
1658 {
1659 this.field = field;
1660 }
1661 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1662 {
1663 if((field.Attributes & FieldAttributes.Literal) == 0)
1664 {
1665 scg.ilGen.Emit(errorAt, OpCodes.Ldsfld, field);
1666 return;
1667 }
1668 if(field.FieldType == typeof(LSL_Rotation))
1669 {
1670 LSL_Rotation rot = (LSL_Rotation)field.GetValue(null);
1671 scg.ilGen.Emit(errorAt, OpCodes.Ldc_R8, rot.x);
1672 scg.ilGen.Emit(errorAt, OpCodes.Ldc_R8, rot.y);
1673 scg.ilGen.Emit(errorAt, OpCodes.Ldc_R8, rot.z);
1674 scg.ilGen.Emit(errorAt, OpCodes.Ldc_R8, rot.s);
1675 scg.ilGen.Emit(errorAt, OpCodes.Newobj, ScriptCodeGen.lslRotationConstructorInfo);
1676 return;
1677 }
1678 if(field.FieldType == typeof(LSL_Vector))
1679 {
1680 LSL_Vector vec = (LSL_Vector)field.GetValue(null);
1681 scg.ilGen.Emit(errorAt, OpCodes.Ldc_R8, vec.x);
1682 scg.ilGen.Emit(errorAt, OpCodes.Ldc_R8, vec.y);
1683 scg.ilGen.Emit(errorAt, OpCodes.Ldc_R8, vec.z);
1684 scg.ilGen.Emit(errorAt, OpCodes.Newobj, ScriptCodeGen.lslRotationConstructorInfo);
1685 return;
1686 }
1687 if(field.FieldType == typeof(string))
1688 {
1689 string str = (string)field.GetValue(null);
1690 scg.ilGen.Emit(errorAt, OpCodes.Ldstr, str);
1691 return;
1692 }
1693 throw new Exception("unsupported literal type " + field.FieldType.Name);
1694 }
1695 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1696 {
1697 if((field.Attributes & FieldAttributes.Literal) != 0)
1698 {
1699 throw new Exception("can't write a constant");
1700 }
1701 scg.ilGen.Emit(errorAt, OpCodes.Ldflda, field);
1702 }
1703 public override void PopPre(ScriptCodeGen scg, Token errorAt)
1704 {
1705 }
1706 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1707 {
1708 if((field.Attributes & FieldAttributes.Literal) != 0)
1709 {
1710 throw new Exception("can't write a constant");
1711 }
1712 scg.ilGen.Emit(errorAt, OpCodes.Stsfld, field);
1713 }
1714
1715 // non-trivial because it needs to be copied into a temp
1716 // in case the idiot does dumb-ass side effects tricks
1717 // eg, (x = 0) + x + 2
1718 // should read old value of x not 0
1719 // but if 'xmroption norighttoleft;' in effect,
1720 // we can read it in any order so reading a
1721 // local variable is trivial.
1722 public override bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
1723 {
1724 return readAt.nr2l;
1725 }
1726 }
1727
1728 // The value is a character within a string
1729 public class CompValuStrChr: CompValu
1730 {
1731 private static readonly MethodInfo getCharFromStringMethodInfo =
1732 typeof(CompValuStrChr).GetMethod("GetCharFromString", new Type[] { typeof(string), typeof(int) });
1733
1734 private CompValu theString;
1735 private CompValu subscript;
1736
1737 public CompValuStrChr(TokenType type, CompValu theString, CompValu subscript) : base(type)
1738 {
1739 this.theString = theString;
1740 this.subscript = subscript;
1741 }
1742 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1743 {
1744 theString.PushVal(scg, errorAt, new TokenTypeStr(type));
1745 subscript.PushVal(scg, errorAt, new TokenTypeInt(type));
1746 scg.ilGen.Emit(errorAt, OpCodes.Call, getCharFromStringMethodInfo);
1747 }
1748 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1749 {
1750 throw new Exception("cannot get string character's address");
1751 }
1752 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1753 {
1754 scg.ErrorMsg(errorAt, "cannot store into string character");
1755 scg.ilGen.Emit(errorAt, OpCodes.Pop);
1756 }
1757
1758 public static char GetCharFromString(string s, int i)
1759 {
1760 return s[i];
1761 }
1762 }
1763
1764 // The value is a key or string constant
1765 public class CompValuString: CompValu
1766 {
1767 public string x;
1768
1769 public CompValuString(TokenType type, string x) : base(type)
1770 {
1771 if(!(type is TokenTypeKey) && !(this.type is TokenTypeStr))
1772 {
1773 throw new Exception("bad type " + type.ToString());
1774 }
1775 this.x = x;
1776 }
1777 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1778 {
1779 scg.ilGen.Emit(errorAt, OpCodes.Ldstr, x);
1780 }
1781 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1782 {
1783 throw new Exception("cannot get constant's address");
1784 }
1785 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1786 {
1787 throw new Exception("cannot store into constant");
1788 }
1789 }
1790
1791 // The value is kept in a temp local variable
1792 public class CompValuTemp: CompValu
1793 {
1794 public ScriptMyLocal localBuilder;
1795
1796 public CompValuTemp(TokenType type, ScriptCodeGen scg) : base(type)
1797 {
1798 string name = "tmp$" + (++scg.tempCompValuNum);
1799 this.localBuilder = scg.ilGen.DeclareLocal(ToSysType(), name);
1800 }
1801 protected CompValuTemp(TokenType type) : base(type) { } // CompValuVoid uses this
1802
1803 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1804 {
1805 scg.ilGen.Emit(errorAt, OpCodes.Ldloc, localBuilder);
1806 }
1807 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1808 {
1809 scg.ilGen.Emit(errorAt, OpCodes.Ldloca, localBuilder);
1810 }
1811 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1812 {
1813 scg.ilGen.Emit(errorAt, OpCodes.Stloc, localBuilder);
1814 }
1815 public void Pop(ScriptCodeGen scg, Token errorAt, TokenType stackType)
1816 {
1817 TypeCast.CastTopOfStack(scg, errorAt, stackType, this.type, false);
1818 this.PopPost(scg, errorAt); // in case PopPost() overridden eg by CompValuVoid
1819 }
1820 public void Pop(ScriptCodeGen scg, Token errorAt)
1821 {
1822 this.PopPost(scg, errorAt); // in case PopPost() overridden eg by CompValuVoid
1823 }
1824 }
1825
1826 // The value is a vector
1827 public class CompValuVec: CompValu
1828 {
1829 public CompValu x;
1830 public CompValu y;
1831 public CompValu z;
1832
1833 private static readonly ConstructorInfo lslVecConstructorInfo =
1834 typeof(LSL_Vector).GetConstructor(new Type[] { typeof (double),
1835 typeof (double),
1836 typeof (double) });
1837
1838 public CompValuVec(TokenType type, CompValu x, CompValu y, CompValu z) : base(type)
1839 {
1840 if(!(type is TokenTypeVec))
1841 {
1842 this.type = new TokenTypeVec(type);
1843 }
1844 this.x = x;
1845 this.y = y;
1846 this.z = z;
1847 }
1848 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1849 {
1850 this.x.PushVal(scg, errorAt, new TokenTypeFloat(this.x.type));
1851 this.y.PushVal(scg, errorAt, new TokenTypeFloat(this.y.type));
1852 this.z.PushVal(scg, errorAt, new TokenTypeFloat(this.z.type));
1853 scg.ilGen.Emit(errorAt, OpCodes.Newobj, lslVecConstructorInfo);
1854 }
1855 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1856 {
1857 throw new Exception("cannot get constant's address");
1858 }
1859 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1860 {
1861 throw new Exception("cannot store into constant");
1862 }
1863
1864 public override bool IsReadTrivial(ScriptCodeGen scg, Token readAt)
1865 {
1866 // the supplied values must be trivial because when we call their PushVal()s
1867 // there will be stuff on the stack for all but the first PushVal() and so
1868 // they would have a non-empty stack at their call label.
1869 if(!this.x.IsReadTrivial(scg, readAt) ||
1870 !this.y.IsReadTrivial(scg, readAt) ||
1871 !this.z.IsReadTrivial(scg, readAt))
1872 {
1873 throw new Exception("vector values must be trivial");
1874 }
1875
1876 return true;
1877 }
1878 }
1879
1880 // Used to indicate value will be discarded (eg, where to put return value from a call)
1881 public class CompValuVoid: CompValuTemp
1882 {
1883 public CompValuVoid(Token token) : base((token is TokenTypeVoid) ? (TokenTypeVoid)token : new TokenTypeVoid(token))
1884 {
1885 }
1886 public override void PushVal(ScriptCodeGen scg, Token errorAt)
1887 {
1888 }
1889 public override void PushRef(ScriptCodeGen scg, Token errorAt)
1890 {
1891 throw new Exception("cannot get void address");
1892 }
1893 public override void PopPost(ScriptCodeGen scg, Token errorAt)
1894 {
1895 }
1896 }
1897}