1 // Machine.cpp A machine to run AS3 code, with AS2 code in the future
2 //
3 // Copyright (C) 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc.
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 //
20
21 #include "Machine.h"
22 #include "as_object.h"
23 #include "ClassHierarchy.h"
24 #include "namedStrings.h"
25 #include "AbcBlock.h"
26 #include "MultiName.h"
27 #include "fn_call.h"
28 #include "abc_function.h"
29 #include "VM.h"
30 #include "Globals.h"
31 #include "Global_as.h"
32 #include "Class.h"
33 #include "CodeStream.h"
34 #include "SWF.h"
35
36 namespace gnash {
37 namespace abc {
38
39 /// The type of exceptions thrown by ActionScript.
40 class ASException
41 {
42 public:
43 as_value mValue;
44
ASException(as_value & v)45 ASException(as_value &v) { mValue = v; }
ASException()46 ASException() { mValue.set_undefined(); }
47 };
48
49 class ASReferenceError : public ASException
50 {
51 public:
ASReferenceError()52 ASReferenceError() : ASException()
53 {/**/}
54 };
55
56 class ASTypeError : public ASException
57 {
58 public:
ASTypeError()59 ASTypeError() : ASException()
60 {}
61 };
62
63 /// Functions for getting pool constants.
64 //
65 /// TODO: it's quite possible for a malformed SWF to ask for out-of-bounds
66 /// pool access, although at the moment it's mainly Gnash bugs causing this.
67 /// Throwing an exception is good here, but it's not clear which one.
68 namespace {
69
70 inline const std::string&
pool_string(std::uint32_t index,AbcBlock * pool)71 pool_string(std::uint32_t index, AbcBlock *pool)
72 {
73 if (!pool) throw ASException();
74 try {
75 return pool->stringPoolAt(index);
76 }
77 catch (std::range_error& e) {
78 throw ASException();
79 }
80 }
81
82 inline int
pool_int(std::uint32_t index,AbcBlock * pool)83 pool_int(std::uint32_t index, AbcBlock *pool)
84 {
85 if (!pool) throw ASException();
86 try {
87 return pool->integerPoolAt(index);
88 }
89 catch (std::range_error& e) {
90 throw ASException();
91 }
92 }
93
94 inline unsigned int
pool_uint(std::uint32_t index,AbcBlock * pool)95 pool_uint(std::uint32_t index, AbcBlock *pool)
96 {
97 if (!pool) throw ASException();
98 try {
99 return pool->uIntegerPoolAt(index);
100 }
101 catch (std::range_error& e) {
102 throw ASException();
103 }
104 }
105
106 inline double
pool_double(std::uint32_t index,AbcBlock * pool)107 pool_double(std::uint32_t index, AbcBlock *pool)
108 {
109 if (!pool) throw ASException();
110 try {
111 return pool->doublePoolAt(index);
112 }
113 catch (std::range_error& e) {
114 throw ASException();
115 }
116 }
117
118 inline Namespace*
pool_namespace(std::uint32_t index,AbcBlock * pool)119 pool_namespace(std::uint32_t index, AbcBlock *pool)
120 {
121 if (!pool) throw ASException();
122 try {
123 return pool->namespacePoolAt(index);
124 }
125 catch (std::range_error& e) {
126 throw ASException();
127 }
128
129 }
130
131 inline Method*
pool_method(std::uint32_t index,AbcBlock * pool)132 pool_method(std::uint32_t index, AbcBlock* pool)
133 {
134 if (!pool) throw ASException();
135 try {
136 return pool->methodPoolAt(index);
137 }
138 catch (std::range_error& e) {
139 throw ASException();
140 }
141 }
142
143 inline Class*
pool_script(std::uint32_t index,AbcBlock * pool)144 pool_script(std::uint32_t index, AbcBlock* pool)
145 {
146 if (!pool) throw ASException();
147 try {
148 return pool->classPoolAt(index);
149 }
150 catch (std::range_error& e) {
151 throw ASException();
152 }
153 }
154
155 // Don't make this a reference or you'll taint the pool.
156 inline MultiName
pool_name(std::uint32_t index,AbcBlock * pool)157 pool_name(std::uint32_t index, AbcBlock* pool)
158 {
159 if (!pool) throw ASException();
160 try {
161 MultiName multiname = pool->multinamePoolAt(index);
162 return multiname;
163 }
164 catch (std::range_error& e) {
165 throw ASException();
166 }
167 }
168
169 } // anonymous namespace
170
171 /// ENSURE_NUMBER makes sure that the given argument is a number,
172 /// calling the valueOf method if necessary -- it's a macro so that
173 /// the valueOf method may be pushed if needed, and then whatever
174 /// opcode asked for this will be re-entered.
175 #define ENSURE_NUMBER(vte) \
176 { \
177 as_value *e = &vte; \
178 if (e->is_object()) \
179 { \
180 Property *b = e->to_object(*_global)->findProperty(NSV::PROP_VALUE_OF, 0); \
181 if (b) \
182 { \
183 mStream->seekTo(opStart); \
184 pushGet(e->to_object(*_global), *e, b); \
185 break; \
186 } \
187 } \
188 } /* end of ENSURE_NUMBER */
189
190 /// ENSURE_OBJECT will throw an exception if the argument isn't an
191 /// object. It's a macro to match with the other ENSURE_ macros.
192 #ifdef PRETEND
193 #define ENSURE_OBJECT(vte) \
194 { \
195 if (!vte.is_object()) \
196 throw ASException(); \
197 } /* end of ENSURE_OBJECT */
198 #else
199 #define ENSURE_OBJECT(vte)
200 #endif
201
202 /// ENSURE_STRING makes sure that the given argument is a string,
203 /// calling the toString method if necessary -- it's a macro so that
204 /// the toString may be pushed if needed, and then whatever opcode
205 /// asked for this will be re-entered.
206 #define ENSURE_STRING(vte) \
207 { \
208 as_value *c = &vte; /* Don't call vte multiple times */ \
209 if (c->is_object()) \
210 { \
211 Property *d = c->to_object(*_global)->findProperty(NSV::PROP_TO_STRING, 0); \
212 if (d) \
213 { \
214 mStream->seekTo(opStart); \
215 pushGet(c->to_object(*_global), *c, d); \
216 break; \
217 } \
218 } \
219 } /* end of ENSURE_STRING */
220
221 /// ABSTRACT_COMPARE is the abstract comparison as described in the ECMA
222 /// standard. The 'truth_of_undefined' is used to specify which value
223 /// should be set for NaN values. It's a macro so that calls may be
224 /// pushed in the ENSURE_STRING and ENSURE_NUMBER macros.
225 #define ABSTRACT_COMPARE(store, rv1, rv2, truth_of_undefined) \
226 { \
227 as_value &a = rv1; /* Don't call rv1 multiple times */ \
228 as_value &b = rv2; /* Don't call rv2 multiple times */ \
229 if (a.ptype() == PTYPE_STRING && b.ptype() == PTYPE_STRING) \
230 { \
231 ENSURE_STRING(a); \
232 ENSURE_STRING(b); \
233 store = a.to_string() < b.to_string(); \
234 } \
235 else \
236 { \
237 ENSURE_NUMBER(a); \
238 ENSURE_NUMBER(b); \
239 double ad = a.to_number(); double bd = b.to_number(); \
240 if (isNaN(ad) || isNaN(bd)) \
241 store = truth_of_undefined; \
242 else if (isInf(ad) && ad > 0) \
243 store = false; \
244 else if (isInf(bd) && bd > 0) \
245 store = true; \
246 else if (isInf(bd) && bd < 0) \
247 store = false; \
248 else if (isInf(ad) && ad < 0) \
249 store = true; \
250 else \
251 store = ad < bd; \
252 } \
253 } /* end of ABSTRACT_COMPARE */
254
abstractEquality(const as_value & a,const as_value & b,bool strictness_on)255 inline bool abstractEquality(const as_value& a, const as_value& b,
256 bool strictness_on)
257 {
258 // TODO: this is a very quick hack to fix some tests without touching
259 // as_value. Tamarin has a detailed algorithm for working out equality,
260 // which can be implemented as a separate member function of as_value.
261 if (a.is_object() && !b.is_object()) {
262 return a.to_string() == b.to_string();
263 }
264 if ( strictness_on ) return a.strictly_equals(b);
265 else return a.equals(b);
266 }
267
268 /// NB: the stubbed but unimplemented as_value::conforms_to no longer exists,
269 /// but the code is left here for later reference.
270 #define ABSTRACT_TYPELATE(st, checkval, matchval) \
271 { \
272 bool *store = &st; \
273 /*as_value &a = checkval; Don't call checkval multiple times */ \
274 as_value &b = matchval; /* Don't call matchval multiple times */ \
275 *store = true; \
276 if (b.is_object()) \
277 { \
278 as_value v; \
279 b.to_object(*_global)->get_member(NSV::INTERNAL_TYPE, &v); \
280 if (true) /*(!a.conforms_to(mST.find(v.to_string()))) */ \
281 *store = false; \
282 } \
283 else if (b.is_string()) \
284 { \
285 if (true) /*(!a.conforms_to(mST.find(b.to_string()))) */ \
286 *store = false; \
287 } \
288 else \
289 *store = false; \
290 } /* end of ABSTRACT_TYPELATE */
291
292 #define JUMPIF(jtruth) \
293 { \
294 std::int32_t jumpOffset = mStream->read_S24(); \
295 if (jtruth) \
296 mStream->seekBy(jumpOffset); \
297 break; \
298 } /* end of JUMPIF */
299
300 namespace {
301
302 /// Switch the execution context to AVM2, and make sure it's
303 /// switched back again to what it was before even when there's an exception.
304 class AVM2Switcher
305 {
306 public:
AVM2Switcher(VM & vm)307 AVM2Switcher(VM& vm)
308 :
309 _vm(vm),
310 _ver(vm.getAVMVersion())
311 {
312 _vm.setAVMVersion(VM::AVM2);
313 }
314
~AVM2Switcher()315 ~AVM2Switcher()
316 {
317 _vm.setAVMVersion(_ver);
318 }
319
320 private:
321 VM& _vm;
322 VM::AVMVersion _ver;
323 };
324
325 }
326
Machine(VM & vm)327 Machine::Machine(VM& vm)
328 :
329 _stack(),
330 _registers(),
331 _scopeStack(),
332 mStream(0),
333 mST(vm.getStringTable()),
334 mDefaultXMLNamespace(0),
335 mCurrentScope(0),
336 mGlobalScope(0),
337 mDefaultThis(0),
338 mThis(0),
339 _global(0),
340 mGlobalReturn(),
341 mIgnoreReturn(),
342 mExitWithReturn(false),
343 mPoolObject(0),
344 mCurrentFunction(0),
345 _vm(vm)
346 {
347
348 }
349
350 void
init()351 Machine::init()
352 {
353
354 // TODO: The Global constructor needs the Machine and VM to be more or less
355 // fully constructed, so we might think how to do this better.
356 AVM2Global* g = new AVM2Global(*this, _vm);
357 _global = g;
358
359 AVM2Switcher switcher(_vm);
360 g->registerClasses();
361
362 }
363
364 Global_as*
global()365 Machine::global()
366 {
367 return _global;
368 }
369
370 void
push_scope_stack(as_value object)371 Machine::push_scope_stack(as_value object)
372 {
373 as_object* scopeObj = object.to_object(*_global);
374 assert(scopeObj);
375 log_abc("Pushing value %s onto scope stack.", object);
376 _scopeStack.push(scopeObj);
377 print_scope_stack();
378 }
379
380 void
execute()381 Machine::execute()
382 {
383
384 // This automatically switches back again when we leave this scope.
385 AVM2Switcher avm2(_vm);
386
387 assert(mStream);
388
389 for (;;) {
390 std::size_t opStart = mStream->tellg();
391
392 try {
393
394 SWF::abc_action_type opcode = static_cast<SWF::abc_action_type>(
395 mStream->read_as3op());
396
397 log_abc("** Executing opcode: %s (%d) **", opcode, (int)opcode);
398
399 switch (opcode)
400 {
401 default:
402 throw ASException();
403
404 /// This is not actually an opcode -- it occurs when the
405 /// stream is empty. We may need to return from a function,
406 /// or we may be done.
407 case SWF::ABC_ACTION_END:
408 return;
409
410 /// 0x01 ABC_ACTION_BKPT
411 /// Do: Enter the debugger if one has been invoked.
412 /// This is a no-op. Enable it if desired.
413 /// 0x02 ABC_ACTION_NOP
414 /// Do: Nothing.
415 /// 0xF3 ABC_ACTION_TIMESTAMP
416 case SWF::ABC_ACTION_NOP:
417 case SWF::ABC_ACTION_BKPT:
418 case SWF::ABC_ACTION_TIMESTAMP:
419 break;
420
421 /// 0x03 ABC_ACTION_THROW
422 /// Stack In:
423 /// obj -- an object
424 /// Stack Out:
425 /// .
426 /// Do: Throw obj as an exception
427 /// Equivalent: ACTIONTHROW
428 case SWF::ABC_ACTION_THROW:
429 {
430 throw ASException(_stack.pop());
431 break;
432 }
433
434 /// 0x04 ABC_ACTION_GETSUPER
435 /// Stream:
436 /// name_id -- V32 index to multiname 'name'
437 /// Stack In:
438 /// [ns [n]] -- Namespace stuff.
439 /// obj -- an object
440 /// Stack Out:
441 /// obj.super.name
442 /// May be the same as the value of obj.name (E.g. inherited
443 /// variables)
444 case SWF::ABC_ACTION_GETSUPER:
445 {
446 // Get the name.
447 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
448 // Finish it, if necessary.
449 _stack.drop(completeName(a));
450 // Get the target object.
451 ENSURE_OBJECT(_stack.top(0));
452
453 as_object *super = _stack.top(0).to_object(*_global)->
454 get_prototype();
455
456 // If we don't have a super, throw.
457 if (!super) throw ASReferenceError();
458 const ObjectURI uri(a.getGlobalName(),
459 a.getNamespace()->getURI());
460 Property *b = super->findProperty(uri);
461 // The object is on the top already.
462 pushGet(super, _stack.top(0), b);
463 break;
464 }
465
466 /// 0x05 ABC_ACTION_SETSUPER
467 /// Stream: UV32 index to multiname 'name'
468 /// Stack In:
469 /// val -- an object
470 /// [ns [n]] -- Namespace stuff.
471 /// obj -- an object
472 /// Stack Out:
473 /// .
474 /// Do: Set obj.super.name to val, if allowable.
475 case SWF::ABC_ACTION_SETSUPER:
476 {
477 // Get and finish the name.
478 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
479 as_value vobj = _stack.pop(); // The value
480
481 _stack.drop(completeName(a));
482
483 ENSURE_OBJECT(_stack.top(0));
484
485 // This is all wrong. It needs fixing once supers are
486 // correctly implemented.
487 as_object* obj = _stack.pop().to_object(*_global);
488 if (!obj) throw ASReferenceError();
489
490 as_object* super = obj->get_prototype();
491 if (!super) throw ASReferenceError();
492
493 const ObjectURI uri(a.getGlobalName(),
494 a.getNamespace()->getURI());
495 Property* b = super->findProperty(uri);
496
497 _stack.push(vobj);
498 pushSet(super, vobj, b);
499 break;
500 }
501
502 /// 0x06 ABC_ACTION_DXNS
503 /// Default XML Namespace
504 /// Stream: UV32 index to string pool 'nsname'
505 /// Do: Create a new public namespace with name nsname, and make
506 /// this the default XML namespace.
507 case SWF::ABC_ACTION_DXNS:
508 {
509 std::uint32_t soffset = mStream->read_V32();
510 const std::string& uri = pool_string(soffset, mPoolObject);
511
512 ClassHierarchy& ch = _global->classHierarchy();
513 mDefaultXMLNamespace = ch.anonNamespace(mST.find(uri));
514 break;
515 }
516
517 /// 0x07 ABC_ACTION_DXNSLATE
518 /// Stack In:
519 /// nsname -- a string object
520 /// Stack Out:
521 /// .
522 /// Do: Same as ABC_ACTION_DXNS, but the uri is in the stack,
523 /// not the stream.
524 case SWF::ABC_ACTION_DXNSLATE:
525 {
526 ENSURE_STRING(_stack.top(0));
527 const std::string& uri = _stack.top(0).to_string();
528
529 ClassHierarchy& ch = _global->classHierarchy();
530 mDefaultXMLNamespace = ch.anonNamespace(mST.find(uri));
531 _stack.drop(1);
532 break;
533 }
534
535 /// 0x08 ABC_ACTION_KILL
536 /// Stream: UV32 frame pointer offset 'offset'
537 /// Frame:
538 /// Kill at offset
539 /// Equivalent: ACTION_DELETE
540 case SWF::ABC_ACTION_KILL:
541 {
542 std::uint32_t regNum = mStream->read_V32();
543 setRegister(regNum, as_value());
544 break;
545 }
546
547 /// 0x09 ABC_ACTION_LABEL
548 /// Do: Unknown purpose, Tamarin does nothing.
549 case SWF::ABC_ACTION_LABEL:
550 {
551 break;
552 }
553
554 /// 0x0C ABC_ACTION_IFNLT
555 /// Stream: S24 jump offset 'jump'
556 /// Stack In:
557 /// b -- an object
558 /// a -- an object
559 /// Stack Out:
560 /// .
561 /// Do: If !(a < b) move by jump in stream, as ABC_ACTION_JUMP
562 /// does.
563 case SWF::ABC_ACTION_IFNLT:
564 {
565 bool truth;
566 ABSTRACT_COMPARE(truth, _stack.top(1), _stack.top(0),
567 false);
568 _stack.drop(2);
569 JUMPIF(!truth); // truth is: a < b
570 break;
571 }
572
573 /// 0x0D ABC_ACTION_IFNLE
574 /// Stream: S24 jump offset 'jump'
575 /// Stack In:
576 /// b -- an object
577 /// a -- an object
578 /// Stack Out:
579 /// .
580 /// Do: If !(a <= b) move by jump in stream, as ABC_ACTION_JUMP
581 /// does.
582 case SWF::ABC_ACTION_IFNLE:
583 {
584 bool truth;
585 ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1), true);
586 _stack.drop(2);
587 JUMPIF(truth); // truth is: b < a
588 break;
589 }
590
591 /// 0x0E ABC_ACTION_IFNGT
592 /// Stream: S24 jump offset 'jump'
593 /// Stack In:
594 /// b -- an object
595 /// a -- an object
596 /// Stack Out:
597 /// .
598 /// Do: If !(a > b) move by jump in stream, as ABC_ACTION_JUMP
599 /// does.
600 case SWF::ABC_ACTION_IFNGT:
601 {
602 bool truth;
603 ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1),
604 false);
605 _stack.drop(2);
606 JUMPIF(!truth); // truth is: b < a
607 break;
608 }
609
610 /// 0x0F ABC_ACTION_IFNGE
611 /// Stream: S24 jump offset 'jump'
612 /// Stack In:
613 /// a -- an object
614 /// b -- an object
615 /// Stack Out:
616 /// .
617 /// Do: If !(a >= b) move by jump in stream, as ABC_ACTION_JUMP
618 /// does.
619 case SWF::ABC_ACTION_IFNGE:
620 {
621 bool truth;
622 ABSTRACT_COMPARE(truth, _stack.top(1), _stack.top(0), true);
623 _stack.drop(2);
624 JUMPIF(truth); // truth is: a < b
625 break;
626 }
627
628 /// 0x10 ABC_ACTION_JUMP
629 /// Stream: S24 jump offset 'jump'
630 /// Do: If jump is negative, check for interrupts. Move by
631 /// jump in stream.
632 /// Equivalent: ACTION_BRANCHALWAYS
633 case SWF::ABC_ACTION_JUMP:
634 {
635 const std::int32_t bytes = mStream->read_S24();
636 log_abc("ABC_ACTION_JUMP: Jumping %d bytes.",bytes);
637 mStream->seekBy(bytes);
638 break;
639 }
640
641 /// 0x11 ABC_ACTION_IFTRUE
642 /// Stream: S24 jump offset 'jump'
643 /// Stack In:
644 /// a -- an object
645 /// Stack Out:
646 /// .
647 /// Do: If a is 'true', move by jump in stream, as
648 /// ABC_ACTION_JUMP does.
649 /// Equivalent: ACTION_BRANCHIFTRUE
650 case SWF::ABC_ACTION_IFTRUE:
651 {
652 const std::int32_t bytes = mStream->read_S24();
653 if (pop_stack().to_bool()) {
654 log_abc("ABC_ACTION_IFTRUE: Jumping %d bytes.",bytes);
655 mStream->seekBy(bytes);
656 }
657 else {
658 log_abc("ABC_ACTION_IFTRUE: Would have jumped %d "
659 "bytes.", bytes);
660 }
661 break;
662 }
663
664 /// 0x12 ABC_ACTION_IFFALSE
665 /// Stream: S24 jump offset 'jump'
666 /// Stack In:
667 /// a -- an object
668 /// Stack Out:
669 /// .
670 /// Do: If a is 'false', move by jump in stream, as
671 /// ABC_ACTION_JUMP does.
672 case SWF::ABC_ACTION_IFFALSE:
673 {
674 const std::int32_t bytes = mStream->read_S24();
675 const bool truth = pop_stack().to_bool();
676 if (!truth) {
677 log_abc("ABC_ACTION_IFFALSE: Jumping %d bytes.", bytes);
678 mStream->seekBy(bytes);
679 }
680 break;
681 }
682
683 /// 0x13 ABC_ACTION_IFEQ
684 /// Stream: S24 jump offset 'jump'
685 /// Stack In:
686 /// b -- an object
687 /// a -- an object
688 /// Stack Out:
689 /// .
690 /// Do: If a == b (weakly), move by jump in stream, as
691 /// ABC_ACTION_JUMP does.
692 case SWF::ABC_ACTION_IFEQ:
693 {
694 const std::int32_t bytes = mStream->read_S24();
695 const as_value b = pop_stack();
696 const as_value a = pop_stack();
697 if (a.equals(b)) {
698 log_abc("Jumping %d bytes.", bytes);
699 mStream->seekBy(bytes);
700 }
701 else{
702 log_abc("Would have jumped %d bytes", bytes);
703 }
704 break;
705 }
706
707 /// 0x14 ABC_ACTION_IFNE
708 /// Stream: S24 jump offset 'jump'
709 /// Stack In:
710 /// b -- an object
711 /// a -- an object
712 /// Stack Out:
713 /// .
714 /// Do: If a != b (weakly), move by jump in stream, as
715 /// ABC_ACTION_JUMP does.
716 case SWF::ABC_ACTION_IFNE:
717 {
718 as_value a = pop_stack();
719 as_value b = pop_stack();
720 const std::int32_t bytes = mStream->read_S24();
721 if (!a.equals(b)) {
722 log_abc("Jumping... %d bytes.", bytes);
723 mStream->seekBy(bytes);
724 }
725 else {
726 log_abc("Would have jumped %d bytes", bytes);
727 }
728 break;
729 }
730
731 /// 0x15 ABC_ACTION_IFLT
732 /// Stream: S24 jump offset 'jump'
733 /// Stack In:
734 /// b -- an object
735 /// a -- an object
736 /// Stack Out:
737 /// .
738 /// Do: If a < b move by jump in stream, as ABC_ACTION_JUMP
739 /// does.
740 case SWF::ABC_ACTION_IFLT:
741 {
742 as_value b = pop_stack();
743 as_value a = pop_stack();
744 const std::int32_t bytes = mStream->read_S24();
745 const bool jump = newLessThan(a, b, _vm).to_bool();
746 if (jump) {
747 log_abc("Jumping... %d bytes.", bytes);
748 mStream->seekBy(bytes);
749 }
750 else {
751 log_abc("Would have jumped %d bytes", bytes);
752 }
753 break;
754 }
755
756 /// 0x16 ABC_ACTION_IFLE
757 /// Stream: S24 jump offset 'jump'
758 /// Stack In:
759 /// b -- an object
760 /// a -- an object
761 /// Stack Out:
762 /// .
763 /// Do: If a <= b move by jump in stream, as ABC_ACTION_JUMP
764 /// does.
765 case SWF::ABC_ACTION_IFLE:
766 {
767 bool truth;
768 ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1), true);
769 _stack.drop(2);
770 JUMPIF(!truth); // truth is: b < a
771 break;
772 }
773
774 /// 0x17 ABC_ACTION_IFGT
775 /// Stream: S24 jump offset 'jump'
776 /// Stack In:
777 /// b -- an object
778 /// a -- an object
779 /// Stack Out:
780 /// .
781 /// Do: If a > b move by jump in stream, as ABC_ACTION_JUMP does.
782 case SWF::ABC_ACTION_IFGT:
783 {
784 std::int32_t bytes = mStream->read_S24();
785 bool truth;
786 // If b < a, then a > b, with undefined as false
787 ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1), false);
788 _stack.drop(2);
789 if (truth) {
790 log_abc("Jumping %d bytes.",bytes);
791 mStream->seekBy(bytes);
792 }
793 else{
794 log_abc("Would have jumped %d bytes.",bytes);
795 }
796 break;
797 }
798
799 /// 0x18 ABC_ACTION_IFGE
800 /// Stream: S24 jump offset 'jump'
801 /// Stack In:
802 /// b -- an object
803 /// a -- an object
804 /// Stack Out:
805 /// .
806 /// Do: If a >= b move by jump in stream, as ABC_ACTION_JUMP
807 /// does.
808 case SWF::ABC_ACTION_IFGE:
809 {
810 bool truth;
811 ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1), true);
812 _stack.drop(2);
813 JUMPIF(!truth); // truth is: a < b
814 break;
815 }
816
817 /// 0x19 ABC_ACTION_IFSTRICTEQ
818 /// Stream: S24 jump offset 'jump'
819 /// Stack In:
820 /// b -- an object
821 /// a -- an object
822 /// Stack Out:
823 /// .
824 /// Do: If a == b (strictly), move by jump in stream, as
825 /// ABC_ACTION_JUMP
826 case SWF::ABC_ACTION_IFSTRICTEQ:
827 {
828 bool truth = abstractEquality(_stack.top(1), _stack.top(0),
829 true);
830 _stack.drop(2);
831 JUMPIF(truth);
832 break;
833 }
834
835 /// 0x1A ABC_ACTION_IFSTRICTNE
836 /// Stream: S24 jump offset 'jump'
837 /// Stack In:
838 /// b -- an object
839 /// a -- an object
840 /// Stack Out:
841 /// .
842 /// Do: If a != b (strongly), move by jump in stream, as
843 /// ABC_ACTION_JUMP
844 case SWF::ABC_ACTION_IFSTRICTNE:
845 {
846 const bool truth = abstractEquality(_stack.top(1),
847 _stack.top(0), true);
848 _stack.drop(2);
849 JUMPIF(!truth);
850 break;
851 }
852
853 /// 0x18 ABC_ACTION_LOOKUPSWITCH
854 /// Stream: 3 bytes | V32 count as 'case_count - 1' |
855 /// case_count of S24 as 'cases'
856 /// Stack In:
857 /// index -- an integer object
858 /// Stack Out:
859 /// .
860 /// Do: If index >= case_count, reset stream to position on
861 /// op entry. Otherwise, move by cases[index] - 1 from stream
862 /// position on op entry.
863 case SWF::ABC_ACTION_LOOKUPSWITCH:
864 {
865 std::size_t npos = mStream->tellg();
866 if (!_stack.top(0).is_number()) throw ASException();
867
868 std::uint32_t index =
869 toNumber(_stack.top(0), getVM(fn));
870 _stack.drop(1);
871
872 mStream->seekBy(3); // Skip the intial offset.
873 std::uint32_t cases = mStream->read_V32();
874 // Read from our original position and use it to skip
875 // if the case is out of range.
876 if (index > cases) {
877 mStream->seekTo(npos);
878 mStream->seekTo(npos + mStream->read_S24());
879 }
880 else {
881 mStream->seekTo(npos + 3 * (index + 1));
882 std::uint32_t newpos = mStream->read_S24();
883 mStream->seekTo(npos - 1 + newpos);
884 }
885 break;
886 }
887
888 /// 0x30 ABC_ACTION_PUSHSCOPE
889 case SWF::ABC_ACTION_PUSHSCOPE:
890 {
891 as_value scope_value = pop_stack();
892 if (!scope_value.to_object(*_global)) {
893 // Should throw an exception.
894 IF_VERBOSE_ASCODING_ERRORS(
895 log_aserror(_("Can't push a null value onto the "
896 "scope stack (%s)."), scope_value);
897 );
898 break;
899 }
900 push_scope_stack(scope_value);
901 break;
902 }
903
904 /// 0x1C ABC_ACTION_PUSHWITH
905 /// Stack In:
906 /// scope -- a scope
907 /// Stack Out:
908 /// .
909 /// Do: Enter scope with previous scope as its base.
910 /// If 0x1C, start a new base if the previous one was global.
911 case SWF::ABC_ACTION_PUSHWITH:
912 {
913 log_unimpl("ABC_ACTION_PUSHWITH");
914 // A scope object is just a regular object.
915 // ENSURE_OBJECT(_stack.top(0));
916 // as_object *a = _stack.top(0).to_object(*_global);
917 //
918 // if (!_scopeStack.empty())
919 // a->set_prototype(_scopeStack.top(0).mScope);
920 // else
921 // a->set_prototype(NULL);
922 //
923 // if (opcode == SWF::ABC_ACTION_PUSHWITH &&
924 // _scopeStack.totalSize() == _scopeStack.size())
925 // {
926 // _scopeStack.push(Scope(0, a));
927 // }
928 // else
929 // {
930 // _scopeStack.push(Scope(_scopeStack.size(), a));
931 // }
932 // mCurrentScope = a;
933 // _stack.drop(1);
934 break;
935 }
936
937 /// 0x1D ABC_ACTION_POPSCOPE
938 /// Do: exit current scope. Clear the base if the depth is now
939 /// shallower than the base's depth.
940 case SWF::ABC_ACTION_POPSCOPE:
941 {
942 pop_scope_stack();
943 break;
944 }
945
946 /// 0x1E ABC_ACTION_NEXTNAME
947 /// Stack In:
948 /// index -- an integer object
949 /// obj -- an object
950 /// Stack Out:
951 /// name -- the key name of the property at index in obj
952 case SWF::ABC_ACTION_NEXTNAME:
953 {
954 ENSURE_NUMBER(_stack.top(0));
955 ENSURE_OBJECT(_stack.top(1));
956 as_object *obj = _stack.top(1).to_object(*_global);
957 const std::uint32_t index =
958 toNumber(_stack.top(0), getVM(fn));
959
960 if (!obj) {
961 // TODO: check what to do here.
962 log_debug("ABC_ACTION_NEXTNAME: expecting object on "
963 "stack, got %s", _stack.top(1));
964 _stack.drop(2);
965 break;
966 }
967
968 _stack.drop(1);
969 const Property *b = obj->getByIndex(index);
970 if (b) _stack.top(0) = mST.value(getName(b->uri()));
971 else _stack.top(0) = "";
972 break;
973 }
974
975 /// 0x1F ABC_ACTION_HASNEXT
976 /// Stack In:
977 /// index -- an integer object
978 /// obj -- an object
979 /// Stack Out:
980 /// next_index -- next index after index in obj, or 0 if none.
981 /// Do: If the index is 0, return the first logical property.
982 /// We'll do this by name, since the name id can be used for
983 /// this directly.
984 case SWF::ABC_ACTION_HASNEXT:
985 {
986 ENSURE_NUMBER(_stack.top(0));
987 ENSURE_OBJECT(_stack.top(1));
988 as_object *obj = _stack.top(1).to_object(*_global);
989 std::uint32_t index =
990 toNumber(_stack.top(0), getVM(fn));
991 _stack.drop(1);
992 assert(obj);
993 _stack.top(0) = obj->nextIndex(index);
994 break;
995 }
996
997 /// 0x20 ABC_ACTION_PUSHNULL
998 /// Stack Out:
999 /// n -- a Null object.
1000 case SWF::ABC_ACTION_PUSHNULL:
1001 {
1002 as_value value = as_value();
1003 value.set_null();
1004 push_stack(value);
1005 break;
1006 }
1007
1008 /// 0x21 ABC_ACTION_PUSHUNDEFINED
1009 /// Stack Out:
1010 /// n -- an Undefined object.
1011 case SWF::ABC_ACTION_PUSHUNDEFINED:
1012 {
1013 _stack.grow(1);
1014 _stack.top(0).set_undefined();
1015 break;
1016 }
1017
1018 /// 0x23 ABC_ACTION_NEXTVALUE
1019 /// Stack In:
1020 /// index -- an integer object
1021 /// obj -- an object (namespaces okay)
1022 /// Stack Out:
1023 /// value -- the value of the key value pair in obj at index.
1024 case SWF::ABC_ACTION_NEXTVALUE:
1025 {
1026 ENSURE_NUMBER(_stack.top(0));
1027 ENSURE_OBJECT(_stack.top(1));
1028 as_object *obj = _stack.top(1).to_object(*_global);
1029 const std::uint32_t index =
1030 toNumber(_stack.top(0), getVM(fn));
1031 const Property *b = obj->getByIndex(index);
1032 _stack.drop(1);
1033 if (!b) _stack.top(0).set_undefined();
1034 else {
1035 _stack.drop(1);
1036 pushGet(obj, _stack.top(0), const_cast<Property*>(b));
1037 }
1038 break;
1039 }
1040
1041 /// 0x24 ABC_ACTION_PUSHBYTE
1042 /// Stream: S8 as 'byte'
1043 /// Stack Out:
1044 /// byte -- as a raw byte
1045 case SWF::ABC_ACTION_PUSHBYTE:
1046 {
1047 const std::int8_t b = mStream->read_s8();
1048 push_stack(b);
1049 break;
1050 }
1051
1052 /// 0x25 ABC_ACTION_PUSHSHORT
1053 /// Stream: V32 as 'value'
1054 /// Stack Out:
1055 /// value -- as a raw integer
1056 case SWF::ABC_ACTION_PUSHSHORT:
1057 {
1058 const std::int16_t s =
1059 static_cast<std::int16_t>(mStream->read_V32());
1060 push_stack(s);
1061 break;
1062 }
1063
1064 /// 0x26 ABC_ACTION_PUSHTRUE
1065 /// Stack Out:
1066 /// true -- the True object
1067 case SWF::ABC_ACTION_PUSHTRUE:
1068 _stack.grow(1);
1069 _stack.top(0).set_bool(true);
1070 break;
1071
1072 /// 0x27 ABC_ACTION_PUSHFALSE
1073 /// Stack Out:
1074 /// false -- the False object
1075 case SWF::ABC_ACTION_PUSHFALSE:
1076 push_stack(false);
1077 break;
1078
1079 /// 0x28 ABC_ACTION_PUSHNAN
1080 /// Stack Out:
1081 /// NaN -- the NaN object
1082 case SWF::ABC_ACTION_PUSHNAN:
1083 _stack.grow(1);
1084 setNaN(_stack.top(0));
1085 break;
1086
1087 /// 0x29 ABC_ACTION_POP
1088 /// Stack In:
1089 /// a -- anything
1090 /// Stack Out:
1091 /// .
1092 case SWF::ABC_ACTION_POP:
1093 pop_stack();
1094 break;
1095
1096 /// 0x2A ABC_ACTION_DUP
1097 /// Stack In:
1098 /// a -- anything
1099 /// Stack Out:
1100 /// a
1101 /// a
1102 case SWF::ABC_ACTION_DUP:
1103 _stack.grow(1);
1104 _stack.top(0) = _stack.top(1);
1105 break;
1106
1107 /// 0x2B ABC_ACTION_SWAP
1108 /// Stack In:
1109 /// a -- anything
1110 /// b -- anything
1111 /// Stack Out:
1112 /// b
1113 /// a
1114 case SWF::ABC_ACTION_SWAP:
1115 {
1116 as_value inter = _stack.top(0);
1117 _stack.top(0) = _stack.top(1);
1118 _stack.top(1) = inter;
1119 break;
1120 }
1121
1122 /// 0x2C ABC_ACTION_PUSHSTRING
1123 /// Stream: V32 string pool index 'index'
1124 /// Stack Out:
1125 /// value -- String object from string_pool[index]
1126 case SWF::ABC_ACTION_PUSHSTRING:
1127 push_stack(pool_string(mStream->read_V32(), mPoolObject));
1128 break;
1129
1130 /// 0x2D ABC_ACTION_PUSHINT
1131 /// Stream: V32 int pool index 'index'
1132 /// Stack Out:
1133 /// value -- Integer object from integer_pool[index]
1134 case SWF::ABC_ACTION_PUSHINT:
1135 push_stack(pool_int(mStream->read_V32(), mPoolObject));
1136 break;
1137
1138 /// 0x2E ABC_ACTION_PUSHUINT
1139 /// Stream: V32 uint pool index 'index'
1140 /// Stack Out:
1141 /// value -- Unsigned Integer object from unsigned_integer_pool[index]
1142 case SWF::ABC_ACTION_PUSHUINT:
1143 {
1144 _stack.grow(1);
1145 _stack.top(0) = pool_uint(mStream->read_V32(), mPoolObject);
1146 break;
1147 }
1148
1149 /// 0x2F ABC_ACTION_PUSHDOUBLE
1150 /// Stream: V32 double pool index 'index'
1151 /// Stack Out:
1152 /// value -- Double object from double_pool[index]
1153 case SWF::ABC_ACTION_PUSHDOUBLE:
1154 {
1155 push_stack(as_value(pool_double(mStream->read_V32(), mPoolObject)));
1156 break;
1157 }
1158
1159 /// 0x31 ABC_ACTION_PUSHNAMESPACE
1160 /// Stream: V32 namespace pool index 'index'
1161 /// Stack Out:
1162 /// ns -- Namespace object from namespace_pool[index]
1163 case SWF::ABC_ACTION_PUSHNAMESPACE:
1164 {
1165 #if 0
1166 Namespace *ns = pool_namespace(mStream->read_V32(),
1167 mPoolObject);
1168 _stack.grow(1);
1169
1170 // Here we should probably construct a Namespace object,
1171 // but there is no need to do it using as_value's
1172 // constructor.
1173
1174 _stack.top(0) = *ns;
1175 break;
1176 #endif
1177 }
1178
1179 /// 0x32 ABC_ACTION_HASNEXT2
1180 /// Stream: V32 frame location 'objloc' | V32 frame location
1181 /// 'indexloc'
1182 /// Stack Out:
1183 /// truth -- True if frame[objloc] has key/val pair after
1184 /// frame[indexloc], following delegates (__proto__) objects
1185 /// if needed. False, otherwise.
1186 /// Frame:
1187 /// Change at objloc to object which possessed next value.
1188 /// Change at indexloc to index (as object) of the next value.
1189 /// N.B.: A value of '0' for indexloc initializes to the
1190 /// first logical property.
1191 case SWF::ABC_ACTION_HASNEXT2:
1192 {
1193 const std::int32_t oindex = mStream->read_V32();
1194 const std::int32_t iindex = mStream->read_V32();
1195
1196 const as_value& objv = getRegister(oindex);
1197 const as_value& indexv = getRegister(iindex);
1198
1199 log_abc("HASNEXT2: Object is %s, index is %d",
1200 objv, indexv);
1201
1202 as_object *obj = objv.to_object(*_global);
1203 if (!obj) {
1204 // TODO: Check what to do here.
1205 log_error("ABC_ACTION_HASNEXT2: expecting object in "
1206 "register %d, got %s", oindex, objv);
1207 // Stack is unchanged, so just break? Or push false?
1208 // Or throw?
1209 break;
1210 }
1211
1212 std::uint32_t index = toInt(indexv);
1213
1214 as_object *owner = 0;
1215 int next = obj->nextIndex(index, &owner);
1216 log_abc("HASNEXT2: Next index is %d", next);
1217
1218 if (next) {
1219 push_stack(true);
1220 if (owner) setRegister(oindex, owner);
1221 else {
1222 as_value null;
1223 null.set_null();
1224 setRegister(oindex, null);
1225 }
1226 setRegister(iindex, next);
1227 }
1228 else {
1229 push_stack(false);
1230 as_value null;
1231 null.set_null();
1232 setRegister(oindex, null);
1233 setRegister(iindex, 0.0);
1234 }
1235 break;
1236 }
1237
1238 /// 0x40 ABC_ACTION_NEWFUNCTION
1239 /// Stream: V32 'index'
1240 /// Stack Out:
1241 /// func -- the function object
1242 /// Do: Information about function is in the pool at index. Construct
1243 /// the function from this information and bind the current scope.
1244 case SWF::ABC_ACTION_NEWFUNCTION:
1245 {
1246 std::int32_t method_index = mStream->read_V32();
1247 log_abc("Creating new abc_function: method index=%u",method_index);
1248 Method *m = pool_method(method_index, mPoolObject);
1249 abc_function* new_function = m->getPrototype();
1250
1251 push_stack(as_value(new_function));
1252 break;
1253 }
1254
1255 /// 0x41 ABC_ACTION_CALL
1256 /// Stream: V32 'arg_count'
1257 /// Stack In:
1258 /// argN ... arg1 -- the arg_count arguments to pass
1259 /// obj -- the object to which the function belongs
1260 /// func -- the function to be called
1261 /// Stack Out:
1262 /// value -- the value returned by obj->func(arg1, ...., argN)
1263 case SWF::ABC_ACTION_CALL:
1264 {
1265 std::uint32_t argc = mStream->read_V32();
1266 ENSURE_OBJECT(_stack.top(argc + 1)); // The func
1267 ENSURE_OBJECT(_stack.top(argc)); // The 'this'
1268 as_function *f = _stack.top(argc + 1).to_function();
1269 as_object *obj = _stack.top(argc).to_object(*_global);
1270 // We start with argc + 2 values related to this call
1271 // on the stack. We want to end with 1 value. We pass
1272 // argc values (the parameters), so we need to drop
1273 // one more than we pass and store the return just
1274 // below that one. Thus:
1275 // return is _stack.top(argc + 1)
1276 // bottom of arguments is argc deep
1277 // drop 1 more value than is passed, on return
1278 pushCall(f, obj, _stack.top(argc + 1), argc, -1);
1279 break;
1280 }
1281
1282 /// 0x42 ABC_ACTION_CONSTRUCT
1283 /// Stream: V32 'arg_count'
1284 /// Stack In:
1285 /// argN ... arg1 -- the arg_count arguments to pass
1286 /// function -- constructor for the object to be constructed
1287 /// Stack Out:
1288 /// value -- obj after it has been constructed as
1289 /// obj(arg1, ..., argN)
1290 case SWF::ABC_ACTION_CONSTRUCT:
1291 {
1292 std::uint32_t argc = mStream->read_V32();
1293 as_function *f = _stack.top(argc).to_function();
1294 if (!f) {
1295 log_abc("CONSTRUCT: No function on stack!");
1296 break;
1297 }
1298 Property b(0, 0, f, NULL);
1299 pushCall(f, NULL, _stack.top(argc), argc, 0);
1300 break;
1301 }
1302
1303 /// 0x43 ABC_ACTION_CALLMETHOD
1304 /// Stream: V32 'method_id + 1' | V32 'arg_count'
1305 /// Stack In:
1306 /// argN ... arg1 -- the arg_count arguments to pass
1307 /// obj -- the object to be called
1308 /// Stack Out:
1309 /// value -- the value returned by obj::'method_id'(arg1,
1310 /// ..., argN)
1311 case SWF::ABC_ACTION_CALLMETHOD:
1312 {
1313 std::uint32_t dispatch_id = mStream->read_V32() - 1;
1314 std::uint32_t argc = mStream->read_V32();
1315 ENSURE_OBJECT(_stack.top(argc));
1316 as_object *obj = _stack.top(argc).to_object(*_global);
1317 const Property *f = obj->getByIndex(dispatch_id);
1318 as_function* func;
1319 #if 0
1320 if (f->isGetterSetter())
1321 {
1322 // Likely an error, but try to handle it.
1323 func = f->getGetter();
1324 }
1325 else
1326 #endif
1327 if (f->getValue(*obj).is_function())
1328 func = f->getValue(*obj).to_function();
1329 else
1330 {
1331 // Definitely an error, and not the kind we can handle.
1332 throw ASException();
1333 }
1334 pushCall(func, obj, _stack.top(argc), argc, 0);
1335 break;
1336 }
1337
1338 /// 0x44 ABC_ACTION_CALLSTATIC
1339 /// Stream: V32 'method_id' | V32 'arg_count'
1340 /// Stack In:
1341 /// argN ... arg1 -- the arg_count arguments to pass
1342 /// obj -- the object to act as a receiver for the static call
1343 /// Stack Out:
1344 /// value -- the value returned by obj->ABC::'method_id' (arg1,
1345 /// ..., argN)
1346 case SWF::ABC_ACTION_CALLSTATIC:
1347 {
1348 Method *m = pool_method(mStream->read_V32(), mPoolObject);
1349 std::uint32_t argc = mStream->read_V32();
1350 as_function *func = m->getPrototype();
1351 ENSURE_OBJECT(_stack.top(argc));
1352 as_object *obj = _stack.top(argc).to_object(*_global);
1353 pushCall(func, obj, _stack.top(argc), argc, 0);
1354 break;
1355 }
1356 /// 0x45 ABC_ACTION_CALLSUPER
1357 /// 0x4E ABC_ACTION_CALLSUPERVOID
1358 /// Stream: V32 'name_offset' | V32 'arg_count'
1359 /// Stack In:
1360 /// [ns [n]] -- Namespace stuff
1361 /// argN ... arg1 -- the arg_count arguments to pass
1362 /// obj -- the object whose super is to be called
1363 /// Stack Out:
1364 /// 0x45: value -- the value returned by obj::(resolve)'name_offset'::
1365 /// super(arg1, ..., argN)
1366 /// 0x4E: .
1367 case SWF::ABC_ACTION_CALLSUPER:
1368 case SWF::ABC_ACTION_CALLSUPERVOID:
1369 {
1370 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
1371 std::uint32_t argc = mStream->read_V32();
1372 int dropsize = completeName(a);
1373 ENSURE_OBJECT(_stack.top(argc + dropsize));
1374 _stack.drop(dropsize);
1375
1376 // get_super is wrong!
1377 as_object* super =
1378 _stack.top(argc).to_object(*_global)->get_super();
1379
1380 if (!super) throw ASReferenceError();
1381
1382 const ObjectURI uri(a.getGlobalName(),
1383 a.getNamespace()->getURI());
1384 Property* b = super->findProperty(uri);
1385
1386 if (!b) throw ASReferenceError();
1387
1388 as_function *f = // b->isGetterSetter() ? b->getGetter() :
1389 b->getValue(*super).to_function();
1390
1391 if (opcode == SWF::ABC_ACTION_CALLSUPER) {
1392 pushCall(f, super, _stack.top(argc), argc, 0);
1393 }
1394 else {
1395 // Void call
1396 pushCall(f, super, mIgnoreReturn, argc, -1); // drop obj too.
1397 }
1398 break;
1399 }
1400
1401 /// 0x46 ABC_ACTION_CALLPROPERTY
1402 /// 0x4C ABC_ACTION_CALLPROPLEX
1403 /// 0x4F ABC_ACTION_CALLPROPVOID
1404 /// Stream: V32 'name_offset' | V32 'arg_count'
1405 /// Stack In:
1406 /// argN ... arg1 -- the arg_count arguments to pass
1407 /// [ns [n]] -- Namespace stuff
1408 /// obj -- The object whose property is to be accessed.
1409 /// Stack Out:
1410 /// value -- the value from obj::(resolve)'name_offset'
1411 /// (arg1, ..., argN)
1412 /// (unless ABC_ACTION_CALL_PROPVOID, then: . )
1413 /// NB: Calls getter/setter if they exist.
1414 /// If the opcode is ABC_ACTION_CALLPROPLEX, obj is
1415 /// not altered by getter/setters
1416 case SWF::ABC_ACTION_CALLPROPERTY:
1417 case SWF::ABC_ACTION_CALLPROPLEX:
1418 case SWF::ABC_ACTION_CALLPROPVOID:
1419 {
1420 as_value result;
1421 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
1422 std::uint32_t argc = mStream->read_V32();
1423
1424 fn_call::Args args;
1425 get_args(argc, args);
1426
1427 if (a.isRuntime()) {
1428 _stack.drop(completeName(a));
1429 }
1430
1431 log_abc("CALL_PROP*: calling property %s of object %s",
1432 mST.value(a.getGlobalName()), _stack.top(0));
1433
1434 as_value object_val = pop_stack();
1435
1436 as_object *object = object_val.to_object(*_global);
1437 if (!object) {
1438 log_abc(_("CALLPROP: Can't call a method of a value "
1439 "that doesn't cast to an object (%s)."),
1440 object_val);
1441 }
1442 else {
1443
1444 as_value property =
1445 getMember(*object, a.getGlobalName());
1446
1447 if (!property.is_undefined() && !property.is_null()) {
1448 log_abc("Calling method %s on object %s",
1449 property, object_val);
1450 as_environment env = as_environment(_vm);
1451 result = invoke(property,env,object,args);
1452
1453 }
1454 else {
1455 log_abc(_("CALLPROP: Property '%s' of object '%s' "
1456 "is '%s', cannot call as method"),
1457 mPoolObject->stringPoolAt(a.getABCName()),
1458 object_val, property);
1459 }
1460
1461 }
1462
1463 if (opcode == SWF::ABC_ACTION_CALLPROPERTY) {
1464 push_stack(result);
1465 }
1466
1467 /* int shift = completeName(a, argc);
1468 ENSURE_OBJECT(_stack.top(shift + argc));
1469 as_object *obj = _stack.top(argc + shift).to_object(*_global);
1470 Property *b = obj->findProperty(a.getABCName(),
1471 a.getNamespace()->getURI());
1472 if (!b)
1473 throw ASReferenceError();
1474
1475 as_function *func;
1476 if (b->isGetterSetter())
1477 {
1478 if (lex_only)
1479 {
1480 _stack.top(argc + shift).set_undefined();
1481 _stack.drop(argc + shift);
1482 break;
1483 }
1484 #if 0
1485 else
1486 {
1487 //func = b->getGetter();
1488 log_error("Can't do ABC_ACTION_CALLPROPVOID or ABC_ACTION_CALLPROPERTY")
1489 break;
1490 }
1491 #endif
1492 }
1493 //else
1494 func = b->getValue(obj).to_function();
1495
1496 if (opcode == SWF::ABC_ACTION_CALLPROPVOID)
1497 pushCall(func, obj, mIgnoreReturn, argc, -shift - 1);
1498 else
1499 pushCall(func, obj, _stack.top(argc + shift), argc, -shift);*/
1500 break;
1501 }
1502
1503 /// 0x47 ABC_ACTION_RETURNVOID
1504 /// Do: Return an undefined object up the callstack.
1505 case SWF::ABC_ACTION_RETURNVOID:
1506 mStream->seekTo(0);
1507 if (!mStateStack.size()) return;
1508
1509 mGlobalReturn = as_value();
1510 restoreState();
1511
1512 if (mExitWithReturn) return;
1513 break;
1514
1515 /// 0x48 ABC_ACTION_RETURNVALUE
1516 /// Stack In:
1517 /// value -- value to be returned
1518 /// Stack Out:
1519 /// .
1520 /// Do: Return value up the callstack.
1521 case SWF::ABC_ACTION_RETURNVALUE:
1522 mStream->seekTo(0);
1523
1524 // Slot the return.
1525 mGlobalReturn = pop_stack();
1526 // And restore the previous state.
1527 restoreState();
1528 return;
1529
1530 /// 0x49 ABC_ACTION_CONSTRUCTSUPER
1531 /// Stream: V32 'arg_count'
1532 /// Stack In:
1533 /// argN ... arg1 -- the arg_count arguments
1534 /// obj -- the object whose super's constructor should be
1535 /// invoked
1536 /// Stack Out:
1537 /// .
1538 case SWF::ABC_ACTION_CONSTRUCTSUPER:
1539 {
1540 std::uint32_t argc = mStream->read_V32();
1541 fn_call::Args args;
1542 get_args(argc, args);
1543
1544 as_object* obj = _stack.top(argc).to_object(*_global);
1545
1546 // Is get_prototype what we want?
1547 as_object* super = obj ? obj->get_prototype() : 0;
1548 log_abc("CONSTRUCTSUPER: object %s, super %s, args %s",
1549 _stack.top(argc), super, argc);
1550
1551 if (!super) {
1552 log_error("ABC_ACTION_CONSTRUCTSUPER: No super found");
1553 throw ASException();
1554 }
1555
1556 as_value c = getMember(*super, NSV::PROP_CONSTRUCTOR);
1557 pushCall(c.to_function(), super, mIgnoreReturn,
1558 argc, -1);
1559 break;
1560
1561 }
1562
1563 /// 0x4A ABC_ACTION_CONSTRUCTPROP
1564 /// Stream: V32 'name_offset' | V32 'arg_count'
1565 /// Stack In:
1566 /// argN ... arg1 -- the arg_count arguments to pass
1567 /// [ns [n]] -- Namespace stuff
1568 /// obj -- the object whose property should be constructed
1569 /// Stack Out:
1570 /// value -- the newly constructed prop from obj::(resolve)
1571 /// 'name_offset'(arg1, ..., argN)
1572 case SWF::ABC_ACTION_CONSTRUCTPROP:
1573 {
1574 print_stack();
1575 as_environment env = as_environment(_vm);
1576 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
1577
1578 std::uint32_t argc = mStream->read_V32();
1579 fn_call::Args args;
1580 get_args(argc, args);
1581
1582 log_abc("CONSTRUCT_PROP: will try to construct property "
1583 "%s on object %s", mST.value(a.getGlobalName()),
1584 _stack.top(0));
1585
1586 as_object* object = pop_stack().to_object(*_global);
1587
1588 if (!object) {
1589 //TODO: Should this result in an exeception or an
1590 // actionscript error?
1591 log_abc("Can't construct property on a null object. "
1592 "Property not constructed.");
1593 push_stack(as_value());
1594 break;
1595 }
1596
1597 string_table::key ns = a.getNamespace() ?
1598 a.getNamespace()->getURI() : 0;
1599
1600 as_value c = getMember(*object,
1601 ObjectURI(a.getGlobalName(), ns));
1602
1603 // TODO: don't do this. Scriptes should not be functions;
1604 // we should always use the constructor member, most
1605 // likely.
1606 as_function* ctor = c.to_function();
1607
1608 if (ctor) {
1609 as_object* newobj = constructInstance(*ctor, env, args);
1610 push_stack(as_value(newobj));
1611 }
1612
1613 // TODO: This is more or less how everything should be done.
1614 else {
1615 log_abc("The property we found (%s) is not a "
1616 "constructor", c);
1617
1618 if (c.is_null() || c.is_undefined()) {
1619
1620 log_abc("Constructor is undefined, will not "
1621 "construct property.");
1622 push_stack(as_value());
1623 }
1624 else {
1625 as_value val = c.to_object(*_global)->getMember(
1626 NSV::PROP_CONSTRUCTOR);
1627
1628 invoke(val, env, c.to_object(*_global), args);
1629
1630 // Push the constructed property
1631 push_stack(c);
1632 }
1633 }
1634
1635 break;
1636 }
1637 /// 0x55 ABC_ACTION_NEWOBJECT
1638 /// Stream: V32 'arg_count'
1639 /// Stack In:
1640 /// prop_value_1 -- a value object
1641 /// prop_name_1 -- a string
1642 /// .
1643 /// . (arg_count value/name pairs in all)
1644 /// .
1645 /// prop_value_n -- a value object
1646 /// prop_name_n -- a string
1647 /// Stack Out:
1648 /// obj -- A new object which contains all of the given
1649 /// properties.
1650 /// NB: This builds an object from its properties, it's not
1651 /// a constructor.
1652 case SWF::ABC_ACTION_NEWOBJECT:
1653 {
1654 as_object *obj = _global->createObject();
1655 std::uint32_t argc = mStream->read_V32();
1656 int i = argc;
1657 while (i--)
1658 {
1659 as_value val = pop_stack();
1660 as_value name = pop_stack();
1661 obj->init_member(name.to_string(),val,0,0);
1662 }
1663 push_stack(as_value(obj));
1664 break;
1665 }
1666 /// 0x56 ABC_ACTION_NEWARRAY
1667 /// Stream: V32 'array_size'
1668 /// Stack In:
1669 /// value_n -- a value
1670 /// .
1671 /// . (array_size of these)
1672 /// .
1673 /// value_1 -- a value
1674 /// Stack Out:
1675 /// array -- an array { value_1, value_2, ..., value_n }
1676 case SWF::ABC_ACTION_NEWARRAY:
1677 {
1678 std::uint32_t asize = mStream->read_V32();
1679 std::uint32_t i = asize;
1680
1681 as_object* arr = _global->createArray();
1682 while (i--) {
1683 callMethod(arr, NSV::PROP_PUSH, pop_stack());
1684 }
1685 push_stack(as_value(arr));
1686 break;
1687 }
1688
1689 /// 0x57 ABC_ACTION_NEWACTIVATION
1690 /// Stack Out:
1691 /// vtable -- A new virtual table, which has the
1692 /// previous one as a parent.
1693 case SWF::ABC_ACTION_NEWACTIVATION:
1694 {
1695 // TODO: The function contains traits that need to be
1696 // included in the activation object.
1697 // For now we are using the function object as the
1698 // activation object. There is probably
1699 // a better way.
1700 //
1701 if (!mCurrentFunction->needsActivation()) {
1702 log_abc("NEWACTIVATION: called for a function without"
1703 "the NEED_ACTIVATION flag");
1704 }
1705 push_stack(as_value(mCurrentFunction));
1706 break;
1707 }
1708
1709 /// 0x58 ABC_ACTION_NEWCLASS
1710 /// Stream: V32 'class_id'
1711 /// Stack In:
1712 /// obj -- An object to be turned into a class. Its super
1713 /// is constructed.
1714 /// Stack Out:
1715 /// class -- The newly made class, made from obj and the
1716 /// information at cinits_pool[class_id]
1717 /// NB: This depends on scope and scope base (to determine
1718 /// lifetime(?))
1719 case SWF::ABC_ACTION_NEWCLASS:
1720 {
1721 std::uint32_t cid = mStream->read_V32();
1722 log_abc("Class index: %s", cid);
1723 Class* c = pool_script(cid, mPoolObject);
1724 log_abc("Creating new class id=%u name=%s", c->getName(),
1725 mST.value(c->getName()));
1726
1727 // This may be 0, and that's fine.
1728 as_object* base_class = pop_stack().to_object(*_global);
1729 as_object* new_class = c->getPrototype();
1730
1731 new_class->set_prototype(base_class);
1732
1733 //Create the class.
1734 Method* scmethod = c->getStaticConstructor();
1735 // What if there isn't one?
1736 assert(scmethod);
1737
1738 as_function* ctor = c->getConstructor()->getPrototype();
1739 new_class->init_member(NSV::PROP_CONSTRUCTOR, ctor, 0);
1740
1741 push_stack(new_class);
1742
1743 // Call the class's static constructor (which may be
1744 // undefined).
1745 as_environment env = as_environment(_vm);
1746
1747 /// This can be null.
1748 as_function* staticCtor = scmethod->getPrototype();
1749
1750 // We don't care about the return.
1751 fn_call::Args args;
1752 invoke(staticCtor, env, new_class, args);
1753
1754 log_abc("NEWCLASS(%1%) finished.",
1755 mST.value(c->getName()));
1756
1757 break;
1758 }
1759
1760 /// 0x59 ABC_ACTION_GETDESCENDANTS
1761 /// Stream: V32 'name_id'
1762 /// Stack In:
1763 /// value -- Whose descendants to get
1764 /// [ns [n]] -- Namespace stuff
1765 /// Stack Out:
1766 /// ?
1767 /// NB: This op seems to always throw a TypeError in Tamarin, though I
1768 /// assume that it ought to do something to yield a list of
1769 /// descendants of a class.
1770 case SWF::ABC_ACTION_GETDESCENDANTS:
1771 {
1772 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
1773 //as_value &v = _stack.top(0);
1774 ENSURE_OBJECT(v);
1775 _stack.drop(1);
1776 _stack.drop(completeName(a));
1777 // TODO: Decide or discover what to do with this.
1778 LOG_ONCE( log_unimpl("ABC_ACTION_GETDESCENDANTS") );
1779 break;
1780 }
1781 /// 0x5A ABC_ACTION_NEWCATCH
1782 /// Stream: V32 'catch_id'
1783 /// Stack Out:
1784 /// vtable -- vtable suitable to catch an exception of type in
1785 /// catch_id.
1786 /// NB: Need more information on how exceptions are set up.
1787 case SWF::ABC_ACTION_NEWCATCH:
1788 {
1789 // TODO: Decide if we need this. (Might be a no-op.)
1790 break;
1791 }
1792
1793 /// 0x5D ABC_ACTION_FINDPROPSTRICT
1794 /// 0x5E ABC_ACTION_FINDPROPERTY
1795 /// Stream: V32 'name_id'
1796 /// Stack In:
1797 /// [ns [n]] -- Namespace stuff
1798 /// Stack Out:
1799 /// owner -- object which owns property given by looking
1800 /// up the name_id.
1801 /// 0x5D is the undefined object if not found
1802 /// 0x5E throws a ReferenceError if not found
1803 case SWF::ABC_ACTION_FINDPROPSTRICT:
1804 case SWF::ABC_ACTION_FINDPROPERTY:
1805 {
1806 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
1807 if (a.isRuntime()) {
1808 _stack.drop(completeName(a));
1809 }
1810
1811 as_value ret = find_prop_strict(a);
1812
1813
1814 /* _stack.drop(completeName(a));
1815 as_object *owner;
1816 Property *b = mCurrentScope->findProperty(a.getABCName(),
1817 a.getNamespace()->getURI(), &owner);
1818 if (!b)
1819 {
1820 if (opcode == SWF::ABC_ACTION_FINDPROPSTRICT)
1821 throw ASReferenceError();
1822 else
1823 _stack.push(as_value());
1824 }
1825 else
1826 {
1827 _stack.push(owner);
1828 }*/
1829 break;
1830 }
1831 /// 0x5F ABC_ACTION_FINDDEF
1832 /// Stream: V32 'name_id' (no ns expansion)
1833 /// Stack Out:
1834 /// def -- The definition of the name at name_id.
1835 case SWF::ABC_ACTION_FINDDEF:
1836 {
1837 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
1838 // The name is expected to be complete.
1839 // TODO
1840 break;
1841 }
1842
1843 /// 0x60 ABC_ACTION_GETLEX
1844 /// Stream: V32 'name_id' (no ns expansion)
1845 /// Stack Out:
1846 /// property -- The result of 0x5D (ABC_ACTION_FINDPROPSTRICT)
1847 /// + 0x66 (ABC_ACTION_GETPROPERTY)
1848 case SWF::ABC_ACTION_GETLEX:
1849 {
1850 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
1851
1852 as_value val = find_prop_strict(a);
1853
1854 log_abc("GETLEX: found value %s", val);
1855 _stack.top(0) = val;
1856
1857 break;
1858 }
1859 /// ABC_ACTION_SETPROPERTY
1860 /// Stream: V32 'name_id'
1861 /// Stack In:
1862 /// value -- The value to be used
1863 /// [ns [n]] -- Namespace stuff
1864 /// OR
1865 /// [key] -- Key name for property. Will not have both
1866 /// Namespace and key.
1867 /// obj -- The object whose property is to be set
1868 /// Stack Out:
1869 /// .
1870 /// NB: If the name at name_id is completely qualified,
1871 /// neither a namespace nor a key is needed. If the
1872 /// name_id refers to a name with a runtime
1873 /// namespace, then this will be used. If neither of
1874 /// those is true and obj is a dictionary and key is
1875 /// a name, then the name_id is discarded and key/value
1876 /// is set in the dictionary obj instead.
1877 case SWF::ABC_ACTION_SETPROPERTY:
1878 {
1879 as_value value = pop_stack();
1880 string_table::key ns = 0;
1881 string_table::key name = 0;
1882
1883 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
1884 // TODO: If multiname is runtime we need to also pop
1885 // namespace and name values off the stack.
1886 if (a.flags() == MultiName::KIND_MultinameL) {
1887 log_abc("SETPROPERTY: name is a late runtime "
1888 "multiname");
1889 as_value nameValue = pop_stack();
1890 name = mST.find(nameValue.to_string());
1891 }
1892 else name = a.getGlobalName();
1893
1894 as_value val = pop_stack();
1895 as_object *object = val.to_object(*_global);
1896
1897 if (!object) {
1898 log_error("ABC_ACTION_SETPROPERTY: expecting object "
1899 "on stack, got %s!", val);
1900 break;
1901 }
1902
1903 object->set_member(ObjectURI(name, ns), value, false);
1904 break;
1905 }
1906
1907 /// 0x62 ABC_ACTION_GETLOCAL
1908 /// Stream: V32 'frame_index'
1909 /// Frame: value at frame_index is needed
1910 /// Stack Out:
1911 /// value
1912 case SWF::ABC_ACTION_GETLOCAL:
1913 {
1914 std::uint32_t index = mStream->read_V32();
1915 push_stack(getRegister(index));
1916 break;
1917 }
1918
1919 /// 0x63 ABC_ACTION_SETLOCAL
1920 /// Stream: V32 'frame_index'
1921 /// Frame: obj at frame_index is set to value
1922 /// Stack In:
1923 /// value
1924 /// Stack Out:
1925 /// .
1926 case SWF::ABC_ACTION_SETLOCAL:
1927 {
1928 std::uint32_t index = mStream->read_V32();
1929 log_abc("Register index: %u",index);
1930 setRegister(index, pop_stack());
1931 break;
1932 }
1933
1934 /// 0x64 ABC_ACTION_GETGLOBALSCOPE
1935 /// Stack Out:
1936 /// global -- The global scope object
1937 case SWF::ABC_ACTION_GETGLOBALSCOPE:
1938 {
1939 push_stack(_scopeStack.value(0));
1940 break;
1941 }
1942
1943 /// 0x65 ABC_ACTION_GETSCOPEOBJECT
1944 /// Stream: S8 'depth'
1945 /// Stack Out:
1946 /// scope -- The scope object at depth
1947 case SWF::ABC_ACTION_GETSCOPEOBJECT:
1948 {
1949 std::uint8_t depth = mStream->read_u8();
1950 push_stack(get_scope_stack(depth));
1951 print_scope_stack();
1952 break;
1953 }
1954
1955 /// 0x66 ABC_ACTION_GETPROPERTY
1956 /// Stream: V32 'name_id'
1957 /// Stack In:
1958 /// [ns [n]] -- Namespace stuff
1959 /// OR
1960 /// [key] -- Key name for property. Will not have both
1961 /// Namespace and key.
1962 /// obj -- The object whose property is to be retrieved
1963 /// Stack Out:
1964 /// prop -- The requested property.
1965 /// NB: See 0x61 (ABC_ACTION_SETPROPETY) for the decision of
1966 /// ns/key.
1967 case SWF::ABC_ACTION_GETPROPERTY:
1968 {
1969 string_table::key ns = 0;
1970 string_table::key name = 0;
1971 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
1972 // TODO: If multiname is runtime we need to also pop
1973 // namespace and name values of the stack.
1974 if (a.flags() == MultiName::KIND_MultinameL) {
1975 as_value nameValue = pop_stack();
1976 name = mST.find(nameValue.to_string());
1977 }
1978 else name = a.getGlobalName();
1979
1980 as_value object_val = pop_stack();
1981 as_object* object = object_val.to_object(*_global);
1982
1983 log_abc(_("GETPROPERTY: Looking for property "
1984 "%s of object %s"), mST.value(name), object_val);
1985
1986 if (!object) {
1987 log_abc(_("GETPROPERTY: expecting object on "
1988 "stack, got %s."), object_val);
1989 push_stack(as_value());
1990 break;
1991 }
1992
1993 as_value prop;
1994
1995 const ObjectURI uri(name, ns);
1996 const bool found = object->get_member(uri, &prop);
1997 if (!found) {
1998 log_abc("GETPROPERTY: property %s not found",
1999 mST.value(name));
2000 }
2001 else {
2002 log_abc("GETPROPERTY: property %s is %s",
2003 mST.value(name), prop);
2004 }
2005
2006 push_stack(prop);
2007 break;
2008 }
2009
2010 /// 0x68 ABC_ACTION_INITPROPERTY
2011 /// Stream V32 'name_id'
2012 /// Stack In:
2013 /// value -- The value to be put into the property.
2014 /// [ns [n]] -- Namespace stuff
2015 /// obj -- The object whose property is to be initialized
2016 /// Stack Out:
2017 /// .
2018 /// Do:
2019 /// Set obj::(resolve)'name_id' to value, set bindings
2020 /// from the context.
2021 case SWF::ABC_ACTION_INITPROPERTY:
2022 {
2023 std::uint32_t index = mStream->read_V32();
2024 MultiName a = pool_name(index, mPoolObject);
2025 as_value v = pop_stack();
2026 // TODO: If multiname is a runtime mutiname we need to also
2027 // pop name and namespace values.
2028 as_value object_val = pop_stack();
2029
2030 as_object* object = object_val.to_object(*_global);
2031 if (!object) {
2032 log_abc("INITPROPERTY: expecting object on stack, "
2033 "got %s", object_val);
2034 }
2035 else {
2036 object->set_member(a.getGlobalName(), v, false);
2037 }
2038 break;
2039 }
2040
2041 /// 0x6A ABC_ACTION_DELETEPROPERTY
2042 /// Stream: V32 'name_id'
2043 /// Stack In:
2044 /// [ns [n]] -- Namespace stuff
2045 /// obj -- The object whose property should be deleted.
2046 /// Stack Out:
2047 /// truth -- True if property was deleted or did not exist,
2048 /// else False.
2049 case SWF::ABC_ACTION_DELETEPROPERTY:
2050 {
2051 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
2052 _stack.drop(completeName(a));
2053 as_object* obj = _stack.top(0).to_object(*_global);
2054
2055 if (!obj) {
2056 // TODO: what here?
2057 log_abc("DELETEPROPERTY: expecting object on stack, "
2058 "got %s", _stack.top(0));
2059 break;
2060 }
2061
2062 // Look in the global namespace if there is none specified.
2063 Namespace* n = a.getNamespace();
2064 const string_table::key ns = n ? n->getURI() : 0;
2065 const string_table::key prop = a.getGlobalName();
2066
2067 const bool deleted = obj->delProperty(
2068 ObjectURI(prop, ns)).second;
2069 _stack.top(0) = deleted;
2070 break;
2071 }
2072
2073 /// 0x6C ABC_ACTION_GETSLOT
2074 /// Stream: V32 'slot_index + 1'
2075 /// Stack In:
2076 /// obj -- The object which owns the desired slot.
2077 /// Stack Out:
2078 /// slot -- obj.slots[slot_index]
2079 case SWF::ABC_ACTION_GETSLOT:
2080 {
2081 as_value val;
2082 std::uint32_t sindex = mStream->read_V32();
2083 as_object* object = pop_stack().to_object(*_global);
2084 if (!object) {
2085 log_abc("GETSLOT: Did not find expected object on "
2086 "stack");
2087 break;
2088 }
2089
2090 object->get_member_slot(sindex + 1, &val);
2091
2092 log_abc("object has value %s at real_slot=%u abc_slot=%u",
2093 val, sindex + 1, sindex);
2094 push_stack(val);
2095
2096 break;
2097 }
2098
2099 /// 0x6D ABC_ACTION_SETSLOT
2100 /// Stream: V32 'slot_index + 1'
2101 /// Stack In:
2102 /// value -- The value intended for the slot.
2103 /// obj -- The object whose slot should be set.
2104 /// Stack Out:
2105 /// .
2106 /// Do: obj.slots[slot_index] = value
2107 //
2108 /// Slot index must be greater than 0 and less than or
2109 /// equal to the number of slots (so one-based index?).
2110 case SWF::ABC_ACTION_SETSLOT:
2111 {
2112 std::uint32_t sindex = mStream->read_V32();
2113 as_value value = pop_stack();
2114 as_value object = pop_stack();
2115 log_abc("SETSLOT object: %s, value: %s, index: %s",
2116 object, value, sindex);
2117
2118 as_object* obj = object.to_object(*_global);
2119 if ( ! obj )
2120 {
2121 IF_VERBOSE_ASCODING_ERRORS(
2122 log_aserror(_("ABC_ACTION_SETSLOT: "
2123 "unexpected non-object stack value %s"), object);
2124 );
2125 break;
2126 }
2127
2128 // We use sindex + 1, because currently as_object sets
2129 // a property at a slot index 1 higher than the
2130 // index the AbcBlock thinks the property is at.
2131 if ( ! obj->set_member_slot(sindex+1, value) )
2132 {
2133 log_abc("Failed to set property at "
2134 "real_slot=%u abc_slot=%u", sindex+1, sindex);
2135 }
2136 else
2137 {
2138 log_abc("Set property at real_slot=%u abc_slot=%u",
2139 sindex+1, sindex);
2140 }
2141
2142 break;
2143 }
2144
2145 /// 0x6E ABC_ACTION_GETGLOBALSLOT
2146 /// Stream: V32 'slot_index + 1'
2147 /// Stack In:
2148 /// .
2149 /// Stack Out:
2150 /// slot -- globals.slots[slot_index]
2151 /// NB: Deprecated
2152 case SWF::ABC_ACTION_GETGLOBALSLOT:
2153 {
2154 std::uint32_t sindex = mStream->read_V32();
2155 if (!sindex)
2156 throw ASException();
2157 --sindex;
2158 _stack.grow(1);
2159 //TODO: _stack.top(0) = mGlobal.getSlot(sindex);
2160 break;
2161 }
2162
2163 /// 0x6F ABC_ACTION_SETGLOBALSLOT
2164 /// Stream: V32 'slot_index + 1'
2165 /// Stack In:
2166 /// value -- The value to be placed into the slot.
2167 /// Stack Out:
2168 /// .
2169 /// Do: globals[slot_index] = value
2170 /// NB: Deprecated
2171 case SWF::ABC_ACTION_SETGLOBALSLOT:
2172 {
2173 std::uint32_t sindex = mStream->read_V32();
2174 if (!sindex)
2175 throw ASException();
2176 --sindex;
2177 //TODO: mGlobal.setSlot(sindex, _stack.pop());
2178 break;
2179 }
2180
2181 /// 0x70 ABC_ACTION_CONVERT_S
2182 /// Stack In:
2183 /// value -- An object
2184 /// Stack Out:
2185 /// str_value -- value as a string
2186 case SWF::ABC_ACTION_CONVERT_S:
2187 _stack.top(0) = _stack.top(0).to_string();
2188 break;
2189
2190 /// 0x71 ABC_ACTION_ESC_XELEM
2191 /// Stack In:
2192 /// value -- An object to be escaped
2193 /// Stack Out:
2194 /// str_value -- value as a string, escaped suitably for
2195 /// an XML element.
2196 case SWF::ABC_ACTION_ESC_XELEM:
2197 log_unimpl("ABC_ACTION_ESC_XELEM");
2198 //TODO: set _stack.top(0) to an escaped string.
2199 break;
2200
2201 /// 0x72 ABC_ACTION_ESC_XATTR
2202 /// Stack In:
2203 /// value -- An object to be escaped
2204 /// Stack Out:
2205 /// str_value -- value as a string, escaped suitably for an
2206 /// XML attribute.
2207 case SWF::ABC_ACTION_ESC_XATTR:
2208 log_unimpl("ABC_ACTION_ESC_XATTR");
2209 //TODO: set _stack.top(0) to an escaped string.
2210 break;
2211
2212 /// 0x73 ABC_ACTION_CONVERT_I
2213 /// 0x83 ABC_ACTION_COERCE_I (deprecated)
2214 /// Stack In:
2215 /// value -- An object to be converted to Integer
2216 /// Stack Out:
2217 /// int_value -- value as an integer object
2218 case SWF::ABC_ACTION_CONVERT_I:
2219 case SWF::ABC_ACTION_COERCE_I:
2220 _stack.top(0) = toInt(_stack.top(0));
2221 break;
2222
2223 /// 0x74 ABC_ACTION_CONVERT_U
2224 /// 0x88 ABC_ACTION_COERCE_U (deprecated)
2225 /// Stack In:
2226 /// value -- An object to be converted to unsigned integer
2227 /// Stack Out:
2228 /// int_value -- value as an unsigned integer object
2229 case SWF::ABC_ACTION_CONVERT_U:
2230 case SWF::ABC_ACTION_COERCE_U:
2231 _stack.top(0) = static_cast<std::uint32_t>(
2232 toNumber(_stack.top(0), getVM(fn)));
2233 break;
2234
2235 /// 0x75 ABC_ACTION_CONVERT_D
2236 /// 0x84 ABC_ACTION_COERCE_D (deprecated)
2237 /// Stack In:
2238 /// value -- An object to be converted to a double
2239 /// Stack Out:
2240 /// double_value -- value as a double object
2241 case SWF::ABC_ACTION_CONVERT_D:
2242 case SWF::ABC_ACTION_COERCE_D:
2243 _stack.top(0) = toNumber(_stack.top(0), getVM(fn));
2244 break;
2245
2246 /// 0x76 ABC_ACTION_CONVERT_B
2247 /// 0x81 ABC_ACTION_COERCE_B (deprecated)
2248 /// Stack In:
2249 /// value -- An object to be converted to a boolean
2250 /// Stack Out:
2251 /// bool_value -- value as a boolean object
2252 case SWF::ABC_ACTION_CONVERT_B:
2253 case SWF::ABC_ACTION_COERCE_B:
2254 _stack.top(0) = _stack.top(0).to_bool();
2255 break;
2256
2257 /// 0x77 ABC_ACTION_CONVERT_O
2258 /// Stack In:
2259 /// obj -- An object
2260 /// Stack Out:
2261 /// obj -- An object
2262 /// Do: If obj is Undefined or Null, throw TypeError
2263 case SWF::ABC_ACTION_CONVERT_O:
2264 {
2265 _stack.top(0) = _stack.top(0).to_object(*_global);
2266 if (_stack.top(0).is_undefined() || _stack.top(0).is_null())
2267 throw ASTypeError();
2268 break;
2269 }
2270
2271 /// 0x78 ABC_ACTION_CHECKFILTER
2272 /// Stack In:
2273 /// obj -- An object
2274 /// Stack Out:
2275 /// obj -- An object
2276 /// Do: If obj is not XML based, throw TypeError
2277 case SWF::ABC_ACTION_CHECKFILTER:
2278 {
2279 if (!_stack.top(0).is_object()) {
2280 // TODO: check whether it is an XML object.
2281 throw ASTypeError();
2282 }
2283 break;
2284 }
2285
2286 /// 0x80 ABC_ACTION_COERCE
2287 /// Stream: V32 'name_index'
2288 /// Stack In:
2289 /// [ns [n]] -- Possibly name/namespace stuff
2290 /// obj -- An object to be converted
2291 /// Stack Out:
2292 /// coerced_obj -- The object as the desired (resolve)
2293 // 'name_index' type.
2294 case SWF::ABC_ACTION_COERCE:
2295 {
2296 // TODO: handle runtime names?
2297 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
2298
2299 as_value value = _stack.top(0);
2300 log_abc("COERCE: object for conversion is %s, "
2301 "desired type %s", value,
2302 mST.value(a.getGlobalName()));
2303
2304 // Examples of desired type: "Sprite" "Button",
2305 // "GlobalListener", "Object".
2306 // Tamarin seems to look up the traits of the
2307 // target type. If it's a builtin type (boolean, number,
2308 // string, in, uint, object, "any") it succeeds.
2309 // Otherwise check null or undefined and do something.
2310 // Otherwise look at the type traits of the original. If
2311 // these traits contain the expected interface, return the
2312 // original value. Otherwise throw error.
2313 break;
2314 }
2315 /// 0x82 ABC_ACTION_COERCE_A
2316 /// Stack In:
2317 /// obj -- An object to be converted
2318 /// Stack Out:
2319 /// obj
2320 /// Do: Nothing. (The 'a' is for atom, and it's unclear
2321 /// if anything is needed.)
2322 case SWF::ABC_ACTION_COERCE_A:
2323 {
2324 break;
2325 }
2326
2327 /// 0x85 ABC_ACTION_COERCE_S
2328 /// Stack In:
2329 /// obj -- An object to be converted
2330 /// Stack Out:
2331 /// str_obj -- obj as string. nullString object if obj is
2332 /// Null or Undefined
2333 case SWF::ABC_ACTION_COERCE_S:
2334 {
2335 if (_stack.top(0).is_undefined() ||
2336 _stack.top(0).is_null()) {
2337 _stack.top(0) = "";
2338 }
2339 else _stack.top(0) = _stack.top(0).to_string();
2340 break;
2341 }
2342
2343 /// 0x86 ABC_ACTION_ASTYPE
2344 /// Stream: V32 'name_index'
2345 /// Stack In:
2346 /// [ns [n]] -- Possible namespace stuff
2347 /// obj -- An object to be checked
2348 /// Stack Out:
2349 /// cobj -- obj if obj is of type (resolve)'name_index',
2350 /// otherwise Null
2351 case SWF::ABC_ACTION_ASTYPE:
2352 {
2353 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
2354 as_value value = pop_stack();
2355 //TODO: Make sure the value is of the correct type;
2356 push_stack(value);
2357 break;
2358 }
2359
2360 /// 0x87 ABC_ACTION_ASTYPELATE
2361 /// Stack In:
2362 /// valid -- The object whose type is to be matched
2363 /// obj -- An object to be checked
2364 /// Stack Out:
2365 /// cobj -- obj if type of obj conforms to valid, otherwise
2366 /// Null
2367 case SWF::ABC_ACTION_ASTYPELATE:
2368 {
2369 as_value type = pop_stack();
2370 as_value value = pop_stack();
2371 //TODO: If value is not the type defined by type, then push null.
2372 push_stack(value);
2373 break;
2374 }
2375 /// 0x89 ABC_ACTION_COERCE_O
2376 /// Stack In:
2377 /// obj -- An object
2378 /// Stack Out:
2379 /// cobj -- obj if obj is not Undefined, otherwise Null
2380 case SWF::ABC_ACTION_COERCE_O:
2381 {
2382 if (_stack.top(0).is_undefined())
2383 _stack.top(0) = _stack.top(0).to_object(*_global);
2384 else
2385 _stack.top(0).set_undefined();
2386 break;
2387 }
2388 /// 0x90 ABC_ACTION_NEGATE
2389 /// Stack In:
2390 /// obj -- An object
2391 /// Stack Out:
2392 /// negdouble -- -1.0 * (double) obj
2393 case SWF::ABC_ACTION_NEGATE:
2394 {
2395 _stack.top(0) = -toNumber(_stack.top(0), getVM(fn));
2396 break;
2397 }
2398 /// 0x91 ABC_ACTION_INCREMENT
2399 /// Stack In:
2400 /// num -- A number, integer or double
2401 /// Stack Out:
2402 /// num + 1
2403 case SWF::ABC_ACTION_INCREMENT:
2404 {
2405 as_value val = pop_stack();
2406 push_stack(as_value(val.to_number() + 1));
2407 break;
2408 }
2409
2410 /// 0x92 ABC_ACTION_INCLOCAL
2411 /// Stream: V32 'frame_addr'
2412 /// Frame: Load i from frame_addr and increment it.
2413 case SWF::ABC_ACTION_INCLOCAL:
2414 {
2415 std::uint32_t foff = mStream->read_V32();
2416 setRegister(foff, toNumber(getRegister(foff), getVM(fn)) + 1);
2417 break;
2418 }
2419
2420 /// 0x93 ABC_ACTION_DECREMENT
2421 /// Stack In:
2422 /// num -- A number, integer or double
2423 /// Stack Out:
2424 /// num - 1
2425 case SWF::ABC_ACTION_DECREMENT:
2426 {
2427 _stack.top(0) = toNumber(_stack.top(0), getVM(fn)) - 1;
2428 break;
2429 }
2430
2431 /// 0x94 ABC_ACTION_DECLOCAL
2432 /// Stream: V32 'frame_addr'
2433 /// Frame: Load i from frame_addr and decrement it.
2434 case SWF::ABC_ACTION_DECLOCAL:
2435 {
2436 const std::uint32_t foff = mStream->read_V32();
2437 setRegister(foff, toNumber(getRegister(foff), getVM(fn)) - 1);
2438 break;
2439 }
2440
2441 /// 0x95 ABC_ACTION_ABC_TYPEOF
2442 /// Stack In:
2443 /// obj -- An object
2444 /// Stack Out:
2445 /// type -- typeof(obj) as a string
2446 case SWF::ABC_ACTION_ABC_TYPEOF:
2447 _stack.top(0) = _stack.top(0).typeOf();
2448 break;
2449
2450 /// 0x96 ABC_ACTION_NOT
2451 /// Stack In:
2452 /// obj -- An object
2453 /// Stack Out:
2454 /// nobj -- A truth object with value !((Boolean) obj)
2455 case SWF::ABC_ACTION_NOT:
2456 _stack.top(0).set_bool(!_stack.top(0).to_bool());
2457 break;
2458
2459 /// 0x97 ABC_ACTION_BITNOT
2460 /// Stack In:
2461 /// obj -- An object
2462 /// Stack Out:
2463 /// nint -- ~((Int) obj)
2464 case SWF::ABC_ACTION_BITNOT:
2465 _stack.top(0) = ~toInt(_stack.top(0));
2466 break;
2467
2468 /// 0xA0 ABC_ACTION_ADD
2469 /// Stack In:
2470 /// a
2471 /// b
2472 /// Stack Out:
2473 /// a + b (double if numeric)
2474 case SWF::ABC_ACTION_ADD:
2475 newAdd(_stack.top(1), _stack.top(0), _vm);
2476 _stack.drop(1);
2477 break;
2478
2479 /// 0xA1 ABC_ACTION_SUBTRACT
2480 /// Stack In:
2481 /// b
2482 /// a
2483 /// Stack Out:
2484 /// a - b (double)
2485 case SWF::ABC_ACTION_SUBTRACT:
2486 subtract(_stack.top(1), _stack.top(0), _vm);
2487 _stack.drop(1);
2488 break;
2489
2490 /// 0xA2 ABC_ACTION_MULTIPLY
2491 /// Stack In:
2492 /// a
2493 /// b
2494 /// Stack Out:
2495 /// a * b (double)
2496 case SWF::ABC_ACTION_MULTIPLY:
2497 _stack.top(1) = toNumber(_stack.top(1), getVM(fn)) * toNumber(_stack.top(0), getVM(fn));
2498 _stack.drop(1);
2499 break;
2500
2501 /// 0xA3 ABC_ACTION_DIVIDE
2502 /// Stack In:
2503 /// b
2504 /// a
2505 /// Stack Out:
2506 /// a / b (double)
2507 case SWF::ABC_ACTION_DIVIDE:
2508 _stack.top(1) = toNumber(_stack.top(1), getVM(fn)) / toNumber(_stack.top(0), getVM(fn));
2509 _stack.drop(1);
2510 break;
2511
2512 /// 0xA4 ABC_ACTION_MODULO
2513 /// Stack In:
2514 /// b
2515 /// a
2516 /// Stack Out:
2517 /// a % b (not integer mod, but remainder)
2518 case SWF::ABC_ACTION_MODULO:
2519 {
2520 // TODO: test this properly and fix the UB (overflow).
2521 double result = toNumber(_stack.top(1), getVM(fn)) / toNumber(_stack.top(0), getVM(fn));
2522 int trunc_result = static_cast<int> (result);
2523 _stack.top(1) = toNumber(_stack.top(1), getVM(fn)) -
2524 (trunc_result * toNumber(_stack.top(0), getVM(fn)));
2525 _stack.drop(1);
2526 break;
2527 }
2528
2529 /// 0xA5 ABC_ACTION_LSHIFT
2530 /// Stack In:
2531 /// b
2532 /// a
2533 /// Stack Out:
2534 /// a << b
2535 case SWF::ABC_ACTION_LSHIFT:
2536 {
2537 _stack.top(1) = toInt(_stack.top(1)) << toInt(_stack.top(0));
2538 _stack.drop(1);
2539 break;
2540 }
2541
2542 /// 0xA6 ABC_ACTION_RSHIFT
2543 /// Stack In:
2544 /// a
2545 /// b
2546 /// Stack Out:
2547 /// a >> b
2548 case SWF::ABC_ACTION_RSHIFT:
2549 {
2550 _stack.top(1) = toInt(_stack.top(1)) >> toInt(_stack.top(0));
2551 _stack.drop(1);
2552 break;
2553 }
2554
2555 /// 0xA7 ABC_ACTION_URSHIFT
2556 /// Stack In:
2557 /// b
2558 /// a
2559 /// Stack Out:
2560 /// ((unsigned) a) >> b
2561 case SWF::ABC_ACTION_URSHIFT:
2562 {
2563 _stack.top(1) =
2564 static_cast<std::uint32_t>(toNumber(_stack.top(1), getVM(fn)))
2565 >> toInt(_stack.top(0));
2566 _stack.drop(1);
2567 break;
2568 }
2569
2570 /// 0xA8 ABC_ACTION_BITAND
2571 /// a
2572 /// b
2573 /// Stack Out:
2574 /// a & b
2575 case SWF::ABC_ACTION_BITAND:
2576 _stack.top(1) = toInt(_stack.top(1)) & toInt(_stack.top(0));
2577 _stack.drop(1);
2578 break;
2579
2580 /// 0xA9 ABC_ACTION_BITOR
2581 /// Stack In:
2582 /// b
2583 /// a
2584 /// Stack Out:
2585 /// a | b
2586 case SWF::ABC_ACTION_BITOR:
2587 _stack.top(1) = toInt(_stack.top(1)) | toInt(_stack.top(0));
2588 _stack.drop(1);
2589 break;
2590
2591 /// 0xAA ABC_ACTION_BITXOR
2592 /// Stack In:
2593 /// b
2594 /// a
2595 /// Stack Out:
2596 /// a ^ b
2597 case SWF::ABC_ACTION_BITXOR:
2598 {
2599 _stack.top(1) = toInt(_stack.top(1)) ^ toInt(_stack.top(0));
2600 _stack.drop(1);
2601 break;
2602 }
2603
2604 /// 0xAB ABC_ACTION_EQUALS
2605 /// Stack In:
2606 /// b
2607 /// a
2608 /// Stack Out:
2609 /// truth -- Truth of (a == b) (weakly)
2610 case SWF::ABC_ACTION_EQUALS:
2611 {
2612 bool truth = abstractEquality(_stack.top(1), _stack.top(0), false);
2613 _stack.drop(1);
2614 _stack.top(0).set_bool(truth);
2615 break;
2616 }
2617
2618 /// 0xAC ABC_ACTION_STRICTEQUALS
2619 /// Stack In:
2620 /// b
2621 /// a
2622 /// Stack Out:
2623 /// truth -- Truth of (a == b) (strongly, as in
2624 /// 0x19 (ABC_ACTION_IFSTRICTEQ))
2625 case SWF::ABC_ACTION_STRICTEQUALS:
2626 {
2627 bool truth = abstractEquality(_stack.top(1), _stack.top(0), true);
2628 _stack.drop(1);
2629 _stack.top(0).set_bool(truth);
2630 break;
2631 }
2632
2633 /// 0xAD ABC_ACTION_LESSTHAN
2634 /// Stack In:
2635 /// b
2636 /// a
2637 /// Stack Out:
2638 /// truth -- Truth of (a < b)
2639 case SWF::ABC_ACTION_LESSTHAN:
2640 {
2641 bool truth;
2642 ABSTRACT_COMPARE(truth, _stack.top(1), _stack.top(0), false);
2643 _stack.drop(1);
2644 _stack.top(0).set_bool(truth); // truth is a < b
2645 break;
2646 }
2647
2648 /// 0xAE ABC_ACTION_LESSEQUALS
2649 /// Stack In:
2650 /// b
2651 /// a
2652 /// Stack Out:
2653 /// truth -- Truth of (a <= b)
2654 case SWF::ABC_ACTION_LESSEQUALS:
2655 {
2656 bool truth;
2657 ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1), true);
2658 _stack.drop(1);
2659 _stack.top(0).set_bool(!truth); // truth is b < a
2660 break;
2661 }
2662
2663 /// 0xAF ABC_ACTION_GREATERTHAN
2664 /// Stack In:
2665 /// b
2666 /// a
2667 /// Stack Out:
2668 /// truth -- Truth of (a > b)
2669 case SWF::ABC_ACTION_GREATERTHAN:
2670 {
2671 bool truth;
2672 ABSTRACT_COMPARE(truth, _stack.top(0), _stack.top(1), false);
2673 _stack.drop(1);
2674 _stack.top(0).set_bool(truth); // truth is b < a
2675 break;
2676 }
2677
2678 /// 0xB0 ABC_ACTION_GREATEREQUALS
2679 /// Stack In:
2680 /// b
2681 /// a
2682 /// Stack Out:
2683 /// truth -- Truth of (a >= b)
2684 case SWF::ABC_ACTION_GREATEREQUALS:
2685 {
2686 bool truth;
2687 ABSTRACT_COMPARE(truth, _stack.top(1), _stack.top(0), true);
2688 _stack.drop(1);
2689 _stack.top(0).set_bool(!truth); // truth is a < b
2690 break;
2691 }
2692
2693 /// 0xB1 ABC_ACTION_INSTANCEOF
2694 /// Stack In:
2695 /// super -- An object
2696 /// val -- An object
2697 /// Stack Out:
2698 /// truth -- Truth of "val is an instance of super"
2699 case SWF::ABC_ACTION_INSTANCEOF:
2700 {
2701 bool truth;
2702 ABSTRACT_TYPELATE(truth, _stack.top(1), _stack.top(0));
2703 _stack.top(1).set_bool(truth);
2704 _stack.drop(1);
2705 break;
2706 }
2707
2708 /// 0xB2 ABC_ACTION_ISTYPE
2709 /// Stream: V32 'name_id'
2710 /// Stack In:
2711 /// [ns] -- Namespace stuff
2712 /// obj -- An object
2713 /// Stack Out:
2714 /// truth -- Truth of "obj is of the type given in (resolve)'name_id'"
2715 case SWF::ABC_ACTION_ISTYPE:
2716 {
2717 MultiName a = pool_name(mStream->read_V32(), mPoolObject);
2718 _stack.drop(completeName(a));
2719 // TODO: Implement it.
2720 //_stack.top(0).set_bool(_stack.top(0).conforms_to(a.getABCName()));
2721 }
2722
2723 /// 0xB3 ABC_ACTION_ISTYPELATE
2724 /// Stack In:
2725 /// type -- A type to match
2726 /// obj -- An object
2727 /// Stack Out:
2728 /// truth -- Truth of "obj is of type"
2729 case SWF::ABC_ACTION_ISTYPELATE:
2730 {
2731 as_value type = pop_stack();
2732 as_value value = pop_stack();
2733 as_object* const valueObject = value.to_object(*_global);
2734 as_object* const typeObject = type.to_object(*_global);
2735
2736 if (!valueObject || !typeObject) {
2737 // TODO: what here!?
2738 // This should eventually be a malformed SWF error.
2739 log_error("ACTION_ISTYPELATE: require two objects "
2740 "on stack, got obj: %s, type: %s.",
2741 value, type);
2742 break;
2743 }
2744 const bool truth = valueObject->instanceOf(typeObject);
2745 push_stack(truth);
2746 break;
2747 }
2748
2749 /// 0xB4 ABC_ACTION_IN
2750 /// Stack In:
2751 /// obj -- The object to search for it
2752 /// name -- The name to find
2753 /// Stack Out:
2754 /// truth -- True if name is in current namespace or anywhere in object.
2755 /// Don't look in the namespace if obj is a dictionary.
2756 /// NB: Since there doesn't seem to be a way to make a dictionary, this
2757 /// is not done. If there is, fix this lack.
2758 case SWF::ABC_ACTION_IN:
2759 {
2760 log_unimpl("ABC_ACTION_IN");
2761 //TODO: _stack.top(1).set_bool(_stack.top(1).to_object(*_global).contains(_stack.top(0)));
2762 _stack.drop(1);
2763 break;
2764 }
2765
2766 /// 0xC0 ABC_ACTION_INCREMENT_I
2767 /// See: 0x91 (ABC_ACTION_INCREMENT), but forces types to int, not double
2768 case SWF::ABC_ACTION_INCREMENT_I:
2769 {
2770 _stack.top(0) = toInt(_stack.top(0)) + 1;
2771 break;
2772 }
2773
2774 /// 0xC1 ABC_ACTION_DECREMENT_I
2775 /// See: 0x93 (ABC_ACTION_DECREMENT), but forces types to int, not double
2776 case SWF::ABC_ACTION_DECREMENT_I:
2777 {
2778 _stack.top(0) = toInt(_stack.top(0)) - 1;
2779 break;
2780 }
2781
2782 /// 0xC2 ABC_ACTION_INCLOCAL_I
2783 /// See: 0x92 (ABC_ACTION_INCLOCAL), but forces types to int
2784 /// not double
2785 case SWF::ABC_ACTION_INCLOCAL_I:
2786 {
2787 const std::uint32_t foff = mStream->read_V32();
2788 setRegister(foff, toInt(getRegister(foff)) + 1);
2789 break;
2790 }
2791
2792 /// 0xC3 ABC_ACTION_DECLOCAL_I
2793 /// See: 0x94 (ABC_ACTION_DECLOCAL), but forces types to int,
2794 /// not double
2795 case SWF::ABC_ACTION_DECLOCAL_I:
2796 {
2797 const std::uint32_t foff = mStream->read_V32();
2798 setRegister(foff, toInt(getRegister(foff)) - 1);
2799 break;
2800 }
2801
2802 /// 0xC4 ABC_ACTION_NEGATE_I
2803 /// See: 0x90 (ABC_ACTION_NEGATE), but forces type to int,
2804 /// not double
2805 case SWF::ABC_ACTION_NEGATE_I:
2806 {
2807 _stack.top(0) = - toInt(_stack.top(0));
2808 break;
2809 }
2810
2811 /// 0xC5 ABC_ACTION_ADD_I
2812 /// See: 0xA0 (ABC_ACTION_ADD), but forces type to int
2813 case SWF::ABC_ACTION_ADD_I:
2814 {
2815 _stack.top(1) = toInt(_stack.top(1)) +
2816 toInt(_stack.top(0));
2817 _stack.drop(1);
2818 break;
2819 }
2820
2821 /// 0xC6 ABC_ACTION_SUBTRACT_I
2822 /// See: 0xA1 (ABC_ACTION_SUBTRACT), but forces type to int
2823 case SWF::ABC_ACTION_SUBTRACT_I:
2824 {
2825 _stack.top(1) = toInt(_stack.top(1)) -
2826 toInt(_stack.top(0));
2827 _stack.drop(1);
2828 break;
2829 }
2830
2831 /// 0xC7 ABC_ACTION_MULTIPLY_I
2832 /// See: 0xA2 (ABC_ACTION_MULTIPLY), but forces type to int
2833 case SWF::ABC_ACTION_MULTIPLY_I:
2834 {
2835 _stack.top(1) = toInt(_stack.top(0)) * toInt(_stack.top(1));
2836 _stack.drop(1);
2837 break;
2838 }
2839
2840 /// 0xD0 ABC_ACTION_GETLOCAL0
2841 /// 0xD1 ABC_ACTION_GETLOCAL1
2842 /// 0xD2 ABC_ACTION_GETLOCAL2
2843 /// 0xD3 ABC_ACTION_GETLOCAL3
2844 /// Frame: Load frame[#] as val
2845 /// Stack Out:
2846 /// val
2847 case SWF::ABC_ACTION_GETLOCAL0:
2848 case SWF::ABC_ACTION_GETLOCAL1:
2849 case SWF::ABC_ACTION_GETLOCAL2:
2850 case SWF::ABC_ACTION_GETLOCAL3:
2851 {
2852 //We shouldn't need to a call grow stack, because each function should now how big the stack will need to be and should allocate all the space, when it is loaded into the vm.
2853 // GROW_STACK();
2854 // _stack.grow(1);
2855 // _stack.push() instead?
2856
2857 push_stack(getRegister(opcode- SWF::ABC_ACTION_GETLOCAL0));
2858 // _stack.top(0) = _registers.value(opcode - SWF::ABC_ACTION_GETLOCAL0);
2859 break;
2860 }
2861 /// 0xD4 ABC_ACTION_SETLOCAL0
2862 /// 0xD5 ABC_ACTION_SETLOCAL1
2863 /// 0xD6 ABC_ACTION_SETLOCAL2
2864 /// 0xD7 ABC_ACTION_SETLOCAL3
2865 /// Frame: Store val as frame[#]
2866 /// Stack In:
2867 /// val
2868 /// Stack Out:
2869 /// .
2870 case SWF::ABC_ACTION_SETLOCAL0:
2871 case SWF::ABC_ACTION_SETLOCAL1:
2872 case SWF::ABC_ACTION_SETLOCAL2:
2873 case SWF::ABC_ACTION_SETLOCAL3:
2874 {
2875 setRegister(opcode - SWF::ABC_ACTION_SETLOCAL0,
2876 pop_stack());
2877 break;
2878 }
2879
2880 /// 0xEF ABC_ACTION_DEBUG
2881 /// Stream: 7 bytes of unknown stuff to be skipped
2882 /// Do: skip ahead 7 bytes in stream
2883 case SWF::ABC_ACTION_DEBUG:
2884 {
2885 mStream->seekBy(7);
2886 break;
2887 }
2888 /// 0xF0 ABC_ACTION_DEBUGLINE
2889 /// Stream: V32 'line_number'
2890 /// Do: Nothing, but line_number is for the debugger if wanted.
2891 case SWF::ABC_ACTION_DEBUGLINE:
2892 {
2893 mStream->skip_V32();
2894 break;
2895 }
2896 /// 0xF1 ABC_ACTION_DEBUGFILE
2897 /// Stream: V32 'name_offset'
2898 /// Do: Nothing. 'name_offset' into string pool is the file name if wanted.
2899 case SWF::ABC_ACTION_DEBUGFILE:
2900 {
2901 mStream->skip_V32();
2902 break;
2903 }
2904
2905 /// 0xF2 ABC_ACTION_BKPTLINE
2906 /// Stream: V32 'line_number'
2907 /// Do: Enter debugger if present, line_number is the
2908 /// line number in source.
2909 case SWF::ABC_ACTION_BKPTLINE:
2910 mStream->skip_V32();
2911 break;
2912 }
2913
2914 log_abc("* DONE *");
2915 IF_VERBOSE_ACTION(print_stack());
2916 }
2917 catch (const StackException& s) {
2918 log_error("Threw a stack exception during AVM2 execution.");
2919 }
2920 catch (ASException& s) {
2921 // TODO: The exception should be pushed back into AS execution
2922 // somehow.
2923 log_unimpl("Caught an AS exception from AVM2 execution.");
2924 }
2925
2926 }
2927 }
2928
2929 void
getMember(Class * pDefinition,MultiName & name,as_value & instance)2930 Machine::getMember(Class* pDefinition, MultiName& name,
2931 as_value& instance)
2932 {
2933 if (!instance.is_object())
2934 throw ASTypeError();
2935 #if 0
2936 if (!pBinding->isGetSet())
2937 {
2938 //TODO: _stack.push(pBinding->getFromInstance(instance));
2939 return;
2940 }
2941
2942 // This is a getter, so we need to execute it. Even those
2943 // written in C++ get called like this, with pushCall handling.
2944 // And push the instance ('this')
2945 _stack.push(instance);
2946 pushCall(1, &_stack.top(0), pBinding); //TODO: pBinding->getGetter());
2947 #else
2948 UNUSED(pDefinition);
2949 UNUSED(name);
2950 #endif
2951 }
2952
2953 void
setMember(Class * pDefinition,MultiName & name,as_value & instance,as_value & newvalue)2954 Machine::setMember(Class *pDefinition, MultiName& name, as_value& instance,
2955 as_value& newvalue)
2956 {
2957 if (!instance.is_object())
2958 throw ASReferenceError();
2959
2960 return;
2961 // TODO:
2962 #if 0
2963 asBinding *pBinding = pDefinition->getBinding(name.getABCName());
2964
2965 if (pBinding->isReadOnly())
2966 throw ASReferenceError();
2967
2968 if (!pBinding->isGetSet())
2969 {
2970 //TODO: pBinding->setInInstance(instance, newvalue);
2971 return;
2972 }
2973
2974 // Two parameters -- the target object, the value to set.
2975 _stack.push(instance);
2976 _stack.push(newvalue);
2977 pushCall(2, &_stack.top(1), pBinding); //TODO: pBinding->getSetter());
2978 #else
2979 UNUSED(pDefinition);
2980 UNUSED(name);
2981 UNUSED(newvalue);
2982 #endif
2983 }
2984
2985 int
completeName(MultiName & name,int offset)2986 Machine::completeName(MultiName& name, int offset)
2987 {
2988
2989 // TODO: implement this properly.
2990 // Should this really be called when there's nothing on the stack?
2991 if (_stack.empty()) return 0;
2992
2993 int size = 0;
2994
2995 if (name.isRuntime())
2996 {
2997 as_value obj = _stack.top(offset);
2998
2999 if (name.isRtns()) {
3000 ++size; // Ignore the Namespace.
3001 }
3002 }
3003 else if (name.isRtns())
3004 {
3005 //TODO: This should be a namespace //name.setNamespace(_stack.top(offset));
3006 ++size;
3007 }
3008 return size;
3009 }
3010
3011 Class*
findSuper(as_value & v,bool find_for_primitive)3012 Machine::findSuper(as_value &v, bool find_for_primitive)
3013 {
3014 if (v.is_undefined() || v.is_null()) return NULL;
3015
3016 if (v.is_object()) {
3017 Class *pProto = NULL; // TODO: v.to_object(*_global)->getScript();
3018 return pProto ? pProto->getSuper() : NULL;
3019 }
3020
3021 if (!find_for_primitive) return 0;
3022
3023 if (v.is_number()) {
3024 return NULL; // TODO: _classes->getScript(NSV::CLASS_NUMBER);
3025 }
3026
3027 // And so on...
3028 // TODO: Other primitives
3029 return 0;
3030 }
3031
3032 void
immediateFunction(const as_function * func,as_object * thisptr,as_value & storage,unsigned char stack_in,short stack_out)3033 Machine::immediateFunction(const as_function* func, as_object* thisptr,
3034 as_value& storage, unsigned char stack_in, short stack_out)
3035 {
3036 assert(func);
3037
3038 // TODO: Set up the fn to use the stack
3039 fn_call::Args args;
3040 size_t st = 0;
3041 while (st < stack_in) {
3042 args += _stack.top(st);
3043 ++st;
3044 }
3045
3046 fn_call fn(thisptr, as_environment(_vm), args);
3047 _stack.drop(stack_in - stack_out);
3048 saveState();
3049 _stack.grow(stack_in - stack_out);
3050 _stack.setDownstop(stack_in);
3051 mThis = thisptr;
3052 storage = const_cast<as_function*>(func)->call(fn);
3053 restoreState();
3054 }
3055
3056 void
pushGet(as_object * this_obj,as_value & return_slot,Property * prop)3057 Machine::pushGet(as_object *this_obj, as_value &return_slot, Property *prop)
3058 {
3059 if (!prop) return;
3060
3061 if (prop->isGetterSetter()) {
3062 //TODO pushCall(prop->getGetter(), this_obj, return_slot, 0);
3063 return;
3064 }
3065
3066 return_slot = prop->getValue(*this_obj);
3067 }
3068
3069 void
pushSet(as_object * this_obj,as_value & value,Property * prop)3070 Machine::pushSet(as_object *this_obj, as_value &value, Property *prop)
3071 {
3072 if (!prop) return;
3073
3074 if (prop->isGetterSetter()) {
3075 _stack.push(value);
3076 //TODO pushCall(prop->getSetter(), this_obj, mIgnoreReturn, 1);
3077 return;
3078 }
3079
3080 prop->setValue(*this_obj, value);
3081 }
3082
3083 void
pushCall(as_function * func,as_object * pthis,as_value & return_slot,unsigned char stack_in,short stack_out)3084 Machine::pushCall(as_function *func, as_object *pthis, as_value& return_slot,
3085 unsigned char stack_in, short stack_out)
3086 {
3087
3088 log_abc("Pushing function call for function %s", func);
3089
3090 if (1 || func->isBuiltin())
3091 {
3092 immediateFunction(func, pthis, return_slot, stack_in, stack_out);
3093 return;
3094 }
3095 // TODO: Make this work for stackless.
3096
3097 // Here is where the SafeStack shines:
3098 // We set the stack the way it should be on return.
3099 _stack.drop(stack_in - stack_out);
3100 // We save that state.
3101 saveState();
3102 // Set the 'this' for the new call
3103 mThis = pthis;
3104 // Retrieve the stack. (It wasn't lost)
3105 _stack.grow(stack_in - stack_out);
3106 // And then we set the downstop
3107 _stack.setDownstop(stack_in);
3108
3109 // When control goes to the main loop of the interpreter, it will
3110 // automatically start executing the method.
3111 }
3112
3113 void
restoreState()3114 Machine::restoreState()
3115 {
3116 log_abc("Restoring state.");
3117 State &s = mStateStack.top(0);
3118 s.to_debug_string();
3119 // _stack.setAllSizes(s._stackTotalSize, s._stackDepth);
3120 _scopeStack.setAllSizes(s.mScopeTotalSize, s._scopeStackDepth);
3121 mStream = s.mStream;
3122 _registers = s._registers;
3123 mCurrentFunction = s.mFunction;
3124 // mExitWithReturn = s.mReturn;
3125 // mDefaultXMLNamespace = s.mDefaultXMLNamespace;
3126 // mCurrentScope = s.mCurrentScope;
3127 // mGlobalReturn = s.mGlobalReturn;
3128 // mThis = s.mThis;
3129 mStateStack.pop();
3130 }
3131
3132 void
saveState()3133 Machine::saveState()
3134 {
3135 log_abc("Saving state.");
3136 mStateStack.grow(1);
3137 State &s = mStateStack.top(0);
3138 s._stackDepth = _stack.getDownstop();
3139 s._stackTotalSize = _stack.totalSize();
3140 s._scopeStackDepth = _scopeStack.getDownstop();
3141 s.mScopeTotalSize = _scopeStack.totalSize();
3142 s.mStream = mStream;
3143 s.to_debug_string();
3144 s._registers = _registers;
3145 s.mFunction = mCurrentFunction;
3146 // s.mReturn = mExitWithReturn;
3147 // s.mDefaultXMLNamespace = mDefaultXMLNamespace;
3148 // s.mCurrentScope = mCurrentScope;
3149 // s.mGlobalReturn = mGlobalReturn;
3150 // s.mThis = mThis;
3151 }
3152
3153 void
initMachine(AbcBlock * pool_block)3154 Machine::initMachine(AbcBlock* pool_block)
3155 {
3156 mPoolObject = pool_block;
3157 log_debug("Getting entry script.");
3158 Class* start_script = pool_block->scripts().back();
3159 log_debug("Getting constructor.");
3160 Method* constructor = start_script->getConstructor();
3161 clearRegisters(constructor->getMaxRegisters());
3162 log_debug("Loading code stream.");
3163 mStream = constructor->getBody();
3164 mCurrentFunction = constructor->getPrototype();
3165 setRegister(0, _global);
3166 }
3167
3168 //This is called by abc_functions to execute their code stream.
3169 //TODO: There is probably a better way to do this, once we understand what the VM is supposed
3170 //todo, this should be fixed.
3171 as_value
executeFunction(Method * method,const fn_call & fn)3172 Machine::executeFunction(Method* method, const fn_call& fn)
3173 {
3174
3175 //TODO: Figure out a good way to use the State object to handle
3176 //returning values.
3177 mCurrentFunction = method->getPrototype();
3178 bool prev_ext = mExitWithReturn;
3179 CodeStream *stream = method->getBody();
3180
3181 // Protect the current stack from alteration
3182 // TODO: use saveState only, but not before checking other effects.
3183 size_t stackdepth = _stack.fixDownstop();
3184 size_t stacksize = _stack.totalSize();
3185 size_t scopedepth = _scopeStack.fixDownstop();
3186 size_t scopesize = _scopeStack.totalSize();
3187
3188 saveState();
3189 mStream = stream;
3190 clearRegisters(method->getMaxRegisters());
3191
3192 log_abc("Executing function: max registers %s, scope depth %s, "
3193 "max scope %s, max stack: %s", method->getMaxRegisters(),
3194 method->scopeDepth(), method->maxScope(), method->maxStack());
3195 mExitWithReturn = true;
3196 setRegister(0, fn.this_ptr);
3197 for (unsigned int i=0;i<fn.nargs;i++) {
3198 setRegister(i + 1, fn.arg(i));
3199 }
3200
3201 execute();
3202 mExitWithReturn = prev_ext;
3203
3204 _stack.setAllSizes(stacksize, stackdepth);
3205 _scopeStack.setAllSizes(scopesize, scopedepth);
3206
3207 return mGlobalReturn;
3208 }
3209
3210 void
executeCodeblock(CodeStream * stream)3211 Machine::executeCodeblock(CodeStream* stream)
3212 {
3213 mStream = stream;
3214 execute();
3215 }
3216
3217 void
markReachableResources() const3218 Machine::markReachableResources() const
3219 {
3220 _global->setReachable();
3221 }
3222
3223 void
instantiateClass(std::string className,as_object *)3224 Machine::instantiateClass(std::string className, as_object* /*global*/)
3225 {
3226
3227 if (!mPoolObject) {
3228 log_debug("No ABC block! Can't instantiate class!");
3229 return;
3230 }
3231
3232 log_debug("instantiateClass: class name %s", className);
3233
3234 Class* cl = mPoolObject->locateClass(className);
3235 if (!cl)
3236 {
3237 /// This seems like a big error.
3238 log_error("Could not locate class '%s' for instantiation", className);
3239 return;
3240 }
3241
3242 Method* ctor = cl->getConstructor();
3243
3244 if (!ctor) {
3245 log_error("Class found has no constructor, can't instantiate "
3246 "class");
3247 return;
3248 }
3249
3250 clearRegisters(ctor->getMaxRegisters());
3251 mCurrentFunction = ctor->getPrototype();
3252
3253 // Protect the current stack from alteration
3254 // TODO: use saveState
3255 size_t stackdepth = _stack.fixDownstop();
3256 size_t stacksize = _stack.totalSize();
3257 size_t scopedepth = _scopeStack.fixDownstop();
3258 size_t scopesize = _scopeStack.totalSize();
3259
3260 // The value at _registers[0] is generally pushed to the stack for
3261 // CONSTRUCTSUPER, which apparently expects the object whose super
3262 // is to be constructed. The pp's stack names the class for instantiation
3263 // at register 0 when the constructor body is executed, which must
3264 // correspond to the class prototype.
3265 setRegister(0, cl->getPrototype());
3266 executeCodeblock(ctor->getBody());
3267 log_debug("Finished instantiating class %s", className);
3268
3269 _stack.setAllSizes(stacksize, stackdepth);
3270 _scopeStack.setAllSizes(scopesize, scopedepth);
3271
3272 }
3273
3274 as_value
find_prop_strict(MultiName multiname)3275 Machine::find_prop_strict(MultiName multiname)
3276 {
3277
3278 log_abc("Looking for property %2% in namespace %1%",
3279 mST.value(multiname.getNamespace()->getURI()),
3280 mST.value(multiname.getGlobalName()));
3281
3282 // We should not push anything onto the scope stack here; whatever is
3283 // needed should already be pushed. The pp will not call FINDPROP*
3284 // when the scope stack is empty.
3285 //
3286 // However, the complete scope stack, including elements that are
3287 // 'invisible' to this scope, is available
3288 as_value val;
3289 print_scope_stack();
3290 const string_table::key var = multiname.getGlobalName();
3291 const string_table::key ns = multiname.getNamespace()->getURI();
3292
3293 for (size_t i = 0; i < _scopeStack.totalSize(); ++i)
3294 {
3295 as_object* scope_object = _scopeStack.at(i);
3296 if (!scope_object) {
3297 log_abc("Scope object is NULL.");
3298 continue;
3299 }
3300
3301 if (scope_object->get_member(ObjectURI(var, ns), &val)) {
3302 push_stack(_scopeStack.at(i));
3303 return val;
3304 }
3305 }
3306
3307 // TODO: find out what to do here.
3308 log_abc("Failed to find property in scope stack.");
3309 as_object* null = 0;
3310 push_stack(null);
3311 return val;
3312 }
3313
3314 void
print_stack()3315 Machine::print_stack()
3316 {
3317
3318 std::stringstream ss;
3319 ss << "Stack: ";
3320 for (unsigned int i = 0; i < _stack.totalSize(); ++i) {
3321 if (i!=0) ss << " | ";
3322 ss << _stack.at(i);
3323 }
3324 log_abc("%s", ss.str());
3325 }
3326
3327 void
print_scope_stack()3328 Machine::print_scope_stack()
3329 {
3330
3331 std::stringstream ss;
3332 ss << "ScopeStack: ";
3333
3334 size_t totalSize = _scopeStack.totalSize();
3335
3336 for (unsigned int i = 0; i < totalSize; ++i) {
3337 ss << as_value(_scopeStack.at(i)).toDebugString();
3338 }
3339 log_abc("%s", ss.str());
3340 }
3341
3342 void
get_args(size_t argc,fn_call::Args & args)3343 Machine::get_args(size_t argc, fn_call::Args& args)
3344 {
3345 std::vector<as_value> v(argc);
3346 for (size_t i = argc; i > 0; --i) {
3347 v.at(i-1) = pop_stack();
3348 }
3349 args.swap(v);
3350 }
3351
3352 void
clearRegisters(std::uint32_t maxRegisters)3353 Machine::clearRegisters(std::uint32_t maxRegisters)
3354 {
3355 _registers.clear();
3356 _registers.resize(maxRegisters);
3357 }
3358
3359
3360 } // namespace abc
3361 } // namespace gnash
3362