github.com/luchsh/agentlib.go@v0.0.0-20221115155834-ffd0caec4d72/jgo/jvm.go (about)

     1  // Copyright 2020 chuanshenglu@gmail.com
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  // http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package jgo
    16  
    17  // #include <jni.h>
    18  // #include <stdlib.h>
    19  import "C"
    20  
    21  import (
    22  	"fmt"
    23  	"runtime"
    24  	"unsafe"
    25  
    26  	"github.com/ClarkGuan/jni"
    27  )
    28  
    29  const (
    30  	ptrSize = 8
    31  )
    32  
    33  type JniEnv jni.Env
    34  type JVM jni.VM
    35  
    36  func (vm JVM) raw() jni.VM {
    37  	return jni.VM(vm)
    38  }
    39  
    40  func (vm JVM) VM() jni.VM {
    41  	return vm.raw()
    42  }
    43  
    44  func (e JniEnv) raw() jni.Env {
    45  	return jni.Env(e)
    46  }
    47  
    48  func (e JniEnv) Env() jni.Env {
    49  	return jni.Env(e)
    50  }
    51  
    52  func jniGetCreatedJavaVMs() (vms []jni.VM) {
    53  	l := 128
    54  	buf := C.malloc(C.size_t(l * ptrSize))
    55  	defer C.free(buf)
    56  	var n C.jsize
    57  	if 0 == C.JNI_GetCreatedJavaVMs((**C.JavaVM)(buf), C.jsize(l), &n) {
    58  		for i := C.jsize(0); i < n; i++ {
    59  			p := uintptr(buf) + uintptr(i)*ptrSize
    60  			addr := *(*uintptr)(unsafe.Pointer(p))
    61  			vms = append(vms, jni.VM(addr))
    62  		}
    63  	}
    64  	return
    65  }
    66  
    67  // Create a Java VM
    68  func jniCreateJavaVM(args []string) (vmp jni.VM, envp jni.Env) {
    69  	vmargs := C.malloc(C.sizeof_JavaVMOption)
    70  	defer C.free(vmargs)
    71  	jva := (*C.JavaVMInitArgs)(vmargs)
    72  	jva.version = jni.JNI_VERSION_1_6
    73  	jva.nOptions = 0
    74  	jva.ignoreUnrecognized = jni.JNI_TRUE
    75  	if len(args) > 0 {
    76  		opts := C.malloc(C.size_t(C.sizeof_JavaVMOption * len(args)))
    77  		defer C.free(opts)
    78  		jva.nOptions = C.jint(len(args))
    79  		jva.options = (*C.JavaVMOption)(opts)
    80  		for i, a := range args {
    81  			o := (*C.JavaVMOption)(unsafe.Pointer(uintptr(opts) + uintptr(i)*C.sizeof_JavaVMOption))
    82  			o.optionString = C.CString(a)
    83  		}
    84  	}
    85  	vmargs = unsafe.Pointer(jva)
    86  
    87  	e := C.JNI_CreateJavaVM((**C.JavaVM)(unsafe.Pointer(&vmp)), (*unsafe.Pointer)(unsafe.Pointer(&envp)), vmargs)
    88  	if e != jni.JNI_OK {
    89  		fmt.Printf("Failed to create Java VM, error=%d (%s)\n", e, describeJNIError(int(e)))
    90  		return 0, 0
    91  	}
    92  
    93  	return vmp, envp
    94  }
    95  
    96  // Lock the target OS thread to prevent goroutine scheduling
    97  func (vm JVM) jniRun(f func(JniEnv)) {
    98  	if f != nil {
    99  		env, res := vm.raw().AttachCurrentThread()
   100  		if res != 0 {
   101  			panic("Failed to attach current thread")
   102  		}
   103  		runtime.LockOSThread()
   104  		defer runtime.UnlockOSThread()
   105  		f(JniEnv(env))
   106  	}
   107  }
   108  
   109  var jniErrorTextMap = map[int]string{
   110  	jni.JNI_ERR:       "JNI_ERR",
   111  	jni.JNI_EDETACHED: "JNI_EDETACHED",
   112  	jni.JNI_EVERSION:  "JNI_EVERSION",
   113  	jni.JNI_ENOMEM:    "JNI_ENOMEM",
   114  	jni.JNI_EEXIST:    "JNI_EEXIST",
   115  	jni.JNI_EINVAL:    "JNI_EINVAL",
   116  }
   117  
   118  func describeJNIError(ev int) string {
   119  	if tv, ok := jniErrorTextMap[ev]; ok {
   120  		return tv
   121  	}
   122  	return "Unknown error"
   123  }