GCC Code Coverage Report


Directory: avs_core/
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 2.9% 15 / 0 / 513
Functions: 8.9% 4 / 0 / 45
Branches: 0.6% 8 / 0 / 1329

core/parser/expression.cpp
Line Branch Exec Source
1 // Avisynth v2.5. Copyright 2002 Ben Rudiak-Gould et al.
2 // http://avisynth.nl
3
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 2 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program; if not, write to the Free Software
16 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA, or visit
17 // http://www.gnu.org/copyleft/gpl.html .
18 //
19 // Linking Avisynth statically or dynamically with other modules is making a
20 // combined work based on Avisynth. Thus, the terms and conditions of the GNU
21 // General Public License cover the whole combination.
22 //
23 // As a special exception, the copyright holders of Avisynth give you
24 // permission to link Avisynth with independent modules that communicate with
25 // Avisynth solely through the interfaces defined in avisynth.h, regardless of the license
26 // terms of these independent modules, and to copy and distribute the
27 // resulting combined work under terms of your choice, provided that
28 // every copy of the combined work is accompanied by a complete copy of
29 // the source code of Avisynth (the version of Avisynth used to produce the
30 // combined work), being distributed under the terms of the GNU General
31 // Public License plus this exception. An independent module is a module
32 // which is not derived from or based on Avisynth, such as 3rd-party filters,
33 // import and export plugins, or graphical user interfaces.
34
35
36 #include "expression.h"
37 #include "script.h"
38 #include "../exception.h"
39 #include "../internal.h"
40 #include "../InternalEnvironment.h"
41 #ifdef AVS_WINDOWS
42 #include <avs/win.h>
43 #else
44 #include <avs/posix.h>
45 #endif
46 #include <cassert>
47 #include <vector>
48
49
50 class BreakStmtException
51 {
52 };
53
54 class ContinueStmtException
55 {
56 };
57
58 6 AVSValue ExpRootBlock::Evaluate(IScriptEnvironment* env)
59 {
60 6 AVSValue retval;
61
62 try {
63
2/4
✓ Branch 4 → 5 taken 6 times.
✗ Branch 4 → 12 not taken.
✓ Branch 5 → 6 taken 6 times.
✗ Branch 5 → 10 not taken.
6 retval = exp->Evaluate(env);
64 }
65 catch (const ReturnExprException &e) {
66 retval = e.value;
67 }
68
69 6 return retval;
70 }
71
72 AVSValue ExpSequence::Evaluate(IScriptEnvironment* env)
73 {
74 AVSValue last = a->Evaluate(env);
75 if (last.IsClip()) env->SetVar("last", last);
76 return b->Evaluate(env);
77 }
78
79 6 AVSValue ExpExceptionTranslator::Evaluate(IScriptEnvironment* env)
80 {
81 try {
82 6 SehGuard seh_guard;
83
1/2
✓ Branch 4 → 5 taken 6 times.
✗ Branch 4 → 10 not taken.
6 return exp->Evaluate(env);
84 6 }
85 catch (const IScriptEnvironment::NotFound&) {
86 throw;
87 }
88 catch (const AvisynthError&) {
89 throw;
90 }
91 catch (const BreakStmtException&) {
92 throw;
93 }
94 catch (const ContinueStmtException&) {
95 throw;
96 }
97 catch (const ReturnExprException&) {
98 throw;
99 }
100 catch (const SehException &seh) {
101 if (seh.m_msg)
102 env->ThrowError(seh.m_msg);
103 else
104 env->ThrowError("Evaluate: System exception - 0x%x", seh.m_code);
105 }
106 catch (...) {
107 env->ThrowError("Evaluate: Unhandled C++ exception!");
108 }
109 return 0;
110 }
111
112
113 AVSValue ExpTryCatch::Evaluate(IScriptEnvironment* env)
114 {
115 AVSValue result;
116 try {
117 return ExpExceptionTranslator::Evaluate(env);
118 }
119 catch (const AvisynthError &ae) {
120 env->SetVar(id, ae.msg);
121 return catch_block->Evaluate(env);
122 }
123 }
124
125
126 6 AVSValue ExpLine::Evaluate(IScriptEnvironment* env)
127 {
128 try {
129
1/2
✓ Branch 2 → 3 taken 6 times.
✗ Branch 2 → 5 not taken.
6 return ExpExceptionTranslator::Evaluate(env);
130 }
131 catch (const AvisynthError &ae) {
132 env->ThrowError("%s\n(%s, line %d)", ae.msg, filename, line);
133 }
134 return 0;
135 }
136
137 AVSValue ExpBlockConditional::Evaluate(IScriptEnvironment* env)
138 {
139 AVSValue result;
140 env->GetVarTry("last", &result);
141
142 AVSValue cond = If->Evaluate(env);
143 if (!cond.IsBool())
144 env->ThrowError("if: condition must be boolean (true/false)");
145 if (cond.AsBool())
146 {
147 if (Then) // note: "Then" can also be NULL if its block is empty
148 result = Then->Evaluate(env);
149 }
150 else if (Else) // note: "Else" can also be NULL if its block is empty
151 result = Else->Evaluate(env);
152
153 if (result.IsClip())
154 env->SetVar("last", result);
155
156 return result;
157 }
158
159 AVSValue ExpWhileLoop::Evaluate(IScriptEnvironment* env)
160 {
161 AVSValue result;
162 env->GetVarTry("last", &result);
163
164 AVSValue cond;
165 do {
166 cond = condition->Evaluate(env);
167 if (!cond.IsBool())
168 env->ThrowError("while: condition must be boolean (true/false)");
169
170 if (!cond.AsBool())
171 break;
172
173 if (body)
174 {
175 try
176 {
177 result = body->Evaluate(env);
178 if (result.IsClip())
179 env->SetVar("last", result);
180 }
181 catch(const BreakStmtException&)
182 {
183 AVSValue result2;
184 env->GetVarTry("last", &result2);
185 if (result2.IsClip())
186 result = result2;
187 break;
188 }
189 catch (const ContinueStmtException&)
190 {
191 AVSValue result2;
192 env->GetVarTry("last", &result2);
193 if (result2.IsClip())
194 result = result2;
195 }
196 }
197 }
198 while (true);
199
200 return result;
201 }
202
203 AVSValue ExpForLoop::Evaluate(IScriptEnvironment* env)
204 {
205 const AVSValue initVal = init->Evaluate(env),
206 limitVal = limit->Evaluate(env),
207 stepVal = step->Evaluate(env);
208
209 if (!initVal.IsInt())
210 env->ThrowError("for: initial value must be int");
211 if (!limitVal.IsInt())
212 env->ThrowError("for: final value must be int");
213 if (!stepVal.IsInt())
214 env->ThrowError("for: step value must be int");
215 if (stepVal.AsLong() == 0)
216 env->ThrowError("for: step value must be non-zero");
217
218 const int64_t iLimit = limitVal.AsLong();
219 const int64_t iStep = stepVal.AsLong();
220 int64_t i = initVal.AsLong();
221
222 AVSValue result;
223 env->GetVarTry("last", &result);
224
225 env->SetVar(id, initVal);
226 while (iStep > 0 ? i <= iLimit : i >= iLimit)
227 {
228 if (body)
229 {
230 try
231 {
232 result = body->Evaluate(env);
233 if (result.IsClip())
234 env->SetVar("last", result);
235 }
236 catch(const BreakStmtException&)
237 {
238 AVSValue result2;
239 env->GetVarTry("last", &result2);
240 if (result2.IsClip())
241 result = result2;
242 break;
243 }
244 catch (const ContinueStmtException&)
245 {
246 AVSValue result2;
247 env->GetVarTry("last", &result2);
248 if (result2.IsClip())
249 result = result2;
250 }
251 }
252
253 AVSValue idVal = env->GetVar(id); // may have been updated in body
254 if (!idVal.IsInt())
255 env->ThrowError("for: loop variable '%s' has been assigned a non-int value", id);
256 i = idVal.AsLong() + iStep;
257 // store back to smaller type
258 if (i >= INT_MIN && i <= INT_MAX)
259 env->SetVar(id, (int)i);
260 else
261 env->SetVar(id, i);
262 }
263 return result; // overall result is that of final body evaluation (if any)
264 }
265
266 AVSValue ExpBreak::Evaluate(IScriptEnvironment* env)
267 {
268 throw BreakStmtException();
269 }
270
271 AVSValue ExpContinue::Evaluate(IScriptEnvironment* env)
272 {
273 throw ContinueStmtException();
274 }
275
276 AVSValue ExpConditional::Evaluate(IScriptEnvironment* env)
277 {
278 AVSValue cond = If->Evaluate(env);
279 if (!cond.IsBool())
280 env->ThrowError("Evaluate: left of `?' must be boolean (true/false)");
281 return (cond.AsBool() ? Then : Else)->Evaluate(env);
282 }
283
284 AVSValue ExpReturn::Evaluate(IScriptEnvironment* env)
285 {
286 ReturnExprException ret;
287 ret.value = value->Evaluate(env);
288 throw ret;
289 }
290
291
292
293 /**** Operators ****/
294
295 AVSValue ExpOr::Evaluate(IScriptEnvironment* env)
296 {
297 AVSValue x = a->Evaluate(env);
298 if (!x.IsBool())
299 env->ThrowError("Evaluate: left operand of || must be boolean (true/false)");
300 if (x.AsBool())
301 return x;
302 AVSValue y = b->Evaluate(env);
303 if (!y.IsBool())
304 env->ThrowError("Evaluate: right operand of || must be boolean (true/false)");
305 return y;
306 }
307
308
309 AVSValue ExpAnd::Evaluate(IScriptEnvironment* env)
310 {
311 AVSValue x = a->Evaluate(env);
312 if (!x.IsBool())
313 env->ThrowError("Evaluate: left operand of && must be boolean (true/false)");
314 if (!x.AsBool())
315 return x;
316 AVSValue y = b->Evaluate(env);
317 if (!y.IsBool())
318 env->ThrowError("Evaluate: right operand of && must be boolean (true/false)");
319 return y;
320 }
321
322
323 AVSValue ExpEqual::Evaluate(IScriptEnvironment* env)
324 {
325 AVSValue x = a->Evaluate(env);
326 AVSValue y = b->Evaluate(env);
327 if (x.IsBool() && y.IsBool()) {
328 return x.AsBool() == y.AsBool();
329 }
330 else if (x.IsInt() && y.IsInt()) { // true for any 32/64 bit data
331 return x.AsLong() == y.AsLong(); // work with 64 bits
332 }
333 else if (x.IsFloatfStrict() && y.IsFloatfStrict()) {
334 return x.AsFloatf() == y.AsFloatf();
335 }
336 else if (x.IsFloat() && y.IsFloat()) {
337 return x.AsFloat() == y.AsFloat(); // AsFloat returns double
338 }
339 else if (x.IsClip() && y.IsClip()) {
340 return x.AsClip() == y.AsClip();
341 }
342 else if (x.IsString() && y.IsString()) {
343 return !lstrcmpi(x.AsString(), y.AsString());
344 }
345 else if (x.IsFunction() && y.IsFunction()) {
346 return x.AsFunction() == y.AsFunction();
347 }
348 else {
349 env->ThrowError("Evaluate: operands of `==' and `!=' must be comparable");
350 return 0;
351 }
352 }
353
354
355 AVSValue ExpLess::Evaluate(IScriptEnvironment* env)
356 {
357 AVSValue x = a->Evaluate(env);
358 AVSValue y = b->Evaluate(env);
359 if (x.IsInt() && y.IsInt()) { // true for any 32/64 bit data
360 return x.AsLong() < y.AsLong(); // work with 64 bits
361 }
362 else if (x.IsFloatfStrict() && y.IsFloatfStrict()) {
363 return x.AsFloatf() < y.AsFloatf();
364 }
365 else if (x.IsFloat() && y.IsFloat()) {
366 return x.AsFloat() < y.AsFloat();
367 }
368 else if (x.IsString() && y.IsString()) {
369 return _stricmp(x.AsString(),y.AsString()) < 0 ? true : false;
370 }
371 else {
372 env->ThrowError("Evaluate: operands of `<' and friends must be string or numeric");
373 return 0;
374 }
375 }
376
377 AVSValue ExpPlus::Evaluate(IScriptEnvironment* env)
378 {
379 AVSValue x = a->Evaluate(env);
380 AVSValue y = b->Evaluate(env);
381 if (x.IsClip() && y.IsClip()) {
382 AVSValue arg[3] = { x, y, 0 };
383 return env->Invoke("UnalignedSplice", AVSValue(arg, 3));
384 }
385 else if (x.IsInt() && y.IsInt()) { // they are true for any 32/64 bits inside
386 int64_t result = x.AsLong() + y.AsLong();
387 // keep the smaller type
388 if (result >= INT_MIN && result <= INT_MAX)
389 return (int)result;
390 return result;
391 }
392 else if (x.IsFloatfStrict() && y.IsFloatfStrict())
393 return x.AsFloatf() + y.AsFloatf();
394 else if (x.IsFloat() && y.IsFloat())
395 return x.AsFloat() + y.AsFloat(); // AsFloat returns double
396 else if (x.IsString() && y.IsString())
397 return env->Sprintf("%s%s", x.AsString(), y.AsString());
398 else {
399 env->ThrowError("Evaluate: operands of `+' must both be numbers, strings, or clips");
400 return 0;
401 }
402 }
403
404
405 AVSValue ExpDoublePlus::Evaluate(IScriptEnvironment* env)
406 {
407 AVSValue x = a->Evaluate(env);
408 AVSValue y = b->Evaluate(env);
409 if (x.IsClip() && y.IsClip()) {
410 AVSValue arg[3] = { x, y, 0 };
411 return env->Invoke("AlignedSplice", AVSValue(arg, 3));
412 }
413 else {
414 env->ThrowError("Evaluate: operands of `++' must be clips");
415 return 0;
416 }
417 }
418
419
420 AVSValue ExpMinus::Evaluate(IScriptEnvironment* env)
421 {
422 AVSValue x = a->Evaluate(env);
423 AVSValue y = b->Evaluate(env);
424 if (x.IsInt() && y.IsInt()) { // they are true for any 32/64 bits inside
425 int64_t result = x.AsLong() - y.AsLong();
426 // keep the smaller type
427 if (result >= INT_MIN && result <= INT_MAX)
428 return (int)result;
429 return result;
430 }
431 else if (x.IsFloatfStrict() && y.IsFloatfStrict())
432 return x.AsFloatf() - y.AsFloatf();
433 else if (x.IsFloat() && y.IsFloat())
434 return x.AsFloat() - y.AsFloat(); // AsFloat returns double
435 else {
436 env->ThrowError("Evaluate: operands of `-' must be numeric");
437 return 0;
438 }
439 }
440
441
442 AVSValue ExpMult::Evaluate(IScriptEnvironment* env)
443 {
444 AVSValue x = a->Evaluate(env);
445 AVSValue y = b->Evaluate(env);
446 if (x.IsInt() && y.IsInt()) { // they are true for any 32/64 bits inside
447 int64_t result = x.AsLong() * y.AsLong();
448 // keep the smaller type
449 if (result >= INT_MIN && result <= INT_MAX)
450 return (int)result;
451 return result;
452 }
453 else if (x.IsFloatfStrict() && y.IsFloatfStrict())
454 return x.AsFloatf() * y.AsFloatf();
455 else if (x.IsFloat() && y.IsFloat())
456 return x.AsFloat() * y.AsFloat(); // AsFloat returns double
457 else {
458 env->ThrowError("Evaluate: operands of `*' must be numeric");
459 return 0;
460 }
461 }
462
463
464 AVSValue ExpDiv::Evaluate(IScriptEnvironment* env)
465 {
466 AVSValue x = a->Evaluate(env);
467 AVSValue y = b->Evaluate(env);
468 if (x.IsInt() && y.IsInt()) { // they are true for any 32/64 bits inside
469 if (y.AsLong() == 0)
470 env->ThrowError("Evaluate: division by zero");
471 int64_t result = x.AsLong() / y.AsLong();
472 // keep the smaller type
473 if (result >= INT_MIN && result <= INT_MAX)
474 return (int)result;
475 return result;
476 }
477 else if (x.IsFloatfStrict() && y.IsFloatfStrict())
478 return x.AsFloatf() / y.AsFloatf();
479 else if (x.IsFloat() && y.IsFloat())
480 return x.AsFloat() / y.AsFloat(); // AsFloat returns double
481 else {
482 env->ThrowError("Evaluate: operands of `/' must be numeric");
483 return 0;
484 }
485 }
486
487
488 AVSValue ExpMod::Evaluate(IScriptEnvironment* env)
489 {
490 AVSValue x = a->Evaluate(env);
491 AVSValue y = b->Evaluate(env);
492 if (x.IsInt() && y.IsInt()) { // they are true for any 32/64 bits inside
493 if (y.AsLong() == 0)
494 env->ThrowError("Evaluate: division by zero");
495 int64_t result = x.AsLong() % y.AsLong();
496 // keep the smaller type
497 if (result >= INT_MIN && result <= INT_MAX)
498 return (int)result;
499 return result;
500 }
501 else {
502 env->ThrowError("Evaluate: operands of `%%' must be integers");
503 return 0;
504 }
505 }
506
507
508 AVSValue ExpNegate::Evaluate(IScriptEnvironment* env)
509 {
510 AVSValue x = e->Evaluate(env);
511 if (x.IsInt()) { // true for any 32/64 bits inside
512 // Note: In the old 32-bit integer case, the special value -(INT_MIN) resulted in a negative value
513 // instead of a positive number.
514 // This change in version 11 is not 100% compatible with the old behavior.
515 int64_t result = -x.AsLong();
516 // keep the smaller type
517 if (result >= INT_MIN && result <= INT_MAX)
518 return (int)result;
519 return result;
520 }
521 else if (x.IsFloatfStrict())
522 return -x.AsFloatf();
523 else if (x.IsFloat())
524 return -x.AsFloat(); // AsFloat returns double
525 else {
526 env->ThrowError("Evaluate: unary minus can only by used with numbers");
527 return 0;
528 }
529 }
530
531
532 AVSValue ExpNot::Evaluate(IScriptEnvironment* env)
533 {
534 AVSValue x = e->Evaluate(env);
535 if (x.IsBool())
536 return !x.AsBool();
537 else {
538 env->ThrowError("Evaluate: operand of `!' must be boolean (true/false)");
539 return 0;
540 }
541 }
542
543
544 3 AVSValue ExpVariableReference::Evaluate(IScriptEnvironment* env)
545 {
546
1/2
✓ Branch 2 → 3 taken 3 times.
✗ Branch 2 → 45 not taken.
3 AVSValue result;
547
548 // first look for a genuine variable
549 // Don't add a cache to this one, it's a Var
550
2/4
✓ Branch 3 → 4 taken 3 times.
✗ Branch 3 → 43 not taken.
✓ Branch 4 → 5 taken 3 times.
✗ Branch 4 → 7 not taken.
3 if (env->GetVarTry(name, &result)) {
551
1/2
✓ Branch 5 → 6 taken 3 times.
✗ Branch 5 → 43 not taken.
3 return result;
552 }
553 else {
554 // Swap order to match ::Call below -- Gavino Jan 2010
555
556 // next look for an argless function
557 if (!env->InvokeTry(&result, name, AVSValue(0,0)))
558 {
559 // finally look for a single-arg function taking implicit "last"
560 AVSValue last;
561 if (!env->GetVarTry("last", &last) || !env->InvokeTry(&result, name, last))
562 {
563 // and we are giving a last chance, the variable may exist here after the avsi autoload mechanism
564 if (env->GetVarTry(name, &result)) {
565 return result;
566 }
567 env->ThrowError("I don't know what '%s' means.", name);
568 return 0;
569 }
570 }
571 }
572
573 return result;
574 3 }
575
576
577 AVSValue ExpAssignment::Evaluate(IScriptEnvironment* env)
578 {
579 env->SetVar(lhs, rhs->Evaluate(env));
580 if (withret) {
581 AVSValue last;
582 AVSValue result;
583
584 if (!env->GetVarTry("last", &last) || !env->InvokeTry(&result, lhs, last))
585 {
586 // and we are giving a last chance, the variable may exist here after the avsi autoload mechanism
587 if (env->GetVarTry(lhs, &result)) {
588 return result;
589 }
590 env->ThrowError("I don't know what '%s' means.", lhs);
591 return 0;
592 }
593 }
594 return AVSValue();
595 }
596
597
598 AVSValue ExpGlobalAssignment::Evaluate(IScriptEnvironment* env)
599 {
600 env->SetGlobalVar(lhs, rhs->Evaluate(env));
601 return AVSValue();
602 }
603
604
605 ExpFunctionCall::ExpFunctionCall( const char* _name, const PExpression& _func, PExpression* _arg_exprs,
606 const char** _arg_expr_names, int _arg_expr_count, bool _oop_notation )
607 : name(_name), func(_func), arg_expr_count(_arg_expr_count), oop_notation(_oop_notation)
608 {
609 arg_exprs = new PExpression[arg_expr_count];
610 arg_expr_names = new const char*[arg_expr_count];
611 for (int i=0; i<arg_expr_count; ++i) {
612 arg_exprs[i] = _arg_exprs[i];
613 arg_expr_names[i] = _arg_expr_names[i];
614 }
615 }
616
617 ExpFunctionCall::~ExpFunctionCall(void)
618 {
619 delete[] arg_exprs;
620 delete[] arg_expr_names;
621 }
622
623 AVSValue ExpFunctionCall::Evaluate(IScriptEnvironment* env)
624 {
625 AVSValue result;
626 InternalEnvironment *env2 = static_cast<InternalEnvironment*>(env);
627
628 const char* real_name = name;
629 const Function* real_func = nullptr;
630 AVSValue eval_result; // function must be exist until the function call ends
631 if (real_name == nullptr) {
632 // if name is not given, evaluate expression to get the function
633 eval_result = func->Evaluate(env);
634 if (!eval_result.IsFunction()) {
635 env->ThrowError(
636 "Script error: '%s' cannot be called. Give me a function!",
637 GetAVSTypeName(eval_result));
638 }
639 //auto& func = eval_result.AsFunction(); // c++ strict conformance: cannot Convert PFunction to PFunction&
640 const PFunction& func = eval_result.AsFunction();
641 real_name = func->GetLegacyName();
642 real_func = func->GetDefinition();
643 }
644
645 assert(real_name || real_func);
646
647 // Keep an entry at the beginning: 0th is implicite_last
648 std::vector<AVSValue> args(arg_expr_count+1, AVSValue());
649 for (int a = 0; a < arg_expr_count; ++a)
650 args[a + 1] = arg_exprs[a]->Evaluate(env);
651
652 AVSValue implicit_last = oop_notation ? AVSValue() : env2->GetVarDef("last");
653 args[0] = implicit_last;
654 bool notfound = false;
655 try
656 { // Invoke can always throw by calling a constructor of a filter that throws
657 // first give args with implicite_last as a separate parameter
658 // and w/o implicit_last in the args array
659 if (env2->Invoke_(&result, implicit_last,
660 real_name, real_func, AVSValue(args.data() + 1, arg_expr_count), arg_expr_names))
661 return result;
662 }
663 catch (const IScriptEnvironment::NotFound&) {
664 notfound = true;
665 }
666
667 if (notfound && implicit_last.IsClip())
668 {
669 // Give a final chance with a forced implicite last trial for functions like "Animate"
670 // which has with-clip and clipless function signatures.
671 // For cases when clipless signature is found but during instantiating a function
672 // inside its internal expression parameter fails to intantiate without a clip input
673 // and it turnes out that the signature with implicit_last would work.
674 try
675 {
676 std::vector<const char *> arg_expr_names2(arg_expr_count + 1);
677 for (int a = 0; a < arg_expr_count; ++a)
678 arg_expr_names2[a + 1] = arg_expr_names[a];
679 arg_expr_names2[0] = nullptr;
680 // with impicite_last inside the array
681 if (env2->Invoke_(&result, AVSValue(),
682 real_name, real_func, AVSValue(args.data(), arg_expr_count + 1), arg_expr_names2.data()))
683 return result;
684 }
685 catch (const IScriptEnvironment::NotFound&) {}
686 }
687
688 if (real_name == nullptr) {
689 // anonymous function
690 env->ThrowError("Script error: Invalid arguments to %s.",
691 eval_result.AsFunction()->ToString(env));
692 }
693 else {
694 AVSValue var;
695 if (env->GetVarTry(real_name, &var) && var.IsFunction() && var.AsFunction()->GetLegacyName()) {
696 real_name = var.AsFunction()->GetLegacyName();
697 }
698 env->ThrowError(env->FunctionExists(real_name) ?
699 "Script error: Invalid arguments to function '%s'." :
700 "Script error: There is no function named '%s'.", real_name);
701 }
702
703 assert(0); // we should never get here
704 return 0;
705 }
706
707
708 class WrappedFunction : public IFunction
709 {
710 public:
711 WrappedFunction(const char* const name)
712 : name(name) { }
713 virtual const char* ToString(IScriptEnvironment* env) {
714 return env->Sprintf("Wrapped Function: %s", name);
715 }
716 virtual const char* GetLegacyName() { return name; }
717 virtual const Function* GetDefinition() { return nullptr; }
718 virtual CaptureVars GetCaptures() { return CaptureVars(); }
719
720 private:
721 const char* const name;
722 };
723
724 ExpFunctionWrapper::ExpFunctionWrapper(const char* name)
725 : func(new WrappedFunction(name)), name(name) { }
726
727 AVSValue ExpFunctionWrapper::Evaluate(IScriptEnvironment* env) {
728 AVSValue result;
729 if (env->GetVarTry(name, &result) && result.IsFunction()) {
730 // if reference variable exists, returns it
731 return result;
732 }
733 return func;
734 }
735
736
737 ExpFunctionDefinition::ExpFunctionDefinition(
738 const PExpression& body,
739 const char* name, const char* param_types,
740 const bool* _param_floats, const char** _param_names, int param_count,
741 const char** _var_names, int var_count,
742 const char* filename, int line)
743 : body(body)
744 , name(name)
745 , param_types(param_types)
746 , param_floats(nullptr)
747 , param_names(nullptr)
748 , var_count(var_count)
749 , var_names(nullptr)
750 , filename(filename)
751 , line(line)
752 {
753 param_floats = new bool[param_count];
754 memcpy(param_floats, _param_floats, param_count * sizeof(const bool));
755
756 param_names = new const char*[param_count];
757 memcpy(param_names, _param_names, param_count * sizeof(const char*));
758
759 if (var_count > 0) {
760 var_names = new const char*[var_count];
761 memcpy(var_names, _var_names, var_count * sizeof(const char*));
762 }
763 }
764
765 AVSValue ExpFunctionDefinition::Evaluate(IScriptEnvironment* env)
766 {
767 AVSValue func = PFunction(new FunctionInstance(this, env));
768 if (name == nullptr) {
769 return func;
770 }
771 env->SetGlobalVar(name, func);
772 return AVSValue();
773 }
774
775
776
777 FunctionInstance::FunctionInstance(ExpFunctionDefinition* pdef, IScriptEnvironment* env)
778 : data(), pdef(pdef), pdef_ref(pdef), var_data(nullptr)
779 {
780 data.apply = Execute_;
781
782 if (pdef->name) {
783 std::string cn("_");
784 cn.append(pdef->name);
785 data.name = pdef->name;
786 data.canon_name = env->SaveString(cn.c_str());
787 }
788
789 data.param_types = pdef->param_types;
790 data.user_data = this;
791 data.dll_path = nullptr;
792
793 if (pdef->var_count > 0) {
794 AVSValue result;
795 var_data = new AVSValue[pdef->var_count];
796 for (int i = 0; i < pdef->var_count; ++i) {
797 if (!env->GetVarTry(pdef->var_names[i], &result)) {
798 env->ThrowError("No variable named '%s'", pdef->var_names[i]);
799 }
800 var_data[i] = result;
801 }
802 }
803 }
804 FunctionInstance::~FunctionInstance() {
805 delete[] var_data;
806 }
807
808 const char* FunctionInstance::ToString(IScriptEnvironment* env)
809 {
810 if (pdef->name) {
811 return env->Sprintf("Function: %s defined at %s, line %d", pdef->name, pdef->filename, pdef->line);
812 }
813 else {
814 return env->Sprintf("Function: defined at %s, line %d", pdef->filename, pdef->line);
815 }
816 }
817
818 static bool is_within_int_in_float32_range(int64_t value) {
819 // 2^24
820 return value >= -16777216 && value <= 16777216;
821 }
822
823 AVSValue FunctionInstance::Execute(const AVSValue& args, IScriptEnvironment* env)
824 {
825 env->PushContext();
826 for (int i = 0; i < pdef->var_count; ++i) {
827 env->SetVar(pdef->var_names[i], var_data[i]);
828 }
829 for (int i = 0; i<args.ArraySize(); ++i)
830 env->SetVar(pdef->param_names[i],
831 // Same as in ScriptFunction::Execute and AVSValue FunctionInstance::Execute
832
833 // force float args that are actually long/int (int64) to be float/double (depending on the range)
834 // opportunity to fit into the smaller float size
835 (pdef->param_floats[i] && args[i].IsInt()) ?
836 is_within_int_in_float32_range(args[i].AsLong()) ? (float)args[i].AsLong() : (double)args[i].AsLong() :
837 args[i]
838 );
839
840 AVSValue result;
841 try {
842 result = pdef->body->Evaluate(env);
843 }
844 catch (...) {
845 env->PopContext();
846 throw;
847 }
848
849 env->PopContext();
850 return result;
851 }
852
853 AVSValue FunctionInstance::Execute_(AVSValue args, void* user_data, IScriptEnvironment* env)
854 {
855 FunctionInstance* self = (FunctionInstance*)user_data;
856 return self->Execute(args, env);
857 }
858