1 // VM.cpp: the Virtual Machine class, for Gnash
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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.
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.
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
22 #include "gnashconfig.h"
29 #include <boost/random.hpp> // for random generator
32 #ifdef HAVE_SYS_UTSNAME_H
33 # include <sys/utsname.h> // For system information
36 #include "SharedObject_as.h" // for SharedObjectLibrary
37 #include "smart_ptr.h"
38 #include "NativeFunction.h"
39 #include "movie_definition.h"
41 #include "movie_root.h"
42 #include "Global_as.h"
44 #include "namedStrings.h"
45 #include "VirtualClock.h" // for getTime()
46 #include "GnashNumeric.h"
49 gnash::RcInitFile
& rcfile
= gnash::RcInitFile::getDefaultInstance();
54 VM::VM(int version
, movie_root
& root
, VirtualClock
& clock
)
57 _global(new Global_as(*this)),
61 _shLib(new SharedObjectLibrary(*this)),
64 NSV::loadStrings(_stringTable
);
65 _global
->registerClasses();
74 VM::setSWFVersion(int v
)
80 VM::randomNumberGenerator()
86 VM::getPlayerVersion() const
89 static const std::string
version(rcfile
.getFlashVersionString());
97 // The directive in gnashrc must override OS detection.
98 if (rcfile
.getFlashSystemOS() != "") {
99 return rcfile
.getFlashSystemOS();
102 #ifdef HAVE_SYS_UTSNAME_H
103 // For Linux- or UNIX-based systems (POSIX 4.4 conformant)
105 struct utsname osname
;
110 tmp
= osname
.sysname
;
112 tmp
+= osname
.release
;
116 // Last resort, hard-coded from compile-time options
118 return DEFAULT_FLASH_SYSTEM_OS
;
124 VM::getSystemLanguage() const
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.
134 if ((loc
= std::getenv("LANG")) || (loc
= std::getenv("LANGUAGE")) ||
135 (loc
= std::getenv("LC_MESSAGES"))) {
139 return std::string();
149 VM::getGlobal() const
157 return _clock
.elapsed();
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();
178 for (CallStack::size_type i
=0, n
=_callStack
.size(); i
<n
; ++i
)
180 _callStack
[i
].markReachableResources();
184 assert (_callStack
.empty());
185 assert (_stack
.totalSize() == 0);
191 VM::getRegister(size_t index
)
193 // If there is a call frame and it has registers, the value must be
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
];
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
210 if (!_callStack
.empty()) {
211 CallFrame
& fr
= currentCall();
212 if (fr
.hasRegisters()) {
213 currentCall().setLocalRegister(index
, val
);
218 // Do nothing if the index is out of bounds.
219 if (index
< _globalRegisters
.size()) _globalRegisters
[index
] = val
;
222 log_action(_("-------------- global register[%d] = '%s'"),
231 assert(!_callStack
.empty());
232 return _callStack
.back();
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
;
253 throw ActionLimitException(ss
.str());
256 _callStack
.push_back(CallFrame(&func
));
257 return _callStack
.back();
263 assert(!_callStack
.empty());
264 _callStack
.pop_back();
268 VM::registerNative(Global_as::ASFunction fun
, unsigned int x
, unsigned int y
)
271 assert(!_asNativeTable
[x
][y
]);
272 _asNativeTable
[x
][y
] = fun
;
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();
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
);
299 VM::dumpState(std::ostream
& out
, size_t limit
)
304 const size_t n
= _stack
.size();
306 if (limit
&& n
> limit
) {
308 out
<< "Stack (last " << limit
<< " of " << n
<< " items): ";
314 for (size_t i
= si
; i
< n
; ++i
) {
315 if (i
!= si
) out
<< " | ";
316 out
<< '"' << _stack
.value(i
) << '"';
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
;
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
<< " | ";
345 ///////////////////////////////////////////////////////////////////////
349 ///////////////////////////////////////////////////////////////////////
352 newAdd(as_value
& op1
, const as_value
& op2
, const VM
& vm
)
354 // We can't change the original value.
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
) {}
368 log_debug(_("(%s + %s) [primitive conversion done]"), op1
, r
);
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
));
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
);
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
);
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()) {
409 try { operand2
= op2
.to_primitive(as_value::NUMBER
); }
410 catch (const ActionTypeError
& e
) {}
412 if (operand2
.is_object() && !operand2
.is_sprite()) {
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
)) {
431 return as_value(num1
< num2
);
435 equals(const as_value
& a
, const as_value
& b
, const VM
& vm
)
437 return a
.equals(b
, vm
.getSWFVersion());
441 toBool(const as_value
& v
, const VM
& vm
)
443 return v
.to_bool(vm
.getSWFVersion());
447 toNumber(const as_value
& v
, const VM
& vm
)
449 return v
.to_number(vm
.getSWFVersion());
453 toObject(const as_value
& v
, VM
& vm
)
455 return v
.to_object(vm
);
459 toInt(const as_value
& v
, const VM
& vm
)
461 const double d
= v
.to_number(vm
.getSWFVersion());
463 if (!isFinite(d
)) return 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.
474 convertToNumber(as_value
& v
, const VM
& vm
)
476 v
.set_double(v
.to_number(vm
.getSWFVersion()));
480 /// Force type to string.
482 convertToString(as_value
& v
, const VM
& vm
)
484 v
.set_string(v
.to_string(vm
.getSWFVersion()));
488 /// Force type to bool.
490 convertToBoolean(as_value
& v
, const VM
& vm
)
492 v
.set_bool(v
.to_bool(vm
.getSWFVersion()));
497 convertToPrimitive(as_value
& v
, const VM
& vm
)
499 const as_value::AsType
t(v
.defaultPrimitive(vm
.getSWFVersion()));
500 v
= v
.to_primitive(t
);
505 } // end of namespace gnash
510 // indent-tabs-mode: t