592 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
			
		
		
	
	
			592 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
/*
 | 
						|
 * Copyright (C)2005-2019 Haxe Foundation
 | 
						|
 *
 | 
						|
 * Permission is hereby granted, free of charge, to any person obtaining a
 | 
						|
 * copy of this software and associated documentation files (the "Software"),
 | 
						|
 * to deal in the Software without restriction, including without limitation
 | 
						|
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 | 
						|
 * and/or sell copies of the Software, and to permit persons to whom the
 | 
						|
 * Software is furnished to do so, subject to the following conditions:
 | 
						|
 *
 | 
						|
 * The above copyright notice and this permission notice shall be included in
 | 
						|
 * all copies or substantial portions of the Software.
 | 
						|
 *
 | 
						|
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
						|
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
						|
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
						|
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
						|
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 | 
						|
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 | 
						|
 * DEALINGS IN THE SOFTWARE.
 | 
						|
 */
 | 
						|
 | 
						|
package haxe;
 | 
						|
 | 
						|
import haxe.ds.List;
 | 
						|
 | 
						|
/**
 | 
						|
	The Serializer class can be used to encode values and objects into a `String`,
 | 
						|
	from which the `Unserializer` class can recreate the original representation.
 | 
						|
 | 
						|
	This class can be used in two ways:
 | 
						|
 | 
						|
	- create a `new Serializer()` instance, call its `serialize()` method with
 | 
						|
		any argument and finally retrieve the String representation from
 | 
						|
		`toString()`
 | 
						|
	- call `Serializer.run()` to obtain the serialized representation of a
 | 
						|
		single argument
 | 
						|
 | 
						|
	Serialization is guaranteed to work for all haxe-defined classes, but may
 | 
						|
	or may not work for instances of external/native classes.
 | 
						|
 | 
						|
	The specification of the serialization format can be found here:
 | 
						|
	<https://haxe.org/manual/std-serialization-format.html>
 | 
						|
**/
 | 
						|
class Serializer {
 | 
						|
	/**
 | 
						|
		If the values you are serializing can contain circular references or
 | 
						|
		objects repetitions, you should set `USE_CACHE` to true to prevent
 | 
						|
		infinite loops.
 | 
						|
 | 
						|
		This may also reduce the size of serialization Strings at the expense of
 | 
						|
		performance.
 | 
						|
 | 
						|
		This value can be changed for individual instances of `Serializer` by
 | 
						|
		setting their `useCache` field.
 | 
						|
	**/
 | 
						|
	public static var USE_CACHE = false;
 | 
						|
 | 
						|
	/**
 | 
						|
		Use constructor indexes for enums instead of names.
 | 
						|
 | 
						|
		This may reduce the size of serialization Strings, but makes them less
 | 
						|
		suited for long-term storage: If constructors are removed or added from
 | 
						|
		the enum, the indices may no longer match.
 | 
						|
 | 
						|
		This value can be changed for individual instances of `Serializer` by
 | 
						|
		setting their `useEnumIndex` field.
 | 
						|
	**/
 | 
						|
	public static var USE_ENUM_INDEX = false;
 | 
						|
 | 
						|
	static var BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789%:";
 | 
						|
	static var BASE64_CODES = null;
 | 
						|
 | 
						|
	var buf:StringBuf;
 | 
						|
	var cache:Array<Dynamic>;
 | 
						|
	var shash:haxe.ds.StringMap<Int>;
 | 
						|
	var scount:Int;
 | 
						|
 | 
						|
	/**
 | 
						|
		The individual cache setting for `this` Serializer instance.
 | 
						|
 | 
						|
		See `USE_CACHE` for a complete description.
 | 
						|
	**/
 | 
						|
	public var useCache:Bool;
 | 
						|
 | 
						|
	/**
 | 
						|
		The individual enum index setting for `this` Serializer instance.
 | 
						|
 | 
						|
		See `USE_ENUM_INDEX` for a complete description.
 | 
						|
	**/
 | 
						|
	public var useEnumIndex:Bool;
 | 
						|
 | 
						|
	/**
 | 
						|
		Creates a new Serializer instance.
 | 
						|
 | 
						|
		Subsequent calls to `this.serialize` will append values to the
 | 
						|
		internal buffer of this String. Once complete, the contents can be
 | 
						|
		retrieved through a call to `this.toString`.
 | 
						|
 | 
						|
		Each `Serializer` instance maintains its own cache if `this.useCache` is
 | 
						|
		`true`.
 | 
						|
	**/
 | 
						|
	public function new() {
 | 
						|
		buf = new StringBuf();
 | 
						|
		cache = new Array();
 | 
						|
		useCache = USE_CACHE;
 | 
						|
		useEnumIndex = USE_ENUM_INDEX;
 | 
						|
		shash = new haxe.ds.StringMap();
 | 
						|
		scount = 0;
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
		Return the String representation of `this` Serializer.
 | 
						|
 | 
						|
		The exact format specification can be found here:
 | 
						|
		https://haxe.org/manual/serialization/format
 | 
						|
	**/
 | 
						|
	public function toString() {
 | 
						|
		return buf.toString();
 | 
						|
	}
 | 
						|
 | 
						|
	/* prefixes :
 | 
						|
		a : array
 | 
						|
		b : hash
 | 
						|
		c : class
 | 
						|
		d : Float
 | 
						|
		e : reserved (float exp)
 | 
						|
		f : false
 | 
						|
		g : object end
 | 
						|
		h : array/list/hash end
 | 
						|
		i : Int
 | 
						|
		j : enum (by index)
 | 
						|
		k : NaN
 | 
						|
		l : list
 | 
						|
		m : -Inf
 | 
						|
		n : null
 | 
						|
		o : object
 | 
						|
		p : +Inf
 | 
						|
		q : haxe.ds.IntMap
 | 
						|
		r : reference
 | 
						|
		s : bytes (base64)
 | 
						|
		t : true
 | 
						|
		u : array nulls
 | 
						|
		v : date
 | 
						|
		w : enum
 | 
						|
		x : exception
 | 
						|
		y : urlencoded string
 | 
						|
		z : zero
 | 
						|
		A : Class<Dynamic>
 | 
						|
		B : Enum<Dynamic>
 | 
						|
		M : haxe.ds.ObjectMap
 | 
						|
		C : custom
 | 
						|
	 */
 | 
						|
	function serializeString(s:String) {
 | 
						|
		var x = shash.get(s);
 | 
						|
		if (x != null) {
 | 
						|
			buf.add("R");
 | 
						|
			buf.add(x);
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		shash.set(s, scount++);
 | 
						|
		#if old_serialize
 | 
						|
		// no more support for -D old_serialize due to 'j' reuse
 | 
						|
		#if error
 | 
						|
		#end
 | 
						|
		#end
 | 
						|
		buf.add("y");
 | 
						|
		s = StringTools.urlEncode(s);
 | 
						|
		buf.add(s.length);
 | 
						|
		buf.add(":");
 | 
						|
		buf.add(s);
 | 
						|
	}
 | 
						|
 | 
						|
	function serializeRef(v:Dynamic) {
 | 
						|
		#if js
 | 
						|
		var vt = js.Syntax.typeof(v);
 | 
						|
		#end
 | 
						|
		for (i in 0...cache.length) {
 | 
						|
			#if js
 | 
						|
			var ci = cache[i];
 | 
						|
			if (js.Syntax.typeof(ci) == vt && ci == v) {
 | 
						|
			#else
 | 
						|
			if (cache[i] == v) {
 | 
						|
			#end
 | 
						|
				buf.add("r");
 | 
						|
				buf.add(i);
 | 
						|
				return true;
 | 
						|
			}
 | 
						|
		}
 | 
						|
		cache.push(v);
 | 
						|
		return false;
 | 
						|
	}
 | 
						|
 | 
						|
	#if flash
 | 
						|
	// only the instance variables
 | 
						|
 | 
						|
	function serializeClassFields(v:Dynamic, c:Dynamic) {
 | 
						|
		var xml:flash.xml.XML = untyped __global__["flash.utils.describeType"](c);
 | 
						|
		var vars = xml.factory[0].child("variable");
 | 
						|
		for (i in 0...vars.length()) {
 | 
						|
			var f = vars[i].attribute("name").toString();
 | 
						|
			if (!v.hasOwnProperty(f))
 | 
						|
				continue;
 | 
						|
			serializeString(f);
 | 
						|
			serialize(Reflect.field(v, f));
 | 
						|
		}
 | 
						|
		buf.add("g");
 | 
						|
	}
 | 
						|
	#end
 | 
						|
 | 
						|
	function serializeFields(v:{}) {
 | 
						|
		for (f in Reflect.fields(v)) {
 | 
						|
			serializeString(f);
 | 
						|
			serialize(Reflect.field(v, f));
 | 
						|
		}
 | 
						|
		buf.add("g");
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	Serializes `v`.
 | 
						|
 | 
						|
	All haxe-defined values and objects with the exception of functions can
 | 
						|
	be serialized. Serialization of external/native objects is not
 | 
						|
	guaranteed to work.
 | 
						|
 | 
						|
	The values of `this.useCache` and `this.useEnumIndex` may affect
 | 
						|
	serialization output.
 | 
						|
**/
 | 
						|
	public function serialize(v:Dynamic) {
 | 
						|
		switch (Type.typeof(v)) {
 | 
						|
			case TNull:
 | 
						|
				buf.add("n");
 | 
						|
			case TInt:
 | 
						|
				var v:Int = v;
 | 
						|
				if (v == 0) {
 | 
						|
					buf.add("z");
 | 
						|
					return;
 | 
						|
				}
 | 
						|
				buf.add("i");
 | 
						|
				buf.add(v);
 | 
						|
			case TFloat:
 | 
						|
				var v:Float = v;
 | 
						|
				if (Math.isNaN(v))
 | 
						|
					buf.add("k");
 | 
						|
				else if (!Math.isFinite(v))
 | 
						|
					buf.add(if (v < 0) "m" else "p");
 | 
						|
				else {
 | 
						|
					buf.add("d");
 | 
						|
					buf.add(v);
 | 
						|
				}
 | 
						|
			case TBool:
 | 
						|
				buf.add(if (v) "t" else "f");
 | 
						|
			case TClass(c):
 | 
						|
				if (#if neko untyped c.__is_String #else c == String #end) {
 | 
						|
					serializeString(v);
 | 
						|
					return;
 | 
						|
				}
 | 
						|
				if (useCache && serializeRef(v))
 | 
						|
					return;
 | 
						|
				switch (#if (neko || cs || python) Type.getClassName(c) #else c #end) {
 | 
						|
					case #if (neko || cs || python) "Array" #else cast Array #end:
 | 
						|
						var ucount = 0;
 | 
						|
						buf.add("a");
 | 
						|
						#if (flash || python || hl)
 | 
						|
						var v:Array<Dynamic> = v;
 | 
						|
						#end
 | 
						|
						var l = #if (neko || flash || php || cs || java || python || hl || lua || eval) v.length #elseif cpp v.__length() #else __getField(v,
 | 
						|
							"length") #end;
 | 
						|
						for (i in 0...l) {
 | 
						|
							if (v[i] == null)
 | 
						|
								ucount++;
 | 
						|
							else {
 | 
						|
								if (ucount > 0) {
 | 
						|
									if (ucount == 1)
 | 
						|
										buf.add("n");
 | 
						|
									else {
 | 
						|
										buf.add("u");
 | 
						|
										buf.add(ucount);
 | 
						|
									}
 | 
						|
									ucount = 0;
 | 
						|
								}
 | 
						|
								serialize(v[i]);
 | 
						|
							}
 | 
						|
						}
 | 
						|
						if (ucount > 0) {
 | 
						|
							if (ucount == 1)
 | 
						|
								buf.add("n");
 | 
						|
							else {
 | 
						|
								buf.add("u");
 | 
						|
								buf.add(ucount);
 | 
						|
							}
 | 
						|
						}
 | 
						|
						buf.add("h");
 | 
						|
					case #if (neko || cs || python) "haxe.ds.List" #else cast List #end:
 | 
						|
						buf.add("l");
 | 
						|
						var v:List<Dynamic> = v;
 | 
						|
						for (i in v)
 | 
						|
							serialize(i);
 | 
						|
						buf.add("h");
 | 
						|
					case #if (neko || cs || python) "Date" #else cast Date #end:
 | 
						|
						var d:Date = v;
 | 
						|
						buf.add("v");
 | 
						|
						buf.add(d.getTime());
 | 
						|
					case #if (neko || cs || python) "haxe.ds.StringMap" #else cast haxe.ds.StringMap #end:
 | 
						|
						buf.add("b");
 | 
						|
						var v:haxe.ds.StringMap<Dynamic> = v;
 | 
						|
						for (k in v.keys()) {
 | 
						|
							serializeString(k);
 | 
						|
							serialize(v.get(k));
 | 
						|
						}
 | 
						|
						buf.add("h");
 | 
						|
					case #if (neko || cs || python) "haxe.ds.IntMap" #else cast haxe.ds.IntMap #end:
 | 
						|
						buf.add("q");
 | 
						|
						var v:haxe.ds.IntMap<Dynamic> = v;
 | 
						|
						for (k in v.keys()) {
 | 
						|
							buf.add(":");
 | 
						|
							buf.add(k);
 | 
						|
							serialize(v.get(k));
 | 
						|
						}
 | 
						|
						buf.add("h");
 | 
						|
					case #if (neko || cs || python) "haxe.ds.ObjectMap" #else cast haxe.ds.ObjectMap #end:
 | 
						|
						buf.add("M");
 | 
						|
						var v:haxe.ds.ObjectMap<Dynamic, Dynamic> = v;
 | 
						|
						for (k in v.keys()) {
 | 
						|
							#if (js || neko)
 | 
						|
							var id = Reflect.field(k, "__id__");
 | 
						|
							Reflect.deleteField(k, "__id__");
 | 
						|
							serialize(k);
 | 
						|
							Reflect.setField(k, "__id__", id);
 | 
						|
							#else
 | 
						|
							serialize(k);
 | 
						|
							#end
 | 
						|
							serialize(v.get(k));
 | 
						|
						}
 | 
						|
						buf.add("h");
 | 
						|
					case #if (neko || cs || python) "haxe.io.Bytes" #else cast haxe.io.Bytes #end:
 | 
						|
						var v:haxe.io.Bytes = v;
 | 
						|
						#if neko
 | 
						|
						var chars = new String(base_encode(v.getData(), untyped BASE64.__s));
 | 
						|
						buf.add("s");
 | 
						|
						buf.add(chars.length);
 | 
						|
						buf.add(":");
 | 
						|
						buf.add(chars);
 | 
						|
						#elseif php
 | 
						|
						var chars = new String(php.Global.base64_encode(v.getData()));
 | 
						|
						chars = php.Global.strtr(chars, '+/', '%:');
 | 
						|
						buf.add("s");
 | 
						|
						buf.add(chars.length);
 | 
						|
						buf.add(":");
 | 
						|
						buf.add(chars);
 | 
						|
						#else
 | 
						|
						buf.add("s");
 | 
						|
						buf.add(Math.ceil((v.length * 8) / 6));
 | 
						|
						buf.add(":");
 | 
						|
 | 
						|
						var i = 0;
 | 
						|
						var max = v.length - 2;
 | 
						|
						var b64 = BASE64_CODES;
 | 
						|
						if (b64 == null) {
 | 
						|
							b64 = new haxe.ds.Vector(BASE64.length);
 | 
						|
							for (i in 0...BASE64.length)
 | 
						|
								b64[i] = BASE64.charCodeAt(i);
 | 
						|
							BASE64_CODES = b64;
 | 
						|
						}
 | 
						|
						while (i < max) {
 | 
						|
							var b1 = v.get(i++);
 | 
						|
							var b2 = v.get(i++);
 | 
						|
							var b3 = v.get(i++);
 | 
						|
 | 
						|
							buf.addChar(b64[b1 >> 2]);
 | 
						|
							buf.addChar(b64[((b1 << 4) | (b2 >> 4)) & 63]);
 | 
						|
							buf.addChar(b64[((b2 << 2) | (b3 >> 6)) & 63]);
 | 
						|
							buf.addChar(b64[b3 & 63]);
 | 
						|
						}
 | 
						|
						if (i == max) {
 | 
						|
							var b1 = v.get(i++);
 | 
						|
							var b2 = v.get(i++);
 | 
						|
							buf.addChar(b64[b1 >> 2]);
 | 
						|
							buf.addChar(b64[((b1 << 4) | (b2 >> 4)) & 63]);
 | 
						|
							buf.addChar(b64[(b2 << 2) & 63]);
 | 
						|
						} else if (i == max + 1) {
 | 
						|
							var b1 = v.get(i++);
 | 
						|
							buf.addChar(b64[b1 >> 2]);
 | 
						|
							buf.addChar(b64[(b1 << 4) & 63]);
 | 
						|
						}
 | 
						|
						#end
 | 
						|
					default:
 | 
						|
						if (useCache) cache.pop();
 | 
						|
						if (#if flash try
 | 
						|
							v.hxSerialize != null
 | 
						|
						catch (e:Dynamic)
 | 
						|
							false #elseif (cs || java || python) Reflect.hasField(v,
 | 
						|
								"hxSerialize") #elseif php php.Global.method_exists(v, 'hxSerialize') #else v.hxSerialize != null #end) {
 | 
						|
							buf.add("C");
 | 
						|
							serializeString(Type.getClassName(c));
 | 
						|
							if (useCache)
 | 
						|
								cache.push(v);
 | 
						|
							v.hxSerialize(this);
 | 
						|
							buf.add("g");
 | 
						|
						} else {
 | 
						|
							buf.add("c");
 | 
						|
							serializeString(Type.getClassName(c));
 | 
						|
							if (useCache)
 | 
						|
								cache.push(v);
 | 
						|
							#if flash
 | 
						|
							serializeClassFields(v, c);
 | 
						|
							#else
 | 
						|
							serializeFields(v);
 | 
						|
							#end
 | 
						|
						}
 | 
						|
				}
 | 
						|
			case TObject:
 | 
						|
				if (Std.isOfType(v, Class)) {
 | 
						|
					var className = Type.getClassName(v);
 | 
						|
					#if (flash || cpp)
 | 
						|
					// Currently, Enum and Class are the same for flash and cpp.
 | 
						|
					//  use resolveEnum to test if it is actually an enum
 | 
						|
					if (Type.resolveEnum(className) != null)
 | 
						|
						buf.add("B")
 | 
						|
					else
 | 
						|
					#end
 | 
						|
					buf.add("A");
 | 
						|
					serializeString(className);
 | 
						|
				} else if (Std.isOfType(v, Enum)) {
 | 
						|
					buf.add("B");
 | 
						|
					serializeString(Type.getEnumName(v));
 | 
						|
				} else {
 | 
						|
					if (useCache && serializeRef(v))
 | 
						|
						return;
 | 
						|
					buf.add("o");
 | 
						|
					serializeFields(v);
 | 
						|
				}
 | 
						|
			case TEnum(e):
 | 
						|
				if (useCache) {
 | 
						|
					if (serializeRef(v))
 | 
						|
						return;
 | 
						|
					cache.pop();
 | 
						|
				}
 | 
						|
				buf.add(useEnumIndex ? "j" : "w");
 | 
						|
				serializeString(Type.getEnumName(e));
 | 
						|
				#if neko
 | 
						|
				if (useEnumIndex) {
 | 
						|
					buf.add(":");
 | 
						|
					buf.add(v.index);
 | 
						|
				} else
 | 
						|
					serializeString(new String(v.tag));
 | 
						|
				buf.add(":");
 | 
						|
				if (v.args == null)
 | 
						|
					buf.add(0);
 | 
						|
				else {
 | 
						|
					var l:Int = untyped __dollar__asize(v.args);
 | 
						|
					buf.add(l);
 | 
						|
					for (i in 0...l)
 | 
						|
						serialize(v.args[i]);
 | 
						|
				}
 | 
						|
				#elseif flash
 | 
						|
				if (useEnumIndex) {
 | 
						|
					buf.add(":");
 | 
						|
					var i:Int = v.index;
 | 
						|
					buf.add(i);
 | 
						|
				} else
 | 
						|
					serializeString(v.tag);
 | 
						|
				buf.add(":");
 | 
						|
				var pl:Array<Dynamic> = v.params;
 | 
						|
				if (pl == null)
 | 
						|
					buf.add(0);
 | 
						|
				else {
 | 
						|
					buf.add(pl.length);
 | 
						|
					for (p in pl)
 | 
						|
						serialize(p);
 | 
						|
				}
 | 
						|
				#elseif cpp
 | 
						|
				var enumBase:cpp.EnumBase = v;
 | 
						|
				if (useEnumIndex) {
 | 
						|
					buf.add(":");
 | 
						|
					buf.add(enumBase.getIndex());
 | 
						|
				} else
 | 
						|
					serializeString(enumBase.getTag());
 | 
						|
				buf.add(":");
 | 
						|
				var len = enumBase.getParamCount();
 | 
						|
				buf.add(len);
 | 
						|
				for (p in 0...len)
 | 
						|
					serialize(enumBase.getParamI(p));
 | 
						|
				#elseif php
 | 
						|
				if (useEnumIndex) {
 | 
						|
					buf.add(":");
 | 
						|
					buf.add(v.index);
 | 
						|
				} else
 | 
						|
					serializeString(v.tag);
 | 
						|
				buf.add(":");
 | 
						|
				var l:Int = php.Syntax.code("count({0})", v.params);
 | 
						|
				if (l == 0 || v.params == null)
 | 
						|
					buf.add(0);
 | 
						|
				else {
 | 
						|
					buf.add(l);
 | 
						|
					for (i in 0...l) {
 | 
						|
						#if php
 | 
						|
						serialize(v.params[i]);
 | 
						|
						#end
 | 
						|
					}
 | 
						|
				}
 | 
						|
				#elseif (java || cs || python || hl || eval)
 | 
						|
				if (useEnumIndex) {
 | 
						|
					buf.add(":");
 | 
						|
					buf.add(Type.enumIndex(v));
 | 
						|
				} else
 | 
						|
					serializeString(Type.enumConstructor(v));
 | 
						|
				buf.add(":");
 | 
						|
				var arr:Array<Dynamic> = Type.enumParameters(v);
 | 
						|
				if (arr != null) {
 | 
						|
					buf.add(arr.length);
 | 
						|
					for (v in arr)
 | 
						|
						serialize(v);
 | 
						|
				} else {
 | 
						|
					buf.add("0");
 | 
						|
				}
 | 
						|
				#elseif (js && !js_enums_as_arrays)
 | 
						|
				if (useEnumIndex) {
 | 
						|
					buf.add(":");
 | 
						|
					buf.add(v._hx_index);
 | 
						|
				} else
 | 
						|
					serializeString(Type.enumConstructor(v));
 | 
						|
				buf.add(":");
 | 
						|
				var params = Type.enumParameters(v);
 | 
						|
				buf.add(params.length);
 | 
						|
				for (p in params)
 | 
						|
					serialize(p);
 | 
						|
				#else
 | 
						|
				if (useEnumIndex) {
 | 
						|
					buf.add(":");
 | 
						|
					buf.add(v[1]);
 | 
						|
				} else
 | 
						|
					serializeString(v[0]);
 | 
						|
				buf.add(":");
 | 
						|
				var l = __getField(v, "length");
 | 
						|
				buf.add(l - 2);
 | 
						|
				for (i in 2...l)
 | 
						|
					serialize(v[i]);
 | 
						|
				#end
 | 
						|
				if (useCache)
 | 
						|
					cache.push(v);
 | 
						|
			case TFunction:
 | 
						|
				throw "Cannot serialize function";
 | 
						|
			default:
 | 
						|
				#if neko
 | 
						|
				if (untyped (__i32__kind != null && __dollar__iskind(v, __i32__kind))) {
 | 
						|
					buf.add("i");
 | 
						|
					buf.add(v);
 | 
						|
					return;
 | 
						|
				}
 | 
						|
				#end
 | 
						|
				throw "Cannot serialize " + Std.string(v);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	extern inline function __getField(o:Dynamic, f:String):Dynamic
 | 
						|
		return o[cast f];
 | 
						|
 | 
						|
	public function serializeException(e:Dynamic) {
 | 
						|
		buf.add("x");
 | 
						|
		#if flash
 | 
						|
		if (untyped __is__(e, __global__["Error"])) {
 | 
						|
			var e:flash.errors.Error = e;
 | 
						|
			var s = e.getStackTrace();
 | 
						|
			if (s == null)
 | 
						|
				serialize(e.message);
 | 
						|
			else
 | 
						|
				serialize(s);
 | 
						|
			return;
 | 
						|
		}
 | 
						|
		#end
 | 
						|
		serialize(e);
 | 
						|
	}
 | 
						|
 | 
						|
	/**
 | 
						|
	Serializes `v` and returns the String representation.
 | 
						|
 | 
						|
	This is a convenience function for creating a new instance of
 | 
						|
	Serializer, serialize `v` into it and obtain the result through a call
 | 
						|
	to `toString()`.
 | 
						|
**/
 | 
						|
	public static function run(v:Dynamic) {
 | 
						|
		var s = new Serializer();
 | 
						|
		s.serialize(v);
 | 
						|
		return s.toString();
 | 
						|
	}
 | 
						|
 | 
						|
	#if neko
 | 
						|
	static var base_encode = neko.Lib.load("std", "base_encode", 2);
 | 
						|
	#end
 | 
						|
}
 |