├── .gitignore ├── README ├── cmd └── vmloader │ ├── Makefile │ └── main.go ├── java ├── org │ └── golang │ │ └── ext │ │ └── gojvm │ │ ├── testing │ │ ├── Pathos.java │ │ ├── Native.java │ │ ├── Trivial.java │ │ └── Cleaner.java │ │ └── examples │ │ └── hello_world │ │ └── Main.java └── Makefile ├── types ├── Makefile ├── class_name.go └── method_sig.go ├── error.go ├── BUGS ├── consts.c.go ├── jvm_init_args.c.go ├── jvm_init_helpers.c ├── Makefile ├── callback_descriptor.go ├── jvm_value_helpers.c ├── param_reflection.go ├── param_reflection_test.go ├── jvm_ref_test.go ├── aardvark_test.go ├── jvm.c.go ├── jvm_string_test.go ├── class.c.go ├── method_sig_helpers.c.go ├── arglist.c.go ├── globals.c.go ├── object.c.go ├── include └── helpers.h ├── jvm_xclass_test.go ├── jvm_env_helpers.c └── environ.c.go /.gitignore: -------------------------------------------------------------------------------- 1 | *.6 2 | *.o 3 | *.class 4 | _* 5 | [468].out 6 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | This is out of date, for Golang JNI package see https://github.com/timob/jnigi 2 | 3 | -------------------------------------------------------------------------------- /cmd/vmloader/Makefile: -------------------------------------------------------------------------------- 1 | include /usr/share/go/src/Make.inc 2 | TARG=gojvm 3 | 4 | DEPS=../../pkg/gojvm 5 | GOFILES=\ 6 | main.go\ 7 | 8 | include /usr/share/go/src/Make.cmd 9 | 10 | -------------------------------------------------------------------------------- /java/org/golang/ext/gojvm/testing/Pathos.java: -------------------------------------------------------------------------------- 1 | package org.golang.ext.gojvm.testing; 2 | 3 | class Pathos { 4 | Pathos() throws Exception { 5 | throw new Exception("Mwahahahahaa"); 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /java/org/golang/ext/gojvm/examples/hello_world/Main.java: -------------------------------------------------------------------------------- 1 | package org.golang.ext.gojvm.examples.hello_world; 2 | 3 | 4 | class Main { 5 | public static void main(String []args){ 6 | System.out.println(""); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /types/Makefile: -------------------------------------------------------------------------------- 1 | include /usr/share/go/src/Make.inc 2 | TARG=gojvm/types 3 | 4 | 5 | JAVA_BASE=../../../java/ 6 | 7 | 8 | CGO_OFILES=\ 9 | 10 | GOFILES=\ 11 | method_sig.go\ 12 | class_name.go\ 13 | 14 | CLEANFILES+=\ 15 | 16 | 17 | include /usr/share/go/src/Make.pkg 18 | 19 | -------------------------------------------------------------------------------- /java/Makefile: -------------------------------------------------------------------------------- 1 | TESTING_JAVA=\ 2 | org/golang/ext/gojvm/testing/Cleaner.class\ 3 | org/golang/ext/gojvm/testing/Native.class\ 4 | org/golang/ext/gojvm/testing/Pathos.class\ 5 | org/golang/ext/gojvm/testing/Trivial.class\ 6 | 7 | java_classes: $(TESTING_JAVA) 8 | install: java_classes 9 | 10 | clean: 11 | rm -f org/golang/ext/gojvm/testing/*.class 12 | 13 | %.class: %.java 14 | javac $< 15 | 16 | -------------------------------------------------------------------------------- /error.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Error struct { 8 | Code int 9 | Message string 10 | } 11 | 12 | var ErrUnimplemented = Error{-400, "Unimplemented functionality"} 13 | var ErrUnknownClass = Error{-403, "Unknown class"} 14 | var ErrUnknownMethod = Error{-404, "Unknown method"} 15 | 16 | func (self Error) Error() string { 17 | return fmt.Sprintf("(%d) %q", self.Code, self.Message) 18 | } 19 | -------------------------------------------------------------------------------- /java/org/golang/ext/gojvm/testing/Native.java: -------------------------------------------------------------------------------- 1 | package org.golang.ext.gojvm.testing; 2 | 3 | class Native { 4 | public native void NativePing(); 5 | public native int NativeInt(); 6 | 7 | public native void NativeComplex(Object a, Object b, int i); 8 | 9 | public native boolean NativeBool(); 10 | public native short NativeShort(); 11 | public native long NativeLong(); 12 | public native float NativeFloat(); 13 | public native double NativeDouble(); 14 | public native String NativeString(); 15 | } 16 | -------------------------------------------------------------------------------- /java/org/golang/ext/gojvm/testing/Trivial.java: -------------------------------------------------------------------------------- 1 | package org.golang.ext.gojvm.testing; 2 | 3 | class Trivial { 4 | String ConstructorUsed; 5 | Trivial(){ 6 | ConstructorUsed = new String("()V"); 7 | } 8 | Trivial(int i){ 9 | ConstructorUsed = new String("(I)V"); 10 | } 11 | Trivial(long i){ 12 | ConstructorUsed = new String("(J)V"); 13 | } 14 | Trivial(String i){ 15 | ConstructorUsed = new String("(Ljava/lang/String;)V"); 16 | } 17 | 18 | String getConstructorUsed(){ 19 | return this.ConstructorUsed; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /java/org/golang/ext/gojvm/testing/Cleaner.java: -------------------------------------------------------------------------------- 1 | package org.golang.ext.gojvm.testing; 2 | 3 | class Cleaner { 4 | class Cleanable { 5 | Cleaner parent; 6 | Cleanable(Cleaner daddy){ 7 | parent = daddy; 8 | } 9 | protected void finalize() throws Throwable { 10 | parent.deadKid(this); 11 | } 12 | } 13 | int deadKids = 0; 14 | void deadKid(Cleanable kid){ 15 | //System.err.println("Child died"); 16 | deadKids++; 17 | } 18 | int getDeadKids(){ return deadKids; } 19 | 20 | Cleanable NewChild(){ 21 | return new Cleanable(this); 22 | } 23 | } -------------------------------------------------------------------------------- /BUGS: -------------------------------------------------------------------------------- 1 | The 'newest' part is the callbacks (Java native -> JNI -> CGO -> Go), 2 | so please use care when using 3 | 4 | 5 | Bugs/ TODO 6 | ========= 7 | - Bad callback function heders can wedge the reflection engine. 8 | - Callbacks are not all throwing/catching exceptions when they should 9 | - Callbacks are limited to the number of "GenericX" functions defined 10 | in the headers and C files (and the matching dispatcher function). 11 | Ways to make this less hacky are WELCOME! 12 | - Probably not releasing all references when we should 13 | - Custom callback types (e.g., children of java/lang/Object) 14 | are not yet supported (with the exception of String) 15 | -------------------------------------------------------------------------------- /consts.c.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | //#cgo CFLAGS:-Iinclude 4 | //#cgo LDFLAGS:-ljvm -L/usr/lib/jvm/default-java/jre/lib/amd64/server 5 | //#include 6 | //#include 7 | //#include 8 | //#include "helpers.h" 9 | import "C" 10 | 11 | const ( 12 | JNI_VERSION_1_2 = C.JNI_VERSION_1_2 13 | JNI_VERSION_1_4 = C.JNI_VERSION_1_4 14 | JNI_VERSION_1_6 = C.JNI_VERSION_1_6 15 | ) 16 | 17 | const DEFAULT_JVM_VERSION = JNI_VERSION_1_6 18 | 19 | const SystemDefaultJREPath = "/usr/lib/jvm/default-java/jre/lib" 20 | const SunJREPath = "/usr/lib/jvm/java-6-sun/jre/lib" 21 | 22 | var DefaultJREPath = SystemDefaultJREPath 23 | -------------------------------------------------------------------------------- /jvm_init_args.c.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | //#cgo LDFLAGS:-ljvm -L/usr/lib/jvm/java-6-sun/jre/lib/amd64/server/ 4 | //#include 5 | //#include 6 | //#include 7 | //#include "helpers.h" 8 | import "C" 9 | import ( 10 | "unsafe" 11 | ) 12 | 13 | type callbacks struct { 14 | calls map[int]interface{} 15 | next int 16 | } 17 | 18 | var cCallbacks = callbacks{map[int]interface{}{}, 0} 19 | 20 | func findCCallback(p int) (i interface{}) { 21 | if v, ok := cCallbacks.calls[p]; ok { 22 | i = v 23 | } 24 | return 25 | } 26 | 27 | func addGoCallback(fptr interface{}) (id int) { 28 | id = cCallbacks.next 29 | cCallbacks.next += 1 30 | cCallbacks.calls[id] = fptr 31 | return 32 | } 33 | 34 | //export callCCallback 35 | func callCCallback(eptr unsafe.Pointer, ptr unsafe.Pointer, id int) { 36 | cb := findCCallback(id) 37 | if cb != nil { 38 | print("Callback known") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /jvm_init_helpers.c: -------------------------------------------------------------------------------- 1 | #include "helpers.h" 2 | 3 | /* string is duplicated into args, and may be freed after calling, 0 on success. */ 4 | int addStringArgument(JavaVMInitArgs *args, const char *string){ 5 | if (args == NULL ){ 6 | return -1; 7 | } 8 | int opti = args->nOptions++; 9 | args->options = realloc(args->options, sizeof(JavaVMInitArgs) * args->nOptions); 10 | if (args -> options == NULL ) { 11 | return -1; 12 | } 13 | args->options[opti].optionString = strdup(string); 14 | args->options[opti].extraInfo = NULL; 15 | if (args->options[opti].optionString == NULL ){ 16 | return -1; 17 | } 18 | return 0; 19 | } 20 | 21 | 22 | 23 | jint newJVMContext(JavaVM **jvm, void *env, JavaVMInitArgs *args){ 24 | jint out = JNI_CreateJavaVM(jvm, (void **)env, args); 25 | return out; 26 | } 27 | 28 | jint vmAttachCurrentThread(JavaVM *jvm, void *env, void *args){ 29 | return (*jvm)->AttachCurrentThread(jvm, env, args); 30 | } 31 | 32 | jint vmDetachCurrentThread(JavaVM *jvm){ 33 | return (*jvm)->DetachCurrentThread(jvm); 34 | } 35 | 36 | 37 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | include /usr/share/go/src/Make.inc 2 | TARG=gojvm 3 | 4 | 5 | JAVA_BASE=../../../java 6 | CGO_CFLAGS=-Iinclude/ 7 | 8 | DEPS=\ 9 | types\ 10 | $(JAVA_BASE)\ 11 | 12 | CGOFILES=\ 13 | arglist.c.go\ 14 | environ.c.go\ 15 | globals.c.go\ 16 | object.c.go\ 17 | class.c.go\ 18 | jvm.c.go\ 19 | method_sig_helpers.c.go\ 20 | 21 | CGO_OFILES=\ 22 | jvm_env_helpers.o\ 23 | jvm_jvm_helpers.o\ 24 | jvm_value_helpers.o\ 25 | 26 | 27 | GOFILES=\ 28 | callback_descriptor.go\ 29 | param_reflection.go\ 30 | 31 | CLEANFILES+=\ 32 | 33 | CLEANFILES+=\ 34 | $(JAVA_BASE)/org/golang/ext/gojvm/*.class\ 35 | $(JAVA_BASE)/org/golang/ext/gojvm/testing/*.class\ 36 | 37 | TESTING_JAVA=\ 38 | $(JAVA_BASE)/org/golang/ext/gojvm/testing/Cleaner.class\ 39 | $(JAVA_BASE)/org/golang/ext/gojvm/testing/Native.class\ 40 | $(JAVA_BASE)/org/golang/ext/gojvm/testing/Pathos.class\ 41 | $(JAVA_BASE)/org/golang/ext/gojvm/testing/Trivial.class\ 42 | 43 | include /usr/share/go/src/Make.pkg 44 | 45 | java_classes: $(TESTING_JAVA) $(DIST_JAVA) 46 | %.class: %.java 47 | javac $< 48 | -------------------------------------------------------------------------------- /callback_descriptor.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | import ( 4 | "github.com/timob/gojvm/types" 5 | "reflect" 6 | ) 7 | 8 | type callbackDescriptor struct { 9 | Signature types.MethodSignature 10 | PTypes []reflect.Type 11 | // name? 12 | F interface{} // func 13 | } 14 | 15 | // Reflects a given function (w/ the specifid environment), and returns a callbackDescriptor 16 | // including the reflected signature, reflected parameter types and the function pointer 17 | // itself. This descriptor is used in the global tables when using 'registerNatives' 18 | func CallbackDescriptor(env *Environment, f interface{}) (cd callbackDescriptor, err error) { 19 | csig, err := ReflectedSignature(env, f) 20 | if err != nil { 21 | return 22 | } 23 | cd.Signature = csig 24 | cd.F = f 25 | // reflected sig has already done basic verification 26 | // of params & return 27 | //print("Reflected signature is ", cd.Signature.String(), "\n") 28 | rfv := reflect.TypeOf(f) 29 | for i := 2; i < rfv.NumIn(); i++ { 30 | cd.PTypes = append(cd.PTypes, rfv.In(i)) 31 | } 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /cmd/vmloader/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "gojvm" 5 | "log" 6 | "flag" 7 | ) 8 | 9 | var jrePath = gojvm.DefaultJREPath 10 | var cpBase = "." 11 | func init(){ 12 | flag.StringVar(&jrePath, "jre", jrePath, "Path to JRE (classes)") 13 | flag.StringVar(&cpBase, "cp", cpBase, "(single) ClassPath") 14 | } 15 | 16 | 17 | 18 | 19 | func main(){ 20 | flag.Parse() 21 | if flag.NArg() != 1 { 22 | /// TODO: brainfart - golang argv[0]? 23 | log.Fatalf("Expected: %s 'class-with-main'", "vmloader") 24 | } 25 | print("Initializing VM\n") 26 | _, env, err := gojvm.NewJVM(0, gojvm.JvmConfig{[]string{cpBase,jrePath}}) 27 | if err != nil { 28 | log.Fatalf("err == %s", err) 29 | } 30 | klass := flag.Arg(0) 31 | inst, err := env.GetClassStr(klass) 32 | 33 | if err != nil { 34 | log.Fatalf("Couldn't find class %s", err) 35 | } 36 | log.Printf("Got instance: %+v", inst) 37 | err = inst.CallVoid(env, true, "main", []string{}) 38 | if err != nil { 39 | log.Fatalf("Couldn't call main: %v", err) 40 | } 41 | 42 | //strt, _ := gojvm.TypeOf("") 43 | //ctx.FindMethod(inst, "toString", strt) 44 | } 45 | -------------------------------------------------------------------------------- /jvm_value_helpers.c: -------------------------------------------------------------------------------- 1 | #include "helpers.h" 2 | 3 | /* arglist helpers */ 4 | /* 5 | jboolean z; 6 | jbyte b; 7 | jchar c; 8 | jshort s; 9 | jint i; 10 | jlong j; 11 | jfloat f; 12 | jdouble d; 13 | jobject l; 14 | */ 15 | 16 | // Ease the coercion of values to jvalue from CGO 17 | jvalue boolValue(jboolean v){ jvalue jv ={.z=v}; return jv; } 18 | jvalue byteValue(jbyte v){ jvalue jv ={.b=v}; return jv; } 19 | jvalue charValue(jchar v){ jvalue jv ={.c=v}; return jv; } 20 | jvalue shortValue(jshort v){ jvalue jv ={.s=v}; return jv; } 21 | jvalue intValue(jint v){ jvalue jv ={.i=v}; return jv; } 22 | jvalue longValue(jlong v){ jvalue jv ={.j=v}; return jv; } 23 | jvalue floatValue(jfloat v){ jvalue jv ={.f=v}; return jv; } 24 | jvalue doubleValue(jdouble v){ jvalue jv ={.d=v}; return jv; } 25 | jvalue objValue(jobject v){ jvalue jv ={.l=v}; return jv; } 26 | 27 | jboolean valBool(jvalue v) { return v.z; } 28 | jbyte valByte(jvalue v) { return v.b; } 29 | jchar valChar(jvalue v) { return v.c; } 30 | jshort valShort(jvalue v) { return v.s; } 31 | jint valInt(jvalue v) { return v.i; } 32 | jlong valLong(jvalue v) { return v.j; } 33 | jfloat valFloat(jvalue v) { return v.f; } 34 | jdouble valDouble(jvalue v) { return v.d; } 35 | jobject valObject(jvalue v) { return v.l; } 36 | 37 | -------------------------------------------------------------------------------- /types/class_name.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | /* A unified representation (with compare) of both foo.bar and foo/bar class names */ 8 | type Name []string 9 | 10 | var JavaLangString = Name{"java","lang","String"} 11 | var JavaLangObject = Name{"java","lang","Object"} 12 | var JavaLangThrowable = Name{"java","lang","Throwable"} 13 | 14 | /* 15 | Parse a string by splitting on '.' and '/'; $Refs are left intact; 16 | returns a new Name instance 17 | */ 18 | func NewName(s string) Name { 19 | return Name(strings.FieldsFunc(s, func(ch rune) bool { 20 | return (ch == '.' || ch == '/') 21 | })) 22 | } 23 | 24 | /* 25 | returns a generalized (but deliberately machine-useless) string representation 26 | of a Name 27 | */ 28 | func (self Name) String() string { 29 | return "" 30 | } 31 | 32 | // returns a '.' joined representation of the className 33 | func (self Name) AsName() string { 34 | return strings.Join(self, ".") 35 | } 36 | 37 | // returns a '/' joined representation of the className 38 | func (self Name) AsPath() string { 39 | return strings.Join(self, "/") 40 | } 41 | 42 | // returns {-1,0,1} for a comparsion of two classnames. 43 | func (self Name) Cmp(rhs Name) (out int) { 44 | for i, ls := range self { 45 | if len(rhs) >= i+1 { 46 | if ls != rhs[i] { 47 | if ls < rhs[i] { 48 | out = -1 49 | } else { 50 | out = 1 51 | } 52 | } 53 | } else { 54 | out = -1 55 | } 56 | if out != 0 { 57 | break 58 | } 59 | } 60 | return 61 | } 62 | -------------------------------------------------------------------------------- /param_reflection.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | import ( 4 | "github.com/timob/gojvm/types" 5 | // "reflect" 6 | // "log" 7 | ) 8 | 9 | /* 10 | Returns the signature string for the configured environment, return type and parameters 11 | (exported for convenience) ; 12 | 13 | Reflection is handled by ParameterString 14 | */ 15 | func FormFor(ctx *Environment, ret types.Typed, params ...interface{}) (s string, err error) { 16 | s, err = ParameterString(ctx, params...) 17 | if err == nil { 18 | s = s + ret.TypeString() 19 | } 20 | return 21 | } 22 | 23 | /* 24 | Returns an array of types.Typeds matching the params list (which may be empty, resulting 25 | in a valid empty (nil) list of types.Typeds). 26 | 27 | Reflection is done by TypeOf. 28 | 29 | * Environment is required in order to interpolate abstract objects into 30 | class names. 31 | */ 32 | func ParameterTypes(ctx *Environment, params ...interface{}) (jtypes []types.Typed, err error) { 33 | for _, param := range params { 34 | var jt types.Typed 35 | jt, err = TypeOf(ctx, param) 36 | //log.Printf("param[%d] '%+v' %v", i, param, reflect.TypeOf(param).String()) 37 | if err != nil { 38 | break 39 | } 40 | jtypes = append(jtypes, jt) 41 | } 42 | return 43 | } 44 | 45 | /* Helper function for the parameter-side only of a JavaMethodSignature. 46 | the resultant string is () quoted by jms.ParameterString() 47 | */ 48 | func ParameterString(ctx *Environment, params ...interface{}) (s string, err error) { 49 | jms := types.MethodSignature{} 50 | jms.Params, err = ParameterTypes(ctx, params...) 51 | if err == nil { 52 | s = jms.ParameterString() 53 | } 54 | return 55 | } 56 | -------------------------------------------------------------------------------- /param_reflection_test.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | import ( 4 | "github.com/timob/gojvm/types" 5 | "testing" 6 | ) 7 | 8 | type formForTest struct { 9 | rtype types.Typed 10 | params []interface{} 11 | eform string 12 | eerror error 13 | } 14 | 15 | var formOfTests = []formForTest{ 16 | formForTest{types.Basic(types.VoidKind), []interface{}{}, "()V", nil}, 17 | formForTest{types.Basic(types.VoidKind), []interface{}{int(5)}, "(I)V", nil}, 18 | formForTest{types.Basic(types.VoidKind), []interface{}{int16(5)}, "(S)V", nil}, 19 | formForTest{types.Basic(types.VoidKind), []interface{}{int64(5)}, "(J)V", nil}, 20 | formForTest{types.Basic(types.VoidKind), []interface{}{"5"}, "(Ljava/lang/String;)V", nil}, 21 | formForTest{types.Basic(types.VoidKind), []interface{}{"5", &Object{}}, "(Ljava/lang/String;Ljava/lang/Object;)V", nil}, 22 | formForTest{types.Basic(types.ShortKind), []interface{}{int64(5)}, "(J)S", nil}, 23 | formForTest{types.Basic(types.BoolKind), []interface{}{int(0)}, "(I)Z", nil}, 24 | formForTest{types.Class{types.JavaLangObject}, []interface{}{int(0)}, "(I)Ljava/lang/Object;", nil}, 25 | formForTest{types.Array{types.Basic(types.ByteKind)}, []interface{}{[]byte{1, 2, 3}}, "([B)[B", nil}, 26 | } 27 | 28 | func TestTrivialFormFor(t *testing.T) { 29 | env := setupJVM(t) 30 | for i, test := range formOfTests { 31 | form, err := FormFor(env, test.rtype, test.params...) 32 | fatalIf(t, err != test.eerror, "[%d] Unexpected error %v", i, err) 33 | fatalIf(t, form != test.eform, "[%d] Unexpected form (got %s, wanted %s)", i, form, test.eform) 34 | } 35 | } 36 | 37 | func BenchmarkFormFor(b *testing.B){ 38 | env := setupJVM(nil) 39 | lft := len(formOfTests) 40 | for i := 0; i < b.N; i++ { 41 | FormFor(env, formOfTests[i%lft].rtype, formOfTests[i%lft].params...) 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /types/method_sig.go: -------------------------------------------------------------------------------- 1 | package types 2 | 3 | 4 | // A 'Typed' contains information for method signatures 5 | type Typed interface { 6 | Kind() Kind 7 | TypeString() string 8 | } 9 | 10 | 11 | // returns the resultant String() of a JavaMethodSignature compiled from a set of 12 | // JavaType's 13 | func MethodSignatureString(in []Typed, out Typed) string { 14 | return MethodSignature{in, out}.String() 15 | } 16 | 17 | 18 | type MethodSignature struct { 19 | Params []Typed 20 | Return Typed 21 | } 22 | 23 | /* Implements the parameter-side only of a JavaMethodSignature. 24 | the resultant string is () quoted. 25 | */ 26 | func (self MethodSignature) ParameterString() string { 27 | plist := "" 28 | for _, parm := range self.Params { 29 | plist += parm.TypeString() 30 | } 31 | return "(" + plist + ")" 32 | } 33 | 34 | // Returns the Java method-signature as a string. 35 | func (self MethodSignature) String() string { 36 | return self.ParameterString() + self.Return.TypeString() 37 | } 38 | 39 | 40 | // a 'Basic' is a single 'primative' type 41 | type Basic Kind 42 | 43 | func (self Basic) TypeString() string { return self.Kind().TypeString() } 44 | func (self Basic) Kind() Kind { return Kind(self) } 45 | 46 | // a 'Class' is a Java Class name type a la "Lclass/path/name;" 47 | type Class struct { 48 | Klass Name 49 | } 50 | 51 | func (self Class) TypeString() string { return "L" + self.Klass.AsPath() + ";" } 52 | func (self Class) Kind() Kind { return ClassKind } 53 | func (self Class) Name() Name { return self.Klass } 54 | 55 | // Java arrays consist of a single type (though the type itself could be 56 | // the generic 'Object' class 57 | type Array struct { 58 | Underlying Typed 59 | } 60 | 61 | func (self Array) TypeString() string { return "[" + self.Underlying.TypeString() } 62 | func (self Array) Kind() Kind { return ArrayKind } 63 | 64 | type Kind int 65 | func (self Kind)TypeString()(string) { 66 | return string(self) 67 | } 68 | 69 | const ( 70 | UnspecKind Kind = '_' 71 | BoolKind Kind = 'Z' 72 | ByteKind Kind = 'B' 73 | CharKind Kind = 'C' 74 | ShortKind Kind = 'S' 75 | IntKind Kind = 'I' 76 | LongKind Kind = 'J' 77 | FloatKind Kind = 'F' 78 | DoubleKind Kind = 'D' 79 | ClassKind Kind = 'L' 80 | ArrayKind Kind = '[' 81 | VoidKind Kind = 'V' // return only 82 | ) 83 | -------------------------------------------------------------------------------- /jvm_ref_test.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | import ( 4 | "github.com/timob/gojvm/types" 5 | "math/rand" 6 | "testing" 7 | "time" 8 | ) 9 | 10 | var CleanerClass = types.Name{"org", "golang", "ext", "gojvm", "testing", "Cleaner"} 11 | var CleanableClass = types.Name{"org", "golang", "ext", "gojvm", "testing", "Cleaner$Cleanable"} 12 | 13 | // tests basic 'created & destroyed' semantics of our interaction w. the JVM. 14 | // a better test would put some objects through their paces, release, and 'somehow' 15 | // get the number of refs unreaped... 16 | func TestJVMBasicRefCounting(t *testing.T) { 17 | env := setupJVM(t) 18 | system := systemClass(env, t) 19 | cleaner, err := env.NewInstanceStr(CleanerClass.AsPath()) 20 | fatalIf(t, err != nil, "Got an exception instantiating %s", CleanerClass.String()) 21 | dead, err := cleaner.CallInt(env, false, "getDeadKids") 22 | fatalIf(t, err != nil, "Got an exception calling getDeadKids %v", err) 23 | fatalIf(t, dead != 0, "Wrong number of dead kids: (Got: %d, exp: %d)", dead, 0) 24 | for i := 0; i < 100; i++ { 25 | obj, err := cleaner.CallObj(env, false, "NewChild", types.Class{CleanableClass}) 26 | fatalIf(t, err != nil, "Got an exception calling NewChild %v", err) 27 | env.DeleteLocalRef(obj) 28 | } 29 | err = system.CallVoid(env, true, "gc") 30 | fatalIf(t, err != nil, "Got an exception calling gc() :%v", err) 31 | // gc is not a blocking call... nor is it required it would actually finalize all of our 32 | // objects, but it seems to work on most JVMs... 33 | time.Sleep(500000000) 34 | dead, err = cleaner.CallInt(env, false, "getDeadKids") 35 | fatalIf(t, err != nil, "Got an exception calling getDeadKids %v", err) 36 | fatalIf(t, dead != 100, "Wrong number of dead kids: (Got: %d, exp: %d)", dead, 100) 37 | } 38 | 39 | // simply for comparison, a totally unfair test 40 | // since java has to break into 'native' for us. 41 | func BenchmarkGoRandInt(b *testing.B) { 42 | ll := int64(0) 43 | for i := 0; i < b.N; i++ { 44 | _ = rand.Int() 45 | ll += 4 // assuming 32 byte ints... 46 | } 47 | b.SetBytes(ll) 48 | } 49 | 50 | func BenchmarkJavaRandInt(b *testing.B) { 51 | env := setupJVM(nil) 52 | ll := int64(0) 53 | robj, err := env.NewInstanceStr("java/util/Random") 54 | if err != nil { 55 | panic("BenchmarkJavaRand failed to setup class") 56 | } 57 | for i := 0; i < b.N; i++ { 58 | _, err = robj.CallInt(env, false, "nextInt") 59 | if err == nil { 60 | ll += 4 // assuming 32 bit ints... 61 | } 62 | } 63 | env.DeleteLocalRef(robj) 64 | b.SetBytes(ll) 65 | } 66 | 67 | func BenchmarkJavaRandLong(b *testing.B) { 68 | env := setupJVM(nil) 69 | ll := int64(0) 70 | robj, err := env.NewInstanceStr("java/util/Random") 71 | if err != nil { 72 | panic("BenchmarkJavaRand failed to setup class") 73 | } 74 | for i := 0; i < b.N; i++ { 75 | _, err = robj.CallLong(env, false, "nextLong") 76 | if err == nil { 77 | ll += 8 // assuming 64 bit longs... 78 | } 79 | } 80 | env.DeleteLocalRef(robj) 81 | b.SetBytes(ll) 82 | } 83 | -------------------------------------------------------------------------------- /aardvark_test.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | /* 3 | This file exists (and is named) primarily for common test functionality; 4 | We also include the first SetupJVM call in here, so that it will not skew the timing 5 | of other tests regardless of other jvm_XX_test files 6 | */ 7 | import ( 8 | "flag" 9 | "sync" 10 | "testing" 11 | ) 12 | 13 | func fatalIf(t *testing.T, tv bool, msg string, args ...interface{}) { 14 | if tv { 15 | t.Fatalf(msg, args...) 16 | } 17 | } 18 | 19 | func fatalEquals(t *testing.T, val interface{}, val2 interface{}, msg string, args ...interface{}) { 20 | args = append(args, []interface{}{val, val2}...) 21 | fatalIf(t, val == val2, msg+" (%v == %v)", args...) 22 | } 23 | 24 | func fatalInEq(t *testing.T, val interface{}, val2 interface{}, msg string, args ...interface{}) { 25 | args = append(args, []interface{}{val, val2}...) 26 | fatalIf(t, val != val2, msg+" (Expected: %v;\t Got: %v)", args...) 27 | } 28 | 29 | var SystemClass = "java/lang/System" 30 | 31 | func systemClass(env *Environment, t *testing.T) (c *Class) { 32 | c, err := env.GetClassStr(SystemClass) 33 | fatalIf(t, err != nil, "Error loading system class: %v", err) 34 | return 35 | } 36 | 37 | var _jvm *JVM 38 | var squelchExceptions bool /* = false */ 39 | 40 | // used in testing; a 'squelch' helper 41 | // such that: 42 | // func X(){ 43 | // defer env.defMute()() /*note the double parens!!!*/ 44 | // doSomeJavaCall 45 | // } 46 | // 47 | // would not output an exception to the console during processing 48 | // regardless othe explicit 'mutedness'. 49 | // there is a race condition here, but you're not supposed 50 | // to be using *Environment in multiple threads anyhow :P 51 | func defMute(env *Environment) func() { 52 | muted := env.Muted() 53 | env.Mute(true) 54 | return func() { 55 | env.Mute(muted) 56 | } 57 | } 58 | 59 | var startLock = &sync.Mutex{} 60 | 61 | func setupJVM(t *testing.T) (env *Environment) { 62 | startLock.Lock() 63 | defer startLock.Unlock() 64 | if _jvm != nil { 65 | var err error 66 | env, err = _jvm.AttachCurrentThread() 67 | if err != nil { 68 | t.Fatalf("Couldn't attach thread: %v", err) 69 | } 70 | return 71 | } 72 | t.Logf("Testing -- using classpath [../../../java/,%s", DefaultJREPath) 73 | var err error 74 | _jvm, env, err = NewJVM(0, JvmConfig{[]string{"../../../java/", DefaultJREPath}}) 75 | fatalIf(t, err != nil, "Error initializing JVM: %v", err) 76 | fatalIf(t, _jvm == nil, "Got a nil context!") 77 | // expected exceptions are pre-muted/unmuted, but if you're testing something 78 | // that causes them to throw, and want readable tests, this is the line 79 | // to uncomment. 80 | //_Ctx.env.Mute(true) 81 | return 82 | } 83 | 84 | // so the timing of other tests/bench's isn't thrown. 85 | func TestJVMFirst(t *testing.T) { setupJVM(t) } 86 | 87 | func init() { 88 | // this only works if you run it directly (not in the gotest framework, as it does not inherit options) 89 | flag.BoolVar(&squelchExceptions, "squelch-ex", squelchExceptions, "Squelch unexpected exceptions from printing") 90 | } 91 | -------------------------------------------------------------------------------- /jvm.c.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | //#include "helpers.h" 4 | import "C" 5 | 6 | import ( 7 | "errors" 8 | "github.com/timob/gojvm/types" 9 | "strings" 10 | "unsafe" 11 | "runtime" 12 | ) 13 | 14 | 15 | type JVM struct { 16 | jvm *C.JavaVM 17 | registered map[int]callbackDescriptor 18 | regId int 19 | } 20 | 21 | func (self *JVM) addNative(env *Environment, f interface{}) (id int, csig types.MethodSignature, err error) { 22 | //func CallbackSignature(f interface{})(sig MethodSignature, err error){ 23 | cbd, err := CallbackDescriptor(env, f) 24 | if err != nil { 25 | return 26 | } 27 | /// todo, this is not thread safe... 28 | id = self.regId 29 | self.registered[id] = cbd 30 | self.regId++ 31 | csig = cbd.Signature 32 | //log.Printf("Calculated signature: %v", csig) 33 | /// TODO 34 | return 35 | } 36 | 37 | /* 38 | Returns a new environment pointer that is appropriate for the /currently executing/ 39 | thread to use. It is safe to call multiply (idempotent), and can be returned 40 | via DetachCurrentThread (not idempotent!) 41 | */ 42 | func (self *JVM) AttachCurrentThread() (env *Environment, err error) { 43 | env = NewEnvironment(self) 44 | runtime.LockOSThread() 45 | //print ("Allocated environment for thread\t", env.Ptr(),"\n") 46 | if 0 != C.vmAttachCurrentThread(self.jvm, env.Ptr(), nil) { 47 | err = errors.New("Couldn't attach thread (and thus cannot gather exception)") 48 | } else { 49 | AllEnvs.Add(env) 50 | } 51 | return 52 | } 53 | 54 | // notifies the JVM of your threads done-ness w/ it, and deallocates the associated 55 | // environment pointer. Depending on the exact JDK version, there are differing semantics 56 | // on whether the 'original' thread can call this (else JVM Shutdown), but most modern 57 | // stacks (>=1.2) should allow this from the 'main' thread. 58 | func (self *JVM) DetachCurrentThread() (err error) { 59 | if 0 != C.vmDetachCurrentThread(self.jvm) { 60 | err = errors.New("Couldn't attach thread (and thus cannot gather exception)") 61 | } 62 | return 63 | } 64 | 65 | type JvmConfig struct { 66 | ClassPath []string 67 | } 68 | 69 | func NewJVM(ver int, conf JvmConfig) (jvm *JVM, env *Environment, err error) { 70 | runtime.LockOSThread() 71 | 72 | args, err := defaultJVMArgs(ver) 73 | if err != nil { 74 | return 75 | } 76 | pathStr := strings.Join(conf.ClassPath, ":") 77 | //print("Adding class path\n") 78 | err = addStringArg(args, "-Djava.class.path="+pathStr) 79 | if err == nil { 80 | //print("Initializing JVM Context\n") 81 | jvm = &JVM{ 82 | registered: map[int]callbackDescriptor{}, 83 | } 84 | env = NewEnvironment(jvm) 85 | if 0 != C.newJVMContext(&jvm.jvm, env.Ptr(), args) { 86 | err = errors.New("Couldn't instantiate JVM") 87 | } else { 88 | AllVMs.Add(jvm) 89 | } 90 | } 91 | return 92 | } 93 | 94 | func defaultJVMArgs(ver int) (args *C.JavaVMInitArgs, err error) { 95 | if ver == 0 { 96 | ver = DEFAULT_JVM_VERSION 97 | } 98 | args = new(C.JavaVMInitArgs) 99 | //print("Default args\t", ver,"\n") 100 | args.version = C.jint(ver) 101 | if 0 != C.JNI_GetDefaultJavaVMInitArgs(unsafe.Pointer(args)) { 102 | err = errors.New("Couldn't contruct default JVM args") 103 | } 104 | return 105 | } 106 | 107 | func addStringArg(args *C.JavaVMInitArgs, s string) (err error) { 108 | cstr := C.CString(s) 109 | defer C.free(unsafe.Pointer(cstr)) 110 | ok := C.addStringArgument(args, cstr) 111 | if ok != 0 { 112 | err = errors.New("addStringArg failed") 113 | } 114 | return 115 | } 116 | -------------------------------------------------------------------------------- /jvm_string_test.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | import ( 4 | "math/rand" 5 | "strconv" 6 | "strings" 7 | "testing" 8 | ) 9 | 10 | var simpleStringTests = []string{ 11 | "basic", 12 | "a modestly long string with no special special characters.", 13 | "", //empty 14 | "embedded\x00nulls", 15 | // this won't pass a simpleStringTest... 16 | // "a modestly long string with invalid \xc0\xaf special characters.", 17 | "\x00\x00\x00nulledstring", 18 | "κόσμε", 19 | } 20 | 21 | func TestJVMNewString(t *testing.T) { 22 | env := setupJVM(t) 23 | for i, str := range simpleStringTests { 24 | jstr, err := env.NewStringObject(str) 25 | fatalIf(t, err != nil, "[%d] Couldn't NewString '%q'", i, str) 26 | 27 | // length compare will fail on the UTF8 test, as length is 'runes' in java, 28 | // and bytes in Go. 29 | 30 | jstr_len, err := jstr.CallInt(env, false, "length") 31 | fatalIf(t, err != nil, "[%d] Couldn't call length on jstr '%v'", i, err) 32 | fatalIf(t, i != 5 && jstr_len != len(str), "[%d] Wrong length (Got %d, expected %d)", i, jstr_len, len(str)) 33 | ostr, _, err := env.CallObjectString(jstr, false, "toString") 34 | fatalIf(t, err != nil, "[%d] Couldn't call toString on jstr '%v'", i, err) 35 | fatalIf(t, str != ostr, "[%d] Wrong inner string (Got %q, expected %q)", i, ostr, str) 36 | 37 | } 38 | } 39 | 40 | var someWords = strings.Split("Mary had a little lamb whose fleece was white as snow and every where that mary went the lamb was sure to go", " ") 41 | 42 | // just a reference value for giving relative measures 43 | // not a fair comparison to 'pure' JVM 44 | func BenchmarkGoShortStringsReference(b *testing.B) { 45 | ll := int64(0) 46 | words := len(someWords) 47 | for i := 0; i < b.N; i++ { 48 | str := someWords[rand.Int()%words] + strconv.Itoa(i) 49 | ll += int64(len(str)) 50 | } 51 | b.SetBytes(ll) 52 | } 53 | 54 | // Creates a 'random' short string, converts it into a Java String, 55 | // and then immediately dereferences it. 56 | func BenchmarkShortStrings(b *testing.B) { 57 | env := setupJVM(nil) 58 | ll := int64(0) 59 | words := len(someWords) 60 | for i := 0; i < b.N; i++ { 61 | str := someWords[rand.Int()%words] + strconv.Itoa(i) 62 | obj, err := env.NewStringObject(str) 63 | if err == nil { 64 | env.DeleteLocalRef(obj) 65 | ll += int64(len(str)) 66 | } // else why didn't the tests fail... 67 | } 68 | 69 | b.SetBytes(ll) 70 | } 71 | 72 | // Benchmarks a long string conversion (but under a page) 73 | func BenchmarkLongStrings(b *testing.B) { 74 | // ~2048 bytes (1/2 page) @ ~4ch/word => 512 75 | wordsPer := 512 76 | env := setupJVM(nil) 77 | ll := int64(0) 78 | words := len(someWords) 79 | for i := 0; i < b.N; i++ { 80 | str := "" 81 | for j := 0; j < wordsPer; j++ { 82 | str += someWords[rand.Int()%words] 83 | } 84 | obj, err := env.NewStringObject(str) 85 | if err == nil { 86 | env.DeleteLocalRef(obj) 87 | ll += int64(len(str)) 88 | } // else why didn't the tests fail... 89 | } 90 | 91 | b.SetBytes(ll) 92 | } 93 | 94 | // Benchmarks a very long string conversion (~2 pages, possibly 3) 95 | func BenchmarkVeryLongStrings(b *testing.B) { 96 | // ~8192 bytes (2 pages) @ ~4ch/word => 2048 97 | wordsPer := 2048 98 | env := setupJVM(nil) 99 | ll := int64(0) 100 | words := len(someWords) 101 | for i := 0; i < b.N; i++ { 102 | str := "" 103 | for j := 0; j < wordsPer; j++ { 104 | str += someWords[rand.Int()%words] 105 | } 106 | obj, err := env.NewStringObject(str) 107 | if err == nil { 108 | env.DeleteLocalRef(obj) 109 | ll += int64(len(str)) 110 | } // else why didn't the tests fail... 111 | } 112 | 113 | b.SetBytes(ll) 114 | } 115 | -------------------------------------------------------------------------------- /class.c.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | //#cgo LDFLAGS:-ljvm -L/usr/lib/jvm/default-java/jre/lib/amd64/server 4 | //#include "helpers.h" 5 | import "C" 6 | import ( 7 | "github.com/timob/gojvm/types" 8 | // "log" 9 | ) 10 | 11 | /* represents a class (object) */ 12 | type Class struct { 13 | class C.jclass 14 | } 15 | 16 | func newClass(class C.jclass) *Class { 17 | return &Class{class} 18 | } 19 | 20 | /* 21 | returns the (potentially cached) types.Name of the class. 22 | */ 23 | func (self *Class) GetName(env *Environment) (name types.Name, err error) { 24 | //log.Printf("Name(miss)") 25 | var cstr string 26 | cstr, _, err = self.CallString(env, false, "getName") 27 | if err == nil { 28 | name = types.NewName(cstr) 29 | } 30 | return 31 | } 32 | 33 | // Calls the named void-method on the class 34 | func (self *Class) CallVoid(env *Environment, static bool, mname string, params ...interface{}) (err error) { 35 | return env.CallClassVoid(self, static, mname, params...) 36 | } 37 | 38 | func (self *Class) CallBoolean(env *Environment, static bool, mname string, params ...interface{}) (i bool, err error) { 39 | return env.CallClassBool(self, static, mname, params...) 40 | } 41 | 42 | func (self *Class) CallShort(env *Environment, static bool, mname string, params ...interface{}) (i int16, err error) { 43 | return env.CallClassShort(self, static, mname, params...) 44 | } 45 | 46 | func (self *Class) CallInt(env *Environment, static bool, mname string, params ...interface{}) (i int, err error) { 47 | return env.CallClassInt(self, static, mname, params...) 48 | } 49 | 50 | func (self *Class) CallLong(env *Environment, static bool, mname string, params ...interface{}) (i int64, err error) { 51 | return env.CallClassLong(self, static, mname, params...) 52 | } 53 | 54 | func (self *Class) CallDouble(env *Environment, static bool, mname string, params ...interface{}) (i float64, err error) { 55 | return env.CallClassDouble(self, static, mname, params...) 56 | } 57 | 58 | func (self *Class) CallFloat(env *Environment, static bool, mname string, params ...interface{}) (i float32, err error) { 59 | return env.CallClassFloat(self, static, mname, params...) 60 | } 61 | 62 | func (self *Class) CallObj(env *Environment, static bool, mname string, rval types.Typed, params ...interface{}) (o *Object, err error) { 63 | return env.CallClassObj(self, static, mname, rval, params...) 64 | } 65 | 66 | func (self *Class) CallString(env *Environment, static bool, mname string, params ...interface{}) (str string, isnull bool, err error) { 67 | return env.CallClassString(self, static, mname, params...) 68 | } 69 | 70 | func (self *Class) CallLongArray(env *Environment, static bool, mname string, params ...interface{}) (i []int64, err error) { 71 | return env.CallClassLongArray(self, static, mname, params...) 72 | } 73 | 74 | func (self *Class) CallIntArray(env *Environment, static bool, mname string, params ...interface{}) (i []int, err error) { 75 | return env.CallClassIntArray(self, static, mname, params...) 76 | } 77 | 78 | //fields 79 | 80 | func (self *Class) GetObjField(env *Environment, static bool, name string, rval types.Typed) (*Object, error) { 81 | return env.GetClassObjField(self, static, name, rval) 82 | } 83 | 84 | func (self *Class) GetBooleanField(env *Environment, static bool, name string) (bool, error) { 85 | return env.GetClassBooleanField(self, static, name) 86 | } 87 | 88 | func (self *Class) GetShortField(env *Environment, static bool, name string) (int16, error) { 89 | return env.GetClassShortField(self, static, name) 90 | } 91 | 92 | func (self *Class) GetIntField(env *Environment, static bool, name string) (int, error) { 93 | return env.GetClassIntField(self, static, name) 94 | } 95 | 96 | func (self *Class) GetLongField(env *Environment, static bool, name string) (int64, error) { 97 | return env.GetClassLongField(self, static, name) 98 | } 99 | 100 | func (self *Class) GetFloatField(env *Environment, static bool, name string) (float32, error) { 101 | return env.GetClassFloatField(self, static, name) 102 | } 103 | 104 | func (self *Class) GetDoubleField(env *Environment, static bool, name string) (float64, error) { 105 | return env.GetClassDoubleField(self, static, name) 106 | } 107 | 108 | 109 | -------------------------------------------------------------------------------- /method_sig_helpers.c.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | //#cgo CFLAGS:-I../include/ 4 | //#cgo LDFLAGS:-ljvm -L/usr/lib/jvm/java-6-sun/jre/lib/amd64/server/ 5 | //#include "helpers.h" 6 | import "C" 7 | import ( 8 | "errors" 9 | "fmt" 10 | "github.com/timob/gojvm/types" 11 | "reflect" 12 | ) 13 | 14 | func TypeOf(env *Environment, v interface{}) (k types.Typed, err error) { 15 | if kind, ok := v.(types.Typed); ok { 16 | return kind, nil 17 | } 18 | switch vt := v.(type) { 19 | case C.jstring: 20 | return types.Class{types.JavaLangString}, nil 21 | case types.Typed: 22 | return vt, nil 23 | case *Object: 24 | name, err2 := vt.Name(env) 25 | if err2 != nil { 26 | err = err2 27 | return 28 | } 29 | return types.Class{name}, nil 30 | case *ObjectArray: 31 | return types.Array{types.Class{vt.Name}}, nil 32 | case *CastObject: 33 | return types.Class{vt.Name}, nil 34 | } 35 | 36 | k, err = reflectedType(env, v) 37 | return 38 | } 39 | 40 | func reflectedType(env *Environment, v interface{}) (k types.Typed, err error) { 41 | vtype := reflect.TypeOf(v) 42 | vkind := vtype.Kind() 43 | switch vkind { 44 | case reflect.Ptr: 45 | k, err = TypeOf(env, reflect.Indirect(reflect.ValueOf(v)).Interface()) 46 | case reflect.Bool: 47 | k = types.Basic(types.BoolKind) 48 | case reflect.Uint8, reflect.Int8: 49 | k = types.Basic(types.ByteKind) 50 | case reflect.Int16, reflect.Uint16: 51 | k = types.Basic(types.ShortKind) 52 | case reflect.Int32, reflect.Uint32: 53 | k = types.Basic(types.IntKind) 54 | case reflect.Uint64, reflect.Int64: 55 | k = types.Basic(types.LongKind) 56 | case reflect.Int, reflect.Uint: 57 | k = types.Basic(types.IntKind) 58 | case reflect.Float32: 59 | k = types.Basic(types.FloatKind) 60 | case reflect.Float64: 61 | k = types.Basic(types.DoubleKind) 62 | case reflect.Struct: 63 | k = types.Class{[]string{"golang", vkind.String()}} 64 | case reflect.String: 65 | k = types.Class{types.JavaLangString} 66 | case reflect.Slice, reflect.Array: 67 | sltype := vtype.Elem() 68 | switch sltype.Kind() { 69 | case reflect.Uint8: 70 | k = types.Array{types.Basic(types.ByteKind)} 71 | case reflect.String: 72 | k = types.Array{types.Class{types.JavaLangString}} 73 | default: 74 | err = errors.New("Unhandled slice type " + sltype.String()) 75 | } 76 | default: 77 | switch T := v.(type) { 78 | default: 79 | err = errors.New(fmt.Sprintf("Unsure how to TypeOf '%s'/%T", vkind.String(), T)) 80 | } 81 | } 82 | return 83 | } 84 | 85 | type errInterface interface { 86 | Error() string 87 | } 88 | 89 | func ReflectedSignature(ctx *Environment, f interface{}) (sig types.MethodSignature, err error) { 90 | sig.Return = types.Basic(types.UnspecKind) 91 | 92 | ftype := reflect.TypeOf(f) 93 | sig.Params = make([]types.Typed, 0) 94 | if ftype.Kind() != reflect.Func { 95 | err = errors.New("ReflectedSignature: f is not a function") 96 | } 97 | if err == nil && ftype.NumIn() < 2 { 98 | err = errors.New("ReflectedSignature: f is not a callback (insufficient args)") 99 | } 100 | if err == nil && ftype.NumOut() > 1 { 101 | err = errors.New("ReflectedSignature: f is not a callback (too many returns)") 102 | } 103 | if err == nil && ftype.In(0) != reflect.TypeOf(&Environment{}) { 104 | err = errors.New("bad first-arg Type: must be *Environment") 105 | } 106 | if err == nil && ftype.In(1) != reflect.TypeOf(&Object{}) { 107 | err = errors.New("bad second-arg Type: must be *Object") 108 | } 109 | if err != nil { 110 | return 111 | } 112 | for i := 2; i < ftype.NumIn(); i++ { 113 | var k types.Typed 114 | if ftype.In(i).Kind() == reflect.Ptr { 115 | if ftype.In(i) == reflect.TypeOf(&Object{}) { 116 | k = types.Class{types.JavaLangObject} 117 | } else { 118 | itype := ftype.In(i).Elem() 119 | pobj := reflect.New(itype).Interface() 120 | k, err = TypeOf(ctx, pobj) 121 | } 122 | } else { 123 | k, err = TypeOf(ctx, reflect.New(ftype.In(i)).Interface()) 124 | } 125 | if err != nil { 126 | break 127 | } 128 | sig.Params = append(sig.Params, k) 129 | } 130 | if ftype.NumOut() == 1 { 131 | var k types.Typed 132 | k, err = reflectedType(ctx, reflect.New(ftype.Out(0)).Interface()) 133 | if err == nil { 134 | sig.Return = k 135 | } 136 | } else { 137 | sig.Return = types.Basic(types.VoidKind) 138 | } 139 | return 140 | } 141 | -------------------------------------------------------------------------------- /arglist.c.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | //#cgo CFLAGS:-I../include/ 3 | //#cgo LDFLAGS:-ljvm -L/usr/lib/jvm/default-java/jre/lib/amd64/server 4 | //#include "helpers.h" 5 | import "C" 6 | import ( 7 | "errors" 8 | "fmt" 9 | 10 | "strconv" 11 | "unsafe" 12 | ) 13 | 14 | // Implements the *C.jvalue pointer required 15 | // for passing arguments _into_ java. 16 | type argList []C.jvalue 17 | 18 | /// Returns an unsafe.Pointer to the arglist, suitable 19 | /// for passing to the JNI xxxA() variadic methods. 20 | /// The pointer is only valid at the time it is returned, 21 | /// and changes to the underlying arglist could invalidate 22 | /// this list. 23 | /// 24 | /// (If you're constructing w/o newArgList, be sure to use make(), 25 | /// in order to ensure the value references are aligned) 26 | func (self *argList) Ptr() unsafe.Pointer { 27 | if self == nil || len(*self) == 0 { 28 | //fmt.Printf("Returning a nil ptr!\n") 29 | return nil 30 | } 31 | return unsafe.Pointer(&((*self)[0])) 32 | } 33 | 34 | /* dereferences objects in a list, useful for deferrals */ 35 | func blowStack(env *Environment, objs []*Object) { 36 | for _, obj := range objs { 37 | env.DeleteLocalRef(obj) 38 | } 39 | } 40 | 41 | // TODO(refcounting): any constructed objects will be leaked on call return, 42 | // as nothing cleans up proxy objects. I'm also torn on how to differentiate 43 | // the objects made here and those coming in from other references. 44 | // 45 | // Refcounting attempt 1, objects _we_ construct will be returned in the objStack, 46 | // otherwise refcounts of 'pass-through' java natives are untouched by the call to newArgList. 47 | // 48 | // On error, the stack has already been blown (and will be empty). 49 | func newArgList(ctx *Environment, params ...interface{}) (alp argList, objStack []*Object, err error) { 50 | alp = make(argList, 0) 51 | defer func() { 52 | if err != nil { 53 | blowStack(ctx, objStack) 54 | objStack = []*Object{} 55 | } 56 | }() 57 | for i, param := range params { 58 | var ok C.int 59 | switch v := param.(type) { 60 | case int: 61 | alp = append(alp, C.intValue(C.jint(v))) 62 | case int64: 63 | alp = append(alp, C.longValue(C.jlong(v))) 64 | case C.jstring: 65 | alp = append(alp, C.objValue(v)) 66 | case C.jboolean: 67 | alp = append(alp, C.boolValue(v)) 68 | case C.jint: 69 | alp = append(alp, C.intValue(v)) 70 | case C.jobject: 71 | alp = append(alp, C.objValue(v)) 72 | case *Object: 73 | alp = append(alp, C.objValue(v.object)) 74 | case *CastObject: 75 | alp = append(alp, C.objValue(v.Object.object)) 76 | case *Class: 77 | alp = append(alp, C.objValue(v.class)) 78 | case C.jvalue: 79 | alp = append(alp, v) 80 | case string: 81 | var str *Object 82 | str, err = ctx.NewStringObject(v) 83 | if err == nil { 84 | objStack = append(objStack, str) 85 | alp = append(alp, C.objValue(str.object)) 86 | } 87 | case *ObjectArray: 88 | var klass *Class 89 | var arrayObj *Object 90 | 91 | klass, err = ctx.GetClass(v.Name) 92 | if err == nil { 93 | arrayObj, err = ctx.newObjectArray(len(v.Objects), klass, nil) 94 | } 95 | if err == nil { 96 | objStack = append(objStack, arrayObj) 97 | for i, o := range v.Objects { 98 | ctx.setObjectArrayElement(arrayObj, i, o) 99 | if ctx.ExceptionCheck() { 100 | err = ctx.ExceptionOccurred() 101 | } 102 | if err != nil { 103 | break 104 | } 105 | } 106 | } 107 | if err == nil { 108 | alp = append(alp, C.objValue(arrayObj.object)) 109 | } 110 | case []string: 111 | var klass *Class 112 | var obj *Object 113 | klass, err = ctx.GetClassStr("java/lang/String") 114 | // classes via this channel are cached and globally referenced by gojvm, not stacked. 115 | if err == nil { 116 | obj, err = ctx.newObjectArray(len(v), klass, nil) 117 | } 118 | if err == nil { 119 | objStack = append(objStack, obj) 120 | for i, s := range v { 121 | var str *Object 122 | str, err = ctx.NewStringObject(s) 123 | if err == nil { 124 | // I'm assuming stuffing the array adds a reference inside the JVM. 125 | defer ctx.DeleteLocalRef(str) 126 | ctx.setObjectArrayElement(obj, i, str) 127 | if ctx.ExceptionCheck() { 128 | err = ctx.ExceptionOccurred() 129 | } 130 | 131 | } 132 | if err != nil { 133 | break 134 | } 135 | } 136 | } 137 | if err == nil { 138 | alp = append(alp, C.objValue(obj.object)) 139 | } 140 | case []byte: 141 | var obj *Object 142 | obj, err = ctx.newByteObject(v) 143 | if err == nil { 144 | alp = append(alp, C.objValue(obj.object)) 145 | } 146 | objStack = append(objStack, obj) 147 | case bool: 148 | val := C.jboolean(C.JNI_FALSE) 149 | if v { 150 | val = C.JNI_TRUE 151 | } 152 | alp = append(alp, C.boolValue(val)) 153 | default: 154 | err = errors.New(fmt.Sprintf("Unknown type: %T/%s", v, v)) 155 | } 156 | if ok != 0 { 157 | err = errors.New("Couldn't parse arg #" + strconv.Itoa(i+1)) 158 | } 159 | if err != nil { 160 | break 161 | } 162 | } 163 | return 164 | } 165 | 166 | // Essentially, the java generic. Type information is NOT carried 167 | // with the value, however is required for proper use. (don't use 168 | // unless you know the distinction between jobject, jclass and jvalue). 169 | type Value struct { 170 | val C.jvalue 171 | } 172 | -------------------------------------------------------------------------------- /globals.c.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | //#include "helpers.h" 4 | import "C" 5 | import ( 6 | "errors" 7 | "github.com/timob/gojvm/types" 8 | "reflect" 9 | "sync" 10 | "unsafe" 11 | ) 12 | 13 | var debug = false 14 | 15 | // due to the design of the JNI callback method, in order to support 16 | // callbacks (safely), we need to be able to map from C.JVM and C.JNIEnv 17 | // into their local types. 18 | // the AllVMs & AllEnvs structures handles this via a go (RW)mutex. 19 | type vmPtrMap struct { 20 | jvms map[uintptr]*JVM 21 | maplock *sync.RWMutex 22 | } 23 | 24 | type envPtrMap struct { 25 | envs map[uintptr]*Environment 26 | maplock *sync.RWMutex 27 | } 28 | 29 | var AllVMs = &vmPtrMap{map[uintptr]*JVM{}, &sync.RWMutex{}} 30 | var AllEnvs = &envPtrMap{map[uintptr]*Environment{}, &sync.RWMutex{}} 31 | 32 | func (self *vmPtrMap) Find(ptr uintptr) *JVM { 33 | self.maplock.RLock() 34 | defer self.maplock.RUnlock() 35 | return self.jvms[ptr] 36 | } 37 | 38 | func (self *vmPtrMap) Add(vm *JVM) { 39 | self.maplock.Lock() 40 | defer self.maplock.Unlock() 41 | self.jvms[uintptr(unsafe.Pointer(vm.jvm))] = vm 42 | } 43 | 44 | func (self *envPtrMap) Find(ptr uintptr) *Environment { 45 | self.maplock.RLock() 46 | defer self.maplock.RUnlock() 47 | return self.envs[ptr] 48 | } 49 | 50 | func (self *envPtrMap) Add(env *Environment) { 51 | self.maplock.Lock() 52 | defer self.maplock.Unlock() 53 | self.envs[uintptr(unsafe.Pointer(env.env))] = env 54 | } 55 | 56 | //export goCallbackNArgs 57 | func goCallbackNArgs(envp, obj uintptr, fId int) int { 58 | env := AllEnvs.Find(envp) 59 | if env == nil { 60 | return -1 61 | } 62 | if env.jvm == nil { 63 | return -1 64 | } 65 | var cd callbackDescriptor 66 | var ok bool 67 | if cd, ok = env.jvm.registered[fId]; !ok { 68 | return -1 69 | } 70 | return len(cd.Signature.Params) 71 | } 72 | 73 | // C callbacks actually start in the 'generifiedX' calls (see the .c files) 74 | // Next, goCallbackNArgs is used to determine the number of variadic paramers to expect ( 75 | // java doesn't inform us of this, and we can't force any of the callback parameters. 76 | // 77 | // Finally, goCallback looks up the 'fId' - our internal function reference ID (the X in generified), 78 | // un(re) marshalls all the parameters appropriately, calls our function, and returns 79 | // any underlying value back to generified who will return it to the JVM. 80 | // The initial jbool indicates success, and any failure should check for exceptions. 81 | // 82 | //export goCallback 83 | func goCallback(envp, obj uintptr, fId int, nargs int, argp uintptr) (ok C.jboolean, val interface{}) { 84 | args := C.ArgListPtr(unsafe.Pointer(argp)) 85 | env := AllEnvs.Find(envp) 86 | if env == nil { 87 | panic("Got a nil environment") 88 | } 89 | if env.jvm == nil { 90 | panic("Environment pointer has no JVM") 91 | } 92 | var cd callbackDescriptor 93 | var _ok bool 94 | if cd, _ok = env.jvm.registered[fId]; !_ok { 95 | print("Unknown callbackId: \t", fId, "\n") 96 | return 97 | } 98 | // TODO: pack argp somehow... 99 | if nargs != len(cd.Signature.Params) { 100 | panic("callback/signature length mismatch") 101 | } 102 | inCall := []reflect.Value{reflect.ValueOf(env), reflect.ValueOf(newObject(C.jobject(unsafe.Pointer(obj))))} 103 | var err error 104 | for i := 0; i < nargs; i++ { 105 | switch cd.Signature.Params[i].Kind() { 106 | case types.BoolKind: 107 | inCall = append(inCall, reflect.ValueOf(bool(0 != C.valBool(C.getArg(args, C.int(i)))))) 108 | case types.LongKind: 109 | inCall = append(inCall, reflect.ValueOf(int64(C.valLong(C.getArg(args, C.int(i)))))) 110 | case types.IntKind: 111 | inCall = append(inCall, reflect.ValueOf(int(C.valInt(C.getArg(args, C.int(i)))))) 112 | case types.ShortKind: 113 | inCall = append(inCall, reflect.ValueOf(int16(C.valShort(C.getArg(args, C.int(i)))))) 114 | case types.FloatKind: 115 | inCall = append(inCall, reflect.ValueOf(float32(C.valFloat(C.getArg(args, C.int(i)))))) 116 | case types.DoubleKind: 117 | inCall = append(inCall, reflect.ValueOf(float64(C.valDouble(C.getArg(args, C.int(i)))))) 118 | case types.ClassKind: 119 | inCall = append(inCall, reflect.ValueOf(newObject(C.valObject(C.getArg(args, C.int(i)))))) 120 | default: 121 | err = errors.New("Couldn't reflect kind " + cd.Signature.Params[i].Kind().TypeString()) 122 | } 123 | if err != nil { 124 | break 125 | } 126 | } 127 | if err != nil { 128 | return 129 | } 130 | outCall := reflect.ValueOf(cd.F).Call(inCall) 131 | switch cd.Signature.Return.Kind() { 132 | case types.VoidKind: 133 | return 1, nil 134 | } 135 | switch cd.Signature.Return.Kind() { 136 | case types.BoolKind: 137 | if outCall[0].Interface().(bool) { 138 | return 1, C.jboolean(C.JNI_TRUE) 139 | } else { 140 | return 1, C.jboolean(C.JNI_FALSE) 141 | } 142 | case types.ByteKind: 143 | return 1, C.jbyte(outCall[0].Interface().(byte)) 144 | case types.CharKind: 145 | return 1, C.jchar(outCall[0].Interface().(int)) 146 | case types.IntKind: 147 | return 1, C.jint(outCall[0].Interface().(int)) 148 | case types.ShortKind: 149 | return 1, C.jshort(outCall[0].Interface().(int16)) 150 | case types.LongKind: 151 | return 1, C.jint(outCall[0].Interface().(int64)) 152 | case types.FloatKind: 153 | return 1, C.jfloat(outCall[0].Interface().(float32)) 154 | case types.DoubleKind: 155 | return 1, C.jdouble(outCall[0].Interface().(float64)) 156 | case types.ClassKind: 157 | klass := cd.Signature.Return.(types.Class).Klass 158 | if klass.Cmp(types.JavaLangString) == 0 { 159 | var obj *Object 160 | str := outCall[0].Interface().(string) 161 | obj, err = env.NewStringObject(str) 162 | if err == nil { 163 | return 1, C.jstring(obj.object) 164 | } // else, exception occurred 165 | // not needed as callbacks will reap their own refs. 166 | // env.DeleteLocalRef(obj) 167 | print("String Error\t", err.Error()) 168 | return 0, nil 169 | } 170 | return 1, C.jobject(outCall[0].Interface().(*Object).object) 171 | default: 172 | panic("array return type not yet supported") 173 | } 174 | 175 | panic("not reached") 176 | } 177 | -------------------------------------------------------------------------------- /object.c.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | //#cgo CFLAGS:-I../include/ 4 | //#cgo LDFLAGS:-ljvm -L/usr/lib/jvm/java-6-sun/jre/lib/amd64/server/ 5 | //#include "helpers.h" 6 | import "C" 7 | import ( 8 | "github.com/timob/gojvm/types" 9 | // "log" 10 | "unsafe" 11 | ) 12 | 13 | type Object struct { 14 | object C.jobject 15 | } 16 | 17 | func NewObjectStruct(o unsafe.Pointer) *Object { 18 | return &Object{C.jobject(o)} 19 | } 20 | 21 | // returns a new object value with specified parameters 22 | // NB: refs are NOT adjusted directly by this call! Use it as a casting/construction-helper, 23 | // not a Clone() 24 | func newObject(obj C.jobject) *Object { 25 | return &Object{obj} 26 | } 27 | 28 | /* 29 | Returns the Class() associated with the object 30 | */ 31 | func (self *Object) ObjectClass(env *Environment) (c *Class, err error) { 32 | return env.GetObjectClass(self) 33 | } 34 | 35 | var ClassClass = types.Name{"java", "lang", "Class"} 36 | /* 37 | Returns the (potentially cached) name of the ObjectClass of the 38 | named object. 39 | */ 40 | func (self *Object) Name(env *Environment) (name types.Name, err error) { 41 | clsObj, err := self.CallObj(env, false, "getClass", types.Class{ClassClass}) 42 | if err != nil { 43 | return 44 | } 45 | 46 | nameStr, _, err := clsObj.CallString(env, false, "getName") 47 | if err != nil { 48 | return 49 | } 50 | return types.NewName(nameStr), nil 51 | /* 52 | var c *Class 53 | c, err = clsObj.ObjectClass(env) 54 | if err == nil { 55 | defer env.DeleteLocalClassRef(c) 56 | name, err = c.GetName(env) 57 | } else { 58 | log.Printf("Couldn't get object class!") 59 | } 60 | return 61 | */ 62 | } 63 | 64 | func (self *Object) CallVoid(env *Environment, static bool, mname string, params ...interface{}) (err error) { 65 | return env.CallObjectVoid(self, static, mname, params...) 66 | } 67 | 68 | func (self *Object) CallInt(env *Environment, static bool, mname string, params ...interface{}) (i int, err error) { 69 | return env.CallObjectInt(self, static, mname, params...) 70 | } 71 | 72 | func (self *Object) CallLong(env *Environment, static bool, mname string, params ...interface{}) (i int64, err error) { 73 | return env.CallObjectLong(self, static, mname, params...) 74 | } 75 | 76 | func (self *Object) CallBool(env *Environment, static bool, mname string, params ...interface{}) (i bool, err error) { 77 | return env.CallObjectBool(self, static, mname, params...) 78 | } 79 | 80 | func (self *Object) CallFloat(env *Environment, static bool, mname string, params ...interface{}) (i float32, err error) { 81 | return env.CallObjectFloat(self, static, mname, params...) 82 | } 83 | 84 | func (self *Object) CallShort(env *Environment, static bool, mname string, params ...interface{}) (i int16, err error) { 85 | return env.CallObjectShort(self, static, mname, params...) 86 | } 87 | 88 | func (self *Object) CallDouble(env *Environment, static bool, mname string, params ...interface{}) (i float64, err error) { 89 | return env.CallObjectDouble(self, static, mname, params...) 90 | } 91 | 92 | func (self *Object) CallLongArray(env *Environment, static bool, mname string, params ...interface{}) (i []int64, err error) { 93 | return env.CallObjectLongArray(self, static, mname, params...) 94 | } 95 | 96 | func (self *Object) CallIntArray(env *Environment, static bool, mname string, params ...interface{}) (i []int, err error) { 97 | return env.CallObjectIntArray(self, static, mname, params...) 98 | } 99 | 100 | // Calls the named Object-method on the object instance 101 | func (self *Object) CallObj(env *Environment, static bool, mname string, rval types.Typed, params ...interface{}) (vObj *Object, err error) { 102 | return env.CallObjectObj(self, static, mname, rval, params...) 103 | } 104 | 105 | /* 106 | A wrapper around ObjCallObj specific to java/lang/String, that will return the result as a GoString 107 | 108 | A null string returned with no Exception can be differentiated via the wasNull return value. 109 | */ 110 | func (self *Object) CallString(env *Environment, static bool, mname string, params ...interface{}) (str string, wasNull bool, err error) { 111 | return env.CallObjectString(self, static, mname, params...) 112 | } 113 | 114 | type CastObject struct { 115 | *Object 116 | types.Name 117 | } 118 | 119 | type ObjectArray struct { 120 | Objects []*Object 121 | types.Name 122 | } 123 | 124 | /* 125 | func (self *AsJavaLangObject) Name(env *Environment) (name types.Name, err error) { 126 | return types.Name{"java", "lang", "Object"}, nil 127 | } 128 | */ 129 | 130 | 131 | //fields 132 | 133 | func (self *Object) GetObjField(env *Environment, static bool, name string, rval types.Typed) (*Object, error) { 134 | return env.GetObjectObjField(self, static, name, rval) 135 | } 136 | 137 | func (self *Object) GetBooleanField(env *Environment, static bool, name string) (bool, error) { 138 | return env.GetObjectBooleanField(self, static, name) 139 | } 140 | 141 | func (self *Object) GetShortField(env *Environment, static bool, name string) (int16, error) { 142 | return env.GetObjectShortField(self, static, name) 143 | } 144 | 145 | func (self *Object) GetIntField(env *Environment, static bool, name string) (int, error) { 146 | return env.GetObjectIntField(self, static, name) 147 | } 148 | 149 | func (self *Object) GetLongField(env *Environment, static bool, name string) (int64, error) { 150 | return env.GetObjectLongField(self, static, name) 151 | } 152 | 153 | func (self *Object) GetFloatField(env *Environment, static bool, name string) (float32, error) { 154 | return env.GetObjectFloatField(self, static, name) 155 | } 156 | 157 | func (self *Object) GetDoubleField(env *Environment, static bool, name string) (float64, error) { 158 | return env.GetObjectDoubleField(self, static, name) 159 | } 160 | 161 | func (self *Object) GetIntArrayField(env *Environment, static bool, name string) ([]int, error) { 162 | return env.GetObjecIntArrayField(self, static, name) 163 | } 164 | 165 | 166 | // ---- 167 | 168 | 169 | func (self *Object) SetObjField(env *Environment, static bool, name string, rval types.Typed, val *Object) (err error) { 170 | return env.setObjField(self, static, name, rval, val) 171 | } 172 | 173 | func (self *Object) SetBooleanField(env *Environment, static bool, name string, val bool) (err error) { 174 | return env.setBoolField(self, static, name, val) 175 | } 176 | 177 | func (self *Object) SetShortField(env *Environment, static bool, name string, val int16) (err error) { 178 | return env.setShortField(self, static, name, val) 179 | } 180 | 181 | func (self *Object) SetIntField(env *Environment, static bool, name string, val int) (err error) { 182 | return env.setIntField(self, static, name, val) 183 | } 184 | 185 | func (self *Object) SetLongField(env *Environment, static bool, name string, val int64) (err error) { 186 | return env.setLongField(self, static, name, val) 187 | } 188 | 189 | func (self *Object) SetFloatField(env *Environment, static bool, name string, val float32) (err error) { 190 | return env.setFloatField(self, static, name, val) 191 | } 192 | 193 | func (self *Object) SetDoubleField(env *Environment, static bool, name string, val float64) (err error) { 194 | return env.setDoubleField(self, static, name, val) 195 | } 196 | -------------------------------------------------------------------------------- /include/helpers.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | 9 | #ifndef HELPERS_H 10 | #define HELPERS_H 11 | 12 | typedef uintptr_t uintptr; 13 | 14 | // VarArgs for variadic 15 | typedef struct { 16 | int length; 17 | jvalue *values; 18 | } ArgList, *ArgListPtr; 19 | 20 | // env wrappers 21 | // exception wrappers 22 | 23 | jboolean envExceptionCheck(JNIEnv *); 24 | jthrowable envExceptionOccurred(JNIEnv *); 25 | void envExceptionDescribe(JNIEnv*); 26 | void envExceptionClear(JNIEnv*); 27 | 28 | 29 | // ref calls 30 | 31 | jobject envNewLocalRef(JNIEnv *env, jobject ref) ; 32 | void envDeleteLocalRef(JNIEnv *env, jobject obj) ; 33 | 34 | 35 | jclass envFindClass(JNIEnv *, const char *); 36 | jmethodID envGetMethodID(JNIEnv *, jobject, const char *, const char *); 37 | jmethodID envGetStaticMethodID(JNIEnv *env, jclass jobj, const char *meth, const char *sig); 38 | 39 | jclass envGetObjectClass(JNIEnv *, jobject); 40 | jstring envNewStringUTF(JNIEnv *, const char *); 41 | jsize envGetStringUTFLength(JNIEnv *, jstring); 42 | const char *envGetStringUTFChars(JNIEnv *, jstring, jboolean *); 43 | void envReleaseStringUTFChars(JNIEnv *, jstring, const char *); 44 | 45 | 46 | 47 | jobjectArray envNewObjectArray(JNIEnv *env, jsize, jclass, jobject); 48 | void envSetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject val); 49 | jobject envGetObjectArrayElement(JNIEnv *env, jobjectArray, jsize); 50 | 51 | jbyteArray envNewByteArray(JNIEnv *env, jsize len); 52 | void envSetByteArrayRegion(JNIEnv *env, jbyteArray array, jsize start, jsize len, const void *buf); 53 | 54 | jfloat envCallFloatMethodA(JNIEnv *, jobject, jmethodID, void *); 55 | jfloat envCallStaticFloatMethodA(JNIEnv *, jobject, jmethodID, void *); 56 | 57 | jdouble envCallDoubleMethodA(JNIEnv *, jobject, jmethodID, void *); 58 | jdouble envCallStaticDoubleMethodA(JNIEnv *, jobject, jmethodID, void *); 59 | 60 | jboolean envCallBoolMethodA(JNIEnv *, jobject, jmethodID, void *); 61 | jboolean envCallStaticBoolMethodA(JNIEnv *, jobject, jmethodID, void *); 62 | 63 | jshort envCallShortMethodA(JNIEnv *, jobject, jmethodID, void *); 64 | jshort envCallStaticShortMethodA(JNIEnv *, jclass, jmethodID, void *); 65 | 66 | jint envCallIntMethodA(JNIEnv *, jobject, jmethodID, void *); 67 | jint envCallStaticIntMethodA(JNIEnv *, jclass, jmethodID, void *); 68 | 69 | jlong envCallLongMethodA(JNIEnv *, jobject, jmethodID, void *); 70 | jlong envCallStaticLongMethodA(JNIEnv *, jclass, jmethodID, void *); 71 | 72 | jobject envCallObjectMethodA(JNIEnv *, jobject, jmethodID, void *); 73 | jobject envCallStaticObjectMethodA(JNIEnv *, jclass, jmethodID, void *); 74 | 75 | void envCallVoidMethodA(JNIEnv *, jobject, jmethodID, void *); 76 | void envCallStaticVoidMethodA(JNIEnv *, jclass, jmethodID, void *); 77 | 78 | jint envGetArrayLength(JNIEnv *, jobject); 79 | jobject envNewGlobalRef(JNIEnv *, jobject); 80 | 81 | jobject envNewObjectA(JNIEnv *, jclass, jmethodID, void *); 82 | jobject envNewObjectALP(JNIEnv *, jclass, jmethodID, ArgListPtr); 83 | 84 | jboolean envIsSameObject(JNIEnv *, jobject, jobject); 85 | 86 | jbyte *envGetByteArrayElements(JNIEnv *, jobject, jboolean *); 87 | void envReleaseByteArrayElements(JNIEnv *, jobject, jbyte *, jint); 88 | 89 | jlong *envGetLongArrayElements(JNIEnv *, jobject, jboolean *); 90 | void envReleaseLongArrayElements(JNIEnv *, jobject, jlong *, jint); 91 | 92 | jint *envGetIntArrayElements(JNIEnv *, jobject, jboolean *); 93 | void envReleaseIntArrayElements(JNIEnv *, jobject, jint *, jint); 94 | 95 | jvalue getArg(ArgListPtr, int); 96 | 97 | // fields 98 | jfieldID envGetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig); 99 | 100 | jobject envGetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID); 101 | jboolean envGetStaticBooleanField(JNIEnv *env, jclass clazz, jfieldID fieldID); 102 | jbyte envGetStaticByteField(JNIEnv *env, jclass clazz, jfieldID fieldID); 103 | jchar envGetStaticCharField(JNIEnv *env, jclass clazz, jfieldID fieldID); 104 | jshort envGetStaticShortField(JNIEnv *env, jclass clazz, jfieldID fieldID); 105 | jint envGetStaticIntField(JNIEnv *env, jclass clazz, jfieldID fieldID); 106 | jlong envGetStaticLongField(JNIEnv *env, jclass clazz, jfieldID fieldID); 107 | jfloat envGetStaticFloatField(JNIEnv *env, jclass clazz, jfieldID fieldID); 108 | jdouble envGetStaticDoubleField(JNIEnv *env, jclass clazz, jfieldID fieldID); 109 | 110 | 111 | jfieldID envGetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig); 112 | 113 | jobject envGetObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID); 114 | jboolean envGetBooleanField(JNIEnv *env, jclass clazz, jfieldID fieldID); 115 | jbyte envGetByteField(JNIEnv *env, jclass clazz, jfieldID fieldID); 116 | jchar envGetCharField(JNIEnv *env, jclass clazz, jfieldID fieldID); 117 | jshort envGetShortField(JNIEnv *env, jclass clazz, jfieldID fieldID); 118 | jint envGetIntField(JNIEnv *env, jclass clazz, jfieldID fieldID); 119 | jlong envGetLongField(JNIEnv *env, jclass clazz, jfieldID fieldID); 120 | jfloat envGetFloatField(JNIEnv *env, jclass clazz, jfieldID fieldID); 121 | jdouble envGetDoubleField(JNIEnv *env, jclass clazz, jfieldID fieldID); 122 | 123 | void envSetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject val); 124 | void envSetStaticBooleanField(JNIEnv *env, jclass clazz, jfieldID fieldID,jboolean val); 125 | void envSetStaticByteField(JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte val); 126 | void envSetStaticCharField(JNIEnv *env, jclass clazz, jfieldID fieldID, jchar val); 127 | void envSetStaticShortField(JNIEnv *env, jclass clazz, jfieldID fieldID, jshort val); 128 | void envSetStaticIntField(JNIEnv *env, jclass clazz, jfieldID fieldID, jint val); 129 | void envSetStaticLongField(JNIEnv *env, jclass clazz, jfieldID fieldID, jlong val); 130 | void envSetStaticFloatField(JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat val); 131 | void envSetStaticDoubleField(JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble val); 132 | 133 | void envSetObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject val); 134 | void envSetBooleanField(JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean val); 135 | void envSetByteField(JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte val); 136 | void envSetCharField(JNIEnv *env, jclass clazz, jfieldID fieldID, jchar val); 137 | void envSetShortField(JNIEnv *env, jclass clazz, jfieldID fieldID, jshort val); 138 | void envSetIntField(JNIEnv *env, jclass clazz, jfieldID fieldID, jint val); 139 | void envSetLongField(JNIEnv *env, jclass clazz, jfieldID fieldID, jlong val); 140 | void envSetFloatField(JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat val); 141 | void envSetDoubleField(JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble val); 142 | 143 | 144 | 145 | 146 | 147 | // internal helpers 148 | int addStringArgument(JavaVMInitArgs *args, const char *string); 149 | // vm Calls 150 | // env is actually a void **, but we allow void to make CGo easier 151 | // cleaner solutions welcome! :) 152 | jint newJVMContext(JavaVM **, void *, JavaVMInitArgs *); 153 | jint vmAttachCurrentThread(JavaVM *jvm, void *env, void *args); 154 | jint vmDetachCurrentThread(JavaVM *jvm); 155 | 156 | 157 | jint envGetJavaVM(JNIEnv *, JavaVM **); 158 | jint envRegisterNative(JNIEnv *, jclass, char *, char *, void *); 159 | jint envUnregisterNatives(JNIEnv *, jclass); 160 | 161 | 162 | 163 | // jvalue conversions 164 | jvalue boolValue(jboolean v); 165 | jvalue byteValue(jbyte v); 166 | jvalue charValue(jchar v); 167 | jvalue shortValue(jshort v); 168 | jvalue intValue(jint v); 169 | jvalue longValue(jlong v); 170 | jvalue floatValue(jfloat v); 171 | jvalue doubleValue(jdouble v); 172 | jvalue objValue(jobject v); 173 | 174 | 175 | 176 | void voidCallback0(JNIEnv *, jobject, void* ); 177 | void voidCallback1(JNIEnv *, jobject, void* ); 178 | 179 | void *generified0(JNIEnv *env, jobject obj, ...); 180 | void *generified1(JNIEnv *env, jobject obj, ...); 181 | void *generified2(JNIEnv *env, jobject obj, ...); 182 | void *generified3(JNIEnv *env, jobject obj, ...); 183 | void *generified4(JNIEnv *env, jobject obj, ...); 184 | void *generified5(JNIEnv *env, jobject obj, ...); 185 | void *generified6(JNIEnv *env, jobject obj, ...); 186 | void *generified7(JNIEnv *env, jobject obj, ...); 187 | void *generified8(JNIEnv *env, jobject obj, ...); 188 | void *generified9(JNIEnv *env, jobject obj, ...); 189 | 190 | 191 | jboolean valBool(jvalue v) ; 192 | jbyte valByte(jvalue v) ; 193 | jchar valChar(jvalue v) ; 194 | jshort valShort(jvalue v) ; 195 | jint valInt(jvalue v) ; 196 | jlong valLong(jvalue v) ; 197 | jfloat valFloat(jvalue v) ; 198 | jdouble valDouble(jvalue v) ; 199 | jobject valObject(jvalue v) ; 200 | 201 | 202 | 203 | #endif 204 | -------------------------------------------------------------------------------- /jvm_xclass_test.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | /* Tests various external classes pre-disposed to have certain.. 'issues' 4 | */ 5 | import ( 6 | "github.com/timob/gojvm/types" 7 | "testing" 8 | ) 9 | 10 | var TrivialClass = "org/golang/ext/gojvm/testing/Trivial" 11 | /* Provides a variety of constructors, and one niladic getConstructorUsed; 12 | 13 | Verifies: 14 | Method (parameter) reflection 15 | Ctx.NewClass, 16 | Marshalling (int,int64,string,{}) 17 | */ 18 | 19 | var PathosClass = "org/golang/ext/gojvm/testing/Pathos" 20 | /* Throws an exception on construction; 21 | Verifies, exception on obj.New() 22 | */ 23 | 24 | var NativeClass = "org/golang/ext/gojvm/testing/Native" 25 | /* 26 | Has attachable native methods 27 | */ 28 | 29 | /* Doesn't exist 30 | 31 | Verifies: 32 | Exception for missing class 33 | */ 34 | var MissingClass = "org/golang/ext/MissingClass" 35 | 36 | type trivialClassTest struct { 37 | ConstArgs []interface{} 38 | } 39 | 40 | var trivialClassTests = []trivialClassTest{ 41 | trivialClassTest{[]interface{}{}}, 42 | trivialClassTest{[]interface{}{3}}, 43 | trivialClassTest{[]interface{}{"aString"}}, 44 | trivialClassTest{[]interface{}{int64(32)}}, 45 | } 46 | 47 | func TestJVMTrivialClass(t *testing.T) { 48 | env := setupJVM(t) 49 | for i, test := range trivialClassTests { 50 | form, err := FormFor(env, types.Basic(types.VoidKind), test.ConstArgs...) 51 | fatalIf(t, err != nil, "[%d] Error generating formFor: %v", i, err) 52 | fatalIf(t, form == "", "Got nil form") 53 | klass, err := env.NewInstanceStr(TrivialClass, test.ConstArgs...) 54 | fatalIf(t, err != nil, "[%d] Error generating formFor: %v", i, err) 55 | kused, _, err := klass.CallString(env, false, "getConstructorUsed") 56 | fatalIf(t, err != nil, "[%d] Error getting constructor used: %v", i, err) 57 | fatalIf(t, kused != form, "[%d] Constructor called was wrong (Exp: %s, got: %s)", form, kused) 58 | 59 | } 60 | } 61 | 62 | func TestJVMPathosClass(t *testing.T) { 63 | env := setupJVM(t) 64 | // We mute expected exceptions because otherwise the test looks sloppy (and FAILS are hard to see) 65 | defer defMute(env)() 66 | klass, err := env.NewInstanceStr(PathosClass) 67 | fatalIf(t, klass != nil, "Pathos should throw an exception (be nil), but got %v", klass) 68 | fatalIf(t, err == nil, "Pathos didn't throw an exception") 69 | } 70 | 71 | func TestJVMMissingClass(t *testing.T) { 72 | env := setupJVM(t) 73 | defer defMute(env)() 74 | // We mute expected exceptions because otherwise the test looks sloppy (and FAILS are hard to see) 75 | klass, err := env.NewInstanceStr(MissingClass) 76 | fatalIf(t, klass != nil, "Missing should throw an exception (be nil), but got %v", klass) 77 | fatalIf(t, err == nil, "Missing didn't throw an exception") 78 | } 79 | 80 | func TestJVMNativeVoidClass(t *testing.T) { 81 | env := setupJVM(t) 82 | //defer defMute(env)() 83 | nativePings := 0 84 | klass, err := env.GetClassStr(NativeClass) 85 | fatalIf(t, err != nil, "Native threw an exception", err) 86 | fatalIf(t, klass == nil, "Native klass is nil!") 87 | err = env.RegisterNative(klass, "NativePing", func(E *Environment, O *Object) { 88 | nativePings += 1 89 | }) 90 | fatalIf(t, err != nil, "RegisterNative threw an exception", err) 91 | obj, err := env.NewInstanceStr(NativeClass) 92 | fatalIf(t, err != nil, "Couldn't instantiate NativeClass: %v", err) 93 | fatalIf(t, obj == nil, "Instantiated NativeClass is nil") 94 | err = obj.CallVoid(env, false, "NativePing") 95 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativePing(): %v", err) 96 | fatalIf(t, nativePings != 1, "Wrong native ping count: %d\n", nativePings) 97 | } 98 | 99 | func TestJVMNativeIntClass(t *testing.T) { 100 | env := setupJVM(t) 101 | //defer defMute(env)() 102 | klass, err := env.GetClassStr(NativeClass) 103 | fatalIf(t, err != nil, "Native threw an exception", err) 104 | fatalIf(t, klass == nil, "Native klass is nil!") 105 | err = env.RegisterNative(klass, "NativeInt", func(E *Environment, O *Object) (i int) { 106 | return 15 107 | }) 108 | fatalIf(t, err != nil, "RegisterNative threw an exception", err) 109 | obj, err := env.NewInstanceStr(NativeClass) 110 | fatalIf(t, err != nil, "Couldn't instantiate NativeClass: %v", err) 111 | fatalIf(t, obj == nil, "Instantiated NativeClass is nil") 112 | ival, err := obj.CallInt(env, false, "NativeInt") 113 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeInt(): %v", err) 114 | fatalIf(t, ival != 15, "wrong returned value from native: %d", ival) 115 | } 116 | 117 | func TestJVMNativeComplexClass(t *testing.T) { 118 | env := setupJVM(t) 119 | //defer defMute(env)() 120 | klass, err := env.GetClassStr(NativeClass) 121 | fatalIf(t, err != nil, "Native threw an exception", err) 122 | fatalIf(t, klass == nil, "Native klass is nil!") 123 | obj1, err := env.NewInstanceStr("java/lang/Object") 124 | fatalIf(t, err != nil, "new(Object) threw an exception", err) 125 | obj2, err := env.NewInstanceStr("java/lang/Object") 126 | fatalIf(t, err != nil, "new(Object2) threw an exception", err) 127 | hit := false 128 | err = env.RegisterNative(klass, "NativeComplex", func(E *Environment, O *Object, o1 *Object, o2 *Object, i1 int) { 129 | hit = true 130 | }) 131 | fatalIf(t, err != nil, "RegisterNative threw an exception", err) 132 | obj, err := env.NewInstanceStr(NativeClass) 133 | fatalIf(t, err != nil, "Couldn't instantiate NativeClass: %v", err) 134 | fatalIf(t, obj == nil, "Instantiated NativeClass is nil") 135 | err = env.CallObjectVoid(obj, false, "NativeComplex", obj1, obj2, 13) 136 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeComplex(): %v", err) 137 | fatalIf(t, !hit, "Native complex never got called", err) 138 | } 139 | 140 | func TestJVMNativeBoolClass(t *testing.T) { 141 | env := setupJVM(t) 142 | //defer defMute(env)() 143 | klass, err := env.GetClassStr(NativeClass) 144 | fatalIf(t, err != nil, "Native threw an exception", err) 145 | fatalIf(t, klass == nil, "Native klass is nil!") 146 | hit := false 147 | err = env.RegisterNative(klass, "NativeBool", func(E *Environment, O *Object) bool { 148 | return !hit 149 | }) 150 | fatalIf(t, err != nil, "RegisterNative threw an exception", err) 151 | obj, err := env.NewInstanceStr(NativeClass) 152 | fatalIf(t, err != nil, "Couldn't instantiate NativeClass: %v", err) 153 | fatalIf(t, obj == nil, "Instantiated NativeClass is nil") 154 | ok, err := obj.CallBool(env, false, "NativeBool") 155 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeBool(): %v", err) 156 | fatalIf(t, ok == hit, "Native complex never got called", err) 157 | hit = !hit 158 | ok, err = obj.CallBool(env, false, "NativeBool") 159 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeBool(): %v", err) 160 | fatalIf(t, ok == hit, "Native complex never got called", err) 161 | } 162 | 163 | func TestJVMNativeLongClass(t *testing.T) { 164 | env := setupJVM(t) 165 | //defer defMute(env)() 166 | klass, err := env.GetClassStr(NativeClass) 167 | fatalIf(t, err != nil, "Native threw an exception", err) 168 | fatalIf(t, klass == nil, "Native klass is nil!") 169 | hit := int64(0) 170 | err = env.RegisterNative(klass, "NativeLong", func(E *Environment, O *Object) int64 { 171 | return hit 172 | }) 173 | fatalIf(t, err != nil, "RegisterNative threw an exception", err) 174 | obj, err := env.NewInstanceStr(NativeClass) 175 | fatalIf(t, err != nil, "Couldn't instantiate NativeClass: %v", err) 176 | fatalIf(t, obj == nil, "Instantiated NativeClass is nil") 177 | ok, err := obj.CallLong(env, false, "NativeLong") 178 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeBool(): %v", err) 179 | fatalIf(t, ok != hit, "NativeLong got wrong value: %d", ok) 180 | hit = -5128 181 | ok, err = obj.CallLong(env, false, "NativeLong") 182 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeBool(): %v", err) 183 | fatalIf(t, ok != hit, "NativeLong got wrong value: %d", ok) 184 | } 185 | 186 | func TestJVMNativeFloatClass(t *testing.T) { 187 | env := setupJVM(t) 188 | //defer defMute(env)() 189 | klass, err := env.GetClassStr(NativeClass) 190 | fatalIf(t, err != nil, "Native threw an exception", err) 191 | fatalIf(t, klass == nil, "Native klass is nil!") 192 | hit := float32(.1234) 193 | err = env.RegisterNative(klass, "NativeFloat", func(E *Environment, O *Object) float32 { 194 | return hit 195 | }) 196 | fatalIf(t, err != nil, "RegisterNative threw an exception", err) 197 | obj, err := env.NewInstanceStr(NativeClass) 198 | fatalIf(t, err != nil, "Couldn't instantiate NativeClass: %v", err) 199 | fatalIf(t, obj == nil, "Instantiated NativeClass is nil") 200 | ok, err := obj.CallFloat(env, false, "NativeFloat") 201 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeFloat(): %v", err) 202 | fatalIf(t, ok != hit, "NativeLong got wrong value: %d", ok) 203 | hit = 1 / 20 204 | ok, err = obj.CallFloat(env, false, "NativeFloat") 205 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeFloat(): %v", err) 206 | fatalIf(t, ok != hit, "NativeFloat got wrong value: %d", ok) 207 | } 208 | 209 | func TestJVMNativeShortClass(t *testing.T) { 210 | env := setupJVM(t) 211 | //defer defMute(env)() 212 | klass, err := env.GetClassStr(NativeClass) 213 | fatalIf(t, err != nil, "Native threw an exception", err) 214 | fatalIf(t, klass == nil, "Native klass is nil!") 215 | hit := int16(0) 216 | err = env.RegisterNative(klass, "NativeShort", func(E *Environment, O *Object) int16 { 217 | return hit 218 | }) 219 | fatalIf(t, err != nil, "RegisterNative threw an exception", err) 220 | obj, err := env.NewInstanceStr(NativeClass) 221 | fatalIf(t, err != nil, "Couldn't instantiate NativeClass: %v", err) 222 | fatalIf(t, obj == nil, "Instantiated NativeClass is nil") 223 | ok, err := obj.CallShort(env, false, "NativeShort") 224 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeBool(): %v", err) 225 | fatalIf(t, ok != hit, "NativeShort got wrong value: %d", ok) 226 | hit = -5128 227 | ok, err = obj.CallShort(env, false, "NativeShort") 228 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeBool(): %v", err) 229 | fatalIf(t, ok != hit, "NativeShort got wrong value: %d", ok) 230 | } 231 | 232 | func TestJVMNativeDoubleClass(t *testing.T) { 233 | env := setupJVM(t) 234 | //defer defMute(env)() 235 | klass, err := env.GetClassStr(NativeClass) 236 | fatalIf(t, err != nil, "Native threw an exception", err) 237 | fatalIf(t, klass == nil, "Native klass is nil!") 238 | hit := float64(1234) 239 | err = env.RegisterNative(klass, "NativeDouble", func(E *Environment, O *Object) float64 { 240 | return hit 241 | }) 242 | fatalIf(t, err != nil, "RegisterNative threw an exception", err) 243 | obj, err := env.NewInstanceStr(NativeClass) 244 | fatalIf(t, err != nil, "Couldn't instantiate NativeClass: %v", err) 245 | defer env.DeleteLocalRef(obj) 246 | fatalIf(t, obj == nil, "Instantiated NativeClass is nil") 247 | ok, err := obj.CallDouble(env, false, "NativeDouble") 248 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeDouble(): %v", err) 249 | fatalIf(t, ok != hit, "NativeDouble got wrong value: %d", ok) 250 | hit = float64(-125 / 7) 251 | ok, err = obj.CallDouble(env, false, "NativeDouble") 252 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeDouble(): %v", err) 253 | fatalIf(t, ok != hit, "NativeDouble got wrong value: %d", ok) 254 | } 255 | 256 | func TestJVMNativeStringClass(t *testing.T) { 257 | env := setupJVM(t) 258 | //defer defMute(env)() 259 | klass, err := env.GetClassStr(NativeClass) 260 | fatalIf(t, err != nil, "Native threw an exception", err) 261 | fatalIf(t, klass == nil, "Native klass is nil!") 262 | s := "test-string" 263 | err = env.RegisterNative(klass, "NativeString", func(E *Environment, O *Object) string { 264 | return s 265 | }) 266 | fatalIf(t, err != nil, "RegisterNative threw an exception", err) 267 | obj, err := env.NewInstanceStr(NativeClass) 268 | fatalIf(t, err != nil, "Couldn't instantiate NativeClass: %v", err) 269 | fatalIf(t, obj == nil, "Instantiated NativeClass is nil") 270 | ok, _, err := obj.CallString(env, false, "NativeString") 271 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeString(): %v", err) 272 | fatalIf(t, ok != s, "NativeString got wrong value: %d", ok) 273 | s = "testStr2" 274 | ok, _, err = obj.CallString(env, false, "NativeString") 275 | fatalIf(t, err != nil, "Couldn't call NativeClass.NativeString(): %v", err) 276 | fatalIf(t, ok != s, "NativeString got wrong value: %d", ok) 277 | } 278 | 279 | func BenchmarkJVMNativePing(b *testing.B) { 280 | env := setupJVM(nil) 281 | //defer defMute(env)() 282 | // We mute expected exceptions because otherwise the test looks sloppy (and FAILS are hard to see) 283 | nativePings := 0 284 | klass, err := env.GetClassStr(NativeClass) 285 | if err != nil { 286 | print("benchmark failed: ", err.Error(), "\n") 287 | return 288 | } 289 | err = env.UnregisterNatives(klass) 290 | if err != nil { 291 | print("benchmark failed: ", err.Error(), "\n") 292 | return 293 | } 294 | err = env.RegisterNative(klass, "NativePing", func(E *Environment, O *Object) { 295 | nativePings += 1 296 | }) 297 | if err != nil { 298 | print("benchmark failed: ", err.Error(), "\n") 299 | return 300 | } 301 | obj, err := env.NewInstanceStr(NativeClass) 302 | if err != nil { 303 | print("benchmark failed: ", err.Error(), "\n") 304 | return 305 | } 306 | ll := int64(0) 307 | for i := 0; i < b.N; i++ { 308 | obj.CallVoid(env, false, "NativePing") 309 | ll += 1 310 | } 311 | env.DeleteLocalRef(obj) 312 | 313 | b.SetBytes(ll) 314 | } 315 | -------------------------------------------------------------------------------- /jvm_env_helpers.c: -------------------------------------------------------------------------------- 1 | #include "helpers.h" 2 | #include "_cgo_export.h" 3 | 4 | /* Exception handlers */ 5 | jboolean envExceptionCheck(JNIEnv *env) { 6 | return (*env)->ExceptionCheck(env); 7 | } 8 | 9 | jthrowable envExceptionOccurred(JNIEnv *env) { 10 | return (*env)->ExceptionOccurred(env); 11 | } 12 | 13 | void envExceptionDescribe(JNIEnv* env){ (*env)->ExceptionDescribe(env); } 14 | void envExceptionClear(JNIEnv* env) { (*env)->ExceptionClear(env); } 15 | 16 | 17 | /* 'Local' ref handlers */ 18 | jobject envNewLocalRef(JNIEnv *env, jobject obj) { return (*env)->NewLocalRef(env, obj); } 19 | 20 | void envDeleteLocalRef(JNIEnv *env, jobject obj) { (*env)->DeleteLocalRef(env, obj); } 21 | 22 | jclass envFindClass(JNIEnv *env, const char *string){ return (*env)->FindClass(env, string); } 23 | 24 | jmethodID envGetMethodID(JNIEnv *env, jobject jobj, const char *meth, const char *sig){ return (*env)->GetMethodID(env, jobj, meth, sig); } 25 | 26 | jmethodID envGetStaticMethodID(JNIEnv *env, jclass jobj, const char *meth, const char *sig){ return (*env)->GetStaticMethodID(env, jobj, meth, sig); } 27 | 28 | jclass envGetObjectClass(JNIEnv *env, jobject jobj){ return (*env)->GetObjectClass(env, jobj); } 29 | 30 | 31 | // Call[Static]XXXMethodA 32 | jint envCallIntMethodA(JNIEnv *env, jobject o, jmethodID m, void *val){ return (*env)->CallIntMethodA(env,o,m,val); } 33 | jint envCallStaticIntMethodA(JNIEnv *env, jclass o, jmethodID m, void *val){ return (*env)->CallStaticIntMethodA(env,o,m,val); } 34 | 35 | jshort envCallShortMethodA(JNIEnv *env, jobject o, jmethodID m, void *val){ return (*env)->CallShortMethodA(env,o,m,val); } 36 | jshort envCallStaticShortMethodA(JNIEnv *env, jclass o, jmethodID m, void *val){ return (*env)->CallStaticShortMethodA(env,o,m,val); } 37 | 38 | jlong envCallLongMethodA(JNIEnv *env, jobject o, jmethodID m, void *val){ return (*env)->CallLongMethodA(env,o,m,val); } 39 | jlong envCallStaticLongMethodA(JNIEnv *env, jclass o, jmethodID m, void *val){ return (*env)->CallStaticLongMethodA(env,o,m,val); } 40 | 41 | jobject envCallObjectMethodA(JNIEnv *env, jobject o, jmethodID m, void *val){ return (*env)->CallObjectMethodA(env,o,m,val); } 42 | jobject envCallStaticObjectMethodA(JNIEnv *env, jclass o, jmethodID m, void *val){ return (*env)->CallStaticObjectMethodA(env,o,m,val); } 43 | 44 | jfloat envCallFloatMethodA(JNIEnv *env, jobject o, jmethodID m, void *val){ return (*env)->CallFloatMethodA(env,o,m,val); } 45 | jfloat envCallStaticFloatMethodA(JNIEnv *env, jclass o, jmethodID m, void *val){ return (*env)->CallStaticFloatMethodA(env,o,m,val); } 46 | 47 | jdouble envCallDoubleMethodA(JNIEnv *env, jobject o, jmethodID m, void *val){ return (*env)->CallDoubleMethodA(env,o,m,val); } 48 | jdouble envCallStaticDoubleMethodA(JNIEnv *env, jclass o, jmethodID m, void *val){ return (*env)->CallStaticDoubleMethodA(env,o,m,val); } 49 | 50 | 51 | jboolean envCallBoolMethodA(JNIEnv *env, jclass o, jmethodID m, void *val){ return (*env)->CallBooleanMethodA(env,o,m,val); } 52 | jboolean envCallStaticBoolMethodA(JNIEnv *env, jclass o, jmethodID m, void *val){ return (*env)->CallStaticBooleanMethodA(env,o,m,val); } 53 | 54 | void envCallStaticVoidMethodA(JNIEnv *env, jclass o, jmethodID m, void *val){ (*env)->CallStaticVoidMethodA(env,o,m,val); } 55 | void envCallVoidMethodA(JNIEnv *env, jobject o, jmethodID m, void *val){ (*env)->CallVoidMethodA(env,o,m,val); } 56 | 57 | jint envGetArrayLength(JNIEnv *env, jobject o){ return (*env)->GetArrayLength(env,o); } 58 | 59 | jbyte *envGetByteArrayElements(JNIEnv *env, jobject o, jboolean *b){ 60 | return (*env)->GetByteArrayElements(env,o, b); 61 | } 62 | 63 | void envReleaseByteArrayElements(JNIEnv *env, jobject o, jbyte *bts, jint mode){ 64 | (*env)->ReleaseByteArrayElements(env,o, bts, mode); 65 | } 66 | 67 | jlong *envGetLongArrayElements(JNIEnv *env, jobject o, jboolean *b){ 68 | return (*env)->GetLongArrayElements(env, o, b); 69 | } 70 | 71 | void envReleaseLongArrayElements(JNIEnv *env, jobject o, jlong *native, jint mode){ 72 | (*env)->ReleaseLongArrayElements(env,o, native, mode); 73 | } 74 | 75 | jint *envGetIntArrayElements(JNIEnv *env, jobject o, jboolean *b){ 76 | return (*env)->GetIntArrayElements(env, o, b); 77 | } 78 | 79 | void envReleaseIntArrayElements(JNIEnv *env, jobject o, jint *native, jint mode){ 80 | (*env)->ReleaseIntArrayElements(env,o, native, mode); 81 | } 82 | 83 | 84 | jboolean envIsSameObject(JNIEnv *env, jobject o, jobject o2){ 85 | return (*env)->IsSameObject(env,o, o2); 86 | } 87 | 88 | jobject envNewGlobalRef(JNIEnv *env, jobject o){ 89 | return (*env)->NewGlobalRef(env,o); 90 | } 91 | 92 | jobject envNewObjectA(JNIEnv *env, jobject o, jmethodID meth, void *jv){ 93 | return (*env)->NewObjectA(env,o, meth, jv); 94 | } 95 | 96 | jobject envNewObjectALP(JNIEnv *env, jobject o, jmethodID meth, ArgListPtr args){ 97 | return (*env)->NewObjectA(env,o, meth, NULL); 98 | } 99 | 100 | jbyteArray envNewByteArray(JNIEnv *env, jsize len){ 101 | return (*env)->NewByteArray(env,len); 102 | } 103 | 104 | jobjectArray envNewObjectArray(JNIEnv *env, jsize len, jclass klass, jobject init){ 105 | return (*env)->NewObjectArray(env,len, klass, init); 106 | } 107 | 108 | void envSetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index, jobject val){ 109 | (*env)->SetObjectArrayElement(env, array, index, val); 110 | } 111 | 112 | jobject envGetObjectArrayElement(JNIEnv *env, jobjectArray array, jsize index) { 113 | return (*env)->GetObjectArrayElement(env, array, index); 114 | } 115 | 116 | jstring envNewStringUTF(JNIEnv *env, const char *s){ 117 | return (*env)->NewStringUTF(env, s); 118 | } 119 | 120 | jsize envGetStringUTFLength(JNIEnv *env, jstring s){ 121 | return (*env)->GetStringUTFLength(env, s); 122 | } 123 | 124 | const char *envGetStringUTFChars(JNIEnv *env, jstring s, jboolean *jb){ 125 | return (*env)->GetStringUTFChars(env, s, jb); 126 | } 127 | 128 | void envReleaseStringUTFChars(JNIEnv *env, jstring s, const char *jb){ 129 | (*env)->ReleaseStringUTFChars(env, s, jb); 130 | } 131 | 132 | void envSetByteArrayRegion(JNIEnv *env, jbyteArray array, jsize start, jsize len, const void *buf){ 133 | (*env)->SetByteArrayRegion(env, array, start, len, buf); 134 | } 135 | 136 | //fields 137 | jfieldID envGetStaticFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig) { 138 | return (*env)->GetStaticFieldID(env, clazz, name, sig); 139 | } 140 | 141 | jobject envGetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 142 | return (*env)->GetStaticObjectField(env, clazz, fieldID); 143 | } 144 | 145 | jboolean envGetStaticBooleanField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 146 | return (*env)->GetStaticBooleanField(env, clazz, fieldID); 147 | } 148 | 149 | jbyte envGetStaticByteField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 150 | return (*env)->GetStaticByteField(env, clazz, fieldID); 151 | } 152 | 153 | jshort envGetStaticShortField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 154 | return (*env)->GetStaticShortField(env, clazz, fieldID); 155 | } 156 | 157 | jint envGetStaticIntField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 158 | return (*env)->GetStaticIntField(env, clazz, fieldID); 159 | } 160 | 161 | jlong envGetStaticLongField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 162 | return (*env)->GetStaticLongField(env, clazz, fieldID); 163 | } 164 | 165 | jfloat envGetStaticFloatField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 166 | return (*env)->GetStaticFloatField(env, clazz, fieldID); 167 | } 168 | 169 | jdouble envGetStaticDoubleField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 170 | return (*env)->GetStaticDoubleField(env, clazz, fieldID); 171 | } 172 | 173 | 174 | //field 175 | 176 | jfieldID envGetFieldID(JNIEnv *env, jclass clazz, const char *name, const char *sig) { 177 | return (*env)->GetFieldID(env, clazz, name, sig); 178 | } 179 | 180 | jobject envGetObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 181 | return (*env)->GetObjectField(env, clazz, fieldID); 182 | } 183 | 184 | jboolean envGetBooleanField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 185 | return (*env)->GetBooleanField(env, clazz, fieldID); 186 | } 187 | 188 | jbyte envGetByteField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 189 | return (*env)->GetByteField(env, clazz, fieldID); 190 | } 191 | 192 | jshort envGetShortField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 193 | return (*env)->GetShortField(env, clazz, fieldID); 194 | } 195 | 196 | jint envGetIntField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 197 | return (*env)->GetIntField(env, clazz, fieldID); 198 | } 199 | 200 | jlong envGetLongField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 201 | return (*env)->GetLongField(env, clazz, fieldID); 202 | } 203 | 204 | jfloat envGetFloatField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 205 | return (*env)->GetFloatField(env, clazz, fieldID); 206 | } 207 | 208 | jdouble envGetDoubleField(JNIEnv *env, jclass clazz, jfieldID fieldID) { 209 | return (*env)->GetDoubleField(env, clazz, fieldID); 210 | } 211 | 212 | /* ---- */ 213 | 214 | void envSetObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject val) { 215 | return (*env)->SetObjectField(env, clazz, fieldID, val); 216 | } 217 | 218 | void envSetBooleanField(JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean val) { 219 | return (*env)->SetBooleanField(env, clazz, fieldID, val); 220 | } 221 | 222 | void envSetByteField(JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte val) { 223 | return (*env)->SetByteField(env, clazz, fieldID, val); 224 | } 225 | 226 | void envSetShortField(JNIEnv *env, jclass clazz, jfieldID fieldID, jshort val) { 227 | return (*env)->SetShortField(env, clazz, fieldID, val); 228 | } 229 | 230 | void envSetIntField(JNIEnv *env, jclass clazz, jfieldID fieldID, jint val) { 231 | return (*env)->SetIntField(env, clazz, fieldID, val); 232 | } 233 | 234 | void envSetLongField(JNIEnv *env, jclass clazz, jfieldID fieldID, jlong val) { 235 | return (*env)->SetLongField(env, clazz, fieldID, val); 236 | } 237 | 238 | void envSetFloatField(JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat val) { 239 | return (*env)->SetFloatField(env, clazz, fieldID, val); 240 | } 241 | 242 | void envSetDoubleField(JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble val) { 243 | return (*env)->SetDoubleField(env, clazz, fieldID, val); 244 | } 245 | 246 | void envSetStaticObjectField(JNIEnv *env, jclass clazz, jfieldID fieldID, jobject val) { 247 | return (*env)->SetStaticObjectField(env, clazz, fieldID, val); 248 | } 249 | 250 | void envSetStaticBooleanField(JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean val) { 251 | return (*env)->SetStaticBooleanField(env, clazz, fieldID, val); 252 | } 253 | 254 | void envSetStaticByteField(JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte val) { 255 | return (*env)->SetStaticByteField(env, clazz, fieldID, val); 256 | } 257 | 258 | void envSetStaticShortField(JNIEnv *env, jclass clazz, jfieldID fieldID, jshort val) { 259 | return (*env)->SetStaticShortField(env, clazz, fieldID, val); 260 | } 261 | 262 | void envSetStaticIntField(JNIEnv *env, jclass clazz, jfieldID fieldID, jint val) { 263 | return (*env)->SetStaticIntField(env, clazz, fieldID, val); 264 | } 265 | 266 | void envSetStaticLongField(JNIEnv *env, jclass clazz, jfieldID fieldID, jlong val) { 267 | return (*env)->SetStaticLongField(env, clazz, fieldID, val); 268 | } 269 | 270 | void envSetStaticFloatField(JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat val) { 271 | return (*env)->SetStaticFloatField(env, clazz, fieldID, val); 272 | } 273 | 274 | void envSetStaticDoubleField(JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble val) { 275 | return (*env)->SetStaticDoubleField(env, clazz, fieldID, val); 276 | } 277 | 278 | 279 | /* 280 | 281 | typedef struct { 282 | char *name; 283 | char *signature; 284 | void *fnPtr; 285 | } JNINativeMethod; 286 | 287 | */ 288 | // NB the actual call is registerNatives... TODO 289 | 290 | 291 | jint envGetJavaVM(JNIEnv *env,JavaVM **jvm){ 292 | return (*env)->GetJavaVM(env, jvm); 293 | } 294 | 295 | 296 | void *ret(GoInterface _if){ return _if.v; } 297 | 298 | /*void *genericCallback0(JNIEnv *env, jobject obj, void *argp){ return ret(goCallback((uintptr)env, (uintptr) obj, 0, (uintptr) argp)); } 299 | void *genericCallback1(JNIEnv *env, jobject obj, void *argp){ return ret(goCallback((uintptr)env, (uintptr) obj, 1, (uintptr) argp)); } 300 | void *genericCallback2(JNIEnv *env, jobject obj, void *argp){ return ret(goCallback((uintptr)env, (uintptr) obj, 2, (uintptr) argp)); } 301 | void *genericCallback3(JNIEnv *env, jobject obj, void *argp){ return ret(goCallback((uintptr)env, (uintptr) obj, 3, (uintptr) argp)); } 302 | void *genericCallback4(JNIEnv *env, jobject obj, void *argp){ return ret(goCallback((uintptr)env, (uintptr) obj, 4, (uintptr) argp)); } 303 | void *genericCallback5(JNIEnv *env, jobject obj, void *argp){ return ret(goCallback((uintptr)env, (uintptr) obj, 5, (uintptr) argp)); } 304 | void *genericCallback6(JNIEnv *env, jobject obj, void *argp){ return ret(goCallback((uintptr)env, (uintptr) obj, 6, (uintptr) argp)); } 305 | void *genericCallback7(JNIEnv *env, jobject obj, void *argp){ return ret(goCallback((uintptr)env, (uintptr) obj, 7, (uintptr) argp)); } 306 | */ 307 | 308 | /* 309 | 310 | typedef struct { 311 | int length; 312 | jvalue *values; 313 | } ArgList, *ArgListPtr; 314 | */ 315 | 316 | ArgListPtr newArgList(int sz){ 317 | ArgListPtr ptr = malloc(sizeof(ArgList)); 318 | if (ptr == NULL) { return NULL; } 319 | ptr->length = sz; 320 | ptr->values = (jvalue *)malloc(sizeof(jvalue) * sz); 321 | if (ptr->values == NULL ){ 322 | free(ptr); 323 | return NULL; 324 | } 325 | return ptr; 326 | } 327 | 328 | jvalue getArg(ArgListPtr l, int p){ 329 | jvalue zeroval; 330 | if (p <= l->length){ 331 | return l->values[p]; 332 | } 333 | // else panic 334 | return zeroval; 335 | } 336 | 337 | void delArgList(ArgListPtr alp){ 338 | free(alp->values); 339 | free(alp); 340 | } 341 | 342 | void *doReturn(JNIEnv *env, struct goCallback_return ret){ 343 | if (ret.r0) { 344 | return ret.r1.v; 345 | } 346 | printf("Failed, need to throw\n"); 347 | return; 348 | } 349 | 350 | jint envRegisterNative(JNIEnv *env, jclass klass, char *funcName, char *signature, void* fnPtr ){ 351 | JNINativeMethod native; 352 | 353 | native.name = funcName; 354 | native.signature = signature; 355 | native.fnPtr = fnPtr; 356 | return (*env)->RegisterNatives(env, klass, &native, 1); 357 | } 358 | 359 | jint envUnregisterNatives(JNIEnv *env, jclass klass){ 360 | return (*env)->UnregisterNatives(env, klass); 361 | } 362 | 363 | 364 | -------------------------------------------------------------------------------- /environ.c.go: -------------------------------------------------------------------------------- 1 | package gojvm 2 | 3 | //#cgo CFLAGS:-I../include/ 4 | //#cgo LDFLAGS:-ljvm -L/usr/lib/jvm/default-java/jre/lib/amd64/server 5 | //#include "helpers.h" 6 | import "C" 7 | import ( 8 | "errors" 9 | "github.com/timob/gojvm/types" 10 | "unsafe" 11 | "log" 12 | ) 13 | 14 | const ( 15 | JAVAClass = iota 16 | JAVAObject 17 | ) 18 | 19 | /* 20 | 21 | An environment consists of a pointer to a JNI environment 22 | and a caching map of class names to (globally referenced) class objects. 23 | 24 | TODO: Handle references on other items (nominally) correctly. 25 | 26 | 27 | */ 28 | type Environment struct { 29 | env *C.JNIEnv 30 | jvm *JVM 31 | classes map[string]*Class 32 | quietExceptions bool 33 | // various 'consts' 34 | _UTF8 C.jstring // "UTF8" parameter 35 | } 36 | 37 | // Returns the underlying JNIEnv pointer. 38 | // (In practice you should not need this ) 39 | func (self *Environment) Ptr() unsafe.Pointer { 40 | return unsafe.Pointer(&self.env) 41 | } 42 | 43 | func (self *Environment) getObjectMethod(obj *Object, static bool, mname string, rType types.Typed, params ...interface{}) (meth *Method, args argList, objList []*Object, err error) { 44 | meth, err = self._objMethod(obj, mname, rType, params...) 45 | if err != nil { 46 | return 47 | } 48 | args, objList, err = newArgList(self, params...) 49 | return 50 | } 51 | 52 | func (self *Environment) getMethod(t interface{}, static bool, mname string, rType types.Typed, params ...interface{}) (jval C.jvalue, meth *Method, args argList, objList []*Object, err error) { 53 | if debug { 54 | log.Printf("getmethod: %s - %v", mname, t) 55 | } 56 | switch v := t.(type) { 57 | case *Object: 58 | //print("getObjMethod\t",mname, "\t",rType.TypeString(),"\n") 59 | jval = C.objValue(v.object) 60 | meth, args, objList, err = self.getObjectMethod(v, static, mname, rType, params...) 61 | case *Class: 62 | // print("getClassMethod\t",mname, "\t",rType.TypeString(),"\n") 63 | jval = C.objValue(v.class) 64 | meth, args, objList, err = self.getClassMethod(v, static, mname, rType, params...) 65 | default: 66 | panic("getMethod called on unknown type") 67 | } 68 | return 69 | } 70 | 71 | // used in testing; a 'squelch' helper 72 | // such that: 73 | // func X(){ 74 | // defer env.defMute()() /*note the double parens!!!*/ 75 | // doSomeJavaCall 76 | // } 77 | // 78 | // would not output an exception to the console during processing 79 | // regardless othe explicit 'mutedness'. 80 | // there is a race condition here, but you're not supposed 81 | // to be using *Environment in multiple threads anyhow :P 82 | func (self *Environment) defMute() func() { 83 | muted := self.Muted() 84 | self.Mute(true) 85 | return func() { 86 | self.Mute(muted) 87 | } 88 | } 89 | 90 | func (self *Environment) getClassMethod(c *Class, static bool, mname string, rType types.Typed, params ...interface{}) (meth *Method, args argList, objList []*Object, err error) { 91 | if !static { 92 | meth, err = self._classMethod(c, mname, rType, params...) 93 | } else { 94 | meth, err = self._classStaticMethod(c, mname, rType, params...) 95 | } 96 | if err != nil { 97 | return 98 | } 99 | args, objList, err = newArgList(self, params...) 100 | return 101 | } 102 | 103 | // (Un)Suppress the java console barf of exceptions 104 | // (execeptions are still caught, cleared and returned) 105 | func (self *Environment) Mute(mute bool) { self.quietExceptions = mute } 106 | 107 | // Returns the current state of the environmental exception mute. 108 | func (self *Environment) Muted() bool { return self.quietExceptions } 109 | 110 | // Refcounting is probably needed here, TODO: figure that out... 111 | func (self *Environment) utf8() C.jstring { 112 | if self._UTF8 == nil { 113 | cs := C.CString("UTF8") 114 | defer C.free(unsafe.Pointer(cs)) 115 | tf8 := C.envNewStringUTF(self.env, cs) 116 | self._UTF8 = C.jstring(C.envNewGlobalRef(self.env, tf8)) 117 | } 118 | return self._UTF8 119 | } 120 | 121 | func NewEnvironment(jvm *JVM) *Environment { 122 | return &Environment{ 123 | env: new(C.JNIEnv), 124 | classes: map[string]*Class{}, 125 | jvm: jvm, 126 | quietExceptions: true, 127 | } 128 | } 129 | 130 | func (self Class) Kind() types.Kind { return types.ClassKind } 131 | 132 | /* represents JNI method call; without subject, style & parameters, 133 | it is useless. It (appears) to be an error to ref/unref methods. 134 | */ 135 | type Method struct { 136 | method C.jmethodID 137 | } 138 | 139 | func (self *Environment) findCachedClass(klass types.Name) (c *Class, err error) { 140 | if class, ok := self.classes[klass.AsPath()]; ok { 141 | c = class 142 | } else { 143 | err = errors.New("cache miss") 144 | } 145 | return 146 | } 147 | 148 | /* 149 | returns a new *Object of class 'java/lang/String', containing the (UTF16 reinterpreted) 150 | representation of 's'. Mostly a helper for passing strings into Java. 151 | */ 152 | func (self *Environment) NewStringObject(s string) (obj *Object, err error) { 153 | obj, err = self.NewInstanceStr("java/lang/String", []byte(s), self.utf8()) 154 | return 155 | } 156 | 157 | func (self *Environment) setObjectArrayElement(arr *Object, pos int, item *Object) (err error) { 158 | C.envSetObjectArrayElement(self.env, arr.object, C.jsize(pos), item.object) 159 | return 160 | } 161 | 162 | func (self *Environment) newObjectArray(sz int, klass *Class, init C.jobject) (o *Object, err error) { 163 | ja := C.envNewObjectArray(self.env, C.jsize(sz), klass.class, init) 164 | if ja == nil { 165 | err = self.ExceptionOccurred() 166 | } 167 | if err == nil { 168 | o = newObject(C.jobject(ja)) 169 | } 170 | return 171 | } 172 | 173 | func (self *Environment) newByteObject(bts []byte) (o *Object, err error) { 174 | ja := C.envNewByteArray(self.env, C.jsize(len(bts))) 175 | if ja == nil { 176 | err = errors.New("Error allocating byte array") 177 | } 178 | if err == nil && len(bts) > 0 { 179 | bptr := make([]byte, len(bts)) 180 | copy(bptr, bts) 181 | //log.Printf("bptr: %s %p %p", bptr,bptr, &bptr[0] ) 182 | C.envSetByteArrayRegion(self.env, ja, 0, C.jsize(len(bptr)), unsafe.Pointer(&bptr[0])) 183 | } 184 | if err == nil { 185 | o = newObject(C.jobject(ja)) 186 | } 187 | return 188 | } 189 | 190 | /* 191 | returns a new *Object of the class named by 'klass' (Wrapper around NewInstance(types.NewName(...))) 192 | */ 193 | func (self *Environment) NewInstanceStr(klass string, params ...interface{}) (obj *Object, err error) { 194 | class, err := self.GetClass(types.NewName(klass)) 195 | if err != nil { 196 | return 197 | } 198 | return self.NewInstance(class, params...) 199 | } 200 | 201 | /* 202 | returns a new *Object of type *Class, using the constructor identified by []params 203 | */ 204 | func (self *Environment) NewInstance(c *Class, params ...interface{}) (o *Object, err error) { 205 | meth, alp, localStack, err := self.getClassMethod(c, false, "", types.Basic(types.VoidKind), params...) 206 | // meth, alp, err := self.getObjectMethod(newObject(self, c, C.jobject( c.class)), "", BasicType(JavaVoidKind), params...) 207 | if err != nil { 208 | return 209 | } 210 | defer blowStack(self, localStack) 211 | obj := C.envNewObjectA(self.env, c.class, meth.method, alp.Ptr()) 212 | if obj != nil { 213 | obj = C.envNewGlobalRef(self.env, obj) 214 | o = newObject(obj) 215 | } else { 216 | err = self.ExceptionOccurred() 217 | } 218 | return 219 | } 220 | 221 | // returns a Class object; the object will first be looked up in cache, 222 | // and if not found there, resolved via Java and stored in the cache path. 223 | // classes returned via /THIS/ channel, need not be unrefed, as they all 224 | // hold a global ref. 225 | // 226 | // TODO: in truth, they should probably ALL be local-refs of the cached one... 227 | func (self *Environment) GetClass(klass types.Name) (c *Class, err error) { 228 | c, err = self.findCachedClass(klass) 229 | if err == nil { 230 | return 231 | } 232 | s := C.CString(klass.AsPath()) 233 | defer C.free(unsafe.Pointer(s)) 234 | // print("envFindClass ", klass,"\n") 235 | kl := C.envFindClass(self.env, s) 236 | if kl == nil { 237 | //print("GetClass missed ", klass.AsPath(), "\n\n") 238 | err = self.ExceptionOccurred() 239 | } else { 240 | err = nil // clear the cache error 241 | //print("found ", klass,"\n") 242 | kl = C.jclass(C.envNewGlobalRef(self.env, kl)) 243 | c = newClass(kl) 244 | self.classes[klass.AsPath()] = c 245 | } 246 | return 247 | } 248 | 249 | // Wrapper around GetClass(types.NewName(...)) 250 | func (self *Environment) GetClassStr(klass string) (c *Class, err error) { 251 | class := types.NewName(klass) 252 | return self.GetClass(class) 253 | } 254 | 255 | func (self *Environment) GetObjectClass(o *Object) (c *Class, err error) { 256 | kl := C.envGetObjectClass(self.env, o.object) 257 | if kl == nil { 258 | err = self.ExceptionOccurred() 259 | } else { 260 | c = newClass(kl) 261 | } 262 | return 263 | } 264 | 265 | func (self *Environment) _objMethod(obj *Object, name string, jt types.Typed, params ...interface{}) (meth *Method, err error) { 266 | class, err := self.GetObjectClass(obj) 267 | defer self.DeleteLocalClassRef(class) 268 | if err != nil { 269 | return 270 | } 271 | form, err := FormFor(self, jt, params...) 272 | if err != nil { 273 | return 274 | } 275 | 276 | cmethod := C.CString(name) 277 | defer C.free(unsafe.Pointer(cmethod)) 278 | cform := C.CString(form) 279 | defer C.free(unsafe.Pointer(cform)) 280 | 281 | if debug { 282 | log.Printf("_objMethod %V %V", name, form) 283 | } 284 | m := C.envGetMethodID(self.env, class.class, cmethod, cform) 285 | if m == nil { 286 | err = self.ExceptionOccurred() 287 | } else { 288 | meth = &Method{m} 289 | } 290 | return 291 | 292 | } 293 | 294 | func (self *Environment) _classMethod(class *Class, name string, jt types.Typed, params ...interface{}) (meth *Method, err error) { 295 | form, err := FormFor(self, jt, params...) 296 | if err != nil { 297 | return 298 | } 299 | cmethod := C.CString(name) 300 | defer C.free(unsafe.Pointer(cmethod)) 301 | cform := C.CString(form) 302 | defer C.free(unsafe.Pointer(cform)) 303 | //cname, err := class.Name() 304 | //if err != nil { return } 305 | if debug { 306 | log.Printf("_classMethod %V %V", name, form) 307 | } 308 | m := C.envGetMethodID(self.env, class.class, cmethod, cform) 309 | if m == nil { 310 | err = self.ExceptionOccurred() 311 | } else { 312 | meth = &Method{m} 313 | } 314 | return 315 | } 316 | 317 | func (self *Environment) _classStaticMethod(class *Class, name string, jt types.Typed, params ...interface{}) (meth *Method, err error) { 318 | form, err := FormFor(self, jt, params...) 319 | if err != nil { 320 | return 321 | } 322 | cmethod := C.CString(name) 323 | defer C.free(unsafe.Pointer(cmethod)) 324 | cform := C.CString(form) 325 | defer C.free(unsafe.Pointer(cform)) 326 | //cname, err := class.Name() 327 | //if err != nil { return } 328 | //print("Looking for (static)", name, "\t", form, "\t in ", cname.AsPath(), "\n") 329 | m := C.envGetStaticMethodID(self.env, class.class, cmethod, cform) 330 | if m == nil { 331 | err = self.ExceptionOccurred() 332 | } else { 333 | meth = &Method{m} 334 | } 335 | return 336 | } 337 | 338 | type Exception struct { 339 | env *Environment 340 | ex C.jthrowable 341 | } 342 | 343 | func (self *Exception) Error() string { 344 | obj := newObject(C.jobject(self.ex)) 345 | str, _, err := obj.CallString(self.env, false, "toString") 346 | if err != nil { 347 | panic(err) 348 | } 349 | return str 350 | } 351 | 352 | /* 353 | JNI documentation is unclear on the semantics of calling this 354 | when an exception has NOT occurred (e.g., is not indicated by 355 | a NULL value), but logic dictates that it _should_ be safe 356 | to call; In that event, nil (should) be returned. 357 | */ 358 | func (self *Environment) ExceptionOccurred() (ex *Exception) { 359 | throwable := C.envExceptionOccurred(self.env) 360 | if throwable != nil { 361 | // TODO: We'll need to do a global reference to this 362 | // if it outlasts a callback... 363 | ex = &Exception{self, throwable} 364 | if !self.quietExceptions { 365 | C.envExceptionDescribe(self.env) 366 | } 367 | C.envExceptionClear(self.env) 368 | } 369 | return 370 | } 371 | 372 | // Returns true if an ExceptionOccurred in this thread 373 | // should produce a non-nil *Exception 374 | func (self *Environment) ExceptionCheck() bool { 375 | return (C.envExceptionCheck(self.env) != C.JNI_FALSE) 376 | } 377 | 378 | // Syntactic sugar around &Class{C.jclass(LocalRef(&Object{C.jobject(class.class)}))} 379 | func (self *Environment) NewLocalClassRef(c *Class) *Class { 380 | return newClass(C.jclass(C.envNewLocalRef(self.env, c.class))) 381 | } 382 | 383 | // Syntactic sugar around LocalUnref(&Object{C.jobject(class.class)}) 384 | func (self *Environment) DeleteLocalClassRef(c *Class) { 385 | C.envDeleteLocalRef(self.env, c.class) 386 | } 387 | 388 | // Adds a 'local' ref to the JVM for Object, and returns an object that is contains reference 389 | func (self *Environment) NewLocalRef(o *Object) *Object { 390 | return newObject(C.envNewLocalRef(self.env, o.object)) 391 | } 392 | 393 | // Release a local reference (returned from LocalRef) back to the JVM 394 | func (self *Environment) DeleteLocalRef(o *Object) { 395 | C.envDeleteLocalRef(self.env, o.object) 396 | } 397 | 398 | // As gojvm is typically the /hosting/ context, 399 | // a global reference in gojvm is more of a 'dont bother GC'ing this, 400 | // I'm going to lose it somewhere in my stack', 401 | // and as such should be use sparingly 402 | func (self *Environment) NewGlobalRef(o *Object) *Object { 403 | return newObject(C.envNewGlobalRef(self.env, o.object)) 404 | } 405 | 406 | func (self *Environment) RegisterNative(className string, method string, sig types.MethodSignature, fptr interface{}) error { 407 | class, err := self.GetClass(types.NewName(className)) 408 | if err != nil { 409 | return err 410 | } 411 | cname := C.CString(method) 412 | defer C.free(unsafe.Pointer(cname)) 413 | 414 | csig := C.CString(sig.String()) 415 | defer C.free(unsafe.Pointer(csig)) 416 | C.envRegisterNative(self.env, class.class, cname, csig, fptr.(unsafe.Pointer)) 417 | 418 | return nil 419 | } 420 | 421 | /* 422 | func (self *Environment) UnregisterNatives(c *Class) (err error) { 423 | if 0 != C.envUnregisterNatives(self.env, c.class) { 424 | err = self.ExceptionOccurred() 425 | } 426 | return 427 | } 428 | 429 | func (self *Environment) RegisterNative(c *Class, name string, fptr interface{}) (err error) { 430 | // env.RegisterNative(klass, "NativePing", func(E *environment.Environment, O *environment.Object)(Error){ 431 | // nativePings += 1 432 | // }) 433 | 434 | cname := C.CString(name) 435 | defer C.free(unsafe.Pointer(cname)) 436 | id, sig, err := self.jvm.addNative(self, fptr) 437 | csig := C.CString(sig.String()) 438 | defer C.free(unsafe.Pointer(csig)) 439 | if err != nil { 440 | return 441 | } 442 | 443 | if 0 != C.envRegisterNative(self.env, c.class, cname, csig, C.int(id)) { 444 | err = self.ExceptionOccurred() 445 | } 446 | return 447 | } 448 | */ 449 | 450 | 451 | /* CallObject methods */ 452 | func asBool(jb C.jboolean) bool { 453 | if jb == C.JNI_FALSE { 454 | return false 455 | } 456 | return true 457 | } 458 | 459 | func (self *Environment) CallObjectVoid(obj *Object, static bool, name string, params ...interface{}) (err error) { 460 | return self.callVoid(obj, static, name, params...) 461 | } 462 | 463 | func (self *Environment) CallClassVoid(obj *Class, static bool, name string, params ...interface{}) (err error) { 464 | return self.callVoid(obj, static, name, params...) 465 | } 466 | 467 | func (self *Environment) CallObjectInt(obj *Object, static bool, name string, params ...interface{}) (v int, err error) { 468 | return self.callInt(obj, static, name, params...) 469 | } 470 | 471 | func (self *Environment) CallClassInt(obj *Class, static bool, name string, params ...interface{}) (v int, err error) { 472 | return self.callInt(obj, static, name, params...) 473 | } 474 | 475 | func (self *Environment) CallObjectLong(obj *Object, static bool, name string, params ...interface{}) (v int64, err error) { 476 | return self.callLong(obj, static, name, params...) 477 | } 478 | 479 | func (self *Environment) CallClassLong(obj *Class, static bool, name string, params ...interface{}) (v int64, err error) { 480 | return self.callLong(obj, static, name, params...) 481 | } 482 | 483 | func (self *Environment) CallObjectShort(obj *Object, static bool, name string, params ...interface{}) (v int16, err error) { 484 | return self.callShort(obj, static, name, params...) 485 | } 486 | 487 | func (self *Environment) CallClassShort(obj *Class, static bool, name string, params ...interface{}) (v int16, err error) { 488 | return self.callShort(obj, static, name, params...) 489 | } 490 | 491 | func (self *Environment) CallObjectBool(obj *Object, static bool, name string, params ...interface{}) (v bool, err error) { 492 | return self.callBool(obj, static, name, params...) 493 | } 494 | 495 | func (self *Environment) CallClassBool(obj *Class, static bool, name string, params ...interface{}) (v bool, err error) { 496 | return self.callBool(obj, static, name, params...) 497 | } 498 | 499 | func (self *Environment) CallObjectFloat(obj *Object, static bool, name string, params ...interface{}) (v float32, err error) { 500 | return self.callFloat(obj, static, name, params...) 501 | } 502 | 503 | func (self *Environment) CallClassFloat(obj *Class, static bool, name string, params ...interface{}) (v float32, err error) { 504 | return self.callFloat(obj, static, name, params...) 505 | } 506 | 507 | func (self *Environment) CallObjectDouble(obj *Object, static bool, name string, params ...interface{}) (v float64, err error) { 508 | return self.callDouble(obj, static, name, params...) 509 | } 510 | 511 | func (self *Environment) CallClassDouble(obj *Class, static bool, name string, params ...interface{}) (v float64, err error) { 512 | return self.callDouble(obj, static, name, params...) 513 | } 514 | 515 | func (self *Environment) CallObjectLongArray(obj *Object, static bool, name string, params ...interface{}) (v []int64, err error) { 516 | return self.callLongArray(obj, static, name, params...) 517 | } 518 | 519 | func (self *Environment) CallClassLongArray(obj *Class, static bool, name string, params ...interface{}) (v []int64, err error) { 520 | return self.callLongArray(obj, static, name, params...) 521 | } 522 | 523 | func (self *Environment) CallObjectIntArray(obj *Object, static bool, name string, params ...interface{}) (v []int, err error) { 524 | return self.callIntArray(obj, static, name, params...) 525 | } 526 | 527 | func (self *Environment) CallClassIntArray(obj *Class, static bool, name string, params ...interface{}) (v []int, err error) { 528 | return self.callIntArray(obj, static, name, params...) 529 | } 530 | 531 | func (self *Environment) CallObjectObj(obj *Object, static bool, name string, rtype types.Typed, params ...interface{}) (v *Object, err error) { 532 | return self.callObj(obj, static, name, rtype, params...) 533 | } 534 | 535 | func (self *Environment) CallClassObj(obj *Class, static bool, name string, rtype types.Typed, params ...interface{}) (v *Object, err error) { 536 | return self.callObj(obj, static, name, rtype, params...) 537 | } 538 | 539 | func (self *Environment) CallObjectString(obj *Object, static bool, name string, params ...interface{}) (s string, isNull bool, err error) { 540 | strObj, err := self.callObj(obj, static, name, types.Class{types.JavaLangString}, params...) 541 | if err == nil { 542 | defer self.DeleteLocalRef(strObj) 543 | return self.ToString(strObj) 544 | } 545 | return 546 | } 547 | 548 | func (self *Environment) CallClassString(obj *Class, static bool, name string, params ...interface{}) (s string, isNull bool, err error) { 549 | strObj, err := self.callObj(obj, static, name, types.Class{types.JavaLangString}, params...) 550 | if err == nil { 551 | defer self.DeleteLocalRef(strObj) 552 | return self.ToString(strObj) 553 | } 554 | return 555 | } 556 | 557 | func (self *Environment) callBool(z interface{}, static bool, name string, params ...interface{}) (b bool, err error) { 558 | jval, meth, args, localStack, err := self.getMethod(z, static, name, types.Basic(types.BoolKind), params...) 559 | if err != nil { 560 | return 561 | } 562 | defer blowStack(self, localStack) 563 | var ji C.jboolean 564 | if static { 565 | ji = C.envCallStaticBoolMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 566 | } else { 567 | ji = C.envCallBoolMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 568 | } 569 | if self.ExceptionCheck() { 570 | err = self.ExceptionOccurred() 571 | } 572 | if err == nil { 573 | b = asBool(ji) 574 | } 575 | return 576 | } 577 | 578 | func (self *Environment) callVoid(z interface{}, static bool, name string, params ...interface{}) (err error) { 579 | jval, meth, args, localStack, err := self.getMethod(z, static, name, types.Basic(types.VoidKind), params...) 580 | if err != nil { 581 | return 582 | } 583 | defer blowStack(self, localStack) 584 | if static { 585 | C.envCallStaticVoidMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 586 | } else { 587 | C.envCallVoidMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 588 | } 589 | if self.ExceptionCheck() { 590 | err = self.ExceptionOccurred() 591 | } 592 | return 593 | } 594 | 595 | func (self *Environment) callInt(z interface{}, static bool, name string, params ...interface{}) (v int, err error) { 596 | jval, meth, args, localStack, err := self.getMethod(z, static, name, types.Basic(types.IntKind), params...) 597 | if err != nil { 598 | return 599 | } 600 | defer blowStack(self, localStack) 601 | var ji C.jint 602 | if static { 603 | ji = C.envCallStaticIntMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 604 | } else { 605 | ji = C.envCallIntMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 606 | } 607 | if self.ExceptionCheck() { 608 | err = self.ExceptionOccurred() 609 | } 610 | v = int(ji) 611 | return 612 | } 613 | 614 | func (self *Environment) callDouble(z interface{}, static bool, name string, params ...interface{}) (v float64, err error) { 615 | jval, meth, args, localStack, err := self.getMethod(z, static, name, types.Basic(types.DoubleKind), params...) 616 | if err != nil { 617 | return 618 | } 619 | defer blowStack(self, localStack) 620 | var ji C.jdouble 621 | if static { 622 | ji = C.envCallStaticDoubleMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 623 | } else { 624 | ji = C.envCallDoubleMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 625 | } 626 | if self.ExceptionCheck() { 627 | err = self.ExceptionOccurred() 628 | } 629 | v = float64(ji) 630 | return 631 | } 632 | 633 | func (self *Environment) callFloat(z interface{}, static bool, name string, params ...interface{}) (v float32, err error) { 634 | jval, meth, args, localStack, err := self.getMethod(z, static, name, types.Basic(types.FloatKind), params...) 635 | if err != nil { 636 | return 637 | } 638 | defer blowStack(self, localStack) 639 | var ji C.jfloat 640 | if static { 641 | ji = C.envCallStaticFloatMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 642 | } else { 643 | ji = C.envCallFloatMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 644 | } 645 | if self.ExceptionCheck() { 646 | err = self.ExceptionOccurred() 647 | } 648 | v = float32(ji) 649 | return 650 | } 651 | 652 | func (self *Environment) callObj(z interface{}, static bool, name string, rval types.Typed, params ...interface{}) (vObj *Object, err error) { 653 | jval, meth, args, localStack, err := self.getMethod(z, static, name, rval, params...) 654 | if err != nil { 655 | return 656 | } 657 | defer blowStack(self, localStack) 658 | var oval C.jobject 659 | if static { 660 | oval = C.envCallStaticObjectMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 661 | } else { 662 | oval = C.envCallObjectMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 663 | } 664 | if self.ExceptionCheck() { 665 | err = self.ExceptionOccurred() 666 | } 667 | if err == nil { 668 | vObj = newObject(oval) 669 | } 670 | return 671 | } 672 | 673 | func (self *Environment) callLong(z interface{}, static bool, name string, params ...interface{}) (v int64, err error) { 674 | jval, meth, args, localStack, err := self.getMethod(z, static, name, types.Basic(types.LongKind), params...) 675 | if err != nil { 676 | return 677 | } 678 | defer blowStack(self, localStack) 679 | var oval C.jlong 680 | if static { 681 | oval = C.envCallStaticLongMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 682 | } else { 683 | oval = C.envCallLongMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 684 | } 685 | if self.ExceptionCheck() { 686 | err = self.ExceptionOccurred() 687 | } 688 | if err == nil { 689 | v = int64(oval) 690 | } 691 | return 692 | } 693 | 694 | func (self *Environment) callShort(z interface{}, static bool, name string, params ...interface{}) (v int16, err error) { 695 | jval, meth, args, localStack, err := self.getMethod(z, static, name, types.Basic(types.ShortKind), params...) 696 | if err != nil { 697 | return 698 | } 699 | defer blowStack(self, localStack) 700 | var oval C.jshort 701 | if static { 702 | oval = C.envCallStaticShortMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 703 | } else { 704 | oval = C.envCallShortMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 705 | } 706 | if self.ExceptionCheck() { 707 | err = self.ExceptionOccurred() 708 | } 709 | if err == nil { 710 | v = int16(oval) 711 | } 712 | return 713 | } 714 | 715 | func (self *Environment) callLongArray(z interface{}, static bool, name string, params ...interface{}) (v []int64, err error) { 716 | jval, meth, args, localStack, err := self.getMethod(z, static, name, types.Array{types.Basic(types.LongKind)}, params...) 717 | if err != nil { 718 | return 719 | } 720 | defer blowStack(self, localStack) 721 | var oval C.jobject 722 | if static { 723 | oval = C.envCallStaticObjectMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 724 | } else { 725 | oval = C.envCallObjectMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 726 | } 727 | if oval == nil { 728 | err = self.ExceptionOccurred() 729 | } 730 | if err == nil { 731 | v = self.ToInt64Array(newObject(oval)) 732 | } 733 | return 734 | } 735 | 736 | func (self *Environment) callIntArray(z interface{}, static bool, name string, params ...interface{}) (v []int, err error) { 737 | jval, meth, args, localStack, err := self.getMethod(z, static, name, types.Array{types.Basic(types.IntKind)}, params...) 738 | if err != nil { 739 | return 740 | } 741 | defer blowStack(self, localStack) 742 | var oval C.jobject 743 | if static { 744 | oval = C.envCallStaticObjectMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 745 | } else { 746 | oval = C.envCallObjectMethodA(self.env, C.valObject(jval), meth.method, args.Ptr()) 747 | } 748 | if oval == nil { 749 | err = self.ExceptionOccurred() 750 | } 751 | if err == nil { 752 | v = self.ToIntArray(newObject(oval)) 753 | } 754 | return 755 | } 756 | 757 | func (self *Environment) ToString(strobj *Object) (str string, isNull bool, err error) { 758 | if strobj.object == nil { 759 | isNull = true 760 | return 761 | } 762 | var bytesObj *Object 763 | bytesObj, err = self.CallObjectObj(strobj, false, "getBytes", types.Array{types.Basic(types.ByteKind)}, self.utf8()) 764 | if err == nil && bytesObj == nil { 765 | isNull = true 766 | return 767 | } 768 | if err == nil { 769 | defer self.DeleteLocalRef(bytesObj) 770 | alen := C.envGetArrayLength(self.env, bytesObj.object) 771 | _false := C.jboolean(C.JNI_FALSE) 772 | ptr := C.envGetByteArrayElements(self.env, bytesObj.object, &_false) 773 | defer C.envReleaseByteArrayElements(self.env, bytesObj.object, ptr, 0) 774 | str = string(C.GoBytes(unsafe.Pointer(ptr), C.int(alen))) 775 | } 776 | return 777 | } 778 | 779 | func (self *Environment) ToInt64Array(arrayObj *Object) (array []int64) { 780 | alen := C.envGetArrayLength(self.env, arrayObj.object) 781 | _false := C.jboolean(C.JNI_FALSE) 782 | ptr := C.envGetLongArrayElements(self.env, arrayObj.object, &_false) 783 | defer C.envReleaseLongArrayElements(self.env, arrayObj.object, ptr, 0) 784 | bytes := C.GoBytes(unsafe.Pointer(ptr), C.int(alen) * 8) 785 | array = (*(*[1024*1024]int64)(unsafe.Pointer(&bytes)))[0:int(alen)] 786 | return 787 | } 788 | 789 | func (self *Environment) ToIntArray(arrayObj *Object) (array []int) { 790 | alen := C.envGetArrayLength(self.env, arrayObj.object) 791 | _false := C.jboolean(C.JNI_FALSE) 792 | ptr := C.envGetIntArrayElements(self.env, arrayObj.object, &_false) 793 | defer C.envReleaseIntArrayElements(self.env, arrayObj.object, ptr, 0) 794 | bytes := C.GoBytes(unsafe.Pointer(ptr), C.int(alen) * 4) 795 | array = (*(*[1024*1024]int)(unsafe.Pointer(&bytes)))[0:int(alen)] 796 | return 797 | } 798 | 799 | func (self *Environment) ToObjectArray(arrayObj *Object) []*Object { 800 | var glen int 801 | if arrayObj.object == nil { 802 | glen = 0 803 | } else { 804 | jlen := C.envGetArrayLength(self.env, arrayObj.object) 805 | glen = int(jlen) 806 | } 807 | objs := make([]*Object, glen) 808 | for i := 0; i < glen; i++ { 809 | objs[i] = &Object{C.envGetObjectArrayElement(self.env, arrayObj.object, C.jsize(i))} 810 | } 811 | return objs 812 | } 813 | 814 | //fields 815 | func (self *Environment) GetClassObjField(obj *Class, static bool, name string, rval types.Typed) (*Object, error) { 816 | return self.getObjField(obj, static, name, rval) 817 | } 818 | 819 | func (self *Environment) GetClassBooleanField(obj *Class, static bool, name string) (v bool, err error) { 820 | return self.getBoolField(obj, static, name) 821 | } 822 | 823 | func (self *Environment) GetClassShortField(obj *Class, static bool, name string) (v int16, err error) { 824 | return self.getShortField(obj, static, name) 825 | } 826 | 827 | func (self *Environment) GetClassIntField(obj *Class, static bool, name string) (v int, err error) { 828 | return self.getIntField(obj, static, name) 829 | } 830 | 831 | func (self *Environment) GetClassLongField(obj *Class, static bool, name string) (v int64, err error) { 832 | return self.getLongField(obj, static, name) 833 | } 834 | 835 | func (self *Environment) GetClassFloatField(obj *Class, static bool, name string) (v float32, err error) { 836 | return self.getFloatField(obj, static, name) 837 | } 838 | 839 | func (self *Environment) GetClassDoubleField(obj *Class, static bool, name string) (v float64, err error) { 840 | return self.getDobuleField(obj, static, name) 841 | } 842 | 843 | //object fields 844 | func (self *Environment) GetObjectObjField(obj *Object, static bool, name string, rval types.Typed) (*Object, error) { 845 | return self.getObjField(obj, static, name, rval) 846 | } 847 | 848 | func (self *Environment) GetObjectBooleanField(obj *Object, static bool, name string) (v bool, err error) { 849 | return self.getBoolField(obj, static, name) 850 | } 851 | 852 | func (self *Environment) GetObjectShortField(obj *Object, static bool, name string) (v int16, err error) { 853 | return self.getShortField(obj, static, name) 854 | } 855 | 856 | func (self *Environment) GetObjectIntField(obj *Object, static bool, name string) (v int, err error) { 857 | return self.getIntField(obj, static, name) 858 | } 859 | 860 | func (self *Environment) GetObjectLongField(obj *Object, static bool, name string) (v int64, err error) { 861 | return self.getLongField(obj, static, name) 862 | } 863 | 864 | func (self *Environment) GetObjectFloatField(obj *Object, static bool, name string) (v float32, err error) { 865 | return self.getFloatField(obj, static, name) 866 | } 867 | 868 | func (self *Environment) GetObjectDoubleField(obj *Object, static bool, name string) (v float64, err error) { 869 | return self.getDobuleField(obj, static, name) 870 | } 871 | 872 | func (self *Environment) GetObjecIntArrayField(obj *Object, static bool, name string) (v []int, err error) { 873 | return self.getIntArrayField(obj, static, name) 874 | } 875 | 876 | type Field struct { 877 | field C.jfieldID 878 | } 879 | 880 | func (self *Environment) getClassField(c *Class, static bool, mname string, rType types.Typed) (meth *Field, err error) { 881 | cmethod := C.CString(mname) 882 | defer C.free(unsafe.Pointer(cmethod)) 883 | cform := C.CString(rType.TypeString()) 884 | defer C.free(unsafe.Pointer(cform)) 885 | 886 | var m C.jfieldID 887 | if !static { 888 | //todo 889 | } else { 890 | m = C.envGetStaticFieldID(self.env, c.class, cmethod, cform) 891 | } 892 | if m == nil { 893 | err = self.ExceptionOccurred() 894 | return nil, err 895 | } 896 | 897 | return &Field{m}, nil 898 | } 899 | 900 | func (self *Environment) getObjectField(o *Object, static bool, mname string, rType types.Typed) (meth *Field, err error) { 901 | class, err := self.GetObjectClass(o) 902 | defer self.DeleteLocalClassRef(class) 903 | if err != nil { 904 | return 905 | } 906 | 907 | cmethod := C.CString(mname) 908 | defer C.free(unsafe.Pointer(cmethod)) 909 | cform := C.CString(rType.TypeString()) 910 | defer C.free(unsafe.Pointer(cform)) 911 | 912 | var m C.jfieldID 913 | if static { 914 | //todo 915 | } else { 916 | m = C.envGetFieldID(self.env, class.class, cmethod, cform) 917 | } 918 | if m == nil { 919 | err = self.ExceptionOccurred() 920 | return nil, err 921 | } 922 | 923 | return &Field{m}, nil 924 | } 925 | 926 | 927 | func (self *Environment) getField(t interface{}, static bool, mname string, rType types.Typed) (jval C.jvalue, field *Field, err error) { 928 | if debug { 929 | log.Printf("getfield: %s - %v", mname, t) 930 | } 931 | switch v := t.(type) { 932 | case *Object: 933 | jval = C.objValue(v.object) 934 | field, err = self.getObjectField(v, static, mname, rType) 935 | /* 936 | case *Object: 937 | //print("getObjMethod\t",mname, "\t",rType.TypeString(),"\n") 938 | jval = C.objValue(v.object) 939 | meth, args, objList, err = self.getObjectMethod(v, static, mname, rType, params...) 940 | */ 941 | case *Class: 942 | jval = C.objValue(v.class) 943 | field, err = self.getClassField(v, static, mname, rType) 944 | default: 945 | panic("getField called on unknown type") 946 | } 947 | 948 | return 949 | } 950 | 951 | func (self *Environment) getObjField(z interface{}, static bool, name string, rval types.Typed) (v *Object, err error) { 952 | jval, field, err := self.getField(z, static, name, rval) 953 | if err != nil { 954 | return 955 | } 956 | var oval C.jobject 957 | if static { 958 | oval = C.envGetStaticObjectField(self.env, C.valObject(jval), field.field); 959 | } else { 960 | oval = C.envGetObjectField(self.env, C.valObject(jval), field.field); 961 | } 962 | if self.ExceptionCheck() { 963 | err = self.ExceptionOccurred() 964 | } 965 | if err == nil { 966 | v = &Object{oval} 967 | } 968 | return 969 | } 970 | 971 | func (self *Environment) getBoolField(z interface{}, static bool, name string) (v bool, err error) { 972 | jval, field, err := self.getField(z, static, name, types.Basic(types.BoolKind)) 973 | if err != nil { 974 | return 975 | } 976 | var oval C.jboolean 977 | if static { 978 | oval = C.envGetStaticBooleanField(self.env, C.valObject(jval), field.field); 979 | } else { 980 | oval = C.envGetBooleanField(self.env, C.valObject(jval), field.field); 981 | } 982 | if self.ExceptionCheck() { 983 | err = self.ExceptionOccurred() 984 | } 985 | if err == nil { 986 | v = asBool(oval) 987 | } 988 | return 989 | } 990 | 991 | func (self *Environment) getShortField(z interface{}, static bool, name string) (v int16, err error) { 992 | jval, field, err := self.getField(z, static, name, types.Basic(types.ShortKind)) 993 | if err != nil { 994 | return 995 | } 996 | var oval C.jshort 997 | if static { 998 | oval = C.envGetStaticShortField(self.env, C.valObject(jval), field.field); 999 | } else { 1000 | oval = C.envGetShortField(self.env, C.valObject(jval), field.field); 1001 | } 1002 | if self.ExceptionCheck() { 1003 | err = self.ExceptionOccurred() 1004 | } 1005 | if err == nil { 1006 | v = int16(oval) 1007 | } 1008 | return 1009 | } 1010 | 1011 | func (self *Environment) getIntField(z interface{}, static bool, name string) (v int, err error) { 1012 | jval, field, err := self.getField(z, static, name, types.Basic(types.IntKind)) 1013 | if err != nil { 1014 | return 1015 | } 1016 | var oval C.jint 1017 | if static { 1018 | oval = C.envGetStaticIntField(self.env, C.valObject(jval), field.field); 1019 | } else { 1020 | oval = C.envGetIntField(self.env, C.valObject(jval), field.field); 1021 | } 1022 | if self.ExceptionCheck() { 1023 | err = self.ExceptionOccurred() 1024 | } 1025 | if err == nil { 1026 | v = int(oval) 1027 | } 1028 | return 1029 | } 1030 | 1031 | func (self *Environment) getLongField(z interface{}, static bool, name string) (v int64, err error) { 1032 | jval, field, err := self.getField(z, static, name, types.Basic(types.IntKind)) 1033 | if err != nil { 1034 | return 1035 | } 1036 | var oval C.jlong 1037 | if static { 1038 | oval = C.envGetStaticLongField(self.env, C.valObject(jval), field.field); 1039 | } else { 1040 | oval = C.envGetLongField(self.env, C.valObject(jval), field.field); 1041 | } 1042 | if self.ExceptionCheck() { 1043 | err = self.ExceptionOccurred() 1044 | } 1045 | if err == nil { 1046 | v = int64(oval) 1047 | } 1048 | return 1049 | } 1050 | 1051 | func (self *Environment) getFloatField(z interface{}, static bool, name string) (v float32, err error) { 1052 | jval, field, err := self.getField(z, static, name, types.Basic(types.IntKind)) 1053 | if err != nil { 1054 | return 1055 | } 1056 | var oval C.jfloat 1057 | if static { 1058 | oval = C.envGetStaticFloatField(self.env, C.valObject(jval), field.field); 1059 | } else { 1060 | oval = C.envGetFloatField(self.env, C.valObject(jval), field.field); 1061 | } 1062 | if self.ExceptionCheck() { 1063 | err = self.ExceptionOccurred() 1064 | } 1065 | if err == nil { 1066 | v = float32(oval) 1067 | } 1068 | return 1069 | } 1070 | 1071 | func (self *Environment) getDobuleField(z interface{}, static bool, name string) (v float64, err error) { 1072 | jval, field, err := self.getField(z, static, name, types.Basic(types.IntKind)) 1073 | if err != nil { 1074 | return 1075 | } 1076 | var oval C.jdouble 1077 | if static { 1078 | oval = C.envGetStaticDoubleField(self.env, C.valObject(jval), field.field); 1079 | } else { 1080 | oval = C.envGetDoubleField(self.env, C.valObject(jval), field.field); 1081 | } 1082 | if self.ExceptionCheck() { 1083 | err = self.ExceptionOccurred() 1084 | } 1085 | if err == nil { 1086 | v = float64(oval) 1087 | } 1088 | return 1089 | } 1090 | 1091 | func (self *Environment) getIntArrayField(z interface{}, static bool, name string) (v []int, err error) { 1092 | jval, field, err := self.getField(z, static, name, types.Array{types.Basic(types.IntKind)}) 1093 | if err != nil { 1094 | return 1095 | } 1096 | var oval C.jobject 1097 | if static { 1098 | oval = C.envGetStaticObjectField(self.env, C.valObject(jval), field.field) 1099 | } else { 1100 | oval = C.envGetObjectField(self.env, C.valObject(jval), field.field) 1101 | } 1102 | if oval == nil { 1103 | err = self.ExceptionOccurred() 1104 | } 1105 | if err == nil { 1106 | v = self.ToIntArray(newObject(oval)) 1107 | } 1108 | 1109 | return 1110 | } 1111 | 1112 | /* ==== */ 1113 | 1114 | func (self *Environment) setObjField(z interface{}, static bool, name string, rval types.Typed, val *Object) (err error) { 1115 | jval, field, err := self.getField(z, static, name, rval) 1116 | if err != nil { 1117 | return 1118 | } 1119 | if static { 1120 | C.envSetStaticObjectField(self.env, C.valObject(jval), field.field, val.object); 1121 | } else { 1122 | C.envSetObjectField(self.env, C.valObject(jval), field.field, val.object); 1123 | } 1124 | if self.ExceptionCheck() { 1125 | err = self.ExceptionOccurred() 1126 | } 1127 | return 1128 | } 1129 | 1130 | func (self *Environment) setBoolField(z interface{}, static bool, name string, gval bool) (err error) { 1131 | jval, field, err := self.getField(z, static, name, types.Basic(types.BoolKind)) 1132 | if err != nil { 1133 | return 1134 | } 1135 | val := C.jboolean(C.JNI_FALSE) 1136 | if gval { 1137 | val = C.JNI_TRUE 1138 | } 1139 | if static { 1140 | C.envSetStaticBooleanField(self.env, C.valObject(jval), field.field, val); 1141 | } else { 1142 | C.envSetBooleanField(self.env, C.valObject(jval), field.field, val); 1143 | } 1144 | if self.ExceptionCheck() { 1145 | err = self.ExceptionOccurred() 1146 | } 1147 | return 1148 | } 1149 | 1150 | func (self *Environment) setShortField(z interface{}, static bool, name string, val int16) (err error) { 1151 | jval, field, err := self.getField(z, static, name, types.Basic(types.ShortKind)) 1152 | if err != nil { 1153 | return 1154 | } 1155 | if static { 1156 | C.envSetStaticShortField(self.env, C.valObject(jval), field.field, C.jshort(val)); 1157 | } else { 1158 | C.envSetShortField(self.env, C.valObject(jval), field.field, C.jshort(val)); 1159 | } 1160 | if self.ExceptionCheck() { 1161 | err = self.ExceptionOccurred() 1162 | } 1163 | return 1164 | } 1165 | 1166 | func (self *Environment) setIntField(z interface{}, static bool, name string, val int) (err error) { 1167 | jval, field, err := self.getField(z, static, name, types.Basic(types.IntKind)) 1168 | if err != nil { 1169 | return 1170 | } 1171 | if static { 1172 | C.envSetStaticIntField(self.env, C.valObject(jval), field.field, C.jint(val)); 1173 | } else { 1174 | C.envSetIntField(self.env, C.valObject(jval), field.field, C.jint(val)); 1175 | } 1176 | if self.ExceptionCheck() { 1177 | err = self.ExceptionOccurred() 1178 | } 1179 | return 1180 | } 1181 | 1182 | func (self *Environment) setLongField(z interface{}, static bool, name string, val int64) (err error) { 1183 | jval, field, err := self.getField(z, static, name, types.Basic(types.IntKind)) 1184 | if err != nil { 1185 | return 1186 | } 1187 | if static { 1188 | C.envSetStaticLongField(self.env, C.valObject(jval), field.field, C.jlong(val)); 1189 | } else { 1190 | C.envSetLongField(self.env, C.valObject(jval), field.field, C.jlong(val)); 1191 | } 1192 | if self.ExceptionCheck() { 1193 | err = self.ExceptionOccurred() 1194 | } 1195 | return 1196 | } 1197 | 1198 | func (self *Environment) setFloatField(z interface{}, static bool, name string, val float32) (err error) { 1199 | jval, field, err := self.getField(z, static, name, types.Basic(types.IntKind)) 1200 | if err != nil { 1201 | return 1202 | } 1203 | if static { 1204 | C.envSetStaticFloatField(self.env, C.valObject(jval), field.field, C.jfloat(val)); 1205 | } else { 1206 | C.envSetFloatField(self.env, C.valObject(jval), field.field, C.jfloat(val)); 1207 | } 1208 | if self.ExceptionCheck() { 1209 | err = self.ExceptionOccurred() 1210 | } 1211 | return 1212 | } 1213 | 1214 | func (self *Environment) setDoubleField(z interface{}, static bool, name string, val float64) (err error) { 1215 | jval, field, err := self.getField(z, static, name, types.Basic(types.IntKind)) 1216 | if err != nil { 1217 | return 1218 | } 1219 | if static { 1220 | C.envSetStaticDoubleField(self.env, C.valObject(jval), field.field, C.jdouble(val)); 1221 | } else { 1222 | C.envSetDoubleField(self.env, C.valObject(jval), field.field, C.jdouble(val)); 1223 | } 1224 | if self.ExceptionCheck() { 1225 | err = self.ExceptionOccurred() 1226 | } 1227 | return 1228 | } 1229 | --------------------------------------------------------------------------------