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