소스 검색

recursive enum support + schema generation

ncannasse 9 년 전
부모
커밋
f9cf007e3f
3개의 변경된 파일142개의 추가작업 그리고 46개의 파일을 삭제
  1. 126 46
      hxd/net/Macros.hx
  2. 15 0
      hxd/net/Schema.hx
  3. 1 0
      hxd/net/Serializable.hx

+ 126 - 46
hxd/net/Macros.hx

@@ -29,27 +29,27 @@ enum RpcMode {
 	Owner;
 }
 
-enum PropTypeDesc {
+enum PropTypeDesc<PropType> {
 	PInt;
 	PFloat;
 	PBool;
 	PString;
 	PBytes;
-	PSerializable;
-	PEnum;
+	PSerializable( name : String );
+	PEnum( name : String );
 	PMap( k : PropType, v : PropType );
 	PArray( k : PropType );
 	PObj( fields : Array<{ name : String, type : PropType, opt : Bool }> );
 	PAlias( k : PropType );
 	PVector( k : PropType );
+	PNull( t : PropType );
 	PUnknown;
 }
 
 typedef PropType = {
-	var d : PropTypeDesc;
+	var d : PropTypeDesc<PropType>;
 	var t : ComplexType;
 	@:optional var isProxy : Bool;
-	@:optional var isNull : Bool;
 	@:optional var increment : Float;
 	@:optional var condSend : Expr;
 	@:optional var notMutable : Bool;
@@ -57,6 +57,8 @@ typedef PropType = {
 
 class Macros {
 
+	static var IN_ENUM_SER = false;
+
 	public static macro function serializeValue( ctx : Expr, v : Expr ) : Expr {
 		var t = Context.typeof(v);
 		var pt = getPropType(t);
@@ -64,6 +66,7 @@ class Macros {
 			Context.error("Unsupported serializable type " + t.toString(), v.pos);
 			return macro { };
 		}
+		IN_ENUM_SER = StringTools.startsWith(Context.getLocalClass().toString(), "hxd.net.enumSer.");
 		return serializeExpr(ctx, v, pt);
 	}
 
@@ -73,11 +76,40 @@ class Macros {
 		if( pt == null ) {
 			return macro { };
 		}
+		IN_ENUM_SER = StringTools.startsWith(Context.getLocalClass().toString(), "hxd.net.enumSer.");
 		return unserializeExpr(ctx, v, pt);
 	}
 
+	public static macro function getFieldType( v : Expr ) {
+		var t = Context.typeof(v);
+		var pt = getPropType(t);
+		if( pt == null )
+			return macro null;
+		var v = toFieldType(pt);
+		return macro $v{v};
+	}
+
 	#if macro
 
+	static function toFieldType( t : PropType ) : Schema.FieldType {
+		return switch( t.d ) {
+		case PInt: PInt;
+		case PFloat: PFloat;
+		case PBool: PBool;
+		case PString: PString;
+		case PBytes: PBytes;
+		case PSerializable(name): PSerializable(name);
+		case PEnum(name): PEnum(name);
+		case PMap(k, v): PMap(toFieldType(k), toFieldType(v));
+		case PArray(v): PArray(toFieldType(v));
+		case PObj(fields): PObj([for( f in fields ) { name : f.name, type : toFieldType(f.type), opt : f.opt }]);
+		case PAlias(t): return toFieldType(t);
+		case PVector(k): PVector(toFieldType(k));
+		case PNull(t): PNull(toFieldType(t));
+		case PUnknown: PUnknown;
+		};
+	}
+
 	static function isSerializable( c : Ref<ClassType> ) {
 		while( true ) {
 			var cg = c.get();
@@ -114,7 +146,7 @@ class Macros {
 				if( inc == null )
 					Context.error("Increment requires value parameter", m.pos);
 				switch( t.d ) {
-				case PFloat:
+				case PFloat, PNull({ d : PFloat }):
 					t.increment = inc;
 				default:
 					Context.error("Increment not allowed on " + t.t.toString(), m.pos);
@@ -181,8 +213,8 @@ class Macros {
 				if( pt == null ) return null;
 				PAlias(pt);
 			}
-		case TEnum(_):
-			PEnum;
+		case TEnum(e,_):
+			PEnum(e.toString());
 		case TAnonymous(a):
 			var a = a.get();
 			var fields = [];
@@ -215,7 +247,7 @@ class Macros {
 				throw "assert";
 			default:
 				if( isSerializable(c) )
-					PSerializable;
+					PSerializable(c.toString());
 				else
 					return null;
 			}
@@ -223,10 +255,8 @@ class Macros {
 			switch( td.toString() ) {
 			case "Null":
 				var p = getPropType(pl[0]);
-				if( p != null && !isNullable(p) ) {
-					p.isNull = true;
-					p.t = TPath( { pack : [], name : "Null", params : [TPType(p.t)] } );
-				}
+				if( p != null && !isNullable(p) )
+					p = { d : PNull(p), t : TPath( { pack : [], name : "Null", params : [TPType(p.t)] } ) };
 				return p;
 			default:
 				var p = getPropType(Context.follow(t, true));
@@ -248,7 +278,7 @@ class Macros {
 	static function isNullable( t : PropType ) {
 		switch( t.d ) {
 		case PInt, PFloat, PBool:
-			return t.isNull;
+			return false;
 		default:
 			return true;
 		}
@@ -263,11 +293,6 @@ class Macros {
 		if( t.isProxy && !skipCheck )
 			return serializeExpr(ctx, { expr : EField(v, "__value"), pos : v.pos }, t, true);
 
-		if( t.isNull && !skipCheck ) {
-			var e = serializeExpr(ctx, v, t, true);
-			return macro if( $v == null ) $ctx.addByte(0) else { $ctx.addByte(1); $e; };
-		}
-
 		switch( t.d ) {
 		case PFloat:
 			return macro $ctx.addFloat($v);
@@ -283,9 +308,12 @@ class Macros {
 			var vk = { expr : EConst(CIdent("k")), pos : v.pos };
 			var vv = { expr : EConst(CIdent("v")), pos : v.pos };
 			return macro $ctx.addMap($v, function(k:$kt) return hxd.net.Macros.serializeValue($ctx, $vk), function(v:$vt) return hxd.net.Macros.serializeValue($ctx, $vv));
-		case PEnum:
+		case PEnum(_):
 			var et = t.t;
-			return macro (null : hxd.net.Serializable.SerializableEnum<$et>).serialize($ctx,$v);
+			var ser = "serialize";
+			if( IN_ENUM_SER )
+				ser += "2";
+			return macro (null : hxd.net.Serializable.SerializableEnum<$et>).$ser($ctx,$v);
 		case PObj(fields):
 			var nullables = [for( f in fields ) if( isNullable(f.type) ) f];
 			var ct = t.t;
@@ -326,22 +354,19 @@ class Macros {
 			var at = toProxy(t);
 			var ve = { expr : EConst(CIdent("e")), pos : v.pos };
 			return macro $ctx.addVector($v, function(e:$at) return hxd.net.Macros.serializeValue($ctx, $ve));
-		case PSerializable:
+		case PSerializable(_):
 			return macro $ctx.addKnownRef($v);
 		case PAlias(t):
 			return serializeExpr(ctx, { expr : ECast(v, null), pos : v.pos }, t);
+		case PNull(t):
+			var e = serializeExpr(ctx, v, t);
+			return macro if( $v == null ) $ctx.addByte(0) else { $ctx.addByte(1); $e; };
 		case PUnknown:
 			throw "assert";
 		}
 	}
 
-	static function unserializeExpr( ctx : Expr, v : Expr, t : PropType, skipCheck = false ) {
-
-		if( t.isNull && !skipCheck ) {
-			var e = unserializeExpr(ctx, v, t, true);
-			return macro if( $ctx.getByte() == 0 ) $v = null else $e;
-		}
-
+	static function unserializeExpr( ctx : Expr, v : Expr, t : PropType ) {
 		switch( t.d ) {
 		case PFloat:
 			return macro $v = $ctx.getFloat();
@@ -361,9 +386,12 @@ class Macros {
 				var v : $vt;
 				$v = $ctx.getMap(function() { hxd.net.Macros.unserializeValue($ctx, $vk); return $vk; }, function() { hxd.net.Macros.unserializeValue($ctx, $vv); return $vv; });
 			};
-		case PEnum:
+		case PEnum(_):
 			var et = t.t;
-			return macro { var e : $et; e = (null : hxd.net.Serializable.SerializableEnum<$et>).unserialize($ctx); $v = e; }
+			var unser = "unserialize";
+			if( IN_ENUM_SER )
+				unser += "2";
+			return macro { var __e : $et; __e = (null : hxd.net.Serializable.SerializableEnum<$et>).$unser($ctx); $v = __e; }
 		case PObj(fields):
 			var nullables = [for( f in fields ) if( isNullable(f.type) ) f];
 			if( nullables.length >= 32 )
@@ -412,7 +440,7 @@ class Macros {
 				var e : $at;
 				$v = $ctx.getVector(function() { hxd.net.Macros.unserializeValue($ctx, e); return e; });
 			};
-		case PSerializable:
+		case PSerializable(_):
 			function loop(t:ComplexType) {
 				switch( t ) {
 				case TPath( { name : "Null", params:[TPType(t)] } ):
@@ -432,6 +460,9 @@ class Macros {
 				${unserializeExpr(ctx,macro v,at)};
 				$v = cast v;
 			};
+		case PNull(t):
+			var e = unserializeExpr(ctx, v, t);
+			return macro if( $ctx.getByte() == 0 ) $v = null else $e;
 		case PUnknown:
 			throw "assert";
 		}
@@ -523,6 +554,20 @@ class Macros {
 					expr : macro @:privateAccess { ${ if( isSubSer ) macro super.serialize(__ctx) else macro { } }; $b{el} }
 				}),
 			});
+			var schema = [for( s in toSerialize ) {
+				var name = s.f.name;
+				macro { schema.fieldsNames.push($v{name}); schema.fieldsTypes.push(hxd.net.Macros.getFieldType(this.$name)); }
+			}];
+			fields.push({
+				name : "getSerializeSchema",
+				pos : pos,
+				access : access,
+				kind : FFun({
+					args : [],
+					ret : null,
+					expr : macro { var schema = ${if( isSubSer ) macro super.getSerializeSchema() else macro new hxd.net.Schema()}; $b{schema}; return schema; }
+				})
+			});
 		}
 
 		if( needUnserialize ) {
@@ -567,8 +612,15 @@ class Macros {
 	}
 
 	public static function buildSerializableEnum() {
-		switch( Context.getLocalType() ) {
-		case TInst(_, [tenum = TEnum(e, [])]):
+		var pt = switch( Context.getLocalType() ) {
+		case TInst(_, [pt]): pt;
+		default: null;
+		}
+		if( pt != null )
+			pt = Context.follow(pt);
+		if( pt != null )
+		switch( pt ) {
+		case TEnum(e, tparams):
 			var e = e.get();
 			var name = e.pack.length == 0 ? e.name : e.pack.join("_") + "_" + e.name;
 			try {
@@ -593,8 +645,8 @@ class Macros {
 
 						var evals = [];
 						for( a in args ) {
-							var aname = "_"+a.name;
-							var at = a.t.toComplexType();
+							var aname = "_" + a.name;
+							var at = haxe.macro.TypeTools.applyTypeParameters(a.t,e.params,tparams).toComplexType();
 							evals.push(macro var $aname : $at);
 							evals.push(macro hxd.net.Macros.unserializeValue(ctx,$i{aname}));
 						}
@@ -619,20 +671,19 @@ class Macros {
 					name : name,
 					pack : ["hxd","net","enumSer"],
 					kind : TDClass(),
-					fields : [{
-						name : "serialize",
-						meta : [{name:":extern",pos:pos}],
-						access : [APublic, AInline],
+					fields : [
+					{
+						name : "doSerialize",
+						access : [AStatic],
 						pos : pos,
 						kind : FFun( {
-							args : [{ name : "ctx", type : macro : hxd.net.Serializer },{ name : "v", type : tenum.toComplexType() }],
+							args : [{ name : "ctx", type : macro : hxd.net.Serializer },{ name : "v", type : pt.toComplexType() }],
 							expr : macro @:privateAccess if( v == null ) ctx.addByte(0) else ${{ expr : ESwitch(macro v,cases,null), pos : pos }},
-							ret : null,
+							ret : macro : Void,
 						}),
 					},{
-						name : "unserialize",
-						access : [APublic, AInline],
-						meta : [{name:":extern",pos:pos}],
+						name : "doUnserialize",
+						access : [AStatic],
 						pos : pos,
 						kind : FFun( {
 							args : [{ name : "ctx", type : macro : hxd.net.Serializer }],
@@ -642,12 +693,41 @@ class Macros {
 									return null;
 								return ${{ expr : ESwitch(macro b,ucases,macro throw "Invalid enum index "+b), pos : pos }}
 							},
-							ret : tenum.toComplexType(),
+							ret : pt.toComplexType(),
 						}),
 
+					},{
+						name : "serialize",
+						access : [AInline, APublic],
+						meta : [{name:":extern",pos:pos}],
+						pos : pos,
+						kind : FFun( {
+							args : [{ name : "ctx", type : macro : hxd.net.Serializer },{ name : "v", type : pt.toComplexType() }],
+							expr : macro doSerialize(ctx,v),
+							ret : null,
+						}),
+					},{
+						name : "unserialize",
+						access : [AInline, APublic],
+						meta : [{name:":extern",pos:pos}],
+						pos : pos,
+						kind : FFun( {
+							args : [{ name : "ctx", type : macro : hxd.net.Serializer }],
+							expr : macro return doUnserialize(ctx),
+							ret : null,
+						}),
 					}],
 					pos : pos,
 				};
+
+				// hack to allow recursion (duplicate serialize/unserialize for recursive usage)
+				var tf = Reflect.copy(t.fields[t.fields.length - 2]);
+				tf.name += "2";
+				t.fields.push(tf);
+				var tf = Reflect.copy(t.fields[t.fields.length - 2]);
+				tf.name += "2";
+				t.fields.push(tf);
+
 				Context.defineType(t);
 				return Context.getType("hxd.net.enumSer." + name);
 			}

+ 15 - 0
hxd/net/Schema.hx

@@ -0,0 +1,15 @@
+package hxd.net;
+
+typedef FieldType = Macros.PropTypeDesc<FieldType>;
+
+class Schema #if !macro implements Serializable #end {
+
+	@:s @:notMutable public var fieldsNames : Array<String>;
+	@:s @:notMutable public var fieldsTypes : Array<FieldType>;
+
+	public function new() {
+		fieldsNames = [];
+		fieldsTypes = [];
+	}
+
+}

+ 1 - 0
hxd/net/Serializable.hx

@@ -6,6 +6,7 @@ interface Serializable {
 	public function getCLID() : Int;
 	public function serialize( ctx : Serializer ) : Void;
 	public function unserialize( ctx : Serializer ) : Void;
+	public function getSerializeSchema() : Schema;
 }
 
 @:genericBuild(hxd.net.Macros.buildSerializableEnum())