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