├── .github └── workflows │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── jnim.nim ├── jnim.nimble ├── jnim ├── java │ ├── lang.nim │ └── util.nim ├── private │ ├── experimental │ │ └── javap_parser.nim │ ├── java_glue.nim │ ├── jni_api.nim │ ├── jni_export.nim │ ├── jni_export_old.nim │ ├── jni_generator.nim │ ├── jni_wrapper.nim │ └── jvm_finder.nim └── support │ └── io │ └── github │ └── vegansk │ └── jnim │ └── NativeInvocationHandler.java └── tests ├── common.nim ├── java ├── BaseClass.java ├── ChildClass.java ├── ConstructorTestClass.java ├── ExceptionTestClass.java ├── ExportTestClass.java ├── GenericsTestClass.java ├── InnerTestClass.java ├── MethodTestClass.java ├── PropsTestClass.java └── TestClass.java ├── test_all.nim ├── test_example.nim ├── test_java_lang.nim ├── test_java_util.nim ├── test_jni_api.nim ├── test_jni_export.nim ├── test_jni_export_old.nim ├── test_jni_generator.nim ├── test_jni_wrapper.nim └── test_jvm_finder.nim /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: push 4 | jobs: 5 | Test: 6 | if: | 7 | !contains(github.event.head_commit.message, '[skip ci]') 8 | strategy: 9 | fail-fast: false 10 | matrix: 11 | os: [ubuntu-latest, windows-latest, macos-latest] 12 | nim-channel: [stable, devel] 13 | 14 | name: ${{ matrix.os }}-${{ matrix.nim-channel }} 15 | runs-on: ${{ matrix.os }} 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Setup nim 20 | uses: jiro4989/setup-nim-action@v1 21 | with: 22 | nim-version: ${{ matrix.nim-channel }} 23 | 24 | - name: Test 25 | shell: bash 26 | run: | 27 | nim --version 28 | nimble test 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | nimcache 3 | bin 4 | build 5 | *.log 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Yuriy Glukhov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | jnim - JNI library for Nim language [![Build Status](https://github.com/yglukhov/jnim/workflows/CI/badge.svg?branch=master)](https://github.com/yglukhov/jnim/actions?query=branch%3Amaster) [![nimble](https://raw.githubusercontent.com/yglukhov/nimble-tag/master/nimble.png)](https://github.com/yglukhov/nimble-tag) 2 | ====================================== 3 | 4 | Native language integration with Java VM has never been easier! 5 | ```nim 6 | import jnim 7 | 8 | # Import a couple of classes 9 | jclass java.io.PrintStream of JVMObject: 10 | proc println(s: string) 11 | 12 | jclass java.lang.System of JVMObject: 13 | proc `out`: PrintStream {.prop, final, `static`.} 14 | 15 | # Initialize JVM 16 | initJNI() 17 | 18 | # Call! 19 | System.`out`.println("This string is printed with System.out.println!") 20 | ``` 21 | 22 | Overview 23 | -------- 24 | 25 | The list of the main features: 26 | 27 | * API splitted in two parts: low and high level. 28 | * It supports Java inheritance and generics. 29 | 30 | The documentation is coming soon. Now you can look the examples in the [tests](tests) directory. 31 | For example, [tests/test_java_lang.nim](tests/test_java_lang.nim) and [tests/test_java_util.nim](tests/test_java_util.nim) 32 | shows how to use high level API. 33 | 34 | If you want to run the tests, use ``nimble test`` command. 35 | 36 | ## Installation 37 | ```sh 38 | nimble install jnim 39 | ``` 40 | 41 | Thanks 42 | ------- 43 | 44 | - The current version of the library is a complete rewrite done by @vegansk. 45 | - Also thanks a lot to all the [contributors](https://github.com/yglukhov/jnim/graphs/contributors) 46 | -------------------------------------------------------------------------------- /jnim.nim: -------------------------------------------------------------------------------- 1 | import jnim/private / [ jvm_finder, jni_wrapper, jni_api, jni_generator, jni_export ] 2 | 3 | export jvm_finder, jni_wrapper, jni_api, jni_generator, jni_export 4 | 5 | -------------------------------------------------------------------------------- /jnim.nimble: -------------------------------------------------------------------------------- 1 | # Package 2 | 3 | version = "0.5.2" 4 | author = "Anatoly Galiulin, Yuriy Glukhov" 5 | description = "Java bridge for Nim" 6 | license = "MIT" 7 | 8 | # Dependencies 9 | 10 | requires "nim >= 0.19" 11 | 12 | from os import `/` 13 | 14 | const BIN_DIR = "bin" 15 | const BUILD_DIR = "build" 16 | 17 | proc compileJava() = 18 | BUILD_DIR.mkDir 19 | var cmd = "javac".toExe & " -d " & BUILD_DIR 20 | for f in [ 21 | "TestClass", 22 | "ConstructorTestClass", 23 | "MethodTestClass", 24 | "PropsTestClass", 25 | "InnerTestClass", 26 | "ExceptionTestClass", 27 | "GenericsTestClass", 28 | "BaseClass", 29 | "ChildClass", 30 | "ExportTestClass" ]: 31 | cmd &= " tests/java/" & f & ".java" 32 | 33 | if fileExists("Jnim.java"): 34 | cmd &= " Jnim.java" 35 | cmd &= " jnim/support/io/github/vegansk/jnim/NativeInvocationHandler.java" 36 | exec cmd 37 | 38 | proc test(name: string, flags = "") = 39 | let outFile = BIN_DIR / "test_" & name 40 | rmFile("Jnim.java") 41 | exec "nim c " & flags & " --passC:-g --threads:on -d:nimEmulateOverflowChecks -d:jnimGlue=Jnim.java --out:" & outFile & " tests/test_" & name 42 | compileJava() 43 | exec outFile 44 | 45 | task test, "Run all tests": 46 | test "all", "--mm:orc" 47 | test "all", "--mm:refc" 48 | 49 | task test_jvm_finder, "Run jvm_finder test": 50 | test "jvm_finder" 51 | 52 | task test_jni_wrapper, "Run jni_wrapper test": 53 | test "jni_wrapper" 54 | 55 | task test_jni_api, "Run jni_api test": 56 | test "jni_api" 57 | 58 | task test_jni_generator, "Run jni_api test": 59 | test "jni_generator" 60 | 61 | task test_jni_export, "Run jni_export test": 62 | test "jni_export" 63 | 64 | task test_jni_export_old, "Run jni_export_old test": 65 | test "jni_export_old" 66 | 67 | task test_java_lang, "Run java.lang test": 68 | test "java_lang" 69 | 70 | task test_java_util, "Run java.util test": 71 | test "java_util" 72 | 73 | task example, "Run example": 74 | test "example" 75 | -------------------------------------------------------------------------------- /jnim/java/lang.nim: -------------------------------------------------------------------------------- 1 | import ../../jnim 2 | 3 | # Forward declarations 4 | jclassDef java.lang.Object* of JVMObject 5 | jclassDef java.lang.Class*[T] of Object 6 | jclassDef java.lang.String* of Object 7 | 8 | jclassImpl java.lang.Object* of JVMObject: 9 | proc new* 10 | proc equals*(o: Object): bool 11 | proc getClass*: Class[Object] 12 | proc hashCode*: jint 13 | proc notify* 14 | proc notifyAll* 15 | proc toString*: String 16 | proc wait* 17 | proc wait*(timeout: jlong) 18 | proc wait*(timeoute: jlong, nanos: jint) 19 | 20 | proc `$`*(o: Object): string = 21 | o.toStringRaw 22 | 23 | jclassImpl java.lang.String* of Object: 24 | proc new* 25 | proc new*(s: string) 26 | proc new*(chars: seq[jbyte]) 27 | proc new*(chars: seq[jbyte], charsetName: string) 28 | proc length*: jint 29 | proc getBytes*: seq[jbyte] 30 | proc getBytes*(charsetName: string): seq[jbyte] 31 | 32 | #################################################################################################### 33 | # Wrapper types 34 | 35 | jclass java.lang.Number* of Object: 36 | proc byteValue*: jbyte 37 | proc doubleValue*: jdouble 38 | proc floatValue*: jfloat 39 | proc intValue*: jint 40 | proc longValue*: jlong 41 | proc shortValue*: jshort 42 | 43 | jclass java.lang.Byte* of Number: 44 | # Static fields 45 | proc MAX_VALUE*: jbyte {.prop, `static`, final.} 46 | proc MIN_VALUE*: jbyte {.prop, `static`, final.} 47 | proc SIZE*: jint {.prop, `static`, final.} 48 | proc TYPE*: Class[Byte] {.prop, `static`, final.} 49 | 50 | proc new*(v: jbyte) 51 | proc new*(s: string) 52 | 53 | converter toValueType*(v: Byte): jbyte = v.byteValue 54 | converter toWrapperType*(v: jbyte): Byte = Byte.new(v) 55 | 56 | jclass java.lang.Short* of Number: 57 | # Static fields 58 | proc MAX_VALUE*: jshort {.prop, `static`, final.} 59 | proc MIN_VALUE*: jshort {.prop, `static`, final.} 60 | proc SIZE*: jint {.prop, `static`, final.} 61 | proc TYPE*: Class[Short] {.prop, `static`, final.} 62 | 63 | proc new*(v: jshort) 64 | proc new*(s: string) 65 | 66 | converter toValueType*(v: Short): jshort = v.shortValue 67 | converter toWrapperType*(v: jshort): Short = Short.new(v) 68 | 69 | jclass java.lang.Integer* of Number: 70 | # Static fields 71 | proc MAX_VALUE*: jint {.prop, `static`, final.} 72 | proc MIN_VALUE*: jint {.prop, `static`, final.} 73 | proc SIZE*: jint {.prop, `static`, final.} 74 | proc TYPE*: Class[Integer] {.prop, `static`, final.} 75 | 76 | proc new*(v: jint) 77 | proc new*(s: string) 78 | 79 | converter toValueType*(v: Integer): jint = v.intValue 80 | converter toWrapperType*(v: jint): Integer = Integer.new(v) 81 | 82 | jclass java.lang.Long* of Number: 83 | # Static fields 84 | proc MAX_VALUE*: jlong {.prop, `static`, final.} 85 | proc MIN_VALUE*: jlong {.prop, `static`, final.} 86 | proc SIZE*: jint {.prop, `static`, final.} 87 | proc TYPE*: Class[Long] {.prop, `static`, final.} 88 | 89 | proc new*(v: jlong) 90 | proc new*(s: string) 91 | 92 | converter toValueType*(v: Long): jlong = v.longValue 93 | converter toWrapperType*(v: jlong): Long = Long.new(v) 94 | 95 | jclass java.lang.Float* of Number: 96 | # Static fields 97 | proc MAX_EXPONENT*: jint {.prop, `static`, final.} 98 | proc MAX_VALUE*: jfloat {.prop, `static`, final.} 99 | proc MIN_EXPONENT*: jint {.prop, `static`, final.} 100 | proc MIN_NORMAL*: jfloat {.prop, `static`, final.} 101 | proc MIN_VALUE*: jfloat {.prop, `static`, final.} 102 | # proc NaN*: jfloat {.prop, `static`, final.} # Collides with system.Nan 103 | proc NEGATIVE_INFINITY*: jfloat {.prop, `static`, final.} 104 | proc POSITIVE_INFINITY*: jfloat {.prop, `static`, final.} 105 | proc SIZE*: jint {.prop, `static`, final.} 106 | proc TYPE*: Class[Long] {.prop, `static`, final.} 107 | 108 | proc new*(v: jfloat) 109 | proc new*(v: jdouble) 110 | proc new*(s: string) 111 | 112 | converter toValueType*(v: Float): jfloat = v.floatValue 113 | converter toWrapperType*(v: jfloat): Float = Float.new(v) 114 | 115 | jclass java.lang.Double* of Number: 116 | # Static fields 117 | proc MAX_EXPONENT*: jint {.prop, `static`, final.} 118 | proc MAX_VALUE*: jdouble {.prop, `static`, final.} 119 | proc MIN_EXPONENT*: jint {.prop, `static`, final.} 120 | proc MIN_NORMAL*: jdouble {.prop, `static`, final.} 121 | proc MIN_VALUE*: jdouble {.prop, `static`, final.} 122 | # proc NaN*: jdouble {.prop, `static`, final.} # Collides with system.Nan 123 | proc NEGATIVE_INFINITY*: jdouble {.prop, `static`, final.} 124 | proc POSITIVE_INFINITY*: jdouble {.prop, `static`, final.} 125 | proc SIZE*: jint {.prop, `static`, final.} 126 | proc TYPE*: Class[Long] {.prop, `static`, final.} 127 | 128 | proc new*(v: jdouble) 129 | proc new*(s: string) 130 | 131 | converter toValueType*(v: Double): jdouble = v.doubleValue 132 | converter toWrapperType*(v: jdouble): Double = Double.new(v) 133 | 134 | jclass java.lang.Boolean* of Object: 135 | # Static fields 136 | proc FALSE*: Boolean {.prop, `static`, final.} 137 | proc TRUE*: Boolean {.prop, `static`, final.} 138 | 139 | proc booleanValue*: jboolean 140 | 141 | proc new*(v: jboolean) 142 | proc new*(s: string) 143 | 144 | converter toValueType*(v: Boolean): jboolean = v.booleanValue 145 | converter toWrapperType*(v: jboolean): Boolean = Boolean.new(v) 146 | 147 | #################################################################################################### 148 | # Exceptions 149 | 150 | jclass java.lang.StackTraceElement* of Object: 151 | proc new*(declaringClass: String, methodName: String, fileName: String, lineNumber: jint) 152 | proc getClassName*: String 153 | proc getFileName*: String 154 | proc getLineNumber*: jint 155 | proc getMethodName*: String 156 | proc isNativeMethod*: bool 157 | 158 | jclass java.lang.Throwable* of Object: 159 | proc new* 160 | proc new*(message: String) 161 | proc new*(message: String, cause: Throwable) 162 | proc new*(cause: Throwable) 163 | proc getCause*: Throwable 164 | proc getLocalizedMessage*: String 165 | proc getMessage*: String 166 | proc getStackTrace*: seq[StackTraceElement] 167 | proc printStackTrace* 168 | 169 | jclass java.lang.Exception* of Throwable: 170 | proc new* 171 | proc new*(message: String) 172 | proc new*(message: String, cause: Throwable) 173 | proc new*( 174 | message: String, 175 | cause: Throwable, 176 | enableSuppression, writableStackTrace: bool) 177 | proc new*(cause: Throwable) 178 | 179 | proc asJVM*(ex: JavaException): Throwable = 180 | Throwable.fromJObject(ex.getJVMException.get) 181 | 182 | proc getCurrentJVMException*: Throwable = 183 | ((ref JavaException)getCurrentException())[].asJVM 184 | 185 | jclass java.lang.Runnable* of JVMObject: 186 | proc run*() 187 | -------------------------------------------------------------------------------- /jnim/java/util.nim: -------------------------------------------------------------------------------- 1 | import ../../jnim, lang 2 | 3 | jclass java.util.Iterator*[V] of Object: 4 | proc hasNext*: bool 5 | proc next*: V 6 | proc remove* 7 | 8 | jclass java.util.Collection*[V] of Object: 9 | proc add*(v: V): bool 10 | proc addAll*(c: Collection[V]): bool 11 | proc clear* 12 | proc contains*(o: JVMObject): bool 13 | proc isEmpty*: bool 14 | proc toIterator*: Iterator[V] {.importc: "iterator".} 15 | proc remove*(o: V): bool 16 | proc removeAll*(o: Collection[V]): bool 17 | proc retainAll*(o: Collection[V]): bool 18 | proc size*: jint 19 | 20 | jclass java.util.List*[V] of Collection[V]: 21 | proc add*(i: jint, v: V) 22 | proc get*(i: jint): V 23 | proc set*(i: jint): V 24 | proc remove*(i: jint) 25 | proc subList*(f, t: jint): List[V] 26 | 27 | jclass java.util.ArrayList*[V] of List[V]: 28 | proc new* 29 | 30 | proc new*[V](t: typedesc[ArrayList[V]], c: openArray[V]): ArrayList[V] = 31 | result = ArrayList[V].new 32 | for v in c: 33 | discard result.add(v) 34 | 35 | jclassDef java.util.Set*[E] of Collection[E] 36 | 37 | jclass java.util.Map$Entry*[K,V] as MapEntry of Object: 38 | proc getKey*: K 39 | proc getValue*: V 40 | proc setValue*(v: V): V 41 | 42 | jclass java.util.Map*[K,V] of Object: 43 | proc clear* 44 | proc containsKey*(k: K): bool 45 | proc containsValue*(k: K): bool 46 | proc entrySet*: Set[MapEntry[K,V]] 47 | proc get*(k: K): V 48 | proc isEmpty*: bool 49 | proc keySet*: Set[K] 50 | proc put*(k: K, v: V): V 51 | proc putAll*(m: Map[K,V]) 52 | proc remove*(k: K): V 53 | proc size*: jint 54 | proc values*: Collection[V] 55 | 56 | jclass java.util.HashMap*[K,V] of Map[K,V]: 57 | proc new* 58 | 59 | #################################################################################################### 60 | # Helpers 61 | 62 | proc toSeq*[V](c: Collection[V]): seq[V] = 63 | result = newSeq[V]() 64 | let it = c.toIterator 65 | while it.hasNext: 66 | result.add it.next 67 | 68 | -------------------------------------------------------------------------------- /jnim/private/experimental/javap_parser.nim: -------------------------------------------------------------------------------- 1 | import parseutils, strutils, macros 2 | 3 | 4 | type 5 | AccessType* = enum 6 | atPublic 7 | atPrivate 8 | atProtected 9 | 10 | GenericArgDef* = object 11 | name*: TypeName 12 | relation*: string 13 | to*: TypeName 14 | 15 | TypeName* = object 16 | name*: string 17 | genericArgs*: seq[GenericArgDef] 18 | isArray*: bool 19 | 20 | MethodDef* = object 21 | retType*: TypeName 22 | argTypes*: seq[TypeName] 23 | genericArgs*: seq[GenericArgDef] 24 | name*: string 25 | access*: AccessType 26 | synchronized*: bool 27 | prop*: bool 28 | throws*: seq[TypeName] 29 | descriptor*: string 30 | 31 | ClassDef* = object 32 | methods*: seq[MethodDef] 33 | name*: TypeName 34 | extends*: TypeName 35 | implements*: seq[TypeName] 36 | access*: AccessType 37 | final*: bool 38 | 39 | proc parseAccessor(s: string, at: var AccessType, start: int): int = 40 | at = atPublic 41 | result = s.skip("public", start) 42 | if result == 0: 43 | at = atPrivate 44 | result = s.skip("private", start) 45 | if result == 0: 46 | at = atProtected 47 | result = s.skip("protected", start) 48 | 49 | proc parseFinalFlag(s: string, final: var bool, start: int): int = 50 | result = s.skip("final ", start) 51 | final = result != 0 52 | 53 | proc parseTypeName(s: string, tn: var TypeName, start: int): int 54 | 55 | proc parseExtendsSection(s: string, extends: var TypeName, start: int): int = 56 | result = s.skip("extends ", start) 57 | if result != 0: 58 | result += s.parseTypeName(extends, result + start) 59 | 60 | proc parseCommaSeparatedTypeList(s: string, types: var seq[TypeName], start: int): int = 61 | result += start 62 | defer: result -= start 63 | while true: 64 | types.add(TypeName()) 65 | result += s.skipWhitespace(result) 66 | var pos = s.parseTypeName(types[^1], result) 67 | result += pos 68 | if pos == 0: 69 | types.setLen(types.len - 1) 70 | break 71 | result += s.skipWhitespace(result) 72 | pos = s.skip(",", result) 73 | result += pos 74 | if pos == 0: 75 | break 76 | 77 | proc parseImplementsSection(s: string, implements: var seq[TypeName], start: int): int = 78 | result = s.skip("implements ", start) 79 | if result != 0: 80 | result += start 81 | defer: result -= start 82 | implements = newSeq[TypeName]() 83 | result += s.parseCommaSeparatedTypeList(implements, result) 84 | 85 | proc parseGenericArgName(s: string, name: var TypeName, start: int): int = 86 | result = s.skip("?", start) 87 | if result != 0: 88 | name.name = "?" 89 | else: 90 | result = s.parseTypeName(name, start) 91 | 92 | proc parseGenericRelation(s: string, relation: var string, start: int): int = 93 | for r in ["super", "extends"]: 94 | result = s.skip(r, start) 95 | if result != 0: 96 | relation = r 97 | break 98 | 99 | proc parseGenericArgDef(s: string, def: var GenericArgDef, start: int): int = 100 | result = s.parseGenericArgName(def.name, start) 101 | result += start 102 | defer: result -= start 103 | result += s.skipWhitespace(result) 104 | var pos = s.parseGenericRelation(def.relation, result) 105 | result += pos 106 | if pos != 0: 107 | result += s.skipWhitespace(result) 108 | result += s.parseTypeName(def.to, result) 109 | 110 | proc parseGenericArgs(s: string, args: var seq[GenericArgDef], start: int): int = 111 | result = s.skip("<", start) 112 | if result != 0: 113 | result += start 114 | defer: result -= start 115 | args = newSeq[GenericArgDef]() 116 | while true: 117 | args.add(GenericArgDef()) 118 | result += s.skipWhitespace(result) 119 | var pos = s.parseGenericArgDef(args[^1], result) 120 | result += pos 121 | if pos == 0: 122 | pos = s.skip(">", result) 123 | assert(pos == 1) 124 | result += pos 125 | args.setLen(args.len - 1) 126 | break 127 | result += s.skipWhitespace(result) 128 | pos = s.skip(",", result) 129 | result += pos 130 | if pos == 0: 131 | pos = s.skip(">", result) 132 | assert(pos == 1) 133 | result += pos 134 | break 135 | 136 | proc parseTypeName(s: string, tn: var TypeName, start: int): int = 137 | result = s.parseWhile(tn.name, IdentChars + {'.', '$'}, start) 138 | if result != 0: 139 | result += s.parseGenericArgs(tn.genericArgs, start + result) 140 | var pos = s.skip("[]", start + result) 141 | result += pos 142 | if pos != 0: 143 | tn.isArray = true 144 | 145 | proc parseMethodModifiers(s: string, meth: var MethodDef, start: int): int = 146 | var pos = s.skip("synchronized", start) 147 | result += pos 148 | result += start 149 | defer: result -= start 150 | if pos != 0: 151 | meth.synchronized = true 152 | result += s.skipWhitespace(result) 153 | pos = s.skip("static", result) 154 | result += pos 155 | result += s.skipWhitespace(result) 156 | pos = s.skip("native", result) 157 | result += pos 158 | result += s.skipWhitespace(result) 159 | pos = s.skip("final", result) 160 | result += pos 161 | result += s.skipWhitespace(result) 162 | 163 | proc parseMethodThrows(s: string, throws: var seq[TypeName], start: int): int = 164 | result += start 165 | defer: result -= start 166 | result += s.skipWhitespace(result) 167 | var pos = s.skip("throws", result) 168 | result += pos 169 | if pos != 0: 170 | throws = newSeq[TypeName]() 171 | result += s.parseCommaSeparatedTypeList(throws, result) 172 | 173 | proc parseFieldDescriptor(s: string, meth: var MethodDef, start: int): int = 174 | var pos = s.skip("descriptor: ", start) 175 | if pos != 0: 176 | result = start + pos 177 | defer: result -= start 178 | result += s.parseUntil(meth.descriptor, '\l', result) 179 | 180 | proc parseMethod(s: string, meth: var MethodDef, start: int): int = 181 | result += start 182 | defer: result -= start 183 | result += s.skipWhitespace(result) 184 | var pos = s.parseAccessor(meth.access, result) 185 | result += pos 186 | if pos == 0: 187 | return 0 188 | result += s.skipWhitespace(result) 189 | result += s.parseMethodModifiers(meth, result) 190 | result += s.parseTypeName(meth.retType, result) 191 | pos = s.skip("(", result) 192 | result += pos 193 | if pos != 0: 194 | # This is constructor 195 | meth.name = meth.retType.name 196 | meth.genericArgs = meth.retType.genericArgs 197 | meth.retType.name = "void" 198 | meth.retType.genericArgs = nil 199 | else: 200 | var dummyTypeName : TypeName 201 | result += s.skipWhitespace(result) 202 | result += s.parseTypeName(dummyTypeName, result) 203 | meth.name = dummyTypeName.name 204 | meth.genericArgs = dummyTypeName.genericArgs 205 | result += s.skipWhitespace(result) 206 | pos = s.skip("(", result) 207 | result += pos 208 | if pos == 0: 209 | if s.skip(";", result) == 1: 210 | meth.prop = true 211 | if not meth.prop: 212 | meth.argTypes = newSeq[TypeName]() 213 | result += s.parseCommaSeparatedTypeList(meth.argTypes, result) 214 | result += s.skipWhitespace(result) 215 | pos = s.skip(")", result) 216 | result += pos 217 | assert(pos != 0) 218 | result += s.parseMethodThrows(meth.throws, result) 219 | pos = s.skip(";", result) 220 | result += pos 221 | assert(pos != 0) 222 | result += s.skipWhitespace(result) 223 | result += s.parseFieldDescriptor(meth, result) 224 | 225 | proc parseMethods(s: string, methods: var seq[MethodDef], start: int): int = 226 | result += start 227 | defer: result -= start 228 | while true: 229 | methods.add(MethodDef()) 230 | var pos = s.parseMethod(methods[^1], result) 231 | result += pos 232 | if pos == 0: 233 | methods.setLen(methods.len - 1) 234 | break 235 | 236 | proc parseJavap*(s: string, def: var ClassDef): int = 237 | def.implements = newSeq[TypeName]() 238 | def.methods = newSeq[MethodDef]() 239 | 240 | var pos = s.skipUntil('\l') + 1 241 | pos += s.parseAccessor(def.access, pos) 242 | pos += s.skipWhitespace(pos) 243 | pos += s.parseFinalFlag(def.final, pos) 244 | pos += s.skip("class", pos) 245 | pos += s.skipWhitespace(pos) 246 | pos += s.parseTypeName(def.name, pos) 247 | pos += s.skipWhitespace(pos) 248 | pos += s.parseExtendsSection(def.extends, pos) 249 | pos += s.skipWhitespace(pos) 250 | pos += s.parseImplementsSection(def.implements, pos) 251 | pos += s.skipWhitespace(pos) 252 | pos += s.skip("{", pos) 253 | pos += s.parseMethods(def.methods, pos) 254 | 255 | proc nodeToString(e: NimNode): string {.compileTime.} = 256 | if e.kind == nnkIdent: 257 | result = $e 258 | elif e.kind == nnkAccQuoted: 259 | result = "" 260 | for s in e.children: 261 | result &= nodeToString(s) 262 | elif e.kind == nnkDotExpr: 263 | result = nodeToString(e[0]) & "." & nodeToString(e[1]) 264 | else: 265 | echo treeRepr(e) 266 | assert(false, "Cannot stringize node") 267 | 268 | macro jnimport_all*(e: untyped): untyped = 269 | let className = nodeToString(e) 270 | let javapOutput = staticExec("javap -public -s " & className) 271 | var cd: ClassDef 272 | discard parseJavap(javapOutput, cd) 273 | 274 | echo cd 275 | -------------------------------------------------------------------------------- /jnim/private/java_glue.nim: -------------------------------------------------------------------------------- 1 | import sets 2 | 3 | const jnimPackageName* {.strdefine.} = "io.github.yglukhov.jnim" 4 | 5 | const 6 | jnimGlue {.strdefine.} = "Jnim.java" 7 | FinalizerName* = "_0" # Private, don't use 8 | 9 | var 10 | javaGlue {.compileTime.} = newStringOfCap(1000000) 11 | existingImports {.compileTime.} = initHashSet[string]() 12 | classCursor {.compileTime.} = 0 13 | importCursor {.compileTime.} = 0 14 | 15 | proc initGlueIfNeeded() = 16 | if classCursor == 0: 17 | javaGlue = "package " & jnimPackageName & ";\n" 18 | importCursor = javaGlue.len 19 | javaGlue &= """ 20 | public class Jnim { 21 | public interface __NimObject {} 22 | public static native void """ & FinalizerName & """(long p); 23 | """ 24 | classCursor = javaGlue.len 25 | javaGlue &= "}\n" 26 | 27 | proc emitGlueImportsP*(imports: openarray[string]) {.compileTime.} = 28 | var newImports = newStringOfCap(10000) 29 | for s in imports: 30 | if s.len != 0 and s notin existingImports: 31 | existingImports.incl(s) 32 | newImports &= "import " 33 | newImports &= s 34 | newImports &= ";\n" 35 | 36 | if newImports.len != 0: 37 | initGlueIfNeeded() 38 | javaGlue.insert(newImports, importCursor) 39 | importCursor += newImports.len 40 | classCursor += newImports.len 41 | 42 | proc emitGlueClassP*(classDef: string) {.compileTime.} = 43 | initGlueIfNeeded() 44 | javaGlue.insert(classDef, classCursor) 45 | classCursor += classDef.len 46 | 47 | proc flushGlueP*() {.compileTime.} = writeFile(jnimGlue, javaGlue) 48 | macro emitGlueImports*(imports: static varargs[string]): untyped = emitGlueImportsP(imports) 49 | macro emitGlueClass*(classCode: static[string]): untyped = emitGlueClassP(classCode) 50 | macro flushGlue*(): untyped = flushGlueP() 51 | macro debugPrintJavaGlue*(): untyped {.deprecated.} = echo javaGlue 52 | -------------------------------------------------------------------------------- /jnim/private/jni_api.nim: -------------------------------------------------------------------------------- 1 | import jni_wrapper, options, macros, strutils 2 | 3 | export jni_wrapper 4 | 5 | type 6 | JNIVersion* {.pure.} = enum 7 | v1_1 = JNI_VERSION_1_1.int, 8 | v1_2 = JNI_VERSION_1_2.int, 9 | v1_4 = JNI_VERSION_1_4.int, 10 | v1_6 = JNI_VERSION_1_6.int, 11 | v1_8 = JNI_VERSION_1_8.int 12 | 13 | var initArgs: JavaVMInitArgs 14 | 15 | # Options for another threads 16 | var theVM: JavaVMPtr 17 | var theEnv* {.threadVar}: JNIEnvPtr 18 | var findClassOverride* {.threadVar.}: proc(env: JNIEnvPtr, name: cstring): JClass {.gcsafe.} 19 | 20 | proc initJNIThread* {.gcsafe.} 21 | 22 | proc initJNIArgs(version: JNIVersion = JNIVersion.v1_6, options: openarray[string] = []) = 23 | ## Setup JNI API 24 | jniAssert(initArgs.version == 0, "JNI API already initialized, you must deinitialize it first") 25 | initArgs.version = version.jint 26 | initArgs.nOptions = options.len.jint 27 | if options.len != 0: 28 | var opts = cast[ptr UncheckedArray[JavaVMOption]](createShared(JavaVMOption, options.len)) 29 | initArgs.options = addr opts[0] 30 | for i in 0 ..< options.len: 31 | opts[i].optionString = cast[cstring](allocShared(options[i].len + 1)) 32 | opts[i].optionString[0] = '\0' 33 | if options[i].len != 0: 34 | copyMem(addr opts[i].optionString[0], unsafeAddr options[i][0], options[i].len + 1) 35 | 36 | proc initJNI*(version: JNIVersion = JNIVersion.v1_6, options: openarray[string] = []) = 37 | ## Setup JNI API 38 | initJNIArgs(version, options) 39 | initJNIThread() 40 | 41 | proc initJNI*(env: JNIEnvPtr) = 42 | theEnv = env 43 | 44 | proc initJNI*(vm: JavaVMPtr) = 45 | theVM = vm 46 | 47 | # This is not supported, as it said here: http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/invocation.html#destroy_java_vm: 48 | # "As of JDK/JRE 1.1.2 unloading of the VM is not supported." 49 | # Maybe it can be usefull with alternative implementations of JRE 50 | when false: 51 | proc deinitJNI* = 52 | ## Deinitialize JNI API 53 | if theVM == nil: 54 | return 55 | jniCall theVM.DestroyJavaVM(theVM), "Error deinitializing JNI" 56 | # TODO: dealloc initArgs 57 | theVM = nil 58 | theEnv = nil 59 | 60 | proc initJNIThread* = 61 | ## Setup JNI API thread 62 | if theEnv != nil: 63 | return 64 | if initArgs.version == 0: 65 | raise newJNIException("You must initialize JNI API before using it") 66 | 67 | if theVM == nil: 68 | # We need to link with JNI and so on 69 | linkWithJVMLib() 70 | jniCall JNI_CreateJavaVM(theVM.addr, cast[ptr pointer](theEnv.addr), initArgs.addr), "Error creating VM" 71 | else: 72 | # We need to attach current thread to JVM 73 | jniCall theVM.AttachCurrentThread(theVM, cast[ptr pointer](theEnv.addr), initArgs.addr), "Error attaching thread to VM" 74 | 75 | proc deinitJNIThread* = 76 | ## Deinitialize JNI API thread 77 | if theEnv == nil: 78 | return 79 | discard theVM.DetachCurrentThread(theVM) 80 | theEnv = nil 81 | 82 | proc isJNIThreadInitialized*: bool = theEnv != nil 83 | 84 | proc findRunningVM() = 85 | if theVM.isNil: 86 | if JNI_GetCreatedJavaVMs.isNil: 87 | linkWithJVMLib() 88 | 89 | var vmBuf: array[1, JavaVMPtr] 90 | var bufSize : jsize = 0 91 | discard JNI_GetCreatedJavaVMs(addr vmBuf[0], jsize(vmBuf.len), addr bufSize) 92 | if bufSize > 0: 93 | theVM = vmBuf[0] 94 | else: 95 | raise newJNIException("No JVM is running. You must call initJNIThread before using JNI API.") 96 | 97 | let res = theVM.GetEnv(theVM, cast[ptr pointer](theEnv.addr), JNI_VERSION_1_6) 98 | if res == JNI_EDETACHED: 99 | initJNIArgs() 100 | initJNIThread() 101 | elif res != 0: 102 | raise newJNIException("GetEnv result: " & $res) 103 | if theEnv.isNil: 104 | raise newJNIException("No JVM found") 105 | 106 | template checkInit* = 107 | if theEnv.isNil: findRunningVM() 108 | 109 | template deleteLocalRef*(env: JNIEnvPtr, r: jobject) = 110 | env.DeleteLocalRef(env, r) 111 | 112 | template deleteGlobalRef*(env: JNIEnvPtr, r: jobject) = 113 | env.DeleteGlobalRef(env, r) 114 | 115 | template newGlobalRef*[T : jobject](env: JNIEnvPtr, r: T): T = 116 | cast[T](env.NewGlobalRef(env, r)) 117 | 118 | #################################################################################################### 119 | # Types 120 | type 121 | JVMMethodID* = distinct jmethodID 122 | JVMFieldID* = distinct jfieldID 123 | JVMClass* = ref object 124 | cls: JClass 125 | JVMObjectObj {.inheritable.} = object 126 | obj: jobject 127 | JVMObject* = ref JVMObjectObj 128 | JnimNonVirtual_JVMObject* {.inheritable.} = object # Not for public use! 129 | obj*: jobject 130 | # clazz*: JVMClass 131 | 132 | 133 | proc freeJVMObjectObj(o: var JVMObjectObj) = 134 | if o.obj != nil and theEnv != nil: 135 | theEnv.deleteGlobalRef(o.obj) 136 | o.obj = nil 137 | 138 | when defined(gcDestructors): 139 | proc `=destroy`*(o: var JVMObjectObj) = 140 | freeJVMObjectObj(o) 141 | 142 | #################################################################################################### 143 | # Exception handling 144 | 145 | type 146 | JavaException* = object of CatchableError 147 | ex: JVMObject 148 | 149 | proc toStringRaw*(o: JVMObject): string {.gcsafe.} 150 | 151 | proc newJavaException*(ex: JVMObject): ref JavaException = 152 | result = newException(JavaException, ex.toStringRaw) 153 | result.ex = ex 154 | 155 | proc newJVMObject*(o: jobject): JVMObject {.gcsafe.} 156 | proc newJVMObjectConsumingLocalRef*(o: jobject): JVMObject {.gcsafe.} 157 | 158 | proc raiseJavaException() = 159 | let ex = theEnv.ExceptionOccurred(theEnv) 160 | theEnv.ExceptionClear(theEnv) 161 | raise newJavaException(newJVMObjectConsumingLocalRef(ex)) 162 | 163 | proc checkJVMException*(e: JNIEnvPtr) {.inline.} = 164 | if unlikely(theEnv.ExceptionCheck(theEnv) != JVM_FALSE): 165 | raiseJavaException() 166 | 167 | template checkException() = 168 | assert(not theEnv.isNil) 169 | checkJVMException(theEnv) 170 | 171 | proc callVM*[T](s: T): T {.inline.} = 172 | checkException() 173 | when T isnot void: 174 | return s 175 | 176 | #################################################################################################### 177 | # JVMMethodID type 178 | template newJVMMethodID*(id: jmethodID): JVMMethodID = JVMMethodID(id) 179 | template get*(id: JVMMethodID): jmethodID = jmethodID(id) 180 | 181 | #################################################################################################### 182 | # JVMFieldID type 183 | template newJVMFieldID*(id: jfieldID): JVMFieldID = JVMFieldID(id) 184 | template get*(id: JVMFieldID): jfieldID = jfieldID(id) 185 | 186 | #################################################################################################### 187 | # JVMClass type 188 | proc freeClass(c: JVMClass) = 189 | if theEnv != nil: 190 | theEnv.deleteGlobalRef(c.cls) 191 | 192 | proc newJVMClass*(c: JClass): JVMClass = 193 | assert(cast[pointer](c) != nil) 194 | result.new(freeClass) 195 | result.cls = theEnv.newGlobalRef(c) 196 | 197 | proc findClass*(env: JNIEnvPtr, name: cstring): JClass = 198 | if not findClassOverride.isNil: 199 | result = findClassOverride(env, name) 200 | else: 201 | result = env.FindClass(env, name) 202 | 203 | proc getByFqcn*(T: typedesc[JVMClass], name: cstring): JVMClass = 204 | ## Finds class by it's full qualified class name 205 | checkInit 206 | let c = callVM findClass(theEnv, name) 207 | result = c.newJVMClass 208 | theEnv.deleteLocalRef(c) 209 | 210 | proc getByName*(T: typedesc[JVMClass], name: string): JVMClass = 211 | ## Finds class by it's name (not fqcn) 212 | T.getByFqcn(name.fqcn.cstring) 213 | 214 | proc getJVMClass*(o: jobject): JVMClass {.inline.} = 215 | checkInit 216 | let c = callVM theEnv.GetObjectClass(theEnv, o) 217 | result = c.newJVMClass 218 | theEnv.deleteLocalRef(c) 219 | 220 | proc get*(c: JVMClass): JClass = 221 | c.cls 222 | 223 | # Static fields 224 | 225 | proc getStaticFieldId*(c: JVMClass, name, sig: cstring): JVMFieldID = 226 | checkInit 227 | (callVM theEnv.GetStaticFieldID(theEnv, c.get, name, sig)).newJVMFieldID 228 | 229 | proc getStaticFieldId*(c: JVMClass, name: cstring, t: typedesc): JVMFieldID {.inline.} = 230 | getStaticFieldId(c, name, jniSig(t).cstring) 231 | 232 | proc getFieldId*(c: JVMClass, name, sig: cstring): JVMFieldID = 233 | checkInit 234 | (callVM theEnv.GetFieldID(theEnv, c.get, name, sig)).newJVMFieldID 235 | 236 | proc getFieldId*(c: JVMClass, name: cstring, t: typedesc): JVMFieldID {.inline.} = 237 | getFieldId(c, name, jniSig(t)) 238 | 239 | proc getFieldId*(c: JVMObject, name, sig: cstring): JVMFieldID = 240 | checkInit 241 | let clazz = callVM theEnv.GetObjectClass(theEnv, c.obj) 242 | result = (callVM theEnv.GetFieldID(theEnv, clazz, name, sig)).newJVMFieldID 243 | theEnv.deleteLocalRef(clazz) 244 | 245 | proc getFieldId*(c: JVMObject, name: cstring, t: typedesc): JVMFieldID {.inline.} = 246 | getFieldId(c, name, jniSig(t).cstring) 247 | 248 | proc getMethodId*(c: JVMClass, name, sig: cstring): JVMMethodID = 249 | checkInit 250 | (callVM theEnv.GetMethodID(theEnv, c.get, name, sig)).newJVMMethodID 251 | 252 | proc getMethodId*(c: JVMObject, name, sig: cstring): JVMMethodID = 253 | checkInit 254 | let clazz = callVM theEnv.GetObjectClass(theEnv, c.obj) 255 | result = (callVM theEnv.GetMethodID(theEnv, clazz, name, sig)).newJVMMethodID 256 | theEnv.deleteLocalRef(clazz) 257 | 258 | proc getStaticMethodId*(c: JVMClass, name, sig: cstring): JVMMethodID = 259 | checkInit 260 | (callVM theEnv.GetStaticMethodID(theEnv, c.get, name, sig)).newJVMMethodID 261 | 262 | proc callVoidMethod*(c: JVMClass, id: JVMMethodID, args: openarray[jvalue] = []) = 263 | checkInit 264 | let a = if args.len == 0: nil else: unsafeAddr args[0] 265 | theEnv.CallStaticVoidMethodA(theEnv, c.get, id.get, a) 266 | checkException 267 | 268 | proc callVoidMethod*(c: JVMClass, name, sig: cstring, args: openarray[jvalue] = []) = 269 | checkInit 270 | let a = if args.len == 0: nil else: unsafeAddr args[0] 271 | theEnv.CallStaticVoidMethodA(theEnv, c.get, c.getStaticMethodId(name, sig).get, a) 272 | checkException 273 | 274 | proc newObject*(c: JVMClass, id: JVMMethodID, args: openarray[jvalue] = []): JVMObject = 275 | checkInit 276 | let a = if args.len == 0: nil else: unsafeAddr args[0] 277 | (callVM theEnv.NewobjectA(theEnv, c.get, id.get, a)).newJVMObjectConsumingLocalRef 278 | 279 | proc newObject*(c: JVMClass, sig: cstring, args: openarray[jvalue] = []): JVMObject = 280 | checkInit 281 | let a = if args.len == 0: nil else: unsafeAddr args[0] 282 | (callVM theEnv.NewobjectA(theEnv, c.get, c.getMethodId("", sig).get, a)).newJVMObjectConsumingLocalRef 283 | 284 | proc newObjectRaw*(c: JVMClass, sig: cstring, args: openarray[jvalue] = []): jobject = 285 | checkInit 286 | let a = if args.len == 0: nil else: unsafeAddr args[0] 287 | callVM theEnv.NewobjectA(theEnv, c.get, c.getMethodId("", sig).get, a) 288 | 289 | #################################################################################################### 290 | # JVMObject type 291 | 292 | proc jniSig*(T: typedesc[JVMObject]): string = sigForClass"java.lang.Object" 293 | 294 | proc freeJVMObject*(o: JVMObject) = 295 | freeJVMObjectObj(o[]) 296 | 297 | proc free*(o: JVMObject) {.deprecated.} = 298 | o.freeJVMObject() 299 | 300 | proc fromJObject*(T: typedesc[JVMObject], o: jobject): T = 301 | if o != nil: 302 | when defined(gcDestructors): 303 | result.new() 304 | else: 305 | result.new(cast[proc(r: T) {.nimcall.}](freeJVMObject)) 306 | checkInit 307 | result.obj = theEnv.newGlobalRef(o) 308 | 309 | proc fromJObjectConsumingLocalRef*(T: typedesc[JVMObject], o: jobject): T = 310 | if not o.isNil: 311 | result = T.fromJObject(o) 312 | theEnv.deleteLocalRef(o) 313 | 314 | proc newJVMObject*(o: jobject): JVMObject = 315 | JVMObject.fromJObject(o) 316 | 317 | proc newJVMObjectConsumingLocalRef*(o: jobject): JVMObject = 318 | if not o.isNil: 319 | result = newJVMObject(o) 320 | theEnv.deleteLocalRef(o) 321 | 322 | proc create*(t: typedesc[JVMObject], o: jobject): JVMObject = newJVMObject(o) 323 | 324 | proc newJVMObject*(s: string): JVMObject = 325 | result = (callVM theEnv.NewStringUTF(theEnv, s)).newJVMObjectConsumingLocalRef 326 | 327 | proc get*(o: JVMObject): jobject = 328 | assert(not o.obj.isNil) 329 | o.obj 330 | 331 | proc getNoCreate*(o: JVMObject): jobject {.inline.} = o.obj 332 | 333 | proc setObj*(o: JVMObject, obj: jobject) = 334 | assert(obj == nil or theEnv.GetObjectRefType(theEnv, obj) in {JNILocalRefType, JNIWeakGlobalRefType}) 335 | o.obj = obj 336 | 337 | proc toJValue*(o: JVMObject): jvalue = 338 | if not o.isNil: 339 | result = o.get.toJValue 340 | 341 | proc getJVMClass*(o: JVMObject): JVMClass = 342 | assert(o.get != nil) 343 | getJVMClass(o.get) 344 | 345 | proc equalsRaw*(v1, v2: JVMObject): jboolean = 346 | # This is low level ``equals`` version 347 | assert v1.obj != nil 348 | let cls = theEnv.GetObjectClass(theEnv, v1.obj) 349 | jniAssertEx(cls.pointer != nil, "Can't find object's class") 350 | const sig = "($#)$#" % [jobject.jniSig, jboolean.jniSig] 351 | let mthId = theEnv.GetMethodID(theEnv, cls, "equals", sig) 352 | theEnv.deleteLocalRef(cls) 353 | jniAssertEx(mthId != nil, "Can't find ``equals`` method") 354 | var v2w = v2.obj.toJValue 355 | result = theEnv.CallBooleanMethodA(theEnv, v1.obj, mthId, addr v2w) 356 | 357 | proc jstringToStringAux(s: jstring): string = 358 | assert(not s.isNil) 359 | let numBytes = theEnv.GetStringUTFLength(theEnv, s) 360 | result = newString(numBytes) 361 | if numBytes != 0: 362 | let numChars = theEnv.GetStringLength(theEnv, s) 363 | theEnv.GetStringUTFRegion(theEnv, s, 0, numChars, addr result[0]) 364 | 365 | proc toStringRaw(o: jobject): string {.gcsafe.} = 366 | # This is low level ``toString`` version. 367 | assert(not o.isNil) 368 | let cls = theEnv.GetObjectClass(theEnv, o) 369 | jniAssertEx(cls.pointer != nil, "Can't find object's class") 370 | const sig = "()" & string.jniSig 371 | let mthId = theEnv.GetMethodID(theEnv, cls, "toString", sig) 372 | theEnv.deleteLocalRef(cls) 373 | jniAssertEx(mthId != nil, "Can't find ``toString`` method") 374 | let s = theEnv.CallObjectMethodA(theEnv, o, mthId, nil).jstring 375 | if s == nil: 376 | return "" 377 | result = jstringToStringAux(s) 378 | theEnv.deleteLocalRef(s) 379 | 380 | proc toStringRawConsumingLocalRef(o: jobject): string = 381 | # This is low level ``toString`` version 382 | if not o.isNil: 383 | result = toStringRaw(o) 384 | theEnv.deleteLocalRef(o) 385 | 386 | proc toStringRaw(o: JVMObject): string = 387 | # This is low level ``toString`` version 388 | if o.isNil: 389 | return "" 390 | toStringRaw(o.obj) 391 | 392 | #################################################################################################### 393 | # Arrays support 394 | 395 | type JVMArray[T] = ref object 396 | arr: jtypedArray[T] 397 | 398 | proc get*[T](arr: JVMArray[T]): jtypedArray[T] = arr.arr 399 | proc jniSig*[T](t: typedesc[JVMArray[T]]): string = "[" & jniSig(T) 400 | proc freeJVMArray[T](a: JVMArray[T]) = 401 | if a.arr != nil and theEnv != nil: 402 | theEnv.deleteGlobalRef(a.arr) 403 | 404 | proc newArray*(T: typedesc, len: int): JVMArray[T] = 405 | checkInit 406 | new(result, freeJVMArray[T]) 407 | let j = callVM theEnv.newArray(T, len.jsize) 408 | result.arr = theEnv.newGlobalRef(j) 409 | theEnv.deleteLocalRef(j) 410 | 411 | proc len*(arr: JVMArray): jsize = 412 | callVM theEnv.GetArrayLength(theEnv, arr.get) 413 | 414 | template genArrayType(typ, arrTyp: typedesc, typName: untyped): untyped = 415 | 416 | # Creation 417 | 418 | type `JVM typName Array`* {.inject.} = JVMArray[typ] 419 | 420 | when `typ` isnot jobject: 421 | proc `newJVM typName Array`*(len: jsize): JVMArray[typ] {.inline, deprecated.} = 422 | newArray(`typ`, len.int) 423 | 424 | else: 425 | 426 | proc `newJVM typName Array`*(len: jsize, cls = JVMClass.getByName("java.lang.Object")): JVMArray[typ] = 427 | checkInit 428 | new(result, freeJVMArray[jobject]) 429 | let j = callVM theEnv.NewObjectArray(theEnv, len, cls.get, nil) 430 | result.arr = theEnv.newGlobalRef(j) 431 | theEnv.deleteLocalRef(j) 432 | 433 | proc newArray*(c: JVMClass, len: int): JVMArray[typ] = 434 | `newJVM typName Array`(len.jsize, c) 435 | 436 | proc newArray*(t: typedesc[JVMObject], len: int): JVMArray[typ] = 437 | `newJVM typName Array`(len.jsize, JVMClass.getByName("java.lang.Object")) 438 | 439 | proc `newJVM typName Array`*(arr: jobject): JVMArray[typ] = 440 | checkInit 441 | new(result, freeJVMArray[typ]) 442 | result.arr = theEnv.newGlobalRef(arr).`arrTyp` 443 | 444 | proc `newJVM typName Array`*(arr: JVMObject): JVMArray[typ] = 445 | `newJVM typName Array`(arr.get) 446 | 447 | proc newArray*(t: typedesc[typ], arr: jobject): JVMArray[typ] = `newJVM typName Array`(arr) 448 | 449 | proc newArray*(t: typedesc[typ], arr: JVMObject): JVMArray[typ] = 450 | `newJVM typName Array`(arr.get) 451 | 452 | proc toJVMObject*(a: JVMArray[typ]): JVMObject = 453 | checkInit 454 | newJVMObject(a.arr.jobject) 455 | 456 | # getters/setters 457 | 458 | proc `get typName Array`*(c: JVMClass, name: cstring): JVMArray[typ] = 459 | checkInit 460 | let j = callVM theEnv.GetStaticObjectField(theEnv, c.get, c.getStaticFieldId(name, seq[`typ`].jniSig.cstring).get) 461 | result = `typ`.newArray(j) 462 | theEnv.deleteLocalRef(j) 463 | 464 | proc `get typName Array`*(o: JVMObject, name: cstring): JVMArray[typ] = 465 | checkInit 466 | let j = callVM theEnv.GetObjectField(theEnv, o.get, o.getFieldId(name, seq[`typ`].jniSig.cstring).get) 467 | result = `typ`.newArray(j) 468 | theEnv.deleteLocalRef(j) 469 | 470 | proc `set typName Array`*(c: JVMClass, name: cstring, arr: JVMArray[typ]) = 471 | checkInit 472 | theEnv.SetStaticObjectField(theEnv, c.get, c.getStaticFieldId(name, seq[`typ`].jniSig.cstring).get, arr.arr) 473 | checkException 474 | 475 | proc `set typName Array`*(o: JVMObject, name: cstring, arr: JVMArray[typ]) = 476 | checkInit 477 | theEnv.SetObjectField(theEnv, o.get, o.getFieldId(name, seq[`typ`].jniSig.cstring).get, arr.arr) 478 | checkException 479 | 480 | 481 | # Array methods 482 | proc `call typName ArrayMethod`*(c: JVMClass, id: JVMMethodID, args: openarray[jvalue] = []): JVMArray[typ] = 483 | checkInit 484 | let a = if args.len == 0: nil else: unsafeAddr args[0] 485 | let j = callVM theEnv.CallStaticObjectMethodA(theEnv, c.get, id.get, a) 486 | result = `typ`.newArray(j) 487 | theEnv.deleteLocalRef(j) 488 | 489 | proc `call typName ArrayMethod`*(c: JVMClass, name, sig: cstring, args: openarray[jvalue] = []): JVMArray[typ] = 490 | `call typName ArrayMethod`(c, c.getStaticMethodId(name, sig), args) 491 | 492 | proc `call typName ArrayMethod`*(o: JVMObject, id: JVMMethodID, args: openarray[jvalue] = []): JVMArray[typ] = 493 | checkInit 494 | let a = if args.len == 0: nil else: unsafeAddr args[0] 495 | let j = callVM theEnv.CallObjectMethodA(theEnv, o.get, id.get, a) 496 | result = `typ`.newArray(j) 497 | theEnv.deleteLocalRef(j) 498 | 499 | proc `call typName ArrayMethod`*(o: JVMObject, name, sig: cstring, args: openarray[jvalue] = []): JVMArray[typ] {.inline.} = 500 | `call typName ArrayMethod`(o, o.getMethodId(name, sig), args) 501 | 502 | genArrayType(jchar, jcharArray, Char) 503 | genArrayType(jbyte, jbyteArray, Byte) 504 | genArrayType(jshort, jshortArray, Short) 505 | genArrayType(jint, jintArray, Int) 506 | genArrayType(jlong, jlongArray, Long) 507 | genArrayType(jfloat, jfloatArray, Float) 508 | genArrayType(jdouble, jdoubleArray, Double) 509 | genArrayType(jboolean, jbooleanArray, Boolean) 510 | genArrayType(jobject, jobjectArray, Object) 511 | 512 | proc `[]`*[T](arr: JVMArray[T], idx: Natural): T = 513 | checkInit 514 | when T is jobject: 515 | callVM theEnv.GetObjectArrayElement(theEnv, arr.get, idx.jsize) 516 | elif T is JVMObject: 517 | (callVM theEnv.GetObjectArrayElement(theEnv, arr.get, idx.jsize)).newJVMObjectConsumingLocalRef 518 | else: 519 | theEnv.getArrayRegion(arr.get, idx.jsize, 1, addr result) 520 | checkException 521 | 522 | proc `[]=`*[T, V](arr: JVMArray[T], idx: Natural, v: V) = 523 | checkInit 524 | theEnv.setArrayRegion(arr.get, idx.jsize, 1, unsafeAddr v) 525 | checkException 526 | 527 | proc `[]=`*(arr: JVMArray[jobject], idx: Natural, v: JVMObject|jobject) = 528 | checkInit 529 | let vv = when v is jobject: v 530 | else: v.get 531 | theEnv.SetObjectArrayElement(theEnv, arr.get, idx.jsize, vv) 532 | checkException 533 | 534 | #################################################################################################### 535 | # Fields accessors generation 536 | proc getField*(c: JVMClass, T: typedesc, id: JVMFieldID): T = 537 | checkInit 538 | when T is JVMObject: 539 | (callVM theEnv.getStaticField(jobject, c.get, id.get)).newJVMObjectConsumingLocalRef 540 | else: 541 | (callVM theEnv.getStaticField(T, c.get, id.get)) 542 | 543 | proc getField*(c: JVMClass, T: typedesc, name: cstring): T {.inline.} = 544 | getField(c, T, c.getStaticFieldId(name, T)) 545 | 546 | proc setField*[T](c: JVMClass, id: JVMFieldID, v: T) = 547 | checkInit 548 | when T is JVMObject: 549 | theEnv.setStaticField(c.get, id.get, v.get) 550 | else: 551 | theEnv.setStaticField(c.get, id.get, v) 552 | checkException 553 | 554 | proc setField*[T](c: JVMClass, name: cstring, v: T) = 555 | setField(c, c.getStaticFieldId(name, T), v) 556 | 557 | proc getField*(o: JVMObject, T: typedesc, id: JVMFieldID): T = 558 | checkInit 559 | when T is JVMObject: 560 | (callVM theEnv.getField(jobject, o.get, id.get)).newJVMObjectConsumingLocalRef 561 | else: 562 | (callVM theEnv.getField(T, o.get, id.get)) 563 | 564 | proc getField*(o: JVMObject, T: typedesc, name: cstring): T = 565 | getField(o, T, o.getFieldId(name, T)) 566 | 567 | proc setField*[T](o: JVMObject, id: JVMFieldID, v: T) = 568 | checkInit 569 | when T is JVMObject: 570 | theEnv.setField(o.get, id.get, v.get) 571 | else: 572 | theEnv.setField(o.get, id.get, v) 573 | checkException 574 | 575 | proc setField*[T](o: JVMObject, name: cstring, v: T) = 576 | setField(o, o.getFieldId(name, T), v) 577 | 578 | template genField(typ: typedesc, typName: untyped): untyped = 579 | proc `get typName`*(c: JVMClass, id: JVMFieldID): typ {.inline.} = 580 | getField(c, typ, id) 581 | 582 | proc `get typName`*(c: JVMClass, name: string): typ {.inline.} = 583 | getField(c, typ, name) 584 | 585 | proc `set typName`*(c: JVMClass, id: JVMFieldID, v: typ) {.inline.} = 586 | setField(c, id, v) 587 | 588 | proc `set typName`*(c: JVMClass, name: string, v: typ) {.inline.} = 589 | setField(c, name, v) 590 | 591 | proc `get typName`*(o: JVMObject, id: JVMFieldID): typ {.inline.} = 592 | getField(o, typ, id) 593 | 594 | proc `get typName`*(o: JVMObject, name: string): typ {.inline.} = 595 | getField(o, typ, name) 596 | 597 | proc `set typName`*(o: JVMObject, id: JVMFieldID, v: typ) {.inline.} = 598 | setField(o, id, v) 599 | 600 | proc `set typName`*(o: JVMObject, name: string, v: typ) {.inline.} = 601 | setField(o, name, v) 602 | 603 | 604 | genField(JVMObject, Object) 605 | genField(jchar, Char) 606 | genField(jbyte, Byte) 607 | genField(jshort, Short) 608 | genField(jint, Int) 609 | genField(jlong, Long) 610 | genField(jfloat, Float) 611 | genField(jdouble, Double) 612 | genField(jboolean, Boolean) 613 | 614 | proc callMethod*(c: JVMClass, retType: typedesc, id: JVMMethodId, args: openarray[jvalue] = []): retType = 615 | checkInit 616 | when retType is JVMObject: 617 | (callVM theEnv.callStaticMethod(jobject, c.get, id.get, args)).newJVMObjectConsumingLocalRef 618 | else: 619 | callVM theEnv.callStaticMethod(retType, c.get, id.get, args) 620 | 621 | proc callMethod*(c: JVMClass, retType: typedesc, name, sig: cstring, args: openarray[jvalue] = []): retType {.inline.} = 622 | callMethod(c, retType, c.getStaticMethodId(name, sig), args) 623 | 624 | proc callMethod*(o: JVMObject, retType: typedesc, id: JVMMethodID, args: openarray[jvalue] = []): retType = 625 | checkInit 626 | when retType is JVMObject: 627 | (callVM theEnv.callMethod(jobject, o.get, id.get, args)).newJVMObjectConsumingLocalRef 628 | else: 629 | callVM theEnv.callMethod(retType, o.get, id.get, args) 630 | 631 | proc callMethod*(o: JVMObject, retType: typedesc, name, sig: cstring, args: openarray[jvalue] = []): retType {.inline.} = 632 | callMethod(o, retType, o.getMethodId(name, sig), args) 633 | 634 | proc callMethod*(o: JnimNonVirtual_JVMObject, retType: typedesc, c: JVMClass, id: JVMMethodID, args: openarray[jvalue] = []): retType = 635 | when retType is JVMObject: 636 | (callVM theEnv.callNonVirtualMethod(jobject, o.obj, c.get, id.get, args)).newJVMObjectConsumingLocalRef 637 | else: 638 | callVM theEnv.callNonVirtualMethod(retType, o.obj, c.get, id.get, args) 639 | 640 | #################################################################################################### 641 | # Methods generation 642 | 643 | template genMethod(typ: typedesc, typName: untyped): untyped = 644 | proc `call typName Method`*(c: JVMClass, id: JVMMethodID, args: openarray[jvalue] = []): `typ` {.inline, deprecated.} = 645 | callMethod(c, typ, id, args) 646 | 647 | proc `call typName Method`*(c: JVMClass, name, sig: string, args: openarray[jvalue] = []): `typ` {.inline, deprecated.} = 648 | callMethod(c, typ, name, sig, args) 649 | 650 | proc `call typName Method`*(o: JVMObject, id: JVMMethodID, args: openarray[jvalue] = []): `typ` {.inline, deprecated.} = 651 | callMethod(o, typ, id, args) 652 | 653 | proc `call typName Method`*(o: JVMObject, name, sig: cstring, args: openarray[jvalue] = []): `typ` {.inline, deprecated.} = 654 | callMethod(o, typ, name, sig, args) 655 | 656 | proc `call typName Method`*(o: JnimNonVirtual_JVMObject, c: JVMClass, id: JVMMethodID, args: openarray[jvalue] = []): `typ` {.inline, deprecated.} = 657 | callMethod(o, typ, c, id, args) 658 | 659 | genMethod(JVMObject, Object) 660 | genMethod(jchar, Char) 661 | genMethod(jbyte, Byte) 662 | genMethod(jshort, Short) 663 | genMethod(jint, Int) 664 | genMethod(jlong, Long) 665 | genMethod(jfloat, Float) 666 | genMethod(jdouble, Double) 667 | genMethod(jboolean, Boolean) 668 | 669 | #################################################################################################### 670 | # Helpers 671 | 672 | proc getJVMException*(ex: JavaException): JVMObject = 673 | ex.ex 674 | 675 | proc toJVMObject*(s: string): JVMObject {.inline.} = 676 | newJVMObject(s) 677 | 678 | type JPrimitiveType = jint | jfloat | jboolean | jdouble | jshort | jlong | jchar | jbyte 679 | 680 | proc toJVMObject*[T](a: openarray[T]): JVMObject = 681 | when T is JVMObject: 682 | var arr = JVMObject.newArray(a.len) 683 | for i, v in a: 684 | arr[i] = v 685 | result = arr.toJVMObject 686 | elif compiles(toJVMObject(a[0])): 687 | var arr = JVMObject.newArray(a.len) 688 | for i, v in a: 689 | arr[i] = v.toJVMObject 690 | result = arr.toJVMObject 691 | elif T is JPrimitiveType: 692 | var arr = T.newArray(a.len) 693 | for i, v in a: 694 | arr[i] = v 695 | result = arr.toJVMObject 696 | else: 697 | {.error: "define toJVMObject method for the openarray element type".} 698 | 699 | template jarrayToSeqImpl[T](arr: jarray, res: var seq[T]) = 700 | checkInit 701 | if arr == nil: 702 | return 703 | let length = theEnv.GetArrayLength(theEnv, arr) 704 | res = newSeq[T](length.int) 705 | when T is JPrimitiveType: 706 | type TT = T 707 | if length != 0: 708 | theEnv.getArrayRegion(jtypedArray[TT](arr), 0, length, addr(res[0])) 709 | elif T is JVMObject: 710 | type TT = T 711 | for i in 0..", prototype: Prototype(ret: "V")), 214 | access: {Static, Constructor}, 215 | code: Code( 216 | registers: 2, ins: 0, outs: 1, instrs: @[ 217 | # System.loadLibrary(libname) 218 | const_string(0, libname), 219 | invoke_static(0, 220 | Method(class: `System.repr`, name: "loadLibrary", prototype: Prototype(ret: "V", params: @[`String.repr`]))), 221 | return_void(), 222 | ]) 223 | ) 224 | ])) 225 | dex.classes.add ClassDef(class: `NimObject.repr`, access: {Public, Interface}, superclass: some(`Object.repr`)) 226 | """ 227 | 228 | let 229 | class = dexClass(jnimPackageName & ".Jnim$" & className) 230 | super = if parentClass.len != 0: dexClass(parentClass) else: Object 231 | field = quot"""Field(class: `class.repr`, typ: "J", name: `PointerFieldName.repr`)""" 232 | Throws = dexClass"dalvik.annotation.Throws" 233 | Throwable = dexClass"java.lang.Throwable" 234 | dexGlue.add quot""" 235 | dex.classes.add ClassDef(class: `class.repr`, access: {Public, Static}, superclass: some(`super.repr`), 236 | interfaces: @`(NimObject & interfaces.map(dexClass)).repr`, 237 | class_data: ClassData( 238 | instance_fields: @[ 239 | # private long PointerFieldName; 240 | EncodedField(f: `field`, access: {Private})], 241 | direct_methods: @[ 242 | EncodedMethod( 243 | m: Method(class: `class.repr`, name: "", prototype: Prototype(ret: "V")), 244 | access: {Static, Constructor}, 245 | code: Code( 246 | registers: 2, ins: 0, outs: 1, instrs: @[ 247 | # System.loadLibrary(libname) 248 | const_string(0, libname), 249 | invoke_static(0, 250 | Method(class: `System.repr`, name: "loadLibrary", prototype: Prototype(ret: "V", params: @[`String.repr`]))), 251 | return_void(), 252 | ]) 253 | ), 254 | EncodedMethod( 255 | m: Method(class: `class.repr`, name: `FinalizerName.repr`, prototype: Prototype(ret: "V", params: @["J"])), 256 | access: {Static, Public, Native}, 257 | code: nil 258 | ), 259 | # private native void 'InitializerName'(); 260 | EncodedMethod( 261 | m: Method(class: `class.repr`, name: `InitializerName.repr`, prototype: Prototype(ret: "V", params: @[])), 262 | access: {Private, Native}, 263 | code: nil 264 | ), 265 | ], 266 | virtual_methods: @[ 267 | # protected void finalize() throws Throwable { super.finalize(); FinalizerName(PointerFieldName); PointerFieldName = 0; } 268 | EncodedMethod( 269 | m: Method(class: `class.repr`, name: "finalize", prototype: Prototype(ret: "V")), 270 | access: {Public}, 271 | annotations: @[ 272 | (VisSystem, EncodedAnnotation(typ: `Throws.repr`, elems: @[ 273 | AnnotationElement(name: "value", value: EVArray(@[ 274 | EVType(`Throwable.repr`), 275 | ])) 276 | ]))], 277 | code: Code( 278 | registers: 3, ins: 1, outs: 2, instrs: @[ 279 | # ins: this 280 | # super.finalize() 281 | invoke_super(2, 282 | Method(class: `super.repr`, name: "finalize", prototype: Prototype(ret: "V"))), 283 | # this.FinalizerName(PointerFieldName) 284 | iget_wide(0, 2, `field`), 285 | invoke_static(0, 1, 286 | Method(class: `class.repr`, name: `FinalizerName.repr`, prototype: Prototype(ret: "V", params: @["J"]))), 287 | # this.PointerFieldName = 0 288 | const_wide_16(0, 0'i16), 289 | iput_wide(0, 2, `field`), 290 | return_void(), 291 | ])), 292 | ], 293 | )) 294 | """ 295 | 296 | func typ(javaType: string): string = 297 | case javaType 298 | of "void": "V" 299 | of "boolean": "Z" 300 | of "byte": "B" 301 | of "char": "C" 302 | of "int": "I" 303 | of "long": "J" 304 | of "float": "F" 305 | of "double": "D" 306 | of "short": "S" 307 | of "String": dexClass"java.lang.String" # TODO: do we need this, or we already get java.lang.String? 308 | of "Object": dexClass"java.lang.Object" 309 | of "Class": dexClass"java.lang.Class" 310 | of "Throwable": dexClass"java.lang.Throwable" 311 | else: dexClass(javaType) 312 | 313 | for m in methodDefs: 314 | let args = m.argTypes.map(typ) 315 | if not m.isConstr: 316 | dexGlue.add quot""" 317 | dex.classes[^1].class_data.virtual_methods.add EncodedMethod( 318 | m: Method(class: `class.repr`, name: `m.name.repr`, 319 | prototype: Prototype(ret: `typ(m.retType).repr`, params: @`args.repr`)), 320 | access: {Public, Native}, code: nil) 321 | """ 322 | else: 323 | # FIXME: handle all wide types, not only "J" 324 | func width(typ: string): int = 325 | if typ[0] in {'J'}: 2 else: 1 326 | let 327 | nregs = args.map(width).foldl(a + b, 1) # extra 1 accounts for 'this' 328 | prototype = quot"""Prototype(ret: "V", params: @`args.repr`)""" 329 | # public `class`(`args...`) { super(`args...`); InitializerName(); } /* a constructor */ 330 | dexGlue.add quot""" 331 | dex.classes[^1].class_data.direct_methods.add EncodedMethod( 332 | m: Method(class: `class.repr`, name: "", prototype: `prototype`), 333 | access: {Public, Constructor}, code: Code( 334 | registers: `nregs.repr`.uint16, ins: `nregs.repr`.uint16, outs: `nregs.repr`.uint16, instrs: @[ 335 | newDexInvoke(0x70, `nregs.repr`, 0, Method(class: `super.repr`, name: "", prototype: `prototype`)), 336 | newDexInvoke(0x70, 1, 0, Method(class: `class.repr`, name: `InitializerName.repr`, prototype: Prototype(ret: "V", params: @[]))), 337 | return_void(), 338 | ])) 339 | """ 340 | 341 | macro jnimDexWrite*(genDex: static[string] = "gen_dex.nim"): untyped = 342 | writeFile(genDex, """ 343 | import options, os, strutils 344 | import dali 345 | 346 | if paramCount() != 2: 347 | stderr.write("error: bad number of arguments\n") 348 | stderr.write("USAGE: gen_dex CLASSES.DEX LIBFOOBAR.SO\n") 349 | quit(1) 350 | var 351 | dexfile = paramStr(1) 352 | libname = paramStr(2) 353 | libname.removePrefix("lib") 354 | libname.removeSuffix(".so") 355 | 356 | proc newDexInvoke(opcode: uint8, nregs, reg0: int, m: Method): Instr = 357 | if nregs <= 5: # Dalvik instruction format 35c 358 | func reg(n: int): Arg = 359 | if n < nregs: RegX(uint4(reg0+n)) else: RawX(0) 360 | newInstr( 361 | opcode, RawX(uint4(nregs)), reg(4), 362 | MethodXXXX(m), 363 | reg(1), reg(0), reg(3), reg(2)) 364 | else: # Dalvik instruction format 3rc 365 | # FIXME: jnimGenDex support for constructors with many params not tested 366 | newInstr( 367 | opcode, RawXX(uint8(nregs)), 368 | MethodXXXX(m), 369 | RawXXXX(uint16(reg0))) 370 | 371 | let dex = newDex() 372 | $1 373 | writeFile(dexfile, dex.render) 374 | """ % dexGlue.join"") 375 | 376 | macro genJexportGlue(className, parentClass: static[string], interfaces: static[seq[string]], isPublic: static[bool], methodDefs: static[seq[MethodDescr]], staticSection, emitSection: static[string]): untyped = 377 | # echo treeRepr(a) 378 | when defined(jnimGenDex): 379 | genDexGlue(className, parentClass, interfaces, isPublic, methodDefs, staticSection, emitSection) 380 | else: 381 | genJavaGlue(className, parentClass, interfaces, isPublic, methodDefs, staticSection, emitSection) 382 | 383 | proc varargsToSeqStr(args: varargs[string]): seq[string] {.compileTime.} = @args 384 | proc varargsToSeqMethodDef(args: varargs[MethodDescr]): seq[MethodDescr] {.compileTime.} = @args 385 | 386 | proc jniFqcn*(T: type[void]): string = "void" 387 | proc jniFqcn*(T: type[jint]): string = "int" 388 | proc jniFqcn*(T: type[jlong]): string = "long" 389 | proc jniFqcn*(T: type[jfloat]): string = "float" 390 | proc jniFqcn*(T: type[jdouble]): string = "double" 391 | proc jniFqcn*(T: type[jboolean]): string = "boolean" 392 | proc jniFqcn*(T: type[string]): string = "String" 393 | 394 | template nimTypeToJNIType(T: type[int32]): type = jint 395 | template nimTypeToJNIType(T: type[int64]): type = jlong 396 | template nimTypeToJNIType(T: type[JVMObject]): type = jobject 397 | template nimTypeToJNIType(T: type[string]): type = jstring 398 | template nimTypeToJNIType(T: type[jboolean]): type = jboolean 399 | 400 | 401 | template jniValueToNim(e: JNIEnvPtr, v: jint, T: type[int32]): int32 = int32(v) 402 | template jniValueToNim(e: JNIEnvPtr, v: jstring, T: type[string]): string = $v 403 | template jniValueToNim(e: JNIEnvPtr, v: jobject, T: type[JVMObject]): auto = 404 | jniObjectToNimObj(e, v, T) 405 | 406 | 407 | template nimValueToJni(e: JNIEnvPtr, v: int32, T: type[jint]): jint = jint(v) 408 | template nimValueToJni(e: JNIEnvPtr, v: int64, T: type[jlong]): jlong = jlong(v) 409 | template nimValueToJni(e: JNIEnvPtr, v: string, T: type[jstring]): jstring = e.NewStringUTF(e, v) 410 | template nimValueToJni[T](e: JNIEnvPtr, v: T, V: type[void]) = v 411 | template nimValueToJni(e: JNIEnvPtr, v: jboolean, T: type[jboolean]): jboolean = v 412 | 413 | 414 | template jniObjectToNimObj*(e: JNIEnvPtr, v: jobject, T: type[JVMObject]): auto = 415 | T.fromJObject(v) 416 | 417 | template constSig(args: varargs[string]): cstring = static(cstring(args.join())) 418 | 419 | proc raiseJVMException(e: JNIEnvPtr) {.noreturn.} = 420 | checkJVMException(e) # This should raise 421 | doAssert(false, "unreachable") 422 | 423 | proc getNimDataFromJObject(e: JNIEnvPtr, j: jobject): RootRef = 424 | let clazz = e.GetObjectClass(e, j) 425 | if unlikely clazz.isNil: raiseJVMException(e) 426 | let fid = e.GetFieldID(e, clazz, PointerFieldName, "J") 427 | if unlikely fid.isNil: raiseJVMException(e) 428 | 429 | e.deleteLocalRef(clazz) 430 | result = cast[RootRef](e.GetLongField(e, j, fid)) 431 | 432 | proc setNimDataToJObject(e: JNIEnvPtr, j: jobject, clazz: JClass, o: RootRef) = 433 | let fid = e.GetFieldID(e, clazz, PointerFieldName, "J") 434 | if unlikely fid.isNil: raiseJVMException(e) 435 | GC_ref(o) 436 | e.SetLongField(e, j, fid, cast[jlong](o)) 437 | 438 | proc finalizeJobject(e: JNIEnvPtr, j: jobject, p: jlong) {.exportc: "Java_" & jnimPackageName.replace('.', '_') & "_Jnim__10", dynlib, cdecl.} = 439 | let p = cast[RootRef](p) 440 | if not p.isNil: 441 | GC_unref(p) 442 | 443 | proc implementConstructor(p: NimNode, className, classSig: string, sig: NimNode): NimNode = 444 | let iClazz = ident"clazz" 445 | let classIdent = ident(className) 446 | result = p 447 | p.params[0] = classIdent # Set result type to className 448 | p.params.insert(1, newIdentDefs(ident"this", newTree(nnkBracketExpr, ident"typedesc", classIdent))) # First arg needs to be typedesc[className] 449 | 450 | var args = newTree(nnkBracket) 451 | for identDef in p.params[2..^1]: 452 | for name in identDef[0..^3]: # [name1, ..., type, defaultValue] 453 | args.add(newCall("toJValue", name)) 454 | 455 | p.body = quote do: 456 | var `iClazz` {.global.}: JVMClass 457 | if unlikely `iClazz`.isNil: 458 | `iClazz` = JVMClass.getByFqcn(`classSig`) 459 | 460 | let inst = `iClazz`.newObjectRaw(`sig`, `args`) 461 | when compiles(result.data): 462 | let data = cast[type(result.data)](getNimDataFromJObject(theEnv, inst)) 463 | result = fromJObjectConsumingLocalRef(`classIdent`, inst) 464 | when compiles(result.data): 465 | result.data = data 466 | 467 | when compileOption("threads"): 468 | var gcInited {.threadVar.}: bool 469 | 470 | proc updateStackBottom() {.inline.} = 471 | when not defined(gcDestructors): 472 | var a {.volatile.}: int 473 | nimGC_setStackBottom(cast[pointer](cast[uint](addr a))) 474 | when compileOption("threads") and not compileOption("tlsEmulation"): 475 | if not gcInited: 476 | gcInited = true 477 | setupForeignThreadGC() 478 | 479 | var nativeMethodDescs: seq[tuple[classSig: cstring, nativeMethods: seq[JNINativeMethod]]] 480 | 481 | proc nativeMethodDef(name, signautre: cstring, fnPtr: pointer): JNINativeMethod {.inline.} = 482 | JNINativeMethod(name: name, signature: signautre, fnPtr: fnPtr) 483 | 484 | proc registerNativeMethods(classSig: cstring, nativeMethods: varargs[JNINativeMethod]) = 485 | if nativeMethods.len != 0: 486 | nativeMethodDescs.add((classSig, @nativeMethods)) 487 | 488 | # Register the finalizer 489 | registerNativeMethods(static(jnimPackageName.replace(".", "/") & "/Jnim"), nativeMethodDef("_0", "(J)V", finalizeJobject)) 490 | 491 | proc registerNativeMethods*() = 492 | for i in 0 .. nativeMethodDescs.high: 493 | let cl = theEnv.findClass(nativeMethodDescs[i].classSig) 494 | if not cl.isNil: 495 | let r = callVM theEnv.RegisterNatives(theEnv, cl, unsafeAddr nativeMethodDescs[i].nativeMethods[0], nativeMethodDescs[i].nativeMethods.len.jint) 496 | assert(r == 0) 497 | theEnv.deleteLocalRef(cl) 498 | nativeMethodDescs = @[] 499 | 500 | macro jexport*(a: varargs[untyped]): untyped = 501 | var (className, parentClass, interfaces, body, isPublic) = extractArguments(a) 502 | let classNameIdent = newIdentNode(className) 503 | let clazzIdent = ident"clazz" 504 | let classSig = jnimPackageName.replace(".", "/") & "/Jnim$" & className 505 | 506 | let nonVirtualClassNameIdent = ident("JnimNonVirtual_" & className) 507 | 508 | let constructors = newNimNode(nnkStmtList) 509 | 510 | result = newNimNode(nnkStmtList) 511 | result.add quote do: 512 | 513 | proc jniFqcn*(t: type[`classNameIdent`]): string = "Jnim." & `className` 514 | 515 | proc jniObjectToNimObj*(e: JNIEnvPtr, v: jobject, T: typedesc[`classNameIdent`]): `classNameIdent` = 516 | result = T.fromJObject(v) 517 | when compiles(result.data): 518 | result.data = cast[type(result.data)](getNimDataFromJObject(e, v)) 519 | 520 | var parentFq: NimNode 521 | if parentClass.len != 0: 522 | parentFq = newCall("jniFqcn", newIdentNode(parentClass)) 523 | let nonVirtualParentClassNameIdent = ident("JnimNonVirtual_" & parentClass) 524 | result.add quote do: 525 | type `nonVirtualClassNameIdent` {.used.} = object of `nonVirtualParentClassNameIdent` 526 | else: 527 | parentFq = newLit("") 528 | result.add quote do: 529 | type `nonVirtualClassNameIdent` {.used.} = object of JnimNonVirtual_JVMObject 530 | 531 | result.add quote do: 532 | proc super*(v: `classNameIdent`): `nonVirtualClassNameIdent` = 533 | assert(not v.getNoCreate.isNil) 534 | `nonVirtualClassNameIdent`(obj: v.getNoCreate) 535 | 536 | var inter = newCall(bindSym"varargsToSeqStr") 537 | for i in interfaces: 538 | inter.add(newCall("jniFqcn", newIdentNode(i))) 539 | 540 | var nativeMethodsRegistration = newCall(bindSym"registerNativeMethods", newLit(classSig)) 541 | 542 | var staticSection = newLit("") 543 | var emitSection = newLit("") 544 | 545 | var methodDefs = newCall(bindSym"varargsToSeqMethodDef") 546 | for m in body: 547 | case m.kind 548 | of nnkDiscardStmt: discard 549 | of nnkProcDef: 550 | let name = $m.name 551 | let isConstr = name == "new" 552 | 553 | let params = m.params 554 | 555 | var thunkParams = newSeq[NimNode]() 556 | let thunkName = ident(JniExportedFunctionPrefix & className & "_" & $m.name) 557 | var thunkCall = newCall(m.name) 558 | let envName = newIdentNode("jniEnv") 559 | 560 | # Prepare for jexportAux call 561 | var retType: NimNode 562 | if params[0].kind == nnkEmpty: 563 | retType = newLit("void") 564 | thunkParams.add(newIdentNode("void")) 565 | else: 566 | retType = newCall("jniFqcn", params[0]) 567 | thunkParams.add(newCall(bindSym"nimTypeToJNIType", copyNimTree(params[0]))) 568 | 569 | thunkParams.add(newIdentDefs(envName, newIdentNode("JNIEnvPtr"))) 570 | thunkParams.add(newIdentDefs(ident"this", ident"jobject")) 571 | 572 | let noinlineIdent = ident"noinline" 573 | let noinlineVar = ident"noinlinep" 574 | let noinlineCall = newCall(noinlineVar, envName, ident"this") 575 | 576 | var thisRef = nskLet.genSym"thisRef" 577 | thunkCall.add(thisRef) 578 | 579 | var sig = newCall(bindSym"constSig") 580 | sig.add(newLit("(")) 581 | 582 | let argTypes = newCall(bindSym"varargsToSeqStr") 583 | for i in 1 ..< params.len: 584 | for j in 0 .. params[i].len - 3: 585 | let paramName = params[i][j] 586 | let pt = params[i][^2] 587 | let thunkParamType = newCall(bindSym"nimTypeToJniType", copyNimTree(pt)) 588 | argTypes.add(newCall("jniFqcn", pt)) 589 | sig.add(newCall("jniSig", copyNimTree(pt))) 590 | thunkParams.add(newIdentDefs(copyNimTree(paramName), thunkParamType)) 591 | thunkCall.add(newCall(bindSym"jniValueToNim", envName, copyNimTree(paramName), copyNimTree(pt))) 592 | noinlineCall.add(copyNimTree(paramName)) 593 | let md = newCall(bindSym"initMethodDescr", newLit($m.name), retType, argTypes) 594 | methodDefs.add(md) 595 | 596 | sig.add(newLit(")")) 597 | if params[0].kind == nnkEmpty: 598 | sig.add(newLit("V")) 599 | else: 600 | sig.add(newCall("jniSig", params[0])) 601 | 602 | if not isConstr: 603 | nativeMethodsRegistration.add( 604 | newCall(bindSym"nativeMethodDef", newLit($m.name), sig, thunkName)) 605 | 606 | # Emit the definition as is 607 | m.params.insert(1, newIdentDefs(ident"this", classNameIdent)) 608 | 609 | result.add(m) 610 | 611 | thunkCall = newCall(bindSym"nimValueToJni", envName, thunkCall, copyNimTree(thunkParams[0])) 612 | 613 | let noinlineProc = newProc(noinlineIdent, thunkParams) 614 | noinlineProc.addPragma(newIdentNode("nimcall")) 615 | noinlineProc.body = quote do: 616 | let `thisRef` = theEnv.jniObjectToNimObj(this, type(`classNameIdent`)) 617 | `thunkCall` 618 | 619 | let thunk = newProc(thunkName, thunkParams) 620 | thunk.addPragma(newIdentNode("cdecl")) 621 | thunk.addPragma(newIdentNode("dynlib")) 622 | thunk.addPragma(newIdentNode("exportc")) # Allow jni runtime to discover the functions 623 | thunk.body = quote do: 624 | # TODO: somehow pair this with a matching tearDownForeignThreadGC when necessary 625 | updateStackBottom() 626 | if theEnv.isNil: theEnv = `envName` 627 | `noinlineProc` 628 | var `noinlineVar` {.volatile.} = `noinlineIdent` 629 | `noinlineCall` 630 | result.add(thunk) 631 | else: 632 | # implement constructor 633 | constructors.add(implementConstructor(m, className, classSig, sig)) 634 | 635 | of nnkCommand: 636 | case $m[0] 637 | of "staticSection": 638 | staticSection = m[1] 639 | of "emit": 640 | emitSection = m[1] 641 | else: 642 | echo "Unexpected AST: ", repr(m) 643 | assert(false) 644 | 645 | else: 646 | echo "Unexpected AST: ", repr(m) 647 | assert(false) 648 | 649 | result.add newCall(bindSym"genJexportGlue", newLit(className), parentFq, inter, newLit(isPublic), methodDefs, staticSection, emitSection) 650 | 651 | block: # Initializer thunk 652 | let iClazz = ident"clazz" 653 | let classIdent = ident(className) 654 | let thunkName = ident(JniExportedFunctionPrefix & className & "_" & InitializerName.replace("_", "_1")) 655 | nativeMethodsRegistration.add( 656 | newCall(bindSym"nativeMethodDef", newLit(InitializerName), newLit("()V"), thunkName)) 657 | result.add quote do: 658 | proc `thunkName`(jniEnv: JNIEnvPtr, this: jobject) {.exportc, dynlib, cdecl.} = 659 | var `iClazz` {.global.}: JVMClass 660 | if unlikely `iClazz`.isNil: 661 | `iClazz` = JVMClass.getByFqcn(`classSig`) 662 | when compiles(`classIdent`.data): 663 | let data = new(type(`classIdent`.data)) 664 | setNimDataToJObject(jniEnv, this, `iClazz`.get, cast[RootRef](data)) 665 | 666 | result.add(constructors) 667 | result.add(nativeMethodsRegistration) 668 | 669 | # Generate interface converters 670 | for interf in interfaces: 671 | let converterName = newIdentNode("to" & interf) 672 | let interfaceName = newIdentNode(interf) 673 | result.add quote do: 674 | converter `converterName`*(v: `classNameIdent`): `interfaceName` {.inline.} = 675 | cast[`interfaceName`](v) 676 | 677 | # echo repr result 678 | -------------------------------------------------------------------------------- /jnim/private/jni_export_old.nim: -------------------------------------------------------------------------------- 1 | import macros 2 | 3 | import jni_wrapper, jni_api, jni_generator 4 | import ../java/lang except Exception 5 | 6 | type ProxyFunc = proc(env: pointer, obj: RootRef, proxiedThis, meth: jobject, args: jobjectArray): jobject {.cdecl.} 7 | 8 | jclass java.lang.reflect.Method of JVMObject: 9 | proc getName(): string 10 | 11 | proc rawHandleInvocation(env: pointer, clazz: JClass, nimRef, fnPtr: jlong, proxiedThis, meth: jobject, args: jobjectArray): jobject {.cdecl.} = 12 | let o = cast[RootRef](nimRef) 13 | let f = cast[ProxyFunc](fnPtr) 14 | f(env, o, proxiedThis, meth, args) 15 | 16 | proc finalizeInvocationHandler(env: pointer, clazz: JClass, nimRef: jlong) {.cdecl.} = 17 | if nimRef != 0: 18 | let o = cast[RootRef](nimRef) 19 | GC_unref(o) 20 | 21 | proc getHandlerClass(): JClass = 22 | checkInit 23 | result = findClass(theEnv, "io/github/vegansk/jnim/NativeInvocationHandler") 24 | if result.pointer.isNil: 25 | theEnv.ExceptionClear(theEnv) 26 | result = findClass(theEnv, "NativeInvocationHandler") 27 | if result.pointer.isNil: 28 | theEnv.ExceptionClear(theEnv) 29 | raise newException(Exception, "invalid jnim integration, NativeInvocationHandler not found") 30 | 31 | var nativeMethods: array[2, JNINativeMethod] 32 | nativeMethods[0].name = "i" 33 | nativeMethods[0].signature = "(JJLjava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;" 34 | nativeMethods[0].fnPtr = cast[pointer](rawHandleInvocation) 35 | nativeMethods[1].name = "f" 36 | nativeMethods[1].signature = "(J)V" 37 | nativeMethods[1].fnPtr = cast[pointer](finalizeInvocationHandler) 38 | 39 | let r = callVM theEnv.RegisterNatives(theEnv, result, addr nativeMethods[0], nativeMethods.len.jint) 40 | assert(r == 0) 41 | 42 | proc makeProxy*(clazz: JClass, o: RootRef, fn: ProxyFunc): jobject = 43 | let handlerClazz = getHandlerClass() 44 | 45 | GC_ref(o) 46 | 47 | var mkArgs: array[3, jvalue] 48 | mkArgs[0].l = cast[jobject](clazz) 49 | mkArgs[1].j = cast[jlong](o) 50 | mkArgs[2].j = cast[jlong](fn) 51 | 52 | let mkId = callVM theEnv.GetStaticMethodID(theEnv, handlerClazz, "m", "(Ljava/lang/Class;JJ)Ljava/lang/Object;") 53 | assert(mkId != nil) 54 | 55 | result = callVM theEnv.CallStaticObjectMethodA(theEnv, handlerClazz, mkId, addr mkArgs[0]) 56 | assert(result != nil) 57 | theEnv.DeleteLocalRef(theEnv, cast[jobject](handlerClazz)) 58 | 59 | template makeProxy*[T](javaInterface: typedesc, o: ref T, fn: proc(env: pointer, obj: ref T, proxiedThis, meth: jobject, args: jobjectArray): jobject {.cdecl.}): untyped = 60 | let clazz = javaInterface.getJVMClassForType() 61 | javaInterface.fromJObject(makeProxy(clazz.get(), cast[RootRef](o), cast[ProxyFunc](fn))) 62 | 63 | ################################################################################ 64 | 65 | 66 | proc getMethodName(m: jobject): string {.inline.} = 67 | let m = Method.fromJObject(m) 68 | result = m.getName() 69 | m.free() 70 | 71 | template objToVal(typ: typedesc, valIdent: untyped, o: jobject): untyped = 72 | let ob = typ.fromJObject(o) 73 | let res = valIdent(ob) 74 | ob.free() 75 | res 76 | 77 | proc getArg(t: typedesc, args: jobjectArray, i: int): t {.inline.} = 78 | let a = theEnv.GetObjectArrayElement(theEnv, args, i.jint) 79 | when t is jobject: a 80 | elif t is string: $cast[jstring](a) 81 | elif t is jint: objToVal(Number, intValue, a) 82 | elif t is jfloat: objToVal(Number, floatValue, a) 83 | elif t is jdouble: objToVal(Number, doubleValue, a) 84 | elif t is jlong: objToVal(Number, longValue, a) 85 | elif t is jshort: objToVal(Number, shortValue, a) 86 | elif t is jbyte: objToVal(Number, byteValue, a) 87 | elif t is jboolean: objToVal(Boolean, booleanValue, a) 88 | elif t is JVMObject: t.fromJObject(a) 89 | else: {.error: "Dont know how to convert type".} 90 | 91 | proc toJObject(r: JVMObject): jobject = 92 | result = r.get() 93 | r.setObj(nil) # Release the reference 94 | 95 | proc toJObject(i: jint | jfloat | jdouble | jlong | jshort | jbyte): jobject = toWrapperType(i).toJObject() 96 | proc toJObject(i: jboolean): jobject = toWrapperType(i).toJObject() 97 | proc toJObject(i: string): jobject = newJVMObject(i).toJObject() 98 | 99 | proc makeCallForProc(p: NimNode): NimNode = 100 | result = newCall(p.name) 101 | result.add(ident("obj")) 102 | for i in 2 ..< p.params.len: 103 | result.add(newCall(bindSym "getArg", p.params[i][1], ident "args", newLit(i - 2))) 104 | if p.params[0].kind != nnkEmpty: 105 | result = newAssignment(ident("result"), newCall(bindSym "toJObject", result)) 106 | 107 | proc makeDispatcherImpl(typ: NimNode, name: NimNode, body: NimNode): NimNode = 108 | let methid = ident("meth") 109 | result = newProc(name, [ident("jobject"), 110 | newIdentDefs(ident("env"), ident("pointer")), 111 | newIdentDefs(ident("obj"), typ), 112 | newIdentDefs(ident("proxiedThis"), ident("jobject")), 113 | newIdentDefs(methid, ident("jobject")), 114 | newIdentDefs(ident("args"), ident("jobjectArray"))]) 115 | result.addPragma(ident("cdecl")) 116 | 117 | result.body = body 118 | 119 | let caseStmt = newNimNode(nnkCaseStmt).add(newCall(bindSym "getMethodName", methid)) 120 | 121 | for p in body: 122 | caseStmt.add(newNimNode(nnkOfBranch).add(newLit($p.name), makeCallForProc(p))) 123 | 124 | result.body.add(caseStmt) 125 | 126 | macro implementDispatcher*(typ: untyped, name: untyped, body: untyped): untyped = 127 | result = makeDispatcherImpl(typ, name, body) 128 | -------------------------------------------------------------------------------- /jnim/private/jni_generator.nim: -------------------------------------------------------------------------------- 1 | import jni_api, strutils, sequtils, macros 2 | 3 | from typetraits import name 4 | 5 | #################################################################################################### 6 | # Module parameters 7 | const CONSTRUCTOR_NAME = "new" 8 | 9 | proc nodeToString(n: NimNode): string = 10 | if n.kind == nnkIdent: 11 | result = $n 12 | elif n.kind == nnkAccQuoted: 13 | result = "" 14 | for s in n: 15 | result &= s.nodeToString 16 | elif n.kind == nnkStrLit: 17 | result = n.strVal 18 | elif n.kind == nnkDotExpr: 19 | result = n[0].nodeToString & "." & n[1].nodeToString 20 | elif n.kind == nnkInfix and n[0].nodeToString == "$": 21 | result = n[1].nodeToString & "$" & n[2].nodeToString 22 | elif n.kind == nnkBracketExpr: 23 | let children = toSeq(n.children) 24 | let params = children[1..^1].map(nodeToString).join(",") 25 | result = "$#[$#]" % [n[0].nodeToString, params] 26 | else: 27 | assert false, "Can't stringify " & $n.kind 28 | 29 | #################################################################################################### 30 | # Types declarations 31 | 32 | type ParamType* = string 33 | type ProcParam* = tuple[ 34 | name: string, 35 | `type`: ParamType 36 | ] 37 | type GenericType* = string 38 | 39 | type 40 | ProcDef* = object 41 | name*: string 42 | jName*: string 43 | isConstructor*: bool 44 | isStatic*: bool 45 | isProp*: bool 46 | isFinal*: bool 47 | isExported*: bool 48 | params*: seq[ProcParam] 49 | retType*: ParamType 50 | genericTypes*: seq[GenericType] 51 | 52 | proc initProcDef(name: string, jName: string, isConstructor, isStatic, isProp, isFinal, isExported: bool, params: seq[ProcParam] = @[], retType = "void", genericTypes: seq[GenericType] = @[]): ProcDef = 53 | ProcDef(name: name, jName: jName, isConstructor: isConstructor, isStatic: isStatic, isProp: isProp, isFinal: isFinal, isExported: isExported, params: params, retType: retType, genericTypes: genericTypes) 54 | 55 | type 56 | ClassDef* = object 57 | name*: string 58 | jName*: string 59 | parent*: string 60 | isExported*: bool 61 | genericTypes*: seq[GenericType] 62 | parentGenericTypes*: seq[GenericType] 63 | 64 | proc initClassDef(name, jName, parent: string, isExported: bool, genericTypes: seq[GenericType] = @[], parentGenericTypes: seq[GenericType] = @[]): ClassDef = 65 | ClassDef(name: name, jName: jName, parent: parent, isExported: isExported, genericTypes: genericTypes, parentGenericTypes: parentGenericTypes) 66 | 67 | #################################################################################################### 68 | # Proc signature parser 69 | 70 | const ProcNamePos = 0 71 | const ProcParamsPos = 3 72 | 73 | proc findNameAndGenerics(n: NimNode): (NimNode, NimNode) = 74 | if n.kind == nnkBracketExpr: 75 | result[0] = n[0] 76 | result[1] = n 77 | elif n.kind == nnkInfix and n[2].kind == nnkBracket: 78 | result[0] = n[1] 79 | result[1] = n[2] 80 | else: 81 | result[0] = n 82 | result[1] = nil 83 | 84 | proc parseGenericsNode(n: NimNode): seq[GenericType] = 85 | if n.isNil: return 86 | 87 | expectKind n, {nnkBracketExpr, nnkBracket} 88 | result = newSeq[GenericType]() 89 | for i in (ord(n.kind)-ord(nnkBracket)).. 0 and n[0].kind != nnkEmpty 139 | let hasParams = n.len > 1 140 | 141 | pd.retType = if hasRet: n[0].nodeToString else: "void" 142 | 143 | pd.params = newSeq[ProcParam]() 144 | if hasParams: 145 | for i in 1.. 0: 407 | let args = newNimNode(nnkBracket) 408 | for p in pd.params: 409 | let pi = ident(p.name) 410 | let q = quote do: 411 | when compiles(toJVMObject(`pi`)): 412 | `pi`.toJVMObject.toJValue 413 | else: 414 | `pi`.toJValue 415 | args.add(q) 416 | result = quote do: 417 | let `argsIdent` = `args` 418 | else: 419 | result = quote do: 420 | template `argsIdent` : untyped = [] 421 | 422 | proc fillGenericParameters(cd: ClassDef, pd: ProcDef, n: NimNode) {.compileTime.} = 423 | # Combines generic parameters from `pd`, `cd` and puts t into proc definition `n` 424 | n[2] = mkGenericParams(collectGenericParameters(cd, pd)) 425 | 426 | template withGCDisabled(body: untyped) = 427 | # Disabling GC is a must on Android (and maybe other platforms) in release 428 | # mode. Otherwise Nim GC may kick in and finalize the JVMObject we're passing 429 | # to JNI call before the actual JNI call is made. That is likely caused 430 | # by release optimizations that prevent Nim GC from "seeing" the JVMObjects 431 | # on the stack after their last usage, even though from the code POV they 432 | # are still here. This template should be used wherever jni references are 433 | # taken from temporary Nim objects. 434 | 435 | when defined(gcDestructors): 436 | body 437 | else: 438 | GC_disable() 439 | body 440 | GC_enable() 441 | 442 | proc generateConstructor(cd: ClassDef, pd: ProcDef, def: NimNode): NimNode = 443 | assert pd.isConstructor 444 | 445 | let sig = getProcSignature(cd, pd) 446 | let cname = cd.jName.newStrLitNode 447 | let ctype = cd.name.ident 448 | let ctypeWithParams = cd.mkType 449 | 450 | result = def.copyNimTree 451 | fillGenericParameters(cd, pd, result) 452 | # Change return type 453 | result.params[0] = ctypeWithParams 454 | # Add first parameter 455 | result.params.insert(1, newIdentDefs(ident"theClassType", cd.mkTypedesc)) 456 | let ai = ident"args" 457 | let args = generateArgs(pd, ai) 458 | result.body = quote do: 459 | checkInit 460 | withGCDisabled: 461 | let clazz = JVMClass.getByName(`cname`) 462 | `args` 463 | fromJObjectConsumingLocalRef(`ctypeWithParams`, newObjectRaw(clazz, toConstCString(`sig`), `ai`)) 464 | 465 | proc generateMethod(cd: ClassDef, pd: ProcDef, def: NimNode): NimNode = 466 | assert(not (pd.isConstructor or pd.isProp)) 467 | 468 | let sig = getProcSignature(cd, pd) 469 | let pname = pd.jName.newStrLitNode 470 | let ctype = cd.name.ident 471 | result = def.copyNimTree 472 | fillGenericParameters(cd, pd, result) 473 | result.pragma = newEmptyNode() 474 | 475 | var objToCall: NimNode 476 | var objToCallIdent: NimNode 477 | var mIdIdent = ident"mId" 478 | 479 | # Add first parameter 480 | if pd.isStatic: 481 | result.params.insert(1, newIdentDefs(ident"theClassType", cd.mkTypedesc)) 482 | objToCallIdent = ident"clazz" 483 | objToCall = quote do: 484 | let `objToCallIdent` = `ctype`.getJVMClassForType 485 | let `mIdIdent` = `objToCallIdent`.getStaticMethodId(`pname`, toConstCString(`sig`)) 486 | 487 | else: 488 | objToCallIdent = ident"this" 489 | result.params.insert(1, newIdentDefs(objToCallIdent, cd.mkType)) 490 | objToCall = quote do: 491 | let `mIdIdent` = `objToCallIdent`.getMethodId(`pname`, toConstCString(`sig`)) 492 | 493 | let retType = parseExpr(pd.retType) 494 | let ai = ident"args" 495 | let args = generateArgs(pd, ai) 496 | result.body = quote do: 497 | withGCDisabled: 498 | `objToCall` 499 | `args` 500 | callMethod(`retType`, `objToCallIdent`, `mIdIdent`, `ai`) 501 | 502 | proc getSuperclass(o: jobject): JVMClass = 503 | let clazz = theEnv.GetObjectClass(theEnv, o) 504 | let sclazz = theEnv.GetSuperclass(theEnv, clazz) 505 | result = newJVMClass(sclazz) 506 | theEnv.deleteLocalRef(clazz) 507 | theEnv.deleteLocalRef(sclazz) 508 | 509 | proc generateNonVirtualMethod(cd: ClassDef, pd: ProcDef, def: NimNode): NimNode = 510 | assert(not (pd.isConstructor or pd.isProp)) 511 | 512 | let sig = getProcSignature(cd, pd) 513 | let pname = pd.jName.newStrLitNode 514 | let ctype = cd.name.ident 515 | result = def.copyNimTree 516 | fillGenericParameters(cd, pd, result) 517 | result.pragma = newEmptyNode() 518 | result.addPragma(ident"used") 519 | 520 | let mIdIdent = ident"mId" 521 | let mClassIdent = ident"clazz" 522 | 523 | # Add first parameter 524 | let thisIdent = ident"this" 525 | result.params.insert(1, newIdentDefs(thisIdent, cd.mkNonVirtualType)) 526 | let objToCall = quote do: 527 | let `mClassIdent` = getSuperclass(`thisIdent`.obj) 528 | let `mIdIdent` = `mClassIdent`.getMethodId(`pname`, toConstCString(`sig`)) 529 | 530 | let retType = parseExpr(pd.retType) 531 | let ai = ident"args" 532 | let args = generateArgs(pd, ai) 533 | result.body = quote do: 534 | withGCDisabled: 535 | `objToCall` 536 | `args` 537 | callNonVirtualMethod(`retType`, `thisIdent`, `mClassIdent`, `mIdIdent`, `ai`) 538 | 539 | proc generateProperty(cd: ClassDef, pd: ProcDef, def: NimNode, isSetter: bool): NimNode = 540 | assert pd.isProp 541 | 542 | let sig = getProcSignature(cd, pd) 543 | let cname = cd.jName.newStrLitNode 544 | let pname = pd.jName.newStrLitNode 545 | let ctype = cd.name.ident 546 | result = def.copyNimTree 547 | fillGenericParameters(cd, pd, result) 548 | result.pragma = newEmptyNode() 549 | result[ProcNamePos] = identEx(pd.isExported, pd.name, isSetter) 550 | var objToCall: NimNode 551 | # Add first parameter 552 | if pd.isStatic: 553 | result.params.insert(1, newIdentDefs(ident"theClassType", cd.mkTypedesc)) 554 | objToCall = quote do: 555 | `ctype`.getJVMClassForType 556 | else: 557 | result.params.insert(1, newIdentDefs(ident"this", cd.mkType)) 558 | objToCall = ident"this" 559 | if isSetter: 560 | result.params.insert(2, newIdentDefs(ident"value", result.params[0])) 561 | result.params[0] = newEmptyNode() 562 | let valType = parseExpr(pd.retType) 563 | var mId: NimNode 564 | if pd.isStatic: 565 | mId = quote do: 566 | `objToCall`.getStaticFieldId(`pname`, `sig`) 567 | else: 568 | mId = quote do: 569 | `objToCall`.getFieldId(`pname`, `sig`) 570 | 571 | if isSetter: 572 | result.body = quote do: 573 | withGCDisabled: 574 | setPropValue(`valType`, `objToCall`, `mId`, value) 575 | else: 576 | result.body = quote do: 577 | getPropValue(`valType`, `objToCall`, `mId`) 578 | 579 | proc generateProc(cd: ClassDef, def: NimNode): NimNode {.compileTime.} = 580 | let pd = parseProcDef(def) 581 | if pd.isConstructor: 582 | result = generateConstructor(cd, pd, def) 583 | elif pd.isProp: 584 | result = newStmtList() 585 | result.add(generateProperty(cd, pd, def, false)) 586 | if not pd.isFinal: 587 | result.add(generateProperty(cd, pd, def, true)) 588 | else: 589 | result = newStmtList() 590 | result.add(generateMethod(cd, pd, def)) 591 | if not pd.isStatic: 592 | result.add(generateNonVirtualMethod(cd, pd, def)) 593 | 594 | proc generateClassImpl(cd: ClassDef, body: NimNode): NimNode {.compileTime.} = 595 | result = newStmtList() 596 | if body.kind == nnkStmtList: 597 | for def in body: 598 | result.add generateProc(cd, def) 599 | else: result.add generateProc(cd, body) 600 | 601 | macro jclass*(head: untyped, body: untyped): untyped = 602 | result = newStmtList() 603 | let cd = parseClassDef(head) 604 | result.add generateClassDef(cd) 605 | result.add generateClassImpl(cd, body) 606 | 607 | macro jclassDef*(head: untyped): untyped = 608 | result = newStmtList() 609 | let cd = parseClassDef(head) 610 | result.add generateClassDef(cd) 611 | 612 | macro jclassImpl*(head: untyped, body: untyped): untyped = 613 | result = newStmtList() 614 | let cd = parseClassDef(head) 615 | result.add generateClassImpl(cd, body) 616 | 617 | 618 | #################################################################################################### 619 | # Operators 620 | 621 | proc instanceOf*[T: JVMObject](obj: JVMObject, t: typedesc[T]): bool = 622 | ## Returns true if java object `obj` is an instance of class, represented 623 | ## by `T`. Behaves the same as `instanceof` operator in Java. 624 | ## **WARNING**: since generic parameters are not represented on JVM level, 625 | ## they are ignored (even though they are required by Nim syntax). This 626 | ## means that the following returns true: 627 | ## .. code-block:: nim 628 | ## let a = ArrayList[Integer].new() 629 | ## # true! 630 | ## a.instanceOf[List[String]] 631 | 632 | instanceOfRaw(obj, T.getJVMClassForType) 633 | 634 | proc jcast*[T: JVMObject](obj: JVMObject): T = 635 | ## Downcast operator for Java objects. 636 | ## Behaves like Java code `(T) obj`. That is: 637 | ## - If java object, referenced by `obj`, is an instance of class, 638 | ## represented by `T` - returns an object of `T` that references 639 | ## the same java object. 640 | ## - Otherwise raises an exception (`ObjectConversionDefect`). 641 | ## **WARNING**: since generic parameters are not represented on JVM level, 642 | ## they are ignored (even though they are required by Nim syntax). This 643 | ## means that the following won't raise an error: 644 | ## .. code-block:: nim 645 | ## let a = ArrayList[Integer].new() 646 | ## # no error here! 647 | ## let b = jcast[List[String]](a) 648 | ## **WARNING**: To simplify reference handling, this implementation directly 649 | ## casts JVMObject to a subtype. This works, since all mapped classes have 650 | ## the same structure, but also breaks Nim's runtime type determination 651 | ## (such as `of` keyword and methods). However, native runtime type 652 | ## determination should not be used with mapped classes anyway. 653 | 654 | if not obj.instanceOf(T): 655 | raise newException( 656 | ObjectConversionDefect, 657 | "Failed to convert " & typetraits.name(obj.type) & 658 | " to " & typetraits.name(T) 659 | ) 660 | # Since it is just a ref 661 | cast[T](obj) 662 | -------------------------------------------------------------------------------- /jnim/private/jni_wrapper.nim: -------------------------------------------------------------------------------- 1 | import os, dynlib, strutils, macros, options 2 | 3 | from jvm_finder import findJVM 4 | 5 | when defined macosx: 6 | {.emit: """ 7 | #include 8 | """.} 9 | {.passL: "-framework CoreFoundation".} 10 | 11 | type 12 | JNIException* = object of CatchableError 13 | 14 | proc newJNIException*(msg: string): ref JNIException = 15 | newException(JNIException, msg) 16 | 17 | template jniAssert*(call: untyped): untyped = 18 | if not `call`: 19 | raise newJNIException(call.astToStr & " is false") 20 | 21 | template jniAssert*(call: untyped, msg: string): untyped = 22 | if not `call`: 23 | raise newJNIException(msg) 24 | 25 | template jniAssertEx*(call: untyped, msg: string): untyped = 26 | if not `call`: 27 | raise newJNIException(msg & " (" & call.astToStr & " is false)") 28 | 29 | template jniCall*(call: untyped): untyped = 30 | let res = `call` 31 | if res != 0.jint: 32 | raise newJNIException(call.astToStr & " returned " & $res) 33 | 34 | template jniCall*(call: untyped, msg: string): untyped = 35 | let res = `call` 36 | if res != 0.jint: 37 | raise newJNIException(msg & " (result = " & $res & ")") 38 | 39 | template jniCallEx*(call: untyped, msg: string): untyped = 40 | let res = `call` 41 | if res != 0.jint: 42 | raise newJNIException(msg & " (" & call.astToStr & " returned " & $res & ")") 43 | 44 | type 45 | jint* = int32 46 | jsize* = jint 47 | jchar* = uint16 48 | jlong* = int64 49 | jshort* = int16 50 | jbyte* = int8 51 | jfloat* = cfloat 52 | jdouble* = cdouble 53 | jboolean* = uint8 54 | 55 | jobject_base {.inheritable, pure.} = object 56 | jobject* = ptr jobject_base 57 | JClass* = ptr object of jobject 58 | jmethodID* = pointer 59 | jfieldID* = pointer 60 | jstring* = ptr object of jobject 61 | jthrowable* = ptr object of jobject 62 | 63 | jarray* = ptr object of jobject 64 | jtypedArray*[T] = ptr object of jarray 65 | 66 | jobjectArray* = jtypedArray[jobject] 67 | jbooleanArray* = jtypedArray[jboolean] 68 | jbyteArray* = jtypedArray[jbyte] 69 | jcharArray* = jtypedArray[jchar] 70 | jshortArray* = jtypedArray[jshort] 71 | jintArray* = jtypedArray[jint] 72 | jlongArray* = jtypedArray[jlong] 73 | jfloatArray* = jtypedArray[jfloat] 74 | jdoubleArray* = jtypedArray[jdouble] 75 | jweak* = jobject 76 | 77 | jvalue* {.union.} = object 78 | z*: jboolean 79 | b*: jbyte 80 | c*: jchar 81 | s*: jshort 82 | i*: jint 83 | j*: jlong 84 | f*: jfloat 85 | d*: jdouble 86 | l*: jobject 87 | 88 | const JVM_TRUE* = 1.jboolean 89 | const JVM_FALSE* = 0.jboolean 90 | 91 | {.pragma: jni, cdecl, gcsafe.} 92 | 93 | type 94 | JNIInvokeInterface* = object 95 | # WARNING: The fields should be defined in exact same order as they are 96 | # defined in jni.h to preserve ABI compatibility. 97 | reserved0: pointer 98 | reserved1: pointer 99 | reserved2: pointer 100 | when defined(not_TARGET_RT_MAC_CFM_and_ppc): # No idea what this means. 101 | cfm_vectors: array[4, pointer] 102 | 103 | DestroyJavaVM*: proc(vm: JavaVMPtr): jint {.jni.} 104 | AttachCurrentThread*: proc(vm: JavaVMPtr, penv: ptr pointer, args: pointer): jint {.jni.} 105 | DetachCurrentThread*: proc(vm: JavaVMPtr): jint {.jni.} 106 | GetEnv*: proc(vm: JavaVMPtr, penv: ptr pointer, version: jint): jint {.jni.} 107 | AttachCurrentThreadAsDaemon*: proc(vm: JavaVMPtr, penv: ptr pointer, args: pointer): jint {.jni.} 108 | 109 | JavaVM* = ptr JNIInvokeInterface 110 | JavaVMPtr* = ptr JavaVM 111 | JavaVMOption* = object 112 | # WARNING: The fields should be defined in exact same order as they are 113 | # defined in jni.h to preserve ABI compatibility. 114 | optionString*: cstring 115 | extraInfo*: pointer 116 | 117 | JavaVMInitArgs* = object 118 | # WARNING: The fields should be defined in exact same order as they are 119 | # defined in jni.h to preserve ABI compatibility. 120 | version*: jint 121 | nOptions*: jint 122 | options*: ptr JavaVMOption 123 | ignoreUnrecognized*: jboolean 124 | 125 | JNINativeInterface* = object 126 | # WARNING: The fields should be defined in exact same order as they are 127 | # defined in jni.h to preserve ABI compatibility. 128 | 129 | reserved0: pointer 130 | reserved1: pointer 131 | reserved2: pointer 132 | reserved3: pointer 133 | 134 | when defined(not_TARGET_RT_MAC_CFM_and_ppc): # No idea what this means. 135 | cfm_vectors: array[225, pointer] 136 | 137 | GetVersion*: proc(env: JNIEnvPtr): jint {.jni.} 138 | 139 | DefineClass*: proc(env: JNIEnvPtr, name: cstring, loader: jobject, buf: ptr jbyte, len: jsize): JClass {.jni.} 140 | FindClass*: proc(env: JNIEnvPtr, name: cstring): JClass {.jni.} 141 | 142 | FromReflectedMethod*: proc(env: JNIEnvPtr, meth: jobject): jmethodID {.jni.} 143 | FromReflectedField*: proc(env: JNIEnvPtr, field: jobject): jfieldID {.jni.} 144 | 145 | ToReflectedMethod*: proc(env: JNIEnvPtr, cls: JClass, methodID: jmethodID, isStatic: jboolean): jobject {.jni.} 146 | 147 | GetSuperclass*: proc(env: JNIEnvPtr, sub: JClass): JClass {.jni.} 148 | IsAssignableFrom*: proc(env: JNIEnvPtr, sub, sup: JClass): jboolean {.jni.} 149 | 150 | ToReflectedField*: proc(env: JNIEnvPtr, cls: JClass, fieldID: jfieldID, isStatic: jboolean): jobject {.jni.} 151 | 152 | Throw*: proc(env: JNIEnvPtr, obj: jthrowable): jint {.jni.} 153 | ThrowNew*: proc(env: JNIEnvPtr, clazz: JClass, msg: cstring): jint {.jni.} 154 | ExceptionOccurred*: proc(env: JNIEnvPtr): jthrowable {.jni.} 155 | ExceptionDescribe*: proc(env: JNIEnvPtr) {.jni.} 156 | ExceptionClear*: proc(env: JNIEnvPtr) {.jni.} 157 | FatalError*: proc(env: JNIEnvPtr, msg: cstring) {.jni.} 158 | 159 | PushLocalFrame*: proc(env: JNIEnvPtr, capacity: jint): jint {.jni.} 160 | PopLocalFrame*: proc(env: JNIEnvPtr, res: jobject): jobject {.jni.} 161 | 162 | NewGlobalRef*: proc(env: JNIEnvPtr, obj: jobject): jobject {.jni.} 163 | DeleteGlobalRef*: proc(env: JNIEnvPtr, obj: jobject) {.jni.} 164 | DeleteLocalRef*: proc(env: JNIEnvPtr, obj: jobject) {.jni.} 165 | IsSameObject*: proc(env: JNIEnvPtr, obj1, obj2: jobject): jboolean {.jni.} 166 | NewLocalRef*: proc(env: JNIEnvPtr, obj: jobject): jobject {.jni.} 167 | EnsureLocalCapacity*: proc(env: JNIEnvPtr, capacity: jint): jint {.jni.} 168 | 169 | AllocObject*: proc(env: JNIEnvPtr, clazz: JClass): jobject {.jni.} 170 | NewObject*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID): jobject {.jni, varargs.} 171 | NewObjectV: pointer # This function utilizes va_list which is not needed in Nim 172 | NewObjectA*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jobject {.jni.} 173 | 174 | GetObjectClass*: proc(env: JNIEnvPtr, obj: jobject): JClass {.jni.} 175 | IsInstanceOf*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass): jboolean {.jni.} 176 | 177 | GetMethodID*: proc(env: JNIEnvPtr, clazz: JClass, name, sig: cstring): jmethodID {.jni.} 178 | 179 | CallObjectMethod*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID): jobject {.jni, varargs.} 180 | CallObjectMethodV: pointer # This function utilizes va_list which is not needed in Nim 181 | CallObjectMethodA*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID, args: ptr jvalue): jobject {.jni.} 182 | 183 | CallBooleanMethod*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID): jboolean {.jni, varargs.} 184 | CallBooleanMethodV: pointer # This function utilizes va_list which is not needed in Nim 185 | CallBooleanMethodA*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID, args: ptr jvalue): jboolean {.jni.} 186 | 187 | CallByteMethod*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID): jbyte {.jni, varargs.} 188 | CallByteMethodV: pointer # This function utilizes va_list which is not needed in Nim 189 | CallByteMethodA*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID, args: ptr jvalue): jbyte {.jni.} 190 | 191 | CallCharMethod*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID): jchar {.jni, varargs.} 192 | CallCharMethodV: pointer # This function utilizes va_list which is not needed in Nim 193 | CallCharMethodA*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID, args: ptr jvalue): jchar {.jni.} 194 | 195 | CallShortMethod*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID): jshort {.jni, varargs.} 196 | CallShortMethodV: pointer # This function utilizes va_list which is not needed in Nim 197 | CallShortMethodA*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID, args: ptr jvalue): jshort {.jni.} 198 | 199 | CallIntMethod*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID): jint {.jni, varargs.} 200 | CallIntMethodV: pointer # This function utilizes va_list which is not needed in Nim 201 | CallIntMethodA*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID, args: ptr jvalue): jint {.jni.} 202 | 203 | CallLongMethod*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID): jlong {.jni, varargs.} 204 | CallLongMethodV: pointer # This function utilizes va_list which is not needed in Nim 205 | CallLongMethodA*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID, args: ptr jvalue): jlong {.jni.} 206 | 207 | CallFloatMethod*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID): jfloat {.jni, varargs.} 208 | CallFloatMethodV: pointer # This function utilizes va_list which is not needed in Nim 209 | CallFloatMethodA*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID, args: ptr jvalue): jfloat {.jni.} 210 | 211 | CallDoubleMethod*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID): jdouble {.jni, varargs.} 212 | CallDoubleMethodV: pointer # This function utilizes va_list which is not needed in Nim 213 | CallDoubleMethodA*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID, args: ptr jvalue): jdouble {.jni.} 214 | 215 | CallVoidMethod*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID) {.jni, varargs.} 216 | CallVoidMethodV: pointer # This function utilizes va_list which is not needed in Nim 217 | CallVoidMethodA*: proc(env: JNIEnvPtr, obj: jobject, methodID: jmethodID, args: ptr jvalue) {.jni.} 218 | 219 | CallNonvirtualObjectMethod*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID): jobject {.jni, varargs.} 220 | CallNonvirtualObjectMethodV: pointer # This function utilizes va_list which is not needed in Nim 221 | CallNonvirtualObjectMethodA*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jobject {.jni.} 222 | 223 | CallNonvirtualBooleanMethod*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID): jboolean {.jni, varargs.} 224 | CallNonvirtualBooleanMethodV: pointer # This function utilizes va_list which is not needed in Nim 225 | CallNonvirtualBooleanMethodA*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jboolean {.jni.} 226 | 227 | CallNonvirtualByteMethod*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID): jbyte {.jni, varargs.} 228 | CallNonvirtualByteMethodV: pointer # This function utilizes va_list which is not needed in Nim 229 | CallNonvirtualByteMethodA*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jbyte {.jni.} 230 | 231 | CallNonvirtualCharMethod*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID): jchar {.jni, varargs.} 232 | CallNonvirtualCharMethodV: pointer # This function utilizes va_list which is not needed in Nim 233 | CallNonvirtualCharMethodA*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jchar {.jni.} 234 | 235 | CallNonvirtualShortMethod*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID): jshort {.jni, varargs.} 236 | CallNonvirtualShortMethodV: pointer # This function utilizes va_list which is not needed in Nim 237 | CallNonvirtualShortMethodA*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jshort {.jni.} 238 | 239 | CallNonvirtualIntMethod*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID): jint {.jni, varargs.} 240 | CallNonvirtualIntMethodV: pointer # This function utilizes va_list which is not needed in Nim 241 | CallNonvirtualIntMethodA*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jint {.jni.} 242 | 243 | CallNonvirtualLongMethod*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID): jlong {.jni, varargs.} 244 | CallNonvirtualLongMethodV: pointer # This function utilizes va_list which is not needed in Nim 245 | CallNonvirtualLongMethodA*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jlong {.jni.} 246 | 247 | CallNonvirtualFloatMethod*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID): jfloat {.jni, varargs.} 248 | CallNonvirtualFloatMethodV: pointer # This function utilizes va_list which is not needed in Nim 249 | CallNonvirtualFloatMethodA*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jfloat {.jni.} 250 | 251 | CallNonvirtualDoubleMethod*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID): jdouble {.jni, varargs.} 252 | CallNonvirtualDoubleMethodV: pointer # This function utilizes va_list which is not needed in Nim 253 | CallNonvirtualDoubleMethodA*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jdouble {.jni.} 254 | 255 | CallNonvirtualVoidMethod*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID) {.jni, varargs.} 256 | CallNonvirtualVoidMethodV: pointer # This function utilizes va_list which is not needed in Nim 257 | CallNonvirtualVoidMethodA*: proc(env: JNIEnvPtr, obj: jobject, clazz: JClass, methodID: jmethodID, args: ptr jvalue) {.jni.} 258 | 259 | GetFieldID*: proc(env: JNIEnvPtr, cls: JClass, name, sig: cstring): jfieldID {.jni.} 260 | 261 | GetObjectField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID): jobject {.jni.} 262 | GetBooleanField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID): jboolean {.jni.} 263 | GetByteField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID): jbyte {.jni.} 264 | GetCharField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID): jchar {.jni.} 265 | GetShortField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID): jshort {.jni.} 266 | GetIntField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID): jint {.jni.} 267 | GetLongField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID): jlong {.jni.} 268 | GetFloatField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID): jfloat {.jni.} 269 | GetDoubleField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID): jdouble {.jni.} 270 | 271 | SetObjectField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID, val: jobject) {.jni.} 272 | SetBooleanField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID, val: jboolean) {.jni.} 273 | SetByteField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID, val: jbyte) {.jni.} 274 | SetCharField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID, val: jchar) {.jni.} 275 | SetShortField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID, val: jshort) {.jni.} 276 | SetIntField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID, val: jint) {.jni.} 277 | SetLongField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID, val: jlong) {.jni.} 278 | SetFloatField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID, val: jfloat) {.jni.} 279 | SetDoubleField*: proc(env: JNIEnvPtr, obj: jobject, fieldId: jfieldID, val: jdouble) {.jni.} 280 | 281 | GetStaticMethodID*: proc(env: JNIEnvPtr, cls: JClass, name, sig: cstring): jmethodID {.jni.} 282 | 283 | CallStaticObjectMethod*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID): jobject {.jni, varargs.} 284 | CallStaticObjectMethodV: pointer # This function utilizes va_list which is not needed in Nim 285 | CallStaticObjectMethodA*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jobject {.jni.} 286 | 287 | CallStaticBooleanMethod*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID): jboolean {.jni, varargs.} 288 | CallStaticBooleanMethodV: pointer # This function utilizes va_list which is not needed in Nim 289 | CallStaticBooleanMethodA*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jboolean {.jni.} 290 | 291 | CallStaticByteMethod*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID): jbyte {.jni, varargs.} 292 | CallStaticByteMethodV: pointer # This function utilizes va_list which is not needed in Nim 293 | CallStaticByteMethodA*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jbyte {.jni.} 294 | 295 | CallStaticCharMethod*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID): jchar {.jni, varargs.} 296 | CallStaticCharMethodV: pointer # This function utilizes va_list which is not needed in Nim 297 | CallStaticCharMethodA*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jchar {.jni.} 298 | 299 | CallStaticShortMethod*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID): jshort {.jni, varargs.} 300 | CallStaticShortMethodV: pointer # This function utilizes va_list which is not needed in Nim 301 | CallStaticShortMethodA*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jshort {.jni.} 302 | 303 | CallStaticIntMethod*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID): jint {.jni, varargs.} 304 | CallStaticIntMethodV: pointer # This function utilizes va_list which is not needed in Nim 305 | CallStaticIntMethodA*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jint {.jni.} 306 | 307 | CallStaticLongMethod*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID): jlong {.jni, varargs.} 308 | CallStaticLongMethodV: pointer # This function utilizes va_list which is not needed in Nim 309 | CallStaticLongMethodA*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jlong {.jni.} 310 | 311 | CallStaticFloatMethod*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID): jfloat {.jni, varargs.} 312 | CallStaticFloatMethodV: pointer # This function utilizes va_list which is not needed in Nim 313 | CallStaticFloatMethodA*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jfloat {.jni.} 314 | 315 | CallStaticDoubleMethod*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID): jdouble {.jni, varargs.} 316 | CallStaticDoubleMethodV: pointer # This function utilizes va_list which is not needed in Nim 317 | CallStaticDoubleMethodA*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID, args: ptr jvalue): jdouble {.jni.} 318 | 319 | CallStaticVoidMethod*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID) {.jni, varargs.} 320 | CallStaticVoidMethodV: pointer # This function utilizes va_list which is not needed in Nim 321 | CallStaticVoidMethodA*: proc(env: JNIEnvPtr, clazz: JClass, methodID: jmethodID, args: ptr jvalue) {.jni.} 322 | 323 | GetStaticFieldID*: proc(env: JNIEnvPtr, cls: JClass, name, sig: cstring): jfieldID {.jni.} 324 | 325 | GetStaticObjectField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID): jobject {.jni.} 326 | GetStaticBooleanField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID): jboolean {.jni.} 327 | GetStaticByteField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID): jbyte {.jni.} 328 | GetStaticCharField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID): jchar {.jni.} 329 | GetStaticShortField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID): jshort {.jni.} 330 | GetStaticIntField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID): jint {.jni.} 331 | GetStaticLongField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID): jlong {.jni.} 332 | GetStaticFloatField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID): jfloat {.jni.} 333 | GetStaticDoubleField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID): jdouble {.jni.} 334 | 335 | SetStaticObjectField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID, val: jobject) {.jni.} 336 | SetStaticBooleanField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID, val: jboolean) {.jni.} 337 | SetStaticByteField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID, val: jbyte) {.jni.} 338 | SetStaticCharField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID, val: jchar) {.jni.} 339 | SetStaticShortField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID, val: jshort) {.jni.} 340 | SetStaticIntField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID, val: jint) {.jni.} 341 | SetStaticLongField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID, val: jlong) {.jni.} 342 | SetStaticFloatField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID, val: jfloat) {.jni.} 343 | SetStaticDoubleField*: proc(env: JNIEnvPtr, obj: JClass, fieldId: jfieldID, val: jdouble) {.jni.} 344 | 345 | NewString*: proc(env: JNIEnvPtr, unicode: ptr jchar, len: jsize): jstring {.jni.} 346 | GetStringLength*: proc(env: JNIEnvPtr, str: jstring): jsize {.jni.} 347 | GetStringChars*: proc(env: JNIEnvPtr, str: jstring, isCopy: ptr jboolean): ptr jchar {.jni.} 348 | ReleaseStringChars*: proc(env: JNIEnvPtr, str: jstring, chars: ptr jchar) {.jni.} 349 | 350 | NewStringUTF*: proc(env: JNIEnvPtr, s: cstring): jstring {.jni.} 351 | GetStringUTFLength*: proc(env: JNIEnvPtr, str: jstring): jsize {.jni.} 352 | GetStringUTFChars*: proc(env: JNIEnvPtr, s: jstring, isCopy: ptr jboolean): cstring {.jni.} 353 | ReleaseStringUTFChars*: proc(env: JNIEnvPtr, s: jstring, cstr: cstring) {.jni.} 354 | 355 | GetArrayLength*: proc(env: JNIEnvPtr, arr: jarray): jsize {.jni.} 356 | 357 | NewObjectArray*: proc(env: JNIEnvPtr, size: jsize, clazz: JClass, init: jobject): jobjectArray {.jni.} 358 | GetObjectArrayElement*: proc(env: JNIEnvPtr, arr: jobjectArray, index: jsize): jobject {.jni.} 359 | SetObjectArrayElement*: proc(env: JNIEnvPtr, arr: jobjectArray, index: jsize, val: jobject) {.jni.} 360 | 361 | NewBooleanArray*: proc(env: JNIEnvPtr, len: jsize): jbooleanArray {.jni.} 362 | NewByteArray*: proc(env: JNIEnvPtr, len: jsize): jbyteArray {.jni.} 363 | NewCharArray*: proc(env: JNIEnvPtr, len: jsize): jcharArray {.jni.} 364 | NewShortArray*: proc(env: JNIEnvPtr, len: jsize): jshortArray {.jni.} 365 | NewIntArray*: proc(env: JNIEnvPtr, len: jsize): jintArray {.jni.} 366 | NewLongArray*: proc(env: JNIEnvPtr, len: jsize): jlongArray {.jni.} 367 | NewFloatArray*: proc(env: JNIEnvPtr, len: jsize): jfloatArray {.jni.} 368 | NewDoubleArray*: proc(env: JNIEnvPtr, len: jsize): jdoubleArray {.jni.} 369 | 370 | GetBooleanArrayElements*: proc(env: JNIEnvPtr, arr: jbooleanArray, isCopy: ptr jboolean): ptr jboolean {.jni.} 371 | GetByteArrayElements*: proc(env: JNIEnvPtr, arr: jbyteArray, isCopy: ptr jboolean): ptr jbyte {.jni.} 372 | GetCharArrayElements*: proc(env: JNIEnvPtr, arr: jcharArray, isCopy: ptr jboolean): ptr jchar {.jni.} 373 | GetShortArrayElements*: proc(env: JNIEnvPtr, arr: jshortArray, isCopy: ptr jboolean): ptr jshort {.jni.} 374 | GetIntArrayElements*: proc(env: JNIEnvPtr, arr: jintArray, isCopy: ptr jboolean): ptr jint {.jni.} 375 | GetLongArrayElements*: proc(env: JNIEnvPtr, arr: jlongArray, isCopy: ptr jboolean): ptr jlong {.jni.} 376 | GetFloatArrayElements*: proc(env: JNIEnvPtr, arr: jfloatArray, isCopy: ptr jboolean): ptr jfloat {.jni.} 377 | GetDoubleArrayElements*: proc(env: JNIEnvPtr, arr: jdoubleArray, isCopy: ptr jboolean): ptr jdouble {.jni.} 378 | 379 | ReleaseBooleanArrayElements*: proc(env: JNIEnvPtr, arr: jbooleanArray, elems: ptr jboolean, mode: jint) {.jni.} 380 | ReleaseByteArrayElements*: proc(env: JNIEnvPtr, arr: jbyteArray, elems: ptr jbyte, mode: jint) {.jni.} 381 | ReleaseCharArrayElements*: proc(env: JNIEnvPtr, arr: jcharArray, elems: ptr jchar, mode: jint) {.jni.} 382 | ReleaseShortArrayElements*: proc(env: JNIEnvPtr, arr: jshortArray, elems: ptr jshort, mode: jint) {.jni.} 383 | ReleaseIntArrayElements*: proc(env: JNIEnvPtr, arr: jintArray, elems: ptr jint, mode: jint) {.jni.} 384 | ReleaseLongArrayElements*: proc(env: JNIEnvPtr, arr: jlongArray, elems: ptr jlong, mode: jint) {.jni.} 385 | ReleaseFloatArrayElements*: proc(env: JNIEnvPtr, arr: jfloatArray, elems: ptr jfloat, mode: jint) {.jni.} 386 | ReleaseDoubleArrayElements*: proc(env: JNIEnvPtr, arr: jdoubleArray, elems: ptr jdouble, mode: jint) {.jni.} 387 | 388 | GetBooleanArrayRegion*: proc(env: JNIEnvPtr, arr: jbooleanArray, start, len: jsize, buf: ptr jboolean) {.jni.} 389 | GetByteArrayRegion*: proc(env: JNIEnvPtr, arr: jbyteArray, start, len: jsize, buf: ptr jbyte) {.jni.} 390 | GetCharArrayRegion*: proc(env: JNIEnvPtr, arr: jcharArray, start, len: jsize, buf: ptr jchar) {.jni.} 391 | GetShortArrayRegion*: proc(env: JNIEnvPtr, arr: jshortArray, start, len: jsize, buf: ptr jshort) {.jni.} 392 | GetIntArrayRegion*: proc(env: JNIEnvPtr, arr: jintArray, start, len: jsize, buf: ptr jint) {.jni.} 393 | GetLongArrayRegion*: proc(env: JNIEnvPtr, arr: jlongArray, start, len: jsize, buf: ptr jlong) {.jni.} 394 | GetFloatArrayRegion*: proc(env: JNIEnvPtr, arr: jfloatArray, start, len: jsize, buf: ptr jfloat) {.jni.} 395 | GetDoubleArrayRegion*: proc(env: JNIEnvPtr, arr: jdoubleArray, start, len: jsize, buf: ptr jdouble) {.jni.} 396 | 397 | SetBooleanArrayRegion*: proc(env: JNIEnvPtr, arr: jbooleanArray, start, len: jsize, buf: ptr jboolean) {.jni.} 398 | SetByteArrayRegion*: proc(env: JNIEnvPtr, arr: jbyteArray, start, len: jsize, buf: ptr jbyte) {.jni.} 399 | SetCharArrayRegion*: proc(env: JNIEnvPtr, arr: jcharArray, start, len: jsize, buf: ptr jchar) {.jni.} 400 | SetShortArrayRegion*: proc(env: JNIEnvPtr, arr: jshortArray, start, len: jsize, buf: ptr jshort) {.jni.} 401 | SetIntArrayRegion*: proc(env: JNIEnvPtr, arr: jintArray, start, len: jsize, buf: ptr jint) {.jni.} 402 | SetLongArrayRegion*: proc(env: JNIEnvPtr, arr: jlongArray, start, len: jsize, buf: ptr jlong) {.jni.} 403 | SetFloatArrayRegion*: proc(env: JNIEnvPtr, arr: jfloatArray, start, len: jsize, buf: ptr jfloat) {.jni.} 404 | SetDoubleArrayRegion*: proc(env: JNIEnvPtr, arr: jdoubleArray, start, len: jsize, buf: ptr jdouble) {.jni.} 405 | 406 | RegisterNatives*: proc(env: JNIEnvPtr, clazz: JClass, methods: ptr JNINativeMethod, nMethods: jint): jint {.jni.} 407 | UnregisterNatives*: proc(env: JNIEnvPtr, clazz: JClass): jint {.jni.} 408 | 409 | MonitorEnter*: proc(env: JNIEnvPtr, obj: jobject): jint {.jni.} 410 | MonitorExit*: proc(env: JNIEnvPtr, obj: jobject): jint {.jni.} 411 | 412 | GetJavaVM*: proc(env: JNIEnvPtr, vm: ptr JavaVMPtr): jint {.jni.} 413 | 414 | GetStringRegion*: proc(env: JNIEnvPtr, str: jstring, start, len: jsize, buf: ptr jchar) {.jni.} 415 | GetStringUTFRegion*: proc(env: JNIEnvPtr, str: jstring, start, len: jsize, buf: ptr char) {.jni.} 416 | 417 | GetPrimitiveArrayCritical*: proc(env: JNIEnvPtr, arr: jarray, isCopy: ptr jboolean): pointer {.jni.} 418 | ReleasePrimitiveArrayCritical*: proc(env: JNIEnvPtr, arr: jarray, carray: jarray, mode: jint) {.jni.} 419 | 420 | GetStringCritical*: proc(env: JNIEnvPtr, str: jstring, isCopy: ptr jboolean): ptr jchar {.jni.} 421 | ReleaseStringCritical*: proc(env: JNIEnvPtr, str: jstring, cstr: ptr jchar) {.jni.} 422 | 423 | NewWeakGlobalRef*: proc(env: JNIEnvPtr, obj: jobject): jweak {.jni.} 424 | DeleteWeakGlobalRef*: proc(env: JNIEnvPtr, r: jweak) {.jni.} 425 | 426 | ExceptionCheck*: proc(env: JNIEnvPtr): jboolean {.jni.} 427 | 428 | NewDirectByteBuffer*: proc(env: JNIEnvPtr, address: pointer, capacity: jlong): jobject {.jni.} 429 | GetDirectBufferAddress*: proc(env: JNIEnvPtr, buf: jobject): pointer {.jni.} 430 | GetDirectBufferCapacity*: proc(env: JNIEnvPtr, buf: jobject): jlong {.jni.} 431 | 432 | # New JNI 1.6 Features 433 | 434 | GetObjectRefType*: proc(env: JNIEnvPtr, obj: jobject): jobjectRefType {.jni.} 435 | 436 | JNIEnv* = ptr JNINativeInterface 437 | JNIEnvPtr* = ptr JNIEnv 438 | 439 | JNINativeMethod* = object 440 | name*: cstring 441 | signature*: cstring 442 | fnPtr*: pointer 443 | 444 | jobjectRefType* {.size: sizeof(cint).} = enum 445 | JNIInvalidRefType 446 | JNILocalRefType 447 | JNIGlobalRefType 448 | JNIWeakGlobalRefType 449 | 450 | const 451 | JNI_VERSION_1_1* = 0x00010001.jint 452 | JNI_VERSION_1_2* = 0x00010002.jint 453 | JNI_VERSION_1_4* = 0x00010004.jint 454 | JNI_VERSION_1_6* = 0x00010006.jint 455 | JNI_VERSION_1_8* = 0x00010008.jint 456 | 457 | const 458 | JNI_OK* = 0.jint 459 | JNI_ERR* = jint(-1) 460 | JNI_EDETACHED* = jint(-2) 461 | JNI_EVERSION* = jint(-3) 462 | JNI_ENOMEM* = jint(-4) 463 | JNI_EEXIST* = jint(-5) 464 | JNI_EINVAL* = jint(-6) 465 | 466 | var JNI_CreateJavaVM*: proc (pvm: ptr JavaVMPtr, penv: ptr pointer, args: pointer): jint {.jni.} 467 | var JNI_GetDefaultJavaVMInitArgs*: proc(vm_args: ptr JavaVMInitArgs): jint {.jni.} 468 | var JNI_GetCreatedJavaVMs*: proc(vmBuf: ptr JavaVMPtr, bufLen: jsize, nVMs: ptr jsize): jint {.jni.} 469 | 470 | proc isJVMLoaded*: bool {.gcsafe.} = 471 | not JNI_CreateJavaVM.isNil and not JNI_GetDefaultJavaVMInitArgs.isNil and not JNI_GetCreatedJavaVMs.isNil 472 | 473 | proc linkWithJVMLib* = 474 | when defined(macosx): 475 | let jvm = findJVM() 476 | if not jvm.isSome: 477 | raise newException(Exception, "Could not find JVM") 478 | let libPath = jvm.get.root.parentDir.parentDir.cstring 479 | {.emit: """ 480 | CFURLRef url = CFURLCreateFromFileSystemRepresentation(kCFAllocatorDefault, (const UInt8 *)`libPath`, strlen(`libPath`), true); 481 | if (url) { 482 | CFBundleRef bundle = CFBundleCreate(kCFAllocatorDefault, url); 483 | CFRelease(url); 484 | 485 | if (bundle) { 486 | *(void**)&`JNI_CreateJavaVM` = CFBundleGetFunctionPointerForName(bundle, CFSTR("JNI_CreateJavaVM")); 487 | *(void**)&`JNI_GetDefaultJavaVMInitArgs` = CFBundleGetFunctionPointerForName(bundle, CFSTR("JNI_GetDefaultJavaVMInitArgs")); 488 | *(void**)&`JNI_GetCreatedJavaVMs` = CFBundleGetFunctionPointerForName(bundle, CFSTR("JNI_GetCreatedJavaVMs")); 489 | } 490 | } 491 | """.} 492 | else: 493 | proc linkWithJVMModule(handle: LibHandle) = 494 | JNI_CreateJavaVM = cast[type(JNI_CreateJavaVM)](symAddr(handle, "JNI_CreateJavaVM")) 495 | JNI_GetDefaultJavaVMInitArgs = cast[type(JNI_GetDefaultJavaVMInitArgs)](symAddr(handle, "JNI_GetDefaultJavaVMInitArgs")) 496 | JNI_GetCreatedJavaVMs = cast[type(JNI_GetCreatedJavaVMs)](symAddr(handle, "JNI_GetCreatedJavaVMs")) 497 | 498 | # First we try to find the JNI functions in the current process. We may already be linked with those. 499 | var handle = loadLib() 500 | if not handle.isNil: 501 | linkWithJVMModule(handle) 502 | 503 | # Then try locating JVM dynamically 504 | if not isJVMLoaded(): 505 | if not handle.isNil: 506 | unloadLib(handle) 507 | let foundJVM = findJVM() 508 | if foundJVM.isSome: 509 | handle = loadLib(foundJVM.get.lib) 510 | linkWithJVMModule(handle) 511 | 512 | if not isJVMLoaded(): 513 | if not handle.isNil: 514 | unloadLib(handle) 515 | 516 | if not isJVMLoaded(): 517 | raise newException(Exception, "JVM could not be loaded") 518 | 519 | proc fqcn*(cls: string): string = 520 | ## Create fullqualified class name 521 | cls.replace(".", "/") 522 | 523 | proc sigForClass*(cls: string): string = 524 | ## Create method/field signature part for class name 525 | "L" & fqcn(cls) & ";" 526 | 527 | proc toJValue*(v: cfloat): jvalue = result.f = v 528 | proc toJValue*(v: jdouble): jvalue = result.d = v 529 | proc toJValue*(v: jint): jvalue = result.i = v 530 | proc toJValue*(v: jlong): jvalue = result.j = v 531 | proc toJValue*(v: jboolean): jvalue = result.z = v 532 | proc toJValue*(v: bool): jvalue = result.z = if v: JVM_TRUE else: JVM_FALSE 533 | proc toJValue*(v: jbyte): jvalue = result.b = v 534 | proc toJValue*(v: jchar): jvalue = result.c = v 535 | proc toJValue*(v: jshort): jvalue = result.s = v 536 | proc toJValue*(v: jobject): jvalue = result.l = v 537 | 538 | template fromJValue*(T: typedesc, v: jvalue): auto = 539 | when T is jboolean: v.z 540 | elif T is bool: v.z != JVM_FALSE 541 | elif T is jbyte: v.b 542 | elif T is jchar: v.c 543 | elif T is jshort: v.s 544 | elif T is jint: v.i 545 | elif T is jlong: v.j 546 | elif T is jfloat: v.f 547 | elif T is jdouble: v.d 548 | elif T is jobject: v.l 549 | else: 550 | {.error: "wrong type".} 551 | 552 | template jniSig*(t: typedesc[jlong]): string = "J" 553 | template jniSig*(t: typedesc[jint]): string = "I" 554 | template jniSig*(t: typedesc[jboolean]): string = "Z" 555 | template jniSig*(t: typedesc[bool]): string = "Z" 556 | template jniSig*(t: typedesc[jbyte]): string = "B" 557 | template jniSig*(t: typedesc[jchar]): string = "C" 558 | template jniSig*(t: typedesc[jshort]): string = "S" 559 | template jniSig*(t: typedesc[jfloat]): string = "F" 560 | template jniSig*(t: typedesc[jdouble]): string = "D" 561 | template jniSig*(t: typedesc[string]): string = sigForClass"java.lang.String" 562 | template jniSig*(t: typedesc[jobject]): string = sigForClass"java.lang.Object" 563 | template jniSig*(t: typedesc[void]): string = "V" 564 | proc elementTypeOfOpenArrayType[OpenArrayType](dummy: OpenArrayType = @[]): auto = dummy[0] 565 | template jniSig*(t: typedesc[openarray]): string = "[" & jniSig(type(elementTypeOfOpenArrayType[t]())) 566 | template jniSig*(t: typedesc[seq]): string = "[" & jniSig(type(elementTypeOfOpenArrayType[t]())) 567 | 568 | type 569 | JVMArrayType* = jobjectArray | 570 | jcharArray | 571 | jbyteArray | 572 | jshortArray | 573 | jintArray | 574 | jlongArray | 575 | jfloatArray | 576 | jdoubleArray | 577 | jbooleanArray 578 | JVMValueType* = jobject | 579 | jchar | 580 | jbyte | 581 | jshort | 582 | jint | 583 | jlong | 584 | jfloat | 585 | jdouble | 586 | jboolean 587 | 588 | proc unexpectedType() = discard # Used only for compilation errors 589 | 590 | proc callMethod*(e: JNIEnvPtr, T: typedesc, o: jobject, m: jmethodID, a: ptr jvalue): T {.inline.} = 591 | when T is jobject: e.CallObjectMethodA(e, o, m, a) 592 | elif T is jchar: e.CallCharMethodA(e, o, m, a) 593 | elif T is jbyte: e.CallByteMethodA(e, o, m, a) 594 | elif T is jshort: e.CallShortMethodA(e, o, m, a) 595 | elif T is jint: e.CallIntMethodA(e, o, m, a) 596 | elif T is jlong: e.CallLongMethodA(e, o, m, a) 597 | elif T is jfloat: e.CallFloatMethodA(e, o, m, a) 598 | elif T is jdouble: e.CallDoubleMethodA(e, o, m, a) 599 | elif T is jboolean: e.CallBooleanMethodA(e, o, m, a) 600 | elif T is void: e.CallVoidMethodA(e, o, m, a) 601 | else: unexpectedType(result) 602 | 603 | proc callMethod*(e: JNIEnvPtr, T: typedesc, o: jobject, m: jmethodID, a: openarray[jvalue]): T {.inline.} = 604 | e.callMethod(T, o, m, cast[ptr jvalue](a)) 605 | 606 | proc callNonvirtualMethod*(e: JNIEnvPtr, T: typedesc, o: jobject, c: JClass, m: jmethodID, a: ptr jvalue): T {.inline.} = 607 | when T is jobject: e.CallNonvirtualObjectMethodA(e, o, c, m, a) 608 | elif T is jchar: e.CallNonvirtualCharMethodA(e, o, c, m, a) 609 | elif T is jbyte: e.CallNonvirtualByteMethodA(e, o, c, m, a) 610 | elif T is jshort: e.CallNonvirtualShortMethodA(e, o, c, m, a) 611 | elif T is jint: e.CallNonvirtualIntMethodA(e, o, c, m, a) 612 | elif T is jlong: e.CallNonvirtualLongMethodA(e, o, c, m, a) 613 | elif T is jfloat: e.CallNonvirtualFloatMethodA(e, o, c, m, a) 614 | elif T is jdouble: e.CallNonvirtualDoubleMethodA(e, o, c, m, a) 615 | elif T is jboolean: e.CallNonvirtualBooleanMethodA(e, o, c, m, a) 616 | elif T is void: e.CallNonvirtualVoidMethodA(e, o, c, m, a) 617 | else: unexpectedType(result) 618 | 619 | proc callNonvirtualMethod*(e: JNIEnvPtr, T: typedesc, o: jobject, c: JClass, m: jmethodID, a: openarray[jvalue]): T {.inline.} = 620 | e.callNonvirtualMethod(T, o, c, m, cast[ptr jvalue](a)) 621 | 622 | proc getField*(e: JNIEnvPtr, T: typedesc, o: jobject, f: jfieldID): T {.inline.} = 623 | when T is jobject: e.GetObjectField(e, o, f) 624 | elif T is jchar: e.GetCharField(e, o, f) 625 | elif T is jbyte: e.GetByteField(e, o, f) 626 | elif T is jshort: e.GetShortField(e, o, f) 627 | elif T is jint: e.GetIntField(e, o, f) 628 | elif T is jlong: e.GetLongField(e, o, f) 629 | elif T is jfloat: e.GetFloatField(e, o, f) 630 | elif T is jdouble: e.GetDoubleField(e, o, f) 631 | elif T is jboolean: e.GetBooleanField(e, o, f) 632 | else: unexpectedType(result) 633 | 634 | proc setField*[T](e: JNIEnvPtr, o: jobject, f: jfieldID, v: T) {.inline.} = 635 | when T is jobject: e.SetObjectField(e, o, f, v) 636 | elif T is jchar: e.SetCharField(e, o, f, v) 637 | elif T is jbyte: e.SetByteField(e, o, f, v) 638 | elif T is jshort: e.SetShortField(e, o, f, v) 639 | elif T is jint: e.SetIntField(e, o, f, v) 640 | elif T is jlong: e.SetLongField(e, o, f, v) 641 | elif T is jfloat: e.SetFloatField(e, o, f, v) 642 | elif T is jdouble: e.SetDoubleField(e, o, f, v) 643 | elif T is jboolean: e.SetBooleanField(e, o, f, v) 644 | else: unexpectedType(v) 645 | 646 | proc callStaticMethod*(e: JNIEnvPtr, T: typedesc, c: JClass, m: jmethodID, a: ptr jvalue): T {.inline.} = 647 | when T is jobject: e.CallStaticObjectMethodA(e, c, m, a) 648 | elif T is jchar: e.CallStaticCharMethodA(e, c, m, a) 649 | elif T is jbyte: e.CallStaticByteMethodA(e, c, m, a) 650 | elif T is jshort: e.CallStaticShortMethodA(e, c, m, a) 651 | elif T is jint: e.CallStaticIntMethodA(e, c, m, a) 652 | elif T is jlong: e.CallStaticLongMethodA(e, c, m, a) 653 | elif T is jfloat: e.CallStaticFloatMethodA(e, c, m, a) 654 | elif T is jdouble: e.CallStaticDoubleMethodA(e, c, m, a) 655 | elif T is jboolean: e.CallStaticBooleanMethodA(e, c, m, a) 656 | elif T is void: e.CallStaticVoidMethodA(e, c, m, a) 657 | else: unexpectedType(result) 658 | 659 | proc callStaticMethod*(e: JNIEnvPtr, T: typedesc, c: JClass, m: jmethodID, a: openarray[jvalue]): T {.inline.} = 660 | e.callStaticMethod(T, c, m, cast[ptr jvalue](a)) 661 | 662 | proc getStaticField*(e: JNIEnvPtr, T: typedesc, o: JClass, f: jfieldID): T {.inline.} = 663 | when T is jobject: e.GetStaticObjectField(e, o, f) 664 | elif T is jchar: e.GetStaticCharField(e, o, f) 665 | elif T is jbyte: e.GetStaticByteField(e, o, f) 666 | elif T is jshort: e.GetStaticShortField(e, o, f) 667 | elif T is jint: e.GetStaticIntField(e, o, f) 668 | elif T is jlong: e.GetStaticLongField(e, o, f) 669 | elif T is jfloat: e.GetStaticFloatField(e, o, f) 670 | elif T is jdouble: e.GetStaticDoubleField(e, o, f) 671 | elif T is jboolean: e.GetStaticBooleanField(e, o, f) 672 | else: unexpectedType(result) 673 | 674 | proc setStaticField*[T](e: JNIEnvPtr, o: JClass, f: jfieldID, v: T) {.inline.} = 675 | when T is jobject: e.SetStaticObjectField(e, o, f, v) 676 | elif T is jchar: e.SetStaticCharField(e, o, f, v) 677 | elif T is jbyte: e.SetStaticByteField(e, o, f, v) 678 | elif T is jshort: e.SetStaticShortField(e, o, f, v) 679 | elif T is jint: e.SetStaticIntField(e, o, f, v) 680 | elif T is jlong: e.SetStaticLongField(e, o, f, v) 681 | elif T is jfloat: e.SetStaticFloatField(e, o, f, v) 682 | elif T is jdouble: e.SetStaticDoubleField(e, o, f, v) 683 | elif T is jboolean: e.SetStaticBooleanField(e, o, f, v) 684 | else: unexpectedType(v) 685 | 686 | proc newArray*(e: JNIEnvPtr, T: typedesc, l: jsize): jtypedArray[T] {.inline.} = 687 | when T is jchar: e.NewCharArray(e, l) 688 | elif T is jbyte: e.NewByteArray(e, l) 689 | elif T is jshort: e.NewShortArray(e, l) 690 | elif T is jint: e.NewIntArray(e, l) 691 | elif T is jlong: e.NewLongArray(e, l) 692 | elif T is jfloat: e.NewFloatArray(e, l) 693 | elif T is jdouble: e.NewDoubleArray(e, l) 694 | elif T is jboolean: e.NewBooleanArray(e, l) 695 | else: unexpectedType(T) 696 | 697 | proc getArrayElements*[T](e: JNIEnvPtr, a: jtypedArray[T], c: ptr jboolean): ptr T {.inline.} = 698 | when T is jchar: e.GetCharArrayElements(e, a, c) 699 | elif T is jbyte: e.GetByteArrayElements(e, a, c) 700 | elif T is jshort: e.GetShortArrayElements(e, a, c) 701 | elif T is jint: e.GetIntArrayElements(e, a, c) 702 | elif T is jlong: e.GetLongArrayElements(e, a, c) 703 | elif T is jfloat: e.GetFloatArrayElements(e, a, c) 704 | elif T is jdouble: e.GetDoubleArrayElements(e, a, c) 705 | elif T is jboolean: e.GetBooleanArrayElements(e, a, c) 706 | else: unexpectedType(T) 707 | 708 | proc releaseArrayElements*[T](e: JNIEnvPtr, a: jtypedArray[T], v: ptr T, m: jint) {.inline.} = 709 | when T is jchar: e.ReleaseCharArrayElements(e, a, v, m) 710 | elif T is jbyte: e.ReleaseByteArrayElements(e, a, v, m) 711 | elif T is jshort: e.ReleaseShortArrayElements(e, a, v, m) 712 | elif T is jint: e.ReleaseIntArrayElements(e, a, v, m) 713 | elif T is jlong: e.ReleaseLongArrayElements(e, a, v, m) 714 | elif T is jfloat: e.ReleaseFloatArrayElements(e, a, v, m) 715 | elif T is jdouble: e.ReleaseDoubleArrayElements(e, a, v, m) 716 | elif T is jboolean: e.ReleaseBooleanArrayElements(e, a, v, m) 717 | else: unexpectedType(T) 718 | 719 | proc getArrayRegion*[T](e: JNIEnvPtr, a: jtypedArray[T], s, l: jsize, b: ptr T) {.inline.} = 720 | when T is jchar: e.GetCharArrayRegion(e, a, s, l, b) 721 | elif T is jbyte: e.GetByteArrayRegion(e, a, s, l, b) 722 | elif T is jshort: e.GetShortArrayRegion(e, a, s, l, b) 723 | elif T is jint: e.GetIntArrayRegion(e, a, s, l, b) 724 | elif T is jlong: e.GetLongArrayRegion(e, a, s, l, b) 725 | elif T is jfloat: e.GetFloatArrayRegion(e, a, s, l, b) 726 | elif T is jdouble: e.GetDoubleArrayRegion(e, a, s, l, b) 727 | elif T is jboolean: e.GetBooleanArrayRegion(e, a, s, l, b) 728 | else: unexpectedType(T) 729 | 730 | proc setArrayRegion*[T](e: JNIEnvPtr, a: jtypedArray[T], s, l: jsize, b: ptr T) {.inline.} = 731 | when T is jchar: e.SetCharArrayRegion(e, a, s, l, b) 732 | elif T is jbyte: e.SetByteArrayRegion(e, a, s, l, b) 733 | elif T is jshort: e.SetShortArrayRegion(e, a, s, l, b) 734 | elif T is jint: e.SetIntArrayRegion(e, a, s, l, b) 735 | elif T is jlong: e.SetLongArrayRegion(e, a, s, l, b) 736 | elif T is jfloat: e.SetFloatArrayRegion(e, a, s, l, b) 737 | elif T is jdouble: e.SetDoubleArrayRegion(e, a, s, l, b) 738 | elif T is jboolean: e.SetBooleanArrayRegion(e, a, s, l, b) 739 | else: unexpectedType(T) 740 | -------------------------------------------------------------------------------- /jnim/private/jvm_finder.nim: -------------------------------------------------------------------------------- 1 | import os, osproc, strutils, options 2 | 3 | type 4 | JVMPath* = tuple[ 5 | root: string, 6 | lib: string 7 | ] 8 | JVMSearchOpts* {.pure.} = enum 9 | ## JVM search options. 10 | JavaHome, 11 | CurrentEnv 12 | 13 | proc findJvmInPath(p: string): string = 14 | const libs = [ 15 | # Windows 16 | "bin\\server\\jvm.dll", 17 | "bin\\client\\jvm.dll", 18 | "jre\\bin\\server\\jvm.dll", 19 | "jre\\bin\\client\\jvm.dll", 20 | # *nix 21 | "jre/lib/libjvm.so", 22 | "jre/lib/libjvm.dylib", 23 | "jre/lib/amd64/jamvm/libjvm.so", 24 | "jre/lib/amd64/server/libjvm.so", 25 | "lib/server/libjvm.so", 26 | "libexec/lib/server/libjvm.so", 27 | ] 28 | for lib in libs: 29 | let lp = p / lib 30 | if fileExists(lp): 31 | return lp 32 | 33 | proc searchInPaths(paths: openarray[string]): Option[JVMPath] = 34 | for p in paths: 35 | let lp = findJvmInPath(p) 36 | if lp.len != 0: 37 | return (root: p, lib: lp).some 38 | 39 | proc searchInJavaHome: Option[JVMPath] = 40 | when defined(android): 41 | ## Hack for Kindle fire. 42 | return (root:"", lib: "/system/lib/libdvm.so").some 43 | else: 44 | var p = getEnv("JAVA_HOME") 45 | var lib: string 46 | if p.len != 0: 47 | lib = findJvmInPath(p) 48 | 49 | if lib.len == 0: 50 | p = "/usr/lib/jvm/default/" 51 | lib = findJvmInPath(p) 52 | 53 | if lib.len != 0: 54 | return (root: p, lib: lib).some 55 | 56 | proc runJavaProcess: string = 57 | when nimvm: 58 | result = staticExec("java -verbose:class -version") 59 | else: 60 | result = execProcess("java -verbose:class -version") 61 | 62 | proc searchInJavaProcessOutput(data: string): Option[JVMPath] = 63 | proc getRtJar(s: string): string = 64 | if s.startsWith("[Opened ") and s.contains("]"): 65 | let path = s[s.find(" ") + 1 .. s.rfind("]") - 1] 66 | if fileExists(path): return path 67 | else: 68 | const prefix = "[info][class,load] opened: " 69 | let i = s.find(prefix) 70 | if i != -1: 71 | let path = s[i + prefix.len .. ^1] 72 | if fileExists(path): return path 73 | 74 | proc findUsingRtJar(jar: string): Option[JVMPath] = 75 | let p1 = jar.splitPath[0].splitPath[0] 76 | when defined(macosx): 77 | if p1.endsWith("/Contents/Home/jre"): 78 | # Assume MacOS. MacOS may not have libjvm, and jvm is loaded in a 79 | # different way, so just return java home here. 80 | return (root: p1.parentDir, lib: "").some 81 | elif p1.endsWith("/Contents/Home"): 82 | # ditto 83 | return (root: p1, lib: "").some 84 | 85 | searchInPaths([p1, p1.splitPath[0]]) 86 | 87 | for s in data.splitLines: 88 | let jar = getRtJar(s) 89 | if jar.len != 0: 90 | result = findUsingRtJar(jar) 91 | if result.isSome: return 92 | 93 | proc searchInCurrentEnv: Option[JVMPath] = 94 | searchInJavaProcessOutput(runJavaProcess()) 95 | 96 | proc findJVM*(opts: set[JVMSearchOpts] = {JVMSearchOpts.JavaHome, JVMSearchOpts.CurrentEnv}, 97 | additionalPaths: openarray[string] = []): Option[JVMPath] = 98 | ## Find the path to JVM. First it tries to find it in ``additionalPaths``, 99 | ## then it tries the ``JAVA_HOME`` environment variable if ``JVMSearchOpts.JavaHome`` is set in ``opts``, 100 | ## and at least, it tries to get it calling java executable in the 101 | ## current environment if ``JVMSearchOpts.CurrentEnv`` is set in ``opts``. 102 | result = searchInPaths(additionalPaths) 103 | if not result.isSome: 104 | if JVMSearchOpts.JavaHome in opts: 105 | result = searchInJavaHome() 106 | if not result.isSome and JVMSearchOpts.CurrentEnv in opts: 107 | result = searchInCurrentEnv() 108 | -------------------------------------------------------------------------------- /jnim/support/io/github/vegansk/jnim/NativeInvocationHandler.java: -------------------------------------------------------------------------------- 1 | package io.github.vegansk.jnim; 2 | import java.lang.reflect.InvocationHandler; 3 | import java.lang.reflect.Method; 4 | import java.lang.reflect.Proxy; 5 | 6 | public class NativeInvocationHandler implements InvocationHandler { 7 | public static Object m(Class cl, long nimObjRef, long nimFuncPtr) { 8 | NativeInvocationHandler handler = new NativeInvocationHandler(); 9 | handler.h = nimObjRef; 10 | handler.fp = nimFuncPtr; 11 | return Proxy.newProxyInstance(cl.getClassLoader(), new Class[] { cl }, handler); 12 | } 13 | 14 | public Object invoke(Object obj, Method m, Object[] args) throws Throwable { 15 | return i(h, fp, obj, m, args); 16 | } 17 | 18 | protected void finalize() { 19 | f(h); 20 | } 21 | 22 | public void d() { 23 | h = 0; 24 | fp = 0; 25 | } 26 | 27 | static native Object i(long nimObjRef, long nimFuncPtr, Object obj, Method m, Object[] args); 28 | static native void f(long nimObjRef); 29 | long h, fp; 30 | } 31 | -------------------------------------------------------------------------------- /tests/common.nim: -------------------------------------------------------------------------------- 1 | import ../jnim/private/jni_api 2 | 3 | proc initJNIForTests* = 4 | initJNI(JNIVersion.v1_6, @["-Djava.class.path=build"]) 5 | 6 | -------------------------------------------------------------------------------- /tests/java/BaseClass.java: -------------------------------------------------------------------------------- 1 | public class BaseClass { 2 | 3 | private T v; 4 | 5 | public BaseClass(T v) { 6 | this.v = v; 7 | } 8 | 9 | public T baseMethod() { 10 | return v; 11 | } 12 | 13 | public T overridedMethod() { 14 | return baseMethod(); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/java/ChildClass.java: -------------------------------------------------------------------------------- 1 | public class ChildClass extends BaseClass { 2 | 3 | private T v; 4 | 5 | public ChildClass(T base, T ch) { 6 | super(base); 7 | this.v = ch; 8 | } 9 | 10 | public T childMethod() { 11 | return v; 12 | } 13 | 14 | @Override 15 | public T overridedMethod() { 16 | return childMethod(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /tests/java/ConstructorTestClass.java: -------------------------------------------------------------------------------- 1 | public class ConstructorTestClass { 2 | private String consResult = ""; 3 | 4 | public String toString() { 5 | return consResult; 6 | } 7 | 8 | public ConstructorTestClass() { 9 | consResult = "Empty constructor called"; 10 | } 11 | 12 | public ConstructorTestClass(int i) { 13 | consResult = "Int constructor called, " + i; 14 | } 15 | 16 | public ConstructorTestClass(String s) { 17 | consResult = "String constructor called, " + s; 18 | } 19 | 20 | public ConstructorTestClass(int i, double d, String s) { 21 | consResult = "Multiparameter constructor called, " + i + ", " + d + ", " + s; 22 | } 23 | 24 | public ConstructorTestClass(int[] ints) { 25 | consResult = "Int array constructor called"; 26 | for(int i: ints) { 27 | consResult += ", " + i; 28 | } 29 | } 30 | 31 | public ConstructorTestClass(String[] strings) { 32 | consResult = "String array constructor called"; 33 | for(String s: strings) { 34 | consResult += ", " + s; 35 | } 36 | } 37 | 38 | public ConstructorTestClass(ConstructorTestClass c) { 39 | consResult = c.consResult; 40 | } 41 | 42 | public ConstructorTestClass(ConstructorTestClass[] cc) { 43 | consResult = ""; 44 | for(ConstructorTestClass c: cc) { 45 | consResult += c.consResult + "\n"; 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /tests/java/ExceptionTestClass.java: -------------------------------------------------------------------------------- 1 | public class ExceptionTestClass { 2 | public static void throwEx(String msg) throws Throwable { 3 | throw new Exception(msg); 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /tests/java/ExportTestClass.java: -------------------------------------------------------------------------------- 1 | package io.github.yglukhov.jnim; 2 | 3 | public class ExportTestClass { 4 | public interface Interface { 5 | public void voidMethod(); 6 | public int intMethod(); 7 | public String stringMethod(); 8 | public String stringMethodWithArgs(String s, int i); 9 | } 10 | 11 | public static class Implementation implements Interface { 12 | public void voidMethod() { } 13 | public int intMethod() { return 123; } 14 | public String stringMethod() { return "Jnim"; } 15 | public String stringMethodWithArgs(String s, int i) { return s; } 16 | } 17 | 18 | public static class Tester { 19 | public void callVoidMethod(Interface c) { 20 | c.voidMethod(); 21 | } 22 | 23 | public int callIntMethod(Interface c) { 24 | return c.intMethod(); 25 | } 26 | 27 | public String callStringMethod(Interface c) { 28 | return c.stringMethod(); 29 | } 30 | 31 | public String callStringMethodWithArgs(Interface c, String s, int i) { 32 | return c.stringMethodWithArgs(s, i); 33 | } 34 | } 35 | } 36 | 37 | -------------------------------------------------------------------------------- /tests/java/GenericsTestClass.java: -------------------------------------------------------------------------------- 1 | import java.util.List; 2 | import java.util.ArrayList; 3 | 4 | public class GenericsTestClass { 5 | 6 | public T genericProp; 7 | 8 | public GenericsTestClass(T v) { 9 | genericProp = v; 10 | } 11 | 12 | public T getGenericValue() { 13 | return genericProp; 14 | } 15 | 16 | public void setGenericValue(T v) { 17 | genericProp = v; 18 | } 19 | 20 | public List getListOfValues(int count) { 21 | List res = new ArrayList(count); 22 | for(int i = 0; i < count; i++) { 23 | res.add(genericProp); 24 | } 25 | 26 | return res; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/java/InnerTestClass.java: -------------------------------------------------------------------------------- 1 | public class InnerTestClass { 2 | 3 | public class InnerClass { 4 | 5 | public int intField = 100; 6 | 7 | } 8 | 9 | } 10 | -------------------------------------------------------------------------------- /tests/java/MethodTestClass.java: -------------------------------------------------------------------------------- 1 | public class MethodTestClass { 2 | 3 | // Static 4 | public static int addStatic(int x, int y) { 5 | return x + y; 6 | } 7 | 8 | int mem = 0; 9 | 10 | // Instance 11 | public int addToMem(int x) { 12 | mem += x; 13 | return mem; 14 | } 15 | 16 | public static MethodTestClass factory(int i) { 17 | MethodTestClass o = new MethodTestClass(); 18 | o.addToMem(i); 19 | return o; 20 | } 21 | 22 | public String[] getStrings() { 23 | String[] arr = {"Hello", "world!"}; 24 | return arr; 25 | } 26 | 27 | public int readBytes(byte[] buf) { 28 | byte[] bs = {0, 1, 2, 3}; 29 | 30 | int result = 0; 31 | for (int i = 0; i < bs.length && i < buf.length; i++) { 32 | buf[i] = bs[i]; 33 | result = i + 1; 34 | } 35 | 36 | return result; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /tests/java/PropsTestClass.java: -------------------------------------------------------------------------------- 1 | public class PropsTestClass { 2 | 3 | public static int staticInt = 100; 4 | 5 | public int instanceInt = 100; 6 | public String instanceString = "Hello"; 7 | 8 | public static final PropsTestClass inst = new PropsTestClass(); 9 | 10 | public static boolean staticBool = false; 11 | } 12 | -------------------------------------------------------------------------------- /tests/java/TestClass.java: -------------------------------------------------------------------------------- 1 | package io.github.yglukhov.jnim; 2 | 3 | public class TestClass { 4 | // Static fields 5 | public static Object objectSField = "obj"; 6 | public static char charSField = 'A'; 7 | public static byte byteSField = 1; 8 | public static short shortSField = 2; 9 | public static int intSField = 3; 10 | public static long longSField = 4; 11 | public static float floatSField = 1.0f; 12 | public static double doubleSField = 2.0; 13 | public static boolean booleanSField = true; 14 | 15 | // Fields 16 | public Object objectField = "obj"; 17 | public char charField = 'A'; 18 | public byte byteField = 1; 19 | public short shortField = 2; 20 | public int intField = 3; 21 | public long longField = 4; 22 | public float floatField = 1.0f; 23 | public double doubleField = 2.0; 24 | public boolean booleanField = true; 25 | public String checkStringProperty = "OK"; 26 | 27 | // Static methods 28 | public static Object objectSMethod(Object v) { 29 | return v; 30 | } 31 | public static char charSMethod(char v) { 32 | return v; 33 | } 34 | public static byte byteSMethod(byte v) { 35 | return v; 36 | } 37 | public static short shortSMethod(short v) { 38 | return v; 39 | } 40 | public static int intSMethod(int v) { 41 | return v; 42 | } 43 | public static long longSMethod(long v) { 44 | return v; 45 | } 46 | public static float floatSMethod(float v) { 47 | return v; 48 | } 49 | public static double doubleSMethod(double v) { 50 | return v; 51 | } 52 | public static boolean booleanSMethod(boolean v) { 53 | return v; 54 | } 55 | 56 | // Methods 57 | public Object objectMethod(Object v) { 58 | return v; 59 | } 60 | public char charMethod(char v) { 61 | return v; 62 | } 63 | public byte byteMethod(byte v) { 64 | return v; 65 | } 66 | public short shortMethod(short v) { 67 | return v; 68 | } 69 | public int intMethod(int v) { 70 | return v; 71 | } 72 | public long longMethod(long v) { 73 | return v; 74 | } 75 | public float floatMethod(float v) { 76 | return v; 77 | } 78 | public double doubleMethod(double v) { 79 | return v; 80 | } 81 | public boolean booleanMethod(boolean v) { 82 | return v; 83 | } 84 | 85 | // Arrays 86 | public int[] intArray = { 1, 2, 3, 4, 5 }; 87 | public static char[] staticCharArray = "Hello".toCharArray(); 88 | public Object[] objectArray; 89 | public boolean checkObjectArray() { 90 | if(objectArray.length != 2) 91 | return false; 92 | if(!objectArray[0].equals("Hello")) 93 | return false; 94 | if(!objectArray[1].equals("world!")) 95 | return false; 96 | return true; 97 | } 98 | 99 | // Methods returning arrays 100 | public double[] getDoubleArray(double factor) { 101 | double[] res = new double[10]; 102 | for(int i = 1; i <= 10; i++) { 103 | res[i-1] = (double)i * factor; 104 | } 105 | return res; 106 | } 107 | public static String[] getStringArrayS() { 108 | String[] arr = {"Hello", "from", "java!"}; 109 | return arr; 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /tests/test_all.nim: -------------------------------------------------------------------------------- 1 | # There is only one JVM per process, so we can't test low and high level APIs together 2 | import test_jvm_finder 3 | # import test_jni_wrapper 4 | import test_jni_api 5 | import test_jni_generator 6 | import test_jni_export 7 | import test_jni_export_old 8 | import test_java_lang 9 | import test_java_util 10 | 11 | -------------------------------------------------------------------------------- /tests/test_example.nim: -------------------------------------------------------------------------------- 1 | import ../jnim 2 | 3 | # Import a couple of classes 4 | jclass java.io.PrintStream of JVMObject: 5 | proc println(s: string) 6 | 7 | jclass java.lang.System of JVMObject: 8 | proc `out`: PrintStream {.prop, final, `static`.} 9 | 10 | # Initialize JVM 11 | initJNI() 12 | 13 | # Call! 14 | System.`out`.println("This string is printed with System.out.println!") 15 | -------------------------------------------------------------------------------- /tests/test_java_lang.nim: -------------------------------------------------------------------------------- 1 | import ../jnim, 2 | ../jnim/java/lang, 3 | common, 4 | unittest, 5 | encodings 6 | 7 | suite "javaapi.core": 8 | setup: 9 | if not isJNIThreadInitialized(): 10 | initJNIForTests() 11 | 12 | test "java.lang.Object": 13 | let o1 = Object.new 14 | let o2 = Object.new 15 | check: not o1.toString.equals(o2.toString) 16 | check: not o1.equals(o2) 17 | check: o1.getClass.equals(o2.getClass) 18 | 19 | test "java.lang.String": 20 | let s1 = String.new("Hi") 21 | let s2 = String.new("Hi") 22 | let s3 = String.new("Hello") 23 | # Check inheritance 24 | check: s1 of String 25 | check: s1 of Object 26 | # Check operations 27 | check: $s1 == "Hi" 28 | check: s1.equals(s2) 29 | check: not s2.equals(s3) 30 | let s = String.new("Привет!") 31 | check: String.new(s.getBytes("CP1251"), "CP1251") == s 32 | 33 | jclass ExceptionTestClass of Object: 34 | proc throwEx(msg: string) {.`static`.} 35 | 36 | test "java.lang.Exception": 37 | expect(JavaException): 38 | ExceptionTestClass.throwEx("test") 39 | try: 40 | ExceptionTestClass.throwEx("test") 41 | except JavaException: 42 | let ex = getCurrentJVMException() 43 | check: ex.getStackTrace.len == 1 44 | 45 | test "java.lang - Numbers": 46 | check: Byte.MIN_VALUE == low(int8) 47 | check: Byte.MAX_VALUE == high(int8) 48 | check: Byte.SIZE == 8 49 | check: $Byte.TYPE == "byte" 50 | check: Byte.new(100).byteValue == Byte.new("100").byteValue 51 | expect JavaException: 52 | discard Byte.new("1000") 53 | check: Short.MIN_VALUE == low(int16) 54 | check: Short.MAX_VALUE == high(int16) 55 | 56 | check: Integer.new(1) == Integer.new(1) 57 | 58 | test "java.lang - Booleans": 59 | check: Boolean.new(JVM_TRUE).booleanValue == JVM_TRUE 60 | check: Boolean.new(JVM_FALSE).booleanValue == JVM_FALSE 61 | -------------------------------------------------------------------------------- /tests/test_java_util.nim: -------------------------------------------------------------------------------- 1 | import ../jnim, 2 | ../jnim/java/util, 3 | ../jnim/java/lang, 4 | common, 5 | unittest 6 | import sequtils except toSeq 7 | 8 | suite "java.util": 9 | setup: 10 | if not isJNIThreadInitialized(): 11 | initJNIForTests() 12 | 13 | test "java.util.List": 14 | let xs = ArrayList[string].new() 15 | discard xs.add("Hello") 16 | xs.add(1, "world") 17 | check: xs.get(0) == "Hello" 18 | check: xs.get(1) == "world" 19 | expect JavaException: 20 | discard xs.get(3) 21 | var s = newSeq[string]() 22 | let it = xs.toIterator 23 | while it.hasNext: 24 | s.add it.next 25 | check: s == @["Hello", "world"] 26 | discard xs.removeAll(ArrayList[string].new(["world", "!"])) 27 | check: xs.toSeq == @["Hello"] 28 | 29 | test "java.util.Map": 30 | let m = HashMap[Integer, string].new() 31 | discard m.put(1.jint, "A") 32 | discard m.put(2.jint, "B") 33 | discard m.put(3.jint, "C") 34 | check: m.get(1.jint) == "A" 35 | check: m.get(2.jint) == "B" 36 | check: m.get(3.jint) == "C" 37 | check: m.keySet.toSeq.mapIt(it.intValue) == @[1.jint, 2, 3] 38 | check: m.keySet.toSeq == @[1.jint, 2, 3].mapIt(Integer.new(it)) 39 | check: m.values.toSeq == @["A", "B", "C"] 40 | -------------------------------------------------------------------------------- /tests/test_jni_api.nim: -------------------------------------------------------------------------------- 1 | import ../jnim/private/jni_api, 2 | ./common, 3 | unittest, 4 | strutils, 5 | typetraits 6 | 7 | suite "jni_api": 8 | test "API - Thread initialization (VM not initialized)": 9 | check: not isJNIThreadInitialized() 10 | expect JNIException: 11 | initJNIThread() 12 | deinitJNIThread() 13 | 14 | test "API - Thread initialization (VM initialized)": 15 | initJNIForTests() 16 | expect JNIException: 17 | initJNI(JNIVersion.v1_6, @[]) 18 | check: isJNIThreadInitialized() 19 | 20 | test "API - JVMClass": 21 | # Find existing class 22 | discard JVMClass.getByFqcn(fqcn"java.lang.Object") 23 | discard JVMClass.getByName("java.lang.Object") 24 | # Find non existing class 25 | expect Exception: 26 | discard JVMClass.getByFqcn(fqcn"java.lang.ObjectThatNotExists") 27 | expect Exception: 28 | discard JVMClass.getByName("java.lang.ObjectThatNotExists") 29 | 30 | test "API - call System.out.println": 31 | let cls = JVMClass.getByName("java.lang.System") 32 | let outId = cls.getStaticFieldId("out", sigForClass"java.io.PrintStream") 33 | let `out` = cls.getField(JVMObject, outId) 34 | let outCls = `out`.getJVMClass 35 | let printlnId = outCls.getMethodId("println", "($#)V" % string.jniSig) 36 | `out`.callMethod(void, printlnId, ["Hello, world".newJVMObject.toJValue]) 37 | 38 | test "API - TestClass - static fields": 39 | let cls = JVMClass.getByName("io.github.yglukhov.jnim.TestClass") 40 | 41 | check: cls.getField(JVMObject, "objectSField").toStringRaw == "obj" 42 | check: cls.getField(jchar, "charSField") == 'A'.jchar 43 | check: cls.getField(jbyte, "byteSField") == 1 44 | check: cls.getField(jshort, "shortSField") == 2 45 | check: cls.getField(jint, "intSField") == 3 46 | check: cls.getField(jlong, "longSField") == 4 47 | check: cls.getField(jfloat, "floatSField") == 1.0 48 | check: cls.getField(jdouble, "doubleSField") == 2.0 49 | check: cls.getField(jboolean, "booleanSField") == JVM_TRUE 50 | 51 | cls.setField("objectSField", "Nim".newJVMObject) 52 | cls.setField("charSField", 'B'.jchar) 53 | cls.setField("byteSField", 100.jbyte) 54 | cls.setField("shortSField", 200.jshort) 55 | cls.setField("intSField", 300.jint) 56 | cls.setField("longSField", 400.jlong) 57 | cls.setField("floatSField", 500.jfloat) 58 | cls.setField("doubleSField", 600.jdouble) 59 | cls.setField("booleanSField", JVM_FALSE) 60 | 61 | check: cls.getField(JVMObject, "objectSField").toStringRaw == "Nim" 62 | check: cls.getField(jchar, "charSField") == 'B'.jchar 63 | check: cls.getField(jbyte, "byteSField") == 100 64 | check: cls.getField(jshort, "shortSField") == 200 65 | check: cls.getField(jint, "intSField") == 300 66 | check: cls.getField(jlong, "longSField") == 400 67 | check: cls.getField(jfloat, "floatSField") == 500.0 68 | check: cls.getField(jdouble, "doubleSField") == 600.0 69 | check: cls.getField(jboolean, "booleanSField") == JVM_FALSE 70 | 71 | test "API - TestClass - fields": 72 | let cls = JVMClass.getByName("io.github.yglukhov.jnim.TestClass") 73 | let obj = cls.newObject("()V") 74 | 75 | check: getPropValue(string, obj, obj.getJVMClass.getFieldId("checkStringProperty", jniSig(string))) == "OK" 76 | 77 | check: obj.getField(JVMObject, "objectField").toStringRaw == "obj" 78 | check: obj.getField(jchar, "charField") == 'A'.jchar 79 | check: obj.getField(jbyte, "byteField") == 1 80 | check: obj.getField(jshort, "shortField") == 2 81 | check: obj.getField(jint, "intField") == 3 82 | check: obj.getField(jlong, "longField") == 4 83 | check: obj.getField(jfloat, "floatField") == 1.0 84 | check: obj.getField(jdouble, "doubleField") == 2.0 85 | check: obj.getField(jboolean, "booleanField") == JVM_TRUE 86 | 87 | obj.setObject("objectField", "Nim".newJVMObject) 88 | obj.setChar("charField", 'B'.jchar) 89 | obj.setByte("byteField", 100) 90 | obj.setShort("shortField", 200) 91 | obj.setInt("intField", 300) 92 | obj.setLong("longField", 400) 93 | obj.setFloat("floatField", 500.0) 94 | obj.setDouble("doubleField", 600.0) 95 | obj.setBoolean("booleanField", JVM_FALSE) 96 | 97 | check: obj.getObject("objectField").toStringRaw == "Nim" 98 | check: obj.getChar("charField") == 'B'.jchar 99 | check: obj.getByte("byteField") == 100 100 | check: obj.getShort("shortField") == 200 101 | check: obj.getInt("intField") == 300 102 | check: obj.getLong("longField") == 400 103 | check: obj.getFloat("floatField") == 500.0 104 | check: obj.getDouble("doubleField") == 600.0 105 | check: obj.getBoolean("booleanField") == JVM_FALSE 106 | 107 | test "JVM - TestClass - static methods": 108 | let cls = JVMClass.getByName("io.github.yglukhov.jnim.TestClass") 109 | 110 | check: cls.callMethod(JVMObject, "objectSMethod", "($1)$1" % JVMObject.jniSig, ["test".newJVMObject.toJValue]).toStringRaw == "test" 111 | check: string.callMethod(cls, cls.getStaticMethodId("objectSMethod", "($1)$1" % JVMObject.jniSig), ["test".newJVMObject.toJValue]) == "test" 112 | check: cls.callMethod(jchar, "charSMethod", "($1)$1" % jchar.jniSig, ['A'.jchar.toJValue]) == 'A'.jchar 113 | check: cls.callMethod(jbyte, "byteSMethod", "($1)$1" % jbyte.jniSig, [1.jbyte.toJValue]) == 1 114 | check: cls.callMethod(jshort, "shortSMethod", "($1)$1" % jshort.jniSig, [2.jshort.toJValue]) == 2 115 | check: cls.callMethod(jint, "intSMethod", "($1)$1" % jint.jniSig, [3.jint.toJValue]) == 3 116 | check: cls.callMethod(jlong, "longSMethod", "($1)$1" % jlong.jniSig, [4.jlong.toJValue]) == 4 117 | check: cls.callMethod(jfloat, "floatSMethod", "($1)$1" % jfloat.jniSig, [5.jfloat.toJValue]) == 5.0 118 | check: cls.callMethod(jdouble, "doubleSMethod", "($1)$1" % jdouble.jniSig, [6.jdouble.toJValue]) == 6.0 119 | check: cls.callMethod(jboolean, "booleanSMethod", "($1)$1" % jboolean.jniSig, [JVM_TRUE.toJValue]) == JVM_TRUE 120 | 121 | 122 | test "JVM - TestClass - methods": 123 | let cls = JVMClass.getByName("io.github.yglukhov.jnim.TestClass") 124 | let obj = cls.newObject("()V") 125 | 126 | check: obj.callMethod(JVMObject, "objectMethod", "($1)$1" % JVMObject.jniSig, ["test".newJVMObject.toJValue]).toStringRaw == "test" 127 | check: string.callMethod(obj, cls.getMethodId("objectMethod", "($1)$1" % JVMObject.jniSig), ["test".newJVMObject.toJValue]) == "test" 128 | check: obj.callMethod(jchar, "charMethod", "($1)$1" % jchar.jniSig, ['A'.jchar.toJValue]) == 'A'.jchar 129 | check: obj.callMethod(jbyte, "byteMethod", "($1)$1" % jbyte.jniSig, [1.jbyte.toJValue]) == 1 130 | check: obj.callMethod(jshort, "shortMethod", "($1)$1" % jshort.jniSig, [2.jshort.toJValue]) == 2 131 | check: obj.callMethod(jint, "intMethod", "($1)$1" % jint.jniSig, [3.jint.toJValue]) == 3 132 | check: obj.callMethod(jlong, "longMethod", "($1)$1" % jlong.jniSig, [4.jlong.toJValue]) == 4 133 | check: obj.callMethod(jfloat, "floatMethod", "($1)$1" % jfloat.jniSig, [5.jfloat.toJValue]) == 5.0 134 | check: obj.callMethod(jdouble, "doubleMethod", "($1)$1" % jdouble.jniSig, [6.jdouble.toJValue]) == 6.0 135 | check: obj.callMethod(jboolean, "booleanMethod", "($1)$1" % jboolean.jniSig, [JVM_TRUE.toJValue]) == JVM_TRUE 136 | 137 | test "JVM - arrays": 138 | discard jchar.newArray(100) 139 | discard newArray(JVMObject, 100.jsize) 140 | discard JVMClass.getByName("java.lang.Object").newArray(100) 141 | 142 | discard @[1.jint, 2, 3].toJVMObject() 143 | discard @["a", "b", "c"].toJVMObject() 144 | 145 | test "JVM - TestClass - arrays": 146 | let cls = JVMClass.getByName("io.github.yglukhov.jnim.TestClass") 147 | let sArr = cls.getCharArray("staticCharArray") 148 | 149 | check: sArr.len == 5 150 | for idx, ch in "Hello": 151 | check: sArr[idx] == ch.jchar 152 | 153 | let obj = cls.newObject("()V") 154 | let arr = obj.getIntArray("intArray") 155 | 156 | check: arr.len == 5 157 | for idx, i in [1,2,3,4,5]: 158 | check: arr[idx] == i 159 | arr[idx] = (i * 2).jint 160 | check: arr[idx] == i * 2 161 | 162 | let objArray: JVMObjectArray = newJVMObjectArray(2) 163 | objArray[0] = "Hello".newJVMObject 164 | objArray[1] = "world".newJVMObject 165 | obj.setObjectArray("objectArray", objArray) 166 | check: obj.callMethod(jboolean, "checkObjectArray", "()" & jboolean.jniSig) == JVM_FALSE 167 | objArray[1] = "world!".newJVMObject 168 | check: obj.callMethod(jboolean, "checkObjectArray", "()" & jboolean.jniSig) == JVM_TRUE 169 | 170 | let doubleArray = obj.callDoubleArrayMethod("getDoubleArray", "($#)$#" % [jdouble.jniSig, seq[jdouble].jniSig], [2.0.jdouble.toJValue]) 171 | for idx in 1..doubleArray.len: 172 | check: doubleArray[idx-1] == (idx * 2).jdouble 173 | 174 | let strArray = cls.callObjectArrayMethod("getStringArrayS", "()" & seq[string].jniSig) 175 | for idx, val in ["Hello", "from", "java!"]: 176 | check: newJVMObjectConsumingLocalRef(strArray[idx]).toStringRaw == val 177 | 178 | test "API - jstring $": 179 | check: $(theEnv.NewStringUTF(theEnv, "test")) == "test" 180 | 181 | -------------------------------------------------------------------------------- /tests/test_jni_export.nim: -------------------------------------------------------------------------------- 1 | import ../jnim/private / [ jni_api, jni_generator, jni_export, java_glue ], 2 | ./common, 3 | unittest 4 | 5 | jclass io.github.yglukhov.jnim.ExportTestClass$Interface of JVMObject: 6 | proc voidMethod() 7 | proc intMethod*(): jint # Test public 8 | proc stringMethod(): string 9 | proc stringMethodWithArgs(s: string, i: jint): string 10 | 11 | jclass io.github.yglukhov.jnim.ExportTestClass$Tester of JVMObject: 12 | proc new 13 | proc callVoidMethod(r: Interface) 14 | proc callIntMethod(r: Interface): jint 15 | proc callStringMethod(r: Interface): string 16 | proc callStringMethodWithArgs(r: Interface, s: string, i: jint): string 17 | 18 | jclass io.github.yglukhov.jnim.ExportTestClass$Implementation of Interface: 19 | proc new 20 | 21 | type 22 | MyObjData = ref object of RootObj 23 | a: int 24 | 25 | MyObj = ref object of JVMObject 26 | data: MyObjData 27 | 28 | MyObjSub = ref object of MyObj 29 | ImplementationSub = ref object of Implementation 30 | 31 | jexport MyObj implements Interface: 32 | proc new() = super() 33 | 34 | proc voidMethod() # Test fwd declaration 35 | 36 | proc intMethod*(): jint = # Test public 37 | return 123 38 | 39 | proc stringMethod(): string = 40 | return "Hello world" 41 | 42 | proc stringMethodWithArgs(s: string, i: jint): string = 43 | return "123" & $i & s 44 | 45 | jexport MyObjSub extends MyObj: 46 | proc new = super() 47 | 48 | proc stringMethod(): string = 49 | "Nim" 50 | 51 | jexport ImplementationSub extends Implementation: 52 | proc new() = super() 53 | 54 | proc stringMethod(): string = 55 | this.super.stringMethod() & " is awesome" 56 | 57 | proc voidMethod(this: MyObj) = 58 | inc this.data.a 59 | 60 | debugPrintJavaGlue() 61 | 62 | suite "jni_export": 63 | setup: 64 | if not isJNIThreadInitialized(): 65 | initJNIForTests() 66 | 67 | registerNativeMethods() 68 | 69 | test "Smoke test": 70 | let mr = MyObj.new() 71 | let tr = Tester.new() 72 | check: 73 | not mr.data.isNil 74 | mr.data.a == 0 75 | tr.callVoidMethod(mr) 76 | check: 77 | mr.data.a == 1 78 | tr.callIntMethod(mr) == 123 79 | tr.callStringMethod(mr) == "Hello world" 80 | tr.callStringMethodWithArgs(mr, "789", 456) == "123456789" 81 | tr.callStringMethod(MyObjSub.new()) == "Nim" 82 | 83 | tr.callStringMethod(Implementation.new()) == "Jnim" 84 | tr.callStringMethod(ImplementationSub.new()) == "Jnim is awesome" 85 | -------------------------------------------------------------------------------- /tests/test_jni_export_old.nim: -------------------------------------------------------------------------------- 1 | import ../jnim/private / [ jni_api, jni_generator, jni_export_old ], 2 | ./common, 3 | unittest 4 | 5 | suite "jni_export_old": 6 | setup: 7 | if not isJNIThreadInitialized(): 8 | initJNIForTests() 9 | 10 | test "Make proxy": 11 | jclassDef io.github.yglukhov.jnim.ExportTestClass$Interface of JVMObject 12 | jclass io.github.yglukhov.jnim.ExportTestClass$Tester of JVMObject: 13 | proc new 14 | proc callVoidMethod(r: Interface) 15 | 16 | type MyObj = ref object of RootObj 17 | a: int 18 | 19 | var mr = MyObj.new() 20 | mr.a = 1 21 | proc handler(env: pointer, o: RootRef, proxiedThis, meth: jobject, args: jobjectArray): jobject {.cdecl.} = 22 | let mr = cast[MyObj](o) 23 | inc mr.a 24 | 25 | let runnableClazz = Interface.getJVMClassForType() 26 | for i in 0 .. 3: 27 | let pr = makeProxy(runnableClazz.get, mr, handler) 28 | 29 | let tr = Tester.new() 30 | tr.callVoidMethod(Interface.fromJObject(pr)) 31 | 32 | check: mr.a == 5 33 | 34 | test "Implement dispatcher": 35 | jclassDef io.github.yglukhov.jnim.ExportTestClass$Interface of JVMObject 36 | jclass io.github.yglukhov.jnim.ExportTestClass$Tester of JVMObject: 37 | proc new 38 | proc callVoidMethod(r: Interface) 39 | proc callIntMethod(r: Interface): jint 40 | proc callStringMethod(r: Interface): string 41 | proc callStringMethodWithArgs(r: Interface, s: string, i: jint): string 42 | 43 | type MyObj = ref object of RootObj 44 | a: int 45 | 46 | implementDispatcher(MyObj, MyObj_dispatcher): 47 | proc voidMethod(self: MyObj) = 48 | inc self.a 49 | 50 | proc intMethod(self: MyObj): jint = 51 | return 123 52 | 53 | proc stringMethod(self: MyObj): string = 54 | return "123" 55 | 56 | proc stringMethodWithArgs(self: MyObj, s: string, i: jint): string = 57 | return "123" & $i & s 58 | 59 | let mr = MyObj.new() 60 | mr.a = 5 61 | let pr = makeProxy(Interface, mr, MyObj_dispatcher) 62 | let tr = Tester.new() 63 | tr.callVoidMethod(pr) 64 | check: mr.a == 6 65 | 66 | check: tr.callIntMethod(pr) == 123 67 | check: tr.callStringMethod(pr) == "123" 68 | check: tr.callStringMethodWithArgs(pr, "789", 456) == "123456789" 69 | -------------------------------------------------------------------------------- /tests/test_jni_generator.nim: -------------------------------------------------------------------------------- 1 | import ../jnim/private/jni_generator, 2 | ../jnim/private/jni_api, 3 | ./common, 4 | macros, 5 | unittest 6 | 7 | jclass java.lang.String2* of JVMObject: 8 | proc new 9 | jclass java.lang.String as JVMString2* of JVMObject: 10 | proc new 11 | 12 | # These classes are not used in actual tests - 13 | # but they should compile. 14 | 15 | jclass java.util.Map[K,V] of JVMObject: 16 | proc get(k: K): V 17 | 18 | # Class with a method that returns a generic with two arguments 19 | jclass java.lang.ProcessBuilder of JVMObject: 20 | proc environment: Map[string, string] 21 | 22 | # Class that inherits from another with get() method 23 | jclass java.util.Properties of Map[JVMObject, JVMObject]: 24 | proc new 25 | 26 | jclass java.lang.Object* of JVMObject: 27 | proc new 28 | jclass java.lang.String* of Object: 29 | proc new 30 | jclass java.lang.Integer* of Object: 31 | proc new(v: jint) 32 | 33 | suite "jni_generator": 34 | setup: 35 | if not isJNIThreadInitialized(): 36 | initJNIForTests() 37 | 38 | test "jni_generator - proc def - constructors": 39 | var pd: ProcDef 40 | 41 | parseProcDefTest pd: 42 | proc new 43 | check: pd.name == "new" 44 | check: pd.jName == "" 45 | check: pd.retType == "void" 46 | check: pd.params.len == 0 47 | check: pd.isConstructor 48 | check: not pd.isStatic 49 | check: not pd.isProp 50 | check: not pd.isFinal 51 | check: not pd.isExported 52 | 53 | parseProcDefTest pd: 54 | proc new* 55 | check: pd.name == "new" 56 | check: pd.jName == "" 57 | check: pd.retType == "void" 58 | check: pd.params.len == 0 59 | check: pd.isConstructor 60 | check: not pd.isStatic 61 | check: not pd.isProp 62 | check: not pd.isFinal 63 | check: pd.isExported 64 | 65 | parseProcDefTest pd: 66 | proc new(o: JVMObject) 67 | check: pd.name == "new" 68 | check: pd.jName == "" 69 | check: pd.retType == "void" 70 | check: pd.params == @[("o", "JVMObject")] 71 | check: pd.isConstructor 72 | check: not pd.isStatic 73 | check: not pd.isProp 74 | check: not pd.isFinal 75 | check: not pd.isExported 76 | 77 | parseProcDefTest pd: 78 | proc new*(i: jint, s: string) 79 | check: pd.name == "new" 80 | check: pd.jName == "" 81 | check: pd.retType == "void" 82 | check: pd.params == @[("i", "jint"), ("s", "string")] 83 | check: pd.isConstructor 84 | check: not pd.isStatic 85 | check: not pd.isProp 86 | check: not pd.isFinal 87 | check: pd.isExported 88 | 89 | test "jni_generator - proc def - methods": 90 | var pd: ProcDef 91 | 92 | parseProcDefTest pd: 93 | proc getStrings: seq[string] 94 | check: pd.name == "getStrings" 95 | check: pd.jName == "getStrings" 96 | check: pd.retType == "seq[string]" 97 | check: pd.params.len == 0 98 | check: not pd.isConstructor 99 | check: not pd.isStatic 100 | check: not pd.isProp 101 | check: not pd.isFinal 102 | check: not pd.isExported 103 | 104 | parseProcDefTest pd: 105 | proc `method`*(i: jint): jshort {.importc: "jmethod".} 106 | check: pd.name == "method" 107 | check: pd.jName == "jmethod" 108 | check: pd.retType == "jshort" 109 | check: pd.params == @[("i", "jint")] 110 | check: not pd.isConstructor 111 | check: not pd.isStatic 112 | check: not pd.isProp 113 | check: not pd.isFinal 114 | check: pd.isExported 115 | 116 | parseProcDefTest pd: 117 | proc `method`*(i, j: jint): jshort {.importc: "jmethod".} 118 | check: pd.name == "method" 119 | check: pd.jName == "jmethod" 120 | check: pd.retType == "jshort" 121 | check: pd.params == @[("i", "jint"), ("j", "jint")] 122 | check: not pd.isConstructor 123 | check: not pd.isStatic 124 | check: not pd.isProp 125 | check: not pd.isFinal 126 | check: pd.isExported 127 | 128 | parseProcDefTest pd: 129 | proc staticMethod(i: jint): jshort {.`static`.} 130 | check: pd.name == "staticMethod" 131 | check: pd.jName == "staticMethod" 132 | check: pd.retType == "jshort" 133 | check: pd.params == @[("i", "jint")] 134 | check: not pd.isConstructor 135 | check: pd.isStatic 136 | check: not pd.isProp 137 | check: not pd.isFinal 138 | check: not pd.isExported 139 | 140 | parseProcDefTest pd: 141 | proc `method`*: Map[string,jint] 142 | check: pd.name == "method" 143 | check: pd.jName == "method" 144 | check: pd.retType == "Map[string,jint]" 145 | check: pd.params == newSeq[ProcParam]() 146 | check: not pd.isConstructor 147 | check: not pd.isStatic 148 | check: not pd.isProp 149 | check: not pd.isFinal 150 | check: pd.isExported 151 | 152 | parseProcDefTest pd: 153 | proc `method`*(m: Map[string,jint]): jshort 154 | check: pd.name == "method" 155 | check: pd.jName == "method" 156 | check: pd.retType == "jshort" 157 | check: pd.params == @[("m", "Map[string,jint]")] 158 | check: not pd.isConstructor 159 | check: not pd.isStatic 160 | check: not pd.isProp 161 | check: not pd.isFinal 162 | check: pd.isExported 163 | 164 | test "jni_generator - proc def - properties": 165 | var pd: ProcDef 166 | 167 | parseProcDefTest pd: 168 | proc `out`*(): JVMObject {.prop, final, `static`.} 169 | check: pd.name == "out" 170 | check: pd.jName == "out" 171 | check: pd.retType == "JVMObject" 172 | check: pd.params.len == 0 173 | check: not pd.isConstructor 174 | check: pd.isStatic 175 | check: pd.isProp 176 | check: pd.isFinal 177 | check: pd.isExported 178 | 179 | test "jni_generator - proc def - generics": 180 | var pd: ProcDef 181 | 182 | parseProcDefTest pd: 183 | proc setAt[K,V](k: K, v: V) 184 | check: pd.name == "setAt" 185 | check: pd.genericTypes == @["K", "V"] 186 | 187 | parseProcDefTest pd: 188 | proc genericProp[V]: V {.prop.} 189 | check: pd.name == "genericProp" 190 | check: pd.genericTypes == @["V"] 191 | check: pd.isProp 192 | check: not pd.isStatic 193 | 194 | parseProcDefTest pd: 195 | proc genericProp*[V]: V {.prop.} 196 | check: pd.name == "genericProp" 197 | check: pd.genericTypes == @["V"] 198 | check: pd.isProp 199 | check: pd.isExported 200 | check: not pd.isStatic 201 | 202 | test "jni_generator - class def - header": 203 | var cd: ClassDef 204 | 205 | parseClassDefTest cd: 206 | java.lang.String of JVMObject 207 | 208 | check: cd.name == "String" 209 | check: cd.jName == "java.lang.String" 210 | check: cd.parent == "JVMObject" 211 | check: not cd.isExported 212 | 213 | parseClassDefTest cd: 214 | java.lang.String as JVMString of JVMObject 215 | 216 | check: cd.name == "JVMString" 217 | check: cd.jName == "java.lang.String" 218 | check: cd.parent == "JVMObject" 219 | check: not cd.isExported 220 | 221 | parseClassDefTest cd: 222 | java.lang.String* of JVMObject 223 | 224 | check: cd.name == "String" 225 | check: cd.jName == "java.lang.String" 226 | check: cd.parent == "JVMObject" 227 | check: cd.isExported 228 | 229 | parseClassDefTest cd: 230 | java.lang.String as JVMString* of JVMObject 231 | 232 | check: cd.name == "JVMString" 233 | check: cd.jName == "java.lang.String" 234 | check: cd.parent == "JVMObject" 235 | check: cd.isExported 236 | 237 | parseClassDefTest cd: 238 | InnerTestClass$InnerClass of JVMObject 239 | check: cd.name == "InnerClass" 240 | check: cd.jName == "InnerTestClass$InnerClass" 241 | check: cd.parent == "JVMObject" 242 | check: not cd.isExported 243 | 244 | test "jni_generator - class def - generic header": 245 | var cd: ClassDef 246 | 247 | parseClassDefTest cd: 248 | java.util.List[T] of JVMObject 249 | check: cd.name == "List" 250 | check: cd.jName == "java.util.List" 251 | check: cd.genericTypes == @["T"] 252 | 253 | parseClassDefTest cd: 254 | java.util.Map[K,V] of JVMObject 255 | check: cd.name == "Map" 256 | check: cd.jName == "java.util.Map" 257 | check: cd.genericTypes == @["K", "V"] 258 | 259 | parseClassDefTest cd: 260 | java.util.HashMap[K,V] of Map[K,V] 261 | check: cd.name == "HashMap" 262 | check: cd.jName == "java.util.HashMap" 263 | check: cd.genericTypes == @["K", "V"] 264 | check: cd.parentGenericTypes == @["K", "V"] 265 | check: cd.parent == "Map" 266 | 267 | parseClassDefTest cd: 268 | java.util.Map2*[K,V] of JVMObject 269 | check: cd.name == "Map2" 270 | check: cd.jName == "java.util.Map2" 271 | check: cd.genericTypes == @["K", "V"] 272 | check: cd.isExported 273 | 274 | parseClassDefTest cd: 275 | java.util.HashMap2*[K,V] of Map2[K,V] 276 | check: cd.name == "HashMap2" 277 | check: cd.jName == "java.util.HashMap2" 278 | check: cd.genericTypes == @["K", "V"] 279 | check: cd.parentGenericTypes == @["K", "V"] 280 | check: cd.parent == "Map2" 281 | check: cd.isExported 282 | 283 | parseClassDefTest cd: 284 | java.util.Map$Entry*[K,V] as MapEntry of JVMObject 285 | check: cd.name == "MapEntry" 286 | check: cd.jName == "java.util.Map$Entry" 287 | check: cd.genericTypes == @["K", "V"] 288 | check: cd.isExported 289 | 290 | test "jni_generator - import class": 291 | jclass java.lang.String1 of JVMObject: 292 | proc new 293 | check: declared(String1) 294 | check: String1.jniSig == sigForClass"java.lang.String1" 295 | jclass java.lang.String as JVMString1 of JVMObject: 296 | proc new 297 | check: declared(JVMString1) 298 | check: JVMString1.jniSig == sigForClass"java.lang.String" 299 | check: declared(String2) 300 | check: String2.jniSig == sigForClass"java.lang.String2" 301 | check: declared(JVMString2) 302 | check: JVMString2.jniSig == sigForClass"java.lang.String" 303 | 304 | jclass ConstructorTestClass of JVMObject: 305 | proc new 306 | proc new(i: jint) 307 | proc new(s: string) 308 | proc new(i: jint, d: jdouble, s: string) 309 | proc new(ints: openarray[jint]) 310 | proc new(strings: openarray[string]) 311 | proc new(c: ConstructorTestClass) 312 | proc new(c: openarray[ConstructorTestClass]) 313 | 314 | test "jni_generator - TestClass - constructors": 315 | var o = ConstructorTestClass.new 316 | check: o.toStringRaw == "Empty constructor called" 317 | o = ConstructorTestClass.new(1.jint) 318 | check: o.toStringRaw == "Int constructor called, 1" 319 | o = ConstructorTestClass.new("hi!") 320 | check: o.toStringRaw == "String constructor called, hi!" 321 | o = ConstructorTestClass.new(1, 2.0, "str") 322 | check: o.toStringRaw == "Multiparameter constructor called, 1, 2.0, str" 323 | o = ConstructorTestClass.new(@[1.jint,2,3]) 324 | check: o.toStringRaw == "Int array constructor called, 1, 2, 3" 325 | o = ConstructorTestClass.new(@["a", "b", "c"]) 326 | check: o.toStringRaw == "String array constructor called, a, b, c" 327 | o = ConstructorTestClass.new(o) 328 | check: o.toStringRaw == "String array constructor called, a, b, c" 329 | let cc = [ConstructorTestClass.new(), ConstructorTestClass.new(1)] 330 | o = ConstructorTestClass.new(cc) 331 | check: o.toStringRaw == "Empty constructor called\nInt constructor called, 1\n" 332 | 333 | jclass MethodTestClass of JVMObject: 334 | proc new 335 | proc add(x, y: jint): jint {.`static`, importc: "addStatic".} 336 | proc addToMem(x: jint): jint {.importc: "addToMem".} 337 | proc factory(i: jint): MethodTestClass {.`static`.} 338 | proc getStrings: seq[string] 339 | proc readBytes(buf: JVMByteArray): jint 340 | 341 | test "jni_generator - TestClass - methods": 342 | check: MethodTestClass.add(1, 2) == 3 343 | let o = MethodTestClass.new 344 | check: o.addToMem(2) == 2 345 | check: o.addToMem(3) == 5 346 | check: MethodTestClass.factory(5).addToMem(1) == 6 347 | check: o.getStrings == @["Hello", "world!"] 348 | 349 | let buf = newArray(jbyte, 10) 350 | check: o.readBytes(buf) == 4 351 | for i in countup(0, 3): 352 | check: buf[i] == i 353 | 354 | let buf2 = newArray(jbyte, 2) 355 | check: o.readBytes(buf2) == 2 356 | for i in countup(0, 1): 357 | check: buf2[i] == i 358 | 359 | jclassDef PropsTestClass of JVMObject 360 | jclassImpl PropsTestClass of JVMObject: 361 | proc new 362 | proc staticInt: jint {.prop, `static`.} 363 | proc instanceInt: jint {.prop.} 364 | proc inst: PropsTestClass {.prop, `static`, final.} 365 | proc instanceString: string {.prop.} 366 | proc staticBool: bool {.prop, `static`.} 367 | 368 | test "jni_generator - TestClass - properties": 369 | check: PropsTestClass.staticInt == 100 370 | PropsTestClass.staticInt = 200 371 | check: PropsTestClass.staticInt == 200 372 | let o = PropsTestClass.new 373 | check: o.instanceInt == 100 374 | o.instanceInt = 300 375 | check: o.instanceInt == 300 376 | check PropsTestClass.inst.instanceInt == 100 377 | PropsTestClass.inst.instanceString = "Hello, world!" 378 | check: PropsTestClass.inst.instanceString == "Hello, world!" 379 | check: not PropsTestClass.staticBool 380 | PropsTestClass.staticBool = true 381 | check: PropsTestClass.staticBool 382 | 383 | jclass InnerTestClass of JVMObject: 384 | proc new 385 | jclass InnerTestClass$InnerClass of JVMObject: 386 | proc new(p: InnerTestClass) 387 | proc intField: jint {.prop.} 388 | 389 | test "jni_generator - TestClass - inner classes": 390 | let p = InnerTestClass.new 391 | let o = InnerClass.new(p) 392 | check: o.intField == 100 393 | 394 | jclass java.util.List[V] of JVMObject: 395 | proc get[V](i: jint): V 396 | 397 | jclass GenericsTestClass[V] of JVMObject: 398 | proc new[V](v: V) 399 | proc genericProp[V]: V {.prop.} 400 | proc getGenericValue[V]: V 401 | proc setGenericValue[V](v: V) 402 | proc getListOfValues[V](count: jint): List[V] 403 | 404 | test "jni_generator - TestClass - generics": 405 | let o = GenericsTestClass[string].new("hello") 406 | o.genericProp = "hello, world!" 407 | check: o.genericProp == "hello, world!" 408 | o.setGenericValue("hi!") 409 | check: o.getGenericValue == "hi!" 410 | let l = o.getListOfValues(3) 411 | for i in 0..2: 412 | check: l.get(i.jint) == "hi!" 413 | 414 | jclass BaseClass[V] of JVMObject: 415 | proc new[V](v: V) 416 | proc baseMethod[V]: V 417 | proc overridedMethod[V]: V 418 | 419 | jclass ChildClass[V] of BaseClass[V]: 420 | proc new[V](base, ch: V) 421 | proc childMethod[V]: V 422 | 423 | test "jni_generator - TestClass - inheritance": 424 | let b = BaseClass[string].new("Base") 425 | let c = ChildClass[string].new("Base", "Child") 426 | check: b.baseMethod == b.overridedMethod 427 | check: c.childMethod == c.overridedMethod 428 | check: b.overridedMethod != c.overridedMethod 429 | 430 | test "jni_generator - instanceOf": 431 | 432 | let 433 | i = newJVMObject(Integer.new(1).get) 434 | s = newJVMObject(String.new().get) 435 | 436 | check: i.instanceOf(Integer) 437 | check: i.instanceOf(Object) 438 | check: s.instanceOf(String) 439 | check: s.instanceOf(Object) 440 | check: not i.instanceOf(String) 441 | check: not s.instanceOf(Integer) 442 | 443 | test "jni_generator - jcast": 444 | 445 | let 446 | s = newJVMObject(String.new().get) 447 | 448 | check: jcast[String](s) is String 449 | check: jcast[Object](s) is Object 450 | expect ObjectConversionDefect: 451 | discard jcast[Integer](s) 452 | -------------------------------------------------------------------------------- /tests/test_jni_wrapper.nim: -------------------------------------------------------------------------------- 1 | import ../jnim/private/jni_wrapper, 2 | unittest, 3 | strutils 4 | 5 | suite "jni_wrapper": 6 | 7 | var vm: JavaVMPtr 8 | var env: JNIEnvPtr 9 | 10 | const version = JNI_VERSION_1_6 11 | 12 | test "JNI - link with JVM library": 13 | linkWithJVMLib() 14 | require isJVMLoaded() == true 15 | 16 | test "JNI - init JVM": 17 | var args: JavaVMInitArgs 18 | args.version = version 19 | let res = JNI_CreateJavaVM(addr vm, cast[ptr pointer](addr env), addr args) 20 | require res == 0.jint 21 | 22 | test "JNI - test version": 23 | check env.GetVersion(env) >= version 24 | 25 | template chkEx: untyped = 26 | if env.ExceptionCheck(env) != JVM_FALSE: 27 | env.ExceptionDescribe(env) 28 | require false 29 | 30 | test "JNI - call System.out.println": 31 | let cls = env.FindClass(env, fqcn"java.lang.System") 32 | chkEx 33 | let outId = env.GetStaticFieldID(env, cls, "out", sigForClass"java.io.PrintStream") 34 | chkEx 35 | let `out` = env.GetStaticObjectField(env, cls, outId) 36 | chkEx 37 | defer: env.DeleteLocalRef(env, `out`) 38 | let outCls = env.GetObjectClass(env, `out`) 39 | chkEx 40 | let printlnId = env.GetMethodID(env, outCls, "println", "($#)V" % sigForClass"java.lang.String") 41 | chkEx 42 | var str = env.NewStringUTF(env, "Hello, world!") 43 | chkEx 44 | defer: env.DeleteLocalRef(env, `str`) 45 | var val = str.jobject.toJValue 46 | env.CallVoidMethodA(env, `out`, printlnId, addr val) 47 | chkEx 48 | 49 | test "JNI - deinit JVM": 50 | check vm.DestroyJavaVM(vm) == 0 51 | -------------------------------------------------------------------------------- /tests/test_jvm_finder.nim: -------------------------------------------------------------------------------- 1 | import ../jnim, unittest, options 2 | 3 | const CT_JVM = findJVM().get # findJVM should work in compile time 4 | 5 | suite "jvm_finder": 6 | test "jvm_finder - Find JVM": 7 | echo findJVM() 8 | echo CT_JVM 9 | --------------------------------------------------------------------------------