Make automated FSCommand invocation tests show player-side output.
[gnash.git] / libcore / as_value.cpp
blob62039f028af361aee07cef2f423f0f2bb2224c9e
1 // as_value.cpp: ActionScript values, 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.
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 "as_value.h"
23 #include <cmath>
24 #include <cctype>
25 #include <boost/lexical_cast.hpp>
26 #include <boost/format.hpp>
27 #include <locale>
28 #include <sstream>
29 #include <iomanip>
30 #include <string>
31 #include <algorithm>
33 #include "as_object.h"
34 #include "as_function.h" // for as_function
35 #include "MovieClip.h" // for DISPLAYOBJECT values
36 #include "DisplayObject.h" // for DISPLAYOBJECT values
37 #include "as_environment.h" // for DISPLAYOBJECT values
38 #include "VM.h" // for DISPLAYOBJECT values
39 #include "movie_root.h" // for DISPLAYOBJECT values
40 #include "utility.h" // for typeName()
41 #include "GnashNumeric.h"
42 #include "namedStrings.h"
43 #include "GnashException.h"
44 #include "Array_as.h"
45 #include "Date_as.h" // for Date type (readAMF0)
46 #include "SimpleBuffer.h"
47 #include "StringPredicates.h"
48 #include "Global_as.h"
49 #include "String_as.h"
50 #include "AMFConverter.h"
52 // Define the macro below to make abstract equality operator verbose
53 //#define GNASH_DEBUG_EQUALITY 1
55 // Define the macro below to make to_primitive verbose
56 //#define GNASH_DEBUG_CONVERSION_TO_PRIMITIVE 1
58 // Define this macro to make soft references activity verbose
59 #define GNASH_DEBUG_SOFT_REFERENCES
61 namespace gnash {
63 namespace {
64 bool objectEqualsPrimitive(const as_value& obj, const as_value& prim,
65 int version);
66 bool stringEqualsNumber(const as_value& str, const as_value& num,
67 int version);
68 bool compareBoolean(const as_value& boolean, const as_value& other,
69 int version);
70 inline bool findMethod(as_object& obj, const ObjectURI& m, as_value& ret);
71 template<typename T> as_object* constructObject(VM& vm, const T& arg,
72 const ObjectURI& className);
75 namespace {
77 enum Base
79 BASE_OCT,
80 BASE_HEX
84 /// Converts a string to a uint32_t cast to an int32_t.
86 /// @param whole When true, any string that isn't wholly valid is rejected.
87 /// @param base The base (8 or 16) to use.
88 /// @param s The string to parse.
89 /// @return The converted number.
90 std::int32_t
91 parsePositiveInt(const std::string& s, Base base, bool whole = true)
94 std::istringstream is(s);
95 std::uint32_t target;
97 switch (base)
99 case BASE_OCT:
100 is >> std::oct;
101 break;
102 case BASE_HEX:
103 is >> std::hex;
104 break;
107 char c;
109 // If the conversion fails, or if the whole string must be convertible and
110 // some DisplayObjects are left, throw an exception.
111 if (!(is >> target) || (whole && is.get(c))) {
112 throw boost::bad_lexical_cast();
115 return target;
118 struct
119 NonNumericChar
121 bool operator()(char c) {
122 return (!std::isdigit(c) && c != '.' && c != '-' && c != '+');
126 /// Omit an empty exponent that is valid in ActionScript but not in C++.
128 /// This function throws a boost::bad_lexical_cast if it finds an invalid
129 /// exponent to avoid attempting an extraction when it will definitely fail.
131 /// A successful return from this function does not mean the exponent is
132 /// valid, only that the result of stringstream's conversion will mirror
133 /// AS behaviour.
135 /// @param si An iterator pointing to the position after an exponent sign.
136 /// @param last The end of the string to extract. If we have an exponent
137 /// with no following digit, this iterator is moved to
138 /// a position before the exponent sign.
139 void
140 validateExponent(std::string::const_iterator si,
141 std::string::const_iterator& last)
144 // Check for exponent with no following character. Depending on the
145 // version of gcc, extraction may be rejected (probably more correct) or
146 // accepted as a valid exponent (what ActionScript wants).
147 // In this case we remove the exponent to get the correct behaviour
148 // on all compilers.
149 if (si == last) {
150 --last;
151 return;
154 // Exponents with a following '-' or '+' are also valid if they end the
155 // string. It's unlikely that any version of gcc allowed this.
156 if (*si == '-' || *si == '+') {
157 ++si;
158 if (si == last) {
159 last -= 2;
160 return;
164 // An exponent ("e", "e-", or "e+") followed by a non digit is invalid.
165 if (!std::isdigit(*si)) {
166 throw boost::bad_lexical_cast();
171 /// Convert a string to a double if the complete string can be converted.
173 /// This follows the conditions of the standard C locale for numbers except
174 /// that an exponent signifier with no following digit (e.g. "2e") is
175 /// considered valid. Moreover, "2e-" is also considered valid.
177 /// This function scans the string twice (once for verification, once for
178 /// extraction) and copies it once (for extraction).
179 double
180 parseDecimalNumber(std::string::const_iterator start,
181 std::string::const_iterator last)
183 assert(start != last);
185 // Find the first position that is not a numeric character ('e' or 'E' not
186 // included). Even if no invalid character is found, it does not mean
187 // that the number is valid ("++++---" would pass the test).
188 std::string::const_iterator si =
189 std::find_if(start, last, NonNumericChar());
191 if (si != last) {
192 // If this character is not an exponent sign, the number is malformed.
193 if (*si != 'e' && *si != 'E') throw boost::bad_lexical_cast();
194 /// Move the last iterator to point before empty exponents.
195 else validateExponent(si + 1, last);
198 return boost::lexical_cast<double>(std::string(start, last));
201 } // anonymous namespace
203 // Conversion to const std::string&.
204 std::string
205 as_value::to_string(int version) const
207 switch (_type)
209 case STRING:
210 return getStr();
211 case DISPLAYOBJECT:
213 const CharacterProxy& sp = getCharacterProxy();
214 if (!sp.get()) return "";
215 return sp.getTarget();
217 case NUMBER:
218 return doubleToString(getNum());
219 case UNDEFINED:
220 if (version <= 6) return "";
221 return "undefined";
222 case NULLTYPE:
223 return "null";
224 case BOOLEAN:
225 return getBool() ? "true" : "false";
226 case OBJECT:
228 as_object* obj = getObj();
229 String_as* s;
230 if (isNativeType(obj, s)) return s->value();
232 try {
233 as_value ret = to_primitive(STRING);
234 // This additional is_string test is NOT compliant with ECMA-262
235 // specification, but seems required for compatibility with the
236 // reference player.
237 if (ret.is_string()) return ret.getStr();
239 catch (const ActionTypeError& e) {}
241 return is_function() ? "[type Function]" : "[type Object]";
245 default:
246 return "[exception]";
251 as_value::AsType
252 as_value::defaultPrimitive(int version) const
254 if (_type == OBJECT && version > 5) {
255 Date_as* d;
256 if (isNativeType(getObj(), d)) return STRING;
258 return NUMBER;
261 // Conversion to primitive value.
262 as_value
263 as_value::to_primitive(AsType hint) const
265 if (_type != OBJECT) return *this;
267 #if GNASH_DEBUG_CONVERSION_TO_PRIMITIVE
268 log_debug("to_primitive(%s)", hint==NUMBER ? "NUMBER" : "STRING");
269 #endif
271 // TODO: implement as_object::DefaultValue (ECMA-262 - 8.6.2.6)
273 as_value method;
274 as_object* obj(nullptr);
276 if (hint == NUMBER) {
277 assert(_type == OBJECT);
278 obj = getObj();
280 if (!findMethod(*obj, NSV::PROP_VALUE_OF, method)) {
281 // Returning undefined here instead of throwing
282 // a TypeError passes tests in actionscript.all/Object.as
283 // and many swfdec tests, with no new failures (though
284 // perhaps we aren't testing enough).
285 return as_value();
288 else {
289 assert(hint == STRING);
290 assert(_type == OBJECT);
291 obj = getObj();
293 // @@ Moock says, "the value that results from
294 // calling toString() on the object".
295 if (!findMethod(*obj, NSV::PROP_TO_STRING, method) &&
296 !findMethod(*obj, NSV::PROP_VALUE_OF, method)) {
297 throw ActionTypeError();
301 assert(obj);
303 as_environment env(getVM(*obj));
304 fn_call::Args args;
305 as_value ret = invoke(method, env, obj, args);
307 #if GNASH_DEBUG_CONVERSION_TO_PRIMITIVE
308 log_debug("to_primitive: method call returned %s", ret);
309 #endif
311 if (ret._type == OBJECT) {
312 throw ActionTypeError();
314 return ret;
317 double
318 as_value::to_number(const int version) const
321 switch (_type) {
322 case STRING:
324 const std::string& s = getStr();
325 if ( s.empty() ) {
326 return version >= 5 ? NaN : 0.0;
329 if (version <= 4)
331 // For SWF4, any valid number before non-numerical
332 // DisplayObjects is returned, including exponent, positive
333 // and negative signs and whitespace before.
334 double d = 0;
335 std::istringstream is(s);
336 is >> d;
337 return d;
340 try {
342 if (version > 5) {
343 double d;
344 // Will throw if invalid.
345 if (parseNonDecimalInt(s, d)) return d;
348 // @@ Moock says the rule here is: if the
349 // string is a valid float literal, then it
350 // gets converted; otherwise it is set to NaN.
351 // Valid for SWF5 and above.
352 const std::string::size_type pos =
353 s.find_first_not_of(" \r\n\t");
355 if (pos == std::string::npos) return NaN;
357 // Will throw a boost::bad_lexical_cast if it fails.
358 return parseDecimalNumber(s.begin() + pos, s.end());
361 catch (const boost::bad_lexical_cast&) {
362 // There is no standard textual representation of infinity
363 // in the C++ standard, so our conversion function an
364 // exception for 'inf', just like for any other
365 // non-numerical text. This is correct behaviour.
366 return NaN;
370 case NULLTYPE:
371 case UNDEFINED:
373 // Evan: from my tests
374 // Martin: FlashPlayer6 gives 0; FP9 gives NaN.
375 return (version >= 7 ? NaN : 0);
378 case BOOLEAN:
379 return getBool() ? 1 : 0;
381 case NUMBER:
382 return getNum();
384 case OBJECT:
386 // @@ Moock says the result here should be
387 // "the return value of the object's valueOf()
388 // method".
390 // Arrays and Movieclips should return NaN.
391 try {
392 as_value ret = to_primitive(NUMBER);
393 return ret.to_number(version);
395 catch (const ActionTypeError& e) {
396 #if GNASH_DEBUG_CONVERSION_TO_PRIMITIVE
397 log_debug("to_primitive(%s, NUMBER) threw an "
398 "ActionTypeError %s", *this, e.what());
399 #endif
400 if (is_function() && version < 6) {
401 return 0;
404 return NaN;
408 case DISPLAYOBJECT:
410 // This is tested, no valueOf is going
411 // to be invoked for movieclips.
412 return NaN;
415 default:
416 // Other object types should return NaN.
417 return NaN;
421 // Conversion to boolean
422 bool
423 as_value::to_bool(const int version) const
425 switch (_type)
427 case STRING:
429 if (version >= 7) return !getStr().empty();
430 const double num = to_number(version);
431 return num && !isNaN(num);
433 case NUMBER:
435 const double d = getNum();
436 // see testsuite/swfdec/if-6.swf
437 return d && ! isNaN(d);
439 case BOOLEAN:
440 return getBool();
441 case OBJECT:
442 return true;
443 case DISPLAYOBJECT:
444 return true;
445 default:
446 assert(_type == UNDEFINED || _type == NULLTYPE || is_exception());
447 return false;
451 // Return value as an object.
452 as_object*
453 as_value::to_object(VM& vm) const
456 switch (_type)
458 case OBJECT:
459 return getObj();
461 case DISPLAYOBJECT:
462 return getObject(toDisplayObject());
464 case STRING:
465 return constructObject(vm, getStr(), NSV::CLASS_STRING);
467 case NUMBER:
468 return constructObject(vm, getNum(), NSV::CLASS_NUMBER);
470 case BOOLEAN:
471 return constructObject(vm, getBool(), NSV::CLASS_BOOLEAN);
473 default:
474 // Invalid to convert exceptions.
475 return nullptr;
479 MovieClip*
480 as_value::toMovieClip(bool allowUnloaded) const
482 if (_type != DISPLAYOBJECT) return nullptr;
484 DisplayObject *ch = getCharacter(allowUnloaded);
485 if (!ch) return nullptr;
486 return ch->to_movie();
489 DisplayObject*
490 as_value::toDisplayObject(bool allowUnloaded) const
492 if (_type != DISPLAYOBJECT) return nullptr;
493 return getCharacter(allowUnloaded);
496 // Return value as an ActionScript function. Returns NULL if value is
497 // not an ActionScript function.
498 as_function*
499 as_value::to_function() const
501 if (_type == OBJECT) {
502 return getObj()->to_function();
505 return nullptr;
508 as_object*
509 as_value::get_object() const
511 if (_type == OBJECT) {
512 return getObj();
515 return nullptr;
518 void
519 as_value::set_undefined()
521 _type = UNDEFINED;
522 _value = boost::blank();
525 void
526 as_value::set_null()
528 _type = NULLTYPE;
529 _value = boost::blank();
532 void
533 as_value::set_as_object(as_object* obj)
535 if (!obj)
537 set_null();
538 return;
540 if (obj->displayObject()) {
541 // The static cast is fine as long as the as_object is genuinely
542 // a DisplayObject.
543 _type = DISPLAYOBJECT;
544 _value = CharacterProxy(obj->displayObject(), getRoot(*obj));
545 return;
548 if (_type != OBJECT || getObj() != obj) {
549 _type = OBJECT;
550 _value = obj;
554 bool
555 as_value::equals(const as_value& v, int version) const
558 // First compare values of the same type.
559 if (_type == v._type) return equalsSameType(v);
561 // Then compare booleans.
562 if (is_bool()) return compareBoolean(*this, v, version);
563 if (v.is_bool()) return compareBoolean(v, *this, version);
565 // Then compare any other primitive, including null and undefined, with
566 // an object.
567 if (!is_object() && v.is_object()) {
568 return objectEqualsPrimitive(v, *this, version);
571 if (is_object() && !v.is_object()) {
572 return objectEqualsPrimitive(*this, v, version);
575 // Remaining null or undefined values only equate to other null or
576 // undefined values.
577 const bool null = (is_undefined() || is_null());
578 const bool v_null = (v.is_undefined() || v.is_null());
579 if (null || v_null) return null == v_null;
581 // Now compare a number with a string.
582 if (is_number() && v.is_string()) {
583 return stringEqualsNumber(v, *this, version);
585 if (is_string() && v.is_number()) {
586 return stringEqualsNumber(*this, v, version);
589 // Finally compare non-identical objects.
590 as_value p = *this;
591 as_value vp = v;
593 try {
594 p = to_primitive(NUMBER);
596 catch (const ActionTypeError& e) {}
598 try {
599 vp = v.to_primitive(NUMBER);
601 catch (const ActionTypeError& e) {}
603 // No conversion took place; the result is false
604 if (strictly_equals(p) && v.strictly_equals(vp)) {
605 return false;
608 return p.equals(vp, version);
611 const char*
612 as_value::typeOf() const
614 switch (_type)
616 case UNDEFINED:
617 return "undefined";
619 case STRING:
620 return "string";
622 case NUMBER:
623 return "number";
625 case BOOLEAN:
626 return "boolean";
628 case OBJECT:
629 return is_function() ? "function" : "object";
631 case DISPLAYOBJECT:
633 DisplayObject* ch = getCharacter();
634 if ( ! ch ) return "movieclip"; // dangling
635 if ( ch->to_movie() ) return "movieclip"; // bound to movieclip
636 return "object"; // bound to some other DisplayObject
639 case NULLTYPE:
640 return "null";
642 default:
643 if (is_exception()) return "exception";
644 std::abort();
645 return nullptr;
649 bool
650 as_value::equalsSameType(const as_value& v) const
652 assert(_type == v._type);
654 switch (_type)
656 case UNDEFINED:
657 case NULLTYPE:
658 return true;
660 case OBJECT:
661 case BOOLEAN:
662 case STRING:
663 return _value == v._value;
665 case DISPLAYOBJECT:
666 return toDisplayObject() == v.toDisplayObject();
668 case NUMBER:
670 const double a = getNum();
671 const double b = v.getNum();
672 if (isNaN(a) && isNaN(b)) return true;
673 return a == b;
675 default:
676 if (is_exception()) return false;
679 std::abort();
680 return false;
683 bool
684 as_value::strictly_equals(const as_value& v) const
686 if ( _type != v._type ) return false;
687 return equalsSameType(v);
690 void
691 as_value::setReachable() const
693 switch (_type)
695 case OBJECT:
697 as_object* op = getObj();
698 if (op) op->setReachable();
699 break;
701 case DISPLAYOBJECT:
703 CharacterProxy sp = getCharacterProxy();
704 sp.setReachable();
705 break;
707 default: break;
711 as_object*
712 as_value::getObj() const
714 assert(_type == OBJECT);
715 return boost::get<as_object*>(_value);
718 CharacterProxy
719 as_value::getCharacterProxy() const
721 assert(_type == DISPLAYOBJECT);
722 return boost::get<CharacterProxy>(_value);
725 DisplayObject*
726 as_value::getCharacter(bool allowUnloaded) const
728 return getCharacterProxy().get(allowUnloaded);
731 void
732 as_value::set_string(const std::string& str)
734 _type = STRING;
735 _value = str;
738 void
739 as_value::set_double(double val)
741 _type = NUMBER;
742 _value = val;
745 void
746 as_value::set_bool(bool val)
748 _type = BOOLEAN;
749 _value = val;
752 bool
753 as_value::is_function() const
755 return _type == OBJECT && getObj()->to_function();
758 bool
759 as_value::writeAMF0(amf::Writer& w) const
762 assert (!is_exception());
764 switch (_type)
766 default:
767 log_unimpl(_("serialization of as_value of type %d"), _type);
768 return false;
770 case OBJECT:
771 if (is_function()) return false;
772 return w.writeObject(getObj());
774 case STRING:
775 return w.writeString(getStr());
777 case NUMBER:
778 return w.writeNumber(getNum());
780 case DISPLAYOBJECT:
781 case UNDEFINED:
782 return w.writeUndefined();
784 case NULLTYPE:
785 return w.writeNull();
787 case BOOLEAN:
788 return w.writeBoolean(getBool());
792 bool
793 parseNonDecimalInt(const std::string& s, double& d, bool whole)
795 const std::string::size_type slen = s.length();
797 // "0#" would still be octal, but has the same value as a decimal.
798 if (slen < 3) return false;
800 bool negative = false;
802 if (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) {
803 // The only legitimate place for a '-' is after 0x. If it's a
804 // '+' we don't care, as it won't disturb the conversion.
805 std::string::size_type start = 2;
806 if (s[2] == '-') {
807 negative = true;
808 ++start;
810 d = parsePositiveInt(s.substr(start), BASE_HEX, whole);
811 if (negative) d = -d;
812 return true;
814 else if ((s[0] == '0' || ((s[0] == '-' || s[0] == '+') && s[1] == '0')) &&
815 s.find_first_not_of("01234567", 1) == std::string::npos) {
817 std::string::size_type start = 0;
818 if (s[0] == '-') {
819 negative = true;
820 ++start;
822 d = parsePositiveInt(s.substr(start), BASE_OCT, whole);
823 if (negative) d = -d;
824 return true;
827 return false;
831 std::string
832 doubleToString(double val, int radix)
834 // Examples:
836 // e.g. for 9*.1234567890123456789:
837 // 9999.12345678901
838 // 99999.123456789
839 // 999999.123456789
840 // 9999999.12345679
841 // [...]
842 // 999999999999.123
843 // 9999999999999.12
844 // 99999999999999.1
845 // 999999999999999
846 // 1e+16
847 // 1e+17
849 // For 1*.111111111111111111111111111111111111:
850 // 1111111111111.11
851 // 11111111111111.1
852 // 111111111111111
853 // 1.11111111111111e+15
854 // 1.11111111111111e+16
856 // For 1.234567890123456789 * 10^-i:
857 // 1.23456789012346
858 // 0.123456789012346
859 // 0.0123456789012346
860 // 0.00123456789012346
861 // 0.000123456789012346
862 // 0.0000123456789012346
863 // 0.00000123456789012346
864 // 1.23456789012346e-6
865 // 1.23456789012346e-7
867 // Handle non-numeric values.
868 if (isNaN(val)) return "NaN";
870 if (isInf(val)) return val < 0 ? "-Infinity" : "Infinity";
872 if (val == 0.0 || val == -0.0) return "0";
874 std::ostringstream ostr;
876 if (radix == 10) {
878 // ActionScript always expects dot as decimal point.
879 ostr.imbue(std::locale::classic());
881 // force to decimal notation for this range (because the
882 // reference player does)
883 if (std::abs(val) < 0.0001 && std::abs(val) >= 0.00001) {
885 // All nineteen digits (4 zeros + up to 15 significant digits)
886 ostr << std::fixed << std::setprecision(19) << val;
888 std::string str = ostr.str();
890 // Because 'fixed' also adds trailing zeros, remove them.
891 std::string::size_type pos = str.find_last_not_of('0');
892 if (pos != std::string::npos) {
893 str.erase(pos + 1);
895 return str;
898 ostr << std::setprecision(15) << val;
900 std::string str = ostr.str();
902 // Remove a leading zero from 2-digit exponent if any
903 std::string::size_type pos = str.find("e", 0);
905 if (pos != std::string::npos && str.at(pos + 2) == '0') {
906 str.erase(pos + 2, 1);
909 return str;
912 // Radix isn't 10
913 bool negative = (val < 0);
914 if (negative) val = -val;
916 double left = std::floor(val);
917 if (left < 1) return "0";
919 std::string str;
920 const std::string digits = "0123456789abcdefghijklmnopqrstuvwxyz";
922 // Construct the string backwards for speed, then reverse.
923 while (left) {
924 double n = left;
925 left = std::floor(left / radix);
926 n -= (left * radix);
927 str.push_back(digits[static_cast<int>(n)]);
929 if (negative) str.push_back('-');
931 std::reverse(str.begin(), str.end());
933 return str;
936 namespace {
938 /// Checks for equality between an object value and a primitive value
940 /// @param obj An as_value of object type. Callers must ensure this
941 /// condition is met.
942 /// @param prim An as_value of primitive type. Callers must ensure this
943 /// condition is met.
945 /// This is a function try-block.
946 bool
947 objectEqualsPrimitive(const as_value& obj, const as_value& prim, int version)
948 try {
950 assert(obj.is_object());
951 assert(!prim.is_object());
953 as_value tmp = obj.to_primitive(as_value::NUMBER);
954 if (obj.strictly_equals(tmp)) return false;
955 return tmp.equals(prim, version);
957 catch (const ActionTypeError&) {
958 return false;
961 /// @param boolean A boolean as_value
962 /// @param other An as_value of any type.
963 bool
964 compareBoolean(const as_value& boolean, const as_value& other, int version)
966 assert(boolean.is_bool());
967 return as_value(boolean.to_number(version)).equals(other, version);
970 bool
971 stringEqualsNumber(const as_value& str, const as_value& num, int version)
973 assert(num.is_number());
974 assert(str.is_string());
975 const double n = str.to_number(version);
976 if (!isFinite(n)) return false;
977 return num.strictly_equals(n);
981 /// Returns a member only if it is an object.
982 inline bool
983 findMethod(as_object& obj, const ObjectURI& m, as_value& ret)
985 return obj.get_member(m, &ret) && ret.is_object();
988 /// Construct an instance of the specified global class.
990 /// If the class is not present or is not a constructor function, this
991 /// function throws an ActionTypeError.
993 /// TODO: consider whether ActionTypeError is an appropriate exception.
994 /// TODO: test the other failure cases.
995 template<typename T>
996 as_object*
997 constructObject(VM& vm, const T& arg, const ObjectURI& className)
1000 as_object& gl = *vm.getGlobal();
1002 as_value clval;
1004 // This is tested in actionscript.all/Object.as to return an
1005 // undefined value. We throw the exception back to the VM, which pushes
1006 // an undefined value onto the stack.
1007 if (!gl.get_member(className, &clval) ) {
1008 throw ActionTypeError();
1011 // This is not properly tested.
1012 if (!clval.is_function()) {
1013 throw ActionTypeError();
1016 as_function* ctor = clval.to_function();
1018 // This is also not properly tested.
1019 if (!ctor) throw ActionTypeError();
1021 fn_call::Args args;
1022 args += arg;
1024 as_environment env(vm);
1025 as_object* ret = constructInstance(*ctor, env, args);
1027 return ret;
1031 } // unnamed namespace
1033 std::ostream&
1034 operator<<(std::ostream& o, const as_value& v)
1037 switch (v._type)
1039 case as_value::UNDEFINED:
1040 return o << "[undefined]";
1041 case as_value::NULLTYPE:
1042 return o << "[null]";
1043 case as_value::BOOLEAN:
1045 std::ios::fmtflags f(o.flags());
1046 o << "[bool:" << std::boolalpha << v.getBool() << "]";
1047 o.flags(f);
1048 return o;
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: