Skip to content

Commit 4625a51

Browse files
authored
Merge pull request #112 from mkitti/zot
Add a JProxy subpackage
2 parents f7f68b0 + e72872a commit 4625a51

File tree

13 files changed

+2102
-0
lines changed

13 files changed

+2102
-0
lines changed

.travis.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ jobs:
4444
branches:
4545
only:
4646
- master
47+
- zot
4748
- v0.7
4849
- /release-.*/
4950

JProxies/Project.toml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
name = "JProxies"
2+
uuid = "5876e902-ba5b-11ea-3851-9ff53fa1e944"
3+
version = "0.0.1"
4+
5+
[deps]
6+
JavaCall = "494afd89-becb-516b-aafa-70d2670c0337"
7+
8+
[compat]
9+
JavaCall = "0.7"
10+
11+
[extras]
12+
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
13+
14+
[targets]
15+
test = ["Test"]

JProxies/doc/index.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
## JProxies
2+
JProxies lets you use Java-like syntax to access fields and methods in Java. You can:
3+
* Get field values
4+
* Set field values
5+
* Call methods
6+
* Create static proxies to access static members
7+
Primitive types and strings are converted to Julia objects on field accesses and method returns and converted back to Java types when sent as arguments to Java methods.
8+
*NOTE: Because of this, if you need to call Java methods on a string that you got from Java, you'll have to use `JProxy(str)` to convert the Julia string to a proxied Java string*
9+
To invoke static methods, use @class (see below).
10+
To get a JProxy's Java object, use `JavaObject(proxy)`
11+
### Examples
12+
```jldoctest
13+
julia> a=JProxy(@jimport(java.util.ArrayList))()
14+
[]
15+
julia> a.size()
16+
0
17+
julia> a.add("hello")
18+
true
19+
julia> a.get(0)
20+
"hello"
21+
julia> a.isEmpty()
22+
false
23+
24+
julia> a.toString()
25+
"[hello]"
26+
27+
julia> b = a.clone()
28+
[hello]
29+
30+
julia> b.add("derp")
31+
true
32+
33+
julia> a == b
34+
false
35+
36+
julia> b == b
37+
true
38+
39+
julia> @class(java.lang.System).getName()
40+
"java.lang.System"
41+
42+
julia> @class(java.lang.System;static=true).out.println("hello")
43+
hello
44+
```

JProxies/src/JProxies.jl

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module JProxies
2+
import JavaCall: JavaCall, JNI,
3+
javaclassname, metaclass, getreturntype, convert_args, convert_arg, geterror,
4+
JavaObject, JavaMetaClass,
5+
jint, jlong, jbyte, jboolean, jchar, jshort, jfloat, jdouble,
6+
JObject, JClass, JMethod, JConstructor, JField, JString,
7+
@jimport, jcall, jfield, isnull,
8+
getname, getclass, listmethods, getreturntype, getparametertypes, classforname,
9+
listfields, gettype,
10+
narrow
11+
12+
13+
import Base: convert
14+
15+
export JProxy, @class, interfacehas, staticproxy, @jimport
16+
17+
include("proxy.jl")
18+
end

JProxies/src/gen.jl

Lines changed: 220 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,220 @@
1+
# This code is not used yet
2+
# It was moved from the work-in-progress code in proxy.jl
3+
4+
struct GenInfo
5+
code
6+
typeCode
7+
deps
8+
classList
9+
methodDicts
10+
fielddicts
11+
end
12+
13+
struct GenArgInfo
14+
name::Symbol
15+
javaType::Type
16+
juliaType
17+
spec
18+
end
19+
20+
const genned = Set()
21+
22+
hasClass(name::AbstractString) = hasClass(Symbol(name))
23+
hasClass(name::Symbol) = name in genned
24+
hasClass(gen, name::AbstractString) = hasClass(gen, Symbol(name))
25+
hasClass(gen, name::Symbol) = name in genned || haskey(gen.methodDicts, string(name))
26+
27+
function genTypeDecl(name::AbstractString, supername::Symbol, gen)
28+
if string(name) != "String" && !haskey(types, Symbol(name)) && !haskey(gen.methodDicts, name)
29+
typeName = typeNameFor(name)
30+
push!(gen.typeCode, :(abstract type $typeName <: $supername end))
31+
end
32+
end
33+
34+
function registerclass(name::AbstractString, classType::Type)
35+
registerclass(Symbol(name), classType)
36+
end
37+
function registerclass(name::Symbol, classType::Type)
38+
if !(classType <: Union{Array, String}) && !haskey(types, name)
39+
types[name] = classType
40+
end
41+
infoFor(classforname(string(name)))
42+
end
43+
44+
gen(name::Symbol; genmode=:none, print=false, eval=true) = _gen(classforname(string(name)), genmode, print, eval)
45+
gen(name::AbstractString; genmode=:none, print=false, eval=true) = _gen(classforname(name), genmode, print, eval)
46+
gen(pxy::JProxy{T, C}) where {T, C} = gen(C)
47+
gen(class::JClass; genmode=:none, print=false, eval=true) = _gen(class, genmode, eval)
48+
function _gen(class::JClass, genmode, print, evalResult)
49+
n = legalClassName(class)
50+
gen = GenInfo()
51+
genClass(class, gen)
52+
if genmode == :deep
53+
while !isempty(gen.deps)
54+
cls = pop!(gen.deps)
55+
!hasClass(gen, cls) && genClass(classforname(string(cls)), gen)
56+
end
57+
else
58+
while !isempty(gen.deps)
59+
cls = pop!(gen.deps)
60+
!hasClass(gen, cls) && genType(classforname(string(cls)), gen)
61+
end
62+
end
63+
expr = :(begin $(gen.typeCode...); $(gen.code...); $(genClasses(getname.(gen.classList))...); end)
64+
if print
65+
for e in expr.args
66+
println(e)
67+
end
68+
end
69+
evalResult && eval(expr)
70+
end
71+
72+
function genType(class, gen::GenInfo)
73+
name = getname(class)
74+
sc = superclass(class)
75+
push!(genned, Symbol(legalClassName(class)))
76+
if !isNull(sc)
77+
if !(Symbol(legalClassName(sc)) in genned)
78+
genType(getcomponentclass(sc), gen)
79+
end
80+
supertype = typeNameFor(sc)
81+
cType = componentType(supertype)
82+
genTypeDecl(name, cType, gen)
83+
else
84+
genTypeDecl(name, :java_lang, gen)
85+
end
86+
end
87+
88+
genClass(class::JClass, gen::GenInfo) = genClass(class, gen, infoFor(class))
89+
function genClass(class::JClass, gen::GenInfo, info::JClassInfo)
90+
name = getname(class)
91+
if !(Symbol(name) in genned)
92+
gen.fielddicts[legalClassName(class)] = fielddict(class)
93+
push!(gen.classList, class)
94+
sc = superclass(class)
95+
#@verbose("SUPERCLASS OF $name is $(isNull(sc) ? "" : "not ")null")
96+
push!(genned, Symbol(legalClassName(class)))
97+
if !isNull(sc)
98+
supertype = typeNameFor(sc)
99+
cType = componentType(supertype)
100+
!hasClass(gen, cType) && genClass(sc, gen)
101+
genTypeDecl(name, cType, gen)
102+
else
103+
genTypeDecl(name, :java_lang, gen)
104+
end
105+
genMethods(class, gen, info)
106+
end
107+
end
108+
109+
GenInfo() = GenInfo([], [], Set(), [], Dict(), Dict())
110+
111+
function GenArgInfo(index, info::JMethodInfo, gen::GenInfo)
112+
javaType = info.argTypes[index]
113+
GenArgInfo(Symbol("a" * string(index)), javaType, argType(javaType, gen), argSpec(javaType, gen))
114+
end
115+
116+
argType(t, gen) = t
117+
argType(::Type{JavaObject{Symbol("java.lang.String")}}, gen) = String
118+
argType(::Type{JavaObject{Symbol("java.lang.Object")}}, gen) = :JLegalArg
119+
argType(::Type{<: Number}, gen) = Number
120+
argType(typ::Type{JavaObject{T}}, gen) where T = :(JProxy{<:$(typeNameFor(T, gen)), T})
121+
122+
argSpec(t, gen) = t
123+
argSpec(::Type{JavaObject{Symbol("java.lang.String")}}, gen) = String
124+
argSpec(::Type{JavaObject{Symbol("java.lang.Object")}}, gen) = :JObject
125+
argSpec(::Type{<: Number}, gen) = Number
126+
argSpec(typ::Type{JavaObject{T}}, gen) where T = :(JProxy{<:$(typeNameFor(T, gen)), T})
127+
argSpec(arg::GenArgInfo) = arg.spec
128+
129+
typeNameFor(T::Symbol, gen::GenInfo) = typeNameFor(string(T), gen)
130+
function typeNameFor(T::AbstractString, gen::GenInfo)
131+
aType, dims = arrayinfo(T)
132+
c = dims != 0 ? aType : T
133+
csym = Symbol(c)
134+
if (dims == 0 || length(c) > 1) && !(csym in gen.deps) && !hasClass(gen, csym) && !get(typeInfo, c, genericFieldInfo).primitive
135+
push!(gen.deps, csym)
136+
end
137+
typeNameFor(T)
138+
end
139+
140+
function argCode(arg::GenArgInfo)
141+
argname = arg.name
142+
if arg.juliaType == String
143+
argname
144+
elseif arg.juliaType == JLegalArg
145+
:(box($argname))
146+
elseif arg.juliaType == Number
147+
:($(arg.javaType)($argname))
148+
else
149+
argname
150+
end
151+
end
152+
153+
function fieldEntry((name, fld))
154+
fieldType = JavaObject{Symbol(legalClassName(fld.owner))}
155+
name => :(jfield($(fld.typeInfo.class), $(string(name)), $fieldType))
156+
end
157+
158+
function genMethods(class, gen, info)
159+
methodList = listmethods(class)
160+
classname = legalClassName(class)
161+
gen.methodDicts[classname] = methods = Dict()
162+
typeName = typeNameFor(classname, gen)
163+
classVar = Symbol("class_" * string(typeName))
164+
fieldsVar = Symbol("fields_" * string(typeName))
165+
methodsVar = Symbol("staticMethods_" * string(typeName))
166+
push!(gen.code, :($classVar = classforname($classname)))
167+
push!(gen.code, :($fieldsVar = Dict([$([fieldEntry(f) for f in gen.fielddicts[classname]]...)])))
168+
push!(gen.code, :($methodsVar = Set($([string(n) for (n, m) in info.methods if any(x->x.static, m)]))))
169+
push!(gen.code, :(function Base.getproperty(p::JProxy{T, C}, name::Symbol) where {T <: $typeName, C}
170+
if (f = get($fieldsVar, name, nothing)) != nothing
171+
getField(p, name, f)
172+
else
173+
JMethodProxy(name, $typeName, p, emptyset)
174+
end
175+
end))
176+
for nameSym in sort(collect(keys(info.methods)))
177+
name = string(nameSym)
178+
multiple = length(info.methods[nameSym]) > 1
179+
symId = 0
180+
for minfo in info.methods[nameSym]
181+
owner = javaType(minfo.owner)
182+
if isSame(class.ptr, minfo.owner.ptr)
183+
symId += 1
184+
args = (GenArgInfo(i, minfo, gen) for i in 1:length(minfo.argTypes))
185+
argDecs = (:($(arg.name)::$(arg.juliaType)) for arg in args)
186+
methodIdName = Symbol("method_" * string(typeName) * "__" * name * (multiple ? string(symId) : ""))
187+
callinfo = jnicalls[minfo.typeInfo.classname]
188+
push!(gen.code, :($methodIdName = getmethodid($(minfo.static), $classVar, $name, $(legalClassName(minfo.returnClass)), $(legalClassName.(minfo.argClasses)))))
189+
push!(gen.code, :(function (pxy::JMethodProxy{Symbol($name), <: $typeName})($(argDecs...))::$(genReturnType(minfo, gen))
190+
@verbose($("Generated method $name$(multiple ? "(" * string(symId) * ")" : "")"))
191+
$(genConvertResult(minfo.typeInfo.convertType, minfo, :(call(pxy.obj, $methodIdName, $(static ? callinfo.static : callinfo.instance), $(minfo.typeInfo.juliaType), ($(argSpec.(args)...),), $((argCode(arg) for arg in args)...)))))
192+
end))
193+
end
194+
end
195+
end
196+
push!(gen.code, :(push!(genned, Symbol($(legalClassName(class))))))
197+
end
198+
199+
function genReturnType(methodInfo, gen)
200+
t = methodInfo.typeInfo.convertType
201+
if methodInfo.typeInfo.primitive || t <: String || t == Nothing
202+
t
203+
else
204+
:(JProxy{<:$(typeNameFor(methodInfo.returnType, gen))})
205+
end
206+
end
207+
208+
genConvertResult(toType::Type{Bool}, info, expr) = :($expr != 0)
209+
genConvertResult(toType::Type{String}, info, expr) = :(unsafe_string($expr))
210+
genConvertResult(toType::Type{<:JBoxTypes}, info, expr) = :(unbox($(toType.parameters[1]), $expr))
211+
function genConvertResult(toType, info, expr)
212+
if isVoid(info) || info.typeInfo.primitive
213+
expr
214+
else
215+
:(asJulia($toType, $expr))
216+
end
217+
end
218+
219+
genClasses(classNames) = (:(registerclass($name, $(Symbol(typeNameFor(name))))) for name in reverse(classNames))
220+

0 commit comments

Comments
 (0)