Fix test for bug #32625
[gnash.git] / libcore / vm / VM.cpp
blobf991fbd9d7575fec98d042921d14a18eab36cb7d
1 // VM.cpp: the Virtual Machine class, for Gnash
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4 // 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
21 #ifdef HAVE_CONFIG_H
22 #include "gnashconfig.h"
23 #endif
25 #include "VM.h"
27 #include <ostream>
28 #include <memory>
29 #include <boost/random.hpp> // for random generator
30 #include <cstdlib>
31 #include <cmath>
32 #ifdef HAVE_SYS_UTSNAME_H
33 # include <sys/utsname.h> // For system information
34 #endif
36 #include "SharedObject_as.h" // for SharedObjectLibrary
37 #include "smart_ptr.h"
38 #include "NativeFunction.h"
39 #include "movie_definition.h"
40 #include "Movie.h"
41 #include "movie_root.h"
42 #include "Global_as.h"
43 #include "rc.h"
44 #include "namedStrings.h"
45 #include "VirtualClock.h" // for getTime()
46 #include "GnashNumeric.h"
48 namespace {
49 gnash::RcInitFile& rcfile = gnash::RcInitFile::getDefaultInstance();
52 namespace gnash {
54 VM::VM(int version, movie_root& root, VirtualClock& clock)
56 _rootMovie(root),
57 _global(new Global_as(*this)),
58 _swfversion(version),
59 _clock(clock),
60 _stack(),
61 _shLib(new SharedObjectLibrary(*this)),
62 _rng(clock.elapsed())
64 NSV::loadStrings(_stringTable);
65 _global->registerClasses();
66 _clock.restart();
69 VM::~VM()
73 void
74 VM::setSWFVersion(int v)
76 _swfversion = v;
79 VM::RNG&
80 VM::randomNumberGenerator()
82 return _rng;
85 const std::string&
86 VM::getPlayerVersion() const
88 //From rcfile
89 static const std::string version(rcfile.getFlashVersionString());
90 return version;
93 std::string
94 VM::getOSName() const
97 // The directive in gnashrc must override OS detection.
98 if (rcfile.getFlashSystemOS() != "") {
99 return rcfile.getFlashSystemOS();
101 else {
102 #ifdef HAVE_SYS_UTSNAME_H
103 // For Linux- or UNIX-based systems (POSIX 4.4 conformant)
105 struct utsname osname;
106 std::string tmp;
108 uname(&osname);
110 tmp = osname.sysname;
111 tmp += " ";
112 tmp += osname.release;
114 return tmp;
115 #else
116 // Last resort, hard-coded from compile-time options
118 return DEFAULT_FLASH_SYSTEM_OS;
119 #endif
123 std::string
124 VM::getSystemLanguage() const
127 char *loc;
129 // Try various environment variables. These should
130 // be in the standard form "de", "de_DE" or "de_DE.utf8"
131 // This should work on most UNIX-like systems.
132 // Callers should work out what to do with it.
133 // TODO: Other OSs.
134 if ((loc = std::getenv("LANG")) || (loc = std::getenv("LANGUAGE")) ||
135 (loc = std::getenv("LC_MESSAGES"))) {
136 return loc;
139 return std::string();
142 movie_root&
143 VM::getRoot() const
145 return _rootMovie;
148 Global_as*
149 VM::getGlobal() const
151 return _global;
154 unsigned long int
155 VM::getTime() const
157 return _clock.elapsed();
160 void
161 VM::markReachableResources() const
163 std::for_each(_globalRegisters.begin(), _globalRegisters.end(),
164 std::mem_fun_ref(&as_value::setReachable));
166 _global->setReachable();
168 if (_shLib.get()) _shLib->markReachableResources();
170 #ifdef ALLOW_GC_RUN_DURING_ACTIONS_EXECUTION
171 /// Mark all (including unreachable) stack elements
172 for (SafeStack<as_value>::StackSize i=0, n=_stack.totalSize(); i<n; ++i)
174 _stack.at(i).setReachable();
177 /// Mark call stack
178 for (CallStack::size_type i=0, n=_callStack.size(); i<n; ++i)
180 _callStack[i].markReachableResources();
183 #else
184 assert (_callStack.empty());
185 assert (_stack.totalSize() == 0);
186 #endif
190 const as_value*
191 VM::getRegister(size_t index)
193 // If there is a call frame and it has registers, the value must be
194 // sought there.
195 if (!_callStack.empty()) {
196 const CallFrame& fr = currentCall();
197 if (fr.hasRegisters()) return fr.getLocalRegister(index);
200 // Otherwise it can be in the global registers.
201 if (index < _globalRegisters.size()) return &_globalRegisters[index];
202 return 0;
205 void
206 VM::setRegister(size_t index, const as_value& val)
208 // If there is a call frame and it has registers, the value must be
209 // set there.
210 if (!_callStack.empty()) {
211 CallFrame& fr = currentCall();
212 if (fr.hasRegisters()) {
213 currentCall().setLocalRegister(index, val);
214 return;
218 // Do nothing if the index is out of bounds.
219 if (index < _globalRegisters.size()) _globalRegisters[index] = val;
221 IF_VERBOSE_ACTION(
222 log_action(_("-------------- global register[%d] = '%s'"),
223 index, val);
228 CallFrame&
229 VM::currentCall()
231 assert(!_callStack.empty());
232 return _callStack.back();
235 CallFrame&
236 VM::pushCallFrame(UserFunction& func)
239 // The stack size can be changed by the ScriptLimits
240 // tag. There is *no* difference between SWF versions.
241 // TODO: override from gnashrc.
243 // A stack size of 0 is apparently legitimate.
244 const boost::uint16_t recursionLimit = getRoot().getRecursionLimit();
246 // Don't proceed if local call frames would reach the recursion limit.
247 if (_callStack.size() + 1 >= recursionLimit) {
249 std::ostringstream ss;
250 ss << boost::format(_("Recursion limit reached (%u)")) % recursionLimit;
252 // throw something
253 throw ActionLimitException(ss.str());
256 _callStack.push_back(CallFrame(&func));
257 return _callStack.back();
260 void
261 VM::popCallFrame()
263 assert(!_callStack.empty());
264 _callStack.pop_back();
267 void
268 VM::registerNative(Global_as::ASFunction fun, unsigned int x, unsigned int y)
270 assert(fun);
271 assert(!_asNativeTable[x][y]);
272 _asNativeTable[x][y] = fun;
275 NativeFunction*
276 VM::getNative(unsigned int x, unsigned int y) const
278 AsNativeTable::const_iterator row = _asNativeTable.find(x);
279 if (row == _asNativeTable.end()) return 0;
280 FuncMap::const_iterator col = row->second.find(y);
281 if (col == row->second.end()) return 0;
282 Global_as::ASFunction fun = col->second;
284 NativeFunction* f = new NativeFunction(*_global, fun);
286 Global_as& gl = *_global;
288 as_function* func = getOwnProperty(gl, NSV::CLASS_FUNCTION).to_function();
289 if (func) {
290 const int flags = as_object::DefaultFlags | PropFlags::onlySWF6Up;
291 f->init_member(NSV::PROP_uuPROTOuu, getMember(*func,
292 NSV::PROP_PROTOTYPE), flags);
293 f->init_member(NSV::PROP_CONSTRUCTOR, func);
295 return f;
298 void
299 VM::dumpState(std::ostream& out, size_t limit)
302 // Dump stack:
303 size_t si = 0;
304 const size_t n = _stack.size();
306 if (limit && n > limit) {
307 si = n - limit;
308 out << "Stack (last " << limit << " of " << n << " items): ";
310 else {
311 out << "Stack: ";
314 for (size_t i = si; i < n; ++i) {
315 if (i != si) out << " | ";
316 out << '"' << _stack.value(i) << '"';
318 out << "\n";
320 out << "Global registers: ";
321 for (GlobalRegisters::const_iterator it = _globalRegisters.begin(),
322 e = _globalRegisters.end(); it != e; ++it) {
323 const as_value& v = *it;
324 if (v.is_undefined()) continue;
325 if (it != _globalRegisters.begin()) out << ", ";
327 out << (it - _globalRegisters.begin()) << ":" << v;
329 out << "\n";
331 // Now local registers and variables from the call stack.
332 if (_callStack.empty()) return;
334 out << "Local registers: ";
335 for (CallStack::const_iterator it = _callStack.begin(),
336 e = _callStack.end(); it != e; ++it) {
337 if (it != _callStack.begin()) out << " | ";
338 out << *it;
340 out << "\n";
345 ///////////////////////////////////////////////////////////////////////
347 // Value ops
349 ///////////////////////////////////////////////////////////////////////
351 void
352 newAdd(as_value& op1, const as_value& op2, const VM& vm)
354 // We can't change the original value.
355 as_value r(op2);
357 // The order of the operations is important: op2 is converted to
358 // primitive before op1.
360 /// It doesn't matter if either of these fail.
361 try { convertToPrimitive(r, vm); }
362 catch (ActionTypeError& e) {}
364 try { convertToPrimitive(op1, vm); }
365 catch (ActionTypeError& e) {}
367 #if GNASH_DEBUG
368 log_debug(_("(%s + %s) [primitive conversion done]"), op1, r);
369 #endif
371 if (op1.is_string() || r.is_string()) {
373 // use string semantic
374 const int version = vm.getSWFVersion();
375 convertToString(op1, vm);
376 op1.set_string(op1.to_string(version) + r.to_string(version));
377 return;
380 // Otherwise use numeric semantic
381 const double num1 = toNumber(op1, vm);
382 const double num2 = toNumber(r, vm);
383 op1.set_double(num2 + num1);
387 void
388 subtract(as_value& op1, const as_value& op2, const VM& vm)
390 const double num2 = toNumber(op2, vm);
391 const double num1 = toNumber(op1, vm);
392 op1.set_double(num1 - num2);
395 as_value
396 newLessThan(const as_value& op1, const as_value& op2, const VM& vm)
399 as_value operand1(op1);
400 as_value operand2(op2);
402 try { operand1 = op1.to_primitive(as_value::NUMBER); }
403 catch (const ActionTypeError& e) {}
405 if (operand1.is_object() && !operand1.is_sprite()) {
406 return false;
409 try { operand2 = op2.to_primitive(as_value::NUMBER); }
410 catch (const ActionTypeError& e) {}
412 if (operand2.is_object() && !operand2.is_sprite()) {
413 return false;
416 if (operand1.is_string() && operand2.is_string())
418 const std::string& s1 = operand1.to_string();
419 const std::string& s2 = operand2.to_string();
420 if (s1.empty()) return false;
421 if (s2.empty()) return true;
422 return as_value(s1 < s2);
425 const double num1 = toNumber(operand1, vm);
426 const double num2 = toNumber(operand2, vm);
428 if (isNaN(num1) || isNaN(num2)) {
429 return as_value();
431 return as_value(num1 < num2);
434 bool
435 equals(const as_value& a, const as_value& b, const VM& vm)
437 return a.equals(b, vm.getSWFVersion());
440 bool
441 toBool(const as_value& v, const VM& vm)
443 return v.to_bool(vm.getSWFVersion());
446 double
447 toNumber(const as_value& v, const VM& vm)
449 return v.to_number(vm.getSWFVersion());
452 as_object*
453 toObject(const as_value& v, VM& vm)
455 return v.to_object(vm);
458 boost::int32_t
459 toInt(const as_value& v, const VM& vm)
461 const double d = v.to_number(vm.getSWFVersion());
463 if (!isFinite(d)) return 0;
465 if (d < 0) {
466 return - static_cast<boost::uint32_t>(std::fmod(-d, 4294967296.0));
469 return static_cast<boost::uint32_t>(std::fmod(d, 4294967296.0));
472 /// Force type to number.
473 as_value&
474 convertToNumber(as_value& v, const VM& vm)
476 v.set_double(v.to_number(vm.getSWFVersion()));
477 return v;
480 /// Force type to string.
481 as_value&
482 convertToString(as_value& v, const VM& vm)
484 v.set_string(v.to_string(vm.getSWFVersion()));
485 return v;
488 /// Force type to bool.
489 as_value&
490 convertToBoolean(as_value& v, const VM& vm)
492 v.set_bool(v.to_bool(vm.getSWFVersion()));
493 return v;
496 as_value&
497 convertToPrimitive(as_value& v, const VM& vm)
499 const as_value::AsType t(v.defaultPrimitive(vm.getSWFVersion()));
500 v = v.to_primitive(t);
501 return v;
505 } // end of namespace gnash
508 // Local Variables:
509 // mode: C++
510 // indent-tabs-mode: t
511 // End: