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