Browse Source

codecgen: Support RawExt, Builtins and (Text|JSON|Binary)(Unm|M)arshal .

Up till now, codecgen generated code did not handle RawExt, Builtins and (Text|JSON|Binary)(Unm|M)arshal .
This change includes the support for these.

Extensions are still not supported in codecgen at this time.

Update #95
Ugorji Nwoke 10 years ago
parent
commit
978848809c
4 changed files with 213 additions and 37 deletions
  1. 14 24
      codec/encode.go
  2. 60 0
      codec/gen-helper.generated.go
  3. 53 1
      codec/gen-helper.go.tmpl
  4. 86 12
      codec/gen.go

+ 14 - 24
codec/encode.go

@@ -337,14 +337,7 @@ func (f encFnInfo) selferMarshal(rv reflect.Value) {
 func (f encFnInfo) binaryMarshal(rv reflect.Value) {
 func (f encFnInfo) binaryMarshal(rv reflect.Value) {
 	if v, proceed := f.getValueForMarshalInterface(rv, f.ti.bmIndir); proceed {
 	if v, proceed := f.getValueForMarshalInterface(rv, f.ti.bmIndir); proceed {
 		bs, fnerr := v.(encoding.BinaryMarshaler).MarshalBinary()
 		bs, fnerr := v.(encoding.BinaryMarshaler).MarshalBinary()
-		if fnerr != nil {
-			panic(fnerr)
-		}
-		if bs == nil {
-			f.ee.EncodeNil()
-		} else {
-			f.ee.EncodeStringBytes(c_RAW, bs)
-		}
+		f.e.marshal(bs, fnerr, c_RAW)
 	}
 	}
 }
 }
 
 
@@ -352,28 +345,14 @@ func (f encFnInfo) textMarshal(rv reflect.Value) {
 	if v, proceed := f.getValueForMarshalInterface(rv, f.ti.tmIndir); proceed {
 	if v, proceed := f.getValueForMarshalInterface(rv, f.ti.tmIndir); proceed {
 		// debugf(">>>> encoding.TextMarshaler: %T", rv.Interface())
 		// debugf(">>>> encoding.TextMarshaler: %T", rv.Interface())
 		bs, fnerr := v.(encoding.TextMarshaler).MarshalText()
 		bs, fnerr := v.(encoding.TextMarshaler).MarshalText()
-		if fnerr != nil {
-			panic(fnerr)
-		}
-		if bs == nil {
-			f.ee.EncodeNil()
-		} else {
-			f.ee.EncodeStringBytes(c_UTF8, bs)
-		}
+		f.e.marshal(bs, fnerr, c_UTF8)
 	}
 	}
 }
 }
 
 
 func (f encFnInfo) jsonMarshal(rv reflect.Value) {
 func (f encFnInfo) jsonMarshal(rv reflect.Value) {
 	if v, proceed := f.getValueForMarshalInterface(rv, f.ti.jmIndir); proceed {
 	if v, proceed := f.getValueForMarshalInterface(rv, f.ti.jmIndir); proceed {
 		bs, fnerr := v.(jsonMarshaler).MarshalJSON()
 		bs, fnerr := v.(jsonMarshaler).MarshalJSON()
-		if fnerr != nil {
-			panic(fnerr)
-		}
-		if bs == nil {
-			f.ee.EncodeNil()
-		} else {
-			f.ee.EncodeStringBytes(c_UTF8, bs)
-		}
+		f.e.marshal(bs, fnerr, c_UTF8)
 	}
 	}
 }
 }
 
 
@@ -1214,6 +1193,17 @@ func (e *Encoder) getEncFn(rtid uintptr, rt reflect.Type, checkFastpath, checkCo
 	return
 	return
 }
 }
 
 
+func (e *Encoder) marshal(bs []byte, fnerr error, c charEncoding) {
+	if fnerr != nil {
+		panic(fnerr)
+	}
+	if bs == nil {
+		e.e.EncodeNil()
+	} else {
+		e.e.EncodeStringBytes(c, bs)
+	}
+}
+
 func (e *Encoder) errorf(format string, params ...interface{}) {
 func (e *Encoder) errorf(format string, params ...interface{}) {
 	err := fmt.Errorf(format, params...)
 	err := fmt.Errorf(format, params...)
 	panic(err)
 	panic(err)

+ 60 - 0
codec/gen-helper.generated.go

@@ -10,6 +10,8 @@
 
 
 package codec
 package codec
 
 
+import "encoding"
+
 // This file is used to generate helper code for codecgen.
 // This file is used to generate helper code for codecgen.
 // The values here i.e. genHelper(En|De)coder are not to be used directly by
 // The values here i.e. genHelper(En|De)coder are not to be used directly by
 // library users. They WILL change continously and without notice.
 // library users. They WILL change continously and without notice.
@@ -60,6 +62,32 @@ func (f genHelperEncoder) EncFallback(iv interface{}) {
 	f.e.encodeI(iv, false, false)
 	f.e.encodeI(iv, false, false)
 }
 }
 
 
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncTextMarshal(iv encoding.TextMarshaler) {
+	bs, fnerr := iv.MarshalText()
+	f.e.marshal(bs, fnerr, c_UTF8)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncJSONMarshal(iv jsonMarshaler) {
+	bs, fnerr := iv.MarshalJSON()
+	f.e.marshal(bs, fnerr, c_UTF8)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncBinaryMarshal(iv encoding.BinaryMarshaler) {
+	bs, fnerr := iv.MarshalBinary()
+	f.e.marshal(bs, fnerr, c_RAW)
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
+	if _, ok := f.e.hh.(*BincHandle); ok {
+		return timeTypId
+	}
+	return 0
+}
+
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) DecBasicHandle() *BasicHandle {
 func (f genHelperDecoder) DecBasicHandle() *BasicHandle {
 	return f.d.h
 	return f.d.h
@@ -100,3 +128,35 @@ func (f genHelperDecoder) DecStructFieldNotFound(index int, name string) {
 func (f genHelperDecoder) DecArrayCannotExpand(sliceLen, streamLen int) {
 func (f genHelperDecoder) DecArrayCannotExpand(sliceLen, streamLen int) {
 	f.d.arrayCannotExpand(sliceLen, streamLen)
 	f.d.arrayCannotExpand(sliceLen, streamLen)
 }
 }
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecTextUnmarshal(tm encoding.TextUnmarshaler) {
+	fnerr := tm.UnmarshalText(f.d.d.DecodeBytes(f.d.b[:], true, true))
+	if fnerr != nil {
+		panic(fnerr)
+	}
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecJSONUnmarshal(tm jsonUnmarshaler) {
+	fnerr := tm.UnmarshalJSON(f.d.d.DecodeBytes(f.d.b[:], true, true))
+	if fnerr != nil {
+		panic(fnerr)
+	}
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecBinaryUnmarshal(bm encoding.BinaryUnmarshaler) {
+	fnerr := bm.UnmarshalBinary(f.d.d.DecodeBytes(nil, false, true))
+	if fnerr != nil {
+		panic(fnerr)
+	}
+}
+
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
+	if _, ok := f.d.hh.(*BincHandle); ok {
+		return timeTypId
+	}
+	return 0
+}

+ 53 - 1
codec/gen-helper.go.tmpl

@@ -10,6 +10,8 @@
 
 
 package codec
 package codec
 
 
+import "encoding"
+
 // This file is used to generate helper code for codecgen. 
 // This file is used to generate helper code for codecgen. 
 // The values here i.e. genHelper(En|De)coder are not to be used directly by 
 // The values here i.e. genHelper(En|De)coder are not to be used directly by 
 // library users. They WILL change continously and without notice.
 // library users. They WILL change continously and without notice.
@@ -48,6 +50,7 @@ type genHelperDecoder struct {
 func (f genHelperEncoder) EncBasicHandle() *BasicHandle {
 func (f genHelperEncoder) EncBasicHandle() *BasicHandle {
 	return f.e.h
 	return f.e.h
 }
 }
+
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperEncoder) EncBinary() bool {
 func (f genHelperEncoder) EncBinary() bool {
 	return f.e.be // f.e.hh.isBinaryEncoding()
 	return f.e.be // f.e.hh.isBinaryEncoding()
@@ -57,6 +60,28 @@ func (f genHelperEncoder) EncFallback(iv interface{}) {
 	// println(">>>>>>>>> EncFallback")
 	// println(">>>>>>>>> EncFallback")
 	f.e.encodeI(iv, false, false)
 	f.e.encodeI(iv, false, false)
 }
 }
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncTextMarshal(iv encoding.TextMarshaler) {
+	bs, fnerr := iv.MarshalText()
+	f.e.marshal(bs, fnerr, c_UTF8)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncJSONMarshal(iv jsonMarshaler) {
+	bs, fnerr := iv.MarshalJSON()
+	f.e.marshal(bs, fnerr, c_UTF8)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) EncBinaryMarshal(iv encoding.BinaryMarshaler) {
+	bs, fnerr := iv.MarshalBinary()
+	f.e.marshal(bs, fnerr, c_RAW)
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperEncoder) TimeRtidIfBinc() uintptr {
+	if _, ok := f.e.hh.(*BincHandle); ok {
+		return timeTypId 
+	}
+	return 0
+}
 
 
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 // FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
 func (f genHelperDecoder) DecBasicHandle() *BasicHandle {
 func (f genHelperDecoder) DecBasicHandle() *BasicHandle {
@@ -91,7 +116,34 @@ func (f genHelperDecoder) DecStructFieldNotFound(index int, name string) {
 func (f genHelperDecoder) DecArrayCannotExpand(sliceLen, streamLen int) {
 func (f genHelperDecoder) DecArrayCannotExpand(sliceLen, streamLen int) {
 	f.d.arrayCannotExpand(sliceLen, streamLen)
 	f.d.arrayCannotExpand(sliceLen, streamLen)
 }
 }
-
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecTextUnmarshal(tm encoding.TextUnmarshaler) {
+	fnerr := tm.UnmarshalText(f.d.d.DecodeBytes(f.d.b[:], true, true))
+	if fnerr != nil {
+		panic(fnerr)
+	}
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecJSONUnmarshal(tm jsonUnmarshaler) {
+	fnerr := tm.UnmarshalJSON(f.d.d.DecodeBytes(f.d.b[:], true, true))
+	if fnerr != nil {
+		panic(fnerr)
+	}
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) DecBinaryUnmarshal(bm encoding.BinaryUnmarshaler) {
+	fnerr := bm.UnmarshalBinary(f.d.d.DecodeBytes(nil, false, true))
+	if fnerr != nil {
+		panic(fnerr)
+	}
+}
+// FOR USE BY CODECGEN ONLY. IT *WILL* CHANGE WITHOUT NOTICE. *DO NOT USE*
+func (f genHelperDecoder) TimeRtidIfBinc() uintptr {
+	if _, ok := f.d.hh.(*BincHandle); ok {
+		return timeTypId 
+	}
+	return 0
+}
 
 
 {{/*
 {{/*
 
 

+ 86 - 12
codec/gen.go

@@ -22,17 +22,20 @@ import (
 )
 )
 
 
 // ---------------------------------------------------
 // ---------------------------------------------------
-// codecgen only works in the following:
-//   - extensions are not supported.
-//     Do not make a type a Selfer and an extension.
-//   - Canonical option is ignored (not supported AT THIS TIME).
+// codecgen MOSTLY supports things that are static.
+// This means that, for dynamic things, we MUST use reflection to at least get the reflect.Type.
+//
+// Currently, codecgen doesn't support the following:
+//   - extensions
+//
+// In addition, codecgen doesn't support the following:
+//   - Canonical option. (codecgen IGNORES it currently)
 //     This is just because it has not been implemented.
 //     This is just because it has not been implemented.
-//   - Selfer takes precedence.
-//     Any type that implements it knows how to encode/decode itself statically.
-//     Extensions are only known at runtime.
-//     codecgen only looks at the Kind of the type.
 //
 //
-//   - the following types are supported:
+// During encode/decode, Selfer takes precedence.
+// A type implementing Selfer will know how to encode/decode itself statically.
+//
+//  The following field types are supported:
 //     array: [n]T
 //     array: [n]T
 //     slice: []T
 //     slice: []T
 //     map: map[K]V
 //     map: map[K]V
@@ -141,9 +144,9 @@ func Gen(w io.Writer, buildTags, pkgName string, useUnsafe bool, typ ...reflect.
 		bp:     typ[0].PkgPath(),
 		bp:     typ[0].PkgPath(),
 		rr:     rand.New(rand.NewSource(time.Now().UnixNano())),
 		rr:     rand.New(rand.NewSource(time.Now().UnixNano())),
 	}
 	}
-
 	// gather imports first:
 	// gather imports first:
 	x.cp = reflect.TypeOf(x).PkgPath()
 	x.cp = reflect.TypeOf(x).PkgPath()
+	x.imn[x.cp] = genCodecPkg
 	for _, t := range typ {
 	for _, t := range typ {
 		// fmt.Printf("###########: PkgPath: '%v', Name: '%s'\n", t.PkgPath(), t.Name())
 		// fmt.Printf("###########: PkgPath: '%v', Name: '%s'\n", t.PkgPath(), t.Name())
 		if t.PkgPath() != x.bp {
 		if t.PkgPath() != x.bp {
@@ -544,17 +547,20 @@ func (x *genRunner) encVar(varname string, t reflect.Type) {
 // enc will encode a variable (varname) of type T,
 // enc will encode a variable (varname) of type T,
 // except t is of kind reflect.Struct or reflect.Array, wherein varname is of type *T (to prevent copying)
 // except t is of kind reflect.Struct or reflect.Array, wherein varname is of type *T (to prevent copying)
 func (x *genRunner) enc(varname string, t reflect.Type) {
 func (x *genRunner) enc(varname string, t reflect.Type) {
-	// varName here must be to a pointer to a struct, or to a value directly.
+	// varName here must be to a pointer to a struct/array, or to a value directly.
 	rtid := reflect.ValueOf(t).Pointer()
 	rtid := reflect.ValueOf(t).Pointer()
 	// We call CodecEncodeSelf if one of the following are honored:
 	// We call CodecEncodeSelf if one of the following are honored:
 	//   - the type already implements Selfer, call that
 	//   - the type already implements Selfer, call that
 	//   - the type has a Selfer implementation just created, use that
 	//   - the type has a Selfer implementation just created, use that
 	//   - the type is in the list of the ones we will generate for, but it is not currently being generated
 	//   - the type is in the list of the ones we will generate for, but it is not currently being generated
+
+	tptr := reflect.PtrTo(t)
 	if t.Implements(selferTyp) {
 	if t.Implements(selferTyp) {
 		x.line(varname + ".CodecEncodeSelf(e)")
 		x.line(varname + ".CodecEncodeSelf(e)")
 		return
 		return
 	}
 	}
-	if t.Kind() == reflect.Struct && reflect.PtrTo(t).Implements(selferTyp) {
+	// if t.Kind() == reflect.Struct && tptr.Implements(selferTyp) { //TODO: verify that no need to check struct
+	if tptr.Implements(selferTyp) {
 		x.line(varname + ".CodecEncodeSelf(e)")
 		x.line(varname + ".CodecEncodeSelf(e)")
 		return
 		return
 	}
 	}
@@ -580,6 +586,39 @@ func (x *genRunner) enc(varname string, t reflect.Type) {
 		rtidAdded = true
 		rtidAdded = true
 	}
 	}
 
 
+	// check if
+	//   - type is RawExt
+	//   - the type implements (Text|JSON|Binary)(Unm|M)arshal
+	mi := x.varsfx()
+	x.linef("%sm%s := z.EncBinary()", genTempVarPfx, mi)
+	x.linef("_ = %sm%s", genTempVarPfx, mi)
+	x.line("if false {")           //start if block
+	defer func() { x.line("}") }() //end if block
+
+	if t == rawExtTyp {
+		x.linef("} else { r.EncodeRawExt(%v, e)", varname)
+		return
+	}
+	// HACK: Support for Builtins.
+	//       Currently, only Binc supports builtins, and the only builtin type is time.Time.
+	//       Have a method that returns the rtid for time.Time if Handle is Binc.
+	if t == timeTyp {
+		vrtid := genTempVarPfx + "m" + x.varsfx()
+		x.linef("} else if %s := z.TimeRtidIfBinc(); %s != 0 { ", vrtid, vrtid)
+		x.linef("r.EncodeBuiltin(%s, %s)", vrtid, varname)
+	}
+
+	if t.Implements(binaryMarshalerTyp) || tptr.Implements(binaryMarshalerTyp) {
+		x.linef("} else if %sm%s { z.EncBinaryMarshal(%v) ", genTempVarPfx, mi, varname)
+	}
+	if t.Implements(textMarshalerTyp) || tptr.Implements(textMarshalerTyp) {
+		x.linef("} else if !%sm%s { z.EncTextMarshal(%v) ", genTempVarPfx, mi, varname)
+	} else if t.Implements(jsonMarshalerTyp) || tptr.Implements(jsonMarshalerTyp) {
+		x.linef("} else if !%sm%s { z.EncJSONMarshal(%v) ", genTempVarPfx, mi, varname)
+	}
+
+	x.line("} else {")
+
 	switch t.Kind() {
 	switch t.Kind() {
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 		x.line("r.EncodeInt(int64(" + varname + "))")
 		x.line("r.EncodeInt(int64(" + varname + "))")
@@ -973,6 +1012,7 @@ func (x *genRunner) dec(varname string, t reflect.Type) {
 	//   - the varname is to a pointer already. No need to take address of it
 	//   - the varname is to a pointer already. No need to take address of it
 
 
 	rtid := reflect.ValueOf(t).Pointer()
 	rtid := reflect.ValueOf(t).Pointer()
+	tptr := reflect.PtrTo(t)
 	if t.Implements(selferTyp) || (t.Kind() == reflect.Struct &&
 	if t.Implements(selferTyp) || (t.Kind() == reflect.Struct &&
 		reflect.PtrTo(t).Implements(selferTyp)) {
 		reflect.PtrTo(t).Implements(selferTyp)) {
 		x.line(varname + ".CodecDecodeSelf(d)")
 		x.line(varname + ".CodecDecodeSelf(d)")
@@ -1000,6 +1040,40 @@ func (x *genRunner) dec(varname string, t reflect.Type) {
 		rtidAdded = true
 		rtidAdded = true
 	}
 	}
 
 
+	// check if
+	//   - type is RawExt
+	//   - the type implements (Text|JSON|Binary)(Unm|M)arshal
+	mi := x.varsfx()
+	x.linef("%sm%s := z.DecBinary()", genTempVarPfx, mi)
+	x.linef("_ = %sm%s", genTempVarPfx, mi)
+	x.line("if false {")           //start if block
+	defer func() { x.line("}") }() //end if block
+
+	if t == rawExtTyp {
+		x.linef("} else { r.DecodeExt(%v, 0, nil)", varname)
+		return
+	}
+
+	// HACK: Support for Builtins.
+	//       Currently, only Binc supports builtins, and the only builtin type is time.Time.
+	//       Have a method that returns the rtid for time.Time if Handle is Binc.
+	if t == timeTyp {
+		vrtid := genTempVarPfx + "m" + x.varsfx()
+		x.linef("} else if %s := z.TimeRtidIfBinc(); %s != 0 { ", vrtid, vrtid)
+		x.linef("r.DecodeBuiltin(%s, %s)", vrtid, varname)
+	}
+
+	if t.Implements(binaryUnmarshalerTyp) || tptr.Implements(binaryUnmarshalerTyp) {
+		x.linef("} else if %sm%s { z.DecBinaryUnmarshal(%v) ", genTempVarPfx, mi, varname)
+	}
+	if t.Implements(textUnmarshalerTyp) || tptr.Implements(textUnmarshalerTyp) {
+		x.linef("} else if !%sm%s { z.DecTextUnmarshal(%v)", genTempVarPfx, mi, varname)
+	} else if t.Implements(jsonUnmarshalerTyp) || tptr.Implements(jsonUnmarshalerTyp) {
+		x.linef("} else if !%sm%s { z.DecJSONUnmarshal(%v)", genTempVarPfx, mi, varname)
+	}
+
+	x.line("} else {")
+
 	// Since these are pointers, we cannot share, and have to use them one by one
 	// Since these are pointers, we cannot share, and have to use them one by one
 	switch t.Kind() {
 	switch t.Kind() {
 	case reflect.Int:
 	case reflect.Int: