From 60728759f9d7e5828e3fa5b7bcbebbc925b5068b Mon Sep 17 00:00:00 2001 From: "Hyang-Ah (Hana) Kim" Date: Mon, 28 Sep 2015 15:08:01 -0400 Subject: [PATCH] bind: add const type support Update golang/go#12475 Change-Id: I7fdc22462b5925c84ebbeb54517032c2fbd0545b Reviewed-on: https://go-review.googlesource.com/15120 Reviewed-by: David Crawshaw --- bind/gengo.go | 3 +- bind/genjava.go | 50 +++++++++--- bind/genobjc.go | 88 ++++++++++++++++++++-- bind/java/SeqTest.java | 16 ++++ bind/java/testpkg/testpkg.go | 18 +++++ bind/objc/SeqTest.m | 59 +++++++++++++++ bind/objc/testpkg/objc_testpkg/GoTestpkg.h | 14 ++++ bind/objc/testpkg/objc_testpkg/GoTestpkg.m | 14 ++++ bind/objc/testpkg/testpkg.go | 18 +++++ bind/testdata/basictypes.go | 9 +++ bind/testdata/basictypes.java.golden | 6 ++ bind/testdata/basictypes.objc.h.golden | 7 ++ bind/testdata/basictypes.objc.m.golden | 7 ++ 13 files changed, 292 insertions(+), 17 deletions(-) diff --git a/bind/gengo.go b/bind/gengo.go index 93fdd51..258621d 100644 --- a/bind/gengo.go +++ b/bind/gengo.go @@ -385,7 +385,6 @@ func (g *goGen) gen() error { } switch obj := obj.(type) { - // TODO(crawshaw): case *types.Const: // TODO(crawshaw): case *types.Var: case *types.Func: // TODO(crawshaw): functions that are not implementable from @@ -402,7 +401,7 @@ func (g *goGen) gen() error { case *types.Interface: g.genInterface(obj) } - + case *types.Const: default: g.errorf("not yet supported, name for %v / %T", obj, obj) continue diff --git a/bind/genjava.go b/bind/genjava.go index ee09ed0..d4bd18e 100644 --- a/bind/genjava.go +++ b/bind/genjava.go @@ -7,9 +7,11 @@ package bind import ( "bytes" "fmt" + "go/constant" "go/token" "go/types" "io" + "math" "regexp" "strings" ) @@ -315,7 +317,7 @@ func (g *javaGen) javaType(T types.Type) string { switch T := T.(type) { case *types.Basic: switch T.Kind() { - case types.Bool: + case types.Bool, types.UntypedBool: return "boolean" case types.Int: return "long" @@ -323,23 +325,23 @@ func (g *javaGen) javaType(T types.Type) string { return "byte" case types.Int16: return "short" - case types.Int32: + case types.Int32, types.UntypedRune: // types.Rune return "int" - case types.Int64: + case types.Int64, types.UntypedInt: return "long" - case types.Uint8: + case types.Uint8: // types.Byte // TODO(crawshaw): Java bytes are signed, so this is // questionable, but vital. return "byte" // TODO(crawshaw): case types.Uint, types.Uint16, types.Uint32, types.Uint64: case types.Float32: return "float" - case types.Float64: + case types.Float64, types.UntypedFloat: return "double" - case types.String: + case types.String, types.UntypedString: return "String" default: - g.errorf("unsupported return type: %s", T) + g.errorf("unsupported basic type: %s", T) return "TODO" } case *types.Slice: @@ -597,6 +599,35 @@ func (g *javaGen) className() string { return strings.Title(javaNameReplacer.Replace(g.pkg.Name())) } +func (g *javaGen) genConst(o *types.Const) { + // TODO(hyangah): should const names use upper cases + "_"? + // TODO(hyangah): check invalid names. + jType := g.javaType(o.Type()) + val := o.Val().String() + switch b := o.Type().(*types.Basic); b.Kind() { + case types.Int64, types.UntypedInt: + i, exact := constant.Int64Val(o.Val()) + if !exact { + g.errorf("const value %s for %s cannot be represented as %s", val, o.Name(), jType) + return + } + val = fmt.Sprintf("%dL", i) + + case types.Float32: + f, _ := constant.Float32Val(o.Val()) + val = fmt.Sprintf("%gf", f) + + case types.Float64, types.UntypedFloat: + f, _ := constant.Float64Val(o.Val()) + if math.IsInf(f, 0) || math.Abs(f) > math.MaxFloat64 { + g.errorf("const value %s for %s cannot be represented as %s", val, o.Name(), jType) + return + } + val = fmt.Sprintf("%g", f) + } + g.Printf("public static final %s %s = %s;\n", g.javaType(o.Type()), o.Name(), val) +} + func (g *javaGen) gen() error { g.Printf(javaPreamble, g.javaPkg, g.className(), g.gobindOpts(), g.pkg.Path()) @@ -613,7 +644,6 @@ func (g *javaGen) gen() error { } switch o := obj.(type) { - // TODO(crawshaw): case *types.Const: // TODO(crawshaw): case *types.Var: case *types.Func: if isCallable(o) { @@ -631,8 +661,10 @@ func (g *javaGen) gen() error { g.errorf("%s: cannot generate binding for %s: %T", g.fset.Position(o.Pos()), o.Name(), t) continue } + case *types.Const: + g.genConst(o) default: - g.errorf("unsupported exported type: ", obj) + g.errorf("unsupported exported type: %T", obj) } } diff --git a/bind/genobjc.go b/bind/genobjc.go index fcc7350..181b17a 100644 --- a/bind/genobjc.go +++ b/bind/genobjc.go @@ -6,8 +6,10 @@ package bind import ( "fmt" + "go/constant" "go/token" "go/types" + "math" "strings" ) @@ -26,6 +28,7 @@ type objcGen struct { namePrefix string funcs []*types.Func names []*types.TypeName + constants []*types.Const } func (g *objcGen) init() { @@ -47,7 +50,15 @@ func (g *objcGen) init() { } case *types.TypeName: g.names = append(g.names, obj) - // TODO(hyangah): *types.Const, *types.Var + case *types.Const: + if _, ok := obj.Type().(*types.Basic); !ok { + g.errorf("unsupported exported const for %s: %T", obj.Name(), obj) + continue + } + g.constants = append(g.constants, obj) + default: + g.errorf("unsupported exported type for %s: %T", obj.Name(), obj) + // TODO(hyangah): *types.Var } } } @@ -95,6 +106,19 @@ func (g *objcGen) genH() error { } } + // const + for _, obj := range g.constants { + switch b := obj.Type().(*types.Basic); b.Kind() { + case types.String, types.UntypedString: + g.Printf("FOUNDATION_EXPORT NSString* const %s%s;\n", g.namePrefix, obj.Name()) + default: + g.Printf("FOUNDATION_EXPORT const %s %s%s;\n", g.objcType(obj.Type()), g.namePrefix, obj.Name()) + } + } + if len(g.constants) > 0 { + g.Printf("\n") + } + // static functions. for _, obj := range g.funcs { g.genFuncH(obj) @@ -155,6 +179,13 @@ func (g *objcGen) genM() error { g.Printf("\n") } + // const + for _, o := range g.constants { + g.genConstM(o) + } + if len(g.constants) > 0 { + g.Printf("\n") + } // global functions. for _, obj := range g.funcs { g.genFuncM(obj) @@ -179,6 +210,51 @@ func (g *objcGen) genM() error { return nil } +func (g *objcGen) genConstM(o *types.Const) { + cName := fmt.Sprintf("%s%s", g.namePrefix, o.Name()) + cType := g.objcType(o.Type()) + + switch b := o.Type().(*types.Basic); b.Kind() { + case types.Bool, types.UntypedBool: + v := "NO" + if constant.BoolVal(o.Val()) { + v = "YES" + } + g.Printf("const BOOL %s = %s;\n", cName, v) + + case types.String, types.UntypedString: + g.Printf("NSString* const %s = @%s;\n", cName, o.Val()) + + case types.Int, types.Int8, types.Int16, types.Int32: + g.Printf("const %s %s = %s;\n", cType, cName, o.Val()) + + case types.Int64, types.UntypedInt: + i, exact := constant.Int64Val(o.Val()) + if !exact { + g.errorf("const value %s for %s cannot be represented as %s", o.Val(), o.Name(), cType) + return + } + if i == math.MinInt64 { + // -9223372036854775808LL does not work because 922337203685477508 is + // larger than max int64. + g.Printf("const int64_t %s = %dLL-1;\n", cName, i+1) + } else { + g.Printf("const int64_t %s = %dLL;\n", cName, i) + } + + case types.Float32, types.Float64, types.UntypedFloat: + f, _ := constant.Float64Val(o.Val()) + if math.IsInf(f, 0) || math.Abs(f) > math.MaxFloat64 { + g.errorf("const value %s for %s cannot be represented as double", o.Val(), o.Name()) + return + } + g.Printf("const %s %s = %g;\n", cType, cName, f) + + default: + g.errorf("unsupported const type %s for %s", b, o.Name()) + } +} + type funcSummary struct { name string ret string @@ -754,7 +830,7 @@ func (g *objcGen) objcType(typ types.Type) string { switch typ := typ.(type) { case *types.Basic: switch typ.Kind() { - case types.Bool: + case types.Bool, types.UntypedBool: return "BOOL" case types.Int: return "int" @@ -762,9 +838,9 @@ func (g *objcGen) objcType(typ types.Type) string { return "int8_t" case types.Int16: return "int16_t" - case types.Int32: + case types.Int32, types.UntypedRune: // types.Rune return "int32_t" - case types.Int64: + case types.Int64, types.UntypedInt: return "int64_t" case types.Uint8: // byte is an alias of uint8, and the alias is lost. @@ -777,9 +853,9 @@ func (g *objcGen) objcType(typ types.Type) string { return "uint64_t" case types.Float32: return "float" - case types.Float64: + case types.Float64, types.UntypedFloat: return "double" - case types.String: + case types.String, types.UntypedString: return "NSString*" default: g.errorf("unsupported type: %s", typ) diff --git a/bind/java/SeqTest.java b/bind/java/SeqTest.java index 81cb866..2f0927f 100644 --- a/bind/java/SeqTest.java +++ b/bind/java/SeqTest.java @@ -17,6 +17,22 @@ public class SeqTest extends AndroidTestCase { public SeqTest() { } + public void testConst() { + assertEquals("const String", "a string", Testpkg.AString); + assertEquals("const Int", 7, Testpkg.AnInt); + assertEquals("const Bool", true, Testpkg.ABool); + assertEquals("const Float", 0.12345, Testpkg.AFloat, 0.0001); + + assertEquals("const MinInt32", -1<<31, Testpkg.MinInt32); + assertEquals("const MaxInt32", (1<<31) - 1, Testpkg.MaxInt32); + assertEquals("const MinInt64", -1L<<63, Testpkg.MinInt64); + assertEquals("const MaxInt64", (1L<<63) - 1, Testpkg.MaxInt64); + assertEquals("const SmallestNonzeroFloat64", 4.940656458412465441765687928682213723651e-324, Testpkg.SmallestNonzeroFloat64, 1e-323); + assertEquals("const MaxFloat64", 1.797693134862315708145274237317043567981e+308, Testpkg.MaxFloat64, 0.0001); + assertEquals("const SmallestNonzeroFloat32", 1.401298464324817070923729583289916131280e-45, Testpkg.SmallestNonzeroFloat32, 1e-44); + assertEquals("const MaxFloat32", 3.40282346638528859811704183484516925440e+38, Testpkg.MaxFloat32, 0.0001); + assertEquals("const Log2E", 1/0.693147180559945309417232121458176568075500134360255254120680009, Testpkg.Log2E, 0.0001); + } public void testAssets() { String want = "Hello, Assets.\n"; String got = Testpkg.ReadAsset(); diff --git a/bind/java/testpkg/testpkg.go b/bind/java/testpkg/testpkg.go index 474bacc..1b3f498 100644 --- a/bind/java/testpkg/testpkg.go +++ b/bind/java/testpkg/testpkg.go @@ -15,12 +15,30 @@ import ( "fmt" "io/ioutil" "log" + "math" "runtime" "time" "golang.org/x/mobile/asset" ) +const ( + AString = "a string" + AnInt = 7 + ABool = true + AFloat = 0.12345 + + MinInt32 int32 = math.MinInt32 + MaxInt32 int32 = math.MaxInt32 + MinInt64 = math.MinInt64 + MaxInt64 = math.MaxInt64 + SmallestNonzeroFloat64 = math.SmallestNonzeroFloat64 + MaxFloat64 = math.MaxFloat64 + SmallestNonzeroFloat32 float32 = math.SmallestNonzeroFloat64 + MaxFloat32 float32 = math.MaxFloat32 + Log2E = math.Log2E +) + type I interface { F() diff --git a/bind/objc/SeqTest.m b/bind/objc/SeqTest.m index 2607a67..5d1cbf8 100644 --- a/bind/objc/SeqTest.m +++ b/bind/objc/SeqTest.m @@ -15,6 +15,63 @@ static int err = 0; +void testConst() { + if (![GoTestpkgAString isEqualToString:@"a string"]) { + ERROR(@"GoTestpkgAString = %@, want 'a string'", GoTestpkgAString); + } + if (GoTestpkgAnInt != 7) { + ERROR(@"GoTestpkgAnInt = %lld, want 7", GoTestpkgAnInt); + } + if (ABS(GoTestpkgAFloat - 0.12345) > 0.0001) { + ERROR(@"GoTestpkgAFloat = %f, want 0.12345", GoTestpkgAFloat); + } + if (GoTestpkgABool != YES) { + ERROR(@"GoTestpkgABool = %@, want YES", GoTestpkgAFloat ? @"YES" : @"NO"); + } + + if (GoTestpkgMinInt32 != INT32_MIN) { + ERROR(@"GoTestpkgMinInt32 = %d, want %d", GoTestpkgMinInt32, INT32_MIN); + } + if (GoTestpkgMaxInt32 != INT32_MAX) { + ERROR(@"GoTestpkgMaxInt32 = %d, want %d", GoTestpkgMaxInt32, INT32_MAX); + } + if (GoTestpkgMinInt64 != INT64_MIN) { + ERROR(@"GoTestpkgMinInt64 = %lld, want %lld", GoTestpkgMinInt64, INT64_MIN); + } + if (GoTestpkgMaxInt64 != INT64_MAX) { + ERROR(@"GoTestpkgMaxInt64 = %lld, want %lld", GoTestpkgMaxInt64, INT64_MAX); + } + if (ABS(GoTestpkgSmallestNonzeroFloat64 - + 4.940656458412465441765687928682213723651e-324) > 1e-323) { + ERROR(@"GoTestpkgSmallestNonzeroFloat64 = %f, want %f", + GoTestpkgSmallestNonzeroFloat64, + 4.940656458412465441765687928682213723651e-324); + } + if (ABS(GoTestpkgMaxFloat64 - + 1.797693134862315708145274237317043567981e+308) > 0.0001) { + ERROR(@"GoTestpkgMaxFloat64 = %f, want %f", GoTestpkgMaxFloat64, + 1.797693134862315708145274237317043567981e+308); + } + if (ABS(GoTestpkgSmallestNonzeroFloat32 - + 1.401298464324817070923729583289916131280e-45) > 1e-44) { + ERROR(@"GoTestpkgSmallestNonzeroFloat32 = %f, want %f", + GoTestpkgSmallestNonzeroFloat32, + 1.401298464324817070923729583289916131280e-45); + } + if (ABS(GoTestpkgMaxFloat32 - 3.40282346638528859811704183484516925440e+38) > + 0.0001) { + ERROR(@"GoTestpkgMaxFloat32 = %f, want %f", GoTestpkgMaxFloat32, + 3.40282346638528859811704183484516925440e+38); + } + if (ABS(GoTestpkgLog2E - + 1 / 0.693147180559945309417232121458176568075500134360255254120680009) > + 0.0001) { + ERROR( + @"GoTestpkgLog2E = %f, want %f", GoTestpkgLog2E, + 1 / 0.693147180559945309417232121458176568075500134360255254120680009); + } +} + void testHello(NSString *input) { NSString *got = GoTestpkgHello(input); NSString *want = [NSString stringWithFormat:@"Hello, %@!", input]; @@ -229,6 +286,8 @@ int main(void) { ERROR(@"GoTestpkgSum(31, 21) = %lld, want 52\n", sum); } + testConst(); + testHello(@"세계"); // korean, utf-8, world. unichar t[] = { diff --git a/bind/objc/testpkg/objc_testpkg/GoTestpkg.h b/bind/objc/testpkg/objc_testpkg/GoTestpkg.h index 224a18d..8c33bc7 100644 --- a/bind/objc/testpkg/objc_testpkg/GoTestpkg.h +++ b/bind/objc/testpkg/objc_testpkg/GoTestpkg.h @@ -42,6 +42,20 @@ - (NSString*)TryTwoStrings:(NSString*)first second:(NSString*)second; @end +FOUNDATION_EXPORT const BOOL GoTestpkgABool; +FOUNDATION_EXPORT const double GoTestpkgAFloat; +FOUNDATION_EXPORT NSString* const GoTestpkgAString; +FOUNDATION_EXPORT const int64_t GoTestpkgAnInt; +FOUNDATION_EXPORT const double GoTestpkgLog2E; +FOUNDATION_EXPORT const float GoTestpkgMaxFloat32; +FOUNDATION_EXPORT const double GoTestpkgMaxFloat64; +FOUNDATION_EXPORT const int32_t GoTestpkgMaxInt32; +FOUNDATION_EXPORT const int64_t GoTestpkgMaxInt64; +FOUNDATION_EXPORT const int32_t GoTestpkgMinInt32; +FOUNDATION_EXPORT const int64_t GoTestpkgMinInt64; +FOUNDATION_EXPORT const float GoTestpkgSmallestNonzeroFloat32; +FOUNDATION_EXPORT const double GoTestpkgSmallestNonzeroFloat64; + FOUNDATION_EXPORT NSData* GoTestpkgBytesAppend(NSData* a, NSData* b); FOUNDATION_EXPORT BOOL GoTestpkgCallIError(id i, BOOL triggerError, NSError** error); diff --git a/bind/objc/testpkg/objc_testpkg/GoTestpkg.m b/bind/objc/testpkg/objc_testpkg/GoTestpkg.m index 345ef21..4ef2d3c 100644 --- a/bind/objc/testpkg/objc_testpkg/GoTestpkg.m +++ b/bind/objc/testpkg/objc_testpkg/GoTestpkg.m @@ -298,6 +298,20 @@ static void proxyGoTestpkgI(id obj, int code, GoSeq* in, GoSeq* out) { @end +const BOOL GoTestpkgABool = YES; +const double GoTestpkgAFloat = 0.12345; +NSString* const GoTestpkgAString = @"a string"; +const int64_t GoTestpkgAnInt = 7LL; +const double GoTestpkgLog2E = 1.4426950408889634; +const float GoTestpkgMaxFloat32 = 3.4028234663852886e+38; +const double GoTestpkgMaxFloat64 = 1.7976931348623157e+308; +const int32_t GoTestpkgMaxInt32 = 2147483647; +const int64_t GoTestpkgMaxInt64 = 9223372036854775807LL; +const int32_t GoTestpkgMinInt32 = -2147483648; +const int64_t GoTestpkgMinInt64 = -9223372036854775807LL-1; +const float GoTestpkgSmallestNonzeroFloat32 = 0; +const double GoTestpkgSmallestNonzeroFloat64 = 5e-324; + NSData* GoTestpkgBytesAppend(NSData* a, NSData* b) { GoSeq in_ = {}; GoSeq out_ = {}; diff --git a/bind/objc/testpkg/testpkg.go b/bind/objc/testpkg/testpkg.go index d5c5218..fb6150c 100644 --- a/bind/objc/testpkg/testpkg.go +++ b/bind/objc/testpkg/testpkg.go @@ -10,10 +10,28 @@ package testpkg import ( "errors" "fmt" + "math" "runtime" "time" ) +const ( + AString = "a string" + AnInt = 7 + ABool = true + AFloat = 0.12345 + + MinInt32 int32 = math.MinInt32 + MaxInt32 int32 = math.MaxInt32 + MinInt64 = math.MinInt64 + MaxInt64 = math.MaxInt64 + SmallestNonzeroFloat64 = math.SmallestNonzeroFloat64 + MaxFloat64 = math.MaxFloat64 + SmallestNonzeroFloat32 float32 = math.SmallestNonzeroFloat64 + MaxFloat32 float32 = math.MaxFloat32 + Log2E = math.Log2E +) + type I interface { Times(v int32) int64 Error(triggerError bool) error diff --git a/bind/testdata/basictypes.go b/bind/testdata/basictypes.go index 579fbf7..6d9577b 100644 --- a/bind/testdata/basictypes.go +++ b/bind/testdata/basictypes.go @@ -4,6 +4,15 @@ package basictypes +const ( + AString = "a string" + AnInt = 7 + AnInt2 = 1<<63 - 1 + AFloat = 0.2015 + ARune = rune(32) + ABool = true +) + func Ints(x int8, y int16, z int32, t int64, u int) {} func Error() error { return nil } diff --git a/bind/testdata/basictypes.java.golden b/bind/testdata/basictypes.java.golden index f761a60..98aed25 100644 --- a/bind/testdata/basictypes.java.golden +++ b/bind/testdata/basictypes.java.golden @@ -9,6 +9,12 @@ import go.Seq; public abstract class Basictypes { private Basictypes() {} // uninstantiable + public static final boolean ABool = true; + public static final double AFloat = 0.2015; + public static final int ARune = 32; + public static final String AString = "a string"; + public static final long AnInt = 7L; + public static final long AnInt2 = 9223372036854775807L; public static boolean Bool(boolean p0) { go.Seq _in = new go.Seq(); go.Seq _out = new go.Seq(); diff --git a/bind/testdata/basictypes.objc.h.golden b/bind/testdata/basictypes.objc.h.golden index 468647d..966b037 100644 --- a/bind/testdata/basictypes.objc.h.golden +++ b/bind/testdata/basictypes.objc.h.golden @@ -8,6 +8,13 @@ #include +FOUNDATION_EXPORT const BOOL GoBasictypesABool; +FOUNDATION_EXPORT const double GoBasictypesAFloat; +FOUNDATION_EXPORT const int32_t GoBasictypesARune; +FOUNDATION_EXPORT NSString* const GoBasictypesAString; +FOUNDATION_EXPORT const int64_t GoBasictypesAnInt; +FOUNDATION_EXPORT const int64_t GoBasictypesAnInt2; + FOUNDATION_EXPORT BOOL GoBasictypesBool(BOOL p0); FOUNDATION_EXPORT NSData* GoBasictypesByteArrays(NSData* x); diff --git a/bind/testdata/basictypes.objc.m.golden b/bind/testdata/basictypes.objc.m.golden index dbec0bc..331a39a 100644 --- a/bind/testdata/basictypes.objc.m.golden +++ b/bind/testdata/basictypes.objc.m.golden @@ -21,6 +21,13 @@ static NSString* errDomain = @"go.basictypes"; #define _CALL_ErrorPair_ 4 #define _CALL_Ints_ 5 +const BOOL GoBasictypesABool = YES; +const double GoBasictypesAFloat = 0.2015; +const int32_t GoBasictypesARune = 32; +NSString* const GoBasictypesAString = @"a string"; +const int64_t GoBasictypesAnInt = 7LL; +const int64_t GoBasictypesAnInt2 = 9223372036854775807LL; + BOOL GoBasictypesBool(BOOL p0) { GoSeq in_ = {}; GoSeq out_ = {};