drop libxv, add lsb-release
[gnash.git] / libcore / as_value.cpp
blobbdab19627f4aa18c6aff12ddc8270a80443129a4
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 int version);
68 bool stringEqualsNumber(const as_value& str, const as_value& num,
69 int version);
70 bool compareBoolean(const as_value& boolean, const as_value& other,
71 int version);
72 inline bool findMethod(as_object& obj, string_table::key m, as_value& ret);
73 template<typename T> as_object* constructObject(VM& vm, const T& arg,
74 string_table::key className);
77 namespace {
79 enum Base
81 BASE_OCT,
82 BASE_HEX
86 /// Converts a string to a uint32_t cast to an int32_t.
88 /// @param whole When true, any string that isn't wholly valid is rejected.
89 /// @param base The base (8 or 16) to use.
90 /// @param s The string to parse.
91 /// @return The converted number.
92 boost::int32_t
93 parsePositiveInt(const std::string& s, Base base, bool whole = true)
96 std::istringstream is(s);
97 boost::uint32_t target;
99 switch (base)
101 case BASE_OCT:
102 is >> std::oct;
103 break;
104 case BASE_HEX:
105 is >> std::hex;
106 break;
109 char c;
111 // If the conversion fails, or if the whole string must be convertible and
112 // some DisplayObjects are left, throw an exception.
113 if (!(is >> target) || (whole && is.get(c))) {
114 throw boost::bad_lexical_cast();
117 return target;
120 struct
121 NonNumericChar
123 bool operator()(char c) {
124 return (!std::isdigit(c) && c != '.' && c != '-' && c != '+');
128 /// Omit an empty exponent that is valid in ActionScript but not in C++.
130 /// This function throws a boost::bad_lexical_cast if it finds an invalid
131 /// exponent to avoid attempting an extraction when it will definitely fail.
133 /// A successful return from this function does not mean the exponent is
134 /// valid, only that the result of stringstream's conversion will mirror
135 /// AS behaviour.
137 /// @param si An iterator pointing to the position after an exponent sign.
138 /// @param last The end of the string to extract. If we have an exponent
139 /// with no following digit, this iterator is moved to
140 /// a position before the exponent sign.
141 void
142 validateExponent(std::string::const_iterator si,
143 std::string::const_iterator& last)
146 // Check for exponent with no following character. Depending on the
147 // version of gcc, extraction may be rejected (probably more correct) or
148 // accepted as a valid exponent (what ActionScript wants).
149 // In this case we remove the exponent to get the correct behaviour
150 // on all compilers.
151 if (si == last) {
152 --last;
153 return;
156 // Exponents with a following '-' or '+' are also valid if they end the
157 // string. It's unlikely that any version of gcc allowed this.
158 if (*si == '-' || *si == '+') {
159 ++si;
160 if (si == last) {
161 last -= 2;
162 return;
166 // An exponent ("e", "e-", or "e+") followed by a non digit is invalid.
167 if (!std::isdigit(*si)) {
168 throw boost::bad_lexical_cast();
173 /// Convert a string to a double if the complete string can be converted.
175 /// This follows the conditions of the standard C locale for numbers except
176 /// that an exponent signifier with no following digit (e.g. "2e") is
177 /// considered valid. Moreover, "2e-" is also considered valid.
179 /// This function scans the string twice (once for verification, once for
180 /// extraction) and copies it once (for extraction).
181 double
182 parseDecimalNumber(std::string::const_iterator start,
183 std::string::const_iterator last)
185 assert(start != last);
187 // Find the first position that is not a numeric character ('e' or 'E' not
188 // included). Even if no invalid character is found, it does not mean
189 // that the number is valid ("++++---" would pass the test).
190 std::string::const_iterator si =
191 std::find_if(start, last, NonNumericChar());
193 if (si != last) {
194 // If this character is not an exponent sign, the number is malformed.
195 if (*si != 'e' && *si != 'E') throw boost::bad_lexical_cast();
196 /// Move the last iterator to point before empty exponents.
197 else validateExponent(si + 1, last);
200 return boost::lexical_cast<double>(std::string(start, last));
203 } // anonymous namespace
205 // Conversion to const std::string&.
206 std::string
207 as_value::to_string(int version) const
209 switch (_type)
211 case STRING:
212 return getStr();
213 case DISPLAYOBJECT:
215 const CharacterProxy& sp = getCharacterProxy();
216 if (!sp.get()) return "";
217 return sp.getTarget();
219 case NUMBER:
220 return doubleToString(getNum());
221 case UNDEFINED:
222 if (version <= 6) return "";
223 return "undefined";
224 case NULLTYPE:
225 return "null";
226 case BOOLEAN:
227 return getBool() ? "true" : "false";
228 case OBJECT:
230 as_object* obj = getObj();
231 String_as* s;
232 if (isNativeType(obj, s)) return s->value();
234 try {
235 as_value ret = to_primitive(STRING);
236 // This additional is_string test is NOT compliant with ECMA-262
237 // specification, but seems required for compatibility with the
238 // reference player.
239 if (ret.is_string()) return ret.getStr();
241 catch (ActionTypeError& e) {}
243 return is_function() ? "[type Function]" : "[type Object]";
247 default:
248 return "[exception]";
253 as_value::AsType
254 as_value::defaultPrimitive(int version) const
256 if (_type == OBJECT && version > 5) {
257 Date_as* d;
258 if (isNativeType(getObj(), d)) return STRING;
260 return NUMBER;
263 // Conversion to primitive value.
264 as_value
265 as_value::to_primitive(AsType hint) const
267 if (_type != OBJECT) return *this;
269 #if GNASH_DEBUG_CONVERSION_TO_PRIMITIVE
270 log_debug("to_primitive(%s)", hint==NUMBER ? "NUMBER" : "STRING");
271 #endif
273 // TODO: implement as_object::DefaultValue (ECMA-262 - 8.6.2.6)
275 as_value method;
276 as_object* obj(0);
278 if (hint == NUMBER) {
279 assert(_type == OBJECT);
280 obj = getObj();
282 if (!findMethod(*obj, NSV::PROP_VALUE_OF, method)) {
283 // Returning undefined here instead of throwing
284 // a TypeError passes tests in actionscript.all/Object.as
285 // and many swfdec tests, with no new failures (though
286 // perhaps we aren't testing enough).
287 return as_value();
290 else {
291 assert(hint == STRING);
292 assert(_type == OBJECT);
293 obj = getObj();
295 // @@ Moock says, "the value that results from
296 // calling toString() on the object".
297 if (!findMethod(*obj, NSV::PROP_TO_STRING, method) &&
298 !findMethod(*obj, NSV::PROP_VALUE_OF, method)) {
299 throw ActionTypeError();
303 assert(obj);
305 as_environment env(getVM(*obj));
306 fn_call::Args args;
307 as_value ret = invoke(method, env, obj, args);
309 #if GNASH_DEBUG_CONVERSION_TO_PRIMITIVE
310 log_debug("to_primitive: method call returned %s", ret);
311 #endif
313 if (ret._type == OBJECT) {
314 throw ActionTypeError();
316 return ret;
319 double
320 as_value::to_number(const int version) const
323 switch (_type) {
324 case STRING:
326 const std::string& s = getStr();
327 if ( s.empty() ) {
328 return version >= 5 ? NaN : 0.0;
331 if (version <= 4)
333 // For SWF4, any valid number before non-numerical
334 // DisplayObjects is returned, including exponent, positive
335 // and negative signs and whitespace before.
336 double d = 0;
337 std::istringstream is(s);
338 is >> d;
339 return d;
342 try {
344 if (version > 5) {
345 double d;
346 // Will throw if invalid.
347 if (parseNonDecimalInt(s, d)) return d;
350 // @@ Moock says the rule here is: if the
351 // string is a valid float literal, then it
352 // gets converted; otherwise it is set to NaN.
353 // Valid for SWF5 and above.
354 const std::string::size_type pos =
355 s.find_first_not_of(" \r\n\t");
357 if (pos == std::string::npos) return NaN;
359 // Will throw a boost::bad_lexical_cast if it fails.
360 return parseDecimalNumber(s.begin() + pos, s.end());
363 catch (boost::bad_lexical_cast&) {
364 // There is no standard textual representation of infinity
365 // in the C++ standard, so our conversion function an
366 // exception for 'inf', just like for any other
367 // non-numerical text. This is correct behaviour.
368 return NaN;
372 case NULLTYPE:
373 case UNDEFINED:
375 // Evan: from my tests
376 // Martin: FlashPlayer6 gives 0; FP9 gives NaN.
377 return (version >= 7 ? NaN : 0);
380 case BOOLEAN:
381 return getBool() ? 1 : 0;
383 case NUMBER:
384 return getNum();
386 case OBJECT:
388 // @@ Moock says the result here should be
389 // "the return value of the object's valueOf()
390 // method".
392 // Arrays and Movieclips should return NaN.
393 try {
394 as_value ret = to_primitive(NUMBER);
395 return ret.to_number(version);
397 catch (ActionTypeError& e)
399 #if GNASH_DEBUG_CONVERSION_TO_PRIMITIVE
400 log_debug(_("to_primitive(%s, NUMBER) threw an "
401 "ActionTypeError %s"), *this, e.what());
402 #endif
403 if (is_function() && version < 6) {
404 return 0;
407 return NaN;
411 case DISPLAYOBJECT:
413 // This is tested, no valueOf is going
414 // to be invoked for movieclips.
415 return NaN;
418 default:
419 // Other object types should return NaN.
420 return NaN;
424 // Conversion to boolean
425 bool
426 as_value::to_bool(const int version) const
428 switch (_type)
430 case STRING:
432 if (version >= 7) return !getStr().empty();
433 const double num = to_number(version);
434 return num && !isNaN(num);
436 case NUMBER:
438 const double d = getNum();
439 // see testsuite/swfdec/if-6.swf
440 return d && ! isNaN(d);
442 case BOOLEAN:
443 return getBool();
444 case OBJECT:
445 return true;
446 case DISPLAYOBJECT:
447 return true;
448 default:
449 assert(_type == UNDEFINED || _type == NULLTYPE || is_exception());
450 return false;
454 // Return value as an object.
455 as_object*
456 as_value::to_object(VM& vm) const
459 switch (_type)
461 case OBJECT:
462 return getObj();
464 case DISPLAYOBJECT:
465 return getObject(toDisplayObject());
467 case STRING:
468 return constructObject(vm, getStr(), NSV::CLASS_STRING);
470 case NUMBER:
471 return constructObject(vm, getNum(), NSV::CLASS_NUMBER);
473 case BOOLEAN:
474 return constructObject(vm, getBool(), NSV::CLASS_BOOLEAN);
476 default:
477 // Invalid to convert exceptions.
478 return NULL;
482 MovieClip*
483 as_value::toMovieClip(bool allowUnloaded) const
485 if (_type != DISPLAYOBJECT) return 0;
487 DisplayObject *ch = getCharacter(allowUnloaded);
488 if (!ch) return 0;
489 return ch->to_movie();
492 DisplayObject*
493 as_value::toDisplayObject(bool allowUnloaded) const
495 if (_type != DISPLAYOBJECT) return 0;
496 return getCharacter(allowUnloaded);
499 // Return value as an ActionScript function. Returns NULL if value is
500 // not an ActionScript function.
501 as_function*
502 as_value::to_function() const
504 if (_type == OBJECT) {
505 return getObj()->to_function();
508 return 0;
511 as_object*
512 as_value::get_object() const
514 if (_type == OBJECT) {
515 return getObj();
518 return 0;
521 void
522 as_value::set_undefined()
524 _type = UNDEFINED;
525 _value = boost::blank();
528 void
529 as_value::set_null()
531 _type = NULLTYPE;
532 _value = boost::blank();
535 void
536 as_value::set_as_object(as_object* obj)
538 if (!obj)
540 set_null();
541 return;
543 if (obj->displayObject()) {
544 // The static cast is fine as long as the as_object is genuinely
545 // a DisplayObject.
546 _type = DISPLAYOBJECT;
547 _value = CharacterProxy(obj->displayObject(), getRoot(*obj));
548 return;
551 if (_type != OBJECT || getObj() != obj) {
552 _type = OBJECT;
553 _value = obj;
557 bool
558 as_value::equals(const as_value& v, int version) const
561 // First compare values of the same type.
562 if (_type == v._type) return equalsSameType(v);
564 // Then compare booleans.
565 if (is_bool()) return compareBoolean(*this, v, version);
566 if (v.is_bool()) return compareBoolean(v, *this, version);
568 // Then compare any other primitive, including null and undefined, with
569 // an object.
570 if (!is_object() && v.is_object()) {
571 return objectEqualsPrimitive(v, *this, version);
574 if (is_object() && !v.is_object()) {
575 return objectEqualsPrimitive(*this, v, version);
578 // Remaining null or undefined values only equate to other null or
579 // undefined values.
580 const bool null = (is_undefined() || is_null());
581 const bool v_null = (v.is_undefined() || v.is_null());
582 if (null || v_null) return null == v_null;
584 // Now compare a number with a string.
585 if (is_number() && v.is_string()) {
586 return stringEqualsNumber(v, *this, version);
588 if (is_string() && v.is_number()) {
589 return stringEqualsNumber(*this, v, version);
592 // Finally compare non-identical objects.
593 as_value p = *this;
594 as_value vp = v;
596 try {
597 p = to_primitive(NUMBER);
599 catch (ActionTypeError& e) {}
601 try {
602 vp = v.to_primitive(NUMBER);
604 catch (ActionTypeError& e) {}
606 // No conversion took place; the result is false
607 if (strictly_equals(p) && v.strictly_equals(vp)) {
608 return false;
611 return p.equals(vp, version);
614 const char*
615 as_value::typeOf() const
617 switch (_type)
619 case UNDEFINED:
620 return "undefined";
622 case STRING:
623 return "string";
625 case NUMBER:
626 return "number";
628 case BOOLEAN:
629 return "boolean";
631 case OBJECT:
632 return is_function() ? "function" : "object";
634 case DISPLAYOBJECT:
636 DisplayObject* ch = getCharacter();
637 if ( ! ch ) return "movieclip"; // dangling
638 if ( ch->to_movie() ) return "movieclip"; // bound to movieclip
639 return "object"; // bound to some other DisplayObject
642 case NULLTYPE:
643 return "null";
645 default:
646 if (is_exception()) return "exception";
647 std::abort();
648 return 0;
652 bool
653 as_value::equalsSameType(const as_value& v) const
655 assert(_type == v._type);
657 switch (_type)
659 case UNDEFINED:
660 case NULLTYPE:
661 return true;
663 case OBJECT:
664 case BOOLEAN:
665 case STRING:
666 return _value == v._value;
668 case DISPLAYOBJECT:
669 return toDisplayObject() == v.toDisplayObject();
671 case NUMBER:
673 const double a = getNum();
674 const double b = v.getNum();
675 if (isNaN(a) && isNaN(b)) return true;
676 return a == b;
678 default:
679 if (is_exception()) return false;
682 std::abort();
683 return false;
686 bool
687 as_value::strictly_equals(const as_value& v) const
689 if ( _type != v._type ) return false;
690 return equalsSameType(v);
693 void
694 as_value::setReachable() const
696 switch (_type)
698 case OBJECT:
700 as_object* op = getObj();
701 if (op) op->setReachable();
702 break;
704 case DISPLAYOBJECT:
706 CharacterProxy sp = getCharacterProxy();
707 sp.setReachable();
708 break;
710 default: break;
714 as_object*
715 as_value::getObj() const
717 assert(_type == OBJECT);
718 return boost::get<as_object*>(_value);
721 CharacterProxy
722 as_value::getCharacterProxy() const
724 assert(_type == DISPLAYOBJECT);
725 return boost::get<CharacterProxy>(_value);
728 DisplayObject*
729 as_value::getCharacter(bool allowUnloaded) const
731 return getCharacterProxy().get(allowUnloaded);
734 void
735 as_value::set_string(const std::string& str)
737 _type = STRING;
738 _value = str;
741 void
742 as_value::set_double(double val)
744 _type = NUMBER;
745 _value = val;
748 void
749 as_value::set_bool(bool val)
751 _type = BOOLEAN;
752 _value = val;
755 bool
756 as_value::is_function() const
758 return _type == OBJECT && getObj()->to_function();
761 bool
762 as_value::writeAMF0(amf::Writer& w) const
765 assert (!is_exception());
767 switch (_type)
769 default:
770 log_unimpl(_("serialization of as_value of type %d"), _type);
771 return false;
773 case OBJECT:
774 if (is_function()) return false;
775 return w.writeObject(getObj());
777 case STRING:
778 return w.writeString(getStr());
780 case NUMBER:
781 return w.writeNumber(getNum());
783 case DISPLAYOBJECT:
784 case UNDEFINED:
785 return w.writeUndefined();
787 case NULLTYPE:
788 return w.writeNull();
790 case BOOLEAN:
791 return w.writeBoolean(getBool());
795 bool
796 parseNonDecimalInt(const std::string& s, double& d, bool whole)
798 const std::string::size_type slen = s.length();
800 // "0#" would still be octal, but has the same value as a decimal.
801 if (slen < 3) return false;
803 bool negative = false;
805 if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
806 // The only legitimate place for a '-' is after 0x. If it's a
807 // '+' we don't care, as it won't disturb the conversion.
808 std::string::size_type start = 2;
809 if (s[2] == '-') {
810 negative = true;
811 ++start;
813 d = parsePositiveInt(s.substr(start), BASE_HEX, whole);
814 if (negative) d = -d;
815 return true;
817 else if ((s[0] == '0' || ((s[0] == '-' || s[0] == '+') && s[1] == '0')) &&
818 s.find_first_not_of("01234567", 1) == std::string::npos) {
820 std::string::size_type start = 0;
821 if (s[0] == '-') {
822 negative = true;
823 ++start;
825 d = parsePositiveInt(s.substr(start), BASE_OCT, whole);
826 if (negative) d = -d;
827 return true;
830 return false;
834 std::string
835 doubleToString(double val, int radix)
837 // Examples:
839 // e.g. for 9*.1234567890123456789:
840 // 9999.12345678901
841 // 99999.123456789
842 // 999999.123456789
843 // 9999999.12345679
844 // [...]
845 // 999999999999.123
846 // 9999999999999.12
847 // 99999999999999.1
848 // 999999999999999
849 // 1e+16
850 // 1e+17
852 // For 1*.111111111111111111111111111111111111:
853 // 1111111111111.11
854 // 11111111111111.1
855 // 111111111111111
856 // 1.11111111111111e+15
857 // 1.11111111111111e+16
859 // For 1.234567890123456789 * 10^-i:
860 // 1.23456789012346
861 // 0.123456789012346
862 // 0.0123456789012346
863 // 0.00123456789012346
864 // 0.000123456789012346
865 // 0.0000123456789012346
866 // 0.00000123456789012346
867 // 1.23456789012346e-6
868 // 1.23456789012346e-7
870 // Handle non-numeric values.
871 if (isNaN(val)) return "NaN";
873 if (isInf(val)) return val < 0 ? "-Infinity" : "Infinity";
875 if (val == 0.0 || val == -0.0) return "0";
877 std::ostringstream ostr;
879 if (radix == 10) {
881 // ActionScript always expects dot as decimal point.
882 ostr.imbue(std::locale::classic());
884 // force to decimal notation for this range (because the
885 // reference player does)
886 if (std::abs(val) < 0.0001 && std::abs(val) >= 0.00001) {
888 // All nineteen digits (4 zeros + up to 15 significant digits)
889 ostr << std::fixed << std::setprecision(19) << val;
891 std::string str = ostr.str();
893 // Because 'fixed' also adds trailing zeros, remove them.
894 std::string::size_type pos = str.find_last_not_of('0');
895 if (pos != std::string::npos) {
896 str.erase(pos + 1);
898 return str;
901 ostr << std::setprecision(15) << val;
903 std::string str = ostr.str();
905 // Remove a leading zero from 2-digit exponent if any
906 std::string::size_type pos = str.find("e", 0);
908 if (pos != std::string::npos && str.at(pos + 2) == '0') {
909 str.erase(pos + 2, 1);
912 return str;
915 // Radix isn't 10
916 bool negative = (val < 0);
917 if (negative) val = -val;
919 double left = std::floor(val);
920 if (left < 1) return "0";
922 std::string str;
923 const std::string digits = "0123456789abcdefghijklmnopqrstuvwxyz";
925 // Construct the string backwards for speed, then reverse.
926 while (left) {
927 double n = left;
928 left = std::floor(left / radix);
929 n -= (left * radix);
930 str.push_back(digits[static_cast<int>(n)]);
932 if (negative) str.push_back('-');
934 std::reverse(str.begin(), str.end());
936 return str;
939 namespace {
941 /// Checks for equality between an object value and a primitive value
943 /// @param obj An as_value of object type. Callers must ensure this
944 /// condition is met.
945 /// @param prim An as_value of primitive type. Callers must ensure this
946 /// condition is met.
948 /// This is a function try-block.
949 bool
950 objectEqualsPrimitive(const as_value& obj, const as_value& prim, int version)
951 try {
953 assert(obj.is_object());
954 assert(!prim.is_object());
956 as_value tmp = obj.to_primitive(as_value::NUMBER);
957 if (obj.strictly_equals(tmp)) return false;
958 return tmp.equals(prim, version);
960 catch (const ActionTypeError&) {
961 return false;
964 /// @param boolean A boolean as_value
965 /// @param other An as_value of any type.
966 bool
967 compareBoolean(const as_value& boolean, const as_value& other, int version)
969 assert(boolean.is_bool());
970 return as_value(boolean.to_number(version)).equals(other, version);
973 bool
974 stringEqualsNumber(const as_value& str, const as_value& num, int version)
976 assert(num.is_number());
977 assert(str.is_string());
978 const double n = str.to_number(version);
979 if (!isFinite(n)) return false;
980 return num.strictly_equals(n);
984 /// Returns a member only if it is an object.
985 inline bool
986 findMethod(as_object& obj, string_table::key m, as_value& ret)
988 return obj.get_member(m, &ret) && ret.is_object();
991 /// Construct an instance of the specified global class.
993 /// If the class is not present or is not a constructor function, this
994 /// function throws an ActionTypeError.
996 /// TODO: consider whether ActionTypeError is an appropriate exception.
997 /// TODO: test the other failure cases.
998 template<typename T>
999 as_object*
1000 constructObject(VM& vm, const T& arg, string_table::key className)
1003 as_object& gl = *vm.getGlobal();
1005 as_value clval;
1007 // This is tested in actionscript.all/Object.as to return an
1008 // undefined value. We throw the exception back to the VM, which pushes
1009 // an undefined value onto the stack.
1010 if (!gl.get_member(className, &clval) ) {
1011 throw ActionTypeError();
1014 // This is not properly tested.
1015 if (!clval.is_function()) {
1016 throw ActionTypeError();
1019 as_function* ctor = clval.to_function();
1021 // This is also not properly tested.
1022 if (!ctor) throw ActionTypeError();
1024 fn_call::Args args;
1025 args += arg;
1027 as_environment env(vm);
1028 as_object* ret = constructInstance(*ctor, env, args);
1030 return ret;
1034 } // unnamed namespace
1036 std::ostream&
1037 operator<<(std::ostream& o, const as_value& v)
1040 switch (v._type)
1042 case as_value::UNDEFINED:
1043 return o << "[undefined]";
1044 case as_value::NULLTYPE:
1045 return o << "[null]";
1046 case as_value::BOOLEAN:
1048 return o << "[bool:" << std::boolalpha << v.getBool() << "]";
1050 case as_value::OBJECT:
1052 as_object* obj = v.getObj();
1053 assert(obj);
1054 const std::string desc = obj->array() ? "array" :
1055 obj->relay() ? typeName(*obj->relay()) : typeName(*obj);
1056 return o << "[object(" << desc << "):" << static_cast<void*>(obj)
1057 << "]";
1059 case as_value::STRING:
1060 return o << "[string:" + v.getStr() + "]";
1061 case as_value::NUMBER:
1062 return o << "[number:" << v.getNum() << "]";
1063 case as_value::DISPLAYOBJECT:
1065 boost::format ret;
1066 const CharacterProxy& sp = v.getCharacterProxy();
1067 if (sp.isDangling()) {
1068 DisplayObject* rebound = sp.get();
1069 if (rebound) {
1070 ret = boost::format("[rebound %s(%s):%p]") %
1071 typeName(*rebound) % sp.getTarget() %
1072 static_cast<void*>(rebound);
1074 else {
1075 ret = boost::format("[dangling DisplayObject:%s]") %
1076 sp.getTarget();
1079 else {
1080 DisplayObject* ch = sp.get();
1081 ret = boost::format("[%s(%s):%p]") % typeName(*ch) %
1082 sp.getTarget() % static_cast<void*>(ch);
1084 return o << ret.str();
1086 default:
1087 assert(v.is_exception());
1088 return o << "[exception]";
1093 } // namespace gnash
1096 // Local Variables:
1097 // mode: C++
1098 // indent-tabs-mode: nil
1099 // End: