|
@@ -73,6 +73,7 @@ class Serializer {
|
|
|
|
|
|
var buf:StringBuf;
|
|
|
var cache:Array<Dynamic>;
|
|
|
+ var cacheMap:Array<Dynamic>;
|
|
|
var shash:haxe.ds.StringMap<Int>;
|
|
|
var scount:Int;
|
|
|
|
|
@@ -171,6 +172,23 @@ class Serializer {
|
|
|
buf.add(s);
|
|
|
}
|
|
|
|
|
|
+ function getRefIndex(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
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ cache.push(v);
|
|
|
+ return -1;
|
|
|
+ }
|
|
|
+
|
|
|
function serializeRef(v:Dynamic) {
|
|
|
#if js
|
|
|
var vt = js.Syntax.typeof(v);
|
|
@@ -493,11 +511,8 @@ class Serializer {
|
|
|
buf.add(0);
|
|
|
else {
|
|
|
buf.add(l);
|
|
|
- for (i in 0...l) {
|
|
|
- #if php
|
|
|
+ for (i in 0...l)
|
|
|
serialize(v.params[i]);
|
|
|
- #end
|
|
|
- }
|
|
|
}
|
|
|
#elseif (java || python || hl || eval)
|
|
|
if (useEnumIndex) {
|
|
@@ -553,6 +568,140 @@ class Serializer {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ public function copyValue(v:Dynamic) : Dynamic {
|
|
|
+ switch (Type.typeof(v)) {
|
|
|
+ case TNull, TInt, TFloat, TBool:
|
|
|
+ return v;
|
|
|
+ case TClass(c):
|
|
|
+ if (#if neko untyped c.__is_String #else c == String #end)
|
|
|
+ return v;
|
|
|
+ if (useCache) {
|
|
|
+ var index = getRefIndex(v);
|
|
|
+ if( index >= 0 ) return cacheMap[index];
|
|
|
+ }
|
|
|
+ switch (#if (neko || python) Type.getClassName(c) #else c #end) {
|
|
|
+ case #if (neko || python) "Array" #else cast Array #end:
|
|
|
+ var nv : Array<Dynamic> = [];
|
|
|
+ if( useCache ) cacheMap.push(nv);
|
|
|
+ #if (flash || python || hl)
|
|
|
+ var v:Array<Dynamic> = v;
|
|
|
+ #end
|
|
|
+ var l = #if (neko || flash || php || java || python || hl || lua || eval) v.length #elseif cpp v.__length() #else __getField(v,
|
|
|
+ "length") #end;
|
|
|
+ for (i in 0...l) {
|
|
|
+ var e : Dynamic = v[i];
|
|
|
+ if( e == null )
|
|
|
+ nv.push(null);
|
|
|
+ else
|
|
|
+ nv.push(copyValue(e));
|
|
|
+ }
|
|
|
+ return nv;
|
|
|
+ case #if (neko || python) "haxe.ds.List" #else cast List #end:
|
|
|
+ var nv = new List<Dynamic>();
|
|
|
+ if( useCache ) cacheMap.push(nv);
|
|
|
+ var v:List<Dynamic> = v;
|
|
|
+ for (e in v)
|
|
|
+ nv.add(copyValue(e));
|
|
|
+ return nv;
|
|
|
+ case #if (neko || python) "Date" #else cast Date #end:
|
|
|
+ return v;
|
|
|
+ case #if (neko || python) "haxe.ds.StringMap" #else cast haxe.ds.StringMap #end:
|
|
|
+ var nv = new haxe.ds.StringMap<Dynamic>();
|
|
|
+ if( useCache ) cacheMap.push(nv);
|
|
|
+ var v:haxe.ds.StringMap<Dynamic> = v;
|
|
|
+ for (k => e in v)
|
|
|
+ nv.set(k, copyValue(e));
|
|
|
+ return nv;
|
|
|
+ case #if (neko || python) "haxe.ds.IntMap" #else cast haxe.ds.IntMap #end:
|
|
|
+ var nv = new haxe.ds.IntMap<Dynamic>();
|
|
|
+ if( useCache ) cacheMap.push(nv);
|
|
|
+ var v:haxe.ds.IntMap<Dynamic> = v;
|
|
|
+ for (k => e in v)
|
|
|
+ nv.set(k, copyValue(e));
|
|
|
+ return nv;
|
|
|
+ case #if (neko || python) "haxe.ds.ObjectMap" #else cast haxe.ds.ObjectMap #end:
|
|
|
+ var nv = new haxe.ds.ObjectMap<Dynamic,Dynamic>();
|
|
|
+ if( useCache ) cacheMap.push(nv);
|
|
|
+ var v:haxe.ds.ObjectMap<Dynamic, Dynamic> = v;
|
|
|
+ for (k => e in v) {
|
|
|
+ #if (js || neko)
|
|
|
+ var id = Reflect.field(k, "__id__");
|
|
|
+ Reflect.deleteField(k, "__id__");
|
|
|
+ nv.set(k, copyValue(e));
|
|
|
+ Reflect.setField(k, "__id__", id);
|
|
|
+ #else
|
|
|
+ nv.set(k, copyValue(e));
|
|
|
+ #end
|
|
|
+ }
|
|
|
+ return nv;
|
|
|
+ case #if (neko || python) "haxe.io.Bytes" #else cast haxe.io.Bytes #end:
|
|
|
+ var v:haxe.io.Bytes = v;
|
|
|
+ var nv = v.sub(0, v.length);
|
|
|
+ if( useCache ) cacheMap.push(nv);
|
|
|
+ return nv;
|
|
|
+ default:
|
|
|
+ var nv = Type.createEmptyInstance(c);
|
|
|
+ if (useCache) cacheMap.push(nv);
|
|
|
+ copyFields(v, nv);
|
|
|
+ return nv;
|
|
|
+ }
|
|
|
+ case TObject:
|
|
|
+ if ( v is Class || v is Enum )
|
|
|
+ return v;
|
|
|
+ if (useCache) {
|
|
|
+ var index = getRefIndex(v);
|
|
|
+ if( index >= 0 ) return cacheMap[index];
|
|
|
+ }
|
|
|
+ var nv = {};
|
|
|
+ if( useCache ) cacheMap.push(nv);
|
|
|
+ copyFields(v, nv);
|
|
|
+ return nv;
|
|
|
+ case TEnum(e):
|
|
|
+ if (useCache) {
|
|
|
+ var index = getRefIndex(v);
|
|
|
+ if( index >= 0 ) return cacheMap[index];
|
|
|
+ }
|
|
|
+ var v : EnumValue = v;
|
|
|
+ var args = v.getParameters();
|
|
|
+ if( args.length == 0 ) {
|
|
|
+ if( useCache ) cacheMap.push(v);
|
|
|
+ return v;
|
|
|
+ }
|
|
|
+ var nv : Dynamic = Type.createEnumIndex(e, v.getIndex(), args);
|
|
|
+ var needCopy = false;
|
|
|
+ if( useCache ) cacheMap.push(nv);
|
|
|
+ for( i => e in args ) {
|
|
|
+ var e = copyValue(e);
|
|
|
+ #if neko
|
|
|
+ nv.args[i] = e;
|
|
|
+ #elseif php
|
|
|
+ nv.params[i] = e;
|
|
|
+ #elseif (js && !js_enums_as_arrays)
|
|
|
+ nv.__params__[i] = e;
|
|
|
+ #elseif js
|
|
|
+ nv[i+2] = e;
|
|
|
+ #else
|
|
|
+ needCopy = true;
|
|
|
+ args[i] = e;
|
|
|
+ #end
|
|
|
+ }
|
|
|
+ // only for platforms that don't know how to modify enum after create
|
|
|
+ // as a result, this might break some circular depencies
|
|
|
+ if( needCopy )
|
|
|
+ nv = Type.createEnumIndex(e, v.getIndex(), args);
|
|
|
+ return nv;
|
|
|
+ default:
|
|
|
+ return v; // assume not mutable
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ function copyFields( v : Dynamic, nv : Dynamic ) {
|
|
|
+ for (f in Reflect.fields(v)) {
|
|
|
+ var e = copyValue(Reflect.field(v,f));
|
|
|
+ Reflect.setField(nv, f, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
extern inline function __getField(o:Dynamic, f:String):Dynamic
|
|
|
return o[cast f];
|
|
|
|
|
@@ -585,6 +734,18 @@ class Serializer {
|
|
|
return s.toString();
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Performs a deep recursive copy of value `v`
|
|
|
+ * If you have several references to the same object inside the value or
|
|
|
+ * if you have circular references, you should set useCache to `true`.
|
|
|
+ */
|
|
|
+ public static function copy<T>(v:T,useCache=false):T {
|
|
|
+ var s = new Serializer();
|
|
|
+ s.useCache = useCache;
|
|
|
+ if( useCache ) s.cacheMap = [];
|
|
|
+ return s.copyValue(v);
|
|
|
+ }
|
|
|
+
|
|
|
#if neko
|
|
|
static var base_encode = neko.Lib.load("std", "base_encode", 2);
|
|
|
#end
|