Drop the bool operator for ObjectURI, to avoid getting the kind of side-effect that...
[gnash.git] / libcore / as_value.cpp
blobd7cf0e1d94d2185b5b1bacda750b55ce801645d2
1 // as_value.cpp: ActionScript values, 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.
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 #include "smart_ptr.h" // GNASH_USE_GC
22 #include "as_value.h"
23 #include "as_object.h"
24 #include "as_function.h" // for as_function
25 #include "MovieClip.h" // for DISPLAYOBJECT values
26 #include "DisplayObject.h" // for DISPLAYOBJECT values
27 #include "as_environment.h" // for DISPLAYOBJECT values
28 #include "VM.h" // for DISPLAYOBJECT values
29 #include "movie_root.h" // for DISPLAYOBJECT values
30 #include "utility.h" // for typeName()
31 #include "GnashNumeric.h"
32 #include "namedStrings.h"
33 #include "GnashException.h"
34 #include "Array_as.h"
35 #include "Date_as.h" // for Date type (readAMF0)
36 #include "SimpleBuffer.h"
37 #include "StringPredicates.h"
38 #include "Global_as.h"
39 #include "String_as.h"
40 #include "AMFConverter.h"
42 #include <boost/shared_ptr.hpp>
43 #include <cmath>
44 #include <cctype>
45 #include <boost/algorithm/string/case_conv.hpp>
46 #include <boost/lexical_cast.hpp>
47 #include <boost/format.hpp>
48 #include <locale>
49 #include <sstream>
50 #include <iomanip>
51 #include <string>
52 #include <algorithm>
54 // Define the macro below to make abstract equality operator verbose
55 //#define GNASH_DEBUG_EQUALITY 1
57 // Define the macro below to make to_primitive verbose
58 //#define GNASH_DEBUG_CONVERSION_TO_PRIMITIVE 1
60 // Define this macro to make soft references activity verbose
61 #define GNASH_DEBUG_SOFT_REFERENCES
63 namespace gnash {
65 namespace {
66 bool objectEqualsPrimitive(const as_value& obj, const as_value& prim);
67 bool stringEqualsNumber(const as_value& str, const as_value& num);
68 bool compareBoolean(const as_value& boolean, const as_value& other);
69 inline bool findMethod(as_object& obj, string_table::key m, as_value& ret);
70 boost::int32_t truncateToInt(double d);
73 namespace {
75 enum Base
77 BASE_OCT,
78 BASE_HEX
82 /// Converts a string to a uint32_t cast to an int32_t.
84 /// @param whole When true, any string that isn't wholly valid is rejected.
85 /// @param base The base (8 or 16) to use.
86 /// @param s The string to parse.
87 /// @return The converted number.
88 boost::int32_t
89 parsePositiveInt(const std::string& s, Base base, bool whole = true)
92 std::istringstream is(s);
93 boost::uint32_t target;
95 switch (base)
97 case BASE_OCT:
98 is >> std::oct;
99 break;
100 case BASE_HEX:
101 is >> std::hex;
102 break;
105 char c;
107 // If the conversion fails, or if the whole string must be convertible and
108 // some DisplayObjects are left, throw an exception.
109 if (!(is >> target) || (whole && is.get(c))) {
110 throw boost::bad_lexical_cast();
113 return target;
116 struct
117 NonNumericChar
119 bool operator()(char c) {
120 return (!std::isdigit(c) && c != '.' && c != '-' && c != '+');
124 /// Omit an empty exponent that is valid in ActionScript but not in C++.
126 /// This function throws a boost::bad_lexical_cast if it finds an invalid
127 /// exponent to avoid attempting an extraction when it will definitely fail.
129 /// A successful return from this function does not mean the exponent is
130 /// valid, only that the result of stringstream's conversion will mirror
131 /// AS behaviour.
133 /// @param si An iterator pointing to the position after an exponent sign.
134 /// @param last The end of the string to extract. If we have an exponent
135 /// with no following digit, this iterator is moved to
136 /// a position before the exponent sign.
137 void
138 validateExponent(std::string::const_iterator si,
139 std::string::const_iterator& last)
142 // Check for exponent with no following character. Depending on the
143 // version of gcc, extraction may be rejected (probably more correct) or
144 // accepted as a valid exponent (what ActionScript wants).
145 // In this case we remove the exponent to get the correct behaviour
146 // on all compilers.
147 if (si == last) {
148 --last;
149 return;
152 // Exponents with a following '-' or '+' are also valid if they end the
153 // string. It's unlikely that any version of gcc allowed this.
154 if (*si == '-' || *si == '+') {
155 ++si;
156 if (si == last) {
157 last -= 2;
158 return;
162 // An exponent ("e", "e-", or "e+") followed by a non digit is invalid.
163 if (!std::isdigit(*si)) {
164 throw boost::bad_lexical_cast();
169 /// Convert a string to a double if the complete string can be converted.
171 /// This follows the conditions of the standard C locale for numbers except
172 /// that an exponent signifier with no following digit (e.g. "2e") is
173 /// considered valid. Moreover, "2e-" is also considered valid.
175 /// This function scans the string twice (once for verification, once for
176 /// extraction) and copies it once (for extraction).
177 double
178 parseDecimalNumber(std::string::const_iterator start,
179 std::string::const_iterator last)
181 assert(start != last);
183 // Find the first position that is not a numeric character ('e' or 'E' not
184 // included). Even if no invalid character is found, it does not mean
185 // that the number is valid ("++++---" would pass the test).
186 std::string::const_iterator si =
187 std::find_if(start, last, NonNumericChar());
189 if (si != last) {
190 // If this character is not an exponent sign, the number is malformed.
191 if (*si != 'e' && *si != 'E') throw boost::bad_lexical_cast();
192 /// Move the last iterator to point before empty exponents.
193 else validateExponent(si + 1, last);
196 return boost::lexical_cast<double>(std::string(start, last));
199 } // anonymous namespace
201 // Conversion to const std::string&.
202 std::string
203 as_value::to_string(int version) const
205 switch (_type)
207 case STRING:
208 return getStr();
209 case DISPLAYOBJECT:
211 const CharacterProxy& sp = getCharacterProxy();
212 if (!sp.get()) return "";
213 return sp.getTarget();
215 case NUMBER:
216 return doubleToString(getNum());
217 case UNDEFINED:
218 if (version <= 6) return "";
219 return "undefined";
220 case NULLTYPE:
221 return "null";
222 case BOOLEAN:
223 return getBool() ? "true" : "false";
224 case OBJECT:
226 as_object* obj = getObj();
227 String_as* s;
228 if (isNativeType(obj, s)) return s->value();
230 try {
231 as_value ret = to_primitive(STRING);
232 // This additional is_string test is NOT compliant with ECMA-262
233 // specification, but seems required for compatibility with the
234 // reference player.
235 if (ret.is_string()) return ret.getStr();
237 catch (ActionTypeError& e) {}
239 return is_function() ? "[type Function]" : "[type Object]";
243 default:
244 return "[exception]";
249 /// This is only used in AVM2.
250 primitive_types
251 as_value::ptype() const
254 switch (_type)
256 case STRING:
257 return PTYPE_STRING;
258 case NUMBER:
259 case UNDEFINED:
260 case NULLTYPE:
261 case DISPLAYOBJECT:
262 case OBJECT:
263 return PTYPE_NUMBER;
264 case BOOLEAN:
265 return PTYPE_BOOLEAN;
266 default:
267 break;
269 return PTYPE_NUMBER;
272 as_value::AsType
273 as_value::defaultPrimitive(int version) const
275 if (_type == OBJECT && version > 5) {
276 Date_as* d;
277 if (isNativeType(getObj(), d)) return STRING;
279 return NUMBER;
282 // Conversion to primitive value.
283 as_value
284 as_value::to_primitive(AsType hint) const
286 if (_type != OBJECT) return *this;
288 #if GNASH_DEBUG_CONVERSION_TO_PRIMITIVE
289 log_debug("to_primitive(%s)", hint==NUMBER ? "NUMBER" : "STRING");
290 #endif
292 // TODO: implement as_object::DefaultValue (ECMA-262 - 8.6.2.6)
294 as_value method;
295 as_object* obj(0);
297 if (hint == NUMBER) {
298 assert(_type == OBJECT);
299 obj = getObj();
301 if (!findMethod(*obj, NSV::PROP_VALUE_OF, method)) {
302 // Returning undefined here instead of throwing
303 // a TypeError passes tests in actionscript.all/Object.as
304 // and many swfdec tests, with no new failures (though
305 // perhaps we aren't testing enough).
306 return as_value();
309 else {
310 assert(hint == STRING);
311 assert(_type == OBJECT);
312 obj = getObj();
314 // @@ Moock says, "the value that results from
315 // calling toString() on the object".
316 if (!findMethod(*obj, NSV::PROP_TO_STRING, method) &&
317 !findMethod(*obj, NSV::PROP_VALUE_OF, method)) {
318 throw ActionTypeError();
322 assert(obj);
324 as_environment env(getVM(*obj));
325 fn_call::Args args;
326 as_value ret = invoke(method, env, obj, args);
328 #if GNASH_DEBUG_CONVERSION_TO_PRIMITIVE
329 log_debug("to_primitive: method call returned %s", ret);
330 #endif
332 if (ret._type == OBJECT) {
333 throw ActionTypeError();
335 return ret;
338 double
339 as_value::to_number() const
342 const int swfversion = VM::get().getSWFVersion();
344 switch (_type)
346 case STRING:
348 const std::string& s = getStr();
349 if ( s.empty() ) {
350 return swfversion >= 5 ? NaN : 0.0;
353 if (swfversion <= 4)
355 // For SWF4, any valid number before non-numerical
356 // DisplayObjects is returned, including exponent, positive
357 // and negative signs and whitespace before.
358 double d = 0;
359 std::istringstream is(s);
360 is >> d;
361 return d;
364 try {
366 if (swfversion > 5) {
367 double d;
368 // Will throw if invalid.
369 if (parseNonDecimalInt(s, d)) return d;
372 // @@ Moock says the rule here is: if the
373 // string is a valid float literal, then it
374 // gets converted; otherwise it is set to NaN.
375 // Valid for SWF5 and above.
376 const std::string::size_type pos =
377 s.find_first_not_of(" \r\n\t");
379 if (pos == std::string::npos) return NaN;
381 // Will throw a boost::bad_lexical_cast if it fails.
382 return parseDecimalNumber(s.begin() + pos, s.end());
385 catch (boost::bad_lexical_cast&) {
386 // There is no standard textual representation of infinity
387 // in the C++ standard, so our conversion function an
388 // exception for 'inf', just like for any other
389 // non-numerical text. This is correct behaviour.
390 return NaN;
394 case NULLTYPE:
395 case UNDEFINED:
397 // Evan: from my tests
398 // Martin: FlashPlayer6 gives 0; FP9 gives NaN.
399 return (swfversion >= 7 ? NaN : 0);
402 case BOOLEAN:
403 return getBool() ? 1 : 0;
405 case NUMBER:
406 return getNum();
408 case OBJECT:
410 // @@ Moock says the result here should be
411 // "the return value of the object's valueOf()
412 // method".
414 // Arrays and Movieclips should return NaN.
415 try {
416 as_value ret = to_primitive(NUMBER);
417 return ret.to_number();
419 catch (ActionTypeError& e)
421 #if GNASH_DEBUG_CONVERSION_TO_PRIMITIVE
422 log_debug(_("to_primitive(%s, NUMBER) threw an "
423 "ActionTypeError %s"), *this, e.what());
424 #endif
425 if (is_function() && swfversion < 6) {
426 return 0;
429 return NaN;
433 case DISPLAYOBJECT:
435 // This is tested, no valueOf is going
436 // to be invoked for movieclips.
437 return NaN;
440 default:
441 // Other object types should return NaN.
442 return NaN;
447 // Conversion to boolean
448 bool
449 as_value::to_bool() const
451 const int version = VM::get().getSWFVersion();
452 switch (_type)
454 case STRING:
456 if (version >= 7) return !getStr().empty();
457 const double num = to_number();
458 return num && !isNaN(num);
460 case NUMBER:
462 const double d = getNum();
463 // see testsuite/swfdec/if-6.swf
464 return d && ! isNaN(d);
466 case BOOLEAN:
467 return getBool();
468 case OBJECT:
469 return true;
470 case DISPLAYOBJECT:
471 return true;
472 default:
473 assert(_type == UNDEFINED || _type == NULLTYPE || is_exception());
474 return false;
478 // Return value as an object.
479 as_object*
480 as_value::to_object(Global_as& global) const
482 switch (_type)
484 case OBJECT:
485 return getObj();
487 case DISPLAYOBJECT:
488 return getObject(toDisplayObject());
490 case STRING:
491 return global.createString(getStr());
493 case NUMBER:
494 return global.createNumber(getNum());
496 case BOOLEAN:
497 return global.createBoolean(getBool());
499 default:
500 // Invalid to convert exceptions.
501 return NULL;
505 MovieClip*
506 as_value::toMovieClip(bool allowUnloaded) const
508 if ( _type != DISPLAYOBJECT ) return 0;
510 DisplayObject *ch = getCharacter(allowUnloaded);
511 if ( ! ch ) return 0;
512 return ch->to_movie();
515 DisplayObject*
516 as_value::toDisplayObject(bool allowUnloaded) const
518 if (_type != DISPLAYOBJECT) return NULL;
519 return getCharacter(allowUnloaded);
522 // Return value as an ActionScript function. Returns NULL if value is
523 // not an ActionScript function.
524 as_function*
525 as_value::to_function() const
527 if (_type == OBJECT) {
528 return getObj()->to_function();
531 return 0;
534 void
535 as_value::set_undefined()
537 _type = UNDEFINED;
538 _value = boost::blank();
541 void
542 as_value::set_null()
544 _type = NULLTYPE;
545 _value = boost::blank();
548 void
549 as_value::set_as_object(as_object* obj)
551 if ( ! obj )
553 set_null();
554 return;
556 if (obj->displayObject()) {
557 // The static cast is fine as long as the as_object is genuinely
558 // a DisplayObject.
559 _type = DISPLAYOBJECT;
560 _value = CharacterProxy(obj->displayObject());
561 return;
564 if (_type != OBJECT || getObj() != obj) {
565 _type = OBJECT;
566 _value = obj;
570 bool
571 as_value::equals(const as_value& v) const
574 // First compare values of the same type.
575 if (_type == v._type) return equalsSameType(v);
577 // Then compare booleans.
578 if (is_bool()) return compareBoolean(*this, v);
579 if (v.is_bool()) return compareBoolean(v, *this);
581 // Then compare any other primitive, including null and undefined, with
582 // an object.
583 if (!is_object() && v.is_object()) {
584 return objectEqualsPrimitive(v, *this);
587 if (is_object() && !v.is_object()) {
588 return objectEqualsPrimitive(*this, v);
591 // Remaining null or undefined values only equate to other null or
592 // undefined values.
593 const bool null = (is_undefined() || is_null());
594 const bool v_null = (v.is_undefined() || v.is_null());
595 if (null || v_null) return null == v_null;
597 // Now compare a number with a string.
598 if (is_number() && v.is_string()) return stringEqualsNumber(v, *this);
599 if (is_string() && v.is_number()) return stringEqualsNumber(*this, v);
601 // Finally compare non-identical objects.
602 as_value p = *this;
603 as_value vp = v;
605 try {
606 p = to_primitive(NUMBER);
608 catch (ActionTypeError& e) {}
610 try {
611 vp = v.to_primitive(NUMBER);
613 catch (ActionTypeError& e) {}
615 // No conversion took place; the result is false
616 if (strictly_equals(p) && v.strictly_equals(vp)) {
617 return false;
620 return p.equals(vp);
623 const char*
624 as_value::typeOf() const
626 switch (_type)
628 case UNDEFINED:
629 return "undefined";
631 case STRING:
632 return "string";
634 case NUMBER:
635 return "number";
637 case BOOLEAN:
638 return "boolean";
640 case OBJECT:
641 return is_function() ? "function" : "object";
643 case DISPLAYOBJECT:
645 DisplayObject* ch = getCharacter();
646 if ( ! ch ) return "movieclip"; // dangling
647 if ( ch->to_movie() ) return "movieclip"; // bound to movieclip
648 return "object"; // bound to some other DisplayObject
651 case NULLTYPE:
652 return "null";
654 default:
655 if (is_exception()) return "exception";
656 std::abort();
657 return 0;
661 bool
662 as_value::equalsSameType(const as_value& v) const
664 assert(_type == v._type);
666 switch (_type)
668 case UNDEFINED:
669 case NULLTYPE:
670 return true;
672 case OBJECT:
673 case BOOLEAN:
674 case STRING:
675 return _value == v._value;
677 case DISPLAYOBJECT:
678 return toDisplayObject() == v.toDisplayObject();
680 case NUMBER:
682 const double a = getNum();
683 const double b = v.getNum();
684 if (isNaN(a) && isNaN(b)) return true;
685 return a == b;
687 default:
688 if (is_exception()) return false;
691 std::abort();
692 return false;
695 bool
696 as_value::strictly_equals(const as_value& v) const
698 if ( _type != v._type ) return false;
699 return equalsSameType(v);
702 std::string
703 as_value::toDebugString() const
705 boost::format ret;
707 switch (_type)
709 case UNDEFINED:
710 return "[undefined]";
711 case NULLTYPE:
712 return "[null]";
713 case BOOLEAN:
714 ret = boost::format("[bool:%s]") % (getBool() ? "true" : "false");
715 return ret.str();
716 case OBJECT:
718 as_object* obj = getObj();
719 ret = boost::format("[object(%s):%p]") % typeName(*obj) %
720 static_cast<void*>(obj);
721 return ret.str();
723 case STRING:
724 return "[string:" + getStr() + "]";
725 case NUMBER:
727 std::stringstream stream;
728 stream << getNum();
729 return "[number:" + stream.str() + "]";
731 case DISPLAYOBJECT:
733 const CharacterProxy& sp = getCharacterProxy();
734 if (sp.isDangling()) {
735 DisplayObject* rebound = sp.get();
736 if (rebound) {
737 ret = boost::format("[rebound %s(%s):%p]") %
738 typeName(*rebound) % sp.getTarget() %
739 static_cast<void*>(rebound);
741 else {
742 ret = boost::format("[dangling DisplayObject:%s]") %
743 sp.getTarget();
746 else {
747 DisplayObject* ch = sp.get();
748 ret = boost::format("[%s(%s):%p]") % typeName(*ch) %
749 sp.getTarget() % static_cast<void*>(ch);
751 return ret.str();
753 default:
754 if (is_exception()) return "[exception]";
755 std::abort();
759 void
760 as_value::operator=(const as_value& v)
762 _type = v._type;
763 _value = v._value;
766 void
767 as_value::setReachable() const
769 switch (_type)
771 case OBJECT:
773 as_object* op = getObj();
774 if (op) op->setReachable();
775 break;
777 case DISPLAYOBJECT:
779 CharacterProxy sp = getCharacterProxy();
780 sp.setReachable();
781 break;
783 default: break;
787 as_object*
788 as_value::getObj() const
790 assert(_type == OBJECT);
791 return boost::get<as_object*>(_value);
794 CharacterProxy
795 as_value::getCharacterProxy() const
797 assert(_type == DISPLAYOBJECT);
798 return boost::get<CharacterProxy>(_value);
801 DisplayObject*
802 as_value::getCharacter(bool allowUnloaded) const
804 return getCharacterProxy().get(allowUnloaded);
807 void
808 as_value::set_string(const std::string& str)
810 _type = STRING;
811 _value = str;
814 void
815 as_value::set_double(double val)
817 _type = NUMBER;
818 _value = val;
821 void
822 as_value::set_bool(bool val)
824 _type = BOOLEAN;
825 _value = val;
828 as_value::as_value()
830 _type(UNDEFINED),
831 _value(boost::blank())
835 as_value::as_value(const as_value& v)
837 _type(v._type),
838 _value(v._value)
842 as_value::as_value(const char* str)
844 _type(STRING),
845 _value(std::string(str))
849 as_value::as_value(const std::string& str)
851 _type(STRING),
852 _value(str)
856 as_value::as_value(double num)
858 _type(NUMBER),
859 _value(num)
863 as_value::as_value(as_object* obj)
865 _type(UNDEFINED)
867 set_as_object(obj);
870 bool
871 as_value::is_function() const
873 return _type == OBJECT && getObj()->to_function();
876 bool
877 as_value::writeAMF0(amf::Writer& w) const
880 assert (!is_exception());
882 switch (_type)
884 default:
885 log_unimpl(_("serialization of as_value of type %d"), _type);
886 return false;
888 case OBJECT:
889 if (is_function()) return false;
890 return w.writeObject(getObj());
892 case STRING:
893 return w.writeString(getStr());
895 case NUMBER:
896 return w.writeNumber(getNum());
898 case DISPLAYOBJECT:
899 case UNDEFINED:
900 return w.writeUndefined();
902 case NULLTYPE:
903 return w.writeNull();
905 case BOOLEAN:
906 return w.writeBoolean(getBool());
911 boost::int32_t
912 toInt(const as_value& val)
914 const double d = val.to_number();
916 if (!isFinite(d)) return 0;
918 return truncateToInt(d);
921 bool
922 parseNonDecimalInt(const std::string& s, double& d, bool whole)
924 const std::string::size_type slen = s.length();
926 // "0#" would still be octal, but has the same value as a decimal.
927 if (slen < 3) return false;
929 bool negative = false;
931 if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
932 // The only legitimate place for a '-' is after 0x. If it's a
933 // '+' we don't care, as it won't disturb the conversion.
934 std::string::size_type start = 2;
935 if (s[2] == '-') {
936 negative = true;
937 ++start;
939 d = parsePositiveInt(s.substr(start), BASE_HEX, whole);
940 if (negative) d = -d;
941 return true;
943 else if ((s[0] == '0' || ((s[0] == '-' || s[0] == '+') && s[1] == '0')) &&
944 s.find_first_not_of("01234567", 1) == std::string::npos) {
946 std::string::size_type start = 0;
947 if (s[0] == '-') {
948 negative = true;
949 ++start;
951 d = parsePositiveInt(s.substr(start), BASE_OCT, whole);
952 if (negative) d = -d;
953 return true;
956 return false;
960 std::string
961 doubleToString(double val, int radix)
963 // Examples:
965 // e.g. for 9*.1234567890123456789:
966 // 9999.12345678901
967 // 99999.123456789
968 // 999999.123456789
969 // 9999999.12345679
970 // [...]
971 // 999999999999.123
972 // 9999999999999.12
973 // 99999999999999.1
974 // 999999999999999
975 // 1e+16
976 // 1e+17
978 // For 1*.111111111111111111111111111111111111:
979 // 1111111111111.11
980 // 11111111111111.1
981 // 111111111111111
982 // 1.11111111111111e+15
983 // 1.11111111111111e+16
985 // For 1.234567890123456789 * 10^-i:
986 // 1.23456789012346
987 // 0.123456789012346
988 // 0.0123456789012346
989 // 0.00123456789012346
990 // 0.000123456789012346
991 // 0.0000123456789012346
992 // 0.00000123456789012346
993 // 1.23456789012346e-6
994 // 1.23456789012346e-7
996 // Handle non-numeric values.
997 if (isNaN(val)) return "NaN";
999 if (isInf(val)) return val < 0 ? "-Infinity" : "Infinity";
1001 if (val == 0.0 || val == -0.0) return "0";
1003 std::ostringstream ostr;
1005 if (radix == 10) {
1007 // ActionScript always expects dot as decimal point.
1008 ostr.imbue(std::locale::classic());
1010 // force to decimal notation for this range (because the
1011 // reference player does)
1012 if (std::abs(val) < 0.0001 && std::abs(val) >= 0.00001) {
1014 // All nineteen digits (4 zeros + up to 15 significant digits)
1015 ostr << std::fixed << std::setprecision(19) << val;
1017 std::string str = ostr.str();
1019 // Because 'fixed' also adds trailing zeros, remove them.
1020 std::string::size_type pos = str.find_last_not_of('0');
1021 if (pos != std::string::npos) {
1022 str.erase(pos + 1);
1024 return str;
1027 ostr << std::setprecision(15) << val;
1029 std::string str = ostr.str();
1031 // Remove a leading zero from 2-digit exponent if any
1032 std::string::size_type pos = str.find("e", 0);
1034 if (pos != std::string::npos && str.at(pos + 2) == '0') {
1035 str.erase(pos + 2, 1);
1038 return str;
1041 // Radix isn't 10
1042 bool negative = (val < 0);
1043 if (negative) val = -val;
1045 double left = std::floor(val);
1046 if (left < 1) return "0";
1048 std::string str;
1049 const std::string digits = "0123456789abcdefghijklmnopqrstuvwxyz";
1051 // Construct the string backwards for speed, then reverse.
1052 while (left) {
1053 double n = left;
1054 left = std::floor(left / radix);
1055 n -= (left * radix);
1056 str.push_back(digits[static_cast<int>(n)]);
1058 if (negative) str.push_back('-');
1060 std::reverse(str.begin(), str.end());
1062 return str;
1065 /// Force type to number.
1066 as_value&
1067 convertToNumber(as_value& v, VM& /*vm*/)
1069 v.set_double(v.to_number());
1070 return v;
1073 /// Force type to string.
1074 as_value&
1075 convertToString(as_value& v, VM& vm)
1077 v.set_string(v.to_string(vm.getSWFVersion()));
1078 return v;
1081 /// Force type to bool.
1082 as_value&
1083 convertToBoolean(as_value& v, VM& /*vm*/)
1085 v.set_bool(v.to_bool());
1086 return v;
1089 as_value&
1090 convertToPrimitive(as_value& v, VM& vm)
1092 const as_value::AsType t(v.defaultPrimitive(vm.getSWFVersion()));
1093 v = v.to_primitive(t);
1094 return v;
1097 namespace {
1099 /// Checks for equality between an object value and a primitive value
1101 /// @param obj An as_value of object type. Callers must ensure this
1102 /// condition is met.
1103 /// @param prim An as_value of primitive type. Callers must ensure this
1104 /// condition is met.
1106 /// This is a function try-block.
1107 bool
1108 objectEqualsPrimitive(const as_value& obj, const as_value& prim)
1109 try {
1111 assert(obj.is_object());
1112 assert(!prim.is_object());
1114 as_value tmp = obj.to_primitive(as_value::NUMBER);
1115 if (obj.strictly_equals(tmp)) return false;
1116 return tmp.equals(prim);
1118 catch (const ActionTypeError&) {
1119 return false;
1122 /// @param boolean A boolean as_value
1123 /// @param other An as_value of any type.
1124 bool
1125 compareBoolean(const as_value& boolean, const as_value& other)
1127 assert(boolean.is_bool());
1128 return as_value(boolean.to_number()).equals(other);
1131 bool
1132 stringEqualsNumber(const as_value& str, const as_value& num) {
1133 assert(num.is_number());
1134 assert(str.is_string());
1135 const double n = str.to_number();
1136 if (!isFinite(n)) return false;
1137 return num.strictly_equals(n);
1141 /// Returns a member only if it is an object.
1142 inline bool
1143 findMethod(as_object& obj, string_table::key m, as_value& ret)
1145 return obj.get_member(m, &ret) && ret.is_object();
1148 /// Truncates a double to a 32-bit unsigned int.
1150 /// In fact, it is a 32-bit unsigned int with an additional sign, cast
1151 /// to an unsigned int. Not sure what the sense is, but that's how it works:
1153 /// 0xffffffff is interpreted as -1, -0xffffffff as 1.
1154 boost::int32_t
1155 truncateToInt(double d)
1157 if (d < 0) {
1158 return - static_cast<boost::uint32_t>(std::fmod(-d, 4294967296.0));
1161 return static_cast<boost::uint32_t>(std::fmod(d, 4294967296.0));
1164 } // unnamed namespace
1167 } // namespace gnash
1170 // Local Variables:
1171 // mode: C++
1172 // indent-tabs-mode: nil
1173 // End: