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