1 /*
2 AngelCode Scripting Library
3 Copyright (c) 2003-2017 Andreas Jonsson
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any
7 damages arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any
10 purpose, including commercial applications, and to alter it and
11 redistribute it freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you
14 must not claim that you wrote the original software. If you use
15 this software in a product, an acknowledgment in the product
16 documentation would be appreciated but is not required.
17
18 2. Altered source versions must be plainly marked as such, and
19 must not be misrepresented as being the original software.
20
21 3. This notice may not be removed or altered from any source
22 distribution.
23
24 The original version of this library can be located at:
25 http://www.angelcode.com/angelscript/
26
27 Andreas Jonsson
28 andreas@angelcode.com
29 */
30
31
32 //
33 // as_context.cpp
34 //
35 // This class handles the execution of the byte code
36 //
37
38 #include <math.h> // fmodf() pow()
39
40 #include "as_config.h"
41 #include "as_context.h"
42 #include "as_scriptengine.h"
43 #include "as_tokendef.h"
44 #include "as_texts.h"
45 #include "as_callfunc.h"
46 #include "as_generic.h"
47 #include "as_debug.h" // mkdir()
48 #include "as_bytecode.h"
49 #include "as_scriptobject.h"
50
51 #ifdef _MSC_VER
52 #pragma warning(disable:4702) // unreachable code
53 #endif
54
55 BEGIN_AS_NAMESPACE
56
57 // We need at least 2 PTRs reserved for exception handling
58 // We need at least 1 PTR reserved for calling system functions
59 const int RESERVE_STACK = 2*AS_PTR_SIZE;
60
61 // For each script function call we push 9 PTRs on the call stack
62 const int CALLSTACK_FRAME_SIZE = 9;
63
64 #if defined(AS_DEBUG)
65
66 class asCDebugStats
67 {
68 public:
asCDebugStats()69 asCDebugStats()
70 {
71 memset(instrCount, 0, sizeof(instrCount));
72 memset(instrCount2, 0, sizeof(instrCount2));
73 lastBC = 255;
74 }
75
~asCDebugStats()76 ~asCDebugStats()
77 {
78 // This code writes out some statistics for the VM.
79 // It's useful for determining what needs to be optimized.
80
81 _mkdir("AS_DEBUG");
82 #if _MSC_VER >= 1500 && !defined(AS_MARMALADE)
83 FILE *f;
84 fopen_s(&f, "AS_DEBUG/stats.txt", "wt");
85 #else
86 FILE *f = fopen("AS_DEBUG/stats.txt", "wt");
87 #endif
88 if( f )
89 {
90 // Output instruction statistics
91 fprintf(f, "\nTotal count\n");
92 int n;
93 for( n = 0; n < asBC_MAXBYTECODE; n++ )
94 {
95 if( asBCInfo[n].name && instrCount[n] > 0 )
96 fprintf(f, "%-10.10s : %.0f\n", asBCInfo[n].name, instrCount[n]);
97 }
98
99 fprintf(f, "\nNever executed\n");
100 for( n = 0; n < asBC_MAXBYTECODE; n++ )
101 {
102 if( asBCInfo[n].name && instrCount[n] == 0 )
103 fprintf(f, "%-10.10s\n", asBCInfo[n].name);
104 }
105
106 fprintf(f, "\nSequences\n");
107 for( n = 0; n < 256; n++ )
108 {
109 if( asBCInfo[n].name )
110 {
111 for( int m = 0; m < 256; m++ )
112 {
113 if( instrCount2[n][m] )
114 fprintf(f, "%-10.10s, %-10.10s : %.0f\n", asBCInfo[n].name, asBCInfo[m].name, instrCount2[n][m]);
115 }
116 }
117 }
118 fclose(f);
119 }
120 }
121
Instr(asBYTE bc)122 void Instr(asBYTE bc)
123 {
124 ++instrCount[bc];
125 ++instrCount2[lastBC][bc];
126 lastBC = bc;
127 }
128
129 // Instruction statistics
130 double instrCount[256];
131 double instrCount2[256][256];
132 int lastBC;
133 } stats;
134
135 #endif
136
137 // interface
asGetActiveContext()138 AS_API asIScriptContext *asGetActiveContext()
139 {
140 asCThreadLocalData *tld = asCThreadManager::GetLocalData();
141
142 // tld can be 0 if asGetActiveContext is called before any engine has been created.
143
144 // Observe! I've seen a case where an application linked with the library twice
145 // and thus ended up with two separate instances of the code and global variables.
146 // The application somehow mixed the two instances so that a function called from
147 // a script ended up calling asGetActiveContext from the other instance that had
148 // never been initialized.
149
150 if( tld == 0 || tld->activeContexts.GetLength() == 0 )
151 return 0;
152 return tld->activeContexts[tld->activeContexts.GetLength()-1];
153 }
154
155 // internal
asPushActiveContext(asIScriptContext * ctx)156 asCThreadLocalData *asPushActiveContext(asIScriptContext *ctx)
157 {
158 asCThreadLocalData *tld = asCThreadManager::GetLocalData();
159 asASSERT( tld );
160 if( tld == 0 )
161 return 0;
162 tld->activeContexts.PushLast(ctx);
163 return tld;
164 }
165
166 // internal
asPopActiveContext(asCThreadLocalData * tld,asIScriptContext * ctx)167 void asPopActiveContext(asCThreadLocalData *tld, asIScriptContext *ctx)
168 {
169 UNUSED_VAR(ctx);
170 asASSERT(tld && tld->activeContexts[tld->activeContexts.GetLength() - 1] == ctx);
171 if (tld)
172 tld->activeContexts.PopLast();
173 }
174
asCContext(asCScriptEngine * engine,bool holdRef)175 asCContext::asCContext(asCScriptEngine *engine, bool holdRef)
176 {
177 m_refCount.set(1);
178
179 m_holdEngineRef = holdRef;
180 if( holdRef )
181 engine->AddRef();
182
183 m_engine = engine;
184 m_status = asEXECUTION_UNINITIALIZED;
185 m_stackBlockSize = 0;
186 m_originalStackPointer = 0;
187 m_inExceptionHandler = false;
188 m_isStackMemoryNotAllocated = false;
189 m_needToCleanupArgs = false;
190 m_currentFunction = 0;
191 m_callingSystemFunction = 0;
192 m_regs.objectRegister = 0;
193 m_initialFunction = 0;
194 m_lineCallback = false;
195 m_exceptionCallback = false;
196 m_regs.doProcessSuspend = false;
197 m_doSuspend = false;
198 m_userData = 0;
199 m_regs.ctx = this;
200 }
201
~asCContext()202 asCContext::~asCContext()
203 {
204 DetachEngine();
205 }
206
207 // interface
IsNested(asUINT * nestCount) const208 bool asCContext::IsNested(asUINT *nestCount) const
209 {
210 if( nestCount )
211 *nestCount = 0;
212
213 asUINT c = GetCallstackSize();
214 if( c == 0 )
215 return false;
216
217 // Search for a marker on the call stack
218 // This loop starts at 2 because the 0th entry is not stored in m_callStack,
219 // and then we need to subtract one more to get the base of each frame
220 for( asUINT n = 2; n <= c; n++ )
221 {
222 const asPWORD *s = m_callStack.AddressOf() + (c - n)*CALLSTACK_FRAME_SIZE;
223 if( s && s[0] == 0 )
224 {
225 if( nestCount )
226 (*nestCount)++;
227 else
228 return true;
229 }
230 }
231
232 if( nestCount && *nestCount > 0 )
233 return true;
234
235 return false;
236 }
237
238 // interface
AddRef() const239 int asCContext::AddRef() const
240 {
241 return m_refCount.atomicInc();
242 }
243
244 // interface
Release() const245 int asCContext::Release() const
246 {
247 int r = m_refCount.atomicDec();
248
249 if( r == 0 )
250 {
251 asDELETE(const_cast<asCContext*>(this),asCContext);
252 return 0;
253 }
254
255 return r;
256 }
257
258 // internal
DetachEngine()259 void asCContext::DetachEngine()
260 {
261 if( m_engine == 0 ) return;
262
263 // Clean up all calls, included nested ones
264 do
265 {
266 // Abort any execution
267 Abort();
268
269 // Free all resources
270 Unprepare();
271 }
272 while( IsNested() );
273
274 // Free the stack blocks
275 for( asUINT n = 0; n < m_stackBlocks.GetLength(); n++ )
276 {
277 if( m_stackBlocks[n] )
278 {
279 #ifndef WIP_16BYTE_ALIGN
280 asDELETEARRAY(m_stackBlocks[n]);
281 #else
282 asDELETEARRAYALIGNED(m_stackBlocks[n]);
283 #endif
284 }
285 }
286 m_stackBlocks.SetLength(0);
287 m_stackBlockSize = 0;
288
289 // Clean the user data
290 for( asUINT n = 0; n < m_userData.GetLength(); n += 2 )
291 {
292 if( m_userData[n+1] )
293 {
294 for( asUINT c = 0; c < m_engine->cleanContextFuncs.GetLength(); c++ )
295 if( m_engine->cleanContextFuncs[c].type == m_userData[n] )
296 m_engine->cleanContextFuncs[c].cleanFunc(this);
297 }
298 }
299 m_userData.SetLength(0);
300
301 // Clear engine pointer
302 if( m_holdEngineRef )
303 m_engine->Release();
304 m_engine = 0;
305 }
306
307 // interface
GetEngine() const308 asIScriptEngine *asCContext::GetEngine() const
309 {
310 return m_engine;
311 }
312
313 // interface
SetUserData(void * data,asPWORD type)314 void *asCContext::SetUserData(void *data, asPWORD type)
315 {
316 // As a thread might add a new new user data at the same time as another
317 // it is necessary to protect both read and write access to the userData member
318 ACQUIREEXCLUSIVE(m_engine->engineRWLock);
319
320 // It is not intended to store a lot of different types of userdata,
321 // so a more complex structure like a associative map would just have
322 // more overhead than a simple array.
323 for( asUINT n = 0; n < m_userData.GetLength(); n += 2 )
324 {
325 if( m_userData[n] == type )
326 {
327 void *oldData = reinterpret_cast<void*>(m_userData[n+1]);
328 m_userData[n+1] = reinterpret_cast<asPWORD>(data);
329
330 RELEASEEXCLUSIVE(m_engine->engineRWLock);
331
332 return oldData;
333 }
334 }
335
336 m_userData.PushLast(type);
337 m_userData.PushLast(reinterpret_cast<asPWORD>(data));
338
339 RELEASEEXCLUSIVE(m_engine->engineRWLock);
340
341 return 0;
342 }
343
344 // interface
GetUserData(asPWORD type) const345 void *asCContext::GetUserData(asPWORD type) const
346 {
347 // There may be multiple threads reading, but when
348 // setting the user data nobody must be reading.
349 ACQUIRESHARED(m_engine->engineRWLock);
350
351 for( asUINT n = 0; n < m_userData.GetLength(); n += 2 )
352 {
353 if( m_userData[n] == type )
354 {
355 RELEASESHARED(m_engine->engineRWLock);
356 return reinterpret_cast<void*>(m_userData[n+1]);
357 }
358 }
359
360 RELEASESHARED(m_engine->engineRWLock);
361
362 return 0;
363 }
364
365 // interface
GetSystemFunction()366 asIScriptFunction *asCContext::GetSystemFunction()
367 {
368 return m_callingSystemFunction;
369 }
370
371 // interface
Prepare(asIScriptFunction * func)372 int asCContext::Prepare(asIScriptFunction *func)
373 {
374 if( func == 0 )
375 {
376 asCString str;
377 str.Format(TXT_FAILED_IN_FUNC_s_WITH_s_d, "Prepare", "null", asNO_FUNCTION);
378 m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
379 return asNO_FUNCTION;
380 }
381
382 if( m_status == asEXECUTION_ACTIVE || m_status == asEXECUTION_SUSPENDED )
383 {
384 asCString str;
385 str.Format(TXT_FAILED_IN_FUNC_s_WITH_s_d, "Prepare", func->GetDeclaration(true, true), asCONTEXT_ACTIVE);
386 m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
387 return asCONTEXT_ACTIVE;
388 }
389
390 // Clean the stack if not done before
391 if( m_status != asEXECUTION_FINISHED && m_status != asEXECUTION_UNINITIALIZED )
392 CleanStack();
393
394 // Release the returned object (if any)
395 CleanReturnObject();
396
397 // Release the object if it is a script object
398 if( m_initialFunction && m_initialFunction->objectType && (m_initialFunction->objectType->flags & asOBJ_SCRIPT_OBJECT) )
399 {
400 asCScriptObject *obj = *(asCScriptObject**)&m_regs.stackFramePointer[0];
401 if( obj )
402 obj->Release();
403
404 *(asPWORD*)&m_regs.stackFramePointer[0] = 0;
405 }
406
407 if( m_initialFunction && m_initialFunction == func )
408 {
409 // If the same function is executed again, we can skip a lot of the setup
410 m_currentFunction = m_initialFunction;
411
412 // Reset stack pointer
413 m_regs.stackPointer = m_originalStackPointer;
414
415 // Make sure the stack pointer is pointing to the original position,
416 // otherwise something is wrong with the way it is being updated
417 asASSERT( IsNested() || m_stackIndex > 0 || (m_regs.stackPointer == m_stackBlocks[0] + m_stackBlockSize) );
418 }
419 else
420 {
421 asASSERT( m_engine );
422
423 // Make sure the function is from the same engine as the context to avoid mixups
424 if( m_engine != func->GetEngine() )
425 {
426 asCString str;
427 str.Format(TXT_FAILED_IN_FUNC_s_WITH_s_d, "Prepare", func->GetDeclaration(true, true), asINVALID_ARG);
428 m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
429 return asINVALID_ARG;
430 }
431
432 if( m_initialFunction )
433 {
434 m_initialFunction->Release();
435
436 // Reset stack pointer
437 m_regs.stackPointer = m_originalStackPointer;
438
439 // Make sure the stack pointer is pointing to the original position,
440 // otherwise something is wrong with the way it is being updated
441 asASSERT( IsNested() || m_stackIndex > 0 || (m_regs.stackPointer == m_stackBlocks[0] + m_stackBlockSize) );
442 }
443
444 // We trust the application not to pass anything else but a asCScriptFunction
445 m_initialFunction = reinterpret_cast<asCScriptFunction *>(func);
446 m_initialFunction->AddRef();
447 m_currentFunction = m_initialFunction;
448
449 // TODO: runtime optimize: GetSpaceNeededForArguments() should be precomputed
450 m_argumentsSize = m_currentFunction->GetSpaceNeededForArguments() + (m_currentFunction->objectType ? AS_PTR_SIZE : 0);
451
452 // Reserve space for the arguments and return value
453 if( m_currentFunction->DoesReturnOnStack() )
454 {
455 m_returnValueSize = m_currentFunction->returnType.GetSizeInMemoryDWords();
456 m_argumentsSize += AS_PTR_SIZE;
457 }
458 else
459 m_returnValueSize = 0;
460
461 // Determine the minimum stack size needed
462 int stackSize = m_argumentsSize + m_returnValueSize;
463 if( m_currentFunction->scriptData )
464 stackSize += m_currentFunction->scriptData->stackNeeded;
465
466 // Make sure there is enough space on the stack for the arguments and return value
467 if( !ReserveStackSpace(stackSize) )
468 return asOUT_OF_MEMORY;
469 }
470
471 // Reset state
472 // Most of the time the previous state will be asEXECUTION_FINISHED, in which case the values are already initialized
473 if( m_status != asEXECUTION_FINISHED )
474 {
475 m_exceptionLine = -1;
476 m_exceptionFunction = 0;
477 m_doAbort = false;
478 m_doSuspend = false;
479 m_regs.doProcessSuspend = m_lineCallback;
480 m_externalSuspendRequest = false;
481 }
482 m_status = asEXECUTION_PREPARED;
483 m_regs.programPointer = 0;
484
485 // Reserve space for the arguments and return value
486 m_regs.stackFramePointer = m_regs.stackPointer - m_argumentsSize - m_returnValueSize;
487 m_originalStackPointer = m_regs.stackPointer;
488 m_regs.stackPointer = m_regs.stackFramePointer;
489
490 // Set arguments to 0
491 memset(m_regs.stackPointer, 0, 4*m_argumentsSize);
492
493 if( m_returnValueSize )
494 {
495 // Set the address of the location where the return value should be put
496 asDWORD *ptr = m_regs.stackFramePointer;
497 if( m_currentFunction->objectType )
498 ptr += AS_PTR_SIZE;
499
500 *(void**)ptr = (void*)(m_regs.stackFramePointer + m_argumentsSize);
501 }
502
503 return asSUCCESS;
504 }
505
506 // Free all resources
Unprepare()507 int asCContext::Unprepare()
508 {
509 if( m_status == asEXECUTION_ACTIVE || m_status == asEXECUTION_SUSPENDED )
510 return asCONTEXT_ACTIVE;
511
512 // Set the context as active so that any clean up code can use access it if desired
513 asCThreadLocalData *tld = asPushActiveContext((asIScriptContext *)this);
514 asDWORD count = m_refCount.get();
515 UNUSED_VAR(count);
516
517 // Only clean the stack if the context was prepared but not executed until the end
518 if( m_status != asEXECUTION_UNINITIALIZED &&
519 m_status != asEXECUTION_FINISHED )
520 CleanStack();
521
522 asASSERT( m_needToCleanupArgs == false );
523
524 // Release the returned object (if any)
525 CleanReturnObject();
526
527 // TODO: Unprepare is called during destruction, so nobody
528 // must be allowed to keep an extra reference
529 asASSERT(m_refCount.get() == count);
530 asPopActiveContext(tld, this);
531
532 // Release the object if it is a script object
533 if( m_initialFunction && m_initialFunction->objectType && (m_initialFunction->objectType->flags & asOBJ_SCRIPT_OBJECT) )
534 {
535 asCScriptObject *obj = *(asCScriptObject**)&m_regs.stackFramePointer[0];
536 if( obj )
537 obj->Release();
538 }
539
540 // Release the initial function
541 if( m_initialFunction )
542 {
543 m_initialFunction->Release();
544
545 // Reset stack pointer
546 m_regs.stackPointer = m_originalStackPointer;
547
548 // Make sure the stack pointer is pointing to the original position,
549 // otherwise something is wrong with the way it is being updated
550 asASSERT( IsNested() || m_stackIndex > 0 || (m_regs.stackPointer == m_stackBlocks[0] + m_stackBlockSize) );
551 }
552
553 // Clear function pointers
554 m_initialFunction = 0;
555 m_currentFunction = 0;
556 m_exceptionFunction = 0;
557 m_regs.programPointer = 0;
558
559 // Reset status
560 m_status = asEXECUTION_UNINITIALIZED;
561
562 m_regs.stackFramePointer = 0;
563
564 return 0;
565 }
566
GetReturnByte()567 asBYTE asCContext::GetReturnByte()
568 {
569 if( m_status != asEXECUTION_FINISHED ) return 0;
570
571 asCDataType *dt = &m_initialFunction->returnType;
572
573 if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) return 0;
574
575 return *(asBYTE*)&m_regs.valueRegister;
576 }
577
GetReturnWord()578 asWORD asCContext::GetReturnWord()
579 {
580 if( m_status != asEXECUTION_FINISHED ) return 0;
581
582 asCDataType *dt = &m_initialFunction->returnType;
583
584 if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) return 0;
585
586 return *(asWORD*)&m_regs.valueRegister;
587 }
588
GetReturnDWord()589 asDWORD asCContext::GetReturnDWord()
590 {
591 if( m_status != asEXECUTION_FINISHED ) return 0;
592
593 asCDataType *dt = &m_initialFunction->returnType;
594
595 if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) return 0;
596
597 return *(asDWORD*)&m_regs.valueRegister;
598 }
599
GetReturnQWord()600 asQWORD asCContext::GetReturnQWord()
601 {
602 if( m_status != asEXECUTION_FINISHED ) return 0;
603
604 asCDataType *dt = &m_initialFunction->returnType;
605
606 if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) return 0;
607
608 return m_regs.valueRegister;
609 }
610
GetReturnFloat()611 float asCContext::GetReturnFloat()
612 {
613 if( m_status != asEXECUTION_FINISHED ) return 0;
614
615 asCDataType *dt = &m_initialFunction->returnType;
616
617 if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) return 0;
618
619 return *(float*)&m_regs.valueRegister;
620 }
621
GetReturnDouble()622 double asCContext::GetReturnDouble()
623 {
624 if( m_status != asEXECUTION_FINISHED ) return 0;
625
626 asCDataType *dt = &m_initialFunction->returnType;
627
628 if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() ) return 0;
629
630 return *(double*)&m_regs.valueRegister;
631 }
632
GetReturnAddress()633 void *asCContext::GetReturnAddress()
634 {
635 if( m_status != asEXECUTION_FINISHED ) return 0;
636
637 asCDataType *dt = &m_initialFunction->returnType;
638
639 if( dt->IsReference() )
640 return *(void**)&m_regs.valueRegister;
641 else if( dt->IsObject() || dt->IsFuncdef() )
642 {
643 if( m_initialFunction->DoesReturnOnStack() )
644 {
645 // The address of the return value was passed as the first argument, after the object pointer
646 int offset = 0;
647 if( m_initialFunction->objectType )
648 offset += AS_PTR_SIZE;
649
650 return *(void**)(&m_regs.stackFramePointer[offset]);
651 }
652
653 return m_regs.objectRegister;
654 }
655
656 return 0;
657 }
658
GetReturnObject()659 void *asCContext::GetReturnObject()
660 {
661 if( m_status != asEXECUTION_FINISHED ) return 0;
662
663 asCDataType *dt = &m_initialFunction->returnType;
664
665 if( !dt->IsObject() && !dt->IsFuncdef() ) return 0;
666
667 if( dt->IsReference() )
668 return *(void**)(asPWORD)m_regs.valueRegister;
669 else
670 {
671 if( m_initialFunction->DoesReturnOnStack() )
672 {
673 // The address of the return value was passed as the first argument, after the object pointer
674 int offset = 0;
675 if( m_initialFunction->objectType )
676 offset += AS_PTR_SIZE;
677
678 return *(void**)(&m_regs.stackFramePointer[offset]);
679 }
680
681 return m_regs.objectRegister;
682 }
683 }
684
GetAddressOfReturnValue()685 void *asCContext::GetAddressOfReturnValue()
686 {
687 if( m_status != asEXECUTION_FINISHED ) return 0;
688
689 asCDataType *dt = &m_initialFunction->returnType;
690
691 // An object is stored in the objectRegister
692 if( !dt->IsReference() && (dt->IsObject() || dt->IsFuncdef()) )
693 {
694 // Need to dereference objects
695 if( !dt->IsObjectHandle() )
696 {
697 if( m_initialFunction->DoesReturnOnStack() )
698 {
699 // The address of the return value was passed as the first argument, after the object pointer
700 int offset = 0;
701 if( m_initialFunction->objectType )
702 offset += AS_PTR_SIZE;
703
704 return *(void**)(&m_regs.stackFramePointer[offset]);
705 }
706
707 return *(void**)&m_regs.objectRegister;
708 }
709 return &m_regs.objectRegister;
710 }
711
712 // Primitives and references are stored in valueRegister
713 return &m_regs.valueRegister;
714 }
715
SetObject(void * obj)716 int asCContext::SetObject(void *obj)
717 {
718 if( m_status != asEXECUTION_PREPARED )
719 return asCONTEXT_NOT_PREPARED;
720
721 if( !m_initialFunction->objectType )
722 {
723 m_status = asEXECUTION_ERROR;
724 return asERROR;
725 }
726
727 asASSERT( *(asPWORD*)&m_regs.stackFramePointer[0] == 0 );
728
729 *(asPWORD*)&m_regs.stackFramePointer[0] = (asPWORD)obj;
730
731 // TODO: This should be optional by having a flag where the application can chose whether it should be done or not
732 // The flag could be named something like takeOwnership and have default value of true
733 if( obj && (m_initialFunction->objectType->flags & asOBJ_SCRIPT_OBJECT) )
734 reinterpret_cast<asCScriptObject*>(obj)->AddRef();
735
736 return 0;
737 }
738
SetArgByte(asUINT arg,asBYTE value)739 int asCContext::SetArgByte(asUINT arg, asBYTE value)
740 {
741 if( m_status != asEXECUTION_PREPARED )
742 return asCONTEXT_NOT_PREPARED;
743
744 if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() )
745 {
746 m_status = asEXECUTION_ERROR;
747 return asINVALID_ARG;
748 }
749
750 // Verify the type of the argument
751 asCDataType *dt = &m_initialFunction->parameterTypes[arg];
752 if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() )
753 {
754 m_status = asEXECUTION_ERROR;
755 return asINVALID_TYPE;
756 }
757
758 if( dt->GetSizeInMemoryBytes() != 1 )
759 {
760 m_status = asEXECUTION_ERROR;
761 return asINVALID_TYPE;
762 }
763
764 // Determine the position of the argument
765 int offset = 0;
766 if( m_initialFunction->objectType )
767 offset += AS_PTR_SIZE;
768
769 // If function returns object by value an extra pointer is pushed on the stack
770 if( m_returnValueSize )
771 offset += AS_PTR_SIZE;
772
773 for( asUINT n = 0; n < arg; n++ )
774 offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords();
775
776 // Set the value
777 *(asBYTE*)&m_regs.stackFramePointer[offset] = value;
778
779 return 0;
780 }
781
SetArgWord(asUINT arg,asWORD value)782 int asCContext::SetArgWord(asUINT arg, asWORD value)
783 {
784 if( m_status != asEXECUTION_PREPARED )
785 return asCONTEXT_NOT_PREPARED;
786
787 if( arg >= m_initialFunction->parameterTypes.GetLength() )
788 {
789 m_status = asEXECUTION_ERROR;
790 return asINVALID_ARG;
791 }
792
793 // Verify the type of the argument
794 asCDataType *dt = &m_initialFunction->parameterTypes[arg];
795 if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() )
796 {
797 m_status = asEXECUTION_ERROR;
798 return asINVALID_TYPE;
799 }
800
801 if( dt->GetSizeInMemoryBytes() != 2 )
802 {
803 m_status = asEXECUTION_ERROR;
804 return asINVALID_TYPE;
805 }
806
807 // Determine the position of the argument
808 int offset = 0;
809 if( m_initialFunction->objectType )
810 offset += AS_PTR_SIZE;
811
812 // If function returns object by value an extra pointer is pushed on the stack
813 if( m_returnValueSize )
814 offset += AS_PTR_SIZE;
815
816 for( asUINT n = 0; n < arg; n++ )
817 offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords();
818
819 // Set the value
820 *(asWORD*)&m_regs.stackFramePointer[offset] = value;
821
822 return 0;
823 }
824
SetArgDWord(asUINT arg,asDWORD value)825 int asCContext::SetArgDWord(asUINT arg, asDWORD value)
826 {
827 if( m_status != asEXECUTION_PREPARED )
828 return asCONTEXT_NOT_PREPARED;
829
830 if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() )
831 {
832 m_status = asEXECUTION_ERROR;
833 return asINVALID_ARG;
834 }
835
836 // Verify the type of the argument
837 asCDataType *dt = &m_initialFunction->parameterTypes[arg];
838 if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() )
839 {
840 m_status = asEXECUTION_ERROR;
841 return asINVALID_TYPE;
842 }
843
844 if( dt->GetSizeInMemoryBytes() != 4 )
845 {
846 m_status = asEXECUTION_ERROR;
847 return asINVALID_TYPE;
848 }
849
850 // Determine the position of the argument
851 int offset = 0;
852 if( m_initialFunction->objectType )
853 offset += AS_PTR_SIZE;
854
855 // If function returns object by value an extra pointer is pushed on the stack
856 if( m_returnValueSize )
857 offset += AS_PTR_SIZE;
858
859 for( asUINT n = 0; n < arg; n++ )
860 offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords();
861
862 // Set the value
863 *(asDWORD*)&m_regs.stackFramePointer[offset] = value;
864
865 return 0;
866 }
867
SetArgQWord(asUINT arg,asQWORD value)868 int asCContext::SetArgQWord(asUINT arg, asQWORD value)
869 {
870 if( m_status != asEXECUTION_PREPARED )
871 return asCONTEXT_NOT_PREPARED;
872
873 if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() )
874 {
875 m_status = asEXECUTION_ERROR;
876 return asINVALID_ARG;
877 }
878
879 // Verify the type of the argument
880 asCDataType *dt = &m_initialFunction->parameterTypes[arg];
881 if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() )
882 {
883 m_status = asEXECUTION_ERROR;
884 return asINVALID_TYPE;
885 }
886
887 if( dt->GetSizeOnStackDWords() != 2 )
888 {
889 m_status = asEXECUTION_ERROR;
890 return asINVALID_TYPE;
891 }
892
893 // Determine the position of the argument
894 int offset = 0;
895 if( m_initialFunction->objectType )
896 offset += AS_PTR_SIZE;
897
898 // If function returns object by value an extra pointer is pushed on the stack
899 if( m_returnValueSize )
900 offset += AS_PTR_SIZE;
901
902 for( asUINT n = 0; n < arg; n++ )
903 offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords();
904
905 // Set the value
906 *(asQWORD*)(&m_regs.stackFramePointer[offset]) = value;
907
908 return 0;
909 }
910
SetArgFloat(asUINT arg,float value)911 int asCContext::SetArgFloat(asUINT arg, float value)
912 {
913 if( m_status != asEXECUTION_PREPARED )
914 return asCONTEXT_NOT_PREPARED;
915
916 if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() )
917 {
918 m_status = asEXECUTION_ERROR;
919 return asINVALID_ARG;
920 }
921
922 // Verify the type of the argument
923 asCDataType *dt = &m_initialFunction->parameterTypes[arg];
924 if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() )
925 {
926 m_status = asEXECUTION_ERROR;
927 return asINVALID_TYPE;
928 }
929
930 if( dt->GetSizeOnStackDWords() != 1 )
931 {
932 m_status = asEXECUTION_ERROR;
933 return asINVALID_TYPE;
934 }
935
936 // Determine the position of the argument
937 int offset = 0;
938 if( m_initialFunction->objectType )
939 offset += AS_PTR_SIZE;
940
941 // If function returns object by value an extra pointer is pushed on the stack
942 if( m_returnValueSize )
943 offset += AS_PTR_SIZE;
944
945 for( asUINT n = 0; n < arg; n++ )
946 offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords();
947
948 // Set the value
949 *(float*)(&m_regs.stackFramePointer[offset]) = value;
950
951 return 0;
952 }
953
SetArgDouble(asUINT arg,double value)954 int asCContext::SetArgDouble(asUINT arg, double value)
955 {
956 if( m_status != asEXECUTION_PREPARED )
957 return asCONTEXT_NOT_PREPARED;
958
959 if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() )
960 {
961 m_status = asEXECUTION_ERROR;
962 return asINVALID_ARG;
963 }
964
965 // Verify the type of the argument
966 asCDataType *dt = &m_initialFunction->parameterTypes[arg];
967 if( dt->IsObject() || dt->IsFuncdef() || dt->IsReference() )
968 {
969 m_status = asEXECUTION_ERROR;
970 return asINVALID_TYPE;
971 }
972
973 if( dt->GetSizeOnStackDWords() != 2 )
974 {
975 m_status = asEXECUTION_ERROR;
976 return asINVALID_TYPE;
977 }
978
979 // Determine the position of the argument
980 int offset = 0;
981 if( m_initialFunction->objectType )
982 offset += AS_PTR_SIZE;
983
984 // If function returns object by value an extra pointer is pushed on the stack
985 if( m_returnValueSize )
986 offset += AS_PTR_SIZE;
987
988 for( asUINT n = 0; n < arg; n++ )
989 offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords();
990
991 // Set the value
992 *(double*)(&m_regs.stackFramePointer[offset]) = value;
993
994 return 0;
995 }
996
SetArgAddress(asUINT arg,void * value)997 int asCContext::SetArgAddress(asUINT arg, void *value)
998 {
999 if( m_status != asEXECUTION_PREPARED )
1000 return asCONTEXT_NOT_PREPARED;
1001
1002 if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() )
1003 {
1004 m_status = asEXECUTION_ERROR;
1005 return asINVALID_ARG;
1006 }
1007
1008 // Verify the type of the argument
1009 asCDataType *dt = &m_initialFunction->parameterTypes[arg];
1010 if( !dt->IsReference() && !dt->IsObjectHandle() )
1011 {
1012 m_status = asEXECUTION_ERROR;
1013 return asINVALID_TYPE;
1014 }
1015
1016 // Determine the position of the argument
1017 int offset = 0;
1018 if( m_initialFunction->objectType )
1019 offset += AS_PTR_SIZE;
1020
1021 // If function returns object by value an extra pointer is pushed on the stack
1022 if( m_returnValueSize )
1023 offset += AS_PTR_SIZE;
1024
1025 for( asUINT n = 0; n < arg; n++ )
1026 offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords();
1027
1028 // Set the value
1029 *(asPWORD*)(&m_regs.stackFramePointer[offset]) = (asPWORD)value;
1030
1031 return 0;
1032 }
1033
SetArgObject(asUINT arg,void * obj)1034 int asCContext::SetArgObject(asUINT arg, void *obj)
1035 {
1036 if( m_status != asEXECUTION_PREPARED )
1037 return asCONTEXT_NOT_PREPARED;
1038
1039 if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() )
1040 {
1041 m_status = asEXECUTION_ERROR;
1042 return asINVALID_ARG;
1043 }
1044
1045 // Verify the type of the argument
1046 asCDataType *dt = &m_initialFunction->parameterTypes[arg];
1047 if( !dt->IsObject() && !dt->IsFuncdef() )
1048 {
1049 m_status = asEXECUTION_ERROR;
1050 return asINVALID_TYPE;
1051 }
1052
1053 // If the object should be sent by value we must make a copy of it
1054 if( !dt->IsReference() )
1055 {
1056 if( dt->IsObjectHandle() )
1057 {
1058 // Increase the reference counter
1059 if (obj && dt->IsFuncdef())
1060 ((asIScriptFunction*)obj)->AddRef();
1061 else
1062 {
1063 asSTypeBehaviour *beh = &CastToObjectType(dt->GetTypeInfo())->beh;
1064 if (obj && beh->addref)
1065 m_engine->CallObjectMethod(obj, beh->addref);
1066 }
1067 }
1068 else
1069 {
1070 obj = m_engine->CreateScriptObjectCopy(obj, dt->GetTypeInfo());
1071 }
1072 }
1073
1074 // Determine the position of the argument
1075 int offset = 0;
1076 if( m_initialFunction->objectType )
1077 offset += AS_PTR_SIZE;
1078
1079 // If function returns object by value an extra pointer is pushed on the stack
1080 if( m_returnValueSize )
1081 offset += AS_PTR_SIZE;
1082
1083 for( asUINT n = 0; n < arg; n++ )
1084 offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords();
1085
1086 // Set the value
1087 *(asPWORD*)(&m_regs.stackFramePointer[offset]) = (asPWORD)obj;
1088
1089 return 0;
1090 }
1091
SetArgVarType(asUINT arg,void * ptr,int typeId)1092 int asCContext::SetArgVarType(asUINT arg, void *ptr, int typeId)
1093 {
1094 if( m_status != asEXECUTION_PREPARED )
1095 return asCONTEXT_NOT_PREPARED;
1096
1097 if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() )
1098 {
1099 m_status = asEXECUTION_ERROR;
1100 return asINVALID_ARG;
1101 }
1102
1103 // Verify the type of the argument
1104 asCDataType *dt = &m_initialFunction->parameterTypes[arg];
1105 if( dt->GetTokenType() != ttQuestion )
1106 {
1107 m_status = asEXECUTION_ERROR;
1108 return asINVALID_TYPE;
1109 }
1110
1111 // Determine the position of the argument
1112 int offset = 0;
1113 if( m_initialFunction->objectType )
1114 offset += AS_PTR_SIZE;
1115
1116 // If function returns object by value an extra pointer is pushed on the stack
1117 if( m_returnValueSize )
1118 offset += AS_PTR_SIZE;
1119
1120 for( asUINT n = 0; n < arg; n++ )
1121 offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords();
1122
1123 // Set the typeId and pointer
1124 *(asPWORD*)(&m_regs.stackFramePointer[offset]) = (asPWORD)ptr;
1125 offset += AS_PTR_SIZE;
1126 *(int*)(&m_regs.stackFramePointer[offset]) = typeId;
1127
1128 return 0;
1129 }
1130
1131 // TODO: Instead of GetAddressOfArg, maybe we need a SetArgValue(int arg, void *value, bool takeOwnership) instead.
1132
1133 // interface
GetAddressOfArg(asUINT arg)1134 void *asCContext::GetAddressOfArg(asUINT arg)
1135 {
1136 if( m_status != asEXECUTION_PREPARED )
1137 return 0;
1138
1139 if( arg >= (unsigned)m_initialFunction->parameterTypes.GetLength() )
1140 return 0;
1141
1142 // Determine the position of the argument
1143 int offset = 0;
1144 if( m_initialFunction->objectType )
1145 offset += AS_PTR_SIZE;
1146
1147 // If function returns object by value an extra pointer is pushed on the stack
1148 if( m_returnValueSize )
1149 offset += AS_PTR_SIZE;
1150
1151 for( asUINT n = 0; n < arg; n++ )
1152 offset += m_initialFunction->parameterTypes[n].GetSizeOnStackDWords();
1153
1154 // We should return the address of the location where the argument value will be placed
1155
1156 // All registered types are always sent by reference, even if
1157 // the function is declared to receive the argument by value.
1158 return &m_regs.stackFramePointer[offset];
1159 }
1160
1161
Abort()1162 int asCContext::Abort()
1163 {
1164 if( m_engine == 0 ) return asERROR;
1165
1166 // TODO: multithread: Make thread safe. There is a chance that the status
1167 // changes to something else after being set to ABORTED here.
1168 if( m_status == asEXECUTION_SUSPENDED )
1169 m_status = asEXECUTION_ABORTED;
1170
1171 m_doSuspend = true;
1172 m_regs.doProcessSuspend = true;
1173 m_externalSuspendRequest = true;
1174 m_doAbort = true;
1175
1176 return 0;
1177 }
1178
1179 // interface
Suspend()1180 int asCContext::Suspend()
1181 {
1182 // This function just sets some internal flags and is safe
1183 // to call from a secondary thread, even if the library has
1184 // been built without multi-thread support.
1185
1186 if( m_engine == 0 ) return asERROR;
1187
1188 m_doSuspend = true;
1189 m_externalSuspendRequest = true;
1190 m_regs.doProcessSuspend = true;
1191
1192 return 0;
1193 }
1194
1195 // interface
Execute()1196 int asCContext::Execute()
1197 {
1198 asASSERT( m_engine != 0 );
1199
1200 if( m_status != asEXECUTION_SUSPENDED && m_status != asEXECUTION_PREPARED )
1201 {
1202 asCString str;
1203 str.Format(TXT_FAILED_IN_FUNC_s_d, "Execute", asCONTEXT_NOT_PREPARED);
1204 m_engine->WriteMessage("", 0, 0, asMSGTYPE_ERROR, str.AddressOf());
1205 return asCONTEXT_NOT_PREPARED;
1206 }
1207
1208 m_status = asEXECUTION_ACTIVE;
1209
1210 asCThreadLocalData *tld = asPushActiveContext((asIScriptContext *)this);
1211
1212 // Make sure there are not too many nested calls, as it could crash the application
1213 // by filling up the thread call stack
1214 if (tld->activeContexts.GetLength() > m_engine->ep.maxNestedCalls)
1215 SetInternalException(TXT_TOO_MANY_NESTED_CALLS);
1216 else if( m_regs.programPointer == 0 )
1217 {
1218 if( m_currentFunction->funcType == asFUNC_DELEGATE )
1219 {
1220 // Push the object pointer onto the stack
1221 asASSERT( m_regs.stackPointer - AS_PTR_SIZE >= m_stackBlocks[m_stackIndex] );
1222 m_regs.stackPointer -= AS_PTR_SIZE;
1223 m_regs.stackFramePointer -= AS_PTR_SIZE;
1224 *(asPWORD*)m_regs.stackPointer = asPWORD(m_currentFunction->objForDelegate);
1225
1226 // Make the call to the delegated object method
1227 m_currentFunction = m_currentFunction->funcForDelegate;
1228 }
1229
1230 if( m_currentFunction->funcType == asFUNC_VIRTUAL ||
1231 m_currentFunction->funcType == asFUNC_INTERFACE )
1232 {
1233 // The currentFunction is a virtual method
1234
1235 // Determine the true function from the object
1236 asCScriptObject *obj = *(asCScriptObject**)(asPWORD*)m_regs.stackFramePointer;
1237 if( obj == 0 )
1238 {
1239 SetInternalException(TXT_NULL_POINTER_ACCESS);
1240 }
1241 else
1242 {
1243 asCObjectType *objType = obj->objType;
1244 asCScriptFunction *realFunc = 0;
1245
1246 if( m_currentFunction->funcType == asFUNC_VIRTUAL )
1247 {
1248 if( objType->virtualFunctionTable.GetLength() > (asUINT)m_currentFunction->vfTableIdx )
1249 {
1250 realFunc = objType->virtualFunctionTable[m_currentFunction->vfTableIdx];
1251 }
1252 }
1253 else
1254 {
1255 // Search the object type for a function that matches the interface function
1256 for( asUINT n = 0; n < objType->methods.GetLength(); n++ )
1257 {
1258 asCScriptFunction *f2 = m_engine->scriptFunctions[objType->methods[n]];
1259 if( f2->signatureId == m_currentFunction->signatureId )
1260 {
1261 if( f2->funcType == asFUNC_VIRTUAL )
1262 realFunc = objType->virtualFunctionTable[f2->vfTableIdx];
1263 else
1264 realFunc = f2;
1265 break;
1266 }
1267 }
1268 }
1269
1270 if( realFunc && realFunc->signatureId == m_currentFunction->signatureId )
1271 m_currentFunction = realFunc;
1272 else
1273 SetInternalException(TXT_NULL_POINTER_ACCESS);
1274 }
1275 }
1276 else if( m_currentFunction->funcType == asFUNC_IMPORTED )
1277 {
1278 int funcId = m_engine->importedFunctions[m_currentFunction->id & ~FUNC_IMPORTED]->boundFunctionId;
1279 if( funcId > 0 )
1280 m_currentFunction = m_engine->scriptFunctions[funcId];
1281 else
1282 SetInternalException(TXT_UNBOUND_FUNCTION);
1283 }
1284
1285 if( m_currentFunction->funcType == asFUNC_SCRIPT )
1286 {
1287 m_regs.programPointer = m_currentFunction->scriptData->byteCode.AddressOf();
1288
1289 // Set up the internal registers for executing the script function
1290 PrepareScriptFunction();
1291 }
1292 else if( m_currentFunction->funcType == asFUNC_SYSTEM )
1293 {
1294 // The current function is an application registered function
1295
1296 // Call the function directly
1297 CallSystemFunction(m_currentFunction->id, this);
1298
1299 // Was the call successful?
1300 if( m_status == asEXECUTION_ACTIVE )
1301 {
1302 m_status = asEXECUTION_FINISHED;
1303 }
1304 }
1305 else
1306 {
1307 // This shouldn't happen unless there was an error in which
1308 // case an exception should have been raised already
1309 asASSERT( m_status == asEXECUTION_EXCEPTION );
1310 }
1311 }
1312
1313 asUINT gcPreObjects = 0;
1314 if( m_engine->ep.autoGarbageCollect )
1315 m_engine->gc.GetStatistics(&gcPreObjects, 0, 0, 0, 0);
1316
1317 while( m_status == asEXECUTION_ACTIVE )
1318 ExecuteNext();
1319
1320 if( m_lineCallback )
1321 {
1322 // Call the line callback one last time before leaving
1323 // so anyone listening can catch the state change
1324 CallLineCallback();
1325 m_regs.doProcessSuspend = true;
1326 }
1327 else
1328 m_regs.doProcessSuspend = false;
1329
1330 m_doSuspend = false;
1331
1332 if( m_engine->ep.autoGarbageCollect )
1333 {
1334 asUINT gcPosObjects = 0;
1335 m_engine->gc.GetStatistics(&gcPosObjects, 0, 0, 0, 0);
1336 if( gcPosObjects > gcPreObjects )
1337 {
1338 // Execute as many steps as there were new objects created
1339 m_engine->GarbageCollect(asGC_ONE_STEP | asGC_DESTROY_GARBAGE | asGC_DETECT_GARBAGE, gcPosObjects - gcPreObjects);
1340 }
1341 else if( gcPosObjects > 0 )
1342 {
1343 // Execute at least one step, even if no new objects were created
1344 m_engine->GarbageCollect(asGC_ONE_STEP | asGC_DESTROY_GARBAGE | asGC_DETECT_GARBAGE, 1);
1345 }
1346 }
1347
1348 // Pop the active context
1349 asPopActiveContext(tld, this);
1350
1351 if( m_status == asEXECUTION_FINISHED )
1352 {
1353 m_regs.objectType = m_initialFunction->returnType.GetTypeInfo();
1354 return asEXECUTION_FINISHED;
1355 }
1356
1357 if( m_doAbort )
1358 {
1359 m_doAbort = false;
1360
1361 m_status = asEXECUTION_ABORTED;
1362 return asEXECUTION_ABORTED;
1363 }
1364
1365 if( m_status == asEXECUTION_SUSPENDED )
1366 return asEXECUTION_SUSPENDED;
1367
1368 if( m_status == asEXECUTION_EXCEPTION )
1369 return asEXECUTION_EXCEPTION;
1370
1371 return asERROR;
1372 }
1373
PushState()1374 int asCContext::PushState()
1375 {
1376 // Only allow the state to be pushed when active
1377 // TODO: Can we support a suspended state too? So the reuse of
1378 // the context can be done outside the Execute() call?
1379 if( m_status != asEXECUTION_ACTIVE )
1380 {
1381 // TODO: Write message. Wrong usage
1382 return asERROR;
1383 }
1384
1385 // Push the current script function that is calling the system function
1386 PushCallState();
1387
1388 // Push the system function too, which will serve both as a marker and
1389 // informing which system function that created the nested call
1390 if( m_callStack.GetLength() == m_callStack.GetCapacity() )
1391 {
1392 // Allocate space for 10 call states at a time to save time
1393 m_callStack.AllocateNoConstruct(m_callStack.GetLength() + 10*CALLSTACK_FRAME_SIZE, true);
1394 }
1395 m_callStack.SetLengthNoConstruct(m_callStack.GetLength() + CALLSTACK_FRAME_SIZE);
1396
1397 // Need to push m_initialFunction as it must be restored later
1398 asPWORD *tmp = m_callStack.AddressOf() + m_callStack.GetLength() - CALLSTACK_FRAME_SIZE;
1399 tmp[0] = 0;
1400 tmp[1] = (asPWORD)m_callingSystemFunction;
1401 tmp[2] = (asPWORD)m_initialFunction;
1402 tmp[3] = (asPWORD)m_originalStackPointer;
1403 tmp[4] = (asPWORD)m_argumentsSize;
1404
1405 // Need to push the value of registers so they can be restored
1406 tmp[5] = (asPWORD)asDWORD(m_regs.valueRegister);
1407 tmp[6] = (asPWORD)asDWORD(m_regs.valueRegister>>32);
1408 tmp[7] = (asPWORD)m_regs.objectRegister;
1409 tmp[8] = (asPWORD)m_regs.objectType;
1410
1411 // Decrease stackpointer to prevent the top value from being overwritten
1412 m_regs.stackPointer -= 2;
1413
1414 // Clear the initial function so that Prepare() knows it must do all validations
1415 m_initialFunction = 0;
1416
1417 // After this the state should appear as if uninitialized
1418 m_callingSystemFunction = 0;
1419
1420 m_regs.objectRegister = 0;
1421 m_regs.objectType = 0;
1422
1423 // Set the status to uninitialized as application
1424 // should call Prepare() after this to reuse the context
1425 m_status = asEXECUTION_UNINITIALIZED;
1426
1427 return asSUCCESS;
1428 }
1429
PopState()1430 int asCContext::PopState()
1431 {
1432 if( !IsNested() )
1433 return asERROR;
1434
1435 // Clean up the current execution
1436 Unprepare();
1437
1438 // The topmost state must be a marker for nested call
1439 asASSERT( m_callStack[m_callStack.GetLength() - CALLSTACK_FRAME_SIZE] == 0 );
1440
1441 // Restore the previous state
1442 asPWORD *tmp = &m_callStack[m_callStack.GetLength() - CALLSTACK_FRAME_SIZE];
1443 m_callingSystemFunction = reinterpret_cast<asCScriptFunction*>(tmp[1]);
1444 m_callStack.SetLength(m_callStack.GetLength() - CALLSTACK_FRAME_SIZE);
1445
1446 // Restore the previous initial function and the associated values
1447 m_initialFunction = reinterpret_cast<asCScriptFunction*>(tmp[2]);
1448 m_originalStackPointer = (asDWORD*)tmp[3];
1449 m_argumentsSize = (int)tmp[4];
1450
1451 m_regs.valueRegister = asQWORD(asDWORD(tmp[5]));
1452 m_regs.valueRegister |= asQWORD(tmp[6])<<32;
1453 m_regs.objectRegister = (void*)tmp[7];
1454 m_regs.objectType = (asITypeInfo*)tmp[8];
1455
1456 // Calculate the returnValueSize
1457 if( m_initialFunction->DoesReturnOnStack() )
1458 m_returnValueSize = m_initialFunction->returnType.GetSizeInMemoryDWords();
1459 else
1460 m_returnValueSize = 0;
1461
1462 // Pop the current script function. This will also restore the previous stack pointer
1463 PopCallState();
1464
1465 m_status = asEXECUTION_ACTIVE;
1466
1467 return asSUCCESS;
1468 }
1469
PushCallState()1470 void asCContext::PushCallState()
1471 {
1472 if( m_callStack.GetLength() == m_callStack.GetCapacity() )
1473 {
1474 // Allocate space for 10 call states at a time to save time
1475 m_callStack.AllocateNoConstruct(m_callStack.GetLength() + 10*CALLSTACK_FRAME_SIZE, true);
1476 }
1477 m_callStack.SetLengthNoConstruct(m_callStack.GetLength() + CALLSTACK_FRAME_SIZE);
1478
1479 // Separating the loads and stores limits data cache trash, and with a smart compiler
1480 // could turn into SIMD style loading/storing if available.
1481 // The compiler can't do this itself due to potential pointer aliasing between the pointers,
1482 // ie writing to tmp could overwrite the data contained in registers.stackFramePointer for example
1483 // for all the compiler knows. So introducing the local variable s, which is never referred to by
1484 // its address we avoid this issue.
1485
1486 asPWORD s[5];
1487 s[0] = (asPWORD)m_regs.stackFramePointer;
1488 s[1] = (asPWORD)m_currentFunction;
1489 s[2] = (asPWORD)m_regs.programPointer;
1490 s[3] = (asPWORD)m_regs.stackPointer;
1491 s[4] = m_stackIndex;
1492
1493 asPWORD *tmp = m_callStack.AddressOf() + m_callStack.GetLength() - CALLSTACK_FRAME_SIZE;
1494 tmp[0] = s[0];
1495 tmp[1] = s[1];
1496 tmp[2] = s[2];
1497 tmp[3] = s[3];
1498 tmp[4] = s[4];
1499 }
1500
PopCallState()1501 void asCContext::PopCallState()
1502 {
1503 // See comments in PushCallState about pointer aliasing and data cache trashing
1504 asPWORD *tmp = m_callStack.AddressOf() + m_callStack.GetLength() - CALLSTACK_FRAME_SIZE;
1505 asPWORD s[5];
1506 s[0] = tmp[0];
1507 s[1] = tmp[1];
1508 s[2] = tmp[2];
1509 s[3] = tmp[3];
1510 s[4] = tmp[4];
1511
1512 m_regs.stackFramePointer = (asDWORD*)s[0];
1513 m_currentFunction = (asCScriptFunction*)s[1];
1514 m_regs.programPointer = (asDWORD*)s[2];
1515 m_regs.stackPointer = (asDWORD*)s[3];
1516 m_stackIndex = (int)s[4];
1517
1518 m_callStack.SetLength(m_callStack.GetLength() - CALLSTACK_FRAME_SIZE);
1519 }
1520
1521 // interface
GetCallstackSize() const1522 asUINT asCContext::GetCallstackSize() const
1523 {
1524 if( m_currentFunction == 0 ) return 0;
1525
1526 // The current function is accessed at stackLevel 0
1527 return asUINT(1 + m_callStack.GetLength() / CALLSTACK_FRAME_SIZE);
1528 }
1529
1530 // interface
GetFunction(asUINT stackLevel)1531 asIScriptFunction *asCContext::GetFunction(asUINT stackLevel)
1532 {
1533 if( stackLevel >= GetCallstackSize() ) return 0;
1534
1535 if( stackLevel == 0 ) return m_currentFunction;
1536
1537 asPWORD *s = m_callStack.AddressOf() + (GetCallstackSize() - stackLevel - 1)*CALLSTACK_FRAME_SIZE;
1538 asCScriptFunction *func = (asCScriptFunction*)s[1];
1539
1540 return func;
1541 }
1542
1543 // interface
GetLineNumber(asUINT stackLevel,int * column,const char ** sectionName)1544 int asCContext::GetLineNumber(asUINT stackLevel, int *column, const char **sectionName)
1545 {
1546 if( stackLevel >= GetCallstackSize() ) return asINVALID_ARG;
1547
1548 asCScriptFunction *func;
1549 asDWORD *bytePos;
1550 if( stackLevel == 0 )
1551 {
1552 func = m_currentFunction;
1553 if( func->scriptData == 0 ) return 0;
1554 bytePos = m_regs.programPointer;
1555 }
1556 else
1557 {
1558 asPWORD *s = m_callStack.AddressOf() + (GetCallstackSize()-stackLevel-1)*CALLSTACK_FRAME_SIZE;
1559 func = (asCScriptFunction*)s[1];
1560 if( func->scriptData == 0 ) return 0;
1561 bytePos = (asDWORD*)s[2];
1562
1563 // Subract 1 from the bytePos, because we want the line where
1564 // the call was made, and not the instruction after the call
1565 bytePos -= 1;
1566 }
1567
1568 // For nested calls it is possible that func is null
1569 if( func == 0 )
1570 {
1571 if( column ) *column = 0;
1572 if( sectionName ) *sectionName = 0;
1573 return 0;
1574 }
1575
1576 int sectionIdx;
1577 asDWORD line = func->GetLineNumber(int(bytePos - func->scriptData->byteCode.AddressOf()), §ionIdx);
1578 if( column ) *column = (line >> 20);
1579 if( sectionName )
1580 {
1581 asASSERT( sectionIdx < int(m_engine->scriptSectionNames.GetLength()) );
1582 if( sectionIdx >= 0 && asUINT(sectionIdx) < m_engine->scriptSectionNames.GetLength() )
1583 *sectionName = m_engine->scriptSectionNames[sectionIdx]->AddressOf();
1584 else
1585 *sectionName = 0;
1586 }
1587 return (line & 0xFFFFF);
1588 }
1589
1590 // internal
ReserveStackSpace(asUINT size)1591 bool asCContext::ReserveStackSpace(asUINT size)
1592 {
1593 #ifdef WIP_16BYTE_ALIGN
1594 // Pad size to a multiple of MAX_TYPE_ALIGNMENT.
1595 const asUINT remainder = size % MAX_TYPE_ALIGNMENT;
1596 if(remainder != 0)
1597 {
1598 size = size + (MAX_TYPE_ALIGNMENT - (size % MAX_TYPE_ALIGNMENT));
1599 }
1600 #endif
1601
1602 // Make sure the first stack block is allocated
1603 if( m_stackBlocks.GetLength() == 0 )
1604 {
1605 m_stackBlockSize = m_engine->initialContextStackSize;
1606 asASSERT( m_stackBlockSize > 0 );
1607
1608 #ifndef WIP_16BYTE_ALIGN
1609 asDWORD *stack = asNEWARRAY(asDWORD,m_stackBlockSize);
1610 #else
1611 asDWORD *stack = asNEWARRAYALIGNED(asDWORD, m_stackBlockSize, MAX_TYPE_ALIGNMENT);
1612 #endif
1613 if( stack == 0 )
1614 {
1615 // Out of memory
1616 return false;
1617 }
1618
1619 #ifdef WIP_16BYTE_ALIGN
1620 asASSERT( isAligned(stack, MAX_TYPE_ALIGNMENT) );
1621 #endif
1622
1623 m_stackBlocks.PushLast(stack);
1624 m_stackIndex = 0;
1625 m_regs.stackPointer = m_stackBlocks[0] + m_stackBlockSize;
1626
1627 #ifdef WIP_16BYTE_ALIGN
1628 // Align the stack pointer. This is necessary as the m_stackBlockSize is not necessarily evenly divisable with the max alignment
1629 ((asPWORD&)m_regs.stackPointer) &= ~(MAX_TYPE_ALIGNMENT-1);
1630
1631 asASSERT( isAligned(m_regs.stackPointer, MAX_TYPE_ALIGNMENT) );
1632 #endif
1633 }
1634
1635 // Check if there is enough space on the current stack block, otherwise move
1636 // to the next one. New and larger blocks will be allocated as necessary
1637 while( m_regs.stackPointer - (size + RESERVE_STACK) < m_stackBlocks[m_stackIndex] )
1638 {
1639 // Make sure we don't allocate more space than allowed
1640 if( m_engine->ep.maximumContextStackSize )
1641 {
1642 // This test will only stop growth once it has already crossed the limit
1643 if( m_stackBlockSize * ((1 << (m_stackIndex+1)) - 1) > m_engine->ep.maximumContextStackSize )
1644 {
1645 m_isStackMemoryNotAllocated = true;
1646
1647 // Set the stackFramePointer, even though the stackPointer wasn't updated
1648 m_regs.stackFramePointer = m_regs.stackPointer;
1649
1650 SetInternalException(TXT_STACK_OVERFLOW);
1651 return false;
1652 }
1653 }
1654
1655 m_stackIndex++;
1656 if( m_stackBlocks.GetLength() == m_stackIndex )
1657 {
1658 // Allocate the new stack block, with twice the size of the previous
1659 #ifndef WIP_16BYTE_ALIGN
1660 asDWORD *stack = asNEWARRAY(asDWORD, (m_stackBlockSize << m_stackIndex));
1661 #else
1662 asDWORD *stack = asNEWARRAYALIGNED(asDWORD, (m_stackBlockSize << m_stackIndex), MAX_TYPE_ALIGNMENT);
1663 #endif
1664 if( stack == 0 )
1665 {
1666 // Out of memory
1667 m_isStackMemoryNotAllocated = true;
1668
1669 // Set the stackFramePointer, even though the stackPointer wasn't updated
1670 m_regs.stackFramePointer = m_regs.stackPointer;
1671
1672 SetInternalException(TXT_STACK_OVERFLOW);
1673 return false;
1674 }
1675
1676 #ifdef WIP_16BYTE_ALIGN
1677 asASSERT( isAligned(stack, MAX_TYPE_ALIGNMENT) );
1678 #endif
1679
1680 m_stackBlocks.PushLast(stack);
1681 }
1682
1683 // Update the stack pointer to point to the new block.
1684 // Leave enough room above the stackpointer to copy the arguments from the previous stackblock
1685 m_regs.stackPointer = m_stackBlocks[m_stackIndex] +
1686 (m_stackBlockSize<<m_stackIndex) -
1687 m_currentFunction->GetSpaceNeededForArguments() -
1688 (m_currentFunction->objectType ? AS_PTR_SIZE : 0) -
1689 (m_currentFunction->DoesReturnOnStack() ? AS_PTR_SIZE : 0);
1690
1691 #ifdef WIP_16BYTE_ALIGN
1692 // Align the stack pointer
1693 (asPWORD&)m_regs.stackPointer &= ~(MAX_TYPE_ALIGNMENT-1);
1694
1695 asASSERT( isAligned(m_regs.stackPointer, MAX_TYPE_ALIGNMENT) );
1696 #endif
1697 }
1698
1699 return true;
1700 }
1701
1702 // internal
CallScriptFunction(asCScriptFunction * func)1703 void asCContext::CallScriptFunction(asCScriptFunction *func)
1704 {
1705 asASSERT( func->scriptData );
1706
1707 // Push the framepointer, function id and programCounter on the stack
1708 PushCallState();
1709
1710 // Update the current function and program position before increasing the stack
1711 // so the exception handler will know what to do if there is a stack overflow
1712 m_currentFunction = func;
1713 m_regs.programPointer = m_currentFunction->scriptData->byteCode.AddressOf();
1714
1715 PrepareScriptFunction();
1716 }
1717
PrepareScriptFunction()1718 void asCContext::PrepareScriptFunction()
1719 {
1720 asASSERT( m_currentFunction->scriptData );
1721
1722 // Make sure there is space on the stack to execute the function
1723 asDWORD *oldStackPointer = m_regs.stackPointer;
1724 if( !ReserveStackSpace(m_currentFunction->scriptData->stackNeeded) )
1725 return;
1726
1727 // If a new stack block was allocated then we'll need to move
1728 // over the function arguments to the new block.
1729 if( m_regs.stackPointer != oldStackPointer )
1730 {
1731 int numDwords = m_currentFunction->GetSpaceNeededForArguments() +
1732 (m_currentFunction->objectType ? AS_PTR_SIZE : 0) +
1733 (m_currentFunction->DoesReturnOnStack() ? AS_PTR_SIZE : 0);
1734 memcpy(m_regs.stackPointer, oldStackPointer, sizeof(asDWORD)*numDwords);
1735 }
1736
1737 // Update framepointer
1738 m_regs.stackFramePointer = m_regs.stackPointer;
1739
1740 // Set all object variables to 0 to guarantee that they are null before they are used
1741 // Only variables on the heap should be cleared. The rest will be cleared by calling the constructor
1742 asUINT n = m_currentFunction->scriptData->objVariablesOnHeap;
1743 while( n-- > 0 )
1744 {
1745 int pos = m_currentFunction->scriptData->objVariablePos[n];
1746 *(asPWORD*)&m_regs.stackFramePointer[-pos] = 0;
1747 }
1748
1749 // Initialize the stack pointer with the space needed for local variables
1750 m_regs.stackPointer -= m_currentFunction->scriptData->variableSpace;
1751
1752 // Call the line callback for each script function, to guarantee that infinitely recursive scripts can
1753 // be interrupted, even if the scripts have been compiled with asEP_BUILD_WITHOUT_LINE_CUES
1754 if( m_regs.doProcessSuspend )
1755 {
1756 if( m_lineCallback )
1757 CallLineCallback();
1758 if( m_doSuspend )
1759 m_status = asEXECUTION_SUSPENDED;
1760 }
1761 }
1762
CallInterfaceMethod(asCScriptFunction * func)1763 void asCContext::CallInterfaceMethod(asCScriptFunction *func)
1764 {
1765 // Resolve the interface method using the current script type
1766 asCScriptObject *obj = *(asCScriptObject**)(asPWORD*)m_regs.stackPointer;
1767 if( obj == 0 )
1768 {
1769 // Tell the exception handler to clean up the arguments to this method
1770 m_needToCleanupArgs = true;
1771 SetInternalException(TXT_NULL_POINTER_ACCESS);
1772 return;
1773 }
1774
1775 asCObjectType *objType = obj->objType;
1776
1777 // Search the object type for a function that matches the interface function
1778 asCScriptFunction *realFunc = 0;
1779 if( func->funcType == asFUNC_INTERFACE )
1780 {
1781 // Find the offset for the interface's virtual function table chunk
1782 asUINT offset = 0;
1783 bool found = false;
1784 asCObjectType *findInterface = func->objectType;
1785
1786 // TODO: runtime optimize: The list of interfaces should be ordered by the address
1787 // Then a binary search pattern can be used.
1788 asUINT intfCount = asUINT(objType->interfaces.GetLength());
1789 for( asUINT n = 0; n < intfCount; n++ )
1790 {
1791 if( objType->interfaces[n] == findInterface )
1792 {
1793 offset = objType->interfaceVFTOffsets[n];
1794 found = true;
1795 break;
1796 }
1797 }
1798
1799 if( !found )
1800 {
1801 // Tell the exception handler to clean up the arguments to this method
1802 m_needToCleanupArgs = true;
1803 SetInternalException(TXT_NULL_POINTER_ACCESS);
1804 return;
1805 }
1806
1807 // Find the real function in the virtual table chunk with the found offset
1808 realFunc = objType->virtualFunctionTable[func->vfTableIdx + offset];
1809
1810 // Since the interface was implemented by the class, it shouldn't
1811 // be possible that the real function isn't found
1812 asASSERT( realFunc );
1813
1814 asASSERT( realFunc->signatureId == func->signatureId );
1815 }
1816 else // if( func->funcType == asFUNC_VIRTUAL )
1817 {
1818 realFunc = objType->virtualFunctionTable[func->vfTableIdx];
1819 }
1820
1821 // Then call the true script function
1822 CallScriptFunction(realFunc);
1823 }
1824
ExecuteNext()1825 void asCContext::ExecuteNext()
1826 {
1827 asDWORD *l_bc = m_regs.programPointer;
1828 asDWORD *l_sp = m_regs.stackPointer;
1829 asDWORD *l_fp = m_regs.stackFramePointer;
1830
1831 for(;;)
1832 {
1833
1834 #ifdef AS_DEBUG
1835 // Gather statistics on executed bytecode
1836 stats.Instr(*(asBYTE*)l_bc);
1837
1838 // Used to verify that the size of the instructions are correct
1839 asDWORD *old = l_bc;
1840 #endif
1841
1842
1843 // Remember to keep the cases in order and without
1844 // gaps, because that will make the switch faster.
1845 // It will be faster since only one lookup will be
1846 // made to find the correct jump destination. If not
1847 // in order, the switch will make two lookups.
1848 switch( *(asBYTE*)l_bc )
1849 {
1850 //--------------
1851 // memory access functions
1852
1853 case asBC_PopPtr:
1854 // Pop a pointer from the stack
1855 l_sp += AS_PTR_SIZE;
1856 l_bc++;
1857 break;
1858
1859 case asBC_PshGPtr:
1860 // Replaces PGA + RDSPtr
1861 l_sp -= AS_PTR_SIZE;
1862 *(asPWORD*)l_sp = *(asPWORD*)asBC_PTRARG(l_bc);
1863 l_bc += 1 + AS_PTR_SIZE;
1864 break;
1865
1866 // Push a dword value on the stack
1867 case asBC_PshC4:
1868 --l_sp;
1869 *l_sp = asBC_DWORDARG(l_bc);
1870 l_bc += 2;
1871 break;
1872
1873 // Push the dword value of a variable on the stack
1874 case asBC_PshV4:
1875 --l_sp;
1876 *l_sp = *(l_fp - asBC_SWORDARG0(l_bc));
1877 l_bc++;
1878 break;
1879
1880 // Push the address of a variable on the stack
1881 case asBC_PSF:
1882 l_sp -= AS_PTR_SIZE;
1883 *(asPWORD*)l_sp = asPWORD(l_fp - asBC_SWORDARG0(l_bc));
1884 l_bc++;
1885 break;
1886
1887 // Swap the top 2 pointers on the stack
1888 case asBC_SwapPtr:
1889 {
1890 asPWORD p = *(asPWORD*)l_sp;
1891 *(asPWORD*)l_sp = *(asPWORD*)(l_sp+AS_PTR_SIZE);
1892 *(asPWORD*)(l_sp+AS_PTR_SIZE) = p;
1893 l_bc++;
1894 }
1895 break;
1896
1897 // Do a boolean not operation, modifying the value of the variable
1898 case asBC_NOT:
1899 #if AS_SIZEOF_BOOL == 1
1900 {
1901 // Set the value to true if it is equal to 0
1902
1903 // We need to use volatile here to tell the compiler it cannot
1904 // change the order of read and write operations on the pointer.
1905
1906 volatile asBYTE *ptr = (asBYTE*)(l_fp - asBC_SWORDARG0(l_bc));
1907 asBYTE val = (ptr[0] == 0) ? VALUE_OF_BOOLEAN_TRUE : 0;
1908 ptr[0] = val; // The result is stored in the lower byte
1909 ptr[1] = 0; // Make sure the rest of the DWORD is 0
1910 ptr[2] = 0;
1911 ptr[3] = 0;
1912 }
1913 #else
1914 *(l_fp - asBC_SWORDARG0(l_bc)) = (*(l_fp - asBC_SWORDARG0(l_bc)) == 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
1915 #endif
1916 l_bc++;
1917 break;
1918
1919 // Push the dword value of a global variable on the stack
1920 case asBC_PshG4:
1921 --l_sp;
1922 *l_sp = *(asDWORD*)asBC_PTRARG(l_bc);
1923 l_bc += 1 + AS_PTR_SIZE;
1924 break;
1925
1926 // Load the address of a global variable in the register, then
1927 // copy the value of the global variable into a local variable
1928 case asBC_LdGRdR4:
1929 *(void**)&m_regs.valueRegister = (void*)asBC_PTRARG(l_bc);
1930 *(l_fp - asBC_SWORDARG0(l_bc)) = **(asDWORD**)&m_regs.valueRegister;
1931 l_bc += 1+AS_PTR_SIZE;
1932 break;
1933
1934 //----------------
1935 // path control instructions
1936
1937 // Begin execution of a script function
1938 case asBC_CALL:
1939 {
1940 int i = asBC_INTARG(l_bc);
1941 l_bc += 2;
1942
1943 asASSERT( i >= 0 );
1944 asASSERT( (i & FUNC_IMPORTED) == 0 );
1945
1946 // Need to move the values back to the context
1947 m_regs.programPointer = l_bc;
1948 m_regs.stackPointer = l_sp;
1949 m_regs.stackFramePointer = l_fp;
1950
1951 CallScriptFunction(m_engine->scriptFunctions[i]);
1952
1953 // Extract the values from the context again
1954 l_bc = m_regs.programPointer;
1955 l_sp = m_regs.stackPointer;
1956 l_fp = m_regs.stackFramePointer;
1957
1958 // If status isn't active anymore then we must stop
1959 if( m_status != asEXECUTION_ACTIVE )
1960 return;
1961 }
1962 break;
1963
1964 // Return to the caller, and remove the arguments from the stack
1965 case asBC_RET:
1966 {
1967 // Return if this was the first function, or a nested execution
1968 if( m_callStack.GetLength() == 0 ||
1969 m_callStack[m_callStack.GetLength() - CALLSTACK_FRAME_SIZE] == 0 )
1970 {
1971 m_status = asEXECUTION_FINISHED;
1972 return;
1973 }
1974
1975 asWORD w = asBC_WORDARG0(l_bc);
1976
1977 // Read the old framepointer, functionid, and programCounter from the call stack
1978 PopCallState();
1979
1980 // Extract the values from the context again
1981 l_bc = m_regs.programPointer;
1982 l_sp = m_regs.stackPointer;
1983 l_fp = m_regs.stackFramePointer;
1984
1985 // Pop arguments from stack
1986 l_sp += w;
1987 }
1988 break;
1989
1990 // Jump to a relative position
1991 case asBC_JMP:
1992 l_bc += 2 + asBC_INTARG(l_bc);
1993 break;
1994
1995 //----------------
1996 // Conditional jumps
1997
1998 // Jump to a relative position if the value in the register is 0
1999 case asBC_JZ:
2000 if( *(int*)&m_regs.valueRegister == 0 )
2001 l_bc += asBC_INTARG(l_bc) + 2;
2002 else
2003 l_bc += 2;
2004 break;
2005
2006 // Jump to a relative position if the value in the register is not 0
2007 case asBC_JNZ:
2008 if( *(int*)&m_regs.valueRegister != 0 )
2009 l_bc += asBC_INTARG(l_bc) + 2;
2010 else
2011 l_bc += 2;
2012 break;
2013
2014 // Jump to a relative position if the value in the register is negative
2015 case asBC_JS:
2016 if( *(int*)&m_regs.valueRegister < 0 )
2017 l_bc += asBC_INTARG(l_bc) + 2;
2018 else
2019 l_bc += 2;
2020 break;
2021
2022 // Jump to a relative position if the value in the register it not negative
2023 case asBC_JNS:
2024 if( *(int*)&m_regs.valueRegister >= 0 )
2025 l_bc += asBC_INTARG(l_bc) + 2;
2026 else
2027 l_bc += 2;
2028 break;
2029
2030 // Jump to a relative position if the value in the register is greater than 0
2031 case asBC_JP:
2032 if( *(int*)&m_regs.valueRegister > 0 )
2033 l_bc += asBC_INTARG(l_bc) + 2;
2034 else
2035 l_bc += 2;
2036 break;
2037
2038 // Jump to a relative position if the value in the register is not greater than 0
2039 case asBC_JNP:
2040 if( *(int*)&m_regs.valueRegister <= 0 )
2041 l_bc += asBC_INTARG(l_bc) + 2;
2042 else
2043 l_bc += 2;
2044 break;
2045 //--------------------
2046 // test instructions
2047
2048 // If the value in the register is 0, then set the register to 1, else to 0
2049 case asBC_TZ:
2050 #if AS_SIZEOF_BOOL == 1
2051 {
2052 // Set the value to true if it is equal to 0
2053
2054 // We need to use volatile here to tell the compiler it cannot
2055 // change the order of read and write operations on valueRegister.
2056
2057 volatile int *regPtr = (int*)&m_regs.valueRegister;
2058 volatile asBYTE *regBptr = (asBYTE*)&m_regs.valueRegister;
2059 asBYTE val = (regPtr[0] == 0) ? VALUE_OF_BOOLEAN_TRUE : 0;
2060 regBptr[0] = val; // The result is stored in the lower byte
2061 regBptr[1] = 0; // Make sure the rest of the register is 0
2062 regBptr[2] = 0;
2063 regBptr[3] = 0;
2064 regBptr[4] = 0;
2065 regBptr[5] = 0;
2066 regBptr[6] = 0;
2067 regBptr[7] = 0;
2068 }
2069 #else
2070 *(int*)&m_regs.valueRegister = (*(int*)&m_regs.valueRegister == 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
2071 #endif
2072 l_bc++;
2073 break;
2074
2075 // If the value in the register is not 0, then set the register to 1, else to 0
2076 case asBC_TNZ:
2077 #if AS_SIZEOF_BOOL == 1
2078 {
2079 // Set the value to true if it is not equal to 0
2080
2081 // We need to use volatile here to tell the compiler it cannot
2082 // change the order of read and write operations on valueRegister.
2083
2084 volatile int *regPtr = (int*)&m_regs.valueRegister;
2085 volatile asBYTE *regBptr = (asBYTE*)&m_regs.valueRegister;
2086 asBYTE val = (regPtr[0] == 0) ? 0 : VALUE_OF_BOOLEAN_TRUE;
2087 regBptr[0] = val; // The result is stored in the lower byte
2088 regBptr[1] = 0; // Make sure the rest of the register is 0
2089 regBptr[2] = 0;
2090 regBptr[3] = 0;
2091 regBptr[4] = 0;
2092 regBptr[5] = 0;
2093 regBptr[6] = 0;
2094 regBptr[7] = 0;
2095 }
2096 #else
2097 *(int*)&m_regs.valueRegister = (*(int*)&m_regs.valueRegister == 0 ? 0 : VALUE_OF_BOOLEAN_TRUE);
2098 #endif
2099 l_bc++;
2100 break;
2101
2102 // If the value in the register is negative, then set the register to 1, else to 0
2103 case asBC_TS:
2104 #if AS_SIZEOF_BOOL == 1
2105 {
2106 // Set the value to true if it is less than 0
2107
2108 // We need to use volatile here to tell the compiler it cannot
2109 // change the order of read and write operations on valueRegister.
2110
2111 volatile int *regPtr = (int*)&m_regs.valueRegister;
2112 volatile asBYTE *regBptr = (asBYTE*)&m_regs.valueRegister;
2113 asBYTE val = (regPtr[0] < 0) ? VALUE_OF_BOOLEAN_TRUE : 0;
2114 regBptr[0] = val; // The result is stored in the lower byte
2115 regBptr[1] = 0; // Make sure the rest of the register is 0
2116 regBptr[2] = 0;
2117 regBptr[3] = 0;
2118 regBptr[4] = 0;
2119 regBptr[5] = 0;
2120 regBptr[6] = 0;
2121 regBptr[7] = 0;
2122 }
2123 #else
2124 *(int*)&m_regs.valueRegister = (*(int*)&m_regs.valueRegister < 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
2125 #endif
2126 l_bc++;
2127 break;
2128
2129 // If the value in the register is not negative, then set the register to 1, else to 0
2130 case asBC_TNS:
2131 #if AS_SIZEOF_BOOL == 1
2132 {
2133 // Set the value to true if it is not less than 0
2134
2135 // We need to use volatile here to tell the compiler it cannot
2136 // change the order of read and write operations on valueRegister.
2137
2138 volatile int *regPtr = (int*)&m_regs.valueRegister;
2139 volatile asBYTE *regBptr = (asBYTE*)&m_regs.valueRegister;
2140 asBYTE val = (regPtr[0] >= 0) ? VALUE_OF_BOOLEAN_TRUE : 0;
2141 regBptr[0] = val; // The result is stored in the lower byte
2142 regBptr[1] = 0; // Make sure the rest of the register is 0
2143 regBptr[2] = 0;
2144 regBptr[3] = 0;
2145 regBptr[4] = 0;
2146 regBptr[5] = 0;
2147 regBptr[6] = 0;
2148 regBptr[7] = 0;
2149 }
2150 #else
2151 *(int*)&m_regs.valueRegister = (*(int*)&m_regs.valueRegister < 0 ? 0 : VALUE_OF_BOOLEAN_TRUE);
2152 #endif
2153 l_bc++;
2154 break;
2155
2156 // If the value in the register is greater than 0, then set the register to 1, else to 0
2157 case asBC_TP:
2158 #if AS_SIZEOF_BOOL == 1
2159 {
2160 // Set the value to true if it is greater than 0
2161
2162 // We need to use volatile here to tell the compiler it cannot
2163 // change the order of read and write operations on valueRegister.
2164
2165 volatile int *regPtr = (int*)&m_regs.valueRegister;
2166 volatile asBYTE *regBptr = (asBYTE*)&m_regs.valueRegister;
2167 asBYTE val = (regPtr[0] > 0) ? VALUE_OF_BOOLEAN_TRUE : 0;
2168 regBptr[0] = val; // The result is stored in the lower byte
2169 regBptr[1] = 0; // Make sure the rest of the register is 0
2170 regBptr[2] = 0;
2171 regBptr[3] = 0;
2172 regBptr[4] = 0;
2173 regBptr[5] = 0;
2174 regBptr[6] = 0;
2175 regBptr[7] = 0;
2176 }
2177 #else
2178 *(int*)&m_regs.valueRegister = (*(int*)&m_regs.valueRegister > 0 ? VALUE_OF_BOOLEAN_TRUE : 0);
2179 #endif
2180 l_bc++;
2181 break;
2182
2183 // If the value in the register is not greater than 0, then set the register to 1, else to 0
2184 case asBC_TNP:
2185 #if AS_SIZEOF_BOOL == 1
2186 {
2187 // Set the value to true if it is not greater than 0
2188
2189 // We need to use volatile here to tell the compiler it cannot
2190 // change the order of read and write operations on valueRegister.
2191
2192 volatile int *regPtr = (int*)&m_regs.valueRegister;
2193 volatile asBYTE *regBptr = (asBYTE*)&m_regs.valueRegister;
2194 asBYTE val = (regPtr[0] <= 0) ? VALUE_OF_BOOLEAN_TRUE : 0;
2195 regBptr[0] = val; // The result is stored in the lower byte
2196 regBptr[1] = 0; // Make sure the rest of the register is 0
2197 regBptr[2] = 0;
2198 regBptr[3] = 0;
2199 regBptr[4] = 0;
2200 regBptr[5] = 0;
2201 regBptr[6] = 0;
2202 regBptr[7] = 0;
2203 }
2204 #else
2205 *(int*)&m_regs.valueRegister = (*(int*)&m_regs.valueRegister > 0 ? 0 : VALUE_OF_BOOLEAN_TRUE);
2206 #endif
2207 l_bc++;
2208 break;
2209
2210 //--------------------
2211 // negate value
2212
2213 // Negate the integer value in the variable
2214 case asBC_NEGi:
2215 *(l_fp - asBC_SWORDARG0(l_bc)) = asDWORD(-int(*(l_fp - asBC_SWORDARG0(l_bc))));
2216 l_bc++;
2217 break;
2218
2219 // Negate the float value in the variable
2220 case asBC_NEGf:
2221 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = -*(float*)(l_fp - asBC_SWORDARG0(l_bc));
2222 l_bc++;
2223 break;
2224
2225 // Negate the double value in the variable
2226 case asBC_NEGd:
2227 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = -*(double*)(l_fp - asBC_SWORDARG0(l_bc));
2228 l_bc++;
2229 break;
2230
2231 //-------------------------
2232 // Increment value pointed to by address in register
2233
2234 // Increment the short value pointed to by the register
2235 case asBC_INCi16:
2236 (**(short**)&m_regs.valueRegister)++;
2237 l_bc++;
2238 break;
2239
2240 // Increment the byte value pointed to by the register
2241 case asBC_INCi8:
2242 (**(char**)&m_regs.valueRegister)++;
2243 l_bc++;
2244 break;
2245
2246 // Decrement the short value pointed to by the register
2247 case asBC_DECi16:
2248 (**(short**)&m_regs.valueRegister)--;
2249 l_bc++;
2250 break;
2251
2252 // Decrement the byte value pointed to by the register
2253 case asBC_DECi8:
2254 (**(char**)&m_regs.valueRegister)--;
2255 l_bc++;
2256 break;
2257
2258 // Increment the integer value pointed to by the register
2259 case asBC_INCi:
2260 ++(**(int**)&m_regs.valueRegister);
2261 l_bc++;
2262 break;
2263
2264 // Decrement the integer value pointed to by the register
2265 case asBC_DECi:
2266 --(**(int**)&m_regs.valueRegister);
2267 l_bc++;
2268 break;
2269
2270 // Increment the float value pointed to by the register
2271 case asBC_INCf:
2272 ++(**(float**)&m_regs.valueRegister);
2273 l_bc++;
2274 break;
2275
2276 // Decrement the float value pointed to by the register
2277 case asBC_DECf:
2278 --(**(float**)&m_regs.valueRegister);
2279 l_bc++;
2280 break;
2281
2282 // Increment the double value pointed to by the register
2283 case asBC_INCd:
2284 ++(**(double**)&m_regs.valueRegister);
2285 l_bc++;
2286 break;
2287
2288 // Decrement the double value pointed to by the register
2289 case asBC_DECd:
2290 --(**(double**)&m_regs.valueRegister);
2291 l_bc++;
2292 break;
2293
2294 // Increment the local integer variable
2295 case asBC_IncVi:
2296 (*(int*)(l_fp - asBC_SWORDARG0(l_bc)))++;
2297 l_bc++;
2298 break;
2299
2300 // Decrement the local integer variable
2301 case asBC_DecVi:
2302 (*(int*)(l_fp - asBC_SWORDARG0(l_bc)))--;
2303 l_bc++;
2304 break;
2305
2306 //--------------------
2307 // bits instructions
2308
2309 // Do a bitwise not on the value in the variable
2310 case asBC_BNOT:
2311 *(l_fp - asBC_SWORDARG0(l_bc)) = ~*(l_fp - asBC_SWORDARG0(l_bc));
2312 l_bc++;
2313 break;
2314
2315 // Do a bitwise and of two variables and store the result in a third variable
2316 case asBC_BAND:
2317 *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) & *(l_fp - asBC_SWORDARG2(l_bc));
2318 l_bc += 2;
2319 break;
2320
2321 // Do a bitwise or of two variables and store the result in a third variable
2322 case asBC_BOR:
2323 *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) | *(l_fp - asBC_SWORDARG2(l_bc));
2324 l_bc += 2;
2325 break;
2326
2327 // Do a bitwise xor of two variables and store the result in a third variable
2328 case asBC_BXOR:
2329 *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) ^ *(l_fp - asBC_SWORDARG2(l_bc));
2330 l_bc += 2;
2331 break;
2332
2333 // Do a logical shift left of two variables and store the result in a third variable
2334 case asBC_BSLL:
2335 *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) << *(l_fp - asBC_SWORDARG2(l_bc));
2336 l_bc += 2;
2337 break;
2338
2339 // Do a logical shift right of two variables and store the result in a third variable
2340 case asBC_BSRL:
2341 *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc)) >> *(l_fp - asBC_SWORDARG2(l_bc));
2342 l_bc += 2;
2343 break;
2344
2345 // Do an arithmetic shift right of two variables and store the result in a third variable
2346 case asBC_BSRA:
2347 *(l_fp - asBC_SWORDARG0(l_bc)) = int(*(l_fp - asBC_SWORDARG1(l_bc))) >> *(l_fp - asBC_SWORDARG2(l_bc));
2348 l_bc += 2;
2349 break;
2350
2351 case asBC_COPY:
2352 {
2353 void *d = (void*)*(asPWORD*)l_sp; l_sp += AS_PTR_SIZE;
2354 void *s = (void*)*(asPWORD*)l_sp;
2355 if( s == 0 || d == 0 )
2356 {
2357 // Need to move the values back to the context
2358 m_regs.programPointer = l_bc;
2359 m_regs.stackPointer = l_sp;
2360 m_regs.stackFramePointer = l_fp;
2361
2362 // Raise exception
2363 SetInternalException(TXT_NULL_POINTER_ACCESS);
2364 return;
2365 }
2366 memcpy(d, s, asBC_WORDARG0(l_bc)*4);
2367
2368 // replace the pointer on the stack with the lvalue
2369 *(asPWORD**)l_sp = (asPWORD*)d;
2370 }
2371 l_bc += 2;
2372 break;
2373
2374 case asBC_PshC8:
2375 l_sp -= 2;
2376 *(asQWORD*)l_sp = asBC_QWORDARG(l_bc);
2377 l_bc += 3;
2378 break;
2379
2380 case asBC_PshVPtr:
2381 l_sp -= AS_PTR_SIZE;
2382 *(asPWORD*)l_sp = *(asPWORD*)(l_fp - asBC_SWORDARG0(l_bc));
2383 l_bc++;
2384 break;
2385
2386 case asBC_RDSPtr:
2387 {
2388 // The pointer must not be null
2389 asPWORD a = *(asPWORD*)l_sp;
2390 if( a == 0 )
2391 {
2392 m_regs.programPointer = l_bc;
2393 m_regs.stackPointer = l_sp;
2394 m_regs.stackFramePointer = l_fp;
2395
2396 SetInternalException(TXT_NULL_POINTER_ACCESS);
2397 return;
2398 }
2399 // Pop an address from the stack, read a pointer from that address and push it on the stack
2400 *(asPWORD*)l_sp = *(asPWORD*)a;
2401 }
2402 l_bc++;
2403 break;
2404
2405 //----------------------------
2406 // Comparisons
2407 case asBC_CMPd:
2408 {
2409 // Do a comparison of the values, rather than a subtraction
2410 // in order to get proper behaviour for infinity values.
2411 double dbl1 = *(double*)(l_fp - asBC_SWORDARG0(l_bc));
2412 double dbl2 = *(double*)(l_fp - asBC_SWORDARG1(l_bc));
2413 if( dbl1 == dbl2 ) *(int*)&m_regs.valueRegister = 0;
2414 else if( dbl1 < dbl2 ) *(int*)&m_regs.valueRegister = -1;
2415 else *(int*)&m_regs.valueRegister = 1;
2416 l_bc += 2;
2417 }
2418 break;
2419
2420 case asBC_CMPu:
2421 {
2422 asDWORD d1 = *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc));
2423 asDWORD d2 = *(asDWORD*)(l_fp - asBC_SWORDARG1(l_bc));
2424 if( d1 == d2 ) *(int*)&m_regs.valueRegister = 0;
2425 else if( d1 < d2 ) *(int*)&m_regs.valueRegister = -1;
2426 else *(int*)&m_regs.valueRegister = 1;
2427 l_bc += 2;
2428 }
2429 break;
2430
2431 case asBC_CMPf:
2432 {
2433 // Do a comparison of the values, rather than a subtraction
2434 // in order to get proper behaviour for infinity values.
2435 float f1 = *(float*)(l_fp - asBC_SWORDARG0(l_bc));
2436 float f2 = *(float*)(l_fp - asBC_SWORDARG1(l_bc));
2437 if( f1 == f2 ) *(int*)&m_regs.valueRegister = 0;
2438 else if( f1 < f2 ) *(int*)&m_regs.valueRegister = -1;
2439 else *(int*)&m_regs.valueRegister = 1;
2440 l_bc += 2;
2441 }
2442 break;
2443
2444 case asBC_CMPi:
2445 {
2446 int i1 = *(int*)(l_fp - asBC_SWORDARG0(l_bc));
2447 int i2 = *(int*)(l_fp - asBC_SWORDARG1(l_bc));
2448 if( i1 == i2 ) *(int*)&m_regs.valueRegister = 0;
2449 else if( i1 < i2 ) *(int*)&m_regs.valueRegister = -1;
2450 else *(int*)&m_regs.valueRegister = 1;
2451 l_bc += 2;
2452 }
2453 break;
2454
2455 //----------------------------
2456 // Comparisons with constant value
2457 case asBC_CMPIi:
2458 {
2459 int i1 = *(int*)(l_fp - asBC_SWORDARG0(l_bc));
2460 int i2 = asBC_INTARG(l_bc);
2461 if( i1 == i2 ) *(int*)&m_regs.valueRegister = 0;
2462 else if( i1 < i2 ) *(int*)&m_regs.valueRegister = -1;
2463 else *(int*)&m_regs.valueRegister = 1;
2464 l_bc += 2;
2465 }
2466 break;
2467
2468 case asBC_CMPIf:
2469 {
2470 // Do a comparison of the values, rather than a subtraction
2471 // in order to get proper behaviour for infinity values.
2472 float f1 = *(float*)(l_fp - asBC_SWORDARG0(l_bc));
2473 float f2 = asBC_FLOATARG(l_bc);
2474 if( f1 == f2 ) *(int*)&m_regs.valueRegister = 0;
2475 else if( f1 < f2 ) *(int*)&m_regs.valueRegister = -1;
2476 else *(int*)&m_regs.valueRegister = 1;
2477 l_bc += 2;
2478 }
2479 break;
2480
2481 case asBC_CMPIu:
2482 {
2483 asDWORD d1 = *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc));
2484 asDWORD d2 = asBC_DWORDARG(l_bc);
2485 if( d1 == d2 ) *(int*)&m_regs.valueRegister = 0;
2486 else if( d1 < d2 ) *(int*)&m_regs.valueRegister = -1;
2487 else *(int*)&m_regs.valueRegister = 1;
2488 l_bc += 2;
2489 }
2490 break;
2491
2492 case asBC_JMPP:
2493 l_bc += 1 + (*(int*)(l_fp - asBC_SWORDARG0(l_bc)))*2;
2494 break;
2495
2496 case asBC_PopRPtr:
2497 *(asPWORD*)&m_regs.valueRegister = *(asPWORD*)l_sp;
2498 l_sp += AS_PTR_SIZE;
2499 l_bc++;
2500 break;
2501
2502 case asBC_PshRPtr:
2503 l_sp -= AS_PTR_SIZE;
2504 *(asPWORD*)l_sp = *(asPWORD*)&m_regs.valueRegister;
2505 l_bc++;
2506 break;
2507
2508 case asBC_STR:
2509 // TODO: NEWSTRING: Deprecate this instruction
2510 asASSERT(false);
2511 l_bc++;
2512 break;
2513
2514 case asBC_CALLSYS:
2515 {
2516 // Get function ID from the argument
2517 int i = asBC_INTARG(l_bc);
2518
2519 // Need to move the values back to the context as the called functions
2520 // may use the debug interface to inspect the registers
2521 m_regs.programPointer = l_bc;
2522 m_regs.stackPointer = l_sp;
2523 m_regs.stackFramePointer = l_fp;
2524
2525 l_sp += CallSystemFunction(i, this);
2526
2527 // Update the program position after the call so that line number is correct
2528 l_bc += 2;
2529
2530 if( m_regs.doProcessSuspend )
2531 {
2532 // Should the execution be suspended?
2533 if( m_doSuspend )
2534 {
2535 m_regs.programPointer = l_bc;
2536 m_regs.stackPointer = l_sp;
2537 m_regs.stackFramePointer = l_fp;
2538
2539 m_status = asEXECUTION_SUSPENDED;
2540 return;
2541 }
2542 // An exception might have been raised
2543 if( m_status != asEXECUTION_ACTIVE )
2544 {
2545 m_regs.programPointer = l_bc;
2546 m_regs.stackPointer = l_sp;
2547 m_regs.stackFramePointer = l_fp;
2548
2549 return;
2550 }
2551 }
2552 }
2553 break;
2554
2555 case asBC_CALLBND:
2556 {
2557 // TODO: Clean-up: This code is very similar to asBC_CallPtr. Create a shared method for them
2558 // Get the function ID from the stack
2559 int i = asBC_INTARG(l_bc);
2560
2561 asASSERT( i >= 0 );
2562 asASSERT( i & FUNC_IMPORTED );
2563
2564 // Need to move the values back to the context
2565 m_regs.programPointer = l_bc;
2566 m_regs.stackPointer = l_sp;
2567 m_regs.stackFramePointer = l_fp;
2568
2569 int funcId = m_engine->importedFunctions[i & ~FUNC_IMPORTED]->boundFunctionId;
2570 if( funcId == -1 )
2571 {
2572 // Need to update the program pointer for the exception handler
2573 m_regs.programPointer += 2;
2574
2575 // Tell the exception handler to clean up the arguments to this function
2576 m_needToCleanupArgs = true;
2577 SetInternalException(TXT_UNBOUND_FUNCTION);
2578 return;
2579 }
2580 else
2581 {
2582 asCScriptFunction *func = m_engine->GetScriptFunction(funcId);
2583 if( func->funcType == asFUNC_SCRIPT )
2584 {
2585 m_regs.programPointer += 2;
2586 CallScriptFunction(func);
2587 }
2588 else if( func->funcType == asFUNC_DELEGATE )
2589 {
2590 // Push the object pointer on the stack. There is always a reserved space for this so
2591 // we don't don't need to worry about overflowing the allocated memory buffer
2592 asASSERT( m_regs.stackPointer - AS_PTR_SIZE >= m_stackBlocks[m_stackIndex] );
2593 m_regs.stackPointer -= AS_PTR_SIZE;
2594 *(asPWORD*)m_regs.stackPointer = asPWORD(func->objForDelegate);
2595
2596 // Call the delegated method
2597 if( func->funcForDelegate->funcType == asFUNC_SYSTEM )
2598 {
2599 m_regs.stackPointer += CallSystemFunction(func->funcForDelegate->id, this);
2600
2601 // Update program position after the call so the line number
2602 // is correct in case the system function queries it
2603 m_regs.programPointer += 2;
2604 }
2605 else
2606 {
2607 m_regs.programPointer += 2;
2608
2609 // TODO: run-time optimize: The true method could be figured out when creating the delegate
2610 CallInterfaceMethod(func->funcForDelegate);
2611 }
2612 }
2613 else
2614 {
2615 asASSERT( func->funcType == asFUNC_SYSTEM );
2616
2617 m_regs.stackPointer += CallSystemFunction(func->id, this);
2618
2619 // Update program position after the call so the line number
2620 // is correct in case the system function queries it
2621 m_regs.programPointer += 2;
2622 }
2623 }
2624
2625 // Extract the values from the context again
2626 l_bc = m_regs.programPointer;
2627 l_sp = m_regs.stackPointer;
2628 l_fp = m_regs.stackFramePointer;
2629
2630 // If status isn't active anymore then we must stop
2631 if( m_status != asEXECUTION_ACTIVE )
2632 return;
2633 }
2634 break;
2635
2636 case asBC_SUSPEND:
2637 if( m_regs.doProcessSuspend )
2638 {
2639 if( m_lineCallback )
2640 {
2641 m_regs.programPointer = l_bc;
2642 m_regs.stackPointer = l_sp;
2643 m_regs.stackFramePointer = l_fp;
2644
2645 CallLineCallback();
2646 }
2647 if( m_doSuspend )
2648 {
2649 l_bc++;
2650
2651 // Need to move the values back to the context
2652 m_regs.programPointer = l_bc;
2653 m_regs.stackPointer = l_sp;
2654 m_regs.stackFramePointer = l_fp;
2655
2656 m_status = asEXECUTION_SUSPENDED;
2657 return;
2658 }
2659 }
2660
2661 l_bc++;
2662 break;
2663
2664 case asBC_ALLOC:
2665 {
2666 asCObjectType *objType = (asCObjectType*)asBC_PTRARG(l_bc);
2667 int func = asBC_INTARG(l_bc+AS_PTR_SIZE);
2668
2669 if( objType->flags & asOBJ_SCRIPT_OBJECT )
2670 {
2671 // Need to move the values back to the context as the construction
2672 // of the script object may reuse the context for nested calls.
2673 m_regs.programPointer = l_bc;
2674 m_regs.stackPointer = l_sp;
2675 m_regs.stackFramePointer = l_fp;
2676
2677 // Pre-allocate the memory
2678 asDWORD *mem = (asDWORD*)m_engine->CallAlloc(objType);
2679
2680 // Pre-initialize the memory by calling the constructor for asCScriptObject
2681 ScriptObject_Construct(objType, (asCScriptObject*)mem);
2682
2683 // Call the constructor to initalize the memory
2684 asCScriptFunction *f = m_engine->scriptFunctions[func];
2685
2686 asDWORD **a = (asDWORD**)*(asPWORD*)(m_regs.stackPointer + f->GetSpaceNeededForArguments());
2687 if( a ) *a = mem;
2688
2689 // Push the object pointer on the stack
2690 m_regs.stackPointer -= AS_PTR_SIZE;
2691 *(asPWORD*)m_regs.stackPointer = (asPWORD)mem;
2692
2693 m_regs.programPointer += 2+AS_PTR_SIZE;
2694
2695 CallScriptFunction(f);
2696
2697 // Extract the values from the context again
2698 l_bc = m_regs.programPointer;
2699 l_sp = m_regs.stackPointer;
2700 l_fp = m_regs.stackFramePointer;
2701
2702 // If status isn't active anymore then we must stop
2703 if( m_status != asEXECUTION_ACTIVE )
2704 return;
2705 }
2706 else
2707 {
2708 // Pre-allocate the memory
2709 asDWORD *mem = (asDWORD*)m_engine->CallAlloc(objType);
2710
2711 if( func )
2712 {
2713 // Push the object pointer on the stack (it will be popped by the function)
2714 l_sp -= AS_PTR_SIZE;
2715 *(asPWORD*)l_sp = (asPWORD)mem;
2716
2717 // Need to move the values back to the context as the called functions
2718 // may use the debug interface to inspect the registers
2719 m_regs.programPointer = l_bc;
2720 m_regs.stackPointer = l_sp;
2721 m_regs.stackFramePointer = l_fp;
2722
2723 l_sp += CallSystemFunction(func, this);
2724 }
2725
2726 // Pop the variable address from the stack
2727 asDWORD **a = (asDWORD**)*(asPWORD*)l_sp;
2728 l_sp += AS_PTR_SIZE;
2729 if( a ) *a = mem;
2730
2731 l_bc += 2+AS_PTR_SIZE;
2732
2733 if( m_regs.doProcessSuspend )
2734 {
2735 // Should the execution be suspended?
2736 if( m_doSuspend )
2737 {
2738 m_regs.programPointer = l_bc;
2739 m_regs.stackPointer = l_sp;
2740 m_regs.stackFramePointer = l_fp;
2741
2742 m_status = asEXECUTION_SUSPENDED;
2743 return;
2744 }
2745 // An exception might have been raised
2746 if( m_status != asEXECUTION_ACTIVE )
2747 {
2748 m_regs.programPointer = l_bc;
2749 m_regs.stackPointer = l_sp;
2750 m_regs.stackFramePointer = l_fp;
2751
2752 m_engine->CallFree(mem);
2753 *a = 0;
2754
2755 return;
2756 }
2757 }
2758 }
2759 }
2760 break;
2761
2762 case asBC_FREE:
2763 {
2764 // Get the variable that holds the object handle/reference
2765 asPWORD *a = (asPWORD*)asPWORD(l_fp - asBC_SWORDARG0(l_bc));
2766 if( *a )
2767 {
2768 asCObjectType *objType = (asCObjectType*)asBC_PTRARG(l_bc);
2769 asSTypeBehaviour *beh = &objType->beh;
2770
2771 // Need to move the values back to the context as the called functions
2772 // may use the debug interface to inspect the registers
2773 m_regs.programPointer = l_bc;
2774 m_regs.stackPointer = l_sp;
2775 m_regs.stackFramePointer = l_fp;
2776
2777 if( objType->flags & asOBJ_REF )
2778 {
2779 asASSERT( (objType->flags & asOBJ_NOCOUNT) || beh->release );
2780 if( beh->release )
2781 m_engine->CallObjectMethod((void*)(asPWORD)*a, beh->release);
2782 }
2783 else
2784 {
2785 if( beh->destruct )
2786 m_engine->CallObjectMethod((void*)(asPWORD)*a, beh->destruct);
2787 else if( objType->flags & asOBJ_LIST_PATTERN )
2788 m_engine->DestroyList((asBYTE*)(asPWORD)*a, objType);
2789
2790 m_engine->CallFree((void*)(asPWORD)*a);
2791 }
2792
2793 // Clear the variable
2794 *a = 0;
2795 }
2796 }
2797 l_bc += 1+AS_PTR_SIZE;
2798 break;
2799
2800 case asBC_LOADOBJ:
2801 {
2802 // Move the object pointer from the object variable into the object register
2803 void **a = (void**)(l_fp - asBC_SWORDARG0(l_bc));
2804 m_regs.objectType = 0;
2805 m_regs.objectRegister = *a;
2806 *a = 0;
2807 }
2808 l_bc++;
2809 break;
2810
2811 case asBC_STOREOBJ:
2812 // Move the object pointer from the object register to the object variable
2813 *(asPWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = asPWORD(m_regs.objectRegister);
2814 m_regs.objectRegister = 0;
2815 l_bc++;
2816 break;
2817
2818 case asBC_GETOBJ:
2819 {
2820 // Read variable index from location on stack
2821 asPWORD *a = (asPWORD*)(l_sp + asBC_WORDARG0(l_bc));
2822 asPWORD offset = *a;
2823 // Move pointer from variable to the same location on the stack
2824 asPWORD *v = (asPWORD*)(l_fp - offset);
2825 *a = *v;
2826 // Clear variable
2827 *v = 0;
2828 }
2829 l_bc++;
2830 break;
2831
2832 case asBC_REFCPY:
2833 {
2834 asCObjectType *objType = (asCObjectType*)asBC_PTRARG(l_bc);
2835 asSTypeBehaviour *beh = &objType->beh;
2836
2837 // Pop address of destination pointer from the stack
2838 void **d = (void**)*(asPWORD*)l_sp;
2839 l_sp += AS_PTR_SIZE;
2840
2841 // Read wanted pointer from the stack
2842 void *s = (void*)*(asPWORD*)l_sp;
2843
2844 // Need to move the values back to the context as the called functions
2845 // may use the debug interface to inspect the registers
2846 m_regs.programPointer = l_bc;
2847 m_regs.stackPointer = l_sp;
2848 m_regs.stackFramePointer = l_fp;
2849
2850 if( !(objType->flags & asOBJ_NOCOUNT) )
2851 {
2852 // Release previous object held by destination pointer
2853 if( *d != 0 )
2854 m_engine->CallObjectMethod(*d, beh->release);
2855 // Increase ref counter of wanted object
2856 if( s != 0 )
2857 m_engine->CallObjectMethod(s, beh->addref);
2858 }
2859
2860 // Set the new object in the destination
2861 *d = s;
2862 }
2863 l_bc += 1+AS_PTR_SIZE;
2864 break;
2865
2866 case asBC_CHKREF:
2867 {
2868 // Verify if the pointer on the stack is null
2869 // This is used when validating a pointer that an operator will work on
2870 asPWORD a = *(asPWORD*)l_sp;
2871 if( a == 0 )
2872 {
2873 m_regs.programPointer = l_bc;
2874 m_regs.stackPointer = l_sp;
2875 m_regs.stackFramePointer = l_fp;
2876
2877 SetInternalException(TXT_NULL_POINTER_ACCESS);
2878 return;
2879 }
2880 }
2881 l_bc++;
2882 break;
2883
2884 case asBC_GETOBJREF:
2885 {
2886 // Get the location on the stack where the reference will be placed
2887 asPWORD *a = (asPWORD*)(l_sp + asBC_WORDARG0(l_bc));
2888
2889 // Replace the variable index with the object handle held in the variable
2890 *(asPWORD**)a = *(asPWORD**)(l_fp - *a);
2891 }
2892 l_bc++;
2893 break;
2894
2895 case asBC_GETREF:
2896 {
2897 // Get the location on the stack where the reference will be placed
2898 asPWORD *a = (asPWORD*)(l_sp + asBC_WORDARG0(l_bc));
2899
2900 // Replace the variable index with the address of the variable
2901 *(asPWORD**)a = (asPWORD*)(l_fp - (int)*a);
2902 }
2903 l_bc++;
2904 break;
2905
2906 case asBC_PshNull:
2907 // Push a null pointer on the stack
2908 l_sp -= AS_PTR_SIZE;
2909 *(asPWORD*)l_sp = 0;
2910 l_bc++;
2911 break;
2912
2913 case asBC_ClrVPtr:
2914 // TODO: runtime optimize: Is this instruction really necessary?
2915 // CallScriptFunction() can clear the null handles upon entry, just as is done for
2916 // all other object variables
2917 // Clear pointer variable
2918 *(asPWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = 0;
2919 l_bc++;
2920 break;
2921
2922 case asBC_OBJTYPE:
2923 // Push the object type on the stack
2924 l_sp -= AS_PTR_SIZE;
2925 *(asPWORD*)l_sp = asBC_PTRARG(l_bc);
2926 l_bc += 1+AS_PTR_SIZE;
2927 break;
2928
2929 case asBC_TYPEID:
2930 // Equivalent to PshC4, but kept as separate instruction for bytecode serialization
2931 --l_sp;
2932 *l_sp = asBC_DWORDARG(l_bc);
2933 l_bc += 2;
2934 break;
2935
2936 case asBC_SetV4:
2937 *(l_fp - asBC_SWORDARG0(l_bc)) = asBC_DWORDARG(l_bc);
2938 l_bc += 2;
2939 break;
2940
2941 case asBC_SetV8:
2942 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = asBC_QWORDARG(l_bc);
2943 l_bc += 3;
2944 break;
2945
2946 case asBC_ADDSi:
2947 {
2948 // The pointer must not be null
2949 asPWORD a = *(asPWORD*)l_sp;
2950 if( a == 0 )
2951 {
2952 m_regs.programPointer = l_bc;
2953 m_regs.stackPointer = l_sp;
2954 m_regs.stackFramePointer = l_fp;
2955
2956 SetInternalException(TXT_NULL_POINTER_ACCESS);
2957 return;
2958 }
2959 // Add an offset to the pointer
2960 *(asPWORD*)l_sp = a + asBC_SWORDARG0(l_bc);
2961 }
2962 l_bc += 2;
2963 break;
2964
2965 case asBC_CpyVtoV4:
2966 *(l_fp - asBC_SWORDARG0(l_bc)) = *(l_fp - asBC_SWORDARG1(l_bc));
2967 l_bc += 2;
2968 break;
2969
2970 case asBC_CpyVtoV8:
2971 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc));
2972 l_bc += 2;
2973 break;
2974
2975 case asBC_CpyVtoR4:
2976 *(asDWORD*)&m_regs.valueRegister = *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc));
2977 l_bc++;
2978 break;
2979
2980 case asBC_CpyVtoR8:
2981 *(asQWORD*)&m_regs.valueRegister = *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc));
2982 l_bc++;
2983 break;
2984
2985 case asBC_CpyVtoG4:
2986 *(asDWORD*)asBC_PTRARG(l_bc) = *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc));
2987 l_bc += 1 + AS_PTR_SIZE;
2988 break;
2989
2990 case asBC_CpyRtoV4:
2991 *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asDWORD*)&m_regs.valueRegister;
2992 l_bc++;
2993 break;
2994
2995 case asBC_CpyRtoV8:
2996 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = m_regs.valueRegister;
2997 l_bc++;
2998 break;
2999
3000 case asBC_CpyGtoV4:
3001 *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asDWORD*)asBC_PTRARG(l_bc);
3002 l_bc += 1 + AS_PTR_SIZE;
3003 break;
3004
3005 case asBC_WRTV1:
3006 // The pointer in the register points to a byte, and *(l_fp - offset) too
3007 **(asBYTE**)&m_regs.valueRegister = *(asBYTE*)(l_fp - asBC_SWORDARG0(l_bc));
3008 l_bc++;
3009 break;
3010
3011 case asBC_WRTV2:
3012 // The pointer in the register points to a word, and *(l_fp - offset) too
3013 **(asWORD**)&m_regs.valueRegister = *(asWORD*)(l_fp - asBC_SWORDARG0(l_bc));
3014 l_bc++;
3015 break;
3016
3017 case asBC_WRTV4:
3018 **(asDWORD**)&m_regs.valueRegister = *(l_fp - asBC_SWORDARG0(l_bc));
3019 l_bc++;
3020 break;
3021
3022 case asBC_WRTV8:
3023 **(asQWORD**)&m_regs.valueRegister = *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc));
3024 l_bc++;
3025 break;
3026
3027 case asBC_RDR1:
3028 {
3029 // The pointer in the register points to a byte, and *(l_fp - offset) will also point to a byte
3030 asBYTE *bPtr = (asBYTE*)(l_fp - asBC_SWORDARG0(l_bc));
3031 bPtr[0] = **(asBYTE**)&m_regs.valueRegister; // read the byte
3032 bPtr[1] = 0; // 0 the rest of the DWORD
3033 bPtr[2] = 0;
3034 bPtr[3] = 0;
3035 }
3036 l_bc++;
3037 break;
3038
3039 case asBC_RDR2:
3040 {
3041 // The pointer in the register points to a word, and *(l_fp - offset) will also point to a word
3042 asWORD *wPtr = (asWORD*)(l_fp - asBC_SWORDARG0(l_bc));
3043 wPtr[0] = **(asWORD**)&m_regs.valueRegister; // read the word
3044 wPtr[1] = 0; // 0 the rest of the DWORD
3045 }
3046 l_bc++;
3047 break;
3048
3049 case asBC_RDR4:
3050 *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = **(asDWORD**)&m_regs.valueRegister;
3051 l_bc++;
3052 break;
3053
3054 case asBC_RDR8:
3055 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = **(asQWORD**)&m_regs.valueRegister;
3056 l_bc++;
3057 break;
3058
3059 case asBC_LDG:
3060 *(asPWORD*)&m_regs.valueRegister = asBC_PTRARG(l_bc);
3061 l_bc += 1+AS_PTR_SIZE;
3062 break;
3063
3064 case asBC_LDV:
3065 *(asDWORD**)&m_regs.valueRegister = (l_fp - asBC_SWORDARG0(l_bc));
3066 l_bc++;
3067 break;
3068
3069 case asBC_PGA:
3070 l_sp -= AS_PTR_SIZE;
3071 *(asPWORD*)l_sp = asBC_PTRARG(l_bc);
3072 l_bc += 1+AS_PTR_SIZE;
3073 break;
3074
3075 case asBC_CmpPtr:
3076 {
3077 // TODO: runtime optimize: This instruction should really just be an equals, and return true or false.
3078 // The instruction is only used for is and !is tests anyway.
3079 asPWORD p1 = *(asPWORD*)(l_fp - asBC_SWORDARG0(l_bc));
3080 asPWORD p2 = *(asPWORD*)(l_fp - asBC_SWORDARG1(l_bc));
3081 if( p1 == p2 ) *(int*)&m_regs.valueRegister = 0;
3082 else if( p1 < p2 ) *(int*)&m_regs.valueRegister = -1;
3083 else *(int*)&m_regs.valueRegister = 1;
3084 l_bc += 2;
3085 }
3086 break;
3087
3088 case asBC_VAR:
3089 l_sp -= AS_PTR_SIZE;
3090 *(asPWORD*)l_sp = (asPWORD)asBC_SWORDARG0(l_bc);
3091 l_bc++;
3092 break;
3093
3094 //----------------------------
3095 // Type conversions
3096 case asBC_iTOf:
3097 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(int*)(l_fp - asBC_SWORDARG0(l_bc)));
3098 l_bc++;
3099 break;
3100
3101 case asBC_fTOi:
3102 *(l_fp - asBC_SWORDARG0(l_bc)) = int(*(float*)(l_fp - asBC_SWORDARG0(l_bc)));
3103 l_bc++;
3104 break;
3105
3106 case asBC_uTOf:
3107 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(l_fp - asBC_SWORDARG0(l_bc)));
3108 l_bc++;
3109 break;
3110
3111 case asBC_fTOu:
3112 // We must cast to int first, because on some compilers the cast of a negative float value to uint result in 0
3113 *(l_fp - asBC_SWORDARG0(l_bc)) = asUINT(int(*(float*)(l_fp - asBC_SWORDARG0(l_bc))));
3114 l_bc++;
3115 break;
3116
3117 case asBC_sbTOi:
3118 // *(l_fp - offset) points to a char, and will point to an int afterwards
3119 *(l_fp - asBC_SWORDARG0(l_bc)) = *(signed char*)(l_fp - asBC_SWORDARG0(l_bc));
3120 l_bc++;
3121 break;
3122
3123 case asBC_swTOi:
3124 // *(l_fp - offset) points to a short, and will point to an int afterwards
3125 *(l_fp - asBC_SWORDARG0(l_bc)) = *(short*)(l_fp - asBC_SWORDARG0(l_bc));
3126 l_bc++;
3127 break;
3128
3129 case asBC_ubTOi:
3130 // (l_fp - offset) points to a byte, and will point to an int afterwards
3131 *(l_fp - asBC_SWORDARG0(l_bc)) = *(asBYTE*)(l_fp - asBC_SWORDARG0(l_bc));
3132 l_bc++;
3133 break;
3134
3135 case asBC_uwTOi:
3136 // *(l_fp - offset) points to a word, and will point to an int afterwards
3137 *(l_fp - asBC_SWORDARG0(l_bc)) = *(asWORD*)(l_fp - asBC_SWORDARG0(l_bc));
3138 l_bc++;
3139 break;
3140
3141 case asBC_dTOi:
3142 *(l_fp - asBC_SWORDARG0(l_bc)) = int(*(double*)(l_fp - asBC_SWORDARG1(l_bc)));
3143 l_bc += 2;
3144 break;
3145
3146 case asBC_dTOu:
3147 // We must cast to int first, because on some compilers the cast of a negative float value to uint result in 0
3148 *(l_fp - asBC_SWORDARG0(l_bc)) = asUINT(int(*(double*)(l_fp - asBC_SWORDARG1(l_bc))));
3149 l_bc += 2;
3150 break;
3151
3152 case asBC_dTOf:
3153 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(double*)(l_fp - asBC_SWORDARG1(l_bc)));
3154 l_bc += 2;
3155 break;
3156
3157 case asBC_iTOd:
3158 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(int*)(l_fp - asBC_SWORDARG1(l_bc)));
3159 l_bc += 2;
3160 break;
3161
3162 case asBC_uTOd:
3163 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(asUINT*)(l_fp - asBC_SWORDARG1(l_bc)));
3164 l_bc += 2;
3165 break;
3166
3167 case asBC_fTOd:
3168 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(float*)(l_fp - asBC_SWORDARG1(l_bc)));
3169 l_bc += 2;
3170 break;
3171
3172 //------------------------------
3173 // Math operations
3174 case asBC_ADDi:
3175 *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) + *(int*)(l_fp - asBC_SWORDARG2(l_bc));
3176 l_bc += 2;
3177 break;
3178
3179 case asBC_SUBi:
3180 *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) - *(int*)(l_fp - asBC_SWORDARG2(l_bc));
3181 l_bc += 2;
3182 break;
3183
3184 case asBC_MULi:
3185 *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) * *(int*)(l_fp - asBC_SWORDARG2(l_bc));
3186 l_bc += 2;
3187 break;
3188
3189 case asBC_DIVi:
3190 {
3191 int divider = *(int*)(l_fp - asBC_SWORDARG2(l_bc));
3192 if( divider == 0 )
3193 {
3194 // Need to move the values back to the context
3195 m_regs.programPointer = l_bc;
3196 m_regs.stackPointer = l_sp;
3197 m_regs.stackFramePointer = l_fp;
3198
3199 // Raise exception
3200 SetInternalException(TXT_DIVIDE_BY_ZERO);
3201 return;
3202 }
3203 else if( divider == -1 )
3204 {
3205 // Need to check if the value that is divided is 0x80000000
3206 // as dividing it with -1 will cause an overflow exception
3207 if( *(int*)(l_fp - asBC_SWORDARG1(l_bc)) == int(0x80000000) )
3208 {
3209 // Need to move the values back to the context
3210 m_regs.programPointer = l_bc;
3211 m_regs.stackPointer = l_sp;
3212 m_regs.stackFramePointer = l_fp;
3213
3214 // Raise exception
3215 SetInternalException(TXT_DIVIDE_OVERFLOW);
3216 return;
3217 }
3218 }
3219 *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) / divider;
3220 }
3221 l_bc += 2;
3222 break;
3223
3224 case asBC_MODi:
3225 {
3226 int divider = *(int*)(l_fp - asBC_SWORDARG2(l_bc));
3227 if( divider == 0 )
3228 {
3229 // Need to move the values back to the context
3230 m_regs.programPointer = l_bc;
3231 m_regs.stackPointer = l_sp;
3232 m_regs.stackFramePointer = l_fp;
3233
3234 // Raise exception
3235 SetInternalException(TXT_DIVIDE_BY_ZERO);
3236 return;
3237 }
3238 else if( divider == -1 )
3239 {
3240 // Need to check if the value that is divided is 0x80000000
3241 // as dividing it with -1 will cause an overflow exception
3242 if( *(int*)(l_fp - asBC_SWORDARG1(l_bc)) == int(0x80000000) )
3243 {
3244 // Need to move the values back to the context
3245 m_regs.programPointer = l_bc;
3246 m_regs.stackPointer = l_sp;
3247 m_regs.stackFramePointer = l_fp;
3248
3249 // Raise exception
3250 SetInternalException(TXT_DIVIDE_OVERFLOW);
3251 return;
3252 }
3253 }
3254 *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) % divider;
3255 }
3256 l_bc += 2;
3257 break;
3258
3259 case asBC_ADDf:
3260 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) + *(float*)(l_fp - asBC_SWORDARG2(l_bc));
3261 l_bc += 2;
3262 break;
3263
3264 case asBC_SUBf:
3265 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) - *(float*)(l_fp - asBC_SWORDARG2(l_bc));
3266 l_bc += 2;
3267 break;
3268
3269 case asBC_MULf:
3270 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) * *(float*)(l_fp - asBC_SWORDARG2(l_bc));
3271 l_bc += 2;
3272 break;
3273
3274 case asBC_DIVf:
3275 {
3276 float divider = *(float*)(l_fp - asBC_SWORDARG2(l_bc));
3277 if( divider == 0 )
3278 {
3279 // Need to move the values back to the context
3280 m_regs.programPointer = l_bc;
3281 m_regs.stackPointer = l_sp;
3282 m_regs.stackFramePointer = l_fp;
3283
3284 // Raise exception
3285 SetInternalException(TXT_DIVIDE_BY_ZERO);
3286 return;
3287 }
3288 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) / divider;
3289 }
3290 l_bc += 2;
3291 break;
3292
3293 case asBC_MODf:
3294 {
3295 float divider = *(float*)(l_fp - asBC_SWORDARG2(l_bc));
3296 if( divider == 0 )
3297 {
3298 // Need to move the values back to the context
3299 m_regs.programPointer = l_bc;
3300 m_regs.stackPointer = l_sp;
3301 m_regs.stackFramePointer = l_fp;
3302
3303 // Raise exception
3304 SetInternalException(TXT_DIVIDE_BY_ZERO);
3305 return;
3306 }
3307 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = fmodf(*(float*)(l_fp - asBC_SWORDARG1(l_bc)), divider);
3308 }
3309 l_bc += 2;
3310 break;
3311
3312 case asBC_ADDd:
3313 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = *(double*)(l_fp - asBC_SWORDARG1(l_bc)) + *(double*)(l_fp - asBC_SWORDARG2(l_bc));
3314 l_bc += 2;
3315 break;
3316
3317 case asBC_SUBd:
3318 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = *(double*)(l_fp - asBC_SWORDARG1(l_bc)) - *(double*)(l_fp - asBC_SWORDARG2(l_bc));
3319 l_bc += 2;
3320 break;
3321
3322 case asBC_MULd:
3323 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = *(double*)(l_fp - asBC_SWORDARG1(l_bc)) * *(double*)(l_fp - asBC_SWORDARG2(l_bc));
3324 l_bc += 2;
3325 break;
3326
3327 case asBC_DIVd:
3328 {
3329 double divider = *(double*)(l_fp - asBC_SWORDARG2(l_bc));
3330 if( divider == 0 )
3331 {
3332 // Need to move the values back to the context
3333 m_regs.programPointer = l_bc;
3334 m_regs.stackPointer = l_sp;
3335 m_regs.stackFramePointer = l_fp;
3336
3337 // Raise exception
3338 SetInternalException(TXT_DIVIDE_BY_ZERO);
3339 return;
3340 }
3341
3342 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = *(double*)(l_fp - asBC_SWORDARG1(l_bc)) / divider;
3343 l_bc += 2;
3344 }
3345 break;
3346
3347 case asBC_MODd:
3348 {
3349 double divider = *(double*)(l_fp - asBC_SWORDARG2(l_bc));
3350 if( divider == 0 )
3351 {
3352 // Need to move the values back to the context
3353 m_regs.programPointer = l_bc;
3354 m_regs.stackPointer = l_sp;
3355 m_regs.stackFramePointer = l_fp;
3356
3357 // Raise exception
3358 SetInternalException(TXT_DIVIDE_BY_ZERO);
3359 return;
3360 }
3361
3362 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = fmod(*(double*)(l_fp - asBC_SWORDARG1(l_bc)), divider);
3363 l_bc += 2;
3364 }
3365 break;
3366
3367 //------------------------------
3368 // Math operations with constant value
3369 case asBC_ADDIi:
3370 *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) + asBC_INTARG(l_bc+1);
3371 l_bc += 3;
3372 break;
3373
3374 case asBC_SUBIi:
3375 *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) - asBC_INTARG(l_bc+1);
3376 l_bc += 3;
3377 break;
3378
3379 case asBC_MULIi:
3380 *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = *(int*)(l_fp - asBC_SWORDARG1(l_bc)) * asBC_INTARG(l_bc+1);
3381 l_bc += 3;
3382 break;
3383
3384 case asBC_ADDIf:
3385 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) + asBC_FLOATARG(l_bc+1);
3386 l_bc += 3;
3387 break;
3388
3389 case asBC_SUBIf:
3390 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) - asBC_FLOATARG(l_bc+1);
3391 l_bc += 3;
3392 break;
3393
3394 case asBC_MULIf:
3395 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = *(float*)(l_fp - asBC_SWORDARG1(l_bc)) * asBC_FLOATARG(l_bc+1);
3396 l_bc += 3;
3397 break;
3398
3399 //-----------------------------------
3400 case asBC_SetG4:
3401 *(asDWORD*)asBC_PTRARG(l_bc) = asBC_DWORDARG(l_bc+AS_PTR_SIZE);
3402 l_bc += 2 + AS_PTR_SIZE;
3403 break;
3404
3405 case asBC_ChkRefS:
3406 {
3407 // Verify if the pointer on the stack refers to a non-null value
3408 // This is used to validate a reference to a handle
3409 asPWORD *a = (asPWORD*)*(asPWORD*)l_sp;
3410 if( *a == 0 )
3411 {
3412 m_regs.programPointer = l_bc;
3413 m_regs.stackPointer = l_sp;
3414 m_regs.stackFramePointer = l_fp;
3415
3416 SetInternalException(TXT_NULL_POINTER_ACCESS);
3417 return;
3418 }
3419 }
3420 l_bc++;
3421 break;
3422
3423 case asBC_ChkNullV:
3424 {
3425 // Verify if variable (on the stack) is not null
3426 asDWORD *a = *(asDWORD**)(l_fp - asBC_SWORDARG0(l_bc));
3427 if( a == 0 )
3428 {
3429 m_regs.programPointer = l_bc;
3430 m_regs.stackPointer = l_sp;
3431 m_regs.stackFramePointer = l_fp;
3432
3433 SetInternalException(TXT_NULL_POINTER_ACCESS);
3434 return;
3435 }
3436 }
3437 l_bc++;
3438 break;
3439
3440 case asBC_CALLINTF:
3441 {
3442 int i = asBC_INTARG(l_bc);
3443 l_bc += 2;
3444
3445 asASSERT( i >= 0 );
3446 asASSERT( (i & FUNC_IMPORTED) == 0 );
3447
3448 // Need to move the values back to the context
3449 m_regs.programPointer = l_bc;
3450 m_regs.stackPointer = l_sp;
3451 m_regs.stackFramePointer = l_fp;
3452
3453 CallInterfaceMethod(m_engine->GetScriptFunction(i));
3454
3455 // Extract the values from the context again
3456 l_bc = m_regs.programPointer;
3457 l_sp = m_regs.stackPointer;
3458 l_fp = m_regs.stackFramePointer;
3459
3460 // If status isn't active anymore then we must stop
3461 if( m_status != asEXECUTION_ACTIVE )
3462 return;
3463 }
3464 break;
3465
3466 case asBC_iTOb:
3467 {
3468 // *(l_fp - offset) points to an int, and will point to a byte afterwards
3469
3470 // We need to use volatile here to tell the compiler not to rearrange
3471 // read and write operations during optimizations.
3472 volatile asDWORD val = *(l_fp - asBC_SWORDARG0(l_bc));
3473 volatile asBYTE *bPtr = (asBYTE*)(l_fp - asBC_SWORDARG0(l_bc));
3474 bPtr[0] = (asBYTE)val; // write the byte
3475 bPtr[1] = 0; // 0 the rest of the DWORD
3476 bPtr[2] = 0;
3477 bPtr[3] = 0;
3478 }
3479 l_bc++;
3480 break;
3481
3482 case asBC_iTOw:
3483 {
3484 // *(l_fp - offset) points to an int, and will point to word afterwards
3485
3486 // We need to use volatile here to tell the compiler not to rearrange
3487 // read and write operations during optimizations.
3488 volatile asDWORD val = *(l_fp - asBC_SWORDARG0(l_bc));
3489 volatile asWORD *wPtr = (asWORD*)(l_fp - asBC_SWORDARG0(l_bc));
3490 wPtr[0] = (asWORD)val; // write the word
3491 wPtr[1] = 0; // 0 the rest of the DWORD
3492 }
3493 l_bc++;
3494 break;
3495
3496 case asBC_SetV1:
3497 // TODO: This is exactly the same as SetV4. This is a left over from the time
3498 // when the bytecode instructions were more tightly packed. It can now
3499 // be removed. When removing it, make sure the value is correctly converted
3500 // on big-endian CPUs.
3501
3502 // The byte is already stored correctly in the argument
3503 *(l_fp - asBC_SWORDARG0(l_bc)) = asBC_DWORDARG(l_bc);
3504 l_bc += 2;
3505 break;
3506
3507 case asBC_SetV2:
3508 // TODO: This is exactly the same as SetV4. This is a left over from the time
3509 // when the bytecode instructions were more tightly packed. It can now
3510 // be removed. When removing it, make sure the value is correctly converted
3511 // on big-endian CPUs.
3512
3513 // The word is already stored correctly in the argument
3514 *(l_fp - asBC_SWORDARG0(l_bc)) = asBC_DWORDARG(l_bc);
3515 l_bc += 2;
3516 break;
3517
3518 case asBC_Cast:
3519 // Cast the handle at the top of the stack to the type in the argument
3520 {
3521 asDWORD **a = (asDWORD**)*(asPWORD*)l_sp;
3522 if( a && *a )
3523 {
3524 asDWORD typeId = asBC_DWORDARG(l_bc);
3525
3526 asCScriptObject *obj = (asCScriptObject *)* a;
3527 asCObjectType *objType = obj->objType;
3528 asCObjectType *to = m_engine->GetObjectTypeFromTypeId(typeId);
3529
3530 // This instruction can only be used with script classes and interfaces
3531 asASSERT( objType->flags & asOBJ_SCRIPT_OBJECT );
3532 asASSERT( to->flags & asOBJ_SCRIPT_OBJECT );
3533
3534 if( objType->Implements(to) || objType->DerivesFrom(to) )
3535 {
3536 m_regs.objectType = 0;
3537 m_regs.objectRegister = obj;
3538 obj->AddRef();
3539 }
3540 else
3541 {
3542 // The object register should already be null, so there
3543 // is no need to clear it if the cast is unsuccessful
3544 asASSERT( m_regs.objectRegister == 0 );
3545 }
3546 }
3547 l_sp += AS_PTR_SIZE;
3548 }
3549 l_bc += 2;
3550 break;
3551
3552 case asBC_i64TOi:
3553 *(l_fp - asBC_SWORDARG0(l_bc)) = int(*(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)));
3554 l_bc += 2;
3555 break;
3556
3557 case asBC_uTOi64:
3558 *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = asINT64(*(asUINT*)(l_fp - asBC_SWORDARG1(l_bc)));
3559 l_bc += 2;
3560 break;
3561
3562 case asBC_iTOi64:
3563 *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = asINT64(*(int*)(l_fp - asBC_SWORDARG1(l_bc)));
3564 l_bc += 2;
3565 break;
3566
3567 case asBC_fTOi64:
3568 *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = asINT64(*(float*)(l_fp - asBC_SWORDARG1(l_bc)));
3569 l_bc += 2;
3570 break;
3571
3572 case asBC_dTOi64:
3573 *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = asINT64(*(double*)(l_fp - asBC_SWORDARG0(l_bc)));
3574 l_bc++;
3575 break;
3576
3577 case asBC_fTOu64:
3578 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = asQWORD(asINT64(*(float*)(l_fp - asBC_SWORDARG1(l_bc))));
3579 l_bc += 2;
3580 break;
3581
3582 case asBC_dTOu64:
3583 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = asQWORD(asINT64(*(double*)(l_fp - asBC_SWORDARG0(l_bc))));
3584 l_bc++;
3585 break;
3586
3587 case asBC_i64TOf:
3588 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)));
3589 l_bc += 2;
3590 break;
3591
3592 case asBC_u64TOf:
3593 #if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC6
3594 {
3595 // MSVC6 doesn't permit UINT64 to double
3596 asINT64 v = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc));
3597 if( v < 0 )
3598 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = 18446744073709551615.0f+float(v);
3599 else
3600 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(v);
3601 }
3602 #else
3603 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = float(*(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)));
3604 #endif
3605 l_bc += 2;
3606 break;
3607
3608 case asBC_i64TOd:
3609 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)));
3610 l_bc++;
3611 break;
3612
3613 case asBC_u64TOd:
3614 #if defined(_MSC_VER) && _MSC_VER <= 1200 // MSVC6
3615 {
3616 // MSVC6 doesn't permit UINT64 to double
3617 asINT64 v = *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc));
3618 if( v < 0 )
3619 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = 18446744073709551615.0+double(v);
3620 else
3621 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(v);
3622 }
3623 #else
3624 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = double(*(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)));
3625 #endif
3626 l_bc++;
3627 break;
3628
3629 case asBC_NEGi64:
3630 *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = -*(asINT64*)(l_fp - asBC_SWORDARG0(l_bc));
3631 l_bc++;
3632 break;
3633
3634 case asBC_INCi64:
3635 ++(**(asQWORD**)&m_regs.valueRegister);
3636 l_bc++;
3637 break;
3638
3639 case asBC_DECi64:
3640 --(**(asQWORD**)&m_regs.valueRegister);
3641 l_bc++;
3642 break;
3643
3644 case asBC_BNOT64:
3645 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = ~*(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc));
3646 l_bc++;
3647 break;
3648
3649 case asBC_ADDi64:
3650 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) + *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc));
3651 l_bc += 2;
3652 break;
3653
3654 case asBC_SUBi64:
3655 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) - *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc));
3656 l_bc += 2;
3657 break;
3658
3659 case asBC_MULi64:
3660 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) * *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc));
3661 l_bc += 2;
3662 break;
3663
3664 case asBC_DIVi64:
3665 {
3666 asINT64 divider = *(asINT64*)(l_fp - asBC_SWORDARG2(l_bc));
3667 if( divider == 0 )
3668 {
3669 // Need to move the values back to the context
3670 m_regs.programPointer = l_bc;
3671 m_regs.stackPointer = l_sp;
3672 m_regs.stackFramePointer = l_fp;
3673
3674 // Raise exception
3675 SetInternalException(TXT_DIVIDE_BY_ZERO);
3676 return;
3677 }
3678 else if( divider == -1 )
3679 {
3680 // Need to check if the value that is divided is 1<<63
3681 // as dividing it with -1 will cause an overflow exception
3682 if( *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)) == (asINT64(1)<<63) )
3683 {
3684 // Need to move the values back to the context
3685 m_regs.programPointer = l_bc;
3686 m_regs.stackPointer = l_sp;
3687 m_regs.stackFramePointer = l_fp;
3688
3689 // Raise exception
3690 SetInternalException(TXT_DIVIDE_OVERFLOW);
3691 return;
3692 }
3693 }
3694
3695 *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)) / divider;
3696 }
3697 l_bc += 2;
3698 break;
3699
3700 case asBC_MODi64:
3701 {
3702 asINT64 divider = *(asINT64*)(l_fp - asBC_SWORDARG2(l_bc));
3703 if( divider == 0 )
3704 {
3705 // Need to move the values back to the context
3706 m_regs.programPointer = l_bc;
3707 m_regs.stackPointer = l_sp;
3708 m_regs.stackFramePointer = l_fp;
3709
3710 // Raise exception
3711 SetInternalException(TXT_DIVIDE_BY_ZERO);
3712 return;
3713 }
3714 else if( divider == -1 )
3715 {
3716 // Need to check if the value that is divided is 1<<63
3717 // as dividing it with -1 will cause an overflow exception
3718 if( *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)) == (asINT64(1)<<63) )
3719 {
3720 // Need to move the values back to the context
3721 m_regs.programPointer = l_bc;
3722 m_regs.stackPointer = l_sp;
3723 m_regs.stackFramePointer = l_fp;
3724
3725 // Raise exception
3726 SetInternalException(TXT_DIVIDE_OVERFLOW);
3727 return;
3728 }
3729 }
3730 *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)) % divider;
3731 }
3732 l_bc += 2;
3733 break;
3734
3735 case asBC_BAND64:
3736 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) & *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc));
3737 l_bc += 2;
3738 break;
3739
3740 case asBC_BOR64:
3741 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) | *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc));
3742 l_bc += 2;
3743 break;
3744
3745 case asBC_BXOR64:
3746 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) ^ *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc));
3747 l_bc += 2;
3748 break;
3749
3750 case asBC_BSLL64:
3751 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) << *(l_fp - asBC_SWORDARG2(l_bc));
3752 l_bc += 2;
3753 break;
3754
3755 case asBC_BSRL64:
3756 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) >> *(l_fp - asBC_SWORDARG2(l_bc));
3757 l_bc += 2;
3758 break;
3759
3760 case asBC_BSRA64:
3761 *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)) >> *(l_fp - asBC_SWORDARG2(l_bc));
3762 l_bc += 2;
3763 break;
3764
3765 case asBC_CMPi64:
3766 {
3767 asINT64 i1 = *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc));
3768 asINT64 i2 = *(asINT64*)(l_fp - asBC_SWORDARG1(l_bc));
3769 if( i1 == i2 ) *(int*)&m_regs.valueRegister = 0;
3770 else if( i1 < i2 ) *(int*)&m_regs.valueRegister = -1;
3771 else *(int*)&m_regs.valueRegister = 1;
3772 l_bc += 2;
3773 }
3774 break;
3775
3776 case asBC_CMPu64:
3777 {
3778 asQWORD d1 = *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc));
3779 asQWORD d2 = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc));
3780 if( d1 == d2 ) *(int*)&m_regs.valueRegister = 0;
3781 else if( d1 < d2 ) *(int*)&m_regs.valueRegister = -1;
3782 else *(int*)&m_regs.valueRegister = 1;
3783 l_bc += 2;
3784 }
3785 break;
3786
3787 case asBC_ChkNullS:
3788 {
3789 // Verify if the pointer on the stack is null
3790 // This is used for example when validating handles passed as function arguments
3791 asPWORD a = *(asPWORD*)(l_sp + asBC_WORDARG0(l_bc));
3792 if( a == 0 )
3793 {
3794 m_regs.programPointer = l_bc;
3795 m_regs.stackPointer = l_sp;
3796 m_regs.stackFramePointer = l_fp;
3797
3798 SetInternalException(TXT_NULL_POINTER_ACCESS);
3799 return;
3800 }
3801 }
3802 l_bc++;
3803 break;
3804
3805 case asBC_ClrHi:
3806 #if AS_SIZEOF_BOOL == 1
3807 {
3808 // Clear the upper bytes, so that trash data don't interfere with boolean operations
3809
3810 // We need to use volatile here to tell the compiler it cannot
3811 // change the order of read and write operations on the pointer.
3812
3813 volatile asBYTE *ptr = (asBYTE*)&m_regs.valueRegister;
3814 ptr[1] = 0; // The boolean value is stored in the lower byte, so we clear the rest
3815 ptr[2] = 0;
3816 ptr[3] = 0;
3817 }
3818 #else
3819 // We don't have anything to do here
3820 #endif
3821 l_bc++;
3822 break;
3823
3824 case asBC_JitEntry:
3825 {
3826 if( m_currentFunction->scriptData->jitFunction )
3827 {
3828 asPWORD jitArg = asBC_PTRARG(l_bc);
3829
3830 if( jitArg )
3831 {
3832 // Resume JIT operation
3833 m_regs.programPointer = l_bc;
3834 m_regs.stackPointer = l_sp;
3835 m_regs.stackFramePointer = l_fp;
3836
3837 (m_currentFunction->scriptData->jitFunction)(&m_regs, jitArg);
3838
3839 l_bc = m_regs.programPointer;
3840 l_sp = m_regs.stackPointer;
3841 l_fp = m_regs.stackFramePointer;
3842
3843 // If status isn't active anymore then we must stop
3844 if( m_status != asEXECUTION_ACTIVE )
3845 return;
3846
3847 break;
3848 }
3849 }
3850
3851 // Not a JIT resume point, treat as nop
3852 l_bc += 1+AS_PTR_SIZE;
3853 }
3854 break;
3855
3856 case asBC_CallPtr:
3857 {
3858 // Get the function pointer from the local variable
3859 asCScriptFunction *func = *(asCScriptFunction**)(l_fp - asBC_SWORDARG0(l_bc));
3860
3861 // Need to move the values back to the context
3862 m_regs.programPointer = l_bc;
3863 m_regs.stackPointer = l_sp;
3864 m_regs.stackFramePointer = l_fp;
3865
3866 if( func == 0 )
3867 {
3868 // Need to update the program pointer anyway for the exception handler
3869 m_regs.programPointer++;
3870
3871 // Tell the exception handler to clean up the arguments to this method
3872 m_needToCleanupArgs = true;
3873
3874 // TODO: funcdef: Should we have a different exception string?
3875 SetInternalException(TXT_UNBOUND_FUNCTION);
3876 return;
3877 }
3878 else
3879 {
3880 if( func->funcType == asFUNC_SCRIPT )
3881 {
3882 m_regs.programPointer++;
3883 CallScriptFunction(func);
3884 }
3885 else if( func->funcType == asFUNC_DELEGATE )
3886 {
3887 // Push the object pointer on the stack. There is always a reserved space for this so
3888 // we don't don't need to worry about overflowing the allocated memory buffer
3889 asASSERT( m_regs.stackPointer - AS_PTR_SIZE >= m_stackBlocks[m_stackIndex] );
3890 m_regs.stackPointer -= AS_PTR_SIZE;
3891 *(asPWORD*)m_regs.stackPointer = asPWORD(func->objForDelegate);
3892
3893 // Call the delegated method
3894 if( func->funcForDelegate->funcType == asFUNC_SYSTEM )
3895 {
3896 m_regs.stackPointer += CallSystemFunction(func->funcForDelegate->id, this);
3897
3898 // Update program position after the call so the line number
3899 // is correct in case the system function queries it
3900 m_regs.programPointer++;
3901 }
3902 else
3903 {
3904 m_regs.programPointer++;
3905
3906 // TODO: run-time optimize: The true method could be figured out when creating the delegate
3907 CallInterfaceMethod(func->funcForDelegate);
3908 }
3909 }
3910 else
3911 {
3912 asASSERT( func->funcType == asFUNC_SYSTEM );
3913
3914 m_regs.stackPointer += CallSystemFunction(func->id, this);
3915
3916 // Update program position after the call so the line number
3917 // is correct in case the system function queries it
3918 m_regs.programPointer++;
3919 }
3920 }
3921
3922 // Extract the values from the context again
3923 l_bc = m_regs.programPointer;
3924 l_sp = m_regs.stackPointer;
3925 l_fp = m_regs.stackFramePointer;
3926
3927 // If status isn't active anymore then we must stop
3928 if( m_status != asEXECUTION_ACTIVE )
3929 return;
3930 }
3931 break;
3932
3933 case asBC_FuncPtr:
3934 // Push the function pointer on the stack. The pointer is in the argument
3935 l_sp -= AS_PTR_SIZE;
3936 *(asPWORD*)l_sp = asBC_PTRARG(l_bc);
3937 l_bc += 1+AS_PTR_SIZE;
3938 break;
3939
3940 case asBC_LoadThisR:
3941 {
3942 // PshVPtr 0
3943 asPWORD tmp = *(asPWORD*)l_fp;
3944
3945 // Make sure the pointer is not null
3946 if( tmp == 0 )
3947 {
3948 // Need to move the values back to the context
3949 m_regs.programPointer = l_bc;
3950 m_regs.stackPointer = l_sp;
3951 m_regs.stackFramePointer = l_fp;
3952
3953 // Raise exception
3954 SetInternalException(TXT_NULL_POINTER_ACCESS);
3955 return;
3956 }
3957
3958 // ADDSi
3959 tmp = tmp + asBC_SWORDARG0(l_bc);
3960
3961 // PopRPtr
3962 *(asPWORD*)&m_regs.valueRegister = tmp;
3963 l_bc += 2;
3964 }
3965 break;
3966
3967 // Push the qword value of a variable on the stack
3968 case asBC_PshV8:
3969 l_sp -= 2;
3970 *(asQWORD*)l_sp = *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc));
3971 l_bc++;
3972 break;
3973
3974 case asBC_DIVu:
3975 {
3976 asUINT divider = *(asUINT*)(l_fp - asBC_SWORDARG2(l_bc));
3977 if( divider == 0 )
3978 {
3979 // Need to move the values back to the context
3980 m_regs.programPointer = l_bc;
3981 m_regs.stackPointer = l_sp;
3982 m_regs.stackFramePointer = l_fp;
3983
3984 // Raise exception
3985 SetInternalException(TXT_DIVIDE_BY_ZERO);
3986 return;
3987 }
3988 *(asUINT*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asUINT*)(l_fp - asBC_SWORDARG1(l_bc)) / divider;
3989 }
3990 l_bc += 2;
3991 break;
3992
3993 case asBC_MODu:
3994 {
3995 asUINT divider = *(asUINT*)(l_fp - asBC_SWORDARG2(l_bc));
3996 if( divider == 0 )
3997 {
3998 // Need to move the values back to the context
3999 m_regs.programPointer = l_bc;
4000 m_regs.stackPointer = l_sp;
4001 m_regs.stackFramePointer = l_fp;
4002
4003 // Raise exception
4004 SetInternalException(TXT_DIVIDE_BY_ZERO);
4005 return;
4006 }
4007 *(asUINT*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asUINT*)(l_fp - asBC_SWORDARG1(l_bc)) % divider;
4008 }
4009 l_bc += 2;
4010 break;
4011
4012 case asBC_DIVu64:
4013 {
4014 asQWORD divider = *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc));
4015 if( divider == 0 )
4016 {
4017 // Need to move the values back to the context
4018 m_regs.programPointer = l_bc;
4019 m_regs.stackPointer = l_sp;
4020 m_regs.stackFramePointer = l_fp;
4021
4022 // Raise exception
4023 SetInternalException(TXT_DIVIDE_BY_ZERO);
4024 return;
4025 }
4026 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) / divider;
4027 }
4028 l_bc += 2;
4029 break;
4030
4031 case asBC_MODu64:
4032 {
4033 asQWORD divider = *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc));
4034 if( divider == 0 )
4035 {
4036 // Need to move the values back to the context
4037 m_regs.programPointer = l_bc;
4038 m_regs.stackPointer = l_sp;
4039 m_regs.stackFramePointer = l_fp;
4040
4041 // Raise exception
4042 SetInternalException(TXT_DIVIDE_BY_ZERO);
4043 return;
4044 }
4045 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = *(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)) % divider;
4046 }
4047 l_bc += 2;
4048 break;
4049
4050 case asBC_LoadRObjR:
4051 {
4052 // PshVPtr x
4053 asPWORD tmp = *(asPWORD*)(l_fp - asBC_SWORDARG0(l_bc));
4054
4055 // Make sure the pointer is not null
4056 if( tmp == 0 )
4057 {
4058 // Need to move the values back to the context
4059 m_regs.programPointer = l_bc;
4060 m_regs.stackPointer = l_sp;
4061 m_regs.stackFramePointer = l_fp;
4062
4063 // Raise exception
4064 SetInternalException(TXT_NULL_POINTER_ACCESS);
4065 return;
4066 }
4067
4068 // ADDSi y
4069 tmp = tmp + asBC_SWORDARG1(l_bc);
4070
4071 // PopRPtr
4072 *(asPWORD*)&m_regs.valueRegister = tmp;
4073 l_bc += 3;
4074 }
4075 break;
4076
4077 case asBC_LoadVObjR:
4078 {
4079 // PSF x
4080 asPWORD tmp = (asPWORD)(l_fp - asBC_SWORDARG0(l_bc));
4081
4082 // ADDSi y
4083 tmp = tmp + asBC_SWORDARG1(l_bc);
4084
4085 // PopRPtr
4086 *(asPWORD*)&m_regs.valueRegister = tmp;
4087 l_bc += 3;
4088 }
4089 break;
4090
4091 case asBC_RefCpyV:
4092 // Same as PSF v, REFCPY
4093 {
4094 asCObjectType *objType = (asCObjectType*)asBC_PTRARG(l_bc);
4095 asSTypeBehaviour *beh = &objType->beh;
4096
4097 // Determine destination from argument
4098 void **d = (void**)asPWORD(l_fp - asBC_SWORDARG0(l_bc));
4099
4100 // Read wanted pointer from the stack
4101 void *s = (void*)*(asPWORD*)l_sp;
4102
4103 // Need to move the values back to the context as the called functions
4104 // may use the debug interface to inspect the registers
4105 m_regs.programPointer = l_bc;
4106 m_regs.stackPointer = l_sp;
4107 m_regs.stackFramePointer = l_fp;
4108
4109 if( !(objType->flags & asOBJ_NOCOUNT) )
4110 {
4111 // Release previous object held by destination pointer
4112 if( *d != 0 )
4113 m_engine->CallObjectMethod(*d, beh->release);
4114 // Increase ref counter of wanted object
4115 if( s != 0 )
4116 m_engine->CallObjectMethod(s, beh->addref);
4117 }
4118
4119 // Set the new object in the destination
4120 *d = s;
4121 }
4122 l_bc += 1+AS_PTR_SIZE;
4123 break;
4124
4125 case asBC_JLowZ:
4126 if( *(asBYTE*)&m_regs.valueRegister == 0 )
4127 l_bc += asBC_INTARG(l_bc) + 2;
4128 else
4129 l_bc += 2;
4130 break;
4131
4132 case asBC_JLowNZ:
4133 if( *(asBYTE*)&m_regs.valueRegister != 0 )
4134 l_bc += asBC_INTARG(l_bc) + 2;
4135 else
4136 l_bc += 2;
4137 break;
4138
4139 case asBC_AllocMem:
4140 // Allocate a buffer and store the pointer in the local variable
4141 {
4142 // TODO: runtime optimize: As the list buffers are going to be short lived, it may be interesting
4143 // to use a memory pool to avoid reallocating the memory all the time
4144
4145 asUINT size = asBC_DWORDARG(l_bc);
4146 asBYTE **var = (asBYTE**)(l_fp - asBC_SWORDARG0(l_bc));
4147 #ifndef WIP_16BYTE_ALIGN
4148 *var = asNEWARRAY(asBYTE, size);
4149 #else
4150 *var = asNEWARRAYALIGNED(asBYTE, size, MAX_TYPE_ALIGNMENT);
4151 #endif
4152
4153 // Clear the buffer for the pointers that will be placed in it
4154 memset(*var, 0, size);
4155 }
4156 l_bc += 2;
4157 break;
4158
4159 case asBC_SetListSize:
4160 {
4161 // Set the size element in the buffer
4162 asBYTE *var = *(asBYTE**)(l_fp - asBC_SWORDARG0(l_bc));
4163 asUINT off = asBC_DWORDARG(l_bc);
4164 asUINT size = asBC_DWORDARG(l_bc+1);
4165
4166 asASSERT( var );
4167
4168 *(asUINT*)(var+off) = size;
4169 }
4170 l_bc += 3;
4171 break;
4172
4173 case asBC_PshListElmnt:
4174 {
4175 // Push the pointer to the list element on the stack
4176 // In essence it does the same as PSF, RDSPtr, ADDSi
4177 asBYTE *var = *(asBYTE**)(l_fp - asBC_SWORDARG0(l_bc));
4178 asUINT off = asBC_DWORDARG(l_bc);
4179
4180 asASSERT( var );
4181
4182 l_sp -= AS_PTR_SIZE;
4183 *(asPWORD*)l_sp = asPWORD(var+off);
4184 }
4185 l_bc += 2;
4186 break;
4187
4188 case asBC_SetListType:
4189 {
4190 // Set the type id in the buffer
4191 asBYTE *var = *(asBYTE**)(l_fp - asBC_SWORDARG0(l_bc));
4192 asUINT off = asBC_DWORDARG(l_bc);
4193 asUINT type = asBC_DWORDARG(l_bc+1);
4194
4195 asASSERT( var );
4196
4197 *(asUINT*)(var+off) = type;
4198 }
4199 l_bc += 3;
4200 break;
4201
4202 //------------------------------
4203 // Exponent operations
4204 case asBC_POWi:
4205 {
4206 bool isOverflow;
4207 *(int*)(l_fp - asBC_SWORDARG0(l_bc)) = as_powi(*(int*)(l_fp - asBC_SWORDARG1(l_bc)), *(int*)(l_fp - asBC_SWORDARG2(l_bc)), isOverflow);
4208 if( isOverflow )
4209 {
4210 // Need to move the values back to the context
4211 m_regs.programPointer = l_bc;
4212 m_regs.stackPointer = l_sp;
4213 m_regs.stackFramePointer = l_fp;
4214
4215 // Raise exception
4216 SetInternalException(TXT_POW_OVERFLOW);
4217 return;
4218 }
4219 }
4220 l_bc += 2;
4221 break;
4222
4223 case asBC_POWu:
4224 {
4225 bool isOverflow;
4226 *(asDWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = as_powu(*(asDWORD*)(l_fp - asBC_SWORDARG1(l_bc)), *(asDWORD*)(l_fp - asBC_SWORDARG2(l_bc)), isOverflow);
4227 if( isOverflow )
4228 {
4229 // Need to move the values back to the context
4230 m_regs.programPointer = l_bc;
4231 m_regs.stackPointer = l_sp;
4232 m_regs.stackFramePointer = l_fp;
4233
4234 // Raise exception
4235 SetInternalException(TXT_POW_OVERFLOW);
4236 return;
4237 }
4238 }
4239 l_bc += 2;
4240 break;
4241
4242 case asBC_POWf:
4243 {
4244 float r = powf(*(float*)(l_fp - asBC_SWORDARG1(l_bc)), *(float*)(l_fp - asBC_SWORDARG2(l_bc)));
4245 *(float*)(l_fp - asBC_SWORDARG0(l_bc)) = r;
4246 if( r == float(HUGE_VAL) )
4247 {
4248 // Need to move the values back to the context
4249 m_regs.programPointer = l_bc;
4250 m_regs.stackPointer = l_sp;
4251 m_regs.stackFramePointer = l_fp;
4252
4253 // Raise exception
4254 SetInternalException(TXT_POW_OVERFLOW);
4255 return;
4256 }
4257 }
4258 l_bc += 2;
4259 break;
4260
4261 case asBC_POWd:
4262 {
4263 double r = pow(*(double*)(l_fp - asBC_SWORDARG1(l_bc)), *(double*)(l_fp - asBC_SWORDARG2(l_bc)));
4264 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = r;
4265 if( r == HUGE_VAL )
4266 {
4267 // Need to move the values back to the context
4268 m_regs.programPointer = l_bc;
4269 m_regs.stackPointer = l_sp;
4270 m_regs.stackFramePointer = l_fp;
4271
4272 // Raise exception
4273 SetInternalException(TXT_POW_OVERFLOW);
4274 return;
4275 }
4276 }
4277 l_bc += 2;
4278 break;
4279
4280 case asBC_POWdi:
4281 {
4282 double r = pow(*(double*)(l_fp - asBC_SWORDARG1(l_bc)), *(int*)(l_fp - asBC_SWORDARG2(l_bc)));
4283 *(double*)(l_fp - asBC_SWORDARG0(l_bc)) = r;
4284 if( r == HUGE_VAL )
4285 {
4286 // Need to move the values back to the context
4287 m_regs.programPointer = l_bc;
4288 m_regs.stackPointer = l_sp;
4289 m_regs.stackFramePointer = l_fp;
4290
4291 // Raise exception
4292 SetInternalException(TXT_POW_OVERFLOW);
4293 return;
4294 }
4295 l_bc += 2;
4296 }
4297 break;
4298
4299 case asBC_POWi64:
4300 {
4301 bool isOverflow;
4302 *(asINT64*)(l_fp - asBC_SWORDARG0(l_bc)) = as_powi64(*(asINT64*)(l_fp - asBC_SWORDARG1(l_bc)), *(asINT64*)(l_fp - asBC_SWORDARG2(l_bc)), isOverflow);
4303 if( isOverflow )
4304 {
4305 // Need to move the values back to the context
4306 m_regs.programPointer = l_bc;
4307 m_regs.stackPointer = l_sp;
4308 m_regs.stackFramePointer = l_fp;
4309
4310 // Raise exception
4311 SetInternalException(TXT_POW_OVERFLOW);
4312 return;
4313 }
4314 }
4315 l_bc += 2;
4316 break;
4317
4318 case asBC_POWu64:
4319 {
4320 bool isOverflow;
4321 *(asQWORD*)(l_fp - asBC_SWORDARG0(l_bc)) = as_powu64(*(asQWORD*)(l_fp - asBC_SWORDARG1(l_bc)), *(asQWORD*)(l_fp - asBC_SWORDARG2(l_bc)), isOverflow);
4322 if( isOverflow )
4323 {
4324 // Need to move the values back to the context
4325 m_regs.programPointer = l_bc;
4326 m_regs.stackPointer = l_sp;
4327 m_regs.stackFramePointer = l_fp;
4328
4329 // Raise exception
4330 SetInternalException(TXT_POW_OVERFLOW);
4331 return;
4332 }
4333 }
4334 l_bc += 2;
4335 break;
4336 case asBC_Thiscall1:
4337 // This instruction is a faster version of asBC_CALLSYS. It is faster because
4338 // it has much less runtime overhead with determining the calling convention
4339 // and no dynamic code for loading the parameters. The instruction can only
4340 // be used to call functions with the following signatures:
4341 //
4342 // type &obj::func(int)
4343 // type &obj::func(uint)
4344 // void obj::func(int)
4345 // void obj::func(uint)
4346 {
4347 // Get function ID from the argument
4348 int i = asBC_INTARG(l_bc);
4349
4350 // Need to move the values back to the context as the called functions
4351 // may use the debug interface to inspect the registers
4352 m_regs.programPointer = l_bc;
4353 m_regs.stackPointer = l_sp;
4354 m_regs.stackFramePointer = l_fp;
4355
4356 // Pop the thispointer from the stack
4357 void *obj = *(void**)l_sp;
4358 if (obj == 0)
4359 SetInternalException(TXT_NULL_POINTER_ACCESS);
4360 else
4361 {
4362 // Only update the stack pointer if all is OK so the
4363 // exception handler can properly clean up the stack
4364 l_sp += AS_PTR_SIZE;
4365
4366 // Pop the int arg from the stack
4367 int arg = *(int*)l_sp;
4368 l_sp++;
4369
4370 // Call the method
4371 m_callingSystemFunction = m_engine->scriptFunctions[i];
4372 void *ptr = m_engine->CallObjectMethodRetPtr(obj, arg, m_callingSystemFunction);
4373 m_callingSystemFunction = 0;
4374 *(asPWORD*)&m_regs.valueRegister = (asPWORD)ptr;
4375 }
4376
4377 // Update the program position after the call so that line number is correct
4378 l_bc += 2;
4379
4380 if( m_regs.doProcessSuspend )
4381 {
4382 // Should the execution be suspended?
4383 if( m_doSuspend )
4384 {
4385 m_regs.programPointer = l_bc;
4386 m_regs.stackPointer = l_sp;
4387 m_regs.stackFramePointer = l_fp;
4388
4389 m_status = asEXECUTION_SUSPENDED;
4390 return;
4391 }
4392 // An exception might have been raised
4393 if( m_status != asEXECUTION_ACTIVE )
4394 {
4395 m_regs.programPointer = l_bc;
4396 m_regs.stackPointer = l_sp;
4397 m_regs.stackFramePointer = l_fp;
4398
4399 return;
4400 }
4401 }
4402 }
4403 break;
4404
4405 // Don't let the optimizer optimize for size,
4406 // since it requires extra conditions and jumps
4407 case 201: l_bc = (asDWORD*)201; break;
4408 case 202: l_bc = (asDWORD*)202; break;
4409 case 203: l_bc = (asDWORD*)203; break;
4410 case 204: l_bc = (asDWORD*)204; break;
4411 case 205: l_bc = (asDWORD*)205; break;
4412 case 206: l_bc = (asDWORD*)206; break;
4413 case 207: l_bc = (asDWORD*)207; break;
4414 case 208: l_bc = (asDWORD*)208; break;
4415 case 209: l_bc = (asDWORD*)209; break;
4416 case 210: l_bc = (asDWORD*)210; break;
4417 case 211: l_bc = (asDWORD*)211; break;
4418 case 212: l_bc = (asDWORD*)212; break;
4419 case 213: l_bc = (asDWORD*)213; break;
4420 case 214: l_bc = (asDWORD*)214; break;
4421 case 215: l_bc = (asDWORD*)215; break;
4422 case 216: l_bc = (asDWORD*)216; break;
4423 case 217: l_bc = (asDWORD*)217; break;
4424 case 218: l_bc = (asDWORD*)218; break;
4425 case 219: l_bc = (asDWORD*)219; break;
4426 case 220: l_bc = (asDWORD*)220; break;
4427 case 221: l_bc = (asDWORD*)221; break;
4428 case 222: l_bc = (asDWORD*)222; break;
4429 case 223: l_bc = (asDWORD*)223; break;
4430 case 224: l_bc = (asDWORD*)224; break;
4431 case 225: l_bc = (asDWORD*)225; break;
4432 case 226: l_bc = (asDWORD*)226; break;
4433 case 227: l_bc = (asDWORD*)227; break;
4434 case 228: l_bc = (asDWORD*)228; break;
4435 case 229: l_bc = (asDWORD*)229; break;
4436 case 230: l_bc = (asDWORD*)230; break;
4437 case 231: l_bc = (asDWORD*)231; break;
4438 case 232: l_bc = (asDWORD*)232; break;
4439 case 233: l_bc = (asDWORD*)233; break;
4440 case 234: l_bc = (asDWORD*)234; break;
4441 case 235: l_bc = (asDWORD*)235; break;
4442 case 236: l_bc = (asDWORD*)236; break;
4443 case 237: l_bc = (asDWORD*)237; break;
4444 case 238: l_bc = (asDWORD*)238; break;
4445 case 239: l_bc = (asDWORD*)239; break;
4446 case 240: l_bc = (asDWORD*)240; break;
4447 case 241: l_bc = (asDWORD*)241; break;
4448 case 242: l_bc = (asDWORD*)242; break;
4449 case 243: l_bc = (asDWORD*)243; break;
4450 case 244: l_bc = (asDWORD*)244; break;
4451 case 245: l_bc = (asDWORD*)245; break;
4452 case 246: l_bc = (asDWORD*)246; break;
4453 case 247: l_bc = (asDWORD*)247; break;
4454 case 248: l_bc = (asDWORD*)248; break;
4455 case 249: l_bc = (asDWORD*)249; break;
4456 case 250: l_bc = (asDWORD*)250; break;
4457 case 251: l_bc = (asDWORD*)251; break;
4458 case 252: l_bc = (asDWORD*)252; break;
4459 case 253: l_bc = (asDWORD*)253; break;
4460 case 254: l_bc = (asDWORD*)254; break;
4461 case 255: l_bc = (asDWORD*)255; break;
4462
4463 #ifdef AS_DEBUG
4464 default:
4465 asASSERT(false);
4466 SetInternalException(TXT_UNRECOGNIZED_BYTE_CODE);
4467 #endif
4468 #if defined(_MSC_VER) && !defined(AS_DEBUG)
4469 default:
4470 // This Microsoft specific code allows the
4471 // compiler to optimize the switch case as
4472 // it will know that the code will never
4473 // reach this point
4474 __assume(0);
4475 #endif
4476 }
4477
4478 #ifdef AS_DEBUG
4479 asDWORD instr = *(asBYTE*)old;
4480 if( instr != asBC_JMP && instr != asBC_JMPP && (instr < asBC_JZ || instr > asBC_JNP) && instr != asBC_JLowZ && instr != asBC_JLowNZ &&
4481 instr != asBC_CALL && instr != asBC_CALLBND && instr != asBC_CALLINTF && instr != asBC_RET && instr != asBC_ALLOC && instr != asBC_CallPtr &&
4482 instr != asBC_JitEntry )
4483 {
4484 asASSERT( (l_bc - old) == asBCTypeSize[asBCInfo[instr].type] );
4485 }
4486 #endif
4487 }
4488 }
4489
SetException(const char * descr)4490 int asCContext::SetException(const char *descr)
4491 {
4492 // Only allow this if we're executing a CALL byte code
4493 if( m_callingSystemFunction == 0 ) return asERROR;
4494
4495 SetInternalException(descr);
4496
4497 return 0;
4498 }
4499
SetInternalException(const char * descr)4500 void asCContext::SetInternalException(const char *descr)
4501 {
4502 if( m_inExceptionHandler )
4503 {
4504 asASSERT(false); // Shouldn't happen
4505 return; // but if it does, at least this will not crash the application
4506 }
4507
4508 m_status = asEXECUTION_EXCEPTION;
4509 m_regs.doProcessSuspend = true;
4510
4511 m_exceptionString = descr;
4512 m_exceptionFunction = m_currentFunction->id;
4513
4514 if( m_currentFunction->scriptData )
4515 {
4516 m_exceptionLine = m_currentFunction->GetLineNumber(int(m_regs.programPointer - m_currentFunction->scriptData->byteCode.AddressOf()), &m_exceptionSectionIdx);
4517 m_exceptionColumn = m_exceptionLine >> 20;
4518 m_exceptionLine &= 0xFFFFF;
4519 }
4520 else
4521 {
4522 m_exceptionSectionIdx = 0;
4523 m_exceptionLine = 0;
4524 m_exceptionColumn = 0;
4525 }
4526
4527 if( m_exceptionCallback )
4528 CallExceptionCallback();
4529 }
4530
CleanReturnObject()4531 void asCContext::CleanReturnObject()
4532 {
4533 if( m_initialFunction && m_initialFunction->DoesReturnOnStack() && m_status == asEXECUTION_FINISHED )
4534 {
4535 // If function returns on stack we need to call the destructor on the returned object
4536 if(CastToObjectType(m_initialFunction->returnType.GetTypeInfo())->beh.destruct )
4537 m_engine->CallObjectMethod(GetReturnObject(), CastToObjectType(m_initialFunction->returnType.GetTypeInfo())->beh.destruct);
4538
4539 return;
4540 }
4541
4542 if( m_regs.objectRegister == 0 ) return;
4543
4544 asASSERT( m_regs.objectType != 0 );
4545
4546 if( m_regs.objectType )
4547 {
4548 if (m_regs.objectType->GetFlags() & asOBJ_FUNCDEF)
4549 {
4550 // Release the function pointer
4551 reinterpret_cast<asIScriptFunction*>(m_regs.objectRegister)->Release();
4552 m_regs.objectRegister = 0;
4553 }
4554 else
4555 {
4556 // Call the destructor on the object
4557 asSTypeBehaviour *beh = &(CastToObjectType(reinterpret_cast<asCTypeInfo*>(m_regs.objectType))->beh);
4558 if (m_regs.objectType->GetFlags() & asOBJ_REF)
4559 {
4560 asASSERT(beh->release || (m_regs.objectType->GetFlags() & asOBJ_NOCOUNT));
4561
4562 if (beh->release)
4563 m_engine->CallObjectMethod(m_regs.objectRegister, beh->release);
4564
4565 m_regs.objectRegister = 0;
4566 }
4567 else
4568 {
4569 if (beh->destruct)
4570 m_engine->CallObjectMethod(m_regs.objectRegister, beh->destruct);
4571
4572 // Free the memory
4573 m_engine->CallFree(m_regs.objectRegister);
4574 m_regs.objectRegister = 0;
4575 }
4576 }
4577 }
4578 }
4579
CleanStack()4580 void asCContext::CleanStack()
4581 {
4582 m_inExceptionHandler = true;
4583
4584 // Run the clean up code for each of the functions called
4585 CleanStackFrame();
4586
4587 // Set the status to exception so that the stack unwind is done correctly.
4588 // This shouldn't be done for the current function, which is why we only
4589 // do this after the first CleanStackFrame() is done.
4590 m_status = asEXECUTION_EXCEPTION;
4591
4592 while( m_callStack.GetLength() > 0 )
4593 {
4594 // Only clean up until the top most marker for a nested call
4595 asPWORD *s = m_callStack.AddressOf() + m_callStack.GetLength() - CALLSTACK_FRAME_SIZE;
4596 if( s[0] == 0 )
4597 break;
4598
4599 PopCallState();
4600
4601 CleanStackFrame();
4602 }
4603
4604 m_inExceptionHandler = false;
4605 }
4606
4607 // Interface
IsVarInScope(asUINT varIndex,asUINT stackLevel)4608 bool asCContext::IsVarInScope(asUINT varIndex, asUINT stackLevel)
4609 {
4610 // Don't return anything if there is no bytecode, e.g. before calling Execute()
4611 if( m_regs.programPointer == 0 ) return false;
4612
4613 if( stackLevel >= GetCallstackSize() ) return false;
4614
4615 asCScriptFunction *func;
4616 asUINT pos;
4617
4618 if( stackLevel == 0 )
4619 {
4620 func = m_currentFunction;
4621 if( func->scriptData == 0 ) return false;
4622 pos = asUINT(m_regs.programPointer - func->scriptData->byteCode.AddressOf());
4623 }
4624 else
4625 {
4626 asPWORD *s = m_callStack.AddressOf() + (GetCallstackSize()-stackLevel-1)*CALLSTACK_FRAME_SIZE;
4627 func = (asCScriptFunction*)s[1];
4628 if( func->scriptData == 0 ) return false;
4629 pos = asUINT((asDWORD*)s[2] - func->scriptData->byteCode.AddressOf());
4630 }
4631
4632 // First determine if the program position is after the variable declaration
4633 if( func->scriptData->variables.GetLength() <= varIndex ) return false;
4634 if( func->scriptData->variables[varIndex]->declaredAtProgramPos > pos ) return false;
4635
4636 asUINT declaredAt = func->scriptData->variables[varIndex]->declaredAtProgramPos;
4637
4638 // If the program position is after the variable declaration it is necessary
4639 // determine if the program position is still inside the statement block where
4640 // the variable was delcared.
4641 for( int n = 0; n < (int)func->scriptData->objVariableInfo.GetLength(); n++ )
4642 {
4643 if( func->scriptData->objVariableInfo[n].programPos >= declaredAt )
4644 {
4645 // If the current block ends between the declaredAt and current
4646 // program position, then we know the variable is no longer visible
4647 int level = 0;
4648 for( ; n < (int)func->scriptData->objVariableInfo.GetLength(); n++ )
4649 {
4650 if( func->scriptData->objVariableInfo[n].programPos > pos )
4651 break;
4652
4653 if( func->scriptData->objVariableInfo[n].option == asBLOCK_BEGIN ) level++;
4654 if( func->scriptData->objVariableInfo[n].option == asBLOCK_END && --level < 0 )
4655 return false;
4656 }
4657
4658 break;
4659 }
4660 }
4661
4662 // Variable is visible
4663 return true;
4664 }
4665
4666 // Internal
DetermineLiveObjects(asCArray<int> & liveObjects,asUINT stackLevel)4667 void asCContext::DetermineLiveObjects(asCArray<int> &liveObjects, asUINT stackLevel)
4668 {
4669 asASSERT( stackLevel < GetCallstackSize() );
4670
4671 asCScriptFunction *func;
4672 asUINT pos;
4673
4674 if( stackLevel == 0 )
4675 {
4676 func = m_currentFunction;
4677 if( func->scriptData == 0 )
4678 return;
4679
4680 pos = asUINT(m_regs.programPointer - func->scriptData->byteCode.AddressOf());
4681
4682 if( m_status == asEXECUTION_EXCEPTION )
4683 {
4684 // Don't consider the last instruction as executed, as it failed with an exception
4685 // It's not actually necessary to decrease the exact size of the instruction. Just
4686 // before the current position is enough to disconsider it.
4687 pos--;
4688 }
4689 }
4690 else
4691 {
4692 asPWORD *s = m_callStack.AddressOf() + (GetCallstackSize()-stackLevel-1)*CALLSTACK_FRAME_SIZE;
4693 func = (asCScriptFunction*)s[1];
4694 if( func->scriptData == 0 )
4695 return;
4696
4697 pos = asUINT((asDWORD*)s[2] - func->scriptData->byteCode.AddressOf());
4698
4699 // Don't consider the last instruction as executed, as the function that was called by it
4700 // is still being executed. If we consider it as executed already, then a value object
4701 // returned by value would be considered alive, which it is not.
4702 pos--;
4703 }
4704
4705 // Determine which object variables that are really live ones
4706 liveObjects.SetLength(func->scriptData->objVariablePos.GetLength());
4707 memset(liveObjects.AddressOf(), 0, sizeof(int)*liveObjects.GetLength());
4708 for( int n = 0; n < (int)func->scriptData->objVariableInfo.GetLength(); n++ )
4709 {
4710 // Find the first variable info with a larger position than the current
4711 // As the variable info are always placed on the instruction right after the
4712 // one that initialized or freed the object, the current position needs to be
4713 // considered as valid.
4714 if( func->scriptData->objVariableInfo[n].programPos > pos )
4715 {
4716 // We've determined how far the execution ran, now determine which variables are alive
4717 for( --n; n >= 0; n-- )
4718 {
4719 switch( func->scriptData->objVariableInfo[n].option )
4720 {
4721 case asOBJ_UNINIT: // Object was destroyed
4722 {
4723 // TODO: optimize: This should have been done by the compiler already
4724 // Which variable is this?
4725 asUINT var = 0;
4726 for( asUINT v = 0; v < func->scriptData->objVariablePos.GetLength(); v++ )
4727 if( func->scriptData->objVariablePos[v] == func->scriptData->objVariableInfo[n].variableOffset )
4728 {
4729 var = v;
4730 break;
4731 }
4732 liveObjects[var] -= 1;
4733 }
4734 break;
4735 case asOBJ_INIT: // Object was created
4736 {
4737 // Which variable is this?
4738 asUINT var = 0;
4739 for( asUINT v = 0; v < func->scriptData->objVariablePos.GetLength(); v++ )
4740 if( func->scriptData->objVariablePos[v] == func->scriptData->objVariableInfo[n].variableOffset )
4741 {
4742 var = v;
4743 break;
4744 }
4745 liveObjects[var] += 1;
4746 }
4747 break;
4748 case asBLOCK_BEGIN: // Start block
4749 // We should ignore start blocks, since it just means the
4750 // program was within the block when the exception ocurred
4751 break;
4752 case asBLOCK_END: // End block
4753 // We need to skip the entire block, as the objects created
4754 // and destroyed inside this block are already out of scope
4755 {
4756 int nested = 1;
4757 while( nested > 0 )
4758 {
4759 int option = func->scriptData->objVariableInfo[--n].option;
4760 if( option == 3 )
4761 nested++;
4762 if( option == 2 )
4763 nested--;
4764 }
4765 }
4766 break;
4767 }
4768 }
4769
4770 // We're done with the investigation
4771 break;
4772 }
4773 }
4774 }
4775
CleanArgsOnStack()4776 void asCContext::CleanArgsOnStack()
4777 {
4778 if( !m_needToCleanupArgs )
4779 return;
4780
4781 asASSERT( m_currentFunction->scriptData );
4782
4783 // Find the instruction just before the current program pointer
4784 asDWORD *instr = m_currentFunction->scriptData->byteCode.AddressOf();
4785 asDWORD *prevInstr = 0;
4786 while( instr < m_regs.programPointer )
4787 {
4788 prevInstr = instr;
4789 instr += asBCTypeSize[asBCInfo[*(asBYTE*)(instr)].type];
4790 }
4791
4792 // Determine what function was being called
4793 asCScriptFunction *func = 0;
4794 asBYTE bc = *(asBYTE*)prevInstr;
4795 if( bc == asBC_CALL || bc == asBC_CALLSYS || bc == asBC_CALLINTF )
4796 {
4797 int funcId = asBC_INTARG(prevInstr);
4798 func = m_engine->scriptFunctions[funcId];
4799 }
4800 else if( bc == asBC_CALLBND )
4801 {
4802 int funcId = asBC_INTARG(prevInstr);
4803 func = m_engine->importedFunctions[funcId & ~FUNC_IMPORTED]->importedFunctionSignature;
4804 }
4805 else if( bc == asBC_CallPtr )
4806 {
4807 asUINT v;
4808 int var = asBC_SWORDARG0(prevInstr);
4809
4810 // Find the funcdef from the local variable
4811 for( v = 0; v < m_currentFunction->scriptData->objVariablePos.GetLength(); v++ )
4812 if( m_currentFunction->scriptData->objVariablePos[v] == var )
4813 {
4814 func = CastToFuncdefType(m_currentFunction->scriptData->objVariableTypes[v])->funcdef;
4815 break;
4816 }
4817
4818 if( func == 0 )
4819 {
4820 // Look in parameters
4821 int paramPos = 0;
4822 if( m_currentFunction->objectType )
4823 paramPos -= AS_PTR_SIZE;
4824 if( m_currentFunction->DoesReturnOnStack() )
4825 paramPos -= AS_PTR_SIZE;
4826 for( v = 0; v < m_currentFunction->parameterTypes.GetLength(); v++ )
4827 {
4828 if( var == paramPos )
4829 {
4830 if (m_currentFunction->parameterTypes[v].IsFuncdef())
4831 func = CastToFuncdefType(m_currentFunction->parameterTypes[v].GetTypeInfo())->funcdef;
4832 break;
4833 }
4834 paramPos -= m_currentFunction->parameterTypes[v].GetSizeOnStackDWords();
4835 }
4836 }
4837 }
4838 else
4839 asASSERT( false );
4840
4841 asASSERT( func );
4842
4843 // Clean parameters
4844 int offset = 0;
4845 if( func->objectType )
4846 offset += AS_PTR_SIZE;
4847 if( func->DoesReturnOnStack() )
4848 offset += AS_PTR_SIZE;
4849 for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ )
4850 {
4851 if( (func->parameterTypes[n].IsObject() || func->parameterTypes[n].IsFuncdef()) && !func->parameterTypes[n].IsReference() )
4852 {
4853 // TODO: cleanup: This logic is repeated twice in CleanStackFrame too. Should create a common function to share the code
4854 if( *(asPWORD*)&m_regs.stackPointer[offset] )
4855 {
4856 // Call the object's destructor
4857 asSTypeBehaviour *beh = func->parameterTypes[n].GetBehaviour();
4858 if (func->parameterTypes[n].GetTypeInfo()->flags & asOBJ_FUNCDEF)
4859 {
4860 (*(asCScriptFunction**)&m_regs.stackPointer[offset])->Release();
4861 }
4862 else if( func->parameterTypes[n].GetTypeInfo()->flags & asOBJ_REF )
4863 {
4864 asASSERT( (func->parameterTypes[n].GetTypeInfo()->flags & asOBJ_NOCOUNT) || beh->release );
4865
4866 if( beh->release )
4867 m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackPointer[offset], beh->release);
4868 }
4869 else
4870 {
4871 if( beh->destruct )
4872 m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackPointer[offset], beh->destruct);
4873
4874 // Free the memory
4875 m_engine->CallFree((void*)*(asPWORD*)&m_regs.stackPointer[offset]);
4876 }
4877 *(asPWORD*)&m_regs.stackPointer[offset] = 0;
4878 }
4879 }
4880
4881 offset += func->parameterTypes[n].GetSizeOnStackDWords();
4882 }
4883
4884 m_needToCleanupArgs = false;
4885 }
4886
CleanStackFrame()4887 void asCContext::CleanStackFrame()
4888 {
4889 // Clean object variables on the stack
4890 // If the stack memory is not allocated or the program pointer
4891 // is not set, then there is nothing to clean up on the stack frame
4892 if( !m_isStackMemoryNotAllocated && m_regs.programPointer )
4893 {
4894 // If the exception occurred while calling a function it is necessary
4895 // to clean up the arguments that were put on the stack.
4896 CleanArgsOnStack();
4897
4898 // Restore the stack pointer
4899 asASSERT( m_currentFunction->scriptData );
4900 m_regs.stackPointer += m_currentFunction->scriptData->variableSpace;
4901
4902 // Determine which object variables that are really live ones
4903 asCArray<int> liveObjects;
4904 DetermineLiveObjects(liveObjects, 0);
4905
4906 for( asUINT n = 0; n < m_currentFunction->scriptData->objVariablePos.GetLength(); n++ )
4907 {
4908 int pos = m_currentFunction->scriptData->objVariablePos[n];
4909 if( n < m_currentFunction->scriptData->objVariablesOnHeap )
4910 {
4911 // Check if the pointer is initialized
4912 if( *(asPWORD*)&m_regs.stackFramePointer[-pos] )
4913 {
4914 // Call the object's destructor
4915 if (m_currentFunction->scriptData->objVariableTypes[n]->flags & asOBJ_FUNCDEF)
4916 {
4917 (*(asCScriptFunction**)&m_regs.stackFramePointer[-pos])->Release();
4918 }
4919 else if( m_currentFunction->scriptData->objVariableTypes[n]->flags & asOBJ_REF )
4920 {
4921 asSTypeBehaviour *beh = &CastToObjectType(m_currentFunction->scriptData->objVariableTypes[n])->beh;
4922 asASSERT( (m_currentFunction->scriptData->objVariableTypes[n]->flags & asOBJ_NOCOUNT) || beh->release );
4923 if( beh->release )
4924 m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackFramePointer[-pos], beh->release);
4925 }
4926 else
4927 {
4928 asSTypeBehaviour *beh = &CastToObjectType(m_currentFunction->scriptData->objVariableTypes[n])->beh;
4929 if( beh->destruct )
4930 m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackFramePointer[-pos], beh->destruct);
4931 else if( m_currentFunction->scriptData->objVariableTypes[n]->flags & asOBJ_LIST_PATTERN )
4932 m_engine->DestroyList((asBYTE*)*(asPWORD*)&m_regs.stackFramePointer[-pos], CastToObjectType(m_currentFunction->scriptData->objVariableTypes[n]));
4933
4934 // Free the memory
4935 m_engine->CallFree((void*)*(asPWORD*)&m_regs.stackFramePointer[-pos]);
4936 }
4937 *(asPWORD*)&m_regs.stackFramePointer[-pos] = 0;
4938 }
4939 }
4940 else
4941 {
4942 asASSERT( m_currentFunction->scriptData->objVariableTypes[n]->GetFlags() & asOBJ_VALUE );
4943
4944 // Only destroy the object if it is truly alive
4945 if( liveObjects[n] > 0 )
4946 {
4947 asSTypeBehaviour *beh = &CastToObjectType(m_currentFunction->scriptData->objVariableTypes[n])->beh;
4948 if( beh->destruct )
4949 m_engine->CallObjectMethod((void*)(asPWORD*)&m_regs.stackFramePointer[-pos], beh->destruct);
4950 }
4951 }
4952 }
4953 }
4954 else
4955 m_isStackMemoryNotAllocated = false;
4956
4957 // Functions that do not own the object and parameters shouldn't do any clean up
4958 if( m_currentFunction->dontCleanUpOnException )
4959 return;
4960
4961 // Clean object and parameters
4962 int offset = 0;
4963 if( m_currentFunction->objectType )
4964 offset += AS_PTR_SIZE;
4965 if( m_currentFunction->DoesReturnOnStack() )
4966 offset += AS_PTR_SIZE;
4967 for( asUINT n = 0; n < m_currentFunction->parameterTypes.GetLength(); n++ )
4968 {
4969 if( (m_currentFunction->parameterTypes[n].IsObject() ||m_currentFunction->parameterTypes[n].IsFuncdef()) && !m_currentFunction->parameterTypes[n].IsReference() )
4970 {
4971 if( *(asPWORD*)&m_regs.stackFramePointer[offset] )
4972 {
4973 // Call the object's destructor
4974 asSTypeBehaviour *beh = m_currentFunction->parameterTypes[n].GetBehaviour();
4975 if (m_currentFunction->parameterTypes[n].GetTypeInfo()->flags & asOBJ_FUNCDEF)
4976 {
4977 (*(asCScriptFunction**)&m_regs.stackFramePointer[offset])->Release();
4978 }
4979 else if( m_currentFunction->parameterTypes[n].GetTypeInfo()->flags & asOBJ_REF )
4980 {
4981 asASSERT( (m_currentFunction->parameterTypes[n].GetTypeInfo()->flags & asOBJ_NOCOUNT) || beh->release );
4982
4983 if( beh->release )
4984 m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackFramePointer[offset], beh->release);
4985 }
4986 else
4987 {
4988 if( beh->destruct )
4989 m_engine->CallObjectMethod((void*)*(asPWORD*)&m_regs.stackFramePointer[offset], beh->destruct);
4990
4991 // Free the memory
4992 m_engine->CallFree((void*)*(asPWORD*)&m_regs.stackFramePointer[offset]);
4993 }
4994 *(asPWORD*)&m_regs.stackFramePointer[offset] = 0;
4995 }
4996 }
4997
4998 offset += m_currentFunction->parameterTypes[n].GetSizeOnStackDWords();
4999 }
5000 }
5001
5002 // interface
GetExceptionLineNumber(int * column,const char ** sectionName)5003 int asCContext::GetExceptionLineNumber(int *column, const char **sectionName)
5004 {
5005 if( GetState() != asEXECUTION_EXCEPTION ) return asERROR;
5006
5007 if( column ) *column = m_exceptionColumn;
5008
5009 if( sectionName )
5010 {
5011 // The section index can be -1 if the exception was raised in a generated function, e.g. $fact for templates
5012 if( m_exceptionSectionIdx >= 0 )
5013 *sectionName = m_engine->scriptSectionNames[m_exceptionSectionIdx]->AddressOf();
5014 else
5015 *sectionName = 0;
5016 }
5017
5018 return m_exceptionLine;
5019 }
5020
5021 // interface
GetExceptionFunction()5022 asIScriptFunction *asCContext::GetExceptionFunction()
5023 {
5024 if( GetState() != asEXECUTION_EXCEPTION ) return 0;
5025
5026 return m_engine->scriptFunctions[m_exceptionFunction];
5027 }
5028
5029 // interface
GetExceptionString()5030 const char *asCContext::GetExceptionString()
5031 {
5032 if( GetState() != asEXECUTION_EXCEPTION ) return 0;
5033
5034 return m_exceptionString.AddressOf();
5035 }
5036
5037 // interface
GetState() const5038 asEContextState asCContext::GetState() const
5039 {
5040 return m_status;
5041 }
5042
5043 // interface
SetLineCallback(asSFuncPtr callback,void * obj,int callConv)5044 int asCContext::SetLineCallback(asSFuncPtr callback, void *obj, int callConv)
5045 {
5046 // First turn off the line callback to avoid a second thread
5047 // attempting to call it while the new one is still being set
5048 m_lineCallback = false;
5049
5050 m_lineCallbackObj = obj;
5051 bool isObj = false;
5052 if( (unsigned)callConv == asCALL_GENERIC || (unsigned)callConv == asCALL_THISCALL_OBJFIRST || (unsigned)callConv == asCALL_THISCALL_OBJLAST )
5053 {
5054 m_regs.doProcessSuspend = m_doSuspend;
5055 return asNOT_SUPPORTED;
5056 }
5057 if( (unsigned)callConv >= asCALL_THISCALL )
5058 {
5059 isObj = true;
5060 if( obj == 0 )
5061 {
5062 m_regs.doProcessSuspend = m_doSuspend;
5063 return asINVALID_ARG;
5064 }
5065 }
5066
5067 int r = DetectCallingConvention(isObj, callback, callConv, 0, &m_lineCallbackFunc);
5068
5069 // Turn on the line callback after setting both the function pointer and object pointer
5070 if( r >= 0 ) m_lineCallback = true;
5071
5072 // The BC_SUSPEND instruction should be processed if either line
5073 // callback is set or if the application has requested a suspension
5074 m_regs.doProcessSuspend = m_doSuspend || m_lineCallback;
5075
5076 return r;
5077 }
5078
CallLineCallback()5079 void asCContext::CallLineCallback()
5080 {
5081 if( m_lineCallbackFunc.callConv < ICC_THISCALL )
5082 m_engine->CallGlobalFunction(this, m_lineCallbackObj, &m_lineCallbackFunc, 0);
5083 else
5084 m_engine->CallObjectMethod(m_lineCallbackObj, this, &m_lineCallbackFunc, 0);
5085 }
5086
5087 // interface
SetExceptionCallback(asSFuncPtr callback,void * obj,int callConv)5088 int asCContext::SetExceptionCallback(asSFuncPtr callback, void *obj, int callConv)
5089 {
5090 m_exceptionCallback = true;
5091 m_exceptionCallbackObj = obj;
5092 bool isObj = false;
5093 if( (unsigned)callConv == asCALL_GENERIC || (unsigned)callConv == asCALL_THISCALL_OBJFIRST || (unsigned)callConv == asCALL_THISCALL_OBJLAST )
5094 return asNOT_SUPPORTED;
5095 if( (unsigned)callConv >= asCALL_THISCALL )
5096 {
5097 isObj = true;
5098 if( obj == 0 )
5099 {
5100 m_exceptionCallback = false;
5101 return asINVALID_ARG;
5102 }
5103 }
5104 int r = DetectCallingConvention(isObj, callback, callConv, 0, &m_exceptionCallbackFunc);
5105 if( r < 0 ) m_exceptionCallback = false;
5106 return r;
5107 }
5108
CallExceptionCallback()5109 void asCContext::CallExceptionCallback()
5110 {
5111 if( m_exceptionCallbackFunc.callConv < ICC_THISCALL )
5112 m_engine->CallGlobalFunction(this, m_exceptionCallbackObj, &m_exceptionCallbackFunc, 0);
5113 else
5114 m_engine->CallObjectMethod(m_exceptionCallbackObj, this, &m_exceptionCallbackFunc, 0);
5115 }
5116
5117 // interface
ClearLineCallback()5118 void asCContext::ClearLineCallback()
5119 {
5120 m_lineCallback = false;
5121 m_regs.doProcessSuspend = m_doSuspend;
5122 }
5123
5124 // interface
ClearExceptionCallback()5125 void asCContext::ClearExceptionCallback()
5126 {
5127 m_exceptionCallback = false;
5128 }
5129
CallGeneric(asCScriptFunction * descr)5130 int asCContext::CallGeneric(asCScriptFunction *descr)
5131 {
5132 asSSystemFunctionInterface *sysFunc = descr->sysFuncIntf;
5133 void (*func)(asIScriptGeneric*) = (void (*)(asIScriptGeneric*))sysFunc->func;
5134 int popSize = sysFunc->paramSize;
5135 asDWORD *args = m_regs.stackPointer;
5136
5137 // Verify the object pointer if it is a class method
5138 void *currentObject = 0;
5139 asASSERT( sysFunc->callConv == ICC_GENERIC_FUNC || sysFunc->callConv == ICC_GENERIC_METHOD );
5140 if( sysFunc->callConv == ICC_GENERIC_METHOD )
5141 {
5142 // The object pointer should be popped from the context stack
5143 popSize += AS_PTR_SIZE;
5144
5145 // Check for null pointer
5146 currentObject = (void*)*(asPWORD*)(args);
5147 if( currentObject == 0 )
5148 {
5149 SetInternalException(TXT_NULL_POINTER_ACCESS);
5150 return 0;
5151 }
5152
5153 asASSERT( sysFunc->baseOffset == 0 );
5154
5155 // Skip object pointer
5156 args += AS_PTR_SIZE;
5157 }
5158
5159 if( descr->DoesReturnOnStack() )
5160 {
5161 // Skip the address where the return value will be stored
5162 args += AS_PTR_SIZE;
5163 popSize += AS_PTR_SIZE;
5164 }
5165
5166 asCGeneric gen(m_engine, descr, currentObject, args);
5167
5168 m_callingSystemFunction = descr;
5169 #ifdef AS_NO_EXCEPTIONS
5170 func(&gen);
5171 #else
5172 // This try/catch block is to catch potential exception that may
5173 // be thrown by the registered function.
5174 try
5175 {
5176 func(&gen);
5177 }
5178 catch (...)
5179 {
5180 // Convert the exception to a script exception so the VM can
5181 // properly report the error to the application and then clean up
5182 SetException(TXT_EXCEPTION_CAUGHT);
5183 }
5184 #endif
5185 m_callingSystemFunction = 0;
5186
5187 m_regs.valueRegister = gen.returnVal;
5188 m_regs.objectRegister = gen.objectRegister;
5189 m_regs.objectType = descr->returnType.GetTypeInfo();
5190
5191 // Clean up arguments
5192 const asUINT cleanCount = sysFunc->cleanArgs.GetLength();
5193 if( cleanCount )
5194 {
5195 asSSystemFunctionInterface::SClean *clean = sysFunc->cleanArgs.AddressOf();
5196 for( asUINT n = 0; n < cleanCount; n++, clean++ )
5197 {
5198 void **addr = (void**)&args[clean->off];
5199 if( clean->op == 0 )
5200 {
5201 if( *addr != 0 )
5202 {
5203 m_engine->CallObjectMethod(*addr, clean->ot->beh.release);
5204 *addr = 0;
5205 }
5206 }
5207 else
5208 {
5209 asASSERT( clean->op == 1 || clean->op == 2 );
5210 asASSERT( *addr );
5211
5212 if( clean->op == 2 )
5213 m_engine->CallObjectMethod(*addr, clean->ot->beh.destruct);
5214
5215 m_engine->CallFree(*addr);
5216 }
5217 }
5218 }
5219
5220 // Return how much should be popped from the stack
5221 return popSize;
5222 }
5223
5224 // interface
GetVarCount(asUINT stackLevel)5225 int asCContext::GetVarCount(asUINT stackLevel)
5226 {
5227 asIScriptFunction *func = GetFunction(stackLevel);
5228 if( func == 0 ) return asINVALID_ARG;
5229
5230 return func->GetVarCount();
5231 }
5232
5233 // interface
GetVarName(asUINT varIndex,asUINT stackLevel)5234 const char *asCContext::GetVarName(asUINT varIndex, asUINT stackLevel)
5235 {
5236 asIScriptFunction *func = GetFunction(stackLevel);
5237 if( func == 0 ) return 0;
5238
5239 const char *name = 0;
5240 int r = func->GetVar(varIndex, &name);
5241 return r >= 0 ? name : 0;
5242 }
5243
5244 // interface
GetVarDeclaration(asUINT varIndex,asUINT stackLevel,bool includeNamespace)5245 const char *asCContext::GetVarDeclaration(asUINT varIndex, asUINT stackLevel, bool includeNamespace)
5246 {
5247 asIScriptFunction *func = GetFunction(stackLevel);
5248 if( func == 0 ) return 0;
5249
5250 return func->GetVarDecl(varIndex, includeNamespace);
5251 }
5252
5253 // interface
GetVarTypeId(asUINT varIndex,asUINT stackLevel)5254 int asCContext::GetVarTypeId(asUINT varIndex, asUINT stackLevel)
5255 {
5256 asIScriptFunction *func = GetFunction(stackLevel);
5257 if( func == 0 ) return asINVALID_ARG;
5258
5259 int typeId;
5260 int r = func->GetVar(varIndex, 0, &typeId);
5261 return r < 0 ? r : typeId;
5262 }
5263
5264 // interface
GetAddressOfVar(asUINT varIndex,asUINT stackLevel)5265 void *asCContext::GetAddressOfVar(asUINT varIndex, asUINT stackLevel)
5266 {
5267 // Don't return anything if there is no bytecode, e.g. before calling Execute()
5268 if( m_regs.programPointer == 0 ) return 0;
5269
5270 if( stackLevel >= GetCallstackSize() ) return 0;
5271
5272 asCScriptFunction *func;
5273 asDWORD *sf;
5274 if( stackLevel == 0 )
5275 {
5276 func = m_currentFunction;
5277 sf = m_regs.stackFramePointer;
5278 }
5279 else
5280 {
5281 asPWORD *s = m_callStack.AddressOf() + (GetCallstackSize()-stackLevel-1)*CALLSTACK_FRAME_SIZE;
5282 func = (asCScriptFunction*)s[1];
5283 sf = (asDWORD*)s[0];
5284 }
5285
5286 if( func == 0 )
5287 return 0;
5288
5289 if( func->scriptData == 0 )
5290 return 0;
5291
5292 if( varIndex >= func->scriptData->variables.GetLength() )
5293 return 0;
5294
5295 // For object variables it's necessary to dereference the pointer to get the address of the value
5296 // Reference parameters must also be dereferenced to give the address of the value
5297 int pos = func->scriptData->variables[varIndex]->stackOffset;
5298 if( (func->scriptData->variables[varIndex]->type.IsObject() && !func->scriptData->variables[varIndex]->type.IsObjectHandle()) || (pos <= 0) )
5299 {
5300 // Determine if the object is really on the heap
5301 bool onHeap = false;
5302 if( func->scriptData->variables[varIndex]->type.IsObject() &&
5303 !func->scriptData->variables[varIndex]->type.IsObjectHandle() )
5304 {
5305 onHeap = true;
5306 if( func->scriptData->variables[varIndex]->type.GetTypeInfo()->GetFlags() & asOBJ_VALUE )
5307 {
5308 for( asUINT n = 0; n < func->scriptData->objVariablePos.GetLength(); n++ )
5309 {
5310 if( func->scriptData->objVariablePos[n] == pos )
5311 {
5312 onHeap = n < func->scriptData->objVariablesOnHeap;
5313
5314 if( !onHeap )
5315 {
5316 // If the object on the stack is not initialized return a null pointer instead
5317 asCArray<int> liveObjects;
5318 DetermineLiveObjects(liveObjects, stackLevel);
5319
5320 if( liveObjects[n] <= 0 )
5321 return 0;
5322 }
5323
5324 break;
5325 }
5326 }
5327 }
5328 }
5329
5330 // If it wasn't an object on the heap, then check if it is a reference parameter
5331 if( !onHeap && pos <= 0 )
5332 {
5333 // Determine what function argument this position matches
5334 int stackPos = 0;
5335 if( func->objectType )
5336 stackPos -= AS_PTR_SIZE;
5337
5338 if( func->DoesReturnOnStack() )
5339 stackPos -= AS_PTR_SIZE;
5340
5341 for( asUINT n = 0; n < func->parameterTypes.GetLength(); n++ )
5342 {
5343 if( stackPos == pos )
5344 {
5345 // The right argument was found. Is this a reference parameter?
5346 if( func->inOutFlags[n] != asTM_NONE )
5347 onHeap = true;
5348
5349 break;
5350 }
5351 stackPos -= func->parameterTypes[n].GetSizeOnStackDWords();
5352 }
5353 }
5354
5355 if( onHeap )
5356 return *(void**)(sf - func->scriptData->variables[varIndex]->stackOffset);
5357 }
5358
5359 return sf - func->scriptData->variables[varIndex]->stackOffset;
5360 }
5361
5362 // interface
5363 // returns the typeId of the 'this' object at the given call stack level (-1 for current)
5364 // returns 0 if the function call at the given stack level is not a method
GetThisTypeId(asUINT stackLevel)5365 int asCContext::GetThisTypeId(asUINT stackLevel)
5366 {
5367 asIScriptFunction *func = GetFunction(stackLevel);
5368 if( func == 0 ) return asINVALID_ARG;
5369
5370 if( func->GetObjectType() == 0 )
5371 return 0; // not in a method
5372
5373 // create a datatype
5374 asCDataType dt = asCDataType::CreateType((asCObjectType*)func->GetObjectType(), false);
5375
5376 // return a typeId from the data type
5377 return m_engine->GetTypeIdFromDataType(dt);
5378 }
5379
5380 // interface
5381 // returns the 'this' object pointer at the given call stack level (-1 for current)
5382 // returns 0 if the function call at the given stack level is not a method
GetThisPointer(asUINT stackLevel)5383 void *asCContext::GetThisPointer(asUINT stackLevel)
5384 {
5385 if( stackLevel >= GetCallstackSize() )
5386 return 0;
5387
5388 asCScriptFunction *func;
5389 asDWORD *sf;
5390 if( stackLevel == 0 )
5391 {
5392 func = m_currentFunction;
5393 sf = m_regs.stackFramePointer;
5394 }
5395 else
5396 {
5397 asPWORD *s = m_callStack.AddressOf() + (GetCallstackSize()-stackLevel-1)*CALLSTACK_FRAME_SIZE;
5398 func = (asCScriptFunction*)s[1];
5399 sf = (asDWORD*)s[0];
5400 }
5401
5402 if( func == 0 )
5403 return 0;
5404
5405 if( func->objectType == 0 )
5406 return 0; // not in a method
5407
5408 void *thisPointer = (void*)*(asPWORD*)(sf);
5409 if( thisPointer == 0 )
5410 {
5411 return 0;
5412 }
5413
5414 // NOTE: this returns the pointer to the 'this' while the GetVarPointer functions return
5415 // a pointer to a pointer. I can't imagine someone would want to change the 'this'
5416 return thisPointer;
5417 }
5418
5419
5420
5421
5422
5423
5424
5425 // TODO: Move these to as_utils.cpp
5426
5427 struct POW_INFO
5428 {
5429 asQWORD MaxBaseu64;
5430 asDWORD MaxBasei64;
5431 asWORD MaxBaseu32;
5432 asWORD MaxBasei32;
5433 char HighBit;
5434 };
5435
5436 const POW_INFO pow_info[] =
5437 {
5438 { 0ULL, 0UL, 0, 0, 0 }, // 0 is a special case
5439 { 0ULL, 0UL, 0, 0, 1 }, // 1 is a special case
5440 { 3037000499ULL, 2147483647UL, 65535, 46340, 2 }, // 2
5441 { 2097152ULL, 1664510UL, 1625, 1290, 2 }, // 3
5442 { 55108ULL, 46340UL, 255, 215, 3 }, // 4
5443 { 6208ULL, 5404UL, 84, 73, 3 }, // 5
5444 { 1448ULL, 1290UL, 40, 35, 3 }, // 6
5445 { 511ULL, 463UL, 23, 21, 3 }, // 7
5446 { 234ULL, 215UL, 15, 14, 4 }, // 8
5447 { 128ULL, 118UL, 11, 10, 4 }, // 9
5448 { 78ULL, 73UL, 9, 8, 4 }, // 10
5449 { 52ULL, 49UL, 7, 7, 4 }, // 11
5450 { 38ULL, 35UL, 6, 5, 4 }, // 12
5451 { 28ULL, 27UL, 5, 5, 4 }, // 13
5452 { 22ULL, 21UL, 4, 4, 4 }, // 14
5453 { 18ULL, 17UL, 4, 4, 4 }, // 15
5454 { 15ULL, 14UL, 3, 3, 5 }, // 16
5455 { 13ULL, 12UL, 3, 3, 5 }, // 17
5456 { 11ULL, 10UL, 3, 3, 5 }, // 18
5457 { 9ULL, 9UL, 3, 3, 5 }, // 19
5458 { 8ULL, 8UL, 3, 2, 5 }, // 20
5459 { 8ULL, 7UL, 2, 2, 5 }, // 21
5460 { 7ULL, 7UL, 2, 2, 5 }, // 22
5461 { 6ULL, 6UL, 2, 2, 5 }, // 23
5462 { 6ULL, 5UL, 2, 2, 5 }, // 24
5463 { 5ULL, 5UL, 2, 2, 5 }, // 25
5464 { 5ULL, 5UL, 2, 2, 5 }, // 26
5465 { 5ULL, 4UL, 2, 2, 5 }, // 27
5466 { 4ULL, 4UL, 2, 2, 5 }, // 28
5467 { 4ULL, 4UL, 2, 2, 5 }, // 29
5468 { 4ULL, 4UL, 2, 2, 5 }, // 30
5469 { 4ULL, 4UL, 2, 1, 5 }, // 31
5470 { 3ULL, 3UL, 1, 1, 6 }, // 32
5471 { 3ULL, 3UL, 1, 1, 6 }, // 33
5472 { 3ULL, 3UL, 1, 1, 6 }, // 34
5473 { 3ULL, 3UL, 1, 1, 6 }, // 35
5474 { 3ULL, 3UL, 1, 1, 6 }, // 36
5475 { 3ULL, 3UL, 1, 1, 6 }, // 37
5476 { 3ULL, 3UL, 1, 1, 6 }, // 38
5477 { 3ULL, 3UL, 1, 1, 6 }, // 39
5478 { 2ULL, 2UL, 1, 1, 6 }, // 40
5479 { 2ULL, 2UL, 1, 1, 6 }, // 41
5480 { 2ULL, 2UL, 1, 1, 6 }, // 42
5481 { 2ULL, 2UL, 1, 1, 6 }, // 43
5482 { 2ULL, 2UL, 1, 1, 6 }, // 44
5483 { 2ULL, 2UL, 1, 1, 6 }, // 45
5484 { 2ULL, 2UL, 1, 1, 6 }, // 46
5485 { 2ULL, 2UL, 1, 1, 6 }, // 47
5486 { 2ULL, 2UL, 1, 1, 6 }, // 48
5487 { 2ULL, 2UL, 1, 1, 6 }, // 49
5488 { 2ULL, 2UL, 1, 1, 6 }, // 50
5489 { 2ULL, 2UL, 1, 1, 6 }, // 51
5490 { 2ULL, 2UL, 1, 1, 6 }, // 52
5491 { 2ULL, 2UL, 1, 1, 6 }, // 53
5492 { 2ULL, 2UL, 1, 1, 6 }, // 54
5493 { 2ULL, 2UL, 1, 1, 6 }, // 55
5494 { 2ULL, 2UL, 1, 1, 6 }, // 56
5495 { 2ULL, 2UL, 1, 1, 6 }, // 57
5496 { 2ULL, 2UL, 1, 1, 6 }, // 58
5497 { 2ULL, 2UL, 1, 1, 6 }, // 59
5498 { 2ULL, 2UL, 1, 1, 6 }, // 60
5499 { 2ULL, 2UL, 1, 1, 6 }, // 61
5500 { 2ULL, 2UL, 1, 1, 6 }, // 62
5501 { 2ULL, 1UL, 1, 1, 6 }, // 63
5502 };
5503
as_powi(int base,int exponent,bool & isOverflow)5504 int as_powi(int base, int exponent, bool& isOverflow)
5505 {
5506 if( exponent < 0 )
5507 {
5508 if( base == 0 )
5509 // Divide by zero
5510 isOverflow = true;
5511 else
5512 // Result is less than 1, so it truncates to 0
5513 isOverflow = false;
5514
5515 return 0;
5516 }
5517 else if( exponent == 0 && base == 0 )
5518 {
5519 // Domain error
5520 isOverflow = true;
5521 return 0;
5522 }
5523 else if( exponent >= 31 )
5524 {
5525 switch( base )
5526 {
5527 case -1:
5528 isOverflow = false;
5529 return exponent & 1 ? -1 : 1;
5530 case 0:
5531 isOverflow = false;
5532 break;
5533 case 1:
5534 isOverflow = false;
5535 return 1;
5536 default:
5537 isOverflow = true;
5538 break;
5539 }
5540 return 0;
5541 }
5542 else
5543 {
5544 const asWORD max_base = pow_info[exponent].MaxBasei32;
5545 const char high_bit = pow_info[exponent].HighBit;
5546 if( max_base != 0 && max_base < (base < 0 ? -base : base) )
5547 {
5548 isOverflow = true;
5549 return 0; // overflow
5550 }
5551
5552 int result = 1;
5553 switch( high_bit )
5554 {
5555 case 5:
5556 if( exponent & 1 ) result *= base;
5557 exponent >>= 1;
5558 base *= base;
5559 case 4:
5560 if( exponent & 1 ) result *= base;
5561 exponent >>= 1;
5562 base *= base;
5563 case 3:
5564 if( exponent & 1 ) result *= base;
5565 exponent >>= 1;
5566 base *= base;
5567 case 2:
5568 if( exponent & 1 ) result *= base;
5569 exponent >>= 1;
5570 base *= base;
5571 case 1:
5572 if( exponent ) result *= base;
5573 default:
5574 isOverflow = false;
5575 return result;
5576 }
5577 }
5578 }
5579
as_powu(asDWORD base,asDWORD exponent,bool & isOverflow)5580 asDWORD as_powu(asDWORD base, asDWORD exponent, bool& isOverflow)
5581 {
5582 if( exponent == 0 && base == 0 )
5583 {
5584 // Domain error
5585 isOverflow = true;
5586 return 0;
5587 }
5588 else if( exponent >= 32 )
5589 {
5590 switch( base )
5591 {
5592 case 0:
5593 isOverflow = false;
5594 break;
5595 case 1:
5596 isOverflow = false;
5597 return 1;
5598 default:
5599 isOverflow = true;
5600 break;
5601 }
5602 return 0;
5603 }
5604 else
5605 {
5606 const asWORD max_base = pow_info[exponent].MaxBaseu32;
5607 const char high_bit = pow_info[exponent].HighBit;
5608 if( max_base != 0 && max_base < base )
5609 {
5610 isOverflow = true;
5611 return 0; // overflow
5612 }
5613
5614 asDWORD result = 1;
5615 switch( high_bit )
5616 {
5617 case 5:
5618 if( exponent & 1 ) result *= base;
5619 exponent >>= 1;
5620 base *= base;
5621 case 4:
5622 if( exponent & 1 ) result *= base;
5623 exponent >>= 1;
5624 base *= base;
5625 case 3:
5626 if( exponent & 1 ) result *= base;
5627 exponent >>= 1;
5628 base *= base;
5629 case 2:
5630 if( exponent & 1 ) result *= base;
5631 exponent >>= 1;
5632 base *= base;
5633 case 1:
5634 if( exponent ) result *= base;
5635 default:
5636 isOverflow = false;
5637 return result;
5638 }
5639 }
5640 }
5641
as_powi64(asINT64 base,asINT64 exponent,bool & isOverflow)5642 asINT64 as_powi64(asINT64 base, asINT64 exponent, bool& isOverflow)
5643 {
5644 if( exponent < 0 )
5645 {
5646 if( base == 0 )
5647 // Divide by zero
5648 isOverflow = true;
5649 else
5650 // Result is less than 1, so it truncates to 0
5651 isOverflow = false;
5652
5653 return 0;
5654 }
5655 else if( exponent == 0 && base == 0 )
5656 {
5657 // Domain error
5658 isOverflow = true;
5659 return 0;
5660 }
5661 else if( exponent >= 63 )
5662 {
5663 switch( base )
5664 {
5665 case -1:
5666 isOverflow = false;
5667 return exponent & 1 ? -1 : 1;
5668 case 0:
5669 isOverflow = false;
5670 break;
5671 case 1:
5672 isOverflow = false;
5673 return 1;
5674 default:
5675 isOverflow = true;
5676 break;
5677 }
5678 return 0;
5679 }
5680 else
5681 {
5682 const asDWORD max_base = pow_info[exponent].MaxBasei64;
5683 const char high_bit = pow_info[exponent].HighBit;
5684 if( max_base != 0 && max_base < (base < 0 ? -base : base) )
5685 {
5686 isOverflow = true;
5687 return 0; // overflow
5688 }
5689
5690 asINT64 result = 1;
5691 switch( high_bit )
5692 {
5693 case 6:
5694 if( exponent & 1 ) result *= base;
5695 exponent >>= 1;
5696 base *= base;
5697 case 5:
5698 if( exponent & 1 ) result *= base;
5699 exponent >>= 1;
5700 base *= base;
5701 case 4:
5702 if( exponent & 1 ) result *= base;
5703 exponent >>= 1;
5704 base *= base;
5705 case 3:
5706 if( exponent & 1 ) result *= base;
5707 exponent >>= 1;
5708 base *= base;
5709 case 2:
5710 if( exponent & 1 ) result *= base;
5711 exponent >>= 1;
5712 base *= base;
5713 case 1:
5714 if( exponent ) result *= base;
5715 default:
5716 isOverflow = false;
5717 return result;
5718 }
5719 }
5720 }
5721
as_powu64(asQWORD base,asQWORD exponent,bool & isOverflow)5722 asQWORD as_powu64(asQWORD base, asQWORD exponent, bool& isOverflow)
5723 {
5724 if( exponent == 0 && base == 0 )
5725 {
5726 // Domain error
5727 isOverflow = true;
5728 return 0;
5729 }
5730 else if( exponent >= 64 )
5731 {
5732 switch( base )
5733 {
5734 case 0:
5735 isOverflow = false;
5736 break;
5737 case 1:
5738 isOverflow = false;
5739 return 1;
5740 default:
5741 isOverflow = true;
5742 break;
5743 }
5744 return 0;
5745 }
5746 else
5747 {
5748 const asQWORD max_base = pow_info[exponent].MaxBaseu64;
5749 const char high_bit = pow_info[exponent].HighBit;
5750 if( max_base != 0 && max_base < base )
5751 {
5752 isOverflow = true;
5753 return 0; // overflow
5754 }
5755
5756 asQWORD result = 1;
5757 switch( high_bit )
5758 {
5759 case 6:
5760 if( exponent & 1 ) result *= base;
5761 exponent >>= 1;
5762 base *= base;
5763 case 5:
5764 if( exponent & 1 ) result *= base;
5765 exponent >>= 1;
5766 base *= base;
5767 case 4:
5768 if( exponent & 1 ) result *= base;
5769 exponent >>= 1;
5770 base *= base;
5771 case 3:
5772 if( exponent & 1 ) result *= base;
5773 exponent >>= 1;
5774 base *= base;
5775 case 2:
5776 if( exponent & 1 ) result *= base;
5777 exponent >>= 1;
5778 base *= base;
5779 case 1:
5780 if( exponent ) result *= base;
5781 default:
5782 isOverflow = false;
5783 return result;
5784 }
5785 }
5786 }
5787
5788 END_AS_NAMESPACE
5789
5790
5791
5792