OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4AulExec.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
5  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
6  *
7  * Distributed under the terms of the ISC license; see accompanying file
8  * "COPYING" for details.
9  *
10  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
11  * See accompanying file "TRADEMARK" for details.
12  *
13  * To redistribute this file separately, substitute the full license texts
14  * for the above references.
15  */
16 // executes script functions
17 
18 #include "C4Include.h"
19 #include "script/C4AulExec.h"
20 
21 #include "script/C4Aul.h"
22 #include "script/C4AulScriptFunc.h"
23 #include "script/C4AulDebug.h"
24 #include "object/C4Object.h"
25 #include "config/C4Config.h"
26 #include "game/C4Game.h"
27 #include "lib/C4Log.h"
28 #include "control/C4Record.h"
29 #include "object/C4Def.h"
30 #include "script/C4ScriptHost.h"
31 #include <algorithm>
32 
34 
35 C4AulExecError::C4AulExecError(const char *szError)
36 {
37  assert(szError);
38  // direct error message string
39  sMessage.Copy(szError ? szError : "(no error message)");
40 }
41 
43 {
44  if (!Func)
45  return StdStrBuf("");
46  // Context
47  if (Obj && Obj->Status)
48  {
49  C4Value ObjVal(Obj);
50  Dump.Append(ObjVal.GetDataString(0));
51  Dump.Append("->");
52  }
53  bool fDirectExec = !Func->GetName();
54  if (!fDirectExec)
55  {
56  // Function name
57  Dump.Append(Func->GetName());
58  // Parameters
59  Dump.AppendChar('(');
60  int iNullPars = 0;
61  for (int i = 0; i < Func->GetParCount(); i++)
62  {
63  if (!Pars[i])
64  iNullPars++;
65  else
66  {
67  if (i > iNullPars)
68  Dump.AppendChar(',');
69  // Insert missing null parameters
70  while (iNullPars > 0)
71  {
72  Dump.Append("0,");
73  iNullPars--;
74  }
75  // Insert parameter
76  Dump.Append(Pars[i].GetDataString());
77  }
78  }
79  Dump.AppendChar(')');
80  }
81  else
82  Dump.Append(Func->Parent->GetDataString());
83  // Script
84  if (!fDirectExec && Func->pOrgScript)
85  Dump.AppendFormat(" (%s:%d)",
88  // Return it
89  return Dump;
90 }
91 
93 {
94  // Log it
95  DebugLog(ReturnDump(Dump).getData());
96 }
97 
99 {
100  for (C4AulScriptContext *pCtx = pCurCtx; pCtx >= Contexts; pCtx--)
101  pCtx->dump(StdStrBuf(" by: "));
102 }
103 
105 {
106 #define ReturnIfTranslationAvailable(script, key) do \
107 { \
108  const auto &s = script; \
109  const auto &k = key; \
110  if (s) \
111  { \
112  try \
113  { \
114  return ::Strings.RegString(s->Translate(k).c_str()); \
115  } \
116  catch (C4LangStringTable::NoSuchTranslation &) {} \
117  } \
118 } while(0)
119 
120  if (!text || text->GetData().isNull()) return nullptr;
121  // Find correct script: translations of the context if possible, containing script as fallback
122  if (_this && _this->GetDef())
123  ReturnIfTranslationAvailable(&(_this->GetDef()->Script), text->GetCStr());
124  ReturnIfTranslationAvailable(AulExec.pCurCtx[0].Func->pOrgScript, text->GetCStr());
125 
126  // No translation available, log
127  DebugLogF("WARNING: Translate: no translation for string \"%s\"", text->GetCStr());
128  // Trace
129  AulExec.LogCallStack();
130  return text;
131 #undef ReturnIfTranslationAvailable
132 }
133 
135 {
136  AulExec.LogCallStack();
137  return true;
138 }
139 
141 {
142  for (C4AulScriptContext *pCtx = pCurCtx; pCtx >= Contexts; pCtx--)
143  {
144  if (pCtx->Obj == obj)
145  pCtx->Obj = nullptr;
146  }
147 }
148 
149 C4Value C4AulExec::Exec(C4AulScriptFunc *pSFunc, C4PropList * p, C4Value *pnPars, bool fPassErrors)
150 {
151  // Save start context
152  C4AulScriptContext *pOldCtx = pCurCtx;
153  C4Value *pPars = pCurVal + 1;
154  try
155  {
156  // Push parameters
157  assert(pnPars);
158  for (int i = 0; i < pSFunc->GetParCount(); i++)
159  PushValue(pnPars[i]);
160 
161  // Push a new context
162  C4AulScriptContext ctx;
163  ctx.tTime = 0;
164  ctx.Obj = p;
165  ctx.Return = nullptr;
166  ctx.Pars = pPars;
167  ctx.Func = pSFunc;
168  ctx.CPos = nullptr;
169  PushContext(ctx);
170 
171  // Execute
172  return Exec(pSFunc->GetCode());
173  }
174  catch (C4AulError &e)
175  {
176  if (!fPassErrors)
178  // Unwind stack
179  // TODO: The stack dump should be passed to the error handler somehow
180  while (pCurCtx > pOldCtx)
181  {
182  pCurCtx->dump(StdStrBuf(" by: "));
183  PopContext();
184  }
185  PopValuesUntil(pPars - 1);
186  // Pass?
187  if (fPassErrors)
188  throw;
189  // Trace
190  LogCallStack();
191  }
192 
193  // Return nothing
194  return C4VNull;
195 }
196 
198 {
199  try
200  {
201 
202  for (;;)
203  {
204 
205  bool fJump = false;
206  switch (pCPos->bccType)
207  {
208  case AB_INT:
209  PushInt(pCPos->Par.i);
210  break;
211 
212  case AB_BOOL:
213  PushBool(!!pCPos->Par.i);
214  break;
215 
216  case AB_STRING:
217  PushString(pCPos->Par.s);
218  break;
219 
220  case AB_CPROPLIST:
221  PushPropList(pCPos->Par.p);
222  break;
223 
224  case AB_CARRAY:
225  PushArray(pCPos->Par.a);
226  break;
227 
228  case AB_CFUNCTION:
229  PushFunction(pCPos->Par.f);
230  break;
231 
232  case AB_NIL:
233  PushValue(C4VNull);
234  break;
235 
236  case AB_DUP:
237  PushValue(pCurVal[pCPos->Par.i]);
238  break;
239  case AB_STACK_SET:
240  pCurVal[pCPos->Par.i] = pCurVal[0];
241  break;
242  case AB_POP_TO:
243  pCurVal[pCPos->Par.i] = pCurVal[0];
244  PopValue();
245  break;
246 
247  case AB_EOFN:
248  throw C4AulExecError("internal error: function didn't return");
249 
250  case AB_ERR:
251  if (pCPos->Par.s)
252  throw C4AulExecError((std::string("syntax error: ") + pCPos->Par.s->GetCStr()).c_str());
253  else
254  throw C4AulExecError("syntax error: see above for details");
255 
256  case AB_DUP_CONTEXT:
257  PushValue(AulExec.GetContext(AulExec.GetContextDepth()-2)->Pars[pCPos->Par.i]);
258  break;
259 
260  case AB_LOCALN:
261  if (!pCurCtx->Obj)
262  throw C4AulExecError("can't access local variables without this");
263  PushNullVals(1);
264  pCurCtx->Obj->GetPropertyByS(pCPos->Par.s, pCurVal);
265  break;
266  case AB_LOCALN_SET:
267  if (!pCurCtx->Obj)
268  throw C4AulExecError("can't access local variables without this");
269  if (pCurCtx->Obj->IsFrozen())
270  throw C4AulExecError("local variable: this is readonly");
271  pCurCtx->Obj->SetPropertyByS(pCPos->Par.s, pCurVal[0]);
272  break;
273 
274  case AB_PROP:
275  if (!pCurVal->CheckConversion(C4V_PropList))
276  throw C4AulExecError(FormatString("proplist access: proplist expected, got %s", pCurVal->GetTypeName()).getData());
277  if (!pCurVal->_getPropList()->GetPropertyByS(pCPos->Par.s, pCurVal))
278  pCurVal->Set0();
279  break;
280  case AB_PROP_SET:
281  {
282  C4Value *pPropList = pCurVal - 1;
283  if (!pPropList->CheckConversion(C4V_PropList))
284  throw C4AulExecError(FormatString("proplist write: proplist expected, got %s", pPropList->GetTypeName()).getData());
285  if (pPropList->_getPropList()->IsFrozen())
286  throw C4AulExecError("proplist write: proplist is readonly");
287  pPropList->_getPropList()->SetPropertyByS(pCPos->Par.s, pCurVal[0]);
288  pPropList->Set(pCurVal[0]);
289  PopValue();
290  break;
291  }
292 
293  case AB_GLOBALN:
294  PushValue(*::ScriptEngine.GlobalNamed.GetItem(pCPos->Par.i));
295  break;
296  case AB_GLOBALN_SET:
297  ::ScriptEngine.GlobalNamed.GetItem(pCPos->Par.i)->Set(pCurVal[0]);
298  break;
299 
300  // prefix
301  case AB_BitNot: // ~
302  CheckOpPar(C4V_Int, "~");
303  pCurVal->SetInt(~pCurVal->_getInt());
304  break;
305  case AB_Not: // !
306  pCurVal->SetBool(!pCurVal->getBool());
307  break;
308  case AB_Neg: // -
309  CheckOpPar(C4V_Int, "-");
310  pCurVal->SetInt(-pCurVal->_getInt());
311  break;
312  case AB_Inc: // ++
313  CheckOpPar(C4V_Int, "++");
314  pCurVal->SetInt(pCurVal->_getInt() + 1);
315  break;
316  case AB_Dec: // --
317  CheckOpPar(C4V_Int, "--");
318  pCurVal->SetInt(pCurVal->_getInt() - 1);
319  break;
320  // postfix
321  case AB_Pow: // **
322  {
323  CheckOpPars(C4V_Int, C4V_Int, "**");
324  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
325  pPar1->SetInt(Pow(pPar1->_getInt(), pPar2->_getInt()));
326  PopValue();
327  break;
328  }
329  case AB_Div: // /
330  {
331  CheckOpPars(C4V_Int, C4V_Int, "/");
332  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
333  if (!pPar2->_getInt())
334  throw C4AulExecError("division by zero");
335  // INT_MIN/-1 cannot be represented in an int and would cause an uncaught exception
336  if (pPar1->_getInt()==INT32_MIN && pPar2->_getInt()==-1)
337  throw C4AulExecError("division overflow");
338  pPar1->SetInt(pPar1->_getInt() / pPar2->_getInt());
339  PopValue();
340  break;
341  }
342  case AB_Mul: // *
343  {
344  CheckOpPars(C4V_Int, C4V_Int, "*");
345  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
346  pPar1->SetInt(pPar1->_getInt() * pPar2->_getInt());
347  PopValue();
348  break;
349  }
350  case AB_Mod: // %
351  {
352  CheckOpPars(C4V_Int, C4V_Int, "%");
353  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
354  // INT_MIN%-1 cannot be represented in an int and would cause an uncaught exception
355  if (pPar1->_getInt()==INT32_MIN && pPar2->_getInt()==-1)
356  throw C4AulExecError("modulo division overflow");
357  if (pPar2->_getInt())
358  pPar1->SetInt(pPar1->_getInt() % pPar2->_getInt());
359  else
360  pPar1->Set0();
361  PopValue();
362  break;
363  }
364  case AB_Sub: // -
365  {
366  CheckOpPars(C4V_Int, C4V_Int, "-");
367  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
368  pPar1->SetInt(pPar1->_getInt() - pPar2->_getInt());
369  PopValue();
370  break;
371  }
372  case AB_Sum: // +
373  {
374  CheckOpPars(C4V_Int, C4V_Int, "+");
375  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
376  pPar1->SetInt(pPar1->_getInt() + pPar2->_getInt());
377  PopValue();
378  break;
379  }
380  case AB_LeftShift: // <<
381  {
382  CheckOpPars(C4V_Int, C4V_Int, "<<");
383  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
384  pPar1->SetInt(pPar1->_getInt() << pPar2->_getInt());
385  PopValue();
386  break;
387  }
388  case AB_RightShift: // >>
389  {
390  CheckOpPars(C4V_Int, C4V_Int, ">>");
391  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
392  pPar1->SetInt(pPar1->_getInt() >> pPar2->_getInt());
393  PopValue();
394  break;
395  }
396  case AB_LessThan: // <
397  {
398  CheckOpPars(C4V_Int, C4V_Int, "<");
399  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
400  pPar1->SetBool(pPar1->_getInt() < pPar2->_getInt());
401  PopValue();
402  break;
403  }
404  case AB_LessThanEqual: // <=
405  {
406  CheckOpPars(C4V_Int, C4V_Int, "<=");
407  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
408  pPar1->SetBool(pPar1->_getInt() <= pPar2->_getInt());
409  PopValue();
410  break;
411  }
412  case AB_GreaterThan: // >
413  {
414  CheckOpPars(C4V_Int, C4V_Int, ">");
415  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
416  pPar1->SetBool(pPar1->_getInt() > pPar2->_getInt());
417  PopValue();
418  break;
419  }
420  case AB_GreaterThanEqual: // >=
421  {
422  CheckOpPars(C4V_Int, C4V_Int, ">=");
423  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
424  pPar1->SetBool(pPar1->_getInt() >= pPar2->_getInt());
425  PopValue();
426  break;
427  }
428  case AB_Equal: // ==
429  {
430  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
431  pPar1->SetBool(pPar1->IsIdenticalTo(*pPar2));
432  PopValue();
433  break;
434  }
435  case AB_NotEqual: // !=
436  {
437  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
438  pPar1->SetBool(!pPar1->IsIdenticalTo(*pPar2));
439  PopValue();
440  break;
441  }
442  case AB_BitAnd: // &
443  {
444  CheckOpPars(C4V_Int, C4V_Int, "&");
445  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
446  pPar1->SetInt(pPar1->_getInt() & pPar2->_getInt());
447  PopValue();
448  break;
449  }
450  case AB_BitXOr: // ^
451  {
452  CheckOpPars(C4V_Int, C4V_Int, "^");
453  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
454  pPar1->SetInt(pPar1->_getInt() ^ pPar2->_getInt());
455  PopValue();
456  break;
457  }
458  case AB_BitOr: // |
459  {
460  CheckOpPars(C4V_Int, C4V_Int, "|");
461  C4Value *pPar1 = pCurVal - 1, *pPar2 = pCurVal;
462  pPar1->SetInt(pPar1->_getInt() | pPar2->_getInt());
463  PopValue();
464  break;
465  }
466 
467  case AB_NEW_ARRAY:
468  {
469  // Create array
470  C4ValueArray *pArray = new C4ValueArray(pCPos->Par.i);
471 
472  // Pop values from stack
473  for (int i = 0; i < pCPos->Par.i; i++)
474  (*pArray)[i] = pCurVal[i - pCPos->Par.i + 1];
475 
476  // Push array
477  PopValues(pCPos->Par.i);
478  PushArray(pArray);
479 
480  break;
481  }
482 
483  case AB_NEW_PROPLIST:
484  {
485  C4PropList * pPropList = C4PropList::New();
486 
487  for (int i = 0; i < pCPos->Par.i; i++)
488  pPropList->SetPropertyByS(pCurVal[-2 * i - 1]._getStr(), pCurVal[-2 * i]);
489 
490  PopValues(pCPos->Par.i * 2);
491  PushPropList(pPropList);
492  break;
493  }
494 
495  case AB_ARRAYA:
496  {
497  C4Value *pIndex = pCurVal, *pStruct = pCurVal - 1, *pResult = pCurVal - 1;
498  // Typcheck to determine whether it's an array or a proplist
499  if(CheckArrayAccess(pStruct, pIndex) == C4V_Array)
500  {
501  *pResult = pStruct->_getArray()->GetItem(pIndex->_getInt());
502  }
503  else
504  {
505  assert(pStruct->GetType() == C4V_PropList);
506  C4PropList *pPropList = pStruct->_getPropList();
507  if (!pPropList->GetPropertyByS(pIndex->_getStr(), pResult))
508  pResult->Set0();
509  }
510  // Remove index
511  PopValue();
512  break;
513  }
514  case AB_ARRAYA_SET:
515  {
516  C4Value *pValue = pCurVal, *pIndex = pCurVal - 1, *pStruct = pCurVal - 2, *pResult = pCurVal - 2;
517  // Typcheck to determine whether it's an array or a proplist
518  if(CheckArrayAccess(pStruct, pIndex) == C4V_Array)
519  {
520  if (pStruct->_getArray()->IsFrozen())
521  throw C4AulExecError("array write: array is readonly");
522  pStruct->_getArray()->SetItem(pIndex->_getInt(), *pValue);
523  }
524  else
525  {
526  assert(pStruct->GetType() == C4V_PropList);
527  C4PropList *pPropList = pStruct->_getPropList();
528  if (pPropList->IsFrozen())
529  throw C4AulExecError("proplist write: proplist is readonly");
530  pPropList->SetPropertyByS(pIndex->_getStr(), *pValue);
531  }
532  // Set result, remove array and index from stack
533  *pResult = *pValue;
534  PopValues(2);
535  break;
536  }
537  case AB_ARRAY_SLICE:
538  {
539  C4Value &Array = pCurVal[-2];
540  C4Value &StartIndex = pCurVal[-1];
541  C4Value &EndIndex = pCurVal[0];
542 
543  // Typcheck
544  if (!Array.CheckConversion(C4V_Array))
545  throw C4AulExecError(FormatString("array slice: can't access %s as an array", Array.GetTypeName()).getData());
546  if (!StartIndex.CheckConversion(C4V_Int))
547  throw C4AulExecError(FormatString("array slice: start index of type %s, int expected", StartIndex.GetTypeName()).getData());
548  if (!EndIndex.CheckConversion(C4V_Int))
549  throw C4AulExecError(FormatString("array slice: end index of type %s, int expected", EndIndex.GetTypeName()).getData());
550 
551  Array.SetArray(Array.GetData().Array->GetSlice(StartIndex._getInt(), EndIndex._getInt()));
552 
553  // Remove both indices
554  PopValues(2);
555  break;
556  }
557 
558  case AB_ARRAY_SLICE_SET:
559  {
560  C4Value &Array = pCurVal[-3];
561  C4Value &StartIndex = pCurVal[-2];
562  C4Value &EndIndex = pCurVal[-1];
563  C4Value &Value = pCurVal[0];
564 
565  // Typcheck
566  if (!Array.CheckConversion(C4V_Array))
567  throw C4AulExecError(FormatString("array slice: can't access %s as an array", Array.GetTypeName()).getData());
568  if (!StartIndex.CheckConversion(C4V_Int))
569  throw C4AulExecError(FormatString("array slice: start index of type %s, int expected", StartIndex.GetTypeName()).getData());
570  if (!EndIndex.CheckConversion(C4V_Int))
571  throw C4AulExecError(FormatString("array slice: end index of type %s, int expected", EndIndex.GetTypeName()).getData());
572 
573  C4ValueArray *pArray = Array._getArray();
574  if (pArray->IsFrozen()) throw C4AulExecError("array write: array is readonly");
575  pArray->SetSlice(StartIndex._getInt(), EndIndex._getInt(), Value);
576 
577  // Set value as result, remove both indices and first copy of value
578  Array = Value;
579  PopValues(3);
580  break;
581  }
582 
583  case AB_STACK:
584  if (pCPos->Par.i < 0)
585  PopValues(-pCPos->Par.i);
586  else
587  PushNullVals(pCPos->Par.i);
588  break;
589 
590  case AB_JUMP:
591  fJump = true;
592  pCPos += pCPos->Par.i;
593  break;
594 
595  case AB_JUMPAND:
596  if (!pCurVal[0])
597  {
598  fJump = true;
599  pCPos += pCPos->Par.i;
600  }
601  else
602  {
603  PopValue();
604  }
605  break;
606 
607  case AB_JUMPOR:
608  if (!!pCurVal[0])
609  {
610  fJump = true;
611  pCPos += pCPos->Par.i;
612  }
613  else
614  {
615  PopValue();
616  }
617  break;
618 
619  case AB_JUMPNNIL: // ??
620  {
621  if (pCurVal[0].GetType() != C4V_Nil)
622  {
623  fJump = true;
624  pCPos += pCPos->Par.i;
625  }
626  else
627  {
628  PopValue();
629  }
630  break;
631  }
632 
633  case AB_CONDN:
634  if (!pCurVal[0])
635  {
636  fJump = true;
637  pCPos += pCPos->Par.i;
638  }
639  PopValue();
640  break;
641 
642  case AB_COND:
643  if (pCurVal[0])
644  {
645  fJump = true;
646  pCPos += pCPos->Par.i;
647  }
648  PopValue();
649  break;
650 
651  case AB_RETURN:
652  {
653  // Trace
654  if (iTraceStart >= 0)
655  {
656  StdStrBuf Buf("T");
657  Buf.AppendChars('>', ContextStackSize() - iTraceStart);
658  LogF("%s%s returned %s", Buf.getData(), pCurCtx->Func->GetName(), pCurVal->GetDataString().getData());
659  }
660 
661  C4Value *pReturn = pCurCtx->Return;
662 
663  // External call?
664  if (!pReturn)
665  {
666  // Get return value and stop executing.
667  C4Value rVal = *pCurVal;
668  PopValuesUntil(pCurCtx->Pars - 1);
669  PopContext();
670  return rVal;
671  }
672 
673  // Save return value
674  if (pCurVal != pReturn)
675  pReturn->Set(*pCurVal);
676 
677  // Pop context
678  PopContext();
679 
680  // Clear value stack, except return value
681  PopValuesUntil(pReturn);
682 
683  // Jump back, continue.
684  pCPos = pCurCtx->CPos + 1;
685  fJump = true;
686 
687  break;
688  }
689 
690  case AB_FUNC:
691  {
692  // Get function call data
693  C4AulFunc *pFunc = pCPos->Par.f;
694  C4Value *pPars = pCurVal - pFunc->GetParCount() + 1;
695  // Save current position
696  pCurCtx->CPos = pCPos;
697  assert(pCurCtx->Func->GetCode() <= pCPos);
698  // Do the call
699  C4AulBCC *pJump = Call(pFunc, pPars, pPars, nullptr);
700  if (pJump)
701  {
702  pCPos = pJump;
703  fJump = true;
704  }
705  break;
706  }
707 
708  case AB_PAR:
709  if (!pCurVal->CheckConversion(C4V_Int))
710  throw C4AulExecError(FormatString("Par: index of type %s, int expected", pCurVal->GetTypeName()).getData());
711  // Push reference to parameter on the stack
712  if (pCurVal->_getInt() >= 0 && pCurVal->_getInt() < pCurCtx->Func->GetParCount())
713  pCurVal->Set(pCurCtx->Pars[pCurVal->_getInt()]);
714  else
715  pCurVal->Set0();
716  break;
717 
718  case AB_THIS:
719  if (!pCurCtx->Obj || !pCurCtx->Obj->Status)
720  PushNullVals(1);
721  else
722  PushPropList(pCurCtx->Obj);
723  break;
724 
725  case AB_FOREACH_NEXT:
726  {
727  // This should always hold
728  assert(pCurVal->CheckConversion(C4V_Int));
729  int iItem = pCurVal->_getInt();
730  // Check array the first time only
731  if (!iItem)
732  {
733  if (!pCurVal[-1].CheckConversion(C4V_Array))
734  throw C4AulExecError(FormatString("for: array expected, but got %s", pCurVal[-1].GetTypeName()).getData());
735  }
736  C4ValueArray *pArray = pCurVal[-1]._getArray();
737  // No more entries?
738  if (pCurVal->_getInt() >= pArray->GetSize())
739  break;
740  // Get next
741  pCurVal[pCPos->Par.i] = pArray->GetItem(iItem);
742  // Save position
743  pCurVal->SetInt(iItem + 1);
744  // Jump over next instruction
745  pCPos += 2;
746  fJump = true;
747  break;
748  }
749 
750  case AB_CALL:
751  case AB_CALLFS:
752  {
753 
754  C4Value *pPars = pCurVal - C4AUL_MAX_Par + 1;
755  C4Value *pTargetVal = pCurVal - C4AUL_MAX_Par;
756 
757  C4PropList *pDest;
758  if (pTargetVal->CheckConversion(C4V_PropList))
759  {
760  pDest = pTargetVal->_getPropList();
761  }
762  else
763  throw C4AulExecError(FormatString("'->': invalid target type %s, expected proplist", pTargetVal->GetTypeName()).getData());
764 
765  // Search function for given context
766  C4AulFunc * pFunc = pDest->GetFunc(pCPos->Par.s);
767  if (!pFunc && pCPos->bccType == AB_CALLFS)
768  {
769  PopValuesUntil(pTargetVal);
770  pTargetVal->Set0();
771  break;
772  }
773 
774  // Function not found?
775  if (!pFunc)
776  throw C4AulExecError(FormatString("'->': no function \"%s\" in object \"%s\"", pCPos->Par.s->GetCStr(), pTargetVal->GetDataString().getData()).getData());
777 
778  // Save current position
779  pCurCtx->CPos = pCPos;
780  assert(pCurCtx->Func->GetCode() <= pCPos);
781 
782  // adjust parameter count
783  if (pCurVal + 1 - pPars > pFunc->GetParCount())
784  PopValues(pCurVal + 1 - pPars - pFunc->GetParCount());
785  else
786  PushNullVals(pFunc->GetParCount() - (pCurVal + 1 - pPars));
787 
788  // Call function
789  C4AulBCC *pNewCPos = Call(pFunc, pTargetVal, pPars, pDest);
790  if (pNewCPos)
791  {
792  // Jump
793  pCPos = pNewCPos;
794  fJump = true;
795  }
796 
797  break;
798  }
799 
800  case AB_DEBUG:
801 #ifndef NOAULDEBUG
802  if (C4AulDebug *pDebug = C4AulDebug::GetDebugger())
803  pDebug->DebugStep(pCPos, pCurVal);
804 #endif
805  break;
806  }
807 
808  // Continue
809  if (!fJump)
810  pCPos++;
811  }
812 
813  }
814  catch (C4AulError &)
815  {
816  // Save current position
817  assert(pCurCtx->Func->GetCode() <= pCPos);
818  pCurCtx->CPos = pCPos;
819  throw;
820  }
821 }
822 
823 C4AulBCC *C4AulExec::Call(C4AulFunc *pFunc, C4Value *pReturn, C4Value *pPars, C4PropList *pContext)
824 {
825  // No object given? Use current context
826  if (!pContext)
827  {
828  assert(pCurCtx >= Contexts);
829  pContext = pCurCtx->Obj;
830  }
831 
832  pFunc->CheckParTypes(pPars, true);
833 
834  // Script function?
835  C4AulScriptFunc *pSFunc = pFunc->SFunc();
836  if (pSFunc)
837  {
838  // Push a new context
839  C4AulScriptContext ctx;
840  ctx.Obj = pContext;
841  if (ctx.Obj && !ctx.Obj->Status)
842  throw C4AulExecError("using removed object");
843  ctx.Return = pReturn;
844  ctx.Pars = pPars;
845  ctx.Func = pSFunc;
846  ctx.CPos = nullptr;
847  PushContext(ctx);
848 
849  // Jump to code
850  return pSFunc->GetCode();
851  }
852  else
853  {
854  if (pContext && !pContext->Status)
855  throw C4AulExecError("using removed object");
856 
857 #ifdef DEBUGREC_SCRIPT
858  if (Config.General.DebugRec)
859  {
860  StdStrBuf sCallText;
861  if (pContext && pContext->GetObject())
862  sCallText.AppendFormat("Object(%d): ", pContext->GetObject()->Number);
863  sCallText.Append(pFunc->GetName());
864  sCallText.AppendChar('(');
865  for (int i=0; i<C4AUL_MAX_Par; ++i)
866  {
867  if (i) sCallText.AppendChar(',');
868  C4Value &rV = pPars[i];
869  if (rV.GetType() == C4V_String)
870  {
871  C4String *s = rV.getStr();
872  if (!s)
873  sCallText.Append("(Snull)");
874  else
875  {
876  sCallText.Append("\"");
877  sCallText.Append(s->GetData());
878  sCallText.Append("\"");
879  }
880  }
881  else
882  sCallText.Append(rV.GetDataString());
883  }
884  sCallText.AppendChar(')');
885  sCallText.AppendChar(';');
886  AddDbgRec(RCT_AulFunc, sCallText.getData(), sCallText.getLength()+1);
887  }
888 #endif
889 
890  // Execute
891 #ifdef _DEBUG
892  C4AulScriptContext *pCtx = pCurCtx;
893 #endif
894  if (pReturn > pCurVal)
895  PushValue(pFunc->Exec(pContext, pPars, true));
896  else
897  pReturn->Set(pFunc->Exec(pContext, pPars, true));
898 #ifdef _DEBUG
899  assert(pCtx == pCurCtx);
900 #endif
901 
902  // Remove parameters from stack
903  PopValuesUntil(pReturn);
904 
905  // Continue
906  return nullptr;
907  }
908 
909 }
910 
912 {
913  if (iTraceStart < 0)
914  iTraceStart = ContextStackSize();
915 }
916 
917 void C4AulExec::StartProfiling(C4ScriptHost *pProfiledScript)
918 {
919  // stop previous profiler run
920  if (fProfiling) StopProfiling();
921  fProfiling = true;
922  // resets profling times and starts recording the times
923  this->pProfiledScript = pProfiledScript;
925  tDirectExecStart = tNow; // in case profiling is started from DirectExec
926  tDirectExecTotal = 0;
927  for (C4AulScriptContext *pCtx = Contexts; pCtx <= pCurCtx; ++pCtx)
928  pCtx->tTime = tNow;
929 }
930 
931 void C4AulExec::PushContext(const C4AulScriptContext &rContext)
932 {
933  if (pCurCtx >= Contexts + MAX_CONTEXT_STACK - 1)
934  throw C4AulExecError("context stack overflow");
935  *++pCurCtx = rContext;
936  // Trace?
937  if (iTraceStart >= 0)
938  {
939  StdStrBuf Buf("T");
940  Buf.AppendChars('>', ContextStackSize() - iTraceStart);
941  pCurCtx->dump(Buf);
942  }
943  // Profiler: Safe time to measure difference afterwards
944  if (fProfiling) pCurCtx->tTime = C4TimeMilliseconds::Now();
945 }
946 
947 void C4AulExec::PopContext()
948 {
949  if (pCurCtx < Contexts)
950  throw C4AulExecError("internal error: context stack underflow");
951  // Profiler adding up times
952  if (fProfiling)
953  {
954  uint32_t dt = C4TimeMilliseconds::Now() - pCurCtx->tTime;
955  if (pCurCtx->Func)
956  pCurCtx->Func->tProfileTime += dt;
957  }
958  // Trace done?
959  if (iTraceStart >= 0)
960  {
961  if (ContextStackSize() <= iTraceStart)
962  {
963  iTraceStart = -1;
964  }
965  }
966  pCurCtx--;
967 }
968 
970 {
971  AulExec.StartProfiling(pScript);
972  if(pScript)
973  ResetTimes(pScript->GetPropList());
974  else
975  ResetTimes();
976 }
977 
979 {
980  if (!AulExec.IsProfiling()) return;
981  AulExec.StopProfiling();
982  // collect profiler times
983  C4AulProfiler Profiler;
984  Profiler.CollectEntry(nullptr, AulExec.tDirectExecTotal);
985  if(AulExec.pProfiledScript)
986  Profiler.CollectTimes(AulExec.pProfiledScript->GetPropList());
987  else
988  Profiler.CollectTimes();
989  Profiler.Show();
990 }
991 
992 void C4AulProfiler::CollectEntry(C4AulScriptFunc *pFunc, uint32_t tProfileTime)
993 {
994  // zero entries are not collected to have a cleaner list
995  if (!tProfileTime) return;
996  // add entry to list
997  Entry e;
998  e.pFunc = pFunc;
999  e.tProfileTime = tProfileTime;
1000  Times.push_back(e);
1001 }
1002 
1003 void C4AulProfiler::Show()
1004 {
1005  // sort by time
1006  std::sort(Times.rbegin(), Times.rend());
1007  // display them
1008  Log("Profiler statistics:");
1009  Log("==============================");
1010  typedef std::vector<Entry> EntryList;
1011  for (EntryList::iterator i = Times.begin(); i!=Times.end(); ++i)
1012  {
1013  Entry &e = (*i);
1014  LogF("%05ums\t%s", e.tProfileTime, e.pFunc ? (e.pFunc->GetFullName().getData()) : "Direct exec");
1015  }
1016  Log("==============================");
1017  // done!
1018 }
1019 
1020 C4Value C4AulExec::DirectExec(C4PropList *p, const char *szScript, const char *szContext, bool fPassErrors, C4AulScriptContext* context, bool parse_function)
1021 {
1022 #ifdef DEBUGREC_SCRIPT
1023  if (Config.General.DebugRec)
1024  {
1025  AddDbgRec(RCT_DirectExec, szScript, strlen(szScript)+1);
1026  int32_t iObjNumber = p && p->GetPropListNumbered() ? p->GetPropListNumbered()->Number : -1;
1027  AddDbgRec(RCT_DirectExec, &iObjNumber, sizeof(int32_t));
1028  }
1029 #endif
1030  // profiler
1031  StartDirectExec();
1033  if (p && p->IsStatic())
1034  script = p->IsStatic();
1035  else if (p && p->GetDef())
1036  script = p->GetDef();
1037  // Add a new function
1038  auto pFunc = std::make_unique<C4AulScriptFunc>(script, nullptr, nullptr, szScript);
1039  // Parse function
1040  try
1041  {
1042  if (parse_function)
1043  {
1044  // Expect a full function (e.g. "func foo() { return bar(); }")
1045  pFunc->ParseDirectExecFunc(&::ScriptEngine, context);
1046  }
1047  else
1048  {
1049  // Expect a single statement (e.g. "bar()")
1050  pFunc->ParseDirectExecStatement(&::ScriptEngine, context);
1051  }
1052  C4AulParSet Pars;
1053  C4Value vRetVal(Exec(pFunc.get(), p, Pars.Par, fPassErrors));
1054  // profiler
1055  StopDirectExec();
1056  return vRetVal;
1057  }
1058  catch (C4AulError &ex)
1059  {
1060  if(fPassErrors)
1061  throw;
1063  LogCallStack();
1064  StopDirectExec();
1065  return C4VNull;
1066  }
1067 }
1068 
1069 void C4AulProfiler::ResetTimes(C4PropListStatic * p)
1070 {
1071  // zero all profiler times of owned functions
1072  C4AulScriptFunc *pSFunc;
1073  for (C4String *pFn = p->EnumerateOwnFuncs(); pFn; pFn = p->EnumerateOwnFuncs(pFn))
1074  if ((pSFunc = p->GetFunc(pFn)->SFunc()))
1075  pSFunc->tProfileTime = 0;
1076 }
1077 
1078 void C4AulProfiler::CollectTimes(C4PropListStatic * p)
1079 {
1080  // collect all profiler times of owned functions
1081  C4AulScriptFunc *pSFunc;
1082  for (C4String *pFn = p->EnumerateOwnFuncs(); pFn; pFn = p->EnumerateOwnFuncs(pFn))
1083  if ((pSFunc = p->GetFunc(pFn)->SFunc()))
1084  CollectEntry(pSFunc, pSFunc->tProfileTime);
1085 }
1086 
1087 void C4AulProfiler::ResetTimes()
1088 {
1089  // zero all profiler times of owned functions
1090  ResetTimes(::ScriptEngine.GetPropList());
1091  // reset sub-scripts
1092  for (C4ScriptHost *pScript = ::ScriptEngine.Child0; pScript; pScript = pScript->Next)
1093  ResetTimes(pScript->GetPropList());
1094 }
1095 
1096 void C4AulProfiler::CollectTimes()
1097 {
1098  // collect all profiler times of owned functions
1099  CollectTimes(::ScriptEngine.GetPropList());
1100  // collect sub-scripts
1101  for (C4ScriptHost *pScript = ::ScriptEngine.Child0; pScript; pScript = pScript->Next)
1102  CollectTimes(pScript->GetPropList());
1103 }
C4Value * GetItem(const char *strName)
Definition: C4ValueMap.cpp:240
C4Value * Pars
Definition: C4AulExec.h:45
const char * getData() const
Definition: StdBuf.h:450
void ParseDirectExecStatement(C4AulScriptEngine *Engine, C4AulScriptContext *context=nullptr)
Definition: C4AulParse.cpp:843
C4Value * Return
Definition: C4AulExec.h:44
StdStrBuf GetData() const
Definition: C4StringTable.h:50
static bool FnLogCallStack(C4PropList *_this)
Definition: C4AulExec.cpp:134
C4Config Config
Definition: C4Config.cpp:837
C4Value Exec(C4AulScriptFunc *pSFunc, C4PropList *p, C4Value pPars[], bool fPassErrors)
C4AulErrorHandler * GetErrorHandler() const
Definition: C4Aul.h:175
C4String * EnumerateOwnFuncs(C4String *prev=0) const
Definition: C4PropList.cpp:917
C4PropListStatic * GetPropList()
Definition: C4Aul.h:153
C4String * getStr() const
Definition: C4Value.h:117
C4GameScriptHost GameScript
void ParseDirectExecFunc(C4AulScriptEngine *Engine, C4AulScriptContext *context=nullptr)
Definition: C4AulParse.cpp:834
C4AulScriptEngine ScriptEngine
Definition: C4Globals.cpp:43
union C4AulBCC::@81 Par
C4ConfigGeneral General
Definition: C4Config.h:252
void StopDirectExec()
Definition: C4AulExec.h:85
int SGetLine(const char *szText, const char *cpPosition)
Definition: Standard.cpp:443
const char * GetName() const
Definition: C4AulFunc.h:57
C4ValueMapData GlobalNamed
Definition: C4Aul.h:137
C4AulFunc * GetFunc(C4PropertyName k) const
Definition: C4PropList.h:107
StdStrBuf ReturnDump(StdStrBuf Dump=StdStrBuf(""))
Definition: C4AulExec.cpp:42
bool DebugLog(const char *strMessage)
Definition: C4Log.cpp:273
void LogCallStack()
Definition: C4AulExec.cpp:98
const char * GetCStr() const
Definition: C4StringTable.h:49
static void StopProfiling()
Definition: C4AulExec.cpp:978
C4String * _getStr() const
Definition: C4Value.h:126
void Set0()
Definition: C4Value.h:336
void SetBool(bool b)
Definition: C4Value.h:137
C4ScriptHost * pOrgScript
virtual class C4PropListStatic * IsStatic()
Definition: C4PropList.h:87
C4Value Par[C4AUL_MAX_Par]
Definition: C4AulFunc.h:30
bool getBool() const
Definition: C4Value.h:113
C4AulExec AulExec
Definition: C4AulExec.cpp:33
StdStrBuf GetDataString(int depth=10, const class C4PropListStatic *ignore_reference_parent=nullptr) const
Definition: C4Value.cpp:133
virtual const char * what() const noexcept
void Set(const C4Value &nValue)
Definition: C4Value.h:134
virtual C4PropListStatic * GetPropList()
Definition: C4ScriptHost.h:50
C4AulScriptFunc * Func
Definition: C4AulExec.h:46
static C4PropList * New(C4PropList *prototype=0)
Definition: C4PropList.cpp:64
virtual C4Object * GetObject()
Definition: C4PropList.cpp:644
const C4Value & GetItem(int32_t iElem) const
Definition: C4ValueArray.h:38
void AppendFormat(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:197
ALWAYS_INLINE bool CheckConversion(C4V_Type vtToType) const
Definition: C4Value.h:189
virtual C4Def const * GetDef() const
Definition: C4PropList.cpp:662
void AppendChar(char cChar)
Definition: StdBuf.h:596
C4ValueArray * _getArray() const
Definition: C4Value.h:127
C4AulScriptContext * GetContext(int iLevel)
Definition: C4AulExec.h:88
#define C4AUL_MAX_Par
Definition: C4AulFunc.h:26
bool DebugLogF(const char *strMessage...)
Definition: C4Log.cpp:281
int GetLineOfCode(C4AulBCC *bcc)
C4V_Type GetType() const
Definition: C4Value.h:161
const char * GetTypeName() const
Definition: C4Value.h:164
virtual bool GetPropertyByS(const C4String *k, C4Value *pResult) const
Definition: C4PropList.cpp:734
static C4AulDebug * GetDebugger()
Definition: C4AulDebug.h:31
C4AulBCC * CPos
Definition: C4AulExec.h:47
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:527
int32_t _getInt() const
Definition: C4Value.h:122
void AddDbgRec(C4RecordChunkType eType, const void *pData, int iSize)
Definition: C4Record.cpp:36
void SetArray(C4ValueArray *Array)
Definition: C4Value.h:139
C4Value Exec(C4PropList *p=nullptr, C4AulParSet *pPars=nullptr, bool fPassErrors=false)
Definition: C4AulFunc.h:73
static C4String * FnTranslate(C4PropList *_this, C4String *text)
Definition: C4AulExec.cpp:104
int32_t Status
Definition: C4PropList.h:170
virtual int GetParCount() const
void SetSlice(int32_t startIndex, int32_t endIndex, const C4Value &Val)
void SetInt(int32_t i)
Definition: C4Value.h:136
bool IsFrozen() const
Definition: C4PropList.h:133
static void StartProfiling(C4ScriptHost *pScript)
Definition: C4AulExec.cpp:969
int32_t GetSize() const
Definition: C4ValueArray.h:36
virtual void OnError(const char *msg)=0
const C4Value C4VNull
Definition: C4Value.cpp:32
const int MAX_CONTEXT_STACK
Definition: C4AulExec.h:26
bool IsFrozen() const
Definition: C4ValueArray.h:63
#define ReturnIfTranslationAvailable(script, key)
bool CheckParTypes(const C4Value pPars[], bool fPassErrors) const
Definition: C4AulFunc.cpp:58
bool isNull() const
Definition: StdBuf.h:449
C4DefScriptHost Script
Definition: C4Def.h:184
virtual C4PropListStatic * GetPropList()
C4AulBCC * GetCode()
C4AulExecError(const char *szError)
Definition: C4AulExec.cpp:35
bool Log(const char *szMessage)
Definition: C4Log.cpp:195
void StartTrace()
Definition: C4AulExec.cpp:911
C4TimeMilliseconds tTime
Definition: C4AulExec.h:48
C4PropList * Obj
Definition: C4AulExec.h:43
int Pow(int base, int exponent)
Definition: Standard.cpp:46
C4AulBCCType bccType
C4ValueArray * Array
Definition: C4Value.h:56
size_t getLength() const
Definition: StdBuf.h:453
int32_t DebugRec
Definition: C4Config.h:61
C4V_Data GetData() const
Definition: C4Value.h:160
virtual void SetPropertyByS(C4String *k, const C4Value &to)
Definition: C4PropList.cpp:929
C4PropListStatic * Parent
Definition: C4AulFunc.h:56
bool IsIdenticalTo(const C4Value &cmp) const
Definition: C4Value.h:149
void ClearPointers(C4Object *)
Definition: C4AulExec.cpp:140
const char * Script
const char * GetScript() const
Definition: C4ScriptHost.h:52
int GetContextDepth() const
Definition: C4AulExec.h:87
C4ValueArray * GetSlice(int32_t startIndex, int32_t endIndex)
C4PropList * _getPropList() const
Definition: C4Value.h:129
virtual C4PropListNumbered * GetPropListNumbered()
Definition: C4PropList.cpp:680
void dump(StdStrBuf Dump=StdStrBuf(""))
Definition: C4AulExec.cpp:92
virtual C4AulScriptFunc * SFunc()
Definition: C4AulFunc.h:66
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:253
void Copy()
Definition: StdBuf.h:475
StdCopyStrBuf ScriptName
Definition: C4ScriptHost.h:57
#define s
StdCopyStrBuf sMessage
Definition: C4Aul.h:46
C4ScriptHost * Next
Definition: C4ScriptHost.h:77
C4ScriptHost * Child0
Definition: C4Aul.h:123
C4Value DirectExec(C4PropList *p, const char *szScript, const char *szContext, bool fPassErrors=false, C4AulScriptContext *context=nullptr, bool parse_function=false)
Definition: C4AulExec.cpp:1020
static C4TimeMilliseconds Now()
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277
virtual int GetParCount() const
Definition: C4AulFunc.h:70
void StartDirectExec()
Definition: C4AulExec.h:84
StdStrBuf GetDataString() const
Definition: C4PropList.cpp:253