1 // ASHandlers.cpp:  ActionScript handlers, for Gnash.
2 //
3 //   Copyright (C) 2005, 2006, 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 #ifdef HAVE_CONFIG_H
22 #include "gnashconfig.h"
23 #endif
24 
25 #include "ASHandlers.h"
26 
27 #include <string>
28 #include <vector>
29 #include <boost/random.hpp>
30 #include <algorithm>
31 
32 #include "log.h"
33 #include "SWF.h"
34 #include "rc.h"
35 #include "NativeFunction.h"
36 #include "Function.h"
37 #include "as_function.h"
38 #include "Function2.h"
39 #include "fn_call.h"
40 #include "ActionExec.h"
41 #include "MovieClip.h"
42 #include "as_environment.h"
43 #include "URL.h"
44 #include "action_buffer.h"
45 #include "as_object.h"
46 #include "DragState.h"
47 #include "VM.h" // for getting the root
48 #include "movie_root.h"
49 #include "sound_handler.h"
50 #include "namedStrings.h"
51 #include "utf8.h"
52 #include "StringPredicates.h"
53 #include "GnashNumeric.h"
54 #include "Global_as.h"
55 #include "DisplayObject.h"
56 #include "as_environment.h"
57 #include "as_value.h"
58 #include "RunResources.h"
59 #include "ObjectURI.h"
60 
61 // GNASH_PARANOIA_LEVEL:
62 // 0 : no assertions
63 // 1 : quick assertions
64 // 2 : check that handlers are called on correct tag
65 #ifndef GNASH_PARANOIA_LEVEL
66 # define GNASH_PARANOIA_LEVEL 1
67 #endif
68 
69 namespace gnash {
70 
71 namespace {
72 
73     as_object* construct_object(as_function* ctor_as_func, as_environment& env,
74             unsigned int nargs);
75 
76     /// Convert to object without an exception being thrown.
77     //
78     /// @return     null if the value cannot be converted to an object.
79     as_object* safeToObject(VM& vm, const as_value& val);
80 
81     /// Common code for ActionGetUrl and ActionGetUrl2
82     //
83     /// @param target         the target window or _level1 to _level10
84     /// @param method       0:NONE, 1:GET, 2:POST
85     void commonGetURL(as_environment& env, as_value target,
86             const std::string& url, std::uint8_t method);
87 
88     /// Common code for SetTarget and SetTargetExpression
89     ///
90     /// @param target_name      The target name. If empty new target will
91     ///                         be the main movie.
92     /// @param thread           The current execution thread.
93     void commonSetTarget(ActionExec& thread, const std::string& target_name);
94 
95 
96     void ActionEnd(ActionExec& thread);
97     void ActionNextFrame(ActionExec& thread);
98     void ActionPrevFrame(ActionExec& thread);
99     void ActionPlay(ActionExec& thread);
100     void ActionStop(ActionExec& thread);
101     void ActionToggleQuality(ActionExec& thread);
102     void ActionStopSounds(ActionExec& thread);
103     void ActionGotoFrame(ActionExec& thread);
104     void ActionGetUrl(ActionExec& thread);
105     void ActionWaitForFrame(ActionExec& thread);
106     void ActionSetTarget(ActionExec& thread);
107     void ActionGotoLabel(ActionExec& thread);
108     void ActionAdd(ActionExec& thread);
109     void ActionSubtract(ActionExec& thread);
110     void ActionMultiply(ActionExec& thread);
111     void ActionDivide(ActionExec& thread);
112     void ActionEqual(ActionExec& thread);
113     void ActionLessThan(ActionExec& thread);
114     void ActionLogicalAnd(ActionExec& thread);
115     void ActionLogicalOr(ActionExec& thread);
116     void ActionLogicalNot(ActionExec& thread);
117     void ActionStringEq(ActionExec& thread);
118     void ActionStringLength(ActionExec& thread);
119     void ActionSubString(ActionExec& thread);
120     void ActionPop(ActionExec& thread);
121     void ActionInt(ActionExec& thread);
122     void ActionGetVariable(ActionExec& thread);
123     void ActionSetVariable(ActionExec& thread);
124     void ActionSetTargetExpression(ActionExec& thread);
125     void ActionStringConcat(ActionExec& thread);
126     void ActionGetProperty(ActionExec& thread);
127     void ActionSetProperty(ActionExec& thread);
128     void ActionDuplicateClip(ActionExec& thread);
129     void ActionRemoveClip(ActionExec& thread);
130     void ActionTrace(ActionExec& thread);
131     void ActionStartDragMovie(ActionExec& thread);
132     void ActionStopDragMovie(ActionExec& thread);
133     void ActionStringCompare(ActionExec& thread);
134     void ActionThrow(ActionExec& thread);
135     void ActionCastOp(ActionExec& thread);
136     void ActionImplementsOp(ActionExec& thread);
137     void ActionFscommand2(ActionExec& thread);
138     void ActionRandom(ActionExec& thread);
139     void ActionMbLength(ActionExec& thread);
140     void ActionOrd(ActionExec& thread);
141     void ActionChr(ActionExec& thread);
142     void ActionGetTimer(ActionExec& thread);
143     void ActionMbSubString(ActionExec& thread);
144     void ActionMbOrd(ActionExec& thread);
145     void ActionMbChr(ActionExec& thread);
146     void ActionStrictMode(ActionExec& thread);
147     void ActionWaitForFrameExpression(ActionExec& thread);
148     void ActionPushData(ActionExec& thread);
149     void ActionBranchAlways(ActionExec& thread);
150     void ActionGetUrl2(ActionExec& thread);
151     void ActionBranchIfTrue(ActionExec& thread);
152     void ActionCallFrame(ActionExec& thread);
153     void ActionGotoExpression(ActionExec& thread);
154     void ActionDelete(ActionExec& thread);
155     void ActionDelete2(ActionExec& thread);
156     void ActionVarEquals(ActionExec& thread);
157     void ActionCallFunction(ActionExec& thread);
158     void ActionReturn(ActionExec& thread);
159     void ActionModulo(ActionExec& thread);
160     void ActionNew(ActionExec& thread);
161     void ActionVar(ActionExec& thread);
162     void ActionInitArray(ActionExec& thread);
163     void ActionInitObject(ActionExec& thread);
164     void ActionTypeOf(ActionExec& thread);
165     void ActionTargetPath(ActionExec& thread);
166     void ActionEnumerate(ActionExec& thread);
167     void ActionNewAdd(ActionExec& thread);
168     void ActionNewLessThan(ActionExec& thread);
169     void ActionNewEquals(ActionExec& thread);
170     void ActionToNumber(ActionExec& thread);
171     void ActionToString(ActionExec& thread);
172     void ActionDup(ActionExec& thread);
173     void ActionSwap(ActionExec& thread);
174     void ActionGetMember(ActionExec& thread);
175     void ActionSetMember(ActionExec& thread);
176     void ActionIncrement(ActionExec& thread);
177     void ActionDecrement(ActionExec& thread);
178     void ActionCallMethod(ActionExec& thread);
179     void ActionNewMethod(ActionExec& thread);
180     void ActionInstanceOf(ActionExec& thread);
181     void ActionEnum2(ActionExec& thread);
182     void ActionBitwiseAnd(ActionExec& thread);
183     void ActionBitwiseOr(ActionExec& thread);
184     void ActionBitwiseXor(ActionExec& thread);
185     void ActionShiftLeft(ActionExec& thread);
186     void ActionShiftRight(ActionExec& thread);
187     void ActionShiftRight2(ActionExec& thread);
188     void ActionStrictEq(ActionExec& thread);
189     void ActionGreater(ActionExec& thread);
190     void ActionStringGreater(ActionExec& thread);
191     void ActionExtends(ActionExec& thread);
192     void ActionConstantPool(ActionExec& thread);
193     void ActionDefineFunction2(ActionExec& thread);
194     void ActionTry(ActionExec& thread);
195     void ActionWith(ActionExec& thread);
196     void ActionDefineFunction(ActionExec& thread);
197     void ActionSetRegister(ActionExec& thread);
198     void ActionUnsupported(ActionExec& thread);
199 }
200 
201 namespace {
202 
203 class Enumerator : public KeyVisitor
204 {
205 public:
Enumerator(as_environment & env)206     explicit Enumerator(as_environment& env) : _env(env) {}
operator ()(const ObjectURI & uri)207     virtual void operator()(const ObjectURI& uri) {
208         _env.push(uri.toString(getStringTable(_env)));
209     }
210 private:
211     as_environment& _env;
212 };
213 
214 }
215 
216 namespace SWF {
217 
ActionHandler()218 ActionHandler::ActionHandler()
219     :
220     _type(ACTION_END), // initialized to zero to have well known value
221     _callback(ActionUnsupported),
222     _arg_format(ARG_NONE)
223 {
224 }
225 
ActionHandler(ActionType type,ActionCallback func,ArgumentType format)226 ActionHandler::ActionHandler(ActionType type, ActionCallback func,
227         ArgumentType format)
228     :
229     _type(type),
230     _callback(func),
231     _arg_format(format)
232 {
233 }
234 
235 void
execute(ActionExec & thread) const236 ActionHandler::execute(ActionExec& thread) const
237 {
238     return _callback(thread);
239 }
240 
SWFHandlers()241 SWFHandlers::SWFHandlers()
242     :
243     _handlers(256)
244 {
245 
246     _handlers[ACTION_END] = ActionHandler(ACTION_END, ActionEnd);
247     _handlers[ACTION_NEXTFRAME] = ActionHandler(ACTION_NEXTFRAME,
248              ActionNextFrame);
249     _handlers[ACTION_PREVFRAME] =  ActionHandler(ACTION_PREVFRAME,
250              ActionPrevFrame);
251     _handlers[ACTION_PLAY] = ActionHandler(ACTION_PLAY, ActionPlay);
252     _handlers[ACTION_STOP] = ActionHandler(ACTION_STOP, ActionStop);
253     _handlers[ACTION_TOGGLEQUALITY] = ActionHandler(ACTION_TOGGLEQUALITY,
254              ActionToggleQuality);
255     _handlers[ACTION_STOPSOUNDS] = ActionHandler(ACTION_STOPSOUNDS,
256              ActionStopSounds);
257     _handlers[ACTION_GOTOFRAME] = ActionHandler(ACTION_GOTOFRAME,
258              ActionGotoFrame, ARG_U16);
259     _handlers[ACTION_GETURL] = ActionHandler(ACTION_GETURL,
260              ActionGetUrl, ARG_STR);
261     _handlers[ACTION_WAITFORFRAME] = ActionHandler(ACTION_WAITFORFRAME,
262              ActionWaitForFrame, ARG_HEX);
263     _handlers[ACTION_SETTARGET] = ActionHandler(ACTION_SETTARGET,
264              ActionSetTarget, ARG_STR);
265     _handlers[ACTION_GOTOLABEL] = ActionHandler(ACTION_GOTOLABEL,
266              ActionGotoLabel, ARG_STR);
267     _handlers[ACTION_ADD] = ActionHandler(ACTION_ADD, ActionAdd);
268     _handlers[ACTION_SUBTRACT] = ActionHandler(ACTION_SUBTRACT, ActionSubtract);
269     _handlers[ACTION_MULTIPLY] = ActionHandler(ACTION_MULTIPLY, ActionMultiply);
270     _handlers[ACTION_DIVIDE] = ActionHandler(ACTION_DIVIDE, ActionDivide);
271     _handlers[ACTION_EQUAL] = ActionHandler(ACTION_EQUAL, ActionEqual);
272     _handlers[ACTION_LESSTHAN] = ActionHandler(ACTION_LESSTHAN, ActionLessThan);
273     _handlers[ACTION_LOGICALAND] = ActionHandler(ACTION_LOGICALAND,
274              ActionLogicalAnd);
275     _handlers[ACTION_LOGICALOR] = ActionHandler(ACTION_LOGICALOR,
276              ActionLogicalOr);
277     _handlers[ACTION_LOGICALNOT] = ActionHandler(ACTION_LOGICALNOT,
278              ActionLogicalNot);
279     _handlers[ACTION_STRINGEQ] = ActionHandler(ACTION_STRINGEQ,
280              ActionStringEq);
281     _handlers[ACTION_STRINGLENGTH] = ActionHandler(ACTION_STRINGLENGTH,
282              ActionStringLength);
283     _handlers[ACTION_SUBSTRING] = ActionHandler(ACTION_SUBSTRING,
284              ActionSubString);
285     _handlers[ACTION_POP] = ActionHandler(ACTION_POP, ActionPop);
286     _handlers[ACTION_INT] = ActionHandler(ACTION_INT, ActionInt);
287     _handlers[ACTION_GETVARIABLE] = ActionHandler(ACTION_GETVARIABLE,
288              ActionGetVariable);
289     _handlers[ACTION_SETVARIABLE] = ActionHandler(ACTION_SETVARIABLE,
290              ActionSetVariable);
291     _handlers[ACTION_SETTARGETEXPRESSION] =
292         ActionHandler(ACTION_SETTARGETEXPRESSION,
293              ActionSetTargetExpression);
294     _handlers[ACTION_STRINGCONCAT] = ActionHandler(ACTION_STRINGCONCAT,
295              ActionStringConcat);
296     _handlers[ACTION_GETPROPERTY] = ActionHandler(ACTION_GETPROPERTY,
297              ActionGetProperty);
298     _handlers[ACTION_SETPROPERTY] = ActionHandler(ACTION_SETPROPERTY,
299              ActionSetProperty);
300     _handlers[ACTION_DUPLICATECLIP] = ActionHandler(ACTION_DUPLICATECLIP,
301              ActionDuplicateClip);
302     _handlers[ACTION_REMOVECLIP] = ActionHandler(ACTION_REMOVECLIP,
303              ActionRemoveClip);
304     _handlers[ACTION_TRACE] = ActionHandler(ACTION_TRACE, ActionTrace);
305     _handlers[ACTION_STARTDRAGMOVIE] = ActionHandler(ACTION_STARTDRAGMOVIE,
306              ActionStartDragMovie);
307     _handlers[ACTION_STOPDRAGMOVIE] = ActionHandler(ACTION_STOPDRAGMOVIE,
308              ActionStopDragMovie);
309     _handlers[ACTION_STRINGCOMPARE] = ActionHandler(ACTION_STRINGCOMPARE,
310              ActionStringCompare);
311     _handlers[ACTION_THROW] = ActionHandler(ACTION_THROW, ActionThrow);
312     _handlers[ACTION_CASTOP] = ActionHandler(ACTION_CASTOP, ActionCastOp);
313     _handlers[ACTION_IMPLEMENTSOP] = ActionHandler(ACTION_IMPLEMENTSOP,
314              ActionImplementsOp);
315     _handlers[ACTION_FSCOMMAND2] = ActionHandler(ACTION_FSCOMMAND2,
316              ActionFscommand2);
317     _handlers[ACTION_RANDOM] = ActionHandler(ACTION_RANDOM, ActionRandom);
318     _handlers[ACTION_MBLENGTH] = ActionHandler(ACTION_MBLENGTH, ActionMbLength);
319     _handlers[ACTION_ORD] = ActionHandler(ACTION_ORD, ActionOrd);
320     _handlers[ACTION_CHR] = ActionHandler(ACTION_CHR, ActionChr);
321     _handlers[ACTION_GETTIMER] = ActionHandler(ACTION_GETTIMER, ActionGetTimer);
322     _handlers[ACTION_MBSUBSTRING] = ActionHandler(ACTION_MBSUBSTRING,
323              ActionMbSubString);
324     _handlers[ACTION_MBORD] = ActionHandler(ACTION_MBORD, ActionMbOrd);
325     _handlers[ACTION_MBCHR] = ActionHandler(ACTION_MBCHR, ActionMbChr);
326     _handlers[ACTION_STRICTMODE] = ActionHandler(ACTION_STRICTMODE,
327              ActionStrictMode, ARG_U8);
328     _handlers[ACTION_WAITFORFRAMEEXPRESSION] =
329         ActionHandler(ACTION_WAITFORFRAMEEXPRESSION,
330              ActionWaitForFrameExpression, ARG_HEX);
331     _handlers[ACTION_PUSHDATA] = ActionHandler(ACTION_PUSHDATA, ActionPushData,
332             ARG_PUSH_DATA);
333     _handlers[ACTION_BRANCHALWAYS] = ActionHandler(ACTION_BRANCHALWAYS,
334              ActionBranchAlways, ARG_S16);
335     _handlers[ACTION_GETURL2] = ActionHandler(ACTION_GETURL2, ActionGetUrl2,
336             ARG_HEX);
337     _handlers[ACTION_BRANCHIFTRUE] = ActionHandler(ACTION_BRANCHIFTRUE,
338              ActionBranchIfTrue, ARG_S16);
339     _handlers[ACTION_CALLFRAME] = ActionHandler(ACTION_CALLFRAME,
340             ActionCallFrame, ARG_HEX);
341     _handlers[ACTION_GOTOEXPRESSION] = ActionHandler(ACTION_GOTOEXPRESSION,
342             ActionGotoExpression, ARG_HEX);
343     _handlers[ACTION_DELETE] = ActionHandler(ACTION_DELETE, ActionDelete);
344     _handlers[ACTION_DELETE2] = ActionHandler(ACTION_DELETE2, ActionDelete2);
345     _handlers[ACTION_VAREQUALS] = ActionHandler(ACTION_VAREQUALS,
346             ActionVarEquals);
347     _handlers[ACTION_CALLFUNCTION] = ActionHandler(ACTION_CALLFUNCTION,
348             ActionCallFunction);
349     _handlers[ACTION_RETURN] = ActionHandler(ACTION_RETURN, ActionReturn);
350     _handlers[ACTION_MODULO] = ActionHandler(ACTION_MODULO, ActionModulo);
351     _handlers[ACTION_NEW] = ActionHandler(ACTION_NEW, ActionNew);
352     _handlers[ACTION_VAR] = ActionHandler(ACTION_VAR, ActionVar);
353     _handlers[ACTION_INITARRAY] = ActionHandler(ACTION_INITARRAY,
354             ActionInitArray);
355     _handlers[ACTION_INITOBJECT] = ActionHandler(ACTION_INITOBJECT,
356             ActionInitObject);
357     _handlers[ACTION_TYPEOF] = ActionHandler(ACTION_TYPEOF, ActionTypeOf);
358     _handlers[ACTION_TARGETPATH] = ActionHandler(ACTION_TARGETPATH,
359             ActionTargetPath);
360     _handlers[ACTION_ENUMERATE] = ActionHandler(ACTION_ENUMERATE,
361             ActionEnumerate);
362     _handlers[ACTION_NEWADD] = ActionHandler(ACTION_NEWADD, ActionNewAdd);
363     _handlers[ACTION_NEWLESSTHAN] = ActionHandler(ACTION_NEWLESSTHAN,
364             ActionNewLessThan);
365     _handlers[ACTION_NEWEQUALS] = ActionHandler(ACTION_NEWEQUALS,
366             ActionNewEquals);
367     _handlers[ACTION_TONUMBER] = ActionHandler(ACTION_TONUMBER, ActionToNumber);
368     _handlers[ACTION_TOSTRING] = ActionHandler(ACTION_TOSTRING, ActionToString);
369     _handlers[ACTION_DUP] = ActionHandler(ACTION_DUP, ActionDup);
370     _handlers[ACTION_SWAP] = ActionHandler(ACTION_SWAP, ActionSwap);
371     _handlers[ACTION_GETMEMBER] = ActionHandler(ACTION_GETMEMBER,
372             ActionGetMember);
373     _handlers[ACTION_SETMEMBER] = ActionHandler(ACTION_SETMEMBER,
374             ActionSetMember);
375     _handlers[ACTION_INCREMENT] = ActionHandler(ACTION_INCREMENT,
376             ActionIncrement);
377     _handlers[ACTION_DECREMENT] = ActionHandler(ACTION_DECREMENT,
378             ActionDecrement);
379     _handlers[ACTION_CALLMETHOD] = ActionHandler(ACTION_CALLMETHOD,
380             ActionCallMethod);
381     _handlers[ACTION_NEWMETHOD] = ActionHandler(ACTION_NEWMETHOD,
382             ActionNewMethod);
383     _handlers[ACTION_INSTANCEOF] = ActionHandler(ACTION_INSTANCEOF,
384             ActionInstanceOf);
385     _handlers[ACTION_ENUM2] = ActionHandler(ACTION_ENUM2, ActionEnum2);
386     _handlers[ACTION_BITWISEAND] = ActionHandler(ACTION_BITWISEAND,
387             ActionBitwiseAnd);
388     _handlers[ACTION_BITWISEOR] = ActionHandler(ACTION_BITWISEOR,
389             ActionBitwiseOr);
390     _handlers[ACTION_BITWISEXOR] = ActionHandler(ACTION_BITWISEXOR,
391             ActionBitwiseXor);
392     _handlers[ACTION_SHIFTLEFT] = ActionHandler(ACTION_SHIFTLEFT,
393             ActionShiftLeft);
394     _handlers[ACTION_SHIFTRIGHT] = ActionHandler(ACTION_SHIFTRIGHT,
395             ActionShiftRight);
396     _handlers[ACTION_SHIFTRIGHT2] = ActionHandler(ACTION_SHIFTRIGHT2,
397             ActionShiftRight2);
398     _handlers[ACTION_STRICTEQ] = ActionHandler(ACTION_STRICTEQ, ActionStrictEq);
399     _handlers[ACTION_GREATER] = ActionHandler(ACTION_GREATER, ActionGreater);
400     _handlers[ACTION_STRINGGREATER] = ActionHandler(ACTION_STRINGGREATER,
401             ActionStringGreater);
402     _handlers[ACTION_EXTENDS] = ActionHandler(ACTION_EXTENDS, ActionExtends);
403     _handlers[ACTION_CONSTANTPOOL] = ActionHandler(ACTION_CONSTANTPOOL,
404             ActionConstantPool, ARG_DECL_DICT);
405     _handlers[ACTION_DEFINEFUNCTION2] = ActionHandler(ACTION_DEFINEFUNCTION2,
406             ActionDefineFunction2, ARG_FUNCTION2);
407     _handlers[ACTION_TRY] = ActionHandler(ACTION_TRY, ActionTry, ARG_FUNCTION2);
408     _handlers[ACTION_WITH] = ActionHandler(ACTION_WITH,
409             ActionWith, ARG_U16);
410     _handlers[ACTION_DEFINEFUNCTION] = ActionHandler(ACTION_DEFINEFUNCTION,
411             ActionDefineFunction, ARG_HEX);
412     _handlers[ACTION_SETREGISTER] = ActionHandler(ACTION_SETREGISTER,
413             ActionSetRegister, ARG_U8);
414 }
415 
~SWFHandlers()416 SWFHandlers::~SWFHandlers()
417 {
418 }
419 
420 const SWFHandlers&
instance()421 SWFHandlers::instance()
422 {
423     static const SWFHandlers instance;
424     return instance;
425 }
426 
427 void
execute(ActionType type,ActionExec & thread) const428 SWFHandlers::execute(ActionType type, ActionExec& thread) const
429 {
430     try {
431         _handlers[type].execute(thread);
432     }
433     catch (const ActionParserException& e) {
434         log_swferror(_("Malformed action code: %s"), e.what());
435     }
436 }
437 
438 } // namespace SWF
439 
440 
441 namespace {
442 
443 void
ActionEnd(ActionExec & thread)444 ActionEnd(ActionExec& thread)
445 {
446 #if GNASH_PARANOIA_LEVEL > 1
447     assert(thread.atActionTag(SWF::ACTION_END));
448 #endif
449     log_error (_("%s: CHECKME: was broken"), __PRETTY_FUNCTION__);
450     thread.skipRemainingBuffer();
451 }
452 
453 
454 void
ActionNextFrame(ActionExec & thread)455 ActionNextFrame(ActionExec& thread)
456 {
457     as_environment& env = thread.env;
458 
459 #if GNASH_PARANOIA_LEVEL > 1
460     assert(thread.atActionTag(SWF::ACTION_NEXTFRAME));
461 #endif
462 
463     DisplayObject* tgtch = env.target();
464     MovieClip* tgt = tgtch ? tgtch->to_movie() : nullptr;
465     if (tgt) tgt->goto_frame(tgt->get_current_frame() + 1);
466     else {
467         log_debug("ActionNextFrame: as_environment target is null "
468                     "or not a sprite");
469     }
470 }
471 
472 void
ActionPrevFrame(ActionExec & thread)473 ActionPrevFrame(ActionExec& thread)
474 {
475     as_environment& env = thread.env;
476 
477 #if GNASH_PARANOIA_LEVEL > 1
478     assert(thread.atActionTag(SWF::ACTION_PREVFRAME));
479 #endif
480 
481     DisplayObject* tgtch = env.target();
482     MovieClip* tgt = tgtch ? tgtch->to_movie() : nullptr;
483     if (tgt) tgt->goto_frame(tgt->get_current_frame() - 1);
484     else log_debug("ActionPrevFrame: as_environment target is null or not a sprite");
485 }
486 
487 void
ActionPlay(ActionExec & thread)488 ActionPlay(ActionExec& thread)
489 {
490     as_environment& env = thread.env;
491 
492 #if GNASH_PARANOIA_LEVEL > 1
493     assert(thread.atActionTag(SWF::ACTION_PLAY));
494 #endif
495 
496     DisplayObject* tgtch = env.target();
497     MovieClip* tgt = tgtch ? tgtch->to_movie() : nullptr;
498     if (tgt) tgt->setPlayState(MovieClip::PLAYSTATE_PLAY);
499     else log_debug("ActionPlay: as_environment target is null or not a sprite");
500 }
501 
502 void
ActionStop(ActionExec & thread)503 ActionStop(ActionExec& thread)
504 {
505     as_environment& env = thread.env;
506 
507 #if GNASH_PARANOIA_LEVEL > 1
508     assert(thread.atActionTag(SWF::ACTION_STOP));
509 #endif
510 
511     DisplayObject* tgtch = env.target();
512     MovieClip* tgt = tgtch ? tgtch->to_movie() : nullptr;
513     if (tgt) tgt->setPlayState(MovieClip::PLAYSTATE_STOP);
514     else log_debug("ActionStop: as_environment target is null or not a sprite");
515 }
516 
517 void
ActionToggleQuality(ActionExec & thread)518 ActionToggleQuality(ActionExec& thread)
519 {
520     movie_root& mr = getRoot(thread.env);
521     if (mr.getQuality() != QUALITY_HIGH) {
522         mr.setQuality(QUALITY_HIGH);
523         return;
524     }
525     mr.setQuality(QUALITY_LOW);
526 }
527 
528 void
ActionStopSounds(ActionExec & thread)529 ActionStopSounds(ActionExec& thread)
530 {
531 #if GNASH_PARANOIA_LEVEL > 1
532     assert(thread.atActionTag(SWF::ACTION_STOPSOUNDS));
533 #endif
534 
535     VM& vm = getVM(thread.env);
536     sound::sound_handler* s = vm.getRoot().runResources().soundHandler();
537     if (s) {
538         s->stop_all_sounds();
539     }
540 }
541 
542 void
ActionGotoFrame(ActionExec & thread)543 ActionGotoFrame(ActionExec& thread)
544 {
545     as_environment& env = thread.env;
546     const action_buffer& code = thread.code;
547 
548 #if GNASH_PARANOIA_LEVEL > 1
549     assert(thread.atActionTag(SWF::ACTION_GOTOFRAME));
550 #endif
551 
552     size_t frame = code.read_int16(thread.getCurrentPC() + 3);
553 
554     DisplayObject* tgtch = env.target();
555     MovieClip* tgt = tgtch ? tgtch->to_movie() : nullptr;
556 
557     // frame number within this tag is hard-coded and 0-based
558     if (tgt) tgt->goto_frame(frame);
559     else {
560         log_debug("ActionGotoFrame: as_environment target is null "
561                 "or not a sprite");
562     }
563 }
564 
565 void
ActionGetUrl(ActionExec & thread)566 ActionGetUrl(ActionExec& thread)
567 {
568     as_environment& env = thread.env;
569     const action_buffer& code = thread.code;
570 
571 #if GNASH_PARANOIA_LEVEL > 1
572     assert(thread.atActionTag(SWF::ACTION_GETURL));
573 #endif
574 
575     size_t pc = thread.getCurrentPC();
576 
577     // If this is an FSCommand, then call the callback
578     // handler, if any.
579 
580     // Two strings as args.
581     // TODO: make sure the NULL terminations are there
582     // we could implement a safe_read_string(pc, maxlen)
583     // and use tag length as maxlen
584     //size_t tag_length = code.read_int16(pc+1);
585     const char* url = code.read_string(pc+3);
586     size_t urlLength = strlen(url)+1;
587 
588     // Will abort if code.read_string returns 0, but action
589     // buffer should always have a null terminator at the
590     // end. This replaces an assertion in commonGetURL.
591     const std::string target(code.read_string(pc + 3 + urlLength));
592 
593     IF_VERBOSE_ACTION (
594         log_action(_("GetUrl: target=%s URL=%s"), target, url);
595     );
596 
597     commonGetURL(env, target, url, 0u);
598 }
599 
600 void
ActionWaitForFrame(ActionExec & thread)601 ActionWaitForFrame(ActionExec& thread)
602 {
603     as_environment& env = thread.env;
604     const action_buffer& code = thread.code;
605 
606 #if GNASH_PARANOIA_LEVEL > 1
607     assert(thread.atActionTag(SWF::ACTION_WAITFORFRAME));
608 #endif
609 
610     // SWF integrity check
611     const size_t tag_len = code.read_int16(thread.getCurrentPC() + 1);
612     if (tag_len != 3) {
613         IF_VERBOSE_MALFORMED_SWF (
614             log_swferror(_("ActionWaitForFrame (0x%X) tag length == %d "
615                    "(expected 3)"), SWF::ACTION_WAITFORFRAME, tag_len);
616         );
617     }
618 
619     // If we haven't loaded a specified frame yet, then
620     // skip the specified number of actions.
621     //
622     unsigned int framenum = code.read_int16(thread.getCurrentPC()+3);
623     std::uint8_t skip = code[thread.getCurrentPC()+5];
624 
625     DisplayObject* target = env.target();
626     MovieClip* target_sprite = target ? target->to_movie() : nullptr;
627     if (!target_sprite) {
628         log_error(_("%s: environment target is null or not a MovieClip"),
629                 __FUNCTION__);
630         return;
631     }
632 
633     const size_t totframes = target_sprite->get_frame_count();
634     if (framenum > totframes) {
635         IF_VERBOSE_ASCODING_ERRORS(
636         log_aserror(_("ActionWaitForFrame(%d): "
637                        "target (%s) has only %d frames"),
638                        framenum, totframes);
639         );
640         framenum = totframes;
641     }
642 
643     // Actually *wait* for target frame, and never skip any action
644     const size_t lastloaded = target_sprite->get_loaded_frames();
645     if (lastloaded < framenum) {
646         // better delegate this to ActionExec
647         thread.skip_actions(skip);
648     }
649 
650 }
651 
652 void
ActionSetTarget(ActionExec & thread)653 ActionSetTarget(ActionExec& thread)
654 {
655     const action_buffer& code = thread.code;
656     size_t pc = thread.getCurrentPC();
657 
658 #if GNASH_PARANOIA_LEVEL > 1
659     assert(thread.atActionTag(SWF::ACTION_SETTARGET)); // 0x8B
660 #endif
661 
662     // Change the movie we're working on.
663     const std::string target_name(code.read_string(pc+3));
664 
665     commonSetTarget(thread, target_name);
666 }
667 
668 void
ActionGotoLabel(ActionExec & thread)669 ActionGotoLabel(ActionExec& thread)
670 {
671     as_environment& env = thread.env;
672     const action_buffer& code = thread.code;
673 
674     const char* frame_label = code.read_string(thread.getCurrentPC()+3);
675     DisplayObject *target = env.target();
676     MovieClip *target_sprite = target ? target->to_movie() : nullptr;
677     if (!target_sprite) {
678         log_error(_("GotoLabel: environment target is null or not a "
679                     "MovieClip"));
680     }
681     else {
682         target_sprite->goto_labeled_frame(frame_label);
683     }
684 }
685 
686 void
ActionAdd(ActionExec & thread)687 ActionAdd(ActionExec& thread)
688 {
689     as_environment& env = thread.env;
690 
691     const double operand2 = toNumber(env.top(0), getVM(env));
692     const double operand1 = toNumber(env.top(1), getVM(env));
693     env.top(1) = operand1 + operand2;
694     env.drop(1);
695 }
696 
697 void
ActionSubtract(ActionExec & thread)698 ActionSubtract(ActionExec& thread)
699 {
700     as_environment& env = thread.env;
701     subtract(env.top(1), env.top(0), getVM(env));
702     env.drop(1);
703 }
704 
705 void
ActionMultiply(ActionExec & thread)706 ActionMultiply(ActionExec& thread)
707 {
708     as_environment& env = thread.env;
709 
710     const double operand2 = toNumber(env.top(0), getVM(env));
711     const double operand1 = toNumber(env.top(1), getVM(env));
712     env.top(1) = operand1 * operand2;
713     env.drop(1);
714 }
715 
716 
717 // Negative number / 0: -infinity
718 // Positive number / 0: infinity
719 // 0 / 0 : NaN
720 // Either operand is NaN: NaN
721 void
ActionDivide(ActionExec & thread)722 ActionDivide(ActionExec& thread)
723 {
724     as_environment& env = thread.env;
725 
726     const double operand2 = toNumber(env.top(0), getVM(env));
727     const double operand1 = toNumber(env.top(1), getVM(env));
728 
729     if (operand2 == 0) {
730         if (env.get_version() < 5) {
731             env.top(1).set_string("#ERROR#");
732         }
733         else if (operand1 == 0 || isNaN(operand1) || isNaN(operand2)) {
734             setNaN(env.top(1));
735         }
736         else {
737             // Division by -0.0 is not possible in AS, so
738             // the sign of the resulting infinity should match the
739             // sign of operand1. Division by 0 in C++ is undefined
740             // behaviour.
741             env.top(1) = operand1 < 0 ?
742                 - std::numeric_limits<double>::infinity() :
743                 std::numeric_limits<double>::infinity();
744         }
745 
746     }
747     else {
748         env.top(1) = operand1 / operand2;
749     }
750     env.drop(1);
751 }
752 
753 void
ActionEqual(ActionExec & thread)754 ActionEqual(ActionExec& thread)
755 {
756     as_environment& env = thread.env;
757 
758 #if GNASH_PARANOIA_LEVEL > 1
759     assert(thread.atActionTag(SWF::ACTION_EQUAL)); // 0x0E
760 #endif
761 
762     const double op1 = toNumber(env.top(0), getVM(env));
763     const double op2 = toNumber(env.top(1), getVM(env));
764 
765     env.top(1).set_bool(op2 == op1);
766 
767     // Flash4 used 1 and 0 as return from this tag
768     if (env.get_version() < 5) convertToNumber(env.top(1), getVM(env));
769 
770     env.drop(1);
771 }
772 
773 void
ActionLessThan(ActionExec & thread)774 ActionLessThan(ActionExec& thread)
775 {
776     as_environment& env = thread.env;
777 
778     // NB: this unusual order is correct!
779     const double d2 = toNumber(env.top(1), getVM(env));
780     const double d1 = toNumber(env.top(0), getVM(env));
781 
782     env.top(1).set_bool(d2 < d1);
783 
784     // Flash4 used 1 and 0 as return from this tag
785     if (env.get_version() < 5) convertToNumber(env.top(1), getVM(env));
786 
787     env.drop(1);
788 }
789 
790 void
ActionLogicalAnd(ActionExec & thread)791 ActionLogicalAnd(ActionExec& thread)
792 {
793     as_environment& env = thread.env;
794 
795     // Note: the order of evaluation of the && operands is specified.
796     env.top(1).set_bool(toBool(env.top(1), getVM(env)) &&
797             toBool(env.top(0), getVM(env)));
798     env.drop(1);
799 }
800 
801 void
ActionLogicalOr(ActionExec & thread)802 ActionLogicalOr(ActionExec& thread)
803 {
804     as_environment& env = thread.env;
805 
806     // Note: the order of evaluation of the || operands is specified.
807     env.top(1).set_bool(toBool(env.top(1), getVM(env)) ||
808             toBool(env.top(0), getVM(env)));
809     env.drop(1);
810 }
811 
812 void
ActionLogicalNot(ActionExec & thread)813 ActionLogicalNot(ActionExec& thread)
814 {
815     as_environment& env = thread.env;
816 
817     env.top(0).set_bool(!toBool(env.top(0), getVM(env)));
818 
819     // Flash4 used 1 and 0 as return from this tag
820     if (env.get_version() < 5) convertToNumber(env.top(0), getVM(env));
821 }
822 
823 void
ActionStringEq(ActionExec & thread)824 ActionStringEq(ActionExec& thread)
825 {
826     as_environment& env = thread.env;
827 
828     const int version = env.get_version();
829     const std::string& str0 = env.top(0).to_string(version);
830     const std::string& str1 = env.top(1).to_string(version);
831 
832     env.top(1).set_bool(str0 == str1);
833     env.drop(1);
834 }
835 
836 void
ActionStringLength(ActionExec & thread)837 ActionStringLength(ActionExec& thread)
838 {
839     as_environment& env = thread.env;
840 
841     // NOTE: I've tested that we should change behaviour
842     //       based on code definition version, not top-level
843     //       SWF version. Just not automated yet.
844     //
845     const int version = thread.code.getDefinitionVersion();
846     if (version > 5) {
847         // when SWF version is > 5 we compute the multi-byte length
848         ActionMbLength(thread);
849     }
850     else {
851         env.top(0).set_double(env.top(0).to_string(version).size());
852     }
853 }
854 
855 void
ActionSubString(ActionExec & thread)856 ActionSubString(ActionExec& thread)
857 {
858     // substring("string",  base,  size)
859     // SWF4 function, deprecated in favour of String.substring.
860     // 1-based (String object methods are 0-based).
861     as_environment& env = thread.env;
862 
863     const as_value& strval = env.top(2);
864 
865     // Undefined values should resolve to 0.
866     int size = toInt(env.top(0), getVM(env));
867     int start = toInt(env.top(1), getVM(env));
868 
869     const int version = env.get_version();
870     const std::wstring wstr = utf8::decodeCanonicalString(
871                                 strval.to_string(version), version);
872 
873     if (size < 0) {
874         IF_VERBOSE_ASCODING_ERRORS(
875             log_aserror(_("Negative size passed to ActionSubString, "
876             "taking as whole length"));
877         );
878         size = wstr.length();
879     }
880 
881     if (size == 0 || wstr.empty()) {
882         env.drop(2);
883         env.top(0).set_string("");
884         return;
885     }
886 
887 
888     // TODO: if 'start' or 'size' do not evaluate to numbers return
889     //       the empty string (how do we check if they evaluate ??)
890     if (start < 1) {
891         IF_VERBOSE_ASCODING_ERRORS (
892             log_aserror(_("Start is less then 1 in ActionSubString, "
893             "setting to 1."));
894         );
895         start = 1;
896     }
897 
898     // If start is longer than the string length, return empty
899     // string
900     else if (static_cast<unsigned int>(start) > wstr.length()) {
901         IF_VERBOSE_ASCODING_ERRORS (
902             log_aserror(_("Start goes beyond input string in ActionSubString, "
903             "returning the empty string."));
904         );
905         env.drop(2);
906         env.top(0).set_string("");
907         return;
908     }
909 
910     // Adjust the start for our own use.
911     --start;
912 
913     if (static_cast<unsigned int>(start + size) > wstr.length()) {
914         IF_VERBOSE_ASCODING_ERRORS (
915             log_aserror(_("start + size goes beyond input string in "
916                     "ActionSubString, adjusting size"));
917         );
918         size = wstr.length() - start;
919     }
920 
921 #if GNASH_PARANOIA_LEVEL > 1
922     assert(start >= 0);
923     assert(static_cast<unsigned int>(start) < wstr.length() );
924     assert(size >= 0);
925 #endif
926 
927     env.drop(2);
928     env.top(0).set_string(utf8::encodeCanonicalString(
929                                     wstr.substr(start, size), version));
930 }
931 
932 void
ActionPop(ActionExec & thread)933 ActionPop(ActionExec& thread)
934 {
935     as_environment& env = thread.env;
936     env.drop(1);
937 }
938 
939 void
ActionInt(ActionExec & thread)940 ActionInt(ActionExec& thread)
941 {
942     as_environment& env = thread.env;
943     env.top(0).set_double(toInt(env.top(0), getVM(env)));
944 }
945 
946 void
ActionGetVariable(ActionExec & thread)947 ActionGetVariable(ActionExec& thread)
948 {
949     as_environment& env = thread.env;
950 
951     as_value& top_value = env.top(0);
952     const std::string& var_string = top_value.to_string();
953     if (var_string.empty()) {
954         top_value.set_undefined();
955         return;
956     }
957 
958     top_value = thread.getVariable(var_string);
959     if (env.get_version() < 5 && top_value.is_sprite()) {
960         // See http://www.ferryhalim.com/orisinal/g2/penguin.htm
961         IF_VERBOSE_ASCODING_ERRORS(
962             log_aserror(_("Can't assign a sprite/DisplayObject to a "
963                 "variable in SWF%d. We'll return undefined instead of %s."),
964                 env.get_version(), top_value);
965         );
966         top_value.set_undefined();
967     }
968 
969     IF_VERBOSE_ACTION(
970         log_action(_("-- get var: %s=%s"), var_string, top_value);
971     );
972 }
973 
974 void
ActionSetVariable(ActionExec & thread)975 ActionSetVariable(ActionExec& thread)
976 {
977     as_environment& env = thread.env;
978 
979     const std::string& name = env.top(1).to_string();
980     if (name.empty()) {
981         IF_VERBOSE_ASCODING_ERRORS (
982             // Invalid object, can't set.
983             log_aserror(_("ActionSetVariable: %s=%s: variable name "
984                     "evaluates to invalid (empty) string"),
985                     env.top(1), env.top(0));
986         );
987     }
988     thread.setVariable(name, env.top(0));
989 
990     IF_VERBOSE_ACTION(
991         log_action(_("-- set var: %s = %s"), name, env.top(0));
992     );
993 
994     env.drop(2);
995 }
996 
997 void
ActionSetTargetExpression(ActionExec & thread)998 ActionSetTargetExpression(ActionExec& thread)
999 {
1000     as_environment& env = thread.env;
1001 
1002     // we don't ues the target sprite directly, instead we fetch the
1003     // _target(string type) of that sprite first and then search the
1004     // final target(might be a different one). See tests in
1005     // opcode_guard_test2.sc
1006     //
1007     // For _versioned, see swfdec's settarget2-tostring.as (swf 7 and 8)
1008     //
1009     std::string target_name = env.top(0).to_string(env.get_version());
1010 
1011     commonSetTarget(thread, target_name);
1012 
1013     env.drop(1); // pop the target sprite off the stack
1014 }
1015 
1016 void
ActionStringConcat(ActionExec & thread)1017 ActionStringConcat(ActionExec& thread)
1018 {
1019     as_environment& env = thread.env;
1020     const int version = getSWFVersion(env);
1021 
1022     const std::string& op1 = env.top(0).to_string(version);
1023     const std::string& op2 = env.top(1).to_string(version);
1024 
1025     env.top(1).set_string(op2 + op1);
1026     env.drop(1);
1027 }
1028 
1029 void
ActionGetProperty(ActionExec & thread)1030 ActionGetProperty(ActionExec& thread)
1031 {
1032     as_environment& env = thread.env;
1033 
1034     as_value& tgt_val = env.top(1);
1035     std::string tgt_str = tgt_val.to_string();
1036 
1037     DisplayObject* target;
1038     if (tgt_str.empty()) {
1039         target = get<DisplayObject>(thread.getTarget());
1040         if (!target) {
1041             IF_VERBOSE_MALFORMED_SWF(
1042                 log_swferror(_("ActionGetProperty(<empty>) called, but "
1043                     "current target is not a DisplayObject"));
1044             );
1045         }
1046     }
1047     else {
1048         target = findTarget(env, tgt_str);
1049     }
1050 
1051     // FIXME: what happens when it's an invalid number? This will cause
1052     // undefined behaviour on overflow.
1053     unsigned int prop_number = toNumber(env.top(0), getVM(env));
1054 
1055     if (target) {
1056         getIndexedProperty(prop_number, *target, env.top(1));
1057     }
1058     else {
1059         // ASCODING error ? (well, last time it was a gnash error ;)
1060         IF_VERBOSE_ASCODING_ERRORS(
1061             log_aserror(_("Could not find GetProperty target (%s)"),
1062                     tgt_val);
1063         );
1064         env.top(1) = as_value();
1065     }
1066     env.drop(1);
1067 }
1068 
1069 void
ActionSetProperty(ActionExec & thread)1070 ActionSetProperty(ActionExec& thread)
1071 {
1072     as_environment& env = thread.env;
1073 
1074     DisplayObject *target = findTarget(env, env.top(2).to_string());
1075     // FIXME: what happens when it's an invalid number? This will cause
1076     // undefined behaviour on overflow.
1077     unsigned int prop_number = toNumber(env.top(1), getVM(env));
1078 
1079     as_value prop_val = env.top(0);
1080 
1081     if (target) {
1082         setIndexedProperty(prop_number, *target, prop_val);
1083     }
1084     else {
1085         IF_VERBOSE_ASCODING_ERRORS(
1086             log_aserror(_("ActionSetProperty: can't find target %s for "
1087                     "setting property %s"), env.top(2), prop_number);
1088         )
1089     }
1090     env.drop(3);
1091 }
1092 
1093 void
ActionDuplicateClip(ActionExec & thread)1094 ActionDuplicateClip(ActionExec& thread)
1095 {
1096     as_environment& env = thread.env;
1097 
1098     // Movies should be attachable from -16384 to 2130690044. See
1099     // Tests in misc-ming.all/DepthLimitsTest.c.
1100     const double depth = toNumber(env.top(0), getVM(env)) +
1101         DisplayObject::staticDepthOffset;
1102 
1103     // This also checks for overflow, as both numbers are expressible as
1104     // std::int32_t.
1105     if (depth < DisplayObject::lowerAccessibleBound ||
1106       depth > DisplayObject::upperAccessibleBound) {
1107 
1108         IF_VERBOSE_ASCODING_ERRORS(
1109             log_aserror(_("duplicateMovieClip: invalid depth %d passed; "
1110                     "not duplicating"), depth);
1111         );
1112         env.drop(3);
1113         return;
1114     }
1115 
1116     std::int32_t depthValue = static_cast<std::int32_t>(depth);
1117 
1118     const std::string& newname = env.top(1).to_string();
1119     const std::string& path = env.top(2).to_string();
1120 
1121     DisplayObject* ch = findTarget(env, path);
1122     if (!ch) {
1123         IF_VERBOSE_ASCODING_ERRORS(
1124             log_aserror(_("Path given to duplicateMovieClip(%s) doesn't "
1125                     "point to a DisplayObject"),
1126                 path);
1127         );
1128         env.drop(3);
1129         return;
1130     }
1131 
1132     MovieClip* sprite = ch->to_movie();
1133     if (!sprite) {
1134         IF_VERBOSE_ASCODING_ERRORS(
1135         log_aserror(_("Path given to duplicateMovieClip(%s) is not a sprite"),
1136             path);
1137         );
1138         env.drop(3);
1139         return;
1140     }
1141 
1142     sprite->duplicateMovieClip(newname, depthValue);
1143     env.drop(3);
1144 }
1145 
1146 void
ActionRemoveClip(ActionExec & thread)1147 ActionRemoveClip(ActionExec& thread)
1148 {
1149     as_environment& env = thread.env;
1150 
1151     const std::string path = env.pop().to_string();
1152 
1153     DisplayObject* ch = findTarget(env, path);
1154     if (!ch) {
1155         IF_VERBOSE_ASCODING_ERRORS(
1156             log_aserror(_("Path given to removeMovieClip(%s) doesn't "
1157                     "point to a DisplayObject"),
1158                 path);
1159         );
1160         return;
1161     }
1162 
1163     MovieClip* sprite = ch->to_movie();
1164     if (!sprite) {
1165         IF_VERBOSE_ASCODING_ERRORS(
1166         log_aserror(_("Path given to removeMovieClip(%s) is not a sprite"),
1167             path);
1168         );
1169         return;
1170     }
1171     sprite->removeMovieClip();
1172 }
1173 
1174 /// \brief Trace messages from the Flash movie using trace();
1175 void
ActionTrace(ActionExec & thread)1176 ActionTrace(ActionExec& thread)
1177 {
1178     as_environment& env = thread.env;
1179     const std::string val = env.pop().to_string();
1180 
1181     // Logging with a std::string here fails the swfdec testsuite,
1182     // probably because the first 0 character terminates the output
1183     // with a c_str, whereas a std::string outputs the entire length
1184     // of the string.
1185     log_trace("%s", val.c_str());
1186 }
1187 
1188 void
ActionStartDragMovie(ActionExec & thread)1189 ActionStartDragMovie(ActionExec& thread)
1190 {
1191     as_environment& env = thread.env;
1192 
1193 #if GNASH_PARANOIA_LEVEL > 1
1194     assert(thread.atActionTag(SWF::ACTION_STARTDRAGMOVIE));
1195 #endif
1196 
1197     DisplayObject* tgt = findTarget(env, env.top(0).to_string());
1198     if (tgt) {
1199         // mark this DisplayObject as script transformed.
1200         tgt->transformedByScript();
1201     }
1202     else {
1203         IF_VERBOSE_ASCODING_ERRORS(
1204             log_aserror(_("startDrag: unknown target '%s'"), env.top(0));
1205         );
1206     }
1207 
1208     const bool lock = toBool(env.top(1), getVM(env));
1209     DragState st(tgt, lock);
1210 
1211     // toNumber because we found out that ming writes "0" for the third
1212     // argument, and this converts to true when converted to a bool when
1213     // it should actually convert to false!
1214     if (toNumber(env.top(2), getVM(env))) {
1215 
1216         std::int32_t y1 = pixelsToTwips(toNumber(env.top(3), getVM(env)));
1217         std::int32_t x1 = pixelsToTwips(toNumber(env.top(4), getVM(env)));
1218         std::int32_t y0 = pixelsToTwips(toNumber(env.top(5), getVM(env)));
1219         std::int32_t x0 = pixelsToTwips(toNumber(env.top(6), getVM(env)));
1220 
1221         // check for swapped values
1222         if (y1 < y0) {
1223             IF_VERBOSE_MALFORMED_SWF(
1224                 log_swferror(_("Y values in ActionStartDrag swapped, fixing"));
1225             );
1226             std::swap(y1, y0);
1227         }
1228 
1229         if (x1 < x0) {
1230             IF_VERBOSE_MALFORMED_SWF(
1231                 log_swferror(_("X values in ActionStartDrag swapped, fixing"));
1232             );
1233             std::swap(x1, x0);
1234         }
1235         const SWFRect bounds(x0, y0, x1, y1);
1236         st.setBounds(bounds);
1237         env.drop(4);
1238     }
1239 
1240     env.drop(3);
1241 
1242     if (tgt) {
1243         VM& vm = getVM(env);
1244         vm.getRoot().setDragState(st);
1245     }
1246 }
1247 
1248 void
ActionStopDragMovie(ActionExec & thread)1249 ActionStopDragMovie(ActionExec& thread)
1250 {
1251     as_environment& env = thread.env;
1252     // Previously this checked the target, but manual tests show no
1253     // need to do that; the drag is always stopped.
1254     getRoot(env).stop_drag();
1255 }
1256 
1257 void
ActionStringCompare(ActionExec & thread)1258 ActionStringCompare(ActionExec& thread)
1259 {
1260     as_environment& env = thread.env;
1261 
1262     const int ver = env.get_version();
1263     const std::string& op1 = env.top(0).to_string(ver);
1264     const std::string& op2 = env.top(1).to_string(ver);
1265     env.top(1).set_bool(op2 < op1);
1266     env.drop(1);
1267 }
1268 
1269 void
ActionThrow(ActionExec & thread)1270 ActionThrow(ActionExec& thread)
1271 {
1272     as_environment& env = thread.env;
1273 
1274     // Throw the value on the top of the stack.
1275     env.top(0).flag_exception();
1276 
1277     // Proceed to the end of the code block to throw.
1278     thread.skipRemainingBuffer();
1279 }
1280 
1281 void
ActionCastOp(ActionExec & thread)1282 ActionCastOp(ActionExec& thread)
1283 {
1284     as_environment& env = thread.env;
1285 
1286     // Get the "instance"
1287     as_object* instance = safeToObject(getVM(thread.env), env.top(0));
1288 
1289     // Get the "super" function
1290     as_object* super = safeToObject(getVM(thread.env), env.top(1));
1291 
1292     // Invalid args!
1293     if (!super || !instance) {
1294         IF_VERBOSE_ASCODING_ERRORS (
1295         log_aserror(_("-- %s cast_to %s (invalid args?)"),
1296             env.top(1),
1297             env.top(0));
1298         );
1299 
1300         env.drop(1);
1301         env.top(0).set_null();
1302         return;
1303     }
1304 
1305     env.drop(1);
1306     if (instance->instanceOf(super)) {
1307         env.top(0) = as_value(instance);
1308     }
1309     else {
1310         env.top(0).set_null();
1311     }
1312 
1313 }
1314 
1315 
1316 /// Implements the "implements" opcode
1317 //
1318 /// Example AS code: "C implements B;"
1319 //
1320 /// The above code makes an instance of C ("var c = new C();") return true
1321 /// for "c instanceOf B);". That seems to be the end of its usefulness, as
1322 /// c inherits no properties from B.
1323 void
ActionImplementsOp(ActionExec & thread)1324 ActionImplementsOp(ActionExec& thread)
1325 {
1326     as_environment& env = thread.env;
1327 
1328     as_value objval = env.pop();
1329     as_object* obj = safeToObject(getVM(thread.env), objval);
1330     int count = toNumber(env.pop(), getVM(env));
1331 
1332     if (!obj) {
1333         IF_VERBOSE_ASCODING_ERRORS(
1334             log_aserror(_("Stack value on IMPLEMENTSOP is not an object: %s."),
1335                 objval);
1336         );
1337         return;
1338     }
1339 
1340     as_value protoval;
1341     if (!obj->get_member(NSV::PROP_PROTOTYPE, &protoval)) {
1342         IF_VERBOSE_ASCODING_ERRORS(
1343             log_aserror(_("Target object for IMPLEMENTSOP has no prototype."));
1344         );
1345         return;
1346     }
1347     obj = safeToObject(getVM(thread.env), protoval);
1348     if (!obj) {
1349         IF_VERBOSE_ASCODING_ERRORS(
1350             log_aserror(_("IMPLEMENTSOP target object's prototype is not "
1351                     "an object (%s)"), protoval);
1352         );
1353         return;
1354     }
1355 
1356     if (count <= 0) {
1357         IF_VERBOSE_ASCODING_ERRORS(
1358             log_aserror(_("Invalid interfaces count (%d) on IMPLEMENTSOP"),
1359                 count);
1360         );
1361         return;
1362     }
1363 
1364     while (count--) {
1365         as_value ctorval = env.pop();
1366         as_object* ctor = safeToObject(getVM(thread.env), ctorval);
1367         if (!ctor) {
1368             IF_VERBOSE_ASCODING_ERRORS(
1369                 log_aserror(_("class found on stack on IMPLEMENTSOP is "
1370                         "not an object: %s"), ctorval);
1371             );
1372             continue;
1373         }
1374         if (!ctor->get_member(NSV::PROP_PROTOTYPE, &protoval)) {
1375             IF_VERBOSE_ASCODING_ERRORS(
1376                 log_aserror(_("Interface object for IMPLEMENTSOP has no "
1377                         "prototype."));
1378             );
1379             continue;
1380         }
1381         as_object *inter = safeToObject(getVM(thread.env), protoval);
1382         if (!inter) {
1383             IF_VERBOSE_ASCODING_ERRORS(
1384                 log_aserror(_("Prototype of interface object for "
1385                         "IMPLEMENTSOP is not an object (%s)."), protoval);
1386             );
1387             continue;
1388         }
1389 
1390         IF_VERBOSE_ACTION(
1391             log_action(_("%s (with .prototype %p) implements %s (with "
1392                          ".prototype %p)"), objval, (void*)obj, ctorval, (void*)inter);
1393         );
1394         obj->addInterface(inter);
1395     }
1396 }
1397 
1398 /// FsCommand2 is not part of the Flash spec. It belongs to
1399 /// Flash Lite (from 1.1 onwards) and is used to control
1400 /// devices (backlight, vibrate etc).
1401 void
ActionFscommand2(ActionExec & thread)1402 ActionFscommand2(ActionExec& thread)
1403 {
1404 #if GNASH_PARANOIA_LEVEL > 1
1405     assert(thread.atActionTag(SWF::ACTION_FSCOMMAND2)); // 0x0E
1406 #endif
1407 
1408     as_environment& env = thread.env;
1409 
1410     unsigned int off=0;
1411 
1412     const unsigned int nargs = toInt(env.top(off++), getVM(env));
1413 
1414     std::string cmd = env.top(off++).to_string();
1415 
1416     std::ostringstream ss;
1417     ss << cmd << "(";
1418     for (unsigned int i = 1; i < nargs; ++i)
1419     {
1420         as_value arg = env.top(off++);
1421         if ( i > 1 ) ss << ", ";
1422         ss << arg;
1423     }
1424     ss << ")";
1425 
1426     LOG_ONCE(log_unimpl(_("fscommand2:%s"), ss.str()) );
1427 
1428     // TODO: check wheter or not we should drop anything from
1429     //       the stack, some reports and the Canonical tests
1430     //       suggest we shoudn't
1431 
1432 }
1433 
1434 void
ActionRandom(ActionExec & thread)1435 ActionRandom(ActionExec& thread)
1436 {
1437     as_environment& env = thread.env;
1438 
1439     // Action random(n) should return an integer from 0 up to (not
1440     // including) n.
1441     // It was introduced in SWF4 and deprecated in favour of
1442     // Math.random() in SWF5.
1443 
1444     int max = toInt(env.top(0), getVM(env));
1445 
1446     if (max < 1) max = 1;
1447 
1448     // Get pointer to static random generator in VM
1449     VM::RNG& rnd = getVM(env).randomNumberGenerator();
1450 
1451     // Produces int (0 <= n <= max - 1)
1452     boost::uniform_int<> uni_dist(0, max - 1);
1453     boost::variate_generator<VM::RNG&,
1454         boost::uniform_int<> > uni(rnd, uni_dist);
1455 
1456     env.top(0).set_double(uni());
1457 }
1458 
1459 void
ActionMbLength(ActionExec & thread)1460 ActionMbLength(ActionExec& thread)
1461 {
1462     as_environment& env = thread.env;
1463 
1464     std::string str = env.top(0).to_string();
1465 
1466     if (str.empty()) {
1467         env.top(0).set_double(0);
1468     }
1469     else {
1470         int length;
1471         std::vector<int> unused;
1472         unused.resize(str.length()+1);
1473         utf8::guessEncoding(str, length, unused);
1474         env.top(0).set_double(length);
1475     }
1476 }
1477 
1478 void
ActionOrd(ActionExec & thread)1479 ActionOrd(ActionExec& thread)
1480 {
1481     as_environment& env = thread.env;
1482 
1483     // Should return 0
1484     const int swfVersion = thread.code.getDefinitionVersion();
1485 
1486     std::string str = env.top(0).to_string();
1487 
1488     if (str.empty()) {
1489         env.top(0).set_double(0);
1490         return;
1491     }
1492 
1493     std::wstring wstr = utf8::decodeCanonicalString(str, swfVersion);
1494 
1495     // decodeCanonicalString should correctly work out what the first
1496     // character is according to version.
1497     env.top(0).set_double(wstr.at(0));
1498 }
1499 
1500 void
ActionChr(ActionExec & thread)1501 ActionChr(ActionExec& thread)
1502 {
1503     as_environment& env = thread.env;
1504 
1505     // This is UB:
1506     const std::uint16_t c = toInt(env.top(0), getVM(env));
1507 
1508     // If the argument to chr() is '0', we return
1509     // nothing, not NULL
1510     if (c == 0) {
1511         env.top(0).set_string("");
1512         return;
1513     }
1514 
1515     const int swfVersion = thread.code.getDefinitionVersion();
1516     if (swfVersion > 5) {
1517         env.top(0).set_string(utf8::encodeUnicodeCharacter(c));
1518         return;
1519     }
1520 
1521     // SWF 5 only:
1522     // This casts to unsigned char to a string, giving
1523     // IS0-8859-1 8-bit characters.
1524     // Values above 256 evaluate to value % 256,
1525     // through the cast, which is expected behaviour.
1526     const unsigned char uc = static_cast<unsigned char>(c);
1527     if (uc == 0) {
1528         env.top(0).set_string("");
1529         return;
1530     }
1531     env.top(0).set_string(std::string(1, uc));
1532 }
1533 
1534 void
ActionGetTimer(ActionExec & thread)1535 ActionGetTimer(ActionExec& thread)
1536 {
1537     as_environment& env = thread.env;
1538     const VM& vm = getVM(env);
1539     env.push(vm.getTime());
1540 }
1541 
1542 void
ActionMbSubString(ActionExec & thread)1543 ActionMbSubString(ActionExec& thread)
1544 {
1545     as_environment& env = thread.env;
1546 
1547     const as_value& arg0 = env.top(0);
1548     const as_value& arg1 = env.top(1);
1549 
1550     // Undefined values should resolve to 0.
1551     int size = toInt(env.top(0), getVM(env));
1552     int start = toInt(env.top(1), getVM(env));
1553 
1554     as_value& string_val = env.top(2);
1555 
1556     IF_VERBOSE_ACTION(
1557         log_action(_(" ActionMbSubString(%s, %d, %d)"), string_val, arg0, arg1);
1558     );
1559 
1560     env.drop(2);
1561 
1562     const int version = env.get_version();
1563     std::string str = string_val.to_string(version);
1564     int length = 0;
1565     std::vector<int> offsets;
1566 
1567     utf8::EncodingGuess encoding = utf8::guessEncoding(str, length, offsets);
1568 
1569     if (size < 0) {
1570         IF_VERBOSE_ASCODING_ERRORS(
1571             log_aserror(_("Negative size passed to ActionSubString, "
1572             "taking as whole length"));
1573         );
1574         size = length;
1575     }
1576 
1577     if (start < 1) {
1578         IF_VERBOSE_ASCODING_ERRORS(
1579             log_aserror(_("Base is less then 1 in ActionMbSubString, "
1580             "setting to 1."));
1581         );
1582         start = 1;
1583     }
1584     else if ( start > length) {
1585         IF_VERBOSE_ASCODING_ERRORS (
1586             log_aserror(_("base goes beyond input string in ActionMbSubString, "
1587             "returning the empty string."));
1588         );
1589         env.top(0).set_string("");
1590         return;
1591     }
1592 
1593     // Adjust the start for our own use.
1594     --start;
1595 
1596     if (size + start > length) {
1597         IF_VERBOSE_ASCODING_ERRORS(
1598             log_aserror(_("base+size goes beyond input string in ActionMbSubString, "
1599                 "adjusting size based on length:%d and start:%d"), length, start);
1600         );
1601         size = length - start;
1602     }
1603 
1604     if (encoding == utf8::ENCGUESS_OTHER) {
1605         env.top(0).set_string(str.substr(start, size));
1606     }
1607     else {
1608         env.top(0).set_string(str.substr(offsets.at(start),
1609                         offsets.at(start + size) - offsets.at(start)));
1610     }
1611 }
1612 
1613 void
ActionMbOrd(ActionExec & thread)1614 ActionMbOrd(ActionExec& thread)
1615 {
1616     /// This only deals with UTF-8 characters.
1617     /// TODO: what else is possible?
1618     /// TODO: fix for SWF5
1619     as_environment& env = thread.env;
1620 
1621     if (env.get_version() == 5) {
1622         log_unimpl(_("Not properly implemented for SWF5"));
1623         // No need to return - it works a bit.
1624     }
1625 
1626     const std::string s = env.top(0).to_string();
1627 
1628     std::string::const_iterator it = s.begin(), e = s.end();
1629 
1630     std::uint32_t out = utf8::decodeNextUnicodeCharacter(it, e);
1631 
1632     /// Always valid, or can it be undefined?
1633     env.top(0).set_double(out);
1634 }
1635 
1636 void
ActionMbChr(ActionExec & thread)1637 ActionMbChr(ActionExec& thread)
1638 {
1639     /// This only generates UTF-8 characters. No idea
1640     /// what difference user locale might make, but UTF-8
1641     /// is generally GOOD.
1642 
1643     /// TODO: fix for SWF5
1644     as_environment& env = thread.env;
1645 
1646     if (env.get_version() == 5) {
1647         log_unimpl(_("Not properly implemented for SWF5"));
1648         // No need to return.
1649     }
1650 
1651     // This is UB
1652     const std::uint16_t i = toInt(env.top(0), getVM(env));
1653 
1654     std::string out = utf8::encodeUnicodeCharacter(i);
1655 
1656     /// Always valid, or can it be undefined?
1657     env.top(0).set_string(out);
1658 
1659 }
1660 
1661 /// Sets strict mode in the compiler.
1662 //
1663 /// This is irrelevant for execution, but included for completeness.
1664 void
ActionStrictMode(ActionExec & thread)1665 ActionStrictMode(ActionExec& thread)
1666 {
1667     const action_buffer& code = thread.code;
1668 
1669     // off if 0, on for anything else.
1670     const bool on = code[thread.getCurrentPC() + 3];
1671 
1672     IF_VERBOSE_ACTION(
1673         log_action(_("ActionStrictMode set to %1%"), on);
1674     );
1675 }
1676 
1677 // also known as WaitForFrame2
1678 void
ActionWaitForFrameExpression(ActionExec & thread)1679 ActionWaitForFrameExpression(ActionExec& thread)
1680 {
1681     as_environment& env = thread.env;
1682     const action_buffer& code = thread.code;
1683 
1684     // how many actions to skip if frame has not been loaded
1685     const std::uint8_t skip = code[thread.getCurrentPC() + 3];
1686 
1687     // env.top(0) contains frame specification,
1688     // evaluated as for ActionGotoExpression
1689     as_value framespec = env.pop();
1690 
1691     DisplayObject* tgtch = env.target();
1692     MovieClip* target_sprite = tgtch ? tgtch->to_movie() : nullptr;
1693     if (!target_sprite) {
1694         log_error(_("%s: environment target is null or not a MovieClip"),
1695             __FUNCTION__);
1696         return;
1697     }
1698 
1699     size_t framenum;
1700     if (!target_sprite->get_frame_number(framespec, framenum)) {
1701         IF_VERBOSE_ASCODING_ERRORS(
1702         log_aserror(_("Frame spec found on stack "
1703             "at ActionWaitForFrame doesn't evaluate "
1704                 "to a valid frame: %s"),
1705             framespec);
1706         );
1707         return;
1708     }
1709 
1710     const size_t lastloaded = target_sprite->get_loaded_frames();
1711     if (lastloaded < framenum) {
1712         // better delegate this to ActionExec
1713         thread.skip_actions(skip);
1714     }
1715 
1716 }
1717 
1718 void
pushConstant(ActionExec & thread,unsigned int id)1719 pushConstant(ActionExec& thread, unsigned int id)
1720 {
1721     as_environment& env = thread.env;
1722     /* const action_buffer& code = thread.code; */
1723 
1724     const ConstantPool *pool = getVM(env).getConstantPool();
1725     if ( ! pool ) {
1726         IF_VERBOSE_MALFORMED_SWF(
1727             log_swferror(_("Unknown constant '%1%' (no pool registered with VM)"), id);
1728         );
1729         env.push(as_value());
1730         return;
1731     }
1732 
1733     if ( id >= pool->size() ) {
1734         IF_VERBOSE_MALFORMED_SWF(
1735             log_swferror(_("Unknown constant '%1%' (registered pool has %2% entries)"), id, pool->size());
1736         );
1737         env.push(as_value());
1738         return;
1739     }
1740 
1741     env.push( (*pool)[id] );
1742 }
1743 
1744 void
ActionPushData(ActionExec & thread)1745 ActionPushData(ActionExec& thread)
1746 {
1747     as_environment& env = thread.env;
1748 
1749     enum {
1750         pushString,
1751         pushFloat,
1752         pushNull,
1753         pushUndefined,
1754         pushRegister,
1755         pushBool,
1756         pushDouble,
1757         pushInt32,
1758         pushDict8,
1759         pushDict16
1760     };
1761     const char* pushType[] = {
1762         "string",
1763         "float",
1764         "null",
1765         "undefined",
1766         "register",
1767         "bool",
1768         "double",
1769         "int",
1770         "dict8",
1771         "dict16"
1772     };
1773 
1774     const action_buffer& code = thread.code;
1775 
1776     const size_t pc = thread.getCurrentPC();
1777     const std::uint16_t length = code.read_uint16(pc + 1);
1778 
1779     //---------------
1780     size_t i = pc;
1781     size_t count = 0;
1782     while (i - pc < length) {
1783 
1784         const std::uint8_t type = code[3 + i];
1785         ++i;
1786 
1787         switch (type)
1788         {
1789             default:
1790             {
1791                 IF_VERBOSE_MALFORMED_SWF(
1792                     log_swferror(_("Unknown push type %d. Execution will "
1793                         "continue but it is likely to fail due to lost "
1794                         "sync."), +type);
1795                 );
1796                 continue;
1797             }
1798 
1799             case pushString: // 0
1800             {
1801                 const std::string str(code.read_string(i + 3));
1802                 i += str.size() + 1;
1803                 env.push(str);
1804                 break;
1805             }
1806 
1807             case pushFloat: // 1
1808             {
1809                 const float f = code.read_float_little(i + 3);
1810                 i += 4;
1811                 env.push(f);
1812                 break;
1813             }
1814 
1815             case pushNull: // 2
1816             {
1817                 as_value nullvalue;
1818                 nullvalue.set_null();
1819                 env.push(nullvalue);
1820                 break;
1821             }
1822 
1823             case pushUndefined: // 3
1824                 env.push(as_value());
1825                 break;
1826 
1827             case pushRegister: // 4
1828             {
1829                 const size_t reg = code[3 + i];
1830                 ++i;
1831                 const as_value* v = getVM(env).getRegister(reg);
1832                 if (!v) {
1833                     IF_VERBOSE_MALFORMED_SWF(
1834                         log_swferror(_("Invalid register %d in ActionPush"),
1835                             reg);
1836                     );
1837                     env.push(as_value());
1838                 }
1839                 else env.push(*v);
1840                 break;
1841             }
1842 
1843             case pushBool: // 5
1844             {
1845                 const bool bool_val = code[i + 3];
1846                 ++i;
1847                 env.push(bool_val);
1848                 break;
1849             }
1850 
1851             case pushDouble: // 6
1852             {
1853                 const double d = code.read_double_wacky(i + 3);
1854                 i += 8;
1855                 env.push(d);
1856                 break;
1857             }
1858 
1859             case pushInt32: // 7
1860             {
1861                 const std::int32_t val = code.read_int32(i + 3);
1862                 i += 4;
1863                 env.push(val);
1864                 break;
1865             }
1866 
1867             case pushDict8: // 8
1868             {
1869                 const std::uint8_t id = code[3 + i];
1870                 ++i;
1871                 pushConstant(thread, id);
1872                 break;
1873             }
1874 
1875             case pushDict16: // 9
1876             {
1877                 const std::uint16_t id = code.read_int16(i + 3);
1878                 i += 2;
1879                 pushConstant(thread, id);
1880                 break;
1881             }
1882         }
1883 
1884         IF_VERBOSE_ACTION(
1885             log_action(_("\t%d) type=%s, value=%s"),
1886                 count, pushType[type], env.top(0));
1887             ++count;
1888         );
1889 
1890     }
1891 }
1892 
1893 void
ActionBranchAlways(ActionExec & thread)1894 ActionBranchAlways(ActionExec& thread)
1895 {
1896     std::int16_t offset = thread.code.read_int16(thread.getCurrentPC()+3);
1897     thread.adjustNextPC(offset);
1898     // @@ TODO range checks
1899 }
1900 
1901 void
ActionGetUrl2(ActionExec & thread)1902 ActionGetUrl2(ActionExec& thread)
1903 {
1904     as_environment& env = thread.env;
1905 
1906     const action_buffer& code = thread.code;
1907 
1908 #if GNASH_PARANOIA_LEVEL > 1
1909     assert(thread.atActionTag(SWF::ACTION_GETURL2));
1910 #endif
1911 
1912     const std::uint8_t method = code[thread.getCurrentPC() + 3];
1913 
1914     as_value url_val = env.top(1);
1915     if (url_val.is_undefined()) {
1916         log_error(_("Undefined GetUrl2 URL on stack, skipping"));
1917     }
1918     else {
1919         const std::string& url = url_val.to_string();
1920         commonGetURL(env, env.top(0), url, method);
1921     }
1922 
1923     env.drop(2);
1924 }
1925 
1926 void
ActionBranchIfTrue(ActionExec & thread)1927 ActionBranchIfTrue(ActionExec& thread)
1928 {
1929     as_environment& env = thread.env;
1930     const action_buffer& code = thread.code;
1931     size_t pc = thread.getCurrentPC();
1932     size_t nextPC = thread.getNextPC();
1933     size_t stopPC = thread.getStopPC();
1934 
1935 #if GNASH_PARANOIA_LEVEL > 1
1936     assert(thread.atActionTag(SWF::ACTION_BRANCHIFTRUE));
1937 #endif
1938 
1939     std::int16_t offset = code.read_int16(pc+3);
1940 
1941     const bool test = toBool(env.pop(), getVM(env));
1942     if (test) {
1943         thread.adjustNextPC(offset);
1944 
1945         if (nextPC > stopPC)
1946         {
1947             IF_VERBOSE_MALFORMED_SWF (
1948             log_swferror(_("branch to offset %d  -- "
1949                 " this section only runs to %d"),
1950                     nextPC, stopPC);
1951             )
1952         }
1953     }
1954 }
1955 
1956 void
ActionCallFrame(ActionExec & thread)1957 ActionCallFrame(ActionExec& thread)
1958 {
1959     as_environment& env = thread.env;
1960 
1961     const std::string& target_frame = env.top(0).to_string();
1962     std::string target_path;
1963     std::string frame_var;
1964 
1965     DisplayObject* target = nullptr;
1966     if (parsePath(target_frame, target_path, frame_var)) {
1967         target = findTarget(env, target_path);
1968     }
1969     else {
1970         frame_var = target_frame;
1971         target = env.target();
1972     }
1973 
1974     env.drop(1);
1975 
1976     MovieClip* target_sprite = target ? target->to_movie() : nullptr;
1977     if (target_sprite) {
1978         target_sprite->call_frame_actions(frame_var);
1979     }
1980     else {
1981         IF_VERBOSE_ASCODING_ERRORS (
1982         log_aserror(_("Couldn't find target_sprite \"%s\" in ActionCallFrame!"
1983             " target frame actions will not be called..."), target_path);
1984         )
1985     }
1986 }
1987 
1988 void
ActionGotoExpression(ActionExec & thread)1989 ActionGotoExpression(ActionExec& thread)
1990 {
1991     as_environment& env = thread.env;
1992 
1993     const action_buffer& code = thread.code;
1994     size_t pc = thread.getCurrentPC();
1995 
1996     // From Alexis SWF ref:
1997     //
1998     // Pop a value or a string and jump to the specified
1999     // frame. When a string is specified, it can include a
2000     // path to a sprite as in:
2001     //
2002     //   /Test:55
2003     //
2004     // When f_play is ON, the action is to play as soon as
2005     // that frame is reached. Otherwise, the
2006     // frame is shown in stop mode.
2007     unsigned char play_flag = code[pc + 3];
2008     const MovieClip::PlayState state =
2009         play_flag ? MovieClip::PLAYSTATE_PLAY : MovieClip::PLAYSTATE_STOP;
2010 
2011     std::string target_frame = env.pop().to_string();
2012     std::string target_path;
2013     std::string frame_var;
2014 
2015     DisplayObject* target = nullptr;
2016     if (parsePath(target_frame, target_path, frame_var)) {
2017         target = findTarget(env, target_path);
2018     }
2019 
2020     // 4.11 would make parsePath above return true,
2021     // we should check if a sprite named '4' is supposed to work
2022     // in that case
2023     if (!target) {
2024         target = env.target();
2025         frame_var = target_frame;
2026     }
2027 
2028     MovieClip *target_sprite = target ? target->to_movie() : nullptr;
2029     if (target_sprite) {
2030         size_t frame_number;
2031         if (!target_sprite->get_frame_number(frame_var, frame_number)) {
2032             IF_VERBOSE_ASCODING_ERRORS(
2033             log_aserror(_("Frame spec found on stack "
2034                 "at ActionGotoExpression doesn't evaluate "
2035                 "to a valid frame: %s"),
2036                 target_frame);
2037             );
2038             return;
2039         }
2040         target_sprite->goto_frame(frame_number);
2041         target_sprite->setPlayState(state);
2042     }
2043     else {
2044         IF_VERBOSE_ASCODING_ERRORS (
2045         log_aserror(_("Couldn't find target sprite \"%s\" in "
2046                 "ActionGotoExpression. Will not go to target frame..."),
2047                 target_frame);
2048         )
2049     }
2050 }
2051 
2052 
2053 void
ActionDelete(ActionExec & thread)2054 ActionDelete(ActionExec& thread)
2055 {
2056     as_environment& env = thread.env;
2057 
2058 #if GNASH_PARANOIA_LEVEL > 1
2059     assert(thread.atActionTag(SWF::ACTION_DELETE)); // 0x3A
2060 #endif
2061 
2062     // Stack                    Result
2063     // 'a'      |               nothing
2064     // 'a.b'    |               a.b deleted.
2065     // 'b'      | a [obj]       a.b deleted (normal use).
2066     // 'a.b'    | a [obj]       nothing.
2067     // 'a.b'    | 'string'      nothing.
2068     // 'c'      | a.b [obj]     a.b.c deleted (normal use).
2069     // 'a.b.c'  |               a.b.c deleted
2070     // 'b.c'    | a [obj]       nothing
2071     // 'a.b.c'  | string        nothing
2072 
2073     const size_t stackSize = env.stack_size();
2074     const int version = getSWFVersion(env);
2075 
2076     std::string propertyname = env.top(0).to_string();
2077 
2078     as_object* obj(nullptr);
2079 
2080     // Behaviour is different according to version. For SWF7 and above,
2081     // the delete fails if there aren't two items on the stack. For SWF6
2082     // and below, a single item should be parsed to see if it's a path,
2083     // then we try to delete it. If it's not a path, we try to delete it as
2084     // a variable.
2085     //
2086     // In both cases, if there are two or more items on the stack, they
2087     // have to be property and object.
2088     if (stackSize < 2) {
2089         if (version > 6) {
2090             env.top(1).set_bool(false);
2091             env.drop(1);
2092             return;
2093         }
2094 
2095         std::string path, var;
2096         if (!parsePath(propertyname, path, var)) {
2097             // It's not a path. For SWF 7 and above, don't delete. Otherwise
2098             // assume it's a variable and try to delete.
2099             env.top(1).set_bool(thread.delVariable(propertyname));
2100             env.drop(1);
2101             return;
2102         }
2103         else {
2104             as_value target = thread.getVariable(path);
2105 
2106             // Don't create an object! Only get the value if it is an object
2107             // already.
2108             if (target.is_object()) {
2109                 obj = safeToObject(getVM(thread.env), target);
2110                 propertyname = var;
2111             }
2112         }
2113     }
2114     else {
2115         // Don't create an object! Only get the value if it is an object
2116         // already.
2117         if (env.top(1).is_object()) {
2118             obj = safeToObject(getVM(thread.env), env.top(1));
2119         }
2120     }
2121 
2122     if (!obj) {
2123         IF_VERBOSE_ASCODING_ERRORS(
2124             log_aserror(_("delete %s.%s: no object found to delete"),
2125                         env.top(1), env.top(0));
2126         );
2127         env.top(1).set_bool(false);
2128         env.drop(1);
2129         return;
2130     }
2131 
2132     VM& vm = getVM(env);
2133     const std::pair<bool, bool> ret = obj->delProperty(getURI(vm, propertyname));
2134 
2135     env.top(1).set_bool(ret.second);
2136 
2137     env.drop(1);
2138 }
2139 
2140 void
ActionDelete2(ActionExec & thread)2141 ActionDelete2(ActionExec& thread)
2142 {
2143     as_environment& env = thread.env;
2144 
2145 #if GNASH_PARANOIA_LEVEL > 1
2146     assert(thread.atActionTag(SWF::ACTION_DELETE2)); // 0x3B
2147 #endif
2148 
2149     const std::string& propertyname = env.top(0).to_string();
2150 
2151     // If it's not a path, delete it as a variable.
2152     std::string path, var;
2153     if (!parsePath(propertyname, path, var)) {
2154         // See bug #18482, this works fine now (assuming the bug
2155         // report is correct)
2156         env.top(0) = thread.delVariable(propertyname);
2157         return;
2158     }
2159 
2160     // Otherwise see if it's an object and delete it.
2161     as_value target = thread.getVariable(path);
2162     if (!target.is_object()) {
2163         IF_VERBOSE_ASCODING_ERRORS(
2164             log_aserror(_("delete2 called with a path that does not resolve "
2165                     "to an object"), env.top(1), env.top(0));
2166         );
2167         env.top(1).set_bool(false);
2168         env.drop(1);
2169         return;
2170     }
2171 
2172     as_object* obj = safeToObject(getVM(thread.env), target);
2173 
2174     VM& vm = getVM(env);
2175     const std::pair<bool, bool> ret = obj->delProperty(getURI(vm, var));
2176 
2177     env.top(1).set_bool(ret.second);
2178 }
2179 
2180 void
ActionVarEquals(ActionExec & thread)2181 ActionVarEquals(ActionExec& thread)
2182 {
2183     as_environment& env = thread.env;
2184 
2185     as_value& value = env.top(0);
2186     as_value& varname = env.top(1);
2187     thread.setLocalVariable(varname.to_string(), value);
2188 
2189     IF_VERBOSE_ACTION(
2190         log_action(_("-- set local var: %s = %s"), varname.to_string(), value);
2191     );
2192 
2193     env.drop(2);
2194 }
2195 
2196 void
ActionCallFunction(ActionExec & thread)2197 ActionCallFunction(ActionExec& thread)
2198 {
2199     as_environment& env = thread.env;
2200 
2201     // Let's consider it a as a string and lookup the function.
2202     //
2203     // Note: this produces the correct behaviour: Any as_value, including
2204     // null or numbers, are converted to a string and the corresponding
2205     // function is called. If it is undefined, nothing happens, even if
2206     // there is a function called 'undefined'.
2207     //
2208     // In all cases, even undefined, the specified number of arguments
2209     // is dropped from the stack.
2210     const std::string& funcname = env.pop().to_string();
2211 
2212     as_object* super(nullptr);
2213 
2214     as_object* this_ptr;
2215     as_value function = thread.getVariable(funcname, &this_ptr);
2216 
2217     if (!function.is_object()) {
2218         // In this case the call to invoke() will fail. We won't return
2219         // because we still need to handle the stack, and the attempt to
2220         // convert the value to an object may have effects in AS (haven't
2221         // checked).
2222         IF_VERBOSE_ASCODING_ERRORS (
2223             log_aserror(_("ActionCallFunction: %s is not an object"),
2224                 funcname);
2225         )
2226     }
2227     else if (!function.is_function()) {
2228         as_object* obj = toObject(function, getVM(thread.env));
2229         super = obj->get_super();
2230         this_ptr = thread.getThisPointer();
2231     }
2232 
2233     // Get number of args, modifying it if not enough values are on the stack.
2234     // TODO: this may cause undefined behaviour if the number on the stack
2235     // is too large. Fix it.
2236     size_t nargs = toNumber(env.pop(), getVM(env));
2237     const size_t available_args = env.stack_size();
2238     if (available_args < nargs) {
2239         IF_VERBOSE_MALFORMED_SWF(
2240             log_swferror(_("Attempt to call a function with %u arguments "
2241                 "while only %u are available on the stack."),
2242                 nargs, available_args);
2243         );
2244         nargs = available_args;
2245     }
2246 
2247     fn_call::Args args;
2248     for (size_t i = 0; i < nargs; ++i) {
2249         args += env.pop();
2250     }
2251 
2252     as_value result = invoke(function, env, this_ptr,
2253                   args, super, &(thread.code.getMovieDefinition()));
2254 
2255     env.push(result);
2256 
2257     // If the function threw an exception, do so here.
2258     if (result.is_exception()) {
2259         thread.skipRemainingBuffer();
2260     }
2261 
2262 }
2263 
2264 void
ActionReturn(ActionExec & thread)2265 ActionReturn(ActionExec& thread)
2266 {
2267     as_environment& env = thread.env;
2268 
2269     // Put top of stack in the provided return slot, if
2270     // it's not NULL.
2271     thread.pushReturn(env.top(0));
2272     env.drop(1);
2273 
2274     // Skip the rest of this buffer (return from this action_buffer).
2275     thread.skipRemainingBuffer();
2276 
2277 }
2278 
2279 void
ActionModulo(ActionExec & thread)2280 ActionModulo(ActionExec& thread)
2281 {
2282     as_environment& env = thread.env;
2283 
2284     const double y = toNumber(env.pop(), getVM(env));
2285     const double x = toNumber(env.pop(), getVM(env));
2286     // Don't need to check for y being 0 here - if it's zero,
2287     // fmod returns NaN, which is what flash would do too
2288     as_value result = std::fmod(x, y);
2289 
2290     env.push(result);
2291 }
2292 
2293 void
ActionNew(ActionExec & thread)2294 ActionNew(ActionExec& thread)
2295 {
2296     as_environment& env = thread.env;
2297 
2298     as_value val = env.pop();
2299     const std::string& classname = val.to_string();
2300 
2301     IF_VERBOSE_ACTION (
2302         log_action(_("---new object: %s"),
2303             classname);
2304     );
2305 
2306     unsigned nargs = toNumber(env.pop(), getVM(env));
2307 
2308     as_value constructorval = thread.getVariable(classname);
2309     as_function* constructor = constructorval.to_function();
2310     if (!constructor) {
2311         IF_VERBOSE_ASCODING_ERRORS(
2312             log_aserror(_("ActionNew: "
2313                 "'%s' is not a constructor"), classname);
2314         );
2315         env.drop(nargs);
2316         env.push(as_value()); // should we push an object anyway ?
2317         return;
2318     }
2319 
2320     // It is possible for constructors to fail, for instance if a
2321     // conversion to object calls a built-in constructor that has been
2322     // deleted. BitmapData also fails to construct anything under
2323     // some circumstances.
2324     try {
2325         as_object* newobj = construct_object(constructor, env, nargs);
2326         env.push(newobj);
2327         return;
2328     }
2329     catch (const GnashException& ) {
2330         env.push(as_value());
2331         return;
2332     }
2333 
2334 }
2335 
2336 void
ActionVar(ActionExec & thread)2337 ActionVar(ActionExec& thread)
2338 {
2339     as_environment& env = thread.env;
2340 
2341     const std::string& varname = env.top(0).to_string();
2342     const ObjectURI& name = getURI(getVM(env), varname);
2343     VM& vm = getVM(env);
2344 
2345     if (vm.calling()) {
2346         declareLocal(vm.currentCall(), name);
2347     }
2348     else {
2349         // See https://savannah.gnu.org/patch/?8721
2350         as_object* this_ptr = thread.getThisPointer();
2351         if (!hasOwnProperty(*this_ptr, name)) {
2352             this_ptr->set_member(name, as_value());
2353         }
2354     }
2355     env.drop(1);
2356 }
2357 
2358 void
ActionInitArray(ActionExec & thread)2359 ActionInitArray(ActionExec& thread)
2360 {
2361     as_environment& env = thread.env;
2362 
2363     const int array_size = toInt(env.pop(), getVM(env));
2364     assert(array_size >= 0); // TODO: trigger this !!
2365 
2366     Global_as& gl = getGlobal(env);
2367 
2368     as_object* ao = gl.createArray();
2369 
2370     VM& vm = getVM(env);
2371     // Fill the elements with the initial values from the stack.
2372     for (int i = 0; i < array_size; i++) {
2373         const ObjectURI& k =
2374             getURI(vm, std::to_string(i));
2375         ao->set_member(k, env.pop());
2376     }
2377 
2378     env.push(ao);
2379 
2380 }
2381 
2382 void
ActionInitObject(ActionExec & thread)2383 ActionInitObject(ActionExec& thread)
2384 {
2385     as_environment& env = thread.env;
2386 
2387     //
2388     //    SWFACTION_PUSH
2389     //     [000]   Constant: 1 "obj"
2390     //     [001]   Constant: 0 "member" <-- we handle up to here
2391     //     [002]   Integer: 1
2392     //     [003]   Integer: 1
2393     //    SWFACTION_INITOBJECT
2394 
2395     const int nmembers = toInt(env.pop(), getVM(env));
2396 
2397     // TODO: see if this could call the ASnative function(101, 9).
2398     Global_as& gl = getGlobal(env);
2399     as_object* obj = createObject(gl);
2400 
2401     obj->init_member(NSV::PROP_CONSTRUCTOR, getMember(gl, NSV::CLASS_OBJECT));
2402 
2403     VM& vm = getVM(env);
2404 
2405     // Set provided members
2406     for (int i = 0; i < nmembers; ++i) {
2407         const as_value& member_value = env.top(0);
2408         std::string member_name = env.top(1).to_string();
2409         obj->set_member(getURI(vm, member_name), member_value);
2410         env.drop(2);
2411     }
2412 
2413     as_value new_obj;
2414     new_obj.set_as_object(obj);
2415 
2416     env.push(new_obj);
2417 
2418 }
2419 
2420 void
ActionTypeOf(ActionExec & thread)2421 ActionTypeOf(ActionExec& thread)
2422 {
2423     as_environment& env = thread.env;
2424     env.top(0).set_string(env.top(0).typeOf());
2425 }
2426 
2427 void
ActionTargetPath(ActionExec & thread)2428 ActionTargetPath(ActionExec& thread)
2429 {
2430     as_environment& env = thread.env;
2431 
2432     DisplayObject* sp = env.top(0).toDisplayObject();
2433     if (sp) {
2434         env.top(0).set_string(sp->getTarget());
2435         return;
2436     }
2437 
2438     IF_VERBOSE_ASCODING_ERRORS(
2439         log_aserror(_("Argument to TargetPath(%s) doesn't cast "
2440                 "to a DisplayObject"), env.top(0));
2441     );
2442     env.top(0).set_undefined();
2443 }
2444 
2445 // Push a each object's member value on the stack
2446 // This is an utility function for use by ActionEnumerate
2447 // and ActionEnum2. The caller is expected to have
2448 // already set the top-of-stack to undefined (as an optimization)
2449 static void
enumerateObject(as_environment & env,const as_object & obj)2450 enumerateObject(as_environment& env, const as_object& obj)
2451 {
2452     assert(env.top(0).is_undefined());
2453     Enumerator en(env);
2454     obj.visitKeys(en);
2455 }
2456 
2457 void
ActionEnumerate(ActionExec & thread)2458 ActionEnumerate(ActionExec& thread)
2459 {
2460     as_environment& env = thread.env;
2461 
2462     // Get the object
2463     as_value var_name = env.top(0);
2464     std::string var_string = var_name.to_string();
2465 
2466     as_value variable = thread.getVariable(var_string);
2467 
2468     env.top(0).set_undefined();
2469 
2470     const as_object* obj = safeToObject(getVM(thread.env), variable);
2471     if ( !obj || !variable.is_object() )
2472     {
2473         IF_VERBOSE_ASCODING_ERRORS(
2474         log_aserror(_("Top of stack doesn't evaluate to an object (%s) at "
2475             "ActionEnumerate execution"),
2476             var_name);
2477         );
2478         return;
2479     }
2480 
2481     enumerateObject(env, *obj);
2482 }
2483 
2484 void
ActionNewAdd(ActionExec & thread)2485 ActionNewAdd(ActionExec& thread)
2486 {
2487     as_environment& env = thread.env;
2488 
2489     const as_value& op2 = env.pop();
2490     as_value op1 = env.pop();
2491     newAdd(op1, op2, getVM(env));
2492 
2493     env.push(op1);
2494 }
2495 
2496 void
ActionNewLessThan(ActionExec & thread)2497 ActionNewLessThan(ActionExec& thread)
2498 {
2499     as_environment& env = thread.env;
2500     env.top(1) = newLessThan(env.top(1), env.top(0), getVM(env));
2501     env.drop(1);
2502 }
2503 
2504 void
ActionNewEquals(ActionExec & thread)2505 ActionNewEquals(ActionExec& thread)
2506 {
2507     as_environment& env = thread.env;
2508 
2509 #if GNASH_PARANOIA_LEVEL > 1
2510     assert(thread.atActionTag(SWF::ACTION_NEWEQUALS));
2511 #endif
2512 
2513     VM& vm = getVM(env);
2514 
2515     int swfVersion = vm.getSWFVersion();
2516     if (swfVersion <= 5)
2517     {
2518         as_value op1 = env.top(0);
2519         try { convertToPrimitive(op1, vm); }
2520         catch (const ActionTypeError& e) {
2521             log_debug("to_primitive(%s) threw an ActionTypeError %s",
2522                     op1, e.what());
2523         }
2524 
2525         as_value op2 = env.top(1);
2526         try { convertToPrimitive(op2, vm); }
2527         catch (const ActionTypeError& e) {
2528             log_debug("to_primitive(%s) threw an ActionTypeError %s",
2529                     op2, e.what());
2530         }
2531 
2532         env.top(1).set_bool(equals(op1, op2, getVM(env)));
2533     }
2534     else
2535     {
2536         /// ECMA-262 abstract equality comparison (sect 11.9.3)
2537         env.top(1).set_bool(equals(env.top(1), env.top(0), getVM(env)));
2538     }
2539     env.drop(1);
2540 }
2541 
2542 void
ActionToNumber(ActionExec & thread)2543 ActionToNumber(ActionExec& thread)
2544 {
2545     as_environment& env = thread.env;
2546     convertToNumber(env.top(0), getVM(env));
2547 }
2548 
2549 void
ActionToString(ActionExec & thread)2550 ActionToString(ActionExec& thread)
2551 {
2552     as_environment& env = thread.env;
2553     convertToString(env.top(0), getVM(env));
2554 }
2555 
2556 void
ActionDup(ActionExec & thread)2557 ActionDup(ActionExec& thread)
2558 {
2559     as_environment& env = thread.env;
2560     env.push(env.top(0));
2561 }
2562 
2563 void
ActionSwap(ActionExec & thread)2564 ActionSwap(ActionExec& thread)
2565 {
2566     as_environment& env = thread.env;
2567     std::swap(env.top(1), env.top(0));
2568 }
2569 
2570 void
ActionGetMember(ActionExec & thread)2571 ActionGetMember(ActionExec& thread)
2572 {
2573     as_environment& env = thread.env;
2574 
2575     as_value member_name = env.top(0);
2576     as_value target = env.top(1);
2577 
2578     as_object* obj = safeToObject(getVM(thread.env), target);
2579     if (!obj) {
2580         IF_VERBOSE_ASCODING_ERRORS(
2581             log_aserror(_("getMember called against a value that does not "
2582                     "cast to an as_object: %s"), target)
2583         );
2584         env.top(1).set_undefined();
2585         env.drop(1);
2586         return;
2587     }
2588 
2589     IF_VERBOSE_ACTION(
2590         log_action(_(" ActionGetMember: target: %s (object %p)"),
2591                    target, static_cast<void*>(obj));
2592     );
2593 
2594     const ObjectURI& k = getURI(getVM(env), member_name.to_string());
2595 
2596     if (!obj->get_member(k, &env.top(1))) {
2597         IF_VERBOSE_ASCODING_ERRORS(
2598             log_aserror("Reference to undefined member %s of object %s",
2599                 member_name, target);
2600         );
2601         env.top(1).set_undefined();
2602     }
2603 
2604     IF_VERBOSE_ACTION (
2605         log_action(_("-- get_member %s.%s=%s"),
2606            target, member_name, env.top(1));
2607     );
2608 
2609     env.drop(1);
2610 }
2611 
2612 void
ActionSetMember(ActionExec & thread)2613 ActionSetMember(ActionExec& thread)
2614 {
2615     as_environment& env = thread.env;
2616 
2617     as_object* obj = safeToObject(getVM(thread.env), env.top(2));
2618     const std::string& member_name = env.top(1).to_string();
2619     const as_value& member_value = env.top(0);
2620 
2621     if (member_name.empty()) {
2622         IF_VERBOSE_ASCODING_ERRORS (
2623             // Invalid object, can't set.
2624             log_aserror(_("ActionSetMember: %s.%s=%s: member name "
2625                     "evaluates to invalid (empty) string"),
2626                     env.top(2), env.top(1), env.top(0));
2627         );
2628     }
2629     else if (obj) {
2630         obj->set_member(getURI(getVM(env), member_name), member_value);
2631 
2632         IF_VERBOSE_ACTION (
2633             log_action(_("-- set_member %s.%s=%s"),
2634                 env.top(2),
2635                 member_name,
2636                 member_value);
2637         );
2638     }
2639     else {
2640         // Malformed SWF ? (don't think this is possible to do with
2641         // ActionScript syntax)
2642         // FIXME, should this be log_swferror?
2643         IF_VERBOSE_ASCODING_ERRORS(
2644             // Invalid object, can't set.
2645             log_aserror(_("-- set_member %s.%s=%s on invalid object!"),
2646                 env.top(2), member_name, member_value);
2647         );
2648     }
2649 
2650     env.drop(3);
2651 }
2652 
2653 void
ActionIncrement(ActionExec & thread)2654 ActionIncrement(ActionExec& thread)
2655 {
2656     as_environment& env = thread.env;
2657     env.top(0).set_double(toNumber(env.top(0), getVM(env)) + 1);
2658 }
2659 
2660 void
ActionDecrement(ActionExec & thread)2661 ActionDecrement(ActionExec& thread)
2662 {
2663     as_environment& env = thread.env;
2664     env.top(0).set_double(toNumber(env.top(0), getVM(env)) - 1);
2665 }
2666 
2667 
2668 /// Call a method of an object
2669 //
2670 /// Stack: method, object, argc, arg0 ... argn
2671 //
2672 /// The standard use of this opcode is:
2673 /// 1. First stack value converted to a string (method name).
2674 /// 2. Second stack value converted to an object (this pointer).
2675 /// 3. Arg count and arguments parsed.
2676 /// 4. The method name must be a property of the object and is called with
2677 ///    the object as its 'this'.
2678 //
2679 /// But it can also be used in a different way under some circumstances.
2680 //
2681 /// 1. If the method name is defined and not empty, the object value must
2682 ///    be an object and the method name must be a property of the object
2683 ///    (may be inherited). Otherwise the call fails and returns undefined.
2684 /// 2. If the method name is undefined or empty, the second stack value is
2685 ///    called, and call's 'this' pointer is undefined.
2686 //
2687 /// In both usages the arguments are passed.
2688 void
ActionCallMethod(ActionExec & thread)2689 ActionCallMethod(ActionExec& thread)
2690 {
2691     as_environment& env = thread.env;
2692 
2693     // Get name function of the method
2694     as_value method_name = env.pop();
2695 
2696     std::string method_string = method_name.to_string();
2697 
2698     // Get an object
2699     as_value obj_value = env.pop();
2700 
2701     // Get number of args, modifying it if not enough values are on the stack.
2702     size_t nargs = toNumber(env.pop(), getVM(env));
2703     const size_t available_args = env.stack_size();
2704     if (available_args < nargs) {
2705         IF_VERBOSE_MALFORMED_SWF(
2706             log_swferror(_("Attempt to call a method with %u arguments "
2707                 "while only %u are available on the stack."),
2708                 nargs, available_args);
2709         );
2710         nargs = available_args;
2711     }
2712 
2713     IF_VERBOSE_ACTION (
2714         log_action(_(" method name: %s"), method_name);
2715         log_action(_(" method object/func: %s"), obj_value);
2716         log_action(_(" method nargs: %d"), nargs);
2717     );
2718 
2719     as_object* obj = safeToObject(getVM(thread.env), obj_value);
2720     if (!obj) {
2721         // If this value is not an object, it can neither have any members
2722         // nor be called as a function, so neither opcode usage is possible.
2723         IF_VERBOSE_ASCODING_ERRORS(
2724             log_aserror(_("ActionCallMethod invoked with "
2725                 "non-object object/func (%s)"), obj_value);
2726         );
2727         env.drop(nargs);
2728         env.push(as_value());
2729         return;
2730     }
2731 
2732     const bool noMeth = (method_name.is_undefined() || method_string.empty());
2733 
2734     as_object* method_obj; // The method to call, as an object
2735 
2736     // The object to be the 'this' pointer during the call.
2737     as_object* this_ptr(nullptr);
2738 
2739     // Will be used to find super later
2740     ObjectURI methURI;
2741 
2742     // If the method name is undefined or evaluates to an empty string,
2743     // the first argument is used as the method name and the 'this' pointer
2744     // is undefined. We can signify this by leaving the 'this' pointer as
2745     // null.a
2746     if (noMeth) {
2747         method_obj = obj;
2748     }
2749     else {
2750 
2751         methURI = getURI(getVM(env), method_string);
2752 
2753         // The method value
2754         as_value method_value;
2755 
2756         if (!obj->get_member(methURI, &method_value) ) {
2757             IF_VERBOSE_ASCODING_ERRORS(
2758             log_aserror(_("ActionCallMethod: "
2759                 "Can't find method %s of object %s"),
2760                 method_name, obj_value);
2761             );
2762             env.drop(nargs);
2763             env.push(as_value());
2764             return;
2765         }
2766 
2767         method_obj = safeToObject(getVM(thread.env), method_value);
2768         if ( ! method_obj ) {
2769             IF_VERBOSE_ASCODING_ERRORS(
2770             log_aserror(_("ActionCallMethod: "
2771                     "property %d of object %d is not callable (%s)"),
2772                     method_name, obj_value, method_value);
2773             );
2774             env.drop(nargs);
2775             env.push(as_value());
2776             return;
2777         }
2778         this_ptr = obj;
2779     }
2780 
2781     assert(method_obj); // or we would should have returned already by now
2782 
2783     // If we are calling a method of a super object, the 'this' pointer
2784     // for the call is always the this pointer of the function that called
2785     // super().
2786     if (obj->isSuper()) {
2787         if (thread.isFunction()) this_ptr = thread.getThisPointer();
2788     }
2789 
2790     fn_call::Args args;
2791     for (size_t i = 0; i < nargs; ++i) {
2792         args += env.pop();
2793     }
2794 
2795     as_object* super;
2796     as_function* func = method_obj->to_function();
2797     if (func && func->isBuiltin()) {
2798         // Do not construct super if method is a builtin
2799         // TODO: check if this is correct!!
2800         super = nullptr;
2801     }
2802     else {
2803         super = obj->get_super(methURI);
2804     }
2805 
2806     fn_call call(this_ptr, env, args);
2807     call.super = super;
2808     call.callerDef = &(thread.code.getMovieDefinition());
2809     as_value result;
2810     try {
2811         result = method_obj->call(call);
2812     }
2813     catch (const ActionTypeError& e) {
2814         IF_VERBOSE_ASCODING_ERRORS(
2815             log_aserror("ActionCallMethod: %s", e.what());
2816         );
2817     }
2818 
2819     env.push(result);
2820 
2821     // Now, if there was an exception, proceed to the end of the block.
2822     if (result.is_exception()) thread.skipRemainingBuffer();
2823 
2824 }
2825 
2826 void
ActionNewMethod(ActionExec & thread)2827 ActionNewMethod(ActionExec& thread)
2828 {
2829     as_environment& env = thread.env;
2830 
2831 #if GNASH_PARANOIA_LEVEL > 1
2832     assert(thread.atActionTag(SWF::ACTION_NEWMETHOD));
2833 #endif
2834 
2835     const as_value method_name = env.pop();
2836     const as_value obj_val = env.pop();
2837 
2838     // Get number of args, modifying it if not enough values are on the stack.
2839     unsigned nargs = toNumber(env.pop(), getVM(env));
2840     const size_t available_args = env.stack_size(); // previous 3 entries popped
2841     if (available_args < nargs) {
2842         IF_VERBOSE_MALFORMED_SWF(
2843         log_swferror(_("Attempt to call a constructor with %u arguments "
2844             "while only %u are available on the stack."),
2845             nargs, available_args);
2846         );
2847         nargs = available_args;
2848     }
2849 
2850     as_object* obj = safeToObject(getVM(thread.env), obj_val);
2851     if (!obj) {
2852         // SWF integrity check
2853         // FIXME, should this be log_swferror?  Or log_aserror?
2854         IF_VERBOSE_MALFORMED_SWF(
2855             log_swferror(_("On ActionNewMethod: "
2856                 "no object found on stack on ActionMethod"));
2857         );
2858         env.drop(nargs);
2859         env.push(as_value());
2860         return;
2861     }
2862 
2863     const std::string& method_string = method_name.to_string();
2864     as_value method_val;
2865     if (method_name.is_undefined() || method_string.empty()) {
2866         method_val = obj_val;
2867     }
2868     else {
2869         const ObjectURI& k = getURI(getVM(env), method_string);
2870         if (!obj->get_member(k, &method_val)) {
2871             IF_VERBOSE_ASCODING_ERRORS(
2872                 log_aserror(_("ActionNewMethod: can't find method %s of "
2873                         "object %s"), method_string, obj_val);
2874             );
2875             env.drop(nargs);
2876             env.push(as_value());
2877             return;
2878         }
2879     }
2880 
2881     as_function* method = method_val.to_function();
2882     if (!method) {
2883         IF_VERBOSE_MALFORMED_SWF(
2884             log_swferror(_("ActionNewMethod: method name is undefined "
2885                 "and object is not a function"));
2886         );
2887         env.drop(nargs);
2888         env.push(as_value());
2889         return;
2890     }
2891 
2892     // Construct the object
2893     // It is possible for constructors to fail, for instance if a
2894     // conversion to object calls a built-in constructor that has been
2895     // deleted. BitmapData also fails to construct anything under
2896     // some circumstances.
2897     try {
2898         as_object* newobj = construct_object(method, env, nargs);
2899         env.push(newobj);
2900         return;
2901     }
2902     catch (const GnashException&) {
2903         env.push(as_value());
2904         return;
2905     }
2906 }
2907 
2908 void
ActionInstanceOf(ActionExec & thread)2909 ActionInstanceOf(ActionExec& thread)
2910 {
2911     as_environment& env = thread.env;
2912 
2913     // Get the "super" function
2914     as_object* super = safeToObject(getVM(thread.env), env.top(0));
2915 
2916     // Get the "instance" (but avoid implicit conversion of primitive values!)
2917     as_object* instance = env.top(1).is_object() ?
2918         safeToObject(getVM(thread.env), env.top(1)) : nullptr;
2919 
2920     // Invalid args!
2921     if (!super || ! instance) {
2922         IF_VERBOSE_ASCODING_ERRORS(
2923         log_aserror(_("-- %s instanceof %s (invalid args?)"),
2924                 env.top(1), env.top(0));
2925         );
2926 
2927         env.drop(1);
2928         env.top(0) = as_value(false);
2929         return;
2930     }
2931 
2932     env.drop(1);
2933     env.top(0) = as_value(instance->instanceOf(super));
2934 }
2935 
2936 void
ActionEnum2(ActionExec & thread)2937 ActionEnum2(ActionExec& thread)
2938 {
2939     as_environment& env = thread.env;
2940 
2941     // Get the object.
2942     // Copy it so we can override env.top(0)
2943     as_value obj_val = env.top(0);
2944 
2945     // End of the enumeration. Won't override the object
2946     // as we copied that as_value.
2947     env.top(0).set_undefined();
2948 
2949     as_object* obj = safeToObject(getVM(thread.env), obj_val);
2950     if (!obj || !obj_val.is_object()) {
2951         IF_VERBOSE_ASCODING_ERRORS(
2952         log_aserror(_("Top of stack not an object %s at ActionEnum2"
2953             " execution"), obj_val);
2954         );
2955         return;
2956     }
2957 
2958     enumerateObject(env, *obj);
2959 }
2960 
2961 void
ActionBitwiseAnd(ActionExec & thread)2962 ActionBitwiseAnd(ActionExec& thread)
2963 {
2964     as_environment& env = thread.env;
2965 
2966     const int operand1 = toInt(env.top(1), getVM(env));
2967     const int operand2 = toInt(env.top(0), getVM(env));
2968 
2969     env.top(1) = operand1 & operand2;
2970     env.drop(1);
2971 }
2972 
2973 void
ActionBitwiseOr(ActionExec & thread)2974 ActionBitwiseOr(ActionExec& thread)
2975 {
2976     as_environment& env = thread.env;
2977 
2978     const int operand1 = toInt(env.top(1), getVM(env));
2979     const int operand2 = toInt(env.top(0), getVM(env));
2980 
2981     env.top(1) = operand1|operand2;
2982     env.drop(1);
2983 }
2984 
2985 void
ActionBitwiseXor(ActionExec & thread)2986 ActionBitwiseXor(ActionExec& thread)
2987 {
2988     as_environment& env = thread.env;
2989 
2990     const int operand1 = toInt(env.top(1), getVM(env));
2991     const int operand2 = toInt(env.top(0), getVM(env));
2992 
2993     env.top(1) = operand1^operand2;
2994     env.drop(1);
2995 }
2996 
2997 inline std::uint32_t
saneShiftParam(std::int32_t value)2998 saneShiftParam(std::int32_t value)
2999 {
3000     // NOTE: ISO-IEC 14882:2003 5.8.1: "The behavior is undefined if the right
3001     // operand is negative, or greater than or equal to the length in bits of
3002     // the promoted left operand."
3003     std::uint32_t rv = value;
3004     return rv % 32;
3005 }
3006 
3007 void
ActionShiftLeft(ActionExec & thread)3008 ActionShiftLeft(ActionExec& thread)
3009 {
3010     as_environment& env = thread.env;
3011 
3012     std::uint32_t amount = saneShiftParam(toInt(env.top(0), getVM(env)));
3013     std::int32_t value = toInt(env.top(1), getVM(env));
3014 
3015     value = static_cast<std::uint32_t>(value) << amount;
3016 
3017     env.top(1) = value;
3018     env.drop(1);
3019 }
3020 
3021 void
ActionShiftRight(ActionExec & thread)3022 ActionShiftRight(ActionExec& thread)
3023 {
3024     as_environment& env = thread.env;
3025 
3026     std::uint32_t amount = saneShiftParam(toInt(env.top(0), getVM(env)));
3027     std::int32_t value = toInt(env.top(1), getVM(env));
3028 
3029     value = value >> amount;
3030 
3031     env.top(1) = value;
3032     env.drop(1);
3033 }
3034 
3035 void
ActionShiftRight2(ActionExec & thread)3036 ActionShiftRight2(ActionExec& thread)
3037 {
3038     as_environment& env = thread.env;
3039 
3040     std::uint32_t amount = saneShiftParam(toInt(env.top(0), getVM(env)));
3041     std::int32_t value = toInt(env.top(1), getVM(env));
3042 
3043     value = static_cast<std::uint32_t>(value) >> amount;
3044 
3045     env.top(1) = value;
3046     env.drop(1);
3047 }
3048 
3049 void
ActionStrictEq(ActionExec & thread)3050 ActionStrictEq(ActionExec& thread)
3051 {
3052     as_environment& env = thread.env;
3053 
3054     env.top(1).set_bool(env.top(1).strictly_equals(env.top(0)));
3055     env.drop(1);
3056 }
3057 
3058 void
ActionGreater(ActionExec & thread)3059 ActionGreater(ActionExec& thread)
3060 {
3061     // Just swap the operator and invoke ActionNewLessThan
3062     as_environment& env = thread.env;
3063     std::swap(env.top(1), env.top(0));
3064     ActionNewLessThan(thread);
3065 }
3066 
3067 void
ActionStringGreater(ActionExec & thread)3068 ActionStringGreater(ActionExec& thread)
3069 {
3070     as_environment& env = thread.env;
3071 
3072     const std::string& op1 = env.top(0).to_string();
3073     const std::string& op2 = env.top(1).to_string();
3074     env.top(1).set_bool(op2 > op1);
3075     env.drop(1);
3076 }
3077 
3078 void
ActionExtends(ActionExec & thread)3079 ActionExtends(ActionExec& thread)
3080 {
3081     as_environment& env = thread.env;
3082 
3083     as_object* super = toObject(env.top(0), getVM(thread.env));
3084     as_function* sub = env.top(1).to_function();
3085 
3086     if (!super ||!sub) {
3087         IF_VERBOSE_ASCODING_ERRORS(
3088             if (!super) {
3089                 log_aserror(_("ActionExtends: Super is not an object (%s)"),
3090                     env.top(0));
3091             }
3092             if (!sub) {
3093                 log_aserror(_("ActionExtends: Sub is not a function (%s)"),
3094                     env.top(1));
3095             }
3096         );
3097         env.drop(2);
3098         return;
3099     }
3100     env.drop(2);
3101 
3102     as_object* newproto = new as_object(getGlobal(thread.env));
3103     as_object* p =
3104         toObject(getMember(*super, NSV::PROP_PROTOTYPE), getVM(thread.env));
3105     newproto->set_prototype(p);
3106 
3107     if (getSWFVersion(*super) > 5) {
3108         const int flags = PropFlags::dontEnum;
3109         newproto->init_member(NSV::PROP_uuCONSTRUCTORuu, super, flags);
3110     }
3111 
3112     sub->init_member(NSV::PROP_PROTOTYPE, as_value(newproto));
3113 
3114 }
3115 
3116 void
ActionConstantPool(ActionExec & thread)3117 ActionConstantPool(ActionExec& thread)
3118 {
3119     /* Register the so-read ConstantPool into the VM */
3120     getVM(thread.env).setConstantPool(
3121         &thread.code.readConstantPool(thread.getCurrentPC(), thread.getNextPC())
3122     );
3123 }
3124 
3125 void
ActionDefineFunction2(ActionExec & thread)3126 ActionDefineFunction2(ActionExec& thread)
3127 {
3128     as_environment& env = thread.env;
3129     const action_buffer& code = thread.code;
3130 
3131     // Code starts at thread.getNextPC() as the DefineFunction tag
3132     // contains name and args, while next tag is first tag
3133     // of the function body.
3134     Function2* func = new Function2(code, env, thread.getNextPC(),
3135             thread.getScopeStack());
3136 
3137     // We're stuck initializing our own prototype at the moment.
3138     as_object* proto = createObject(getGlobal(env));
3139     proto->init_member(NSV::PROP_CONSTRUCTOR, func);
3140     func->init_member(NSV::PROP_PROTOTYPE, proto);
3141 
3142     Global_as& gl = getGlobal(env);
3143 
3144     as_function* f = getOwnProperty(gl, NSV::CLASS_FUNCTION).to_function();
3145     if (f) {
3146         const int flags = as_object::DefaultFlags | PropFlags::onlySWF6Up;
3147         func->init_member(NSV::PROP_uuPROTOuu, getMember(*f,
3148                     NSV::PROP_PROTOTYPE), flags);
3149         func->init_member(NSV::PROP_CONSTRUCTOR, f);
3150     }
3151 
3152     size_t i = thread.getCurrentPC() + 3; // skip tag id and length
3153 
3154     // Extract name.
3155     // @@ security: watch out for possible missing terminator here!
3156     const std::string name = code.read_string(i);
3157     i += name.length() + 1; // add NULL-termination
3158 
3159     // Get number of arguments.
3160     const std::uint16_t nargs = code.read_uint16(i);
3161     i += 2;
3162 
3163     // Get the count of local registers used by this function.
3164     const std::uint8_t register_count = code[i];
3165     ++i;
3166 
3167     func->setRegisterCount(register_count);
3168 
3169     // Flags, for controlling register assignment of implicit args.
3170     const std::uint16_t flags = code.read_uint16(i);
3171     i += 2;
3172 
3173     func->setFlags(flags);
3174 
3175     // Get the register assignments and names of the arguments.
3176     for (size_t n = 0; n < nargs; ++n) {
3177         std::uint8_t arg_register = code[i];
3178         ++i;
3179 
3180         // @@ security: watch out for possible missing terminator here!
3181         const std::string arg(code.read_string(i));
3182 
3183         func->add_arg(arg_register, getURI(getVM(env), arg));
3184         i += arg.size() + 1;
3185     }
3186 
3187     // Get the length of the actual function code.
3188     std::uint16_t code_size = code.read_int16(i);
3189 
3190     // Check code_size value consistency
3191     const size_t actionbuf_size = thread.code.size();
3192     if (thread.getNextPC() + code_size > actionbuf_size) {
3193         IF_VERBOSE_MALFORMED_SWF(
3194             log_swferror(_("function2 code len (%u) "
3195                 "overflows DOACTION tag boundaries "
3196                 "(DOACTION tag len=%d"
3197                 ", function2 code offset=%d). "
3198                 "Forcing code len to eat the whole buffer "
3199                 "(would this work?)."),
3200                 code_size, actionbuf_size, thread.getNextPC());
3201         );
3202         code_size = actionbuf_size-thread.getNextPC();
3203     }
3204 
3205     i += 2;
3206     func->setLength(code_size);
3207 
3208     // Skip the function body (don't interpret it now).
3209     thread.adjustNextPC(code_size);
3210 
3211     // If we have a name, then save the function in this
3212     // environment under that name.
3213     as_value function_value(func);
3214     if (!name.empty()) {
3215         IF_VERBOSE_ACTION(
3216             log_action(_("DefineFunction2: named function '%s' "
3217                         "starts at PC %d"), name, func->getStartPC());
3218         );
3219 
3220         thread.setVariable(name, function_value);
3221     }
3222 
3223     // Otherwise push the function literal on the stack
3224     else {
3225         IF_VERBOSE_ACTION(
3226             log_action(_("DefineFunction2: anonymous function starts at "
3227                         "PC %d"), func->getStartPC());
3228         );
3229         env.push(function_value);
3230     }
3231 }
3232 
3233 void
ActionTry(ActionExec & thread)3234 ActionTry(ActionExec& thread)
3235 {
3236     const action_buffer& code = thread.code;
3237 
3238 #if GNASH_PARANOIA_LEVEL > 1
3239     assert(thread.atActionTag(SWF::ACTION_TRY));
3240 #endif
3241 
3242     size_t i = thread.getCurrentPC() + 3; // skip tag id and length
3243 
3244     std::uint8_t flags = code[i];
3245     ++i;
3246 
3247     bool doCatch = flags & 1;
3248     bool doFinally = flags & (1<<1);
3249     bool catchInRegister = flags&(1<<2);
3250     std::uint8_t reserved = flags&0xE0;
3251 
3252     std::uint16_t trySize = code.read_uint16(i); i += 2;
3253     std::uint16_t catchSize = code.read_uint16(i); i += 2;
3254     std::uint16_t finallySize = code.read_uint16(i); i += 2;
3255 
3256     const char* catchName = nullptr;
3257     std::uint8_t catchRegister = 0;
3258 
3259     if (!doFinally) finallySize = 0;
3260     if (!doCatch) catchSize = 0;
3261 
3262     if (!catchInRegister) {
3263         catchName = code.read_string(i);
3264         i += strlen(catchName) + 1;
3265         thread.pushTryBlock(
3266                 TryBlock(i, trySize, catchSize, finallySize, catchName));
3267     }
3268     else {
3269         catchRegister = code[i];
3270         ++i;
3271         thread.pushTryBlock(
3272                 TryBlock(i, trySize, catchSize, finallySize, catchRegister));
3273     }
3274 
3275     thread.setNextPC(i); // Proceed into the try block.
3276 
3277     IF_VERBOSE_ACTION(
3278     log_action(_("ActionTry: reserved:%x doFinally:%d doCatch:%d trySize:%u "
3279                 "catchSize:%u finallySize:%u catchName:%s catchRegister:%u"),
3280                 static_cast<int>(reserved), doFinally, doCatch, trySize,
3281                 catchSize, finallySize,
3282                 catchName ? catchName : "(null)", catchRegister);
3283     );
3284 }
3285 
3286 /// See: http://sswf.sourceforge.net/SWFalexref.html#action_with
3287 void
ActionWith(ActionExec & thread)3288 ActionWith(ActionExec& thread)
3289 {
3290     as_environment& env = thread.env;
3291     const action_buffer& code = thread.code;
3292     size_t pc = thread.getCurrentPC();
3293 
3294 #if GNASH_PARANOIA_LEVEL > 1
3295     assert(thread.atActionTag(SWF::ACTION_WITH));
3296 #endif
3297 
3298     const as_value& val = env.pop();
3299     as_object* with_obj = toObject(val, getVM(thread.env));
3300 
3301     ++pc; // skip tag code
3302 
3303     int tag_length = code.read_int16(pc); // read tag len (should be 2)
3304     if ( tag_length != 2 )
3305     {
3306         IF_VERBOSE_MALFORMED_SWF(
3307         log_swferror(_("ActionWith tag length != 2; skipping"));
3308         );
3309         return;
3310     }
3311     pc += 2; // skip tag len
3312 
3313     unsigned block_length = code.read_int16(pc); // read 'with' body size
3314     if ( block_length == 0 )
3315     {
3316         IF_VERBOSE_ASCODING_ERRORS(
3317         log_aserror(_("Empty with() block..."));
3318         );
3319         return;
3320     }
3321     pc += 2; // skip with body size
3322 
3323     // now we should be on the first action of the 'with' body
3324     assert(thread.getNextPC() == pc);
3325 
3326     if (!with_obj) {
3327         IF_VERBOSE_ASCODING_ERRORS(
3328         log_aserror(_("with(%s) : first argument doesn't cast to an object!"),
3329             val);
3330         );
3331         // skip the full block
3332         thread.adjustNextPC(block_length);
3333         return;
3334     }
3335 
3336     // where does the 'with' block end?
3337     const size_t block_end = thread.getNextPC() + block_length;
3338 
3339     if (!thread.pushWith(With(with_obj, block_end))) {
3340         // skip the full block
3341         thread.adjustNextPC(block_length);
3342     }
3343 
3344 }
3345 
3346 void
ActionDefineFunction(ActionExec & thread)3347 ActionDefineFunction(ActionExec& thread)
3348 {
3349     as_environment& env = thread.env;
3350     const action_buffer& code = thread.code;
3351 
3352 #ifndef NDEBUG
3353     // TODO: check effects of the following 'length'
3354     std::int16_t length = code.read_int16(thread.getCurrentPC()+1);
3355     assert( length >= 0 );
3356 #endif
3357 
3358     // Create a new Function
3359     // Code starts at thread.getNextPC() as the DefineFunction tag
3360     // contains name and args, while next tag is first tag
3361     // of the function body.
3362     Function* func = new Function(code, env, thread.getNextPC(),
3363             thread.getScopeStack());
3364 
3365     // We're stuck initializing our own prototype at the moment.
3366     as_object* proto = createObject(getGlobal(env));
3367     proto->init_member(NSV::PROP_CONSTRUCTOR, func);
3368     func->init_member(NSV::PROP_PROTOTYPE, proto);
3369 
3370     Global_as& gl = getGlobal(env);
3371 
3372     as_function* f = getOwnProperty(gl, NSV::CLASS_FUNCTION).to_function();
3373     if (f) {
3374         const int flags = as_object::DefaultFlags | PropFlags::onlySWF6Up;
3375         func->init_member(NSV::PROP_uuPROTOuu, getMember(*f,
3376                     NSV::PROP_PROTOTYPE), flags);
3377         func->init_member(NSV::PROP_CONSTRUCTOR, f);
3378     }
3379 
3380     size_t i = thread.getCurrentPC() + 3;
3381 
3382     // Extract name.
3383     // @@ security: watch out for possible missing terminator here!
3384     const std::string name = code.read_string(i);
3385     i += name.length() + 1;
3386 
3387     // Get number of arguments.
3388     const size_t nargs = code.read_uint16(i);
3389     i += 2;
3390 
3391     // Get the names of the arguments.
3392     for (size_t n = 0; n < nargs; ++n) {
3393         const std::string arg(code.read_string(i));
3394         func->add_arg(0, getURI(getVM(env), arg));
3395         i += arg.size() + 1;
3396     }
3397 
3398     // Get the length of the actual function code.
3399     const std::uint16_t code_size = code.read_uint16(i);
3400 
3401     func->setLength(code_size);
3402 
3403     // Skip the function body (don't interpret it now).
3404     // getNextPC() is assumed to point to first action of
3405     // the function body (one-past the current tag, whic
3406     // is DefineFunction). We add code_size to it.
3407     thread.adjustNextPC(code_size);
3408 
3409     // If we have a name, then save the function in this
3410     // environment under that name.
3411     as_value function_value(func);
3412     if (!name.empty()) {
3413         IF_VERBOSE_ACTION(
3414             log_action("DefineFunction: named function '%s' starts at "
3415                         "PC %d", name, func->getStartPC());
3416         );
3417         thread.setVariable(name, function_value);
3418     }
3419     else {
3420         // Otherwise push the function literal on the stack
3421         IF_VERBOSE_ACTION(
3422             log_action("DefineFunction: anonymous function starts at "
3423                         "PC %d", func->getStartPC());
3424         );
3425         env.push(function_value);
3426     }
3427 }
3428 
3429 void
ActionSetRegister(ActionExec & thread)3430 ActionSetRegister(ActionExec& thread)
3431 {
3432     as_environment& env = thread.env;
3433     const action_buffer& code = thread.code;
3434     const size_t reg = code[thread.getCurrentPC() + 3];
3435     // Save top of stack in specified register.
3436     getVM(env).setRegister(reg, env.top(0));
3437 }
3438 
3439 
3440 void
ActionUnsupported(ActionExec & thread)3441 ActionUnsupported(ActionExec& thread)
3442 {
3443     log_error(_("Unsupported action handler invoked, code at pc is %#x"),
3444             static_cast<int>(thread.code[thread.getCurrentPC()]));
3445 }
3446 
3447 as_object*
safeToObject(VM & vm,const as_value & val)3448 safeToObject(VM& vm, const as_value& val)
3449 {
3450     try {
3451         return toObject(val, vm);
3452     }
3453     catch (const GnashException&) {
3454         return nullptr;
3455     }
3456 }
3457 
3458 // Utility: construct an object using given constructor.
3459 // This is used by both ActionNew and ActionNewMethod and
3460 // hides differences between builtin and actionscript-defined
3461 // constructors.
3462 as_object*
construct_object(as_function * ctor_as_func,as_environment & env,unsigned int nargs)3463 construct_object(as_function* ctor_as_func, as_environment& env,
3464         unsigned int nargs)
3465 {
3466     assert(ctor_as_func);
3467     fn_call::Args args;
3468     for (size_t i = 0; i < nargs; ++i) {
3469         args += env.pop();
3470     }
3471     return constructInstance(*ctor_as_func, env, args);
3472 }
3473 
3474 // Common code for GetUrl and GetUrl2. See:
3475 // http://sswf.sourceforge.net/SWFalexref.html#action_get_url
3476 // http://sswf.sourceforge.net/SWFalexref.html#action_get_url2
3477 //
3478 // Testcases:
3479 //
3480 // - http://www.garfield.com/comics/comics_todays.html
3481 //   lower_todayscomic.swf should render four flash files in its canvas
3482 //
3483 // - http://www.voiptalk.org
3484 //   pressing 'My Account' button should open
3485 //   https://www.voiptalk.org/products/login.php
3486 //   NOTE: this is affected by the GetUrl bug reported with an excerpt
3487 //         from Colin Moock book, see below. (won't work, and won't fix)
3488 //
3489 // - http://www.uptoten.com
3490 //   Should load in _level0, with loadTargetFlag set.
3491 //
3492 // Method isbBit-packed as follows:
3493 // SendVarsMethod:2 (0:NONE 1:GET 2:POST)
3494 // Reserved:4
3495 // LoadTargetFlag:1
3496 // LoadVariableFlag:1
3497 /// @param target        the target window, or _level1..10
3498 void
commonGetURL(as_environment & env,as_value target,const std::string & url,std::uint8_t method)3499 commonGetURL(as_environment& env, as_value target,
3500         const std::string& url, std::uint8_t method)
3501 {
3502     if (url.empty()) {
3503         log_error(_("Bogus empty GetUrl URL in SWF file, skipping"));
3504         return;
3505     }
3506 
3507     // Parse the method bitfield
3508     bool loadTargetFlag    = method & 64;
3509     bool loadVariableFlag  = method & 128;
3510 
3511     MovieClip::VariablesMethod sendVarsMethod;
3512 
3513     // handle malformed sendVarsMethod
3514     if ((method & 3) == 3) {
3515         log_error(_("Bogus GetUrl2 send vars method"
3516             " in SWF file (both GET and POST requested). Using GET"));
3517         sendVarsMethod = MovieClip::METHOD_GET;
3518     }
3519     else {
3520         sendVarsMethod = static_cast<MovieClip::VariablesMethod>(method & 3);
3521     }
3522 
3523     std::string target_string;
3524     if (!target.is_undefined() && !target.is_null()) {
3525         target_string = target.to_string();
3526     }
3527 
3528     VM& vm = getVM(env);
3529     movie_root& m = vm.getRoot();
3530 
3531     // If the url starts with "FSCommand:", then this is
3532     // a message for the host app.
3533     StringNoCaseEqual noCaseCompare;
3534     if (noCaseCompare(url.substr(0, 10), "FSCommand:")) {
3535         m.handleFsCommand(url.substr(10), target_string);
3536         return;
3537     }
3538 
3539     // If the url starts with "print:", then this is
3540     // a print request.
3541     if (noCaseCompare(url.substr(0, 6), "print:")) {
3542         log_unimpl("print: URL");
3543         return;
3544     }
3545 
3546     //
3547     // From "ActionScript: The Definitive Guide" by Colin Moock p. 470
3548     // --------8<------------------------------------------------------
3549     // In most browsers, getURL() relative links are resolved relative
3550     // to the HTML file that contains the .swf file. In IE 4.5 and older
3551     // versions on Macintosh, relative links are resolved relative to
3552     // the location of the .swf file, not the HTML file, which causes
3553     // problems when the two are in different directories. To avoid
3554     // the problem, either place the .swf and the .html file in the
3555     // same directory or use absolute URLs when invoking getURL().
3556     // --------8<------------------------------------------------------
3557     //
3558     // We'll resolve relative to our "base url".
3559     // The base url must be set with the set_base_url() command.
3560     //
3561 
3562     log_debug("get url: target=%s, URL=%s, method=%x "
3563                 "(sendVars:%X, loadTarget:%d, loadVariable:%d)",
3564             target_string, url, static_cast<int>(method),
3565             sendVarsMethod, loadTargetFlag, loadVariableFlag);
3566 
3567     DisplayObject* target_ch = findTarget(env, target_string);
3568     MovieClip* target_movie = target_ch ? target_ch->to_movie() : nullptr;
3569 
3570     if (loadVariableFlag) {
3571         log_debug("getURL2 loadVariable");
3572 
3573         if (!target_ch) {
3574             log_error(_("getURL: target %s not found"), target_string);
3575             // might want to invoke the external url opener here...
3576             return;
3577         }
3578 
3579         if (!target_movie) {
3580             log_error(_("getURL: target %s is not a sprite"), target_string);
3581             // might want to invoke the external url opener here...
3582             return;
3583         }
3584 
3585         target_movie->loadVariables(url, sendVarsMethod);
3586 
3587         return;
3588     }
3589 
3590     std::string varsToSend;
3591     if (sendVarsMethod != MovieClip::METHOD_NONE) {
3592 
3593         // TESTED: variables sent are those in current target,
3594         //         no matter the target found on stack (which
3595         //         is the target to load the resource into).
3596         //
3597         as_object* curtgt = getObject(env.target());
3598         if (!curtgt) {
3599             log_error(_("commonGetURL: current target is undefined"));
3600             return;
3601         }
3602         varsToSend = getURLEncodedVars(*curtgt);
3603     }
3604 
3605 
3606     if (loadTargetFlag) {
3607         log_debug("getURL2 target load");
3608 
3609         if (!target_ch) {
3610 
3611             unsigned int levelno;
3612             if (isLevelTarget(getSWFVersion(env), target_string, levelno)) {
3613                 log_debug("Testing _level loading (level %u)", levelno);
3614                 m.loadMovie(url, target_string, varsToSend, sendVarsMethod);
3615                 return;
3616             }
3617 
3618             IF_VERBOSE_ASCODING_ERRORS(
3619                 log_aserror(_("Unknown loadMovie target: %s"),
3620                     target_string);
3621             );
3622 
3623             // TESTED: Even if the target is created right-after
3624             //         the load request, the player won't load
3625             //         into it. In other words, the target MUST
3626             //         exist at time of interpreting the GETURL2
3627             //         tag with loadTarget flag.
3628 
3629             return;
3630         }
3631 
3632         if (!target_movie) {
3633             log_error(_("get URL: target %s is not a sprite"), target_string);
3634             return;
3635         }
3636 
3637         const std::string s = target_movie->getTarget(); // or getOrigTarget ?
3638         if (s != target_movie->getOrigTarget()) {
3639             log_debug("TESTME: target of a loadMovie changed its target path");
3640         }
3641 
3642         // To trigger: https://savannah.gnu.org/bugs/?32506
3643         if ( m.findCharacterByTarget(s) != target_movie ) {
3644             log_error("FIXME: "
3645                 "getURL target %1% is resolved by findTarget(env) "
3646                 "to sprite %2%. Sprite %2% has "
3647                 "target %3%. Target %3% will be resolved "
3648                 "by movie_root::findCharacterByTarget() to %4%",
3649                 target_string, target_movie, s,
3650                 m.findCharacterByTarget(s));
3651         }
3652 
3653         // We might probably use target_string here rather than
3654         // ``s'' (which is the _official_ target) but at time
3655         // of writing the MovieLoader will use
3656         // movie_root::findCharacterByTarget to resolve paths,
3657         // and that one, in turn, won't support /slash/notation
3658         // which may be found in target_string (see also loadMovieTest.swf
3659         // under misc-ming.all)
3660         //
3661         m.loadMovie(url, s, varsToSend, sendVarsMethod);
3662         return;
3663     }
3664 
3665     unsigned int levelno;
3666     if (isLevelTarget(getSWFVersion(env), target_string, levelno)) {
3667         log_debug("Testing _level loading (level %u)", levelno);
3668         m.loadMovie(url, target_string, varsToSend, sendVarsMethod);
3669         return;
3670     }
3671 
3672     // Just plain getURL
3673     // This should be the original URL string, as the hosting application
3674     // will decide how to resolve the URL. If there is no hosting
3675     // application, movie_root::getURL will resolve the URL.
3676     m.getURL(url, target_string, varsToSend, sendVarsMethod);
3677 
3678 }
3679 
3680 // Common code for SetTarget and SetTargetExpression. See:
3681 // http://sswf.sourceforge.net/SWFalexref.html#action_set_target
3682 // http://sswf.sourceforge.net/SWFalexref.html#action_get_dynamic
3683 void
commonSetTarget(ActionExec & thread,const std::string & target_name)3684 commonSetTarget(ActionExec& thread, const std::string& target_name)
3685 {
3686     as_environment& env = thread.env;
3687 
3688     // see swfdec's settarget-relative-*.swf
3689     env.reset_target();
3690 
3691     // if the string is blank, we reset the target to its original value
3692     if (target_name.empty()) return;
3693 
3694     // TODO: pass thread.getScopeStack()
3695     DisplayObject* new_target = findTarget(env, target_name);
3696     if (!new_target) {
3697         IF_VERBOSE_ASCODING_ERRORS (
3698             log_aserror(_("Couldn't find movie \"%s\" to set target to!"
3699                 " Setting target to NULL..."), target_name);
3700         );
3701     }
3702 
3703     env.set_target(new_target);
3704 }
3705 
3706 }
3707 
3708 
3709 } // namespace gnash
3710