[Openmcl-cvs-notifications] r11435 - in /trunk/source/examples/jfli: ./ com/ com/richhickey/ com/richhickey/jfli/ docs/ examples/

gb at clozure.com gb at clozure.com
Thu Nov 27 12:37:00 EST 2008


Author: gb
Date: Thu Nov 27 12:37:00 2008
New Revision: 11435

Log:
Rich Hickey's 'jfli' (Lisp<->Java) bridge, as of jfli091104.zip.
The file 'jni.lisp' is LispWorks-specific FFI code; there'll need
to be something CCL-specific that exports the same interface.

Text files in the .zip were CRLF-terminated; hopefully, svn will
fix that ...

Added:
    trunk/source/examples/jfli/CPL.TXT
    trunk/source/examples/jfli/com/
    trunk/source/examples/jfli/com/richhickey/
    trunk/source/examples/jfli/com/richhickey/jfli/
    trunk/source/examples/jfli/com/richhickey/jfli/LispInvocationHandler.ja=
va
    trunk/source/examples/jfli/docs/
    trunk/source/examples/jfli/docs/bullet.gif   (with props)
    trunk/source/examples/jfli/docs/bullet2.gif   (with props)
    trunk/source/examples/jfli/docs/jfli.css
    trunk/source/examples/jfli/docs/jfli.html
    trunk/source/examples/jfli/docs/jfli_bkgrnd.gif   (with props)
    trunk/source/examples/jfli/docs/jfli_new.gif   (with props)
    trunk/source/examples/jfli/examples/
    trunk/source/examples/jfli/examples/session.lisp
    trunk/source/examples/jfli/examples/swtdemo.lisp
    trunk/source/examples/jfli/jfli.jar   (with props)
    trunk/source/examples/jfli/jfli.lisp
    trunk/source/examples/jfli/jni.lisp
Modified:
    trunk/source/examples/jfli/   (props changed)

Propchange: trunk/source/examples/jfli/
---------------------------------------------------------------------------=
---
--- svn:ignore (added)
+++ svn:ignore Thu Nov 27 12:37:00 2008
@@ -1,0 +1,1 @@
+.cvsignore

Added: trunk/source/examples/jfli/CPL.TXT
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/source/examples/jfli/CPL.TXT (added)
+++ trunk/source/examples/jfli/CPL.TXT Thu Nov 27 12:37:00 2008
@@ -1,0 +1,94 @@
+Common Public License Version 1.0
+
+THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC=
 LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRA=
M CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
+
+
+1. DEFINITIONS =

+
+"Contribution" means:
+
+a) in the case of the initial Contributor, the initial code and documentat=
ion distributed under this Agreement, and =

+
+b) in the case of each subsequent Contributor:
+
+i) changes to the Program, and
+
+ii) additions to the Program;
+
+where such changes and/or additions to the Program originate from and are =
distributed by that particular Contributor. A Contribution 'originates' fro=
m a Contributor if it was added to the Program by such Contributor itself o=
r anyone acting on such Contributor's behalf. Contributions do not include =
additions to the Program which: (i) are separate modules of software distri=
buted in conjunction with the Program under their own license agreement, an=
d (ii) are not derivative works of the Program.
+
+"Contributor" means any person or entity that distributes the Program. =

+
+"Licensed Patents " mean patent claims licensable by a Contributor which a=
re necessarily infringed by the use or sale of its Contribution alone or wh=
en combined with the Program.
+
+"Program" means the Contributions distributed in accordance with this Agre=
ement. =

+
+"Recipient" means anyone who receives the Program under this Agreement, in=
cluding all Contributors. =

+
+
+2. GRANT OF RIGHTS
+
+a) Subject to the terms of this Agreement, each Contributor hereby grants =
Recipient a non-exclusive, worldwide, royalty-free copyright license to rep=
roduce, prepare derivative works of, publicly display, publicly perform, di=
stribute and sublicense the Contribution of such Contributor, if any, and s=
uch derivative works, in source code and object code form.
+
+b) Subject to the terms of this Agreement, each Contributor hereby grants =
Recipient a non-exclusive, worldwide, royalty-free patent license under Lic=
ensed Patents to make, use, sell, offer to sell, import and otherwise trans=
fer the Contribution of such Contributor, if any, in source code and object=
 code form. This patent license shall apply to the combination of the Contr=
ibution and the Program if, at the time the Contribution is added by the Co=
ntributor, such addition of the Contribution causes such combination to be =
covered by the Licensed Patents. The patent license shall not apply to any =
other combinations which include the Contribution. No hardware per se is li=
censed hereunder.
+
+c) Recipient understands that although each Contributor grants the license=
s to its Contributions set forth herein, no assurances are provided by any =
Contributor that the Program does not infringe the patent or other intellec=
tual property rights of any other entity. Each Contributor disclaims any li=
ability to Recipient for claims brought by any other entity based on infrin=
gement of intellectual property rights or otherwise. As a condition to exer=
cising the rights and licenses granted hereunder, each Recipient hereby ass=
umes sole responsibility to secure any other intellectual property rights n=
eeded, if any. For example, if a third party patent license is required to =
allow Recipient to distribute the Program, it is Recipient's responsibility=
 to acquire that license before distributing the Program.
+
+d) Each Contributor represents that to its knowledge it has sufficient cop=
yright rights in its Contribution, if any, to grant the copyright license s=
et forth in this Agreement.
+
+
+3. REQUIREMENTS =

+
+A Contributor may choose to distribute the Program in object code form und=
er its own license agreement, provided that: =

+
+a) it complies with the terms and conditions of this Agreement; and
+
+b) its license agreement:
+
+i) effectively disclaims on behalf of all Contributors all warranties and =
conditions, express and implied, including warranties or conditions of titl=
e and non-infringement, and implied warranties or conditions of merchantabi=
lity and fitness for a particular purpose; =

+
+ii) effectively excludes on behalf of all Contributors all liability for d=
amages, including direct, indirect, special, incidental and consequential d=
amages, such as lost profits; =

+
+iii) states that any provisions which differ from this Agreement are offer=
ed by that Contributor alone and not by any other party; and =

+
+iv) states that source code for the Program is available from such Contrib=
utor, and informs licensees how to obtain it in a reasonable manner on or t=
hrough a medium customarily used for software exchange. =

+
+When the Program is made available in source code form:
+
+a) it must be made available under this Agreement; and
+
+b) a copy of this Agreement must be included with each copy of the Program=
. =

+
+Contributors may not remove or alter any copyright notices contained withi=
n the Program.
+
+Each Contributor must identify itself as the originator of its Contributio=
n, if any, in a manner that reasonably allows subsequent Recipients to iden=
tify the originator of the Contribution. =

+
+
+4. COMMERCIAL DISTRIBUTION =

+
+Commercial distributors of software may accept certain responsibilities wi=
th respect to end users, business partners and the like. While this license=
 is intended to facilitate the commercial use of the Program, the Contribut=
or who includes the Program in a commercial product offering should do so i=
n a manner which does not create potential liability for other Contributors=
. Therefore, if a Contributor includes the Program in a commercial product =
offering, such Contributor ("Commercial Contributor") hereby agrees to defe=
nd and indemnify every other Contributor ("Indemnified Contributor") agains=
t any losses, damages and costs (collectively "Losses") arising from claims=
, lawsuits and other legal actions brought by a third party against the Ind=
emnified Contributor to the extent caused by the acts or omissions of such =
Commercial Contributor in connection with its distribution of the Program i=
n a commercial product offering. The obligations in this section do not app=
ly to any claims or Losses relating to any actual or alleged intellectual p=
roperty infringement. In order to qualify, an Indemnified Contributor must:=
 a) promptly notify the Commercial Contributor in writing of such claim, an=
d b) allow the Commercial Contributor to control, and cooperate with the Co=
mmercial Contributor in, the defense and any related settlement negotiation=
s. The Indemnified Contributor may participate in any such claim at its own=
 expense. =

+
+For example, a Contributor might include the Program in a commercial produ=
ct offering, Product X. That Contributor is then a Commercial Contributor. =
If that Commercial Contributor then makes performance claims, or offers war=
ranties related to Product X, those performance claims and warranties are s=
uch Commercial Contributor's responsibility alone. Under this section, the =
Commercial Contributor would have to defend claims against the other Contri=
butors related to those performance claims and warranties, and if a court r=
equires any other Contributor to pay any damages as a result, the Commercia=
l Contributor must pay those damages. =

+
+
+5. NO WARRANTY
+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED O=
N AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EX=
PRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITION=
S OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR P=
URPOSE. Each Recipient is solely responsible for determining the appropriat=
eness of using and distributing the Program and assumes all risks associate=
d with its exercise of rights under this Agreement, including but not limit=
ed to the risks and costs of program errors, compliance with applicable law=
s, damage to or loss of data, programs or equipment, and unavailability or =
interruption of operations. =

+
+
+6. DISCLAIMER OF LIABILITY =

+
+EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY=
 CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL=
, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATIO=
N LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN =
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARI=
SING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCI=
SE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF S=
UCH DAMAGES. =

+
+
+7. GENERAL
+
+If any provision of this Agreement is invalid or unenforceable under appli=
cable law, it shall not affect the validity or enforceability of the remain=
der of the terms of this Agreement, and without further action by the parti=
es hereto, such provision shall be reformed to the minimum extent necessary=
 to make such provision valid and enforceable. =

+
+If Recipient institutes patent litigation against a Contributor with respe=
ct to a patent applicable to software (including a cross-claim or countercl=
aim in a lawsuit), then any patent licenses granted by that Contributor to =
such Recipient under this Agreement shall terminate as of the date such lit=
igation is filed. In addition, if Recipient institutes patent litigation ag=
ainst any entity (including a cross-claim or counterclaim in a lawsuit) all=
eging that the Program itself (excluding combinations of the Program with o=
ther software or hardware) infringes such Recipient's patent(s), then such =
Recipient's rights granted under Section 2(b) shall terminate as of the dat=
e such litigation is filed.
+
+All Recipient's rights under this Agreement shall terminate if it fails to=
 comply with any of the material terms or conditions of this Agreement and =
does not cure such failure in a reasonable period of time after becoming aw=
are of such noncompliance. If all Recipient's rights under this Agreement t=
erminate, Recipient agrees to cease use and distribution of the Program as =
soon as reasonably practicable. However, Recipient's obligations under this=
 Agreement and any licenses granted by Recipient relating to the Program sh=
all continue and survive. =

+
+Everyone is permitted to copy and distribute copies of this Agreement, but=
 in order to avoid inconsistency the Agreement is copyrighted and may only =
be modified in the following manner. The Agreement Steward reserves the rig=
ht to publish new versions (including revisions) of this Agreement from tim=
e to time. No one other than the Agreement Steward has the right to modify =
this Agreement. IBM is the initial Agreement Steward. IBM may assign the re=
sponsibility to serve as the Agreement Steward to a suitable separate entit=
y. Each new version of the Agreement will be given a distinguishing version=
 number. The Program (including Contributions) may always be distributed su=
bject to the version of the Agreement under which it was received. In addit=
ion, after a new version of the Agreement is published, Contributor may ele=
ct to distribute the Program (including its Contributions) under the new ve=
rsion. Except as expressly stated in Sections 2(a) and 2(b) above, Recipien=
t receives no rights or licenses to the intellectual property of any Contri=
butor under this Agreement, whether expressly, by implication, estoppel or =
otherwise. All rights in the Program not expressly granted under this Agree=
ment are reserved. =

+
+This Agreement is governed by the laws of the State of New York and the in=
tellectual property laws of the United States of America. No party to this =
Agreement will bring a legal action under this Agreement more than one year=
 after the cause of action arose. Each party waives its rights to a jury tr=
ial in any resulting litigation.

Added: trunk/source/examples/jfli/com/richhickey/jfli/LispInvocationHandler=
.java
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/source/examples/jfli/com/richhickey/jfli/LispInvocationHandler.ja=
va (added)
+++ trunk/source/examples/jfli/com/richhickey/jfli/LispInvocationHandler.ja=
va Thu Nov 27 12:37:00 2008
@@ -1,0 +1,17 @@
+package com.richhickey.jfli;
+
+//    Copyright (c) Rich Hickey. All rights reserved.
+//    The use and distribution terms for this software are covered by the
+//    Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
+//    which can be found in the file CPL.TXT at the root of this distribut=
ion.
+//    By using this software in any fashion, you are agreeing to be bound =
by
+//    the terms of this license.
+//    You must not remove this notice, or any other, from this software.
+
+import java.lang.*;
+import java.lang.reflect.*;
+
+public class LispInvocationHandler implements InvocationHandler
+{
+public native Object invoke(Object proxy,Method method, Object[] args) thr=
ows Throwable;
+}

Added: trunk/source/examples/jfli/docs/bullet.gif
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
Binary file - no diff available.

Propchange: trunk/source/examples/jfli/docs/bullet.gif
---------------------------------------------------------------------------=
---
    svn:mime-type =3D application/octet-stream

Added: trunk/source/examples/jfli/docs/bullet2.gif
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
Binary file - no diff available.

Propchange: trunk/source/examples/jfli/docs/bullet2.gif
---------------------------------------------------------------------------=
---
    svn:mime-type =3D application/octet-stream

Added: trunk/source/examples/jfli/docs/jfli.css
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/source/examples/jfli/docs/jfli.css (added)
+++ trunk/source/examples/jfli/docs/jfli.css Thu Nov 27 12:37:00 2008
@@ -1,0 +1,126 @@
+img {
+  border: 0;
+}
+body {
+  margin: 5px 10% 5px 55px;
+  padding: 5px;
+  border: 0;
+  background: #fff url(jfli_bkgrnd.gif) top left repeat-y;
+  color: #222;
+  font: small/1.6em "Lucida Grande", "Trebuchet MS", "Bitstream Vera Sans"=
, Verdana, Helvetica, sans-serif;
+}
+
+
+a:link {
+  color: #339;
+  text-decoration: none;
+  border-bottom: 2px solid #ccf;
+}
+a:visited {
+  color: #339;
+  text-decoration: none;
+  border-bottom: 2px solid #ddd;
+}
+a:hover {
+  color: #66c;
+  text-decoration: none;
+  border-bottom: 2px solid #ccf;
+}
+a:active {
+  color: #339;
+  text-decoration: none;
+  border-bottom: 2px solid #ccf;
+}
+
+p {
+  margin: 0 0 1em 0;
+  line-height: 140%;
+}
+
+
+h1 {
+  text-indent: -9999px;
+  margin: 0;
+  padding: 0;
+  border: 0;
+}
+	=

+h1 a:link, h1 a:visited {
+  display: block;	=

+  width: 539px;
+  height: 86px;
+  background: #fff url(jfli_new.gif) top left no-repeat;
+  margin: 0;
+  padding: 0;
+  border: 0;
+  text-decoration: none;
+}
+	=

+h1 a:hover {
+  background: #fff url(jfli_new.gif) top left no-repeat;
+  border: 0;
+}
+
+h2 {
+  margin: .75em 0 .25em;
+  font: 170%/1.7em Georgia, serif;
+  color: #66c;
+}
+h3 {
+  margin: 1em 0 .25em;
+  font: 130%/1.4em Georgia, serif;
+  color: #339;
+}
+h4 {
+  margin: 1em 0 .25em;
+  font: 120%/1.3em Georgia, serif;
+  color: #888;
+}
+
+pre, code {
+  color: #404;
+  /*
+  font-family: monaco, "Bitstream Vera Sans Mono", "Courier New", courier,=
 monospace;
+  font-weight: normal;
+  font-size: small;
+  */
+  font: small/1.6em monaco, "Bitstream Vera Sans Mono", "Courier New", cou=
rier, monospace;
+  line-height: 1.4em;
+}
+pre {
+  margin: 1em 0 1.4em;
+  margin-left: 3%;
+  width: 77%;
+  border-left: 4px solid #eee;
+  padding: 2px;
+  padding-left: 10px;
+}
+
+ol li {
+  list-style: decimal outside;
+  line-height: 150%;
+  margin: 0 5px 5px 0;
+  padding: 0 0 0 5px;
+}
+
+ul {
+  margin: 3px 0;
+  padding: 0 0 5px 0;
+}
+	=

+ul li	{
+  line-height: 150%;
+  margin-bottom: 5px;
+  padding-bottom: 0;
+  padding-left: 18px;
+  padding-top: 2px;
+  margin-left: 5px;
+  list-style: none;
+  background:url("bullet.gif") no-repeat 0px .5em;
+}
+	=

+ul ul li {
+  margin-top: 4px;
+  list-style: none;
+  background:url("bullet2.gif") no-repeat 0px .5em;
+}

Added: trunk/source/examples/jfli/docs/jfli.html
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/source/examples/jfli/docs/jfli.html (added)
+++ trunk/source/examples/jfli/docs/jfli.html Thu Nov 27 12:37:00 2008
@@ -1,0 +1,620 @@
+<html>
+<head>
+	<link rel=3D"stylesheet" type=3D"text/css" media=3D"screen" href=3D"jfli.=
css">
+</head>
+	<body>
+	<h1><a href=3D"/" title=3D"Rich Hickey's jfli - a Java Foreign Language I=
nterface for Common Lisp">jfli =

+	- a Java Foreign Language Interface for Common Lisp</a></h1>
+
+	<h5>Copyright (c) Rich Hickey. All rights reserved.</h5>
+
+	<p>
+	The use and distribution terms for this software are covered by the <a hr=
ef=3D"http://opensource.org/licenses/cpl.php">Common =

+	Public License 1.0</a>, which can be found in the file CPL.TXT at the roo=
t of =

+	this distribution. By using this software in any fashion, you are agreein=
g to be =

+	bound by the terms of this license. You must not remove this notice, or a=
ny =

+	other, from this software.
+	</p>
+
+	<h2>Contents</h2>
+
+	<ul>
+		<li>
+			<a href=3D"#intro">Introduction</a>
+		</li>
+		<li>
+			<a href=3D"#download">Download</a>
+		</li>
+		<li>
+			<a href=3D"#setup">Setup and Configuration</a>
+		</li>
+		<li>
+			<a href=3D"#quickstart">Quick Start</a>
+		</li>
+		<li>
+			<a href=3D"#api">API Reference</a>
+			<ul>
+				<li>
+					<a href=3D"#jvmcreation">JVM Creation and Initialization</a>
+				</li>
+				<li>
+					<a href=3D"#wrappergen">Wrapper Generation</a>
+				</li>
+				<li>
+					<a href=3D"#objects">Object Creation</a>
+				</li>
+				<li>
+					<a href=3D"#arrays">Arrays</a>
+				</li>
+				<li>
+					<a href=3D"#proxies">Proxies - Java calling back to Lisp</a>
+				</li>
+				<li>
+					<a href=3D"#utilities">Utilities</a>
+				</li>
+			</ul>
+		</li>
+		<li>
+			<a href=3D"#summary">Summary</a>
+		</li>
+	</ul>
+
+
+	<a name=3D"intro"></a> <h3>Introduction</h3>
+
+	<p>
+	My objective was to provide comprehensive, safe, dynamic and Lisp-y acces=
s to =

+	Java and Java libraries as if they were Lisp libraries, for use in Lisp p=
rograms, =

+	i.e.  with an emphasis on working in Lisp rather than in Java.
+	</p>
+
+	<p>
+	The approach I took was to embed a JVM instance in the Lisp process using=
 JNI. I =

+	was able to do this using LispWorks' own FLI and no C (or Java! *) code, =
which =

+	is a tribute to the LW FLI.  On top of the JNI layer (essentially a wrapp=
er =

+	around the entire JNI API), I built this user-level API using Java Reflec=
tion. =

+	This first version was built with, and contains code specific to, Xanalys=
 <a
+								 href=3D"http://www.lispworks.com/">LispWorks</a>.
+	</p>
+
+
+	<p>
+	<em>jfli</em> ("jay fly") provides:
+	</p>
+
+	<ul>
+
+		<li>
+			Automatic function generation for constructors, fields and methods, eit=
her by =

+			named class, or entire package (sub)trees given a jar file.
+		</li>
+
+		<li>
+			Java -> Lisp package and name mapping with an eye towards lack of surpr=
ise, lack =

+			of conflict, and useful editor completion.
+		</li>
+
+		<li>
+			setf-able setter generation for fields as well as for methods that foll=
ow the =

+			JavaBeans property protocol.
+		</li>
+
+		<li>
+			Java array creation and aref-like access to Java arrays.
+		</li>
+
+		<li>
+			A 'new' macro that allows for keyword-style field and property initiali=
zation.
+		</li>
+
+		<li>
+			Typed references to Java objects with an inheritance hierarchy on the L=
isp side =

+			mirroring that on the Java side - allowing for Lisp methods specialized=
 on Java =

+			class and interface types.
+		</li>
+
+		<li>
+			Implementation of arbitrary Java interfaces in Lisp, and callbacks from=
 Java to =

+			Lisp via those interfaces.  (* this required a single 5-line dummy Java=
 proxy =

+			stub, provided with jfli)
+		</li>
+
+		<li>
+			Automatic lifetime maintenance of Lisp-referenced Java objects, boxing/=
unboxing =

+			of primitive args/returns, string conversions, Java exception handling,=
 overload =

+			resolution etc.
+		</li>
+	</ul>
+
+	<p>
+	I built jfli using LWM and LWW (using Apple's and Sun's JVMs respectively=
), and =

+	it works fine on both.  Should be a trivial port to other LispWorks, and =
a =

+	possible port to any Common Lisp with a robust FLI. Should also work with=
 any =

+	JVM with a conformant JNI implementation.
+	</p>
+
+	<a name=3D"download"></a> <h3>Download</h3>
+
+	<p>
+	jfli is hosted on <a href=3D"http://sourceforge.net/projects/jfli/">Sourc=
eForge</a>
+	</p>
+
+	<a name=3D"setup"></a> <h3>Setup and Configuration</h3>
+
+	<p>
+	jfli is supplied in 2 Lisp files and an optional Java .jar file.  The fir=
st Lisp =

+	file, jni.lisp, defines a low-level API to the Java Native Interface, and=
 is not =

+	documented here. The second, jfli.lisp, depends upon jni.lisp, and provid=
es the =

+	user API documented here. Simply compile and load jni.lisp, then compile =
and =

+	load jfli.lisp. <code>(use-package :jfli)</code> and you are ready to use=
 the =

+	API. Note that prior to creating the JVM you must tell the library how to=
 find =

+	the Java JNI library by setting <a
+								  href=3D"#jnilibpath"><code>*jni-lib-path*</code></a>.
+	</p>
+	<p>
+	If you wish to allow for callbacks from Java to Lisp, you must place jfli=
.jar in =

+	your classpath when <a href=3D"#jvmcreation">creating the JVM</a>.
+	</p>
+
+
+	<a name=3D"quickstart"></a> <h3>Quick Start</h3>
+	<p>
+	This sample session presumes you have already compiled jni.lisp and jfli.=
lisp into fasl files.
+	</p>
+	<pre>
+CL-USER 4 > (load "/lisp/jni")
+; Loading fasl file C:\lisp\jni.fsl
+#P"C:/lisp/jni.fsl"
+
+CL-USER 5 > (load "/lisp/jfli")
+; Loading fasl file C:\lisp\jfli.fsl
+#P"C:/lisp/jfli.fsl"
+
+;The user API is entirely in the jfli package
+CL-USER 6 > (use-package :jfli)
+T
+
+;tell the library where Java is located
+CL-USER 7 > (setf *jni-lib-path* "/j2sdk1.4.2_01/jre/bin/client/jvm.dll")
+"/j2sdk1.4.2_01/jre/bin/client/jvm.dll"
+
+;this starts the VM - note how you can set the classpath
+CL-USER 8 > (create-jvm "-Djava.class.path=3D/lisp/jfli.jar")
+0
+#&lt;Pointer: JNI:PVM =3D #x081022A0>
+#&lt;Pointer: JNI:PENV =3D #x0086A858>
+
+;define wrappers for the members of Object
+CL-USER 9 > (def-java-class "java.lang.Object")
+NIL
+
+;and of Properties, a Hashtable-like class
+CL-USER 10 > (def-java-class "java.util.Properties")
+#&lt;STANDARD-CLASS |java.util|:PROPERTIES. 2066B964>
+
+;the above will create these packages if they do not already exist
+;use the packages for easy name access
+
+CL-USER 11 > (use-package "java.lang")
+T
+
+CL-USER 12 > (use-package "java.util")
+T
+
+;create a Properties instance, note keyword-style member inits, string con=
version etc
+;also note typed return value
+CL-USER 13 > (setf p (new properties. :getproperty "fred" "ethel"))
+#&lt;PROPERTIES. 20664A94>
+
+;virtual functions work as normal
+CL-USER 14 > (object.tostring p)
+"{fred=3Dethel}"
+
+;setter was generated for member function because it follows the JavaBeans=
 property protocol
+CL-USER 15 > (setf (properties.getproperty p "ricky") "lucy")
+"lucy"
+
+CL-USER 16 > (object.tostring p)
+"{ricky=3Dlucy, fred=3Dethel}"
+
+CL-USER 17 > (properties.size p)
+2
+
+;totally dynamic access, create wrappers as you need
+CL-USER 18 > (def-java-class "java.lang.Class")
+#&lt;STANDARD-CLASS CLASS. 20680EC4>
+
+CL-USER 19 > (class.getname (object.getclass p))
+"java.util.Properties"
+
+CL-USER 20 > (def-java-class "java.util.Enumeration")
+#&lt;STANDARD-CLASS ENUMERATION. 20669274>
+
+;no need to wait for the vendor to enhance the language - you use Lisp!
+CL-USER 21 > (defmacro doenum ((e enum) &body body)
+               (let ((genum (gensym)))
+                 `(let ((,genum ,enum))
+                    (do ()
+                        ((not (enumeration.hasmoreelements ,genum)))
+                      (let ((,e (enumeration.nextelement ,genum)))
+                        , at body)))))
+DOENUM
+
+;can't do this in Java yet
+CL-USER 22 > (doenum (prop (properties.elements p)) (print (object.tostrin=
g prop)))
+
+"lucy"
+"ethel"
+NIL
+
+;doc strings are created giving original Java signatures and indicating
+overloads
+CL-USER 23 > (documentation 'properties.getproperty 'function)
+"java.lang.String getProperty(java.lang.String,java.lang.String)
+java.lang.String getProperty(java.lang.String)
+"
+
+CL-USER 24 > (documentation 'properties.new 'function)
+"java.util.Properties()
+java.util.Properties(java.util.Properties)
+"
+	</pre>
+
+	<a name=3D"api"></a> <h2>API Reference</h2>
+
+	<a name=3D"jvmcreation"></a> <h3>JVM Creation and Initialization</h3>
+	<ul>
+
+		<li>
+			<a name=3D"jnilibpath"></a><code>*jni-lib-path*</code>
+			<p>
+			Set this to point to your jvm dll prior to calling create-jvm.
+			</p>
+		</li>
+
+		<li>
+			<strong>Function</strong> <code>(create-jvm &rest option-strings) -> un=
specified</code>
+			<p>
+			Creates/starts the JVM. This can only be done once (a Java limitation).=
 <em>You =

+			must call this prior to calling any other jfli function.</em> The optio=
n strings =

+			can be used to control the JVM, esp. the classpath:
+			</p>
+			<p>
+			<pre>(create-jvm "-Djava.class.path=3D/Lisp/jfli.jar")</pre>
+			</p>
+			<p>
+			See the JNI documentation for other initialization options.
+			</p>
+		</li>
+
+		<li>
+			<a name=3D"enableproxies"></a> <strong>Function</strong> <code>(enable-=
java-proxies) =

+			-> unspecified</code>
+			<p>
+			Sets up the Java->Lisp callback support. Must be called (once) before a=
ny calls =

+			to new-proxy, and requires jfli.jar be in the classpath.
+			</p>
+		</li>
+	</ul>
+
+	<a name=3D"wrappergen"></a> <h3>Wrapper Generation</h3>
+
+	<ul>
+		<li>
+			<strong>Macro</strong> <code>(def-java-class full-class-name) -> unspec=
ified</code>
+			<p>
+			Given the package-qualified, case-correct name of a Java class as a str=
ing, will =

+			generate wrapper functions for its public constructors, fields and meth=
ods.
+			</p>
+			<p>
+			The core API for generation interfaces to Java is the def-java-class ma=
cro. This =

+			macro will, at expansion time, use Java reflection to find all of the p=
ublic =

+			constructors, fields and methods of the given class and generate functi=
ons to =

+			access them.
+			</p>
+			<h4>The Generated API</h4> When you e.g. <code>(def-java-class "java.la=
ng.ClassName =

+			 ")</code> you get several symbols/functions:
+			<ul>
+				<li>
+					A package named <code>|java.lang|</code> (note case)<br>
+					from which the following are exported:
+				</li>
+				<li>
+					A class-symbol: <code>classname.</code> (note the dot is part of the =
name)<br>
+					which can usually be used where a typename is required. It also serve=
s as the =

+					name of the Lisp typed reference class.
+				</li>
+				<li>
+					Every non-interface class with a public constructor will get;
+					<ul>
+						<li>
+							A constructor, <code>(classname.new &rest args) -> typed-reference<=
/code>, which =

+							returns a typed reference to the newly created object
+						</li>
+						<li>
+							A method defined on <a href=3D#makenew><code>make-new</code></a>, u=
ltimately =

+							calling <code>classname.new</code>, specialized on (the value of) t=
he class-symbol
+						</li>
+					</ul>
+					Note that if the constructor is overloaded, there is just one functio=
n generated, =

+					which handles overload resolution. The function documentation string =
describes =

+					the constructor signature(s) from the Java perspective. The same argu=
ment =

+					conversions are performed as are for fields (see below).
+				</li>
+				<li>
+					All public fields will get a getter function:<br>
+					<code>(classname.fieldname [instance]) -> field value</code><br>
+					and a setter:<br>
+					<code>(setf classname.fieldname [instance])</code><br>
+					Instance field wrappers take a first arg which is the instance. Stati=
c fields =

+					get a symbol-macro <code>*classname.fieldname*</code>
+					<p>
+					If the type of the field is primitive, the field value will be conver=
ted to a =

+					native Lisp value. If it is a Java String, it will be converted to a =
Lisp string. =

+					Otherwise, a generic reference to the Java object is returned. Simila=
rly, when =

+					setting, Lisp values will be accepted for primitives, Lisp strings fo=
r Strings, =

+					or (generic or typed) references for reference types.
+					</p>
+				</li>
+				<li>
+					Every public method will get a wrapper function:<br>
+					<code>(classname.methodname &rest args) -> return-value</code><br>
+					As with constructors, if a method is overloaded a single wrapper is c=
reated that =

+					handles overload resolution. If a method follows the JavaBeans proper=
ty protocol =

+					(i.e. it is called <code>getSomething</code> or <code>isSomething</co=
de> and =

+					there is a corresponding <code>setSomething</code>), then a <code>(se=
tf =

+					classname.methodname)</code> will be defined that calls the latter.
+					<p>
+					The same argument and return value conversions are performed as are f=
or fields. =

+					The function documentation string describes the method signature(s) f=
rom the =

+					Java perspective.
+					</p>
+
+				</li>
+				<li>
+					A Lisp class with the class-symbol as its name. It will have as its s=
uperclasses =

+					other Lisp classes corresponding to the Java superclass/superinterfac=
es, some of =

+					which may be forward-referenced-classes.  An instance of this class w=
ill be =

+					returned by classname.new/make-new/new, at which point the entire hie=
rarchy will =

+					consist of finalized standard-classes.
+				</li>
+				<li>
+					Note that, due to the need to reference other Java types during the d=
efinition =

+					of a class wrapper, symbols, classes, and packages relating to those =
other types =

+					may also be created. In all cases they will be created with names and =

+					packages as described above.
+				</li>
+			</ul>
+		</li>
+
+		<li>
+			<strong>Function</strong> <code>(get-jar-classnames jar-file-name &rest=
 packages) =

+			-> list-of-strings</code>
+			<p>
+			Returns a list of class name strings. Packages should be strings of the=
 form "java/lang =

+			 " for recursive lookup and "java/util/" (note trailing slash) for non-=
recursive.
+			</p>
+		</li>
+		<li>
+			<strong>Function</strong> <code>(dump-wrapper-defs-to-file filename cla=
ssnames)  -> =

+			unspecified</code>
+			<p>
+			Given a list of classnames (say from <code>get-jar-classnames</code>), =
writes =

+			calls to <code>def-java-class</code> to a file:
+			</p>
+			<pre>
+(dump-wrapper-defs-to-file "/lisp/java-lang.lisp"
+  (get-jar-classnames "/j2sdk1.4.2_01/jre/lib/rt.jar " "java/lang/"))
+(compile-file "/lisp/java-lang")
+(load "/lisp/java-lang")
+(use-package "java.lang")
+;Wrappers for all of java.lang are now available
+</pre>
+		</li>
+	</ul>
+
+	<a name=3D"objects"></a> <h3>Object Creation</h3>
+	<ul>
+		<li>
+			<strong><a name=3D"makenew"></a>Generic Function</strong> <code>(make-n=
ew class-symbol =

+			&rest args) -> typed-reference</code>
+			<p>
+			Allows for definition of before/after methods on constructors. Calls <c=
ode>classname.new</code>. =

+			The new macro expands into a call to this.
+			</p>
+		</li>
+		<li>
+			<strong>Macro</strong> <code>(new class-spec &rest args) -> typed-refer=
ence</code>
+			<br>
+			<p>
+			class-spec -> class-name | (class-name this-name)<br>
+			class-name -> "package.qualified.ClassName" | classname.<br>
+			args -> [actual-arg]* [init-arg-spec]*<br>
+			init-arg-spec -> init-arg | (init-arg)<br>
+			init-arg -> :settable-field-or-method [params]* value (note keyword)<br>
+			| .method-name [args]* (note leading dot)<br>
+			</p>
+
+			<p>
+			Creates a new instance of class-name, by expanding into a call to the m=
ake-new =

+			generic function, then initializes it by setting fields or accessors an=
d/or =

+			calling member functions. If this-name is supplied, it will be bound to=
 the =

+			newly-allocated object and available to the init-args:
+			</p>
+			<pre>
+(new (button. this) shell *SWT.CENTER*   ;the actual args
+       :gettext "Call Lisp"               ;a javabean property
+       (.addlistener *swt.selection*      ;a method call
+         (new-proxy (listener.
+                     (handleevent (event)
+                       (declare (ignore event))
+                       (setf (button.gettext this)   ;this is bound to new=
 instance
+                             (format nil "~A ~A"
+                                   (lisp-implementation-type)
+                                   (lisp-implementation-version)))
+                       nil))))
+      .setsize 200 100                    ;can omit parens
+      (.setlocation 40 40))
+			</pre>
+			Expands into:
+			<pre>
+(LET* ((#:G598 (MAKE-NEW BUTTON. SHELL *SWT.CENTER*)) (THIS #:G598))
+  (SETF (BUTTON.GETTEXT #:G598) "Call Lisp")
+  (BUTTON.ADDLISTENER #:G598
+                      *SWT.SELECTION*
+                      (NEW-PROXY (LISTENER.
+                                  (HANDLEEVENT
+                                   (EVENT)
+                                   (DECLARE (IGNORE EVENT))
+                                   (SETF (BUTTON.GETTEXT THIS)
+                                         (FORMAT NIL
+                                                 "~A ~A"
+                                                 (LISP-IMPLEMENTATION-TYPE)
+                                                 (LISP-IMPLEMENTATION-VERS=
ION)))
+                                   NIL))))
+  (BUTTON.SETSIZE #:G598 200 100)
+  (BUTTON.SETLOCATION #:G598 40 40)
+  #:G598)
+			</pre>
+
+		</li>
+	</ul>
+	<a name=3D"arrays"></a> <h3>Array Support</h3>
+	<ul>
+		<li>
+			<strong>Generic Function</strong> <code>(make-new-array type &rest dime=
nsions) -> =

+			reference to new array</code>
+			<p>
+			Generic function with methods defined for all Java class designators:
+			<ul>
+				<li>
+					A "package.qualified.ClassName" string
+				</li>
+				<li>
+					(the value of) A class-symbol - classname.
+				</li>
+				<li>
+					A primitive designator keyword - :boolean|:byte|:char|:double|:float|=
:int|:long|:short
+				</li>
+			</ul>
+			</p>
+			<p>
+			Creates a Java array of the requested type with the requested dimension=
s.
+			</p>
+		</li>
+		<li>
+			<strong>Function</strong> <code>(jlength array) -> integer</code>
+			<p>
+			Like length, for Java arrays
+			</p>
+		</li>
+
+		<li>
+			<strong>Function</strong> <code>(jref array &rest subscripts) -> refere=
nce</code>
+			<p>
+			Like aref, for Java arrays of non-primitive (reference) types, settable.
+			</p>
+		</li>
+		<li>
+			<strong>Function</strong> <code>(jref-xxx array &rest subscripts) -> va=
lue</code>
+			<p>
+			Where xxx =3D boolean|byte|char|double|float|int|long|short. Like jref,=
 for Java =

+			arrays of primitive types, settable.
+			</p>
+		</li>
+
+	</ul>
+
+	<a name=3D"proxies"></a> <h3>Proxies - Java calling back to Lisp</h3>
+	<ul>
+		<p>
+		Proxies allow the creation of Java objects that implement one or more in=
terfaces =

+		in Lisp, and thus callbacks from Java to Lisp. You must call <a href=3D#=
enableproxies>
+		<code>enable-java-proxies</code></a> before using this proxy API. A sign=
ificant =

+		limitation is that LispWorks appears to not support calls back into Lisp=
 other =

+		than from threads initiated by Lisp, so you must ensure that the proxy w=
ill not =

+		be called from an arbitrary Java thread!
+		</p>
+		<li>
+			<strong>Macro</strong> <code>(new-proxy &rest interface-defs) -> refere=
nce</code>
+			<p>
+			interface-def -> (interface-name method-defs+)<br>
+			interface-name -> "package.qualified.ClassName" | classname. (must name=
 a Java =

+			interface type)<br>
+			method-def -> (method-name arg-defs* body) <br>
+			arg-def -> arg-name | (arg-name arg-type) arg-type -> "package.qualifie=
d.ClassName =

+			 " | classname. | :primitive <br>
+			method-name -> symbol | string (matched case-insensitively)
+			</p>
+
+			<p>
+			Creates, registers and returns a Java object that implements the suppli=
ed =

+			interfaces
+			</p>
+		</li>
+		<li>
+			<strong>Function</strong> <code>(unregister-proxy proxy) -> unspecified=
</code>
+			<p>
+			Stops handling for the proxy (which must have been created by <code>new=
-proxy</code>) =

+			and removes references from the Lisp side. Make sure it is no longer re=
ferenced =

+			from Java first!
+			</p>
+		</li>
+	</ul>
+
+	<a name=3D"utilities"></a> <h3>Utilities</h3>
+	<ul>
+
+		<li>
+			<strong>Function</strong> <code>(jeq obj1 obj2) -> boolean</code>
+			<p>
+			Are the 2 java objects the same object? Note that this is not the same =
as Object.equals()
+			<p>
+		</li>
+		<li>
+			<strong>Function</strong> <code>(find-java-class class-sym-or-string) -=
> =

+			reference to Java Class object</code>
+			<p>
+			Given a Java class designator, returns the Java Class object. Use this =
in =

+			preference to Class.forName() when using jfli.
+			</p>
+		</li>
+
+		<li>
+			<strong>Function</strong> <code>(make-typed-ref java-ref) -> typed-refe=
rence</code>
+			<p>
+			Given a generic Java reference, determines the full type of the object =
and =

+			returns an instance of a typed reference wrapper. classname.new/make-ne=
w/new always return typed =

+			references, but since Java methods might return Object or some interfac=
e type, =

+			and we don't want to always incur the cost of type determination, field=
 and =

+			method wrapper functions return generic references. Use this function t=
o create =

+			a typed reference corresponding to the full actual type of the object w=
hen =

+			desired.
+			</p>
+		 </li>
+		<li>
+			<strong>Function</strong> <code>(box-xxx value) -> reference to Java pr=
imitive wrapper class</code>
+			<p>Where xxx =3D boolean|byte|char|double|float|int|long|short|string. =

+			Given a compatible Lisp value, creates an instance of the corresponding=
 Java primitive wrapper class,
+			e.g. Integer. This should rarely be needed, but can be used to force ov=
erloading resolution</p>
+		</li>
+		<li>
+			<strong>Function</strong> <code>(unbox-xxx ref) -> Lisp value</code>
+			<p>Where xxx =3D boolean|byte|char|double|float|int|long|short|string. =

+			Given an instance of a Java primitive wrapper class, creates an instanc=
e of the corresponding =

+			compatible Lisp value. This should rarely be needed, but can be used to=
 unbox values returned by Java
+			Object-based APIs.</p>
+		</li>
+ 	</ul>
+	=

+	<a name=3D"summary"></a> <h3>Summary</h3>
+<p>
+I hope you find jfli useful. It is my sincere intent that it enhance the u=
tility and interoperability of Common Lisp,
+a language with which I am still becoming familiar, and grow to appreciate=
 more every day. I welcome comments
+and code contributions.
+</p>
+<p>
+Rich Hickey, July 2004
+</p>
+	</body>
+</html>

Added: trunk/source/examples/jfli/docs/jfli_bkgrnd.gif
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
Binary file - no diff available.

Propchange: trunk/source/examples/jfli/docs/jfli_bkgrnd.gif
---------------------------------------------------------------------------=
---
    svn:mime-type =3D application/octet-stream

Added: trunk/source/examples/jfli/docs/jfli_new.gif
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
Binary file - no diff available.

Propchange: trunk/source/examples/jfli/docs/jfli_new.gif
---------------------------------------------------------------------------=
---
    svn:mime-type =3D application/octet-stream

Added: trunk/source/examples/jfli/examples/session.lisp
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/source/examples/jfli/examples/session.lisp (added)
+++ trunk/source/examples/jfli/examples/session.lisp Thu Nov 27 12:37:00 20=
08
@@ -1,0 +1,104 @@
+;this presumes you have already compiled jni.lisp and jfli.lisp into fasl =
files
+
+CL-USER 4 > (load "/lisp/jni")
+; Loading fasl file C:\lisp\jni.fsl
+#P"C:/lisp/jni.fsl"
+
+CL-USER 5 > (load "/lisp/jfli")
+; Loading fasl file C:\lisp\jfli.fsl
+#P"C:/lisp/jfli.fsl"
+
+;The user API is entirely in the jfli package
+CL-USER 6 > (use-package :jfli)
+T
+
+;tell the library where Java is located
+CL-USER 7 > (setf *jni-lib-path* "/j2sdk1.4.2_01/jre/bin/client/jvm.dll")
+"/j2sdk1.4.2_01/jre/bin/client/jvm.dll"
+
+;this starts the VM - note how you can set the classpath
+CL-USER 8 > (create-jvm "-Djava.class.path=3D/lisp/jfli.jar")
+0
+#<Pointer: JNI:PVM =3D #x081022A0>
+#<Pointer: JNI:PENV =3D #x0086A858>
+
+;define wrappers for the members of Object
+CL-USER 9 > (def-java-class "java.lang.Object")
+NIL
+
+;and of Properties, a Hashtable-like class
+CL-USER 10 > (def-java-class "java.util.Properties")
+#<STANDARD-CLASS |java.util|:PROPERTIES. 2066B964>
+
+;the above will create these packages if they do not already exist
+;use the packages for easy name access
+
+CL-USER 11 > (use-package "java.lang")
+T
+
+CL-USER 12 > (use-package "java.util")
+T
+
+;create a Properties instance, note keyword-style member inits, string con=
version etc
+;also note typed return value
+CL-USER 13 > (setf p (new properties. :getproperty "fred" "ethel"))
+#<PROPERTIES. 20664A94>
+
+;virtual functions work as normal
+CL-USER 14 > (object.tostring p)
+"{fred=3Dethel}"
+
+;setter was generated for member function because it follows the JavaBeans=
 property protocol
+CL-USER 15 > (setf (properties.getproperty p "ricky") "lucy")
+"lucy"
+
+CL-USER 16 > (object.tostring p)
+"{ricky=3Dlucy, fred=3Dethel}"
+
+CL-USER 17 > (properties.size p)
+2
+
+;totally dynamic access, create wrappers as you need
+CL-USER 18 > (def-java-class "java.lang.Class")
+#<STANDARD-CLASS CLASS. 20680EC4>
+
+CL-USER 19 > (class.getname (object.getclass p))
+"java.util.Properties"
+
+CL-USER 20 > (def-java-class "java.util.Enumeration")
+#<STANDARD-CLASS ENUMERATION. 20669274>
+
+;no need to wait for the vendor to enhance the language - you use Lisp!
+CL-USER 21 > (defmacro doenum ((e enum) &body body)
+  (let ((genum (gensym)))
+    `(let ((,genum ,enum))
+       (do ()
+           ((not (enumeration.hasmoreelements ,genum)))
+         (let ((,e (enumeration.nextelement ,genum)))
+           , at body)))))(defmacro doenum ((e enum) &body body)
+               (let ((genum (gensym)))
+                 `(let ((,genum ,enum))
+                    (do ()
+                        ((not (enumeration.hasmoreelements ,genum)))
+                      (let ((,e (enumeration.nextelement ,genum)))
+                        , at body)))))
+DOENUM
+
+;can't do this in Java yet can in Lisp
+CL-USER 22 > (doenum (prop (properties.elements p)) (print (object.tostrin=
g prop)))
+
+"lucy" =

+"ethel" =

+NIL
+
+;doc strings are created giving original Java signatures and indicating ov=
erloads
+CL-USER 23 > (documentation 'properties.getproperty 'function)
+"java.lang.String getProperty(java.lang.String,java.lang.String)
+java.lang.String getProperty(java.lang.String)
+"
+
+CL-USER 24 > (documentation 'properties.new 'function)
+"java.util.Properties()
+java.util.Properties(java.util.Properties)
+"
+

Added: trunk/source/examples/jfli/examples/swtdemo.lisp
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/source/examples/jfli/examples/swtdemo.lisp (added)
+++ trunk/source/examples/jfli/examples/swtdemo.lisp Thu Nov 27 12:37:00 20=
08
@@ -1,0 +1,54 @@
+;Just load this from LispWorks menu
+;note works on Windows, some issues on OS X due to windowing conflict w/IDE
+(load "~/jfli/jni")
+(load "~/jfli/jfli")
+(use-package :jfli)
+(create-jvm =

+#+:macosx "-Djava.class.path=3D/Users/rich/swt/swt.jar:/Users/rich/swt/swt=
-pi.jar:User/rich/jfli/jfli.jar"
+ #+:macosx "-Djava.library.path=3D/Users/rich/swt"
+ #+:win32 "-Djava.class.path=3D/swt/swt.jar;/lisp/jfli.jar"
+ #+:win32 "-Djava.library.path=3D/swt"
+ )
+(enable-java-proxies)
+
+
+
+(def-java-class "org.eclipse.swt.widgets.Display")
+(def-java-class "org.eclipse.swt.widgets.Button")
+(def-java-class "org.eclipse.swt.widgets.Shell")
+(def-java-class "org.eclipse.swt.widgets.Listener")
+(def-java-class "org.eclipse.swt.SWT")
+
+(use-package "org.eclipse.swt")
+(use-package "org.eclipse.swt.widgets")
+
+
+(defun swt-demo ()
+  (let* ((display (new display.))
+         (shell (new shell. display
+                     :gettext "Using SWT from Lisp"
+                     (.setsize 300 200)
+                     (.setlocation 100 100)))
+         (button (new (button. this) shell *SWT.CENTER*
+                      :gettext "Call Lisp"
+                      (.addlistener *swt.selection*
+                       (new-proxy (listener.
+                                   (handleevent (event)
+                                     (declare (ignore event))
+                                     (setf (button.gettext this)
+                                           (format nil "~A ~A"
+                                                   (lisp-implementation-ty=
pe)
+                                                   (lisp-implementation-ve=
rsion)))
+                                     nil))))
+                      (.setsize 200 100)
+                      (.setlocation 40 40))))
+    (declare (ignore button))
+    (shell.open shell)
+    (do ()
+        ((shell.isdisposed shell))
+      (unless (display.readanddispatch display)
+        (display.sleep display)))
+    (display.dispose display)))
+
+(mp:process-run-function "swt-proc" '() #'swt-demo)
+

Added: trunk/source/examples/jfli/jfli.jar
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
Binary file - no diff available.

Propchange: trunk/source/examples/jfli/jfli.jar
---------------------------------------------------------------------------=
---
    svn:mime-type =3D application/octet-stream

Added: trunk/source/examples/jfli/jfli.lisp
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/source/examples/jfli/jfli.lisp (added)
+++ trunk/source/examples/jfli/jfli.lisp Thu Nov 27 12:37:00 2008
@@ -1,0 +1,1390 @@
+;    Copyright (c) Rich Hickey. All rights reserved.
+;    The use and distribution terms for this software are covered by the
+;    Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
+;    which can be found in the file CPL.TXT at the root of this distributi=
on.
+;    By using this software in any fashion, you are agreeing to be bound by
+;    the terms of this license.
+;    You must not remove this notice, or any other, from this software.
+
+#|
+
+jfli is a library that provides access to Java from Lisp
+It depends on the jni package (included)
+Callbacks from Java to Lisp also require jfli.jar (included)
+
+|#
+
+(defpackage :jfli
+  (:use :common-lisp :lispworks :jni)
+  (:export
+
+   ;jvm creation
+   :*jni-lib-path*  ;exposed from jni
+   :create-jvm      ;exposed from jni, you must call this prior to calling=
 any other jfli function
+   :enable-java-proxies
+
+   ;wrapper generation
+   :def-java-class
+   :get-jar-classnames
+   :dump-wrapper-defs-to-file
+
+   ;object creation etc
+   :find-java-class
+   :new
+   :make-new
+   :make-typed-ref
+   :jeq
+
+   ;array support
+   :make-new-array
+   :jlength
+   :jref
+   :jref-boolean
+   :jref-byte
+   :jref-char
+   :jref-double
+   :jref-float
+   :jref-int
+   :jref-short
+   :jref-long
+
+   ;proxy support
+   :new-proxy
+   :unregister-proxy
+
+   ;conversions
+   :box-boolean
+   :box-byte
+   :box-char
+   :box-double
+   :box-float
+   :box-integer
+   :box-long
+   :box-short
+   :box-string
+   :unbox-boolean
+   :unbox-byte
+   :unbox-char
+   :unbox-double
+   :unbox-float
+   :unbox-integer
+   :unbox-long
+   :unbox-short
+   :unbox-string
+
+;   :ensure-package
+;   :member-symbol
+;   :class-symbol
+;   :constructor-symbol
+   ))
+
+(in-package :jfli)
+
+#|
+bootstrap the implementation of reflection wrappers with =

+a few (primitive, less safe and maybe faster) jni wrappers
+|#
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (def-jni-functions "java.lang.Object"
+                     ("getClass" () "Class")
+                     ("hashCode" () "int")
+                     ("toString" () "String")
+                     ("equals" ((obj "Object")) "boolean"))
+
+  (def-jni-functions "java.lang.Class"
+                   ;should be :overloaded t, but we only use this version
+                     ("forName" ((className "String")) "Class"  :static t)
+                     ("getConstructors" () "java.lang.reflect.Constructor<=
>")
+                     ("getFields" () "java.lang.reflect.Field<>")
+                     ("getMethods" () "java.lang.reflect.Method<>")
+
+                     ("getConstructor" ((parameter-types "Class<>")) "java=
.lang.reflect.Constructor")
+                     ("getField" ((name "String"))
+                                 "java.lang.reflect.Field")
+                     ("getMethod" ((name "String") (parameter-types "Class=
<>"))
+                                  "java.lang.reflect.Method")
+
+                     ("getSuperclass" () "Class")
+                     ("getInterfaces" () "Class<>")
+
+                     ("getName" () "String")
+                     ("isArray" () "boolean")
+                     ("isPrimitive" () "boolean"))
+
+  (def-jni-functions "java.lang.reflect.Field"
+                     ("getName" () "java.lang.String")
+                     ("getType" () "java.lang.Class")
+                     ("getModifiers" () "int")
+
+                     ("get" ((obj "java.lang.Object")) "java.lang.Object" =
:raw-return t)
+                     ("getBoolean" ((obj "java.lang.Object")) "boolean")
+                     ("getByte" ((obj "java.lang.Object")) "byte")
+                     ("getChar" ((obj "java.lang.Object")) "char")
+                     ("getDouble" ((obj "java.lang.Object")) "double")
+                     ("getFloat" ((obj "java.lang.Object")) "float")
+                     ("getInt" ((obj "java.lang.Object")) "int")
+                     ("getLong" ((obj "java.lang.Object")) "long")
+                     ("getShort" ((obj "java.lang.Object")) "short")
+
+                     ("set" ((obj "java.lang.Object") (value "java.lang.Ob=
ject")) "void")
+                     ("setBoolean" ((obj "java.lang.Object") (b "boolean")=
) "void")
+                     ("setByte" ((obj "java.lang.Object") (b "byte")) "voi=
d")
+                     ("setChar" ((obj "java.lang.Object") (c "char")) "voi=
d")
+                     ("setDouble" ((obj "java.lang.Object") (d "double")) =
"void")
+                     ("setFloat" ((obj "java.lang.Object") (f "float")) "v=
oid")
+                     ("setInt" ((obj "java.lang.Object") ( i "int")) "void=
")
+                     ("setLong" ((obj "java.lang.Object") (l "long")) "voi=
d")
+                     ("setShort" ((obj "java.lang.Object") (s "short")) "v=
oid"))
+
+  (def-jni-functions "java.lang.reflect.Constructor"
+                     ("getParameterTypes" () "java.lang.Class<>")
+                     ("newInstance" ((initargs "java.lang.Object<>")) "jav=
a.lang.Object"))
+
+  (def-jni-functions "java.lang.reflect.Method"
+                     ("getName" () "java.lang.String")
+                     ("getParameterTypes" () "java.lang.Class<>")
+                     ("getReturnType" () "java.lang.Class")
+                     ("getModifiers" () "int")
+                     ("invoke" ((object "java.lang.Object")
+                                (args "java.lang.Object<>")) "java.lang.Ob=
ject"
+                               :raw-return t))
+
+  (def-jni-functions "java.lang.reflect.Array"
+                     ("get" ((array "java.lang.Object") (index "int")) "ja=
va.lang.Object" :static t)
+                     ("getBoolean"
+                      ((array "java.lang.Object") (index "int")) "boolean"=
 :static t)
+                     ("getByte"
+                      ((array "java.lang.Object") (index "int")) "byte" :s=
tatic t)
+                     ("getChar"
+                      ((array "java.lang.Object") (index "int")) "char" :s=
tatic t)
+                     ("getDouble"
+                      ((array "java.lang.Object") (index "int")) "double" =
:static t)
+                     ("getFloat"
+                      ((array "java.lang.Object") (index "int")) "float" :=
static t)
+                     ("getInt"
+                      ((array "java.lang.Object") (index "int")) "int" :st=
atic t)
+                     ("getShort"
+                      ((array "java.lang.Object") (index "int")) "short" :=
static t)
+                     ("getLong"
+                      ((array "java.lang.Object") (index "int")) "long" :s=
tatic t)
+                     ("getLength" ((array "java.lang.Object")) "int" :stat=
ic t)
+                     ("newInstance" ((componentType "java.lang.Class")
+                                     (length "int")) "java.lang.Object" :s=
tatic t :overloaded t)
+                     ("newInstance" ((componentType "java.lang.Class")
+                                     (dimensions "int<>")) "java.lang.Obje=
ct" :static t :overloaded t)
+                     ("set" ((array "java.lang.Object") (index "int") (val=
ue "java.lang.Object"))
+                            "void" :static t))
+
+
+  (def-jni-function "java.lang.reflect.Modifier"
+                    "isStatic" ((mod "int")) "boolean" :static t)
+
+  (def-jni-constructor "java.lang.Boolean" ((value "boolean")))
+  (def-jni-constructor "java.lang.Byte" ((value "byte")))
+  (def-jni-constructor "java.lang.Character" ((value "char")))
+  (def-jni-constructor "java.lang.Double" ((value "double")))
+  (def-jni-constructor "java.lang.Float" ((value "float")))
+  (def-jni-constructor "java.lang.Integer" ((value "int")))
+  (def-jni-constructor "java.lang.Short" ((value "short")))
+
+  (def-jni-function "java.lang.Boolean" "booleanValue" () "boolean")
+  (def-jni-function "java.lang.Byte" "byteValue" () "byte")
+  (def-jni-function "java.lang.Character" "charValue" () "char")
+  (def-jni-function "java.lang.Double" "doubleValue" () "double")
+  (def-jni-function "java.lang.Float" "floatValue" () "float")
+  (def-jni-function "java.lang.Integer" "intValue" () "int")
+  (def-jni-function "java.lang.Short" "shortValue" () "short")
+
+  (def-jni-constructor "java.util.jar.JarFile" ((filename "java.lang.Strin=
g")))
+  (def-jni-function "java.util.jar.JarFile"
+                    "entries" () "java.util.Enumeration")
+  (def-jni-functions "java.util.Enumeration"
+                     ("hasMoreElements" () "boolean")
+                     ("nextElement" () "java.lang.Object"))
+  (def-jni-functions "java.util.zip.ZipEntry"
+                     ("isDirectory" () "boolean")
+                     ("getName" () "java.lang.String"))
+
+
+  (def-jni-functions "java.lang.Long"
+                     ("valueOf" ((s "String")) "java.lang.Long" :static t)
+                     ("intValue" () "int"))
+
+  (def-jni-field "java.lang.Boolean" "TYPE" "Class" :static t)
+  (def-jni-field "java.lang.Byte" "TYPE" "Class" :static t)
+  (def-jni-field "java.lang.Character" "TYPE" "Class" :static t)
+  (def-jni-field "java.lang.Float" "TYPE" "Class" :static t)
+  (def-jni-field "java.lang.Integer" "TYPE" "Class" :static t)
+  (def-jni-field "java.lang.Double" "TYPE" "Class" :static t)
+  (def-jni-field "java.lang.Short" "TYPE" "Class" :static t)
+  (def-jni-field "java.lang.Long" "TYPE" "Class" :static t)
+
+  (def-jni-constructor "com.richhickey.jfli.LispInvocationHandler" ())
+  (def-jni-function "java.lang.reflect.Proxy"
+                    "newProxyInstance" ((loader "java.lang.ClassLoader")
+                                        (interfaces "java.lang.Class<>")
+                                        (h "InvocationHandler")) "java.lan=
g.Object" :static t)
+
+  (def-jni-function "java.lang.ClassLoader"
+                    "getSystemClassLoader" () "ClassLoader" :static t)
+
+  ) ;eval-when
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;; utilities ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;=
;;;;;;;;;
+
+(eval-when (:compile-toplevel :load-toplevel)
+  (defun ensure-package (name)
+    "find the package or create it if it doesn't exist"
+    (or (find-package name)
+        (make-package name :use '())))
+  (intern "Object" (ensure-package "java.lang"))
+  (intern "String" (ensure-package "java.lang")))
+
+(defmacro do-jarray ((x array) &body body)
+  "jni-based, so not safe and not exported, but used by the implementation"
+  (let ((gcount (gensym))
+        (gi (gensym))
+        (garray (gensym)))
+    `(let* ((,garray ,array)
+            (,gcount (jni:get-array-length ,garray)))
+       (dotimes (,gi ,gcount)
+         (let ((,x (jaref ,garray ,gi)))
+           , at body)))))
+
+
+(defmacro doenum ((e enum) &body body)
+  "jni-based, so not safe and not exported, but used by the implementation"
+  (let ((genum (gensym)))
+    `(let ((,genum ,enum))
+       (do ()
+           ((not (enumeration.hasmoreelements ,genum)))
+         (let ((,e (enumeration.nextelement ,genum)))
+           , at body)))))
+
+;probably insufficiently general, works as used here
+(defmacro get-or-init (place init-form)
+  `(or ,place
+       (setf ,place ,init-form)))
+
+;from c.l.l.
+(defmacro case-equal (exp &body clauses)
+  (let ((temp (gensym)))
+    `(let ((,temp ,exp))
+       (cond ,@(mapcar #'(lambda (clause)
+                           (destructuring-bind (keys . clause-forms) clause
+                             (cond ((eq keys 'otherwise)
+                                    `(t , at clause-forms))
+                                   (t
+                                    (if (atom keys) (setq keys (list keys)=
))
+                                    `((member ,temp ',keys :test #'equal)
+                                      , at clause-forms)))))
+                       clauses)))))
+
+;create object. to bootstrap the hierarchy
+(defclass |java.lang|::object. ()
+  ((ref :reader ref :initarg :ref)
+   (lisp-allocated :reader lisp-allocated-p :initarg :lisp-allocated :init=
form nil))
+  (:documentation "the superclass of all Java typed reference classes"))
+
+(defun get-ref (x)
+  "any function taking an object can be passed a raw java-ref ptr or a typ=
ed reference instance.
+Will also convert strings for use as objects"
+  (etypecase x
+    (java-ref x)
+    (|java.lang|::object. (ref x))
+    (string (convert-to-java-string x))
+    (null nil)))
+
+(defun jeq (obj1 obj2)
+  "are these 2 java objects the same object? Note that is not the same as =
Object.equals()"
+  (is-same-object (get-ref obj1) (get-ref obj2)))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;; names and symbols ;;;;;;;;;;;;;;;;;;;;;;;
+#|
+The library does a lot with names and symbols, needing at various times to:
+ - find stuff in Java - full names w/case required
+ - create hopefully non-conflicting packages and member names
+
+When you (def-java-class "java.lang.String") you get a bunch of symbols/na=
mes:
+a package named '|java.lang|
+a class-symbol '|java.lang|:STRING. (note the dot and case), =

+   which can usually be used where a typename is required
+   it also serves as the name of the Lisp typed reference class for string
+   its symbol-value is the canonic-class-symbol (see below)
+a canonic-class-symbol '|java.lang|::|String|
+   can be used to reconstitute the full class name
+
+I've started trying to flesh out the notion of a Java class designator, wh=
ich can either be
+the full class name as a string, the class-symbol, or one of :boolean, :in=
t etc
+|#
+
+(defun canonic-class-symbol (full-class-name)
+  "(\"java.lang.Object\") -> '|java.lang|:|Object|"
+  (multiple-value-bind (package class) (split-package-and-class full-class=
-name)
+    (intern class (ensure-package package))))
+
+(defun class-symbol (full-class-name)
+  "(\"java.lang.Object\") -> '|java.lang|:object."
+  (multiple-value-bind (package class) (split-package-and-class full-class=
-name)
+    (intern (string-upcase (string-append class ".")) (ensure-package pack=
age))))
+
+(defun java-class-name (class-sym)
+  "inverse of class-symbol, only valid on class-syms created by def-java-c=
lass"
+  (let ((canonic-class-symbol (symbol-value class-sym)))
+    (string-append (package-name (symbol-package canonic-class-symbol))
+                                                "."
+                                                canonic-class-symbol)))
+
+(defun member-symbol (full-class-name member-name)
+  "members are defined case-insensitively in case-sensitive packages,
+prefixed by 'classname.' -
+(member-symbol \"java.lang.Object\" \"toString\") -> '|java.lang|::OBJECT.=
TOSTRING"
+  (multiple-value-bind (package class) (split-package-and-class full-class=
-name)
+    (intern (string-upcase (string-append class "." member-name)) (ensure-=
package package))))
+
+(defun constructor-symbol (full-class-name)
+  (member-symbol full-class-name "new"))
+
+(defun get-java-class-ref (canonic-class-symbol)
+  "class-ref is cached on the plist of the canonic class symbol"
+  (get-or-init (get canonic-class-symbol :class-ref)
+               (let ((class-name (string-append (package-name
+                                                 (symbol-package canonic-c=
lass-symbol))
+                                                "."
+                                                canonic-class-symbol)))
+                 (try-null (jni-find-class (nsubstitute #\/ #\. class-name=
))))))
+
+(defun find-java-class (class-sym-or-string)
+  "Given a Java class designator, returns the Java Class object."
+  (ctypecase class-sym-or-string
+    (symbol (case class-sym-or-string
+              (:int integer.type)
+              (:char character.type)
+              (:long long.type)
+              (:float float.type)
+              (:boolean boolean.type)
+              (:short short.type)
+              (:double double.type)
+              (:byte byte.type)
+              (otherwise (get-java-class-ref class-sym-or-string))))
+    (string (get-java-class-ref (canonic-class-symbol class-sym-or-string)=
))))
+
+;;;;;;;;;;;;;;;;;;;;;; typed reference support ;;;;;;;;;;;;;;;;;;;;;;;;
+#|
+The library maintains a hierarchy of typed reference classes that parallel=
 the
+class hierarchy on the Java side
+new returns a typed reference, but other functions that return objects
+return raw references (for efficiency) =

+make-typed-ref can create fully-typed wrappers when desired
+|#
+
+(defun get-superclass-names (full-class-name)
+  (let* ((class (get-java-class-ref (canonic-class-symbol full-class-name)=
))
+         (super (class.getsuperclass class))
+         (interfaces (class.getinterfaces class))
+         (supers ()))
+    (do-jarray (i interfaces)
+      (push i supers))
+    ;hmmm - where should the base class go in the precedence list?
+    ;is it more important than the interfaces? this says no
+    (if super
+        (push super supers)
+      (push (find-java-class "java.lang.Object") supers))
+    (setf supers (nreverse supers))
+    ;now we need to fix up order so more derived classes are first
+    ;but don't have a total ordering, so merge one at a time
+    (let (result)
+      (dolist (s supers)
+        (setf result (merge 'list result (list s)
+                            (lambda (x y)
+                              (is-assignable-from x y)))))
+      (mapcar #'class.getname result))))
+#|
+(defun get-superclass-names (full-class-name)
+  (let* ((class (get-java-class-ref (canonic-class-symbol full-class-name)=
))
+         (super (class.getsuperclass class))
+         (interfaces (class.getinterfaces class))
+         (supers ()))
+    (do-jarray (i interfaces)
+      (push (class.getname i) supers))
+    ;hmmm - where should the base class go in the precedence list?
+    ;is it more important than the interfaces? this says no
+    (if super
+        (push (class.getname super) supers)
+      (push "java.lang.Object" supers))
+    (nreverse supers)))
+|#
+
+(defun ensure-java-class (full-class-name)
+  "walks the superclass hierarchy and makes sure all the classes are fully=
 defined
+(they may be undefined or just forward-referenced-class)
+caches this has been done on the class-symbol's plist"
+  (let* ((class-sym (class-symbol full-class-name))
+         (class (find-class class-sym nil)))
+    (if (or (eql class-sym '|java.lang|::object.)
+            (get class-sym :ensured))
+        class
+      (let ((supers (get-superclass-names full-class-name)))
+        (dolist (super supers)
+          (ensure-java-class super))
+        (unless (and class (subtypep class 'standard-object))
+          (setf class
+                (clos:ensure-class class-sym :direct-superclasses (mapcar =
#'class-symbol supers))))
+        (setf (get class-sym :ensured) t)
+        class))))
+
+(defun ensure-java-hierarchy (class-sym)
+  "Works off class-sym for efficient use in new
+This will only work on class-syms created by def-java-class,
+as it depends upon symbol-value being the canonic class symbol"
+  (unless (get class-sym :ensured)
+    (ensure-java-class (java-class-name class-sym))))
+
+(defun make-typed-ref (java-ref)
+  "Given a raw java-ref, determines the full type of the object
+and returns an instance of a typed reference wrapper"
+  (when java-ref
+    (let ((class (object.getclass (get-ref java-ref))))
+      (if (class.isarray class)
+          (error "typed refs not supported for arrays (yet)")
+        (make-instance (ensure-java-class (class.getname class)) :ref (get=
-ref java-ref) )))))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;; Wrapper Generation ;;;;;;;;;;;;;;;;;;;;;;;;;;;
+#|
+In an effort to reduce the volume of stuff generated when wrapping entire =
libraries,
+the wrappers just generate minimal stubs, which, if and when invoked at ru=
ntime,
+complete the work of building thunking closures, so very little code is ge=
nerated for
+things never called (Java libraries have huge numbers of symbols).
+Not sure if this approach matters, but that's how it works
+|#
+
+(defmacro def-java-class (full-class-name)
+  "Given the package-qualified, case-correct name of a java class, will ge=
nerate
+wrapper functions for its constructors, fields and methods."
+  (multiple-value-bind (package class) (split-package-and-class full-class=
-name)
+    (declare (ignore class))
+    (let* ((class-sym (class-symbol full-class-name))
+           (defs
+            (list*
+             `(ensure-package ,package)
+          ;build a path from the simple class symbol to the canonic
+             `(defconstant ,class-sym ',(canonic-class-symbol full-class-n=
ame))
+             `(export ',class-sym (symbol-package ',class-sym))
+             `(def-java-constructors ,full-class-name)
+             `(def-java-methods ,full-class-name)
+             `(def-java-fields ,full-class-name)
+             (unless (string=3D full-class-name "java.lang.Object")
+               (let ((supers (mapcar #'class-symbol (get-superclass-names =
full-class-name))))
+                 (append (mapcar (lambda (p)
+                                   `(ensure-package ,(package-name p)))
+                                 (remove (symbol-package class-sym)
+                                         (remove-duplicates (mapcar #'symb=
ol-package supers))))
+                         (list `(defclass ,(class-symbol full-class-name)
+                                          ,supers ()))))))))
+      `(locally , at defs))))
+
+(defun get-jar-classnames (jar-file-name &rest packages)
+  "returns a list of strings, packages should be of the form \"java/lang\"
+  for recursive lookup and \"java/util/\" for non-recursive"
+  (let* ((jar (jarfile.new jar-file-name))
+         (entries (jarfile.entries jar))
+         (names ()))
+    (doenum (e entries)
+      (unless (zipentry.isdirectory e)
+        (let ((ename (zipentry.getname e)))
+          (flet ((matches (package)
+                   (and (eql 0 (search package ename))
+                        (or (not (eql #\/ (schar package (1- (length packa=
ge))))) ;recursive
+                            (not (find #\/ ename :start (length package)))=
)))) ;non-subdirectory
+            (when (and (eql (search ".class" ename)
+                            (- (length ename) 6)) ;classname
+                       ;don't grab anonymous inner classes
+                       (not (and (find #\$ ename)
+                                 (digit-char-p (schar ename (1+ (position =
#\$ ename))))))
+                       (some #'matches packages))
+              (push (nsubstitute #\. #\/ (subseq ename 0 (- (length ename)=
 6)))
+                    names))))))
+    names))
+
+(defun dump-wrapper-defs-to-file (filename classnames)
+  "given a list of classnames (say from get-jar-classnames), writes
+calls to def-java-class to a file"
+  (with-open-file (s filename :direction :output :if-exists :supersede)
+    (dolist (name (sort classnames #'string-lessp))
+      (format s "(def-java-class ~S)~%" name))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;; constructors and new ;;;;;;;;;;;;;;;;;;;;;;;;;;
+#|
+
+Every non-interface class with a public ctor will get;
+  a constructor, classname.new
+  a method defined on make-new, ultimately calling classname.new,
+   specialized on (the value of) it's class-symbol (e.g. canonic sym)
+
+Note that if the ctor is overloaded, there is just one function (taking a =
rest arg), =

+which handles overload resolution
+
+The new macro expands into a call to make-new
+|#
+
+(defgeneric make-new (class-sym &rest args)
+  (:documentation "Allows for definition of before/after methods on ctors.
+The new macro expands into call to this"))
+
+(defun build-ctor-doc-string (name ctors)
+  (with-output-to-string (s)
+    (dolist (c ctors)
+      (format s "~A(~{~#[~;~A~:;~A,~]~})~%"
+              name
+              (mapcar #'class-name-for-doc (jarray-to-list (constructor.ge=
tparametertypes c)))))))
+
+(defmacro def-java-constructors (full-class-name)
+"creates and exports a ctor func classname.new, defines a method of =

+make-new specialized on the class-symbol"
+  (let ((ctor-list (get-ctor-list full-class-name)))
+    (when ctor-list
+      (let ((ctor-sym (constructor-symbol full-class-name))
+            (class-sym (class-symbol full-class-name)))
+        `(locally
+           (defun ,ctor-sym (&rest args)
+             ,(build-ctor-doc-string full-class-name ctor-list)
+             (apply #'install-constructors-and-call ,full-class-name args))
+           (export ',ctor-sym (symbol-package ',ctor-sym))
+           (defmethod make-new ((class-sym (eql ,class-sym)) &rest args)
+             (apply (function ,ctor-sym) args)))))))
+
+(defun get-ctor-list (full-class-name)
+  (let* ((class-sym (canonic-class-symbol full-class-name))
+         (class (get-java-class-ref class-sym))
+         (ctor-array (class.getconstructors class))
+         (ctor-list (jarray-to-list ctor-array)))
+    ctor-list))
+
+(defun install-constructors-and-call (full-class-name &rest args)
+  "initially the constructor symbol for a class is bound to this function,
+when first called it will replace itself with the appropriate direct thunk,
+then call the requested ctor - subsequent calls will be direct"
+  (install-constructors full-class-name)
+  (apply (constructor-symbol full-class-name) args))
+
+(defun install-constructors (full-class-name)
+  (let* ((ctor-list (get-ctor-list full-class-name)))
+    (when ctor-list
+      (setf (fdefinition (constructor-symbol full-class-name))
+            (make-ctor-thunk ctor-list (class-symbol full-class-name))))))
+
+(defun make-ctor-thunk (ctors class-sym)
+  (if (rest ctors) ;overloaded
+      (make-overloaded-ctor-thunk ctors class-sym)
+    (make-non-overloaded-ctor-thunk (first ctors) class-sym)))
+
+(defun make-non-overloaded-ctor-thunk (ctor class-sym)
+  (let ((arg-boxers (get-arg-boxers (constructor.getparametertypes ctor))))
+    (lambda (&rest args)
+      (let ((arg-array (build-arg-array args arg-boxers)))
+        (ensure-java-hierarchy class-sym)
+        (prog1
+            (make-instance class-sym
+                           :ref (constructor.newinstance ctor arg-array)
+                           :lisp-allocated t)
+            ;(constructor.newinstance ctor arg-array)
+          (when arg-array
+            (delete-local-ref arg-array)))))))
+
+(defun make-overloaded-ctor-thunk (ctors class-sym)
+  (let ((thunks (make-ctor-thunks-by-args-length ctors class-sym)))
+    (lambda (&rest args)
+      (let ((fn (cdr (assoc (length args) thunks))))
+        (if fn
+            (apply fn
+                   args)
+          (error "invalid arity"))))))
+
+(defun make-ctor-thunks-by-args-length (ctors class-sym)
+  "returns an alist of thunks keyed by number of args"
+  (let ((ctors-by-args-length (make-hash-table))
+        (thunks-by-args-length nil))
+    (dolist (ctor ctors)
+      (let ((params-len (get-array-length (constructor.getparametertypes c=
tor))))
+        (push ctor (gethash params-len ctors-by-args-length))))
+    (maphash #'(lambda (args-len ctors)
+                 (push (cons args-len
+                             (if (rest ctors);truly overloaded
+                                 (make-type-overloaded-ctor-thunk ctors cl=
ass-sym)
+                               ;only one ctor with this number of args
+                               (make-non-overloaded-ctor-thunk (first ctor=
s) class-sym)))
+                       thunks-by-args-length))
+             ctors-by-args-length)
+    thunks-by-args-length))
+
+(defun make-type-overloaded-ctor-thunk (ctors class-sym)
+  "these methods have the same number of args and must be distinguished by=
 type"
+  (let ((thunks (mapcar #'(lambda (ctor)
+                            (list (make-non-overloaded-ctor-thunk ctor cla=
ss-sym)
+                                  (jarray-to-list (constructor.getparamete=
rtypes ctor))))
+                        ctors)))
+    (lambda (&rest args)
+      (block fn
+        (let ((arg-types (get-types-of-args args)))
+          (dolist (thunk-info thunks)
+            (destructuring-bind (thunk param-types) thunk-info
+              (when (is-congruent-type-list param-types arg-types)
+                (return-from fn (apply thunk args)))))
+          (error "No matching constructor"))))))
+
+(defmacro new (class-spec &rest args)
+"new class-spec args
+class-spec -> class-name | (class-name this-name)
+class-name -> \"package.qualified.ClassName\" | classname.
+args -> [actual-arg]* [init-arg-spec]*
+init-arg-spec -> init-arg | (init-arg)
+init-arg -> :settable-field-or-method [params]* value ;note keyword
+            | =

+            .method-name [args]*                      ;note dot
+
+Creates a new instance of class-name, using make-new generic function,
+then initializes it by setting fields or accessors and/or calling member f=
unctions
+If this-name is supplied it will be bound to the newly-allocated object an=
d available
+to the init-args"
+  (labels ((mem-sym? (x)
+             (or (keywordp x)
+                 (and (symbolp x) (eql 0 (position #\. (symbol-name x))))))
+           (mem-form? (x)
+             (and (listp x) (mem-sym? (first x))))
+           (mem-init? (x)
+             (or (mem-sym? x) (mem-form? x)))
+           (init-forms (x)
+             (if x
+                 (if (mem-form? (first x))
+                     (cons (first x) (init-forms (rest x)))
+                   (let ((more (member-if #'mem-init? (rest x))))
+                     (cons (ldiff x more) (init-forms more)))))))
+    (let* ((inits (member-if #'mem-init? args))
+           (real-args (ldiff args inits))
+           (class-atom (if (atom class-spec)
+                           class-spec
+                         (first class-spec)))
+           (class-sym (if (symbolp class-atom)
+                          ;(find-symbol (string-append (symbol-name class-=
atom) "."))
+                          class-atom
+                        (multiple-value-bind (package class) (jni::split-p=
ackage-and-class class-atom)
+                          (find-symbol (string-append (string-upcase class=
) ".") package))))
+           (class-name (subseq (symbol-name class-sym) 0 (1- (length (symb=
ol-name class-sym)))))
+           (gthis (gensym)))
+      (flet ((expand-init (x)
+               (if (keywordp (first x)) ;setf field or property
+                   `(setf (,(find-symbol (string-append class-name "." (sy=
mbol-name (first x))))
+                           ,gthis ,@(butlast (rest x)))
+                          ,@(last (rest x)))
+                 ;.memfunc
+                 `(,(find-symbol (string-append class-name (symbol-name (f=
irst x))))
+                   ,gthis
+                   ,@(rest x)))))
+        `(let* ((,gthis (make-new ,class-sym , at real-args))
+                ,@(when (listp class-spec)
+                    `((,(second class-spec) ,gthis))))
+           ,@(mapcar #'expand-init (init-forms inits))
+           ,gthis)))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Fields ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+#|
+all public fields will get a getter function classname.fieldname and a set=
ter - (setf classname.fieldname)
+instance fields take an first arg which is the instance
+static fields also get a symbol-macro *classname.fieldname*
+|#
+
+(defmacro def-java-fields (full-class-name)
+"fields will get a getter function classname.fieldname and a setter - (set=
f classname.fieldname)
+instance fields take an first arg which is the instance
+static fields also get a symbol-macro *classname.fieldname*"
+  (let* ((class-sym (canonic-class-symbol full-class-name))
+         (class (get-java-class-ref class-sym))
+         (fields (jarray-to-list (class.getfields class)))
+         (defs nil))
+    (dolist (field fields)
+      (let* ((field-name (field.getname field))
+             (field-sym (member-symbol full-class-name field-name))
+             (is-static (modifier.isstatic (field.getmodifiers field))))
+        (if is-static
+            (let ((macsym (intern (string-append "*" (symbol-name field-sy=
m) "*")
+                                  (symbol-package field-sym))))
+              (push `(defun ,field-sym ()
+                       (install-static-field-and-get ,full-class-name ,fie=
ld-name))
+                    defs)
+              (push `(defun (setf ,field-sym) (val)
+                       (install-static-field-and-set ,full-class-name ,fie=
ld-name val))
+                    defs)
+              (push `(export ',field-sym (symbol-package ',field-sym)) def=
s)
+              (push `(define-symbol-macro ,macsym (,field-sym)) defs)
+              (push `(export ',macsym (symbol-package ',macsym)) defs))
+          (progn
+            (push `(defun ,field-sym (obj)
+                     (install-field-and-get ,full-class-name ,field-name o=
bj))
+                  defs)
+            (push `(defun (setf ,field-sym) (val obj)
+                     (install-field-and-set ,full-class-name ,field-name v=
al obj))
+                  defs)
+            (push `(export ',field-sym (symbol-package ',field-sym)) defs)=
))))
+    `(locally ,@(nreverse defs))))
+
+(defun install-field-and-get (full-class-name field-name obj)
+  (install-field full-class-name field-name)
+  (funcall (member-symbol full-class-name field-name) obj))
+
+(defun install-field-and-set (full-class-name field-name val obj)
+  (install-field full-class-name field-name)
+  (funcall (fdefinition `(setf ,(member-symbol full-class-name field-name)=
)) val obj))
+
+(defun install-static-field-and-get (full-class-name field-name)
+  (install-field full-class-name field-name)
+  (funcall (member-symbol full-class-name field-name)))
+
+(defun install-static-field-and-set (full-class-name field-name val)
+  (install-field full-class-name field-name)
+  (funcall (fdefinition `(setf ,(member-symbol full-class-name field-name)=
)) val))
+
+(defun install-field (full-class-name field-name)
+  (let* ((class-sym (canonic-class-symbol full-class-name))
+         (class (get-java-class-ref class-sym))
+         (field (class.getfield class field-name))
+         (field-sym (member-symbol full-class-name field-name))
+         (is-static (modifier.isstatic (field.getmodifiers field)))
+         (field-type-name (class.getname (field.gettype field)))
+         (boxer (get-boxer-fn field-type-name))
+         (unboxer (get-unboxer-fn field-type-name)))
+    (if is-static
+        (progn
+          (setf (fdefinition field-sym)
+                (lambda ()
+                  (funcall unboxer (field.get field nil) t)))
+          (setf (fdefinition `(setf ,field-sym))
+                (lambda (arg)
+                  (field.set field nil
+                             (get-ref (if (and boxer (not (boxed? arg)))
+                                          (funcall boxer arg)
+                                        arg)))
+                  arg)))
+      (progn
+        (setf (fdefinition field-sym)
+              (lambda (obj)
+                (funcall unboxer (field.get field (get-ref obj)) t)))
+        (setf (fdefinition `(setf ,field-sym))
+              (lambda (arg obj)
+                (field.set field (get-ref obj)
+                           (get-ref (if (and boxer (not (boxed? arg)))
+                                        (funcall boxer arg)
+                                      arg)))
+                arg))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;; methods ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+#|
+defines wrappers for all public methods of the class
+As with ctors, if a method is overloaded a single wrapper is created that =
handles
+overload resolution.
+The wrappers have the name classname.methodname
+If a method follows the JavaBeans property protocol (i.e. it is called get=
Something or isSomething
+and there is a corresponding setSomething, then a (setf classname.methodna=
me) will be defined
+that calls the latter
+|#
+
+(defun class-name-for-doc (class)
+  (let ((name (class.getname class)))
+    (if (class.isarray class)
+        (decode-array-name name)
+      name)))
+
+(defun build-method-doc-string (name methods)
+  (with-output-to-string (s)
+    (dolist (m methods)
+      (format s "~A~A ~A(~{~#[~;~A~:;~A,~]~})~%"
+              (if (modifier.isstatic (method.getmodifiers m))
+                  "static "
+                "")
+              (class.getname (method.getreturntype m))
+              name
+              (mapcar #'class-name-for-doc (jarray-to-list (method.getpara=
metertypes m)))))))
+
+(defmacro def-java-methods (full-class-name)
+  (let ((methods-by-name (get-methods-by-name full-class-name))
+        (defs nil))
+    (maphash (lambda (name methods)
+               (let ((method-sym (member-symbol full-class-name name)))
+                 (push `(defun ,method-sym (&rest args)
+                          ,(build-method-doc-string name methods)
+                          (apply #'install-methods-and-call ,full-class-na=
me ,name args))
+                       defs)
+                 (push `(export ',method-sym (symbol-package ',method-sym))
+                       defs)
+                 ;build setters when finding beans property protocol
+                 (flet ((add-setter-if (prefix)
+                          (when (eql 0 (search prefix name))
+                            (let ((setname (string-append "set" (subseq na=
me (length prefix)))))
+                              (when (gethash setname methods-by-name)
+                                (push `(defun (setf ,method-sym) (val &res=
t args)
+                                         (progn
+                                           (apply #',(member-symbol full-c=
lass-name setname)
+                                                  (append args (list val)))
+                                           val))
+                                      defs))))))
+                   (add-setter-if "get")
+                   (add-setter-if "is"))))
+             methods-by-name)
+    `(locally ,@(nreverse defs))))
+
+(defun install-methods-and-call (full-class-name method &rest args)
+  "initially all the member function symbols for a class are bound to this=
 function,
+when first called it will replace them with the appropriate direct thunks,
+then call the requested method - subsequent calls via those symbols will b=
e direct"
+  (install-methods full-class-name)
+  (apply (member-symbol full-class-name method) args))
+
+(defun decode-array-name (tn)
+  (let ((prim (assoc tn
+                     '(("Z" . "boolean")
+                       ("B" . "byte")
+                       ("C" . "char")
+                       ("S" . "short")
+                       ("I" . "int")
+                       ("J" . "long")
+                       ("F" . "float")
+                       ("D" . "double")
+                       ("V" . "void"))
+                     :test #'string-equal)))
+    (if prim
+        (rest prim)
+      (let ((array-depth (count #\[ tn)))
+        (if (=3D 0 array-depth)
+            (subseq tn 1 (1- (length tn))) ;strip leading L and trailing ;
+          (with-output-to-string (s)
+            (write-string (decode-array-name (subseq tn array-depth)) s)
+            (dotimes (x array-depth)
+              (write-string "[]" s))))))))
+
+(defun jarray-to-list (array)
+  (let (ret)
+    (do-jarray (x array)
+      (push x ret))
+    (nreverse ret)))
+
+(defun get-methods-by-name (full-class-name)
+  "returns an #'equal hashtable of lists of java.lang.Method refs keyed by=
 name"
+  (let* ((class-sym (canonic-class-symbol full-class-name))
+         (class (get-java-class-ref class-sym))
+         (method-array (class.getmethods class))
+         (methods-by-name (make-hash-table :test #'equal)))
+    (do-jarray (method method-array)
+      (push method (gethash (method.getName method) methods-by-name)))
+    methods-by-name))
+
+(defun install-methods (full-class-name)
+  (let ((methods-by-name (get-methods-by-name full-class-name)))
+    (maphash
+     (lambda (name methods)
+       (setf (fdefinition (member-symbol full-class-name name))
+             (make-method-thunk methods)))
+     methods-by-name)))
+
+(defun make-method-thunk (methods)
+  (if (rest methods) ;overloaded
+      (make-overloaded-thunk methods)
+    (make-non-overloaded-thunk (first methods))))
+
+(defun make-non-overloaded-thunk (method)
+  (let ((unboxer-fn (get-unboxer-fn (class.getname (method.getreturntype m=
ethod))))
+        (arg-boxers (get-arg-boxers (method.getparametertypes method)))
+        (is-static (modifier.isstatic (method.getmodifiers method))))
+    (lambda (&rest args)
+      (let ((arg-array (build-arg-array (if is-static args (rest args)) ar=
g-boxers)))
+        (prog1
+            (funcall unboxer-fn
+                     (method.invoke method
+                                    (if is-static nil (get-ref (first args=
)))
+                                    arg-array) t)
+          (when arg-array
+            (delete-local-ref arg-array)))))))
+
+(defun make-overloaded-thunk (methods)
+  (let ((thunks (make-thunks-by-args-length methods)))
+    (lambda (&rest args)
+      (let ((fn (cdr (assoc (length args) thunks))))
+        (if fn
+            (apply fn
+                   args)
+          (error "invalid arity"))))))
+
+(defun make-thunks-by-args-length (methods)
+  "returns an alist of thunks keyed by number of args"
+  (let ((methods-by-args-length (make-hash-table))
+        (thunks-by-args-length nil))
+    (dolist (method methods)
+      (let ((is-static (modifier.isstatic (method.getmodifiers method)))
+            (params-len (get-array-length (method.getparametertypes method=
))))
+        (push method (gethash (if is-static params-len (1+ params-len))
+                              methods-by-args-length))))
+    (maphash #'(lambda (args-len methods)
+                 (push (cons args-len
+                             (if (rest methods);truly overloaded
+                                 (make-type-overloaded-thunk methods)
+                               ;only one method with this number of args
+                               (make-non-overloaded-thunk (first methods))=
))
+                       thunks-by-args-length))
+             methods-by-args-length)
+    thunks-by-args-length))
+
+(defun make-type-overloaded-thunk (methods)
+  "these methods have the same number of args and must be distinguished by=
 type"
+  (let ((thunks (mapcar #'(lambda (method)
+                            (list (make-non-overloaded-thunk method)
+                                  (modifier.isstatic (method.getmodifiers =
method))
+                                  (jarray-to-list (method.getparametertype=
s method))))
+                        methods)))
+    (lambda (&rest args)
+      (block fn
+        (let ((arg-types (get-types-of-args args)))
+          (dolist (thunk-info thunks)
+            (destructuring-bind (thunk is-static param-types) thunk-info
+              (when (is-congruent-type-list param-types (if is-static arg-=
types (rest arg-types)))
+                (return-from fn (apply thunk args)))))
+          (error "No matching method"))))))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;; array support ;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun jref (array &rest subscripts)
+  "like aref, for Java arrays of non-primitive/reference types, settable"
+  (assert (every #'integerp subscripts))
+  (do*
+       ((sub subscripts (rest sub))
+        (a (get-ref array) (get-ref (array.get a (first sub)))))
+       ((null (rest sub))
+        (array.get a (first sub)))))
+
+(defun (setf jref) (val array &rest subscripts)
+  (assert (every #'integerp subscripts))
+  (do*
+       ((sub subscripts (rest sub))
+        (a (get-ref array) (get-ref (array.get a (first sub)))))
+       ((null (rest sub))
+        (array.set a (first sub) (get-ref val))
+        val)))
+
+(eval-when (:compile-toplevel :load-toplevel :execute)
+  (defmacro def-refs (&rest types)
+    `(locally
+       ,@(mapcan
+          (lambda (type)
+            (let ((ref-sym (intern (string-upcase (string-append "jref-" (=
symbol-name type))))))
+              (list =

+               `(defun ,ref-sym (array &rest subscripts)
+                  ,(format nil "like aref, for Java arrays of ~A, settable=
" (symbol-name type))
+                  (assert (every #'integerp subscripts))
+                  (do*
+                       ((sub subscripts (rest sub))
+                        (a (get-ref array) (get-ref (array.get a (first su=
b)))))
+                       ((null (rest sub))
+                        (,(intern (string-upcase (string-append "array.get=
" (symbol-name type))))
+                         a (first sub)))))
+
+               `(defun (setf ,ref-sym) (val array &rest subscripts)
+                  (assert (every #'integerp subscripts))
+                  (do*
+                       ((sub subscripts (rest sub))
+                        (a (get-ref array) (get-ref (array.get a (first su=
b)))))
+                       ((null (rest sub))
+                        (array.set a (first sub)
+                                   (,(intern (string-upcase (string-append=
 "box-"
+                                                                          =
 (symbol-name type))))
+                                    val))
+                        val))))))
+          types))))
+
+;arrays of primitives have their own accessors
+(def-refs boolean byte char double float int short long)
+
+(defun jlength (array)
+  "like length, for Java arrays"
+  (array.getlength (get-ref array)))
+
+(defgeneric make-new-array (type &rest dimensions)
+  (:documentation "generic function, with methods for all Java class desig=
nators")
+  (:method (type &rest dims)
+   (assert (every #'integerp dims))
+   (if (rest dims)
+       (let* ((ndim (length dims))
+              (dim-array (new-int-array ndim)))
+         (dotimes (i ndim)
+           (array.set dim-array i (box-int (nth i dims))))
+         (array.newinstance<java.lang.class-int<>> type dim-array))
+     (array.newinstance<java.lang.class-int> type (first dims)))))
+
+(defmethod make-new-array ((type symbol) &rest dimensions)
+  (apply #'make-new-array (get-java-class-ref type) dimensions))
+
+(defmethod make-new-array ((type string) &rest dimensions)
+  (apply #'make-new-array (find-java-class type) dimensions))
+
+(defmethod make-new-array ((type (eql :char)) &rest dimensions)
+  (apply #'make-new-array character.type dimensions))
+
+(defmethod make-new-array ((type (eql :int)) &rest dimensions)
+  (apply #'make-new-array integer.type dimensions))
+
+(defmethod make-new-array ((type (eql :boolean)) &rest dimensions)
+  (apply #'make-new-array boolean.type dimensions))
+
+(defmethod make-new-array ((type (eql :double)) &rest dimensions)
+  (apply #'make-new-array double.type dimensions))
+
+(defmethod make-new-array ((type (eql :byte)) &rest dimensions)
+  (apply #'make-new-array byte.type dimensions))
+
+(defmethod make-new-array ((type (eql :float)) &rest dimensions)
+  (apply #'make-new-array float.type dimensions))
+
+(defmethod make-new-array ((type (eql :short)) &rest dimensions)
+  (apply #'make-new-array short.type dimensions))
+
+(defmethod make-new-array ((type (eql :long)) &rest dimensions)
+  (apply #'make-new-array long.type dimensions))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;; arg/param helpers ;;;;;;;;;;;;;;;;;;;;;;
+
+(defun get-arg-boxers (param-types)
+  "returns a list with one entry per param, either nil or a function that =
boxes the arg"
+  (let (ret)
+    (do-jarray (param-type param-types)
+      (push (get-boxer-fn (class.getname param-type))
+            ret))
+    (nreverse ret)))
+
+(defun build-arg-array (args arg-boxers)
+  (when args
+    (let* ((arg-array (new-object-array (length args)
+                                      ;duplication of class-symbol logic
+                                      ;but must be fast
+                                        (get-java-class-ref '|java.lang|::=
|Object|)
+                                        nil)))
+      (do ((i 0 (incf i))
+           (args args (rest args))
+           (boxers arg-boxers (rest boxers)))
+          ((null args))
+        (let ((arg (first args))
+              (boxer (first boxers)))
+          (setf (jaref arg-array i)
+                (get-ref (if (and boxer (not (boxed? arg)))
+                             (funcall boxer arg)
+                           arg)))))
+      arg-array)))
+
+(defun get-types-of-args (args)
+  (let (ret)
+    (dolist (arg args)
+      (push (infer-box-type arg)
+            ret))
+    (nreverse ret)))
+
+(defun is-congruent-type-list (param-types arg-types)
+  (every #'(lambda (arg-type param-type)
+             (if arg-type
+                 (is-assignable-from arg-type param-type)
+               ;nil was passed - must be boolean or non-primitive target t=
ype
+               (or (not (class.isprimitive param-type))
+                   (is-assignable-from boolean.type param-type))))
+         arg-types param-types))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;; argument conversion and boxing ;;;;;;;;;;;;;;;;;;=
;;;;;;;;
+(defun box-string (s)
+"Given a string or symbol, returns reference to a Java string"
+  (local-ref-to-global-ref (convert-to-java-string s)))
+
+(defun unbox-string (ref &optional delete-local)
+  "Given a reference to a Java string, returns a Lisp string" =

+  (declare (ignore delete-local))
+  (convert-from-java-string (get-ref ref)))
+
+(defun get-boxer-fn (class-name)
+  (case-equal class-name
+    ("int" #'box-int)
+    ("boolean" #'box-boolean)
+    ("double" #'box-double)
+    ("java.lang.String" #'convert-to-java-string)
+    ("char" #'box-char)
+    ("byte" #'box-byte)
+    ("float" #'box-float)
+    ("long" #'box-long)
+    ("short" #'box-short)
+    (otherwise nil)))
+
+(defun get-boxer-fn-sym (class-name)
+  (case-equal class-name
+    ("int" 'box-int)
+    ("boolean" 'box-boolean)
+    ("double" 'box-double)
+    ("java.lang.String" 'convert-to-java-string)
+    ("char" 'box-char)
+    ("byte" 'box-byte)
+    ("float" 'box-float)
+    ("long" 'box-long)
+    ("short" 'box-short)
+    ("void" 'box-void)
+    (otherwise 'identity)))
+
+(defun boxed? (x)
+  (or (java-ref-p x)
+      (typep x '|java.lang|::object.)))
+
+(defun infer-box-type (x)
+  (cond
+   ((null x) nil)
+   ((boxed? x) (object.getclass (get-ref x)))
+   ((integerp x) integer.type)
+   ((numberp x) double.type)
+   ((eq x t) boolean.type)
+   ((or (stringp x) (symbolp x))
+    (get-java-class-ref '|java.lang|::|String|))
+   (t (error "can't infer box type"))))
+
+(defun get-unboxer-fn (class-name)
+  (case-equal class-name
+    ("int" #'unbox-int)
+    ("boolean" #'unbox-boolean)
+    ("double" #'unbox-double)
+    ("java.lang.String" #'unbox-string)
+    ("void" #'unbox-void)
+    ("char" #'unbox-char)
+    ("byte" #'unbox-byte)
+    ("float" #'unbox-float)
+    ("long" #'unbox-long)
+    ("short" #'unbox-short)
+    (otherwise  #'unbox-ref)))
+
+(defun get-unboxer-fn-sym (class-name)
+  (case-equal class-name
+    ("int" 'unbox-int)
+    ("boolean" 'unbox-boolean)
+    ("double" 'unbox-double)
+    ("java.lang.String" 'unbox-string)
+    ("void" 'unbox-void)
+    ("char" 'unbox-char)
+    ("byte" 'unbox-byte)
+    ("float" 'unbox-float)
+    ("long" 'unbox-long)
+    ("short" 'unbox-short)
+    (otherwise  'unbox-ref)))
+
+(defun unbox-ref (x &optional delete-local)
+  (declare (ignore delete-local))
+  (local-ref-to-global-ref x))
+
+(defun unbox-void (x &optional delete-local)
+  (declare (ignore x delete-local))
+  nil)
+
+(defun box-void (x)
+  (declare (ignore x))
+  nil)
+
+(defun box-boolean (x)
+  (boolean.new x))
+
+(defun unbox-boolean (obj &optional delete-local)
+  (prog1
+      (boolean.booleanvalue (get-ref obj))
+    (when delete-local (delete-local-ref obj))))
+
+(defun box-byte (x)
+  (assert (integerp x))
+  (byte.new x))
+
+(defun unbox-byte (x &optional delete-local)
+  (prog1
+      (byte.bytevalue (get-ref x))
+    (when delete-local (delete-local-ref x))))
+
+(defun box-char (x)
+  (character.new x))
+
+(defun unbox-char (x &optional delete-local)
+  (prog1
+      (character.charvalue (get-ref x))
+    (when delete-local (delete-local-ref x))))
+
+(defun box-double (x)
+  (assert (floatp x))
+  (double.new (coerce x 'double-float)))
+
+(defun unbox-double (x &optional delete-local)
+  (prog1
+      (double.doublevalue (get-ref x))
+    (when delete-local (delete-local-ref x))))
+
+(defun box-float (x)
+  (assert (floatp x))
+  (float.new x))
+
+(defun unbox-float (x &optional delete-local)
+  (prog1
+      (float.floatvalue (get-ref x))
+    (when delete-local (delete-local-ref x))))
+
+(defun box-int (x)
+  (assert (integerp x))
+  (integer.new x))
+
+(defun unbox-int (x &optional delete-local)
+  (prog1
+      (integer.intvalue (get-ref x))
+    (when delete-local (delete-local-ref x))))
+
+;can't directly construct Long because LW doesn't support long long fli on=
 32 bit platforms
+(defun box-long (x)
+  (assert (integerp x))
+  (long.valueof (princ-to-string x)))
+
+;here too, can only get an ints worth - aargh
+(defun unbox-long (obj &optional delete-local)
+  (prog1
+      (parse-integer (object.tostring (get-ref obj)))
+    (when delete-local (delete-local-ref obj))))
+
+(defun box-short (x)
+  (assert (integerp x))
+  (short.new x))
+
+(defun unbox-short (x &optional delete-local)
+  (prog1
+      (short.shortvalue (get-ref x))
+    (when delete-local (delete-local-ref x))))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;; proxy support ;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun proxy-hashcode (proxy)
+  ;use the hashcode of the proxy's class, =

+  ;because hashcode() on the proxy flows through to the invocation handler
+  ;is this rem guaranteed to be a fixnum?
+  (rem (object.hashcode (object.getclass proxy)) most-positive-fixnum))
+
+(defvar *proxy-table* (make-hash-table :test #'jeq :hash-function #'proxy-=
hashcode))
+
+;(defvar *proxy-list* nil)
+
+(defun store-proxy (proxy method-fn-alist)
+  ;(push (cons proxy method-fn-alist) *proxy-list*)
+  (setf (gethash proxy *proxy-table*) method-fn-alist))
+
+(defun recall-proxy (proxy)
+  ;(cdr (assoc proxy *proxy-list* :test #'jeq))
+  (gethash proxy *proxy-table*))
+
+(defun unregister-proxy (proxy)
+"Stops handling for the proxy and removes references from the Lisp side.
+Make sure it is no longer referenced from Java first!"
+  (remhash proxy *proxy-table*))
+
+(defun invocation-handler (proxy method args)
+  (let* ((method-fn-alist (recall-proxy proxy))
+         (fn (and method-fn-alist (second (assoc (object.tostring method) =
method-fn-alist
+                                                 :test #'equal)))))
+    (if fn
+        (funcall fn args)
+      (progn
+        ;(throw-new  (find-java-class "java.lang.UnsupportedOperationExcep=
tion")
+        ;            "No function registered in Lisp proxy object")
+        nil))))
+
+(defun enable-java-proxies ()
+  "must be called before any call to new-proxy, and requires jfli.jar be i=
n the classpath"
+  (jni:register-invocation-handler #'invocation-handler))
+
+(defun make-proxy-instance (&rest interface-defs)
+  (let* ((interfaces (mapcar #'first interface-defs))
+         (method-fn-alist (mapcan #'second interface-defs))
+         (len (length interfaces))
+         (iarray (array.newinstance<java.lang.class-int> (get-java-class-r=
ef '|java.lang|::|Class|)
+                                                         len)))
+    (dotimes (x len)
+      (setf (jref iarray x) (nth x interfaces)))
+    (let ((proxy (proxy.newproxyinstance (classloader.getsystemclassloader)
+                                         iarray
+                                         (lispinvocationhandler.new))))
+      (store-proxy proxy method-fn-alist)
+      proxy)))
+
+(defun find-java-class-in-macro (name)
+  (find-java-class
+   (if (symbolp name)
+       (symbol-value name)
+     name)))
+
+(defmacro new-proxy (&rest interface-defs)
+"interface-def -> (interface-name method-defs+)
+interface-name -> \"package.qualified.ClassName\" | classname. (must name =
a Java interface type)
+method-def -> (method-name arg-defs* body)
+arg-def -> arg-name | (arg-name arg-type)
+arg-type -> \"package.qualified.ClassName\" | classname. | :primitive
+method-name -> symbol | string (matched case-insensitively)
+
+Creates, registers and returns a Java object that implements the supplied =
interfaces"
+
+  (labels ((process-idefs (idefs)
+             (when idefs
+               (cons (process-idef (first idefs))
+                     (process-idefs (rest idefs)))))
+           (process-idef (idef)
+             (destructuring-bind (interface-name &rest method-defs) idef
+               (let* ((methods (class.getmethods (find-java-class-in-macro=
 interface-name)))
+                      (ret `(list (find-java-class ,interface-name)
+                                  (list ,@(mapcar (lambda (method-def)
+                                                    (process-method-def me=
thod-def methods))
+                                                  method-defs)))))
+                 ;check to make sure every function is defined
+                 (do-jarray (method methods)
+                   (let ((mname (object.tostring method)))
+                     (unless (member mname (rest (third ret)) :key #'secon=
d :test #'equal)
+                       (warn (format nil "proxy doesn't define:~%~A" mname=
)))))
+                 ret)))
+           (process-method-def (method-def methods)
+             (destructuring-bind (method-name (&rest arg-defs) &body body)=
 method-def
+               (let ((method (matching-method method-name arg-defs methods=
))
+                     (gargs (gensym)))
+                 `(list ,(object.tostring method)
+                        (lambda (,gargs)
+                          (,(get-boxer-fn-sym (class.getname (method.getre=
turntype method)))
+                           (let ,(arg-lets arg-defs
+                                           (jarray-to-list (method.getpara=
metertypes method))
+                                           gargs
+                                           0)
+                             , at body)))))))
+           (arg-lets (arg-defs params gargs idx)
+             (when arg-defs
+               (let ((arg (first arg-defs))
+                     (param (first params)))
+                 (cons `(,(if (atom arg) arg (first arg))
+                         (,(get-unboxer-fn-sym (class.getname param))
+                          (jref ,gargs ,idx) t))
+                       (arg-lets (rest arg-defs) (rest params) gargs (1+ i=
dx))))))
+           (matching-method (method-name arg-defs methods)
+             (let (match)
+               (do-jarray (method methods)
+                 (when (method-matches method-name arg-defs method)
+                   (if match
+                       (error (format nil "more than one method matches ~A=
" method-name))
+                     (setf match method))))
+               (or match (error (format nil "no method matches ~A" method-=
name)))))
+           (method-matches (method-name arg-defs method)
+             (when (string-equal method-name (method.getname method))
+               (let ((params (method.getparametertypes method)))
+                 (when (=3D (length arg-defs) (jlength params))
+                   (is-congruent arg-defs params)))))
+           (is-congruent (arg-defs params)
+             (every (lambda (arg param)
+                      (or (atom arg) ;no type spec matches anything
+                          (jeq (find-java-class-in-macro (second arg)) par=
am)))
+                    arg-defs (jarray-to-list params))))
+    `(make-proxy-instance ,@(process-idefs interface-defs))))

Added: trunk/source/examples/jfli/jni.lisp
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D
--- trunk/source/examples/jfli/jni.lisp (added)
+++ trunk/source/examples/jfli/jni.lisp Thu Nov 27 12:37:00 2008
@@ -1,0 +1,1239 @@
+;    Copyright (c) Rich Hickey. All rights reserved.
+;    The use and distribution terms for this software are covered by the
+;    Common Public License 1.0 (http://opensource.org/licenses/cpl.php)
+;    which can be found in the file CPL.TXT at the root of this distributi=
on.
+;    By using this software in any fashion, you are agreeing to be bound by
+;    the terms of this license.
+;    You must not remove this notice, or any other, from this software.
+
+#|
+This is a straight wrapper around the JNI API
+Originally I intended to expose this API directly, but it turns out
+that JNI is very sensitive to errors, and, given bad args, wrong types etc
+causes the JVM (and Lisp) to crash, not very much in the spirit of safe, r=
obust,
+interactive development offered by Lisp
+
+So, now this just forms the substrate under jfli, which uses the Reflectio=
n API, and is much
+more robust and error tolerant, at some cost in speed I guess.
+
+Bottom line is you shouldn't be using this API directly unless you are ext=
ending jfli,
+and then you must take care not to allow bad end-user data to pass through=
 to JNI. =

+
+Caveat emptor.
+
+I have tried to limit LispWorks FLI code to this file.
+|#
+
+(defpackage :jni
+  (:export
+   :*jni-lib-path*
+   :*pvm*
+   :*penv*
+   :register-invocation-handler
+   :create-jvm
+   :JNI-VERSION-1-2
+   :JNI-VERSION-1-4
+   :JNI-OK
+   :java-ref
+   :jvoid :jboolean :jbyte :jchar :jshort :jint :jlong :jfloat :jdouble :j=
size
+   :jobject :jclass :jthrowable :jstring :jarray
+   :jboolean-array :jbyte-array :jchar-array :jshort-array :jint-array :jl=
ong-array
+   :jfloat-array :jdouble-array :jobject-array
+   :jfield-id :jmethod-id :jweak
+   :pvm :penv
+   :jvalue
+   :arg-array
+   :jni-native-method :jni-env
+   :java-vm :java-vm-option :jdk-1-1-init-args
+   :jni-get-default-java-vm-init-args :java-vm-inits-args
+   :jni-create-java-vm :jni-get-created-java-vms
+   :try :try-null :try-neg
+   :local-ref-to-global-ref :local-ref-to-string
+   :def-jni-function :def-jni-functions :def-jni-constructor :def-jni-field
+   :jaref :convert-to-java-string :convert-from-java-string :java-ref-p
+   :is-name-of-primitive :split-package-and-class))
+
+(in-package :jni)
+
+(defvar *jni-lib-path*
+#+:MACOSX "/System/Library/Frameworks/JavaVM.framework/JavaVM"
+#+:WIN32 "C:/j2sdk1.4.2_01/jre/bin/client/jvm.dll"
+"Set this to point to your jvm dll prior to calling create-jvm")
+
+(defparameter *pvm* nil)
+(defparameter *penv* nil)
+
+(defparameter *process-envs* nil)
+
+(defconstant JNI-VERSION-1-2 #X10002)
+(defconstant JNI-VERSION-1-4 #X10004)
+(defconstant JNI-OK 0)
+
+(defun load-jni-lib (&optional (libpath *jni-lib-path*))
+  (fli:register-module :jni-lib
+                     :real-name libpath
+                     :connection-style :immediate))
+
+(fli:define-c-typedef pvoid (:ptr :void))
+(fli:define-c-typedef const-char-* (:reference-pass :ef-mb-string))
+(fli:define-c-typedef const-jchar-* (:reference-pass :ef-wc-string))
+(fli:define-foreign-pointer (java-ref (:allow-null t) (:predicate java-ref=
-p)) pvoid)
+
+(fli:define-c-typedef jvoid :void)
+(fli:define-c-typedef jboolean (:boolean (:unsigned :byte)))
+(fli:define-c-typedef jbyte :byte)
+(fli:define-c-typedef jchar :wchar-t)
+(fli:define-c-typedef jshort :short)
+(fli:define-c-typedef jint :int)
+(fli:define-c-typedef jlong :long-long)
+(fli:define-c-typedef jfloat :float)
+(fli:define-c-typedef jdouble :double)
+(fli:define-c-typedef jsize jint)
+(fli:define-c-typedef jobject java-ref)
+(fli:define-c-typedef jclass java-ref)
+(fli:define-c-typedef jthrowable java-ref)
+(fli:define-c-typedef jstring java-ref)
+(fli:define-c-typedef jarray java-ref)
+(fli:define-c-typedef jboolean-array java-ref)
+(fli:define-c-typedef jbyte-array java-ref)
+(fli:define-c-typedef jchar-array java-ref)
+(fli:define-c-typedef jshort-array java-ref)
+(fli:define-c-typedef jint-array java-ref)
+(fli:define-c-typedef jlong-array java-ref)
+(fli:define-c-typedef jfloat-array java-ref)
+(fli:define-c-typedef jdouble-array java-ref)
+(fli:define-c-typedef jobject-array java-ref)
+(fli:define-c-typedef jfield-id pvoid)
+(fli:define-c-typedef jmethod-id pvoid)
+(fli:define-c-typedef jweak java-ref)
+
+(fli:define-c-typedef pvm (:ptr (:ptr java-vm)))
+(fli:define-c-typedef penv (:ptr (:ptr jni-env)))
+(fli:define-foreign-type pfunc (&rest fargs)
+  `(:ptr (:function , at fargs)))
+
+(fli:define-c-union jvalue
+  (:z jboolean)
+  (:b jbyte)
+  (:c jchar)
+  (:s jshort)
+  (:i jint)
+  (:j jlong)
+  (:f jfloat)
+  (:d jdouble)
+  (:l jobject))
+
+(fli:define-c-typedef arg-array (:c-array jvalue))
+
+(eval-when (:compile-toplevel)
+  (defun build-struct-entries (name members)
+    (mapcar #'(lambda (member)
+                (if (=3D 2 (length member)) ;padding or other non-function=
 entry
+                    member
+                  (destructuring-bind (func args ret &key lambda-list) mem=
ber
+                    (declare (ignore lambda-list))
+                    `(,func (pfunc ,(cons `(:ptr (:ptr ,name))
+                                          (mapcar #'second args))
+                                   ,ret)))))
+            members)))
+
+(eval-when (:compile-toplevel)
+  (defun build-access-functions (name global members)
+    (mapcar #'(lambda (member)
+                (if (=3D 2 (length member)) ;padding or other non-function=
 entry
+                    ()
+                  (destructuring-bind (func args ret &key lambda-list) mem=
ber
+                    (let ((thunk (intern (concatenate 'string (symbol-name=
 func) "-thunk")))
+                          (genv (gensym))
+                        ;(func (intern (symbol-name f)))
+                          )
+                      `(locally
+                         (fli:define-foreign-funcallable
+                          ,thunk
+                          ,(cons `(this (:ptr (:ptr ,name))) args)
+                          :result-type ,ret)
+                         (defun ,func ,(if lambda-list
+                                           lambda-list
+                                         (mapcar #'first args))
+                           (let ((,genv ,global))
+                             (,thunk
+                              (fli:foreign-slot-value (fli:dereference ,ge=
nv) ',func)
+                              ,genv
+                              ,@(mapcar #'first args))))
+                         (export ',func))))))
+            members)))
+
+(defmacro defvtable (name global &rest members)
+  `(locally
+     (fli:define-c-struct ,name ,@(build-struct-entries name members))
+     ,@(build-access-functions name global members)))
+
+(fli:define-c-struct jni-native-method
+  (name (:ptr :char))
+  (signature (:ptr :char))
+  (fn-ptr pvoid)
+  )
+
+(defun current-env ()
+  "memoizes attach-current-thread per process"
+  (or
+   *penv*
+   (cdr (assoc mp:*current-process* *process-envs*))
+   (multiple-value-bind (ret env) (attach-current-thread)
+     (declare (ignore ret))
+     (push (cons mp:*current-process* env) *process-envs*)
+     env)))
+
+(defvtable jni-env (current-env)
+           (reserved-0 pvoid)                                             =
 ;0
+           (reserved-1 pvoid)                                             =
 ;1
+           (reserved-2 pvoid)                                             =
 ;2
+           (reserved-3 pvoid)                                             =
 ;3
+  ;some mac nonsense requires this non-portable padding, so much for a bin=
ary spec
+           #+:MACOSX  (cfm-padding (:foreign-array pvoid (225)))
+           (get-version () jint)                                          =
 ;4
+           (define-class ((name const-char-*)                             =
 ;5
+                          (loader jobject)
+                          (buf (:ptr jbyte))
+                          (len jsize)) jclass) =

+           (jni-find-class ((name const-char-*)) jclass)                  =
     ;6
+           (from-reflected-method ((method jobject)) jmethod-id)          =
 ;7
+           (from-reflected-field ((field jobject)) jfield-id)             =
 ;8
+           (to-reflected-method ((cls jclass)                             =
 ;9
+                                 (method-id jmethod-id)
+                                 (is-static jboolean)) jobject)
+           (get-superclass ((clazz jclass)) jclass)                       =
 ;10
+           (is-assignable-from ((sub jclass)                              =
 ;11
+                                (sup jclass)) jboolean)
+           (to-reflected-field ((cls jclass)                              =
 ;12
+                                (field-id jfield-id)
+                                (is-static jboolean)) jobject)
+           (jni-throw ((obj jthrowable)) jint)                            =
     ;13
+           (throw-new ((clazz jclass)                                     =
 ;14
+                       (msg const-char-*)) jint)
+           (exception-occurred () jthrowable)                             =
 ;15
+           (exception-describe () :void)                                  =
 ;16
+           (exception-clear () :void)                                     =
 ;17
+           (fatal-error ((msg const-char-*)) :void)                       =
 ;18
+           (push-local-frame ((capacity jint)) jint)                      =
 ;19
+           (pop-local-frame ((result jobject)) jobject)                   =
 ;20
+           (new-global-ref ((lobj jobject)) jobject)                      =
 ;21
+           (delete-global-ref ((gref jobject)) :void)                     =
 ;22
+           (delete-local-ref ((lref jobject)) :void)                      =
 ;23
+           (is-same-object ((obj1 jobject)                                =
 ;24
+                            (obj2 jobject)) jboolean)
+           (new-local-ref ((ref jobject)) jobject)                        =
 ;25
+           (ensure-local-capacity ((capacity jint)) jint)                 =
 ;26
+           (alloc-object ((clazz jclass)) jobject)                        =
 ;27
+           (new-object pvoid)                                             =
 ;28
+           (new-object-v pvoid)                                           =
 ;29
+           (new-object-a ((clazz jclass)                                  =
 ;30
+                          (method-id jmethod-id)
+                          (args arg-array)) jobject)
+           (get-object-class ((obj jobject)) jclass)                      =
 ;31
+           (is-instance-of ((obj jobject)                                 =
 ;32
+                            (clazz jclass)) jboolean)
+           (get-method-id ((clazz jclass)                                 =
 ;33
+                           (name const-char-*)
+                           (sig const-char-*)) jmethod-id)
+
+           (call-object-method pvoid)                                     =
 ;34
+           (call-object-method-v pvoid)                                   =
 ;35
+           (call-object-method-a ((obj jobject)                           =
 ;36
+                                  (method-id jmethod-id)
+                                  (args arg-array)) jobject)
+           (call-boolean-method pvoid)                                    =
 ;37
+           (call-boolean-method-v pvoid)                                  =
 ;38
+           (call-boolean-method-a ((obj jobject)                          =
 ;39
+                                   (method-id jmethod-id)                  =

+                                   (args arg-array)) jboolean)
+           (call-byte-method pvoid)                                       =
 ;40
+           (call-byte-method-v pvoid)                                     =
 ;41
+           (call-byte-method-a ((obj jobject)                             =
 ;42
+                                (method-id jmethod-id)
+                                (args arg-array)) jbyte)
+           (call-char-method pvoid)                                       =
 ;43
+           (call-char-method-v pvoid)                                     =
 ;44
+           (call-char-method-a ((obj jobject)                             =
 ;45
+                                (method-id jmethod-id)
+                                (args arg-array)) jchar)
+           (call-short-method pvoid)                                      =
 ;46
+           (call-short-method-v pvoid)                                    =
 ;47
+           (call-short-method-a ((obj jobject)                            =
 ;48
+                                 (method-id jmethod-id)
+                                 (args arg-array)) jshort)
+           (call-int-method pvoid)                                        =
 ;49
+           (call-int-method-v pvoid)                                      =
 ;50
+           (call-int-method-a ((obj jobject)                              =
 ;51
+                               (method-id jmethod-id)
+                               (args arg-array)) jint)
+           (call-long-method pvoid)                                       =
 ;52
+           (call-long-method-v pvoid)                                     =
 ;53
+           (call-long-method-a ((obj jobject)                             =
 ;54
+                                (method-id jmethod-id)
+                                (args arg-array)) jlong)
+           (call-float-method pvoid)                                      =
 ;55
+           (call-float-method-v pvoid)                                    =
 ;56
+           (call-float-method-a ((obj jobject)                            =
 ;57
+                                 (method-id jmethod-id)
+                                 (args arg-array)) jfloat)
+           (call-double-method pvoid)                                     =
 ;58
+           (call-double-method-v pvoid)                                   =
 ;59
+           (call-double-method-a ((obj jobject)                           =
 ;60
+                                  (method-id jmethod-id)
+                                  (args arg-array)) jdouble)
+           (call-void-method pvoid)                                       =
 ;61
+           (call-void-method-v pvoid)                                     =
 ;62
+           (call-void-method-a ((obj jobject)                             =
 ;63
+                                (method-id jmethod-id)
+                                (args arg-array)) jvoid)
+
+           (call-nonvirtual-object-method pvoid)                          =
 ;64
+           (call-nonvirtual-object-method-v pvoid)                        =
 ;65
+           (call-nonvirtual-object-method-a ((obj jobject)                =
 ;66
+                                             (clazz jclass)
+                                             (method-id jmethod-id)
+                                             (args arg-array)) jobject)
+           (call-nonvirtual-boolean-method pvoid)                         =
 ;67
+           (call-nonvirtual-boolean-method-v pvoid)                       =
 ;68
+           (call-nonvirtual-boolean-method-a ((obj jobject)               =
 ;69
+                                              (clazz jclass)
+                                              (method-id jmethod-id)
+                                              (args arg-array)) jboolean)
+           (call-nonvirtual-byte-method pvoid)                            =
 ;70
+           (call-nonvirtual-byte-method-v pvoid)                          =
 ;71
+           (call-nonvirtual-byte-method-a ((obj jobject)                  =
 ;72
+                                           (clazz jclass)
+                                           (method-id jmethod-id)
+                                           (args arg-array)) jbyte)
+           (call-nonvirtual-char-method pvoid)                            =
 ;73
+           (call-nonvirtual-char-method-v pvoid)                          =
 ;74
+           (call-nonvirtual-char-method-a ((obj jobject)                  =
 ;75
+                                           (clazz jclass)
+                                           (method-id jmethod-id)
+                                           (args arg-array)) jchar)
+           (call-nonvirtual-short-method pvoid)                           =
 ;76
+           (call-nonvirtual-short-method-v pvoid)                         =
 ;77
+           (call-nonvirtual-short-method-a ((obj jobject)                 =
 ;78
+                                            (clazz jclass)
+                                            (method-id jmethod-id)
+                                            (args arg-array)) jshort)
+           (call-nonvirtual-int-method pvoid)                             =
 ;79
+           (call-nonvirtual-int-method-v pvoid)                           =
 ;80
+           (call-nonvirtual-int-method-a ((obj jobject)                   =
 ;81
+                                          (clazz jclass)
+                                          (method-id jmethod-id)
+                                          (args arg-array)) jint)
+           (call-nonvirtual-long-method pvoid)                            =
 ;82
+           (call-nonvirtual-long-method-v pvoid)                          =
 ;83
+           (call-nonvirtual-long-method-a ((obj jobject)                  =
 ;84
+                                           (clazz jclass)
+                                           (method-id jmethod-id)
+                                           (args arg-array)) jlong)
+           (call-nonvirtual-float-method pvoid)                           =
 ;85
+           (call-nonvirtual-float-method-v pvoid)                         =
 ;86
+           (call-nonvirtual-float-method-a ((obj jobject)                 =
 ;87
+                                            (clazz jclass)
+                                            (method-id jmethod-id)
+                                            (args arg-array)) jfloat)
+           (call-nonvirtual-double-method pvoid)                          =
 ;88
+           (call-nonvirtual-double-method-v pvoid)                        =
 ;89
+           (call-nonvirtual-double-method-a ((obj jobject)                =
 ;90
+                                             (clazz jclass)
+                                             (method-id jmethod-id)
+                                             (args arg-array)) jdouble)
+           (call-nonvirtual-void-method pvoid)                            =
 ;91
+           (call-nonvirtual-void-method-v pvoid)                          =
 ;92
+           (call-nonvirtual-void-method-a ((obj jobject)                  =
 ;93
+                                           (clazz jclass)
+                                           (method-id jmethod-id)
+                                           (args arg-array)) jvoid)
+           (get-field-id ((clazz jclass)                                  =
 ;94
+                          (name const-char-*)
+                          (sig const-char-*)) jfield-id)
+
+           (get-object-field ((obj jobject)                               =
 ;95
+                              (field-id jfield-id)) jobject)
+           (get-boolean-field ((obj jobject)                              =
 ;96
+                               (field-id jfield-id)) jboolean)  =

+           (get-byte-field ((obj jobject)                                 =
 ;97
+                            (field-id jfield-id)) jbyte)  =

+           (get-char-field ((obj jobject)                                 =
 ;98
+                            (field-id jfield-id)) jchar)  =

+           (get-short-field ((obj jobject)                                =
 ;99
+                             (field-id jfield-id)) jshort)  =

+           (get-int-field ((obj jobject)                                  =
 ;100
+                           (field-id jfield-id)) jint)  =

+           (get-long-field ((obj jobject)                                 =
 ;101
+                            (field-id jfield-id)) jlong)  =

+           (get-float-field ((obj jobject)                                =
 ;102
+                             (field-id jfield-id)) jfloat)  =

+           (get-double-field ((obj jobject)                               =
 ;103
+                              (field-id jfield-id)) jdouble)  =

+
+           (set-object-field ((obj jobject)                               =
 ;104
+                              (field-id jfield-id)
+                              (val jobject)) jvoid)
+           (set-boolean-field ((obj jobject)                              =
 ;105
+                               (field-id jfield-id)
+                               (val jboolean)) jvoid)
+           (set-byte-field ((obj jobject)                                 =
 ;106
+                            (field-id jfield-id)
+                            (val jbyte)) jvoid)
+           (set-char-field ((obj jobject)                                 =
 ;107
+                            (field-id jfield-id)
+                            (val jchar)) jvoid)
+           (set-short-field ((obj jobject)                                =
 ;108
+                             (field-id jfield-id)
+                             (val jshort)) jvoid)
+           (set-int-field ((obj jobject)                                  =
 ;109
+                           (field-id jfield-id)
+                           (val jint)) jvoid)
+           (set-long-field ((obj jobject)                                 =
 ;110
+                            (field-id jfield-id)
+                            (val jlong)) jvoid)
+           (set-float-field ((obj jobject)                                =
 ;111
+                             (field-id jfield-id)
+                             (val jfloat)) jvoid)
+           (set-double-field ((obj jobject)                               =
 ;112
+                              (field-id jfield-id)
+                              (val jdouble)) jvoid)
+
+           (get-static-method-id ((clazz jclass)                          =
 ;113
+                                  (name const-char-*)
+                                  (sig const-char-*)) jmethod-id)
+
+           (call-static-object-method pvoid)                              =
 ;114
+           (call-static-object-method-v pvoid)                            =
 ;115
+           (call-static-object-method-a ((clazz jclass)                   =
 ;116
+                                         (method-id jmethod-id)
+                                         (args arg-array)) jobject)
+           (call-static-boolean-method pvoid)                             =
 ;117
+           (call-static-boolean-method-v pvoid)                           =
 ;118
+           (call-static-boolean-method-a ((clazz jclass)                  =
 ;119
+                                          (method-id jmethod-id)
+                                          (args arg-array)) jboolean)
+           (call-static-byte-method pvoid)                                =
 ;120
+           (call-static-byte-method-v pvoid)                              =
 ;121
+           (call-static-byte-method-a ((clazz jclass)                     =
 ;122
+                                       (method-id jmethod-id)
+                                       (args arg-array)) jbyte)
+           (call-static-char-method pvoid)                                =
 ;123
+           (call-static-char-method-v pvoid)                              =
 ;124
+           (call-static-char-method-a ((clazz jclass)                     =
 ;125
+                                       (method-id jmethod-id)
+                                       (args arg-array)) jchar)
+           (call-static-short-method pvoid)                               =
 ;126
+           (call-static-short-method-v pvoid)                             =
 ;127
+           (call-static-short-method-a ((clazz jclass)                    =
 ;128
+                                        (method-id jmethod-id)
+                                        (args arg-array)) jshort)
+           (call-static-int-method pvoid)                                 =
 ;129
+           (call-static-int-method-v pvoid)                               =
 ;130
+           (call-static-int-method-a ((clazz jclass)                      =
 ;131
+                                      (method-id jmethod-id)
+                                      (args arg-array)) jint)
+           (call-static-long-method pvoid)                                =
 ;132
+           (call-static-long-method-v pvoid)                              =
 ;133
+           (call-static-long-method-a ((clazz jclass)                     =
 ;134
+                                       (method-id jmethod-id)
+                                       (args arg-array)) jlong)
+           (call-static-float-method pvoid)                               =
 ;135
+           (call-static-float-method-v pvoid)                             =
 ;136
+           (call-static-float-method-a ((clazz jclass)                    =
 ;137
+                                        (method-id jmethod-id)
+                                        (args arg-array)) jfloat)
+           (call-static-double-method pvoid)                              =
 ;138
+           (call-static-double-method-v pvoid)                            =
 ;139
+           (call-static-double-method-a ((clazz jclass)                   =
 ;140
+                                         (method-id jmethod-id)
+                                         (args arg-array)) jdouble)
+           (call-static-void-method pvoid)                                =
 ;141
+           (call-static-void-method-v pvoid)                              =
 ;142
+           (call-static-void-method-a ((clazz jclass)                     =
 ;143
+                                       (method-id jmethod-id)
+                                       (args arg-array)) jvoid)
+
+           (get-static-field-id ((clazz jclass)                           =
 ;144
+                                 (name const-char-*)
+                                 (sig const-char-*)) jfield-id)
+
+           (get-static-object-field ((clazz jclass)                       =
 ;145
+                                     (field-id jfield-id)) jobject)
+           (get-static-boolean-field ((clazz jclass)                      =
 ;146
+                                      (field-id jfield-id)) jboolean)
+           (get-static-byte-field ((clazz jclass)                         =
 ;147
+                                   (field-id jfield-id)) jbyte)
+           (get-static-char-field ((clazz jclass)                         =
 ;148
+                                   (field-id jfield-id)) jchar)
+           (get-static-short-field ((clazz jclass)                        =
 ;149
+                                    (field-id jfield-id)) jshort)
+           (get-static-int-field ((clazz jclass)                          =
 ;150
+                                  (field-id jfield-id)) jint)
+           (get-static-long-field ((clazz jclass)                         =
 ;151
+                                   (field-id jfield-id)) jlong)
+           (get-static-float-field ((clazz jclass)                        =
 ;152
+                                    (field-id jfield-id)) jfloat)
+           (get-static-double-field ((clazz jclass)                       =
 ;153
+                                     (field-id jfield-id)) jdouble)
+
+           (set-static-object-field ((clazz jclass)                       =
 ;154
+                                     (field-id jfield-id)
+                                     (val jobject)) jvoid)
+           (set-static-boolean-field ((clazz jclass)                      =
 ;155
+                                      (field-id jfield-id)
+                                      (val jboolean)) jvoid)
+           (set-static-byte-field ((clazz jclass)                         =
 ;156
+                                   (field-id jfield-id)
+                                   (val jbyte)) jvoid)
+           (set-static-char-field ((clazz jclass)                         =
 ;157
+                                   (field-id jfield-id)
+                                   (val jchar)) jvoid)
+           (set-static-short-field ((clazz jclass)                        =
 ;158
+                                    (field-id jfield-id)
+                                    (val jshort)) jvoid)
+           (set-static-int-field ((clazz jclass)                          =
 ;159
+                                  (field-id jfield-id)
+                                  (val jint)) jvoid)
+           (set-static-long-field ((clazz jclass)                         =
 ;160
+                                   (field-id jfield-id)
+                                   (val jlong)) jvoid)
+           (set-static-float-field ((clazz jclass)                        =
 ;161
+                                    (field-id jfield-id)
+                                    (val jfloat)) jvoid)
+           (set-static-double-field ((clazz jclass)                       =
 ;162
+                                     (field-id jfield-id)
+                                     (val jdouble)) jvoid)
+
+           (new-string ((uchars (:reference-pass :ef-wc-string))          =
     ;163
+                        (len jsize)) jstring)
+           (get-string-length ((str jstring)) jsize)                      =
 ;164
+           (get-string-chars ((str jstring)                               =
 ;165
+                              (is-copy (:reference-return jboolean)))
+                             ;(:c-array jchar 1000)
+                             (:ptr :wchar-t)
+                             ;(:ef-wc-string :external-format :unicode)
+                             :lambda-list (str &optional is-copy))
+           (release-string-chars ((str jstring)                           =
 ;166
+                                  (chars (:ptr jchar))) jvoid)
+
+           (new-string-utf ((chars const-char-*)) jstring)                =
 ;167
+           (get-string-utf-length ((str jstring)) jsize)                  =
 ;168
+           (get-string-utf-chars ((str jstring)                           =
 ;169
+                                  (is-copy (:reference-return jboolean)))
+                                 ;(:c-array :char 1000)
+                                 (:ptr :char)
+                                 :lambda-list (str &optional is-copy))
+           (release-string-utf-chars ((str jstring)                       =
 ;170
+                                      (chars (:ptr :char))) jvoid)
+
+           (get-array-length ((array jarray)) jsize)                      =
 ;171
+           =

+           (new-object-array ((len jsize)                                 =
 ;172
+                              (element-type jclass)
+                              (initial-element jobject)) jarray)
+           (get-object-array-element ((array jobject-array)               =
 ;173
+                                      (index jsize)) jobject)
+           (set-object-array-element ((array jobject-array)               =
 ;174
+                                      (index jsize)
+                                      (val jobject)) jvoid)
+
+           (new-boolean-array ((len jsize)) jboolean-array)               =
 ;175
+           (new-byte-array ((len jsize)) jbyte-array)                     =
 ;176
+           (new-char-array ((len jsize)) jchar-array)                     =
 ;177
+           (new-short-array ((len jsize)) jshort-array)                   =
 ;178
+           (new-int-array ((len jsize)) jint-array)                       =
 ;179
+           (new-long-array ((len jsize)) jlong-array)                     =
 ;180
+           (new-float-array ((len jsize)) jfloat-array)                   =
 ;181
+           (new-double-array ((len jsize)) jdouble-array)                 =
 ;182
+
+           (get-boolean-array-elements ((array jboolean-array)            =
 ;183
+                                        (is-copy (:reference-return jboole=
an)))
+                                       (:ptr jboolean)
+                                       :lambda-list (array &optional is-co=
py))
+           (get-byte-array-elements ((array jbyte-array)                  =
 ;184
+                                     (is-copy (:reference-return jboolean)=
))
+                                    (:ptr jbyte)
+                                    :lambda-list (array &optional is-copy))
+           (get-char-array-elements ((array jchar-array)                  =
 ;185
+                                     (is-copy (:reference-return jboolean)=
))
+                                    (:ptr jchar)
+                                    :lambda-list (array &optional is-copy))
+           (get-short-array-elements ((array jshort-array)                =
 ;186
+                                      (is-copy (:reference-return jboolean=
)))
+                                     (:ptr jshort)
+                                     :lambda-list (array &optional is-copy=
))
+           (get-int-array-elements ((array jint-array)                    =
 ;187
+                                    (is-copy (:reference-return jboolean)))
+                                   (:ptr jint)
+                                   :lambda-list (array &optional is-copy))
+           (get-long-array-elements ((array jlong-array)                  =
 ;188
+                                     (is-copy (:reference-return jboolean)=
))
+                                    (:ptr jlong)
+                                    :lambda-list (array &optional is-copy))
+           (get-float-array-elements ((array jfloat-array)                =
 ;189
+                                      (is-copy (:reference-return jboolean=
)))
+                                     (:ptr jfloat)
+                                     :lambda-list (array &optional is-copy=
))
+           (get-double-array-elements ((array jdouble-array)              =
 ;190
+                                       (is-copy (:reference-return jboolea=
n)))
+                                      (:ptr jdouble)
+                                      :lambda-list (array &optional is-cop=
y))
+
+           (release-boolean-array-elements ((array jboolean-array)        =
 ;191
+                                            (elems (:ptr jboolean))
+                                            (mode jint)) jvoid
+                                           :lambda-list (array elems &opti=
onal (mode 0)))
+           (release-byte-array-elements ((array jbyte-array)              =
 ;192
+                                         (elems (:ptr jbyte))
+                                         (mode jint)) jvoid
+                                        :lambda-list (array elems &optiona=
l (mode 0)))
+           (release-char-array-elements ((array jchar-array)              =
 ;193
+                                         (elems (:ptr jchar))
+                                         (mode jint)) jvoid
+                                        :lambda-list (array elems &optiona=
l (mode 0)))
+           (release-short-array-elements ((array jshort-array)            =
 ;194
+                                          (elems (:ptr jshort))
+                                          (mode jint)) jvoid
+                                         :lambda-list (array elems &option=
al (mode 0)))
+           (release-int-array-elements ((array jint-array)                =
 ;195
+                                        (elems (:ptr jint))
+                                        (mode jint)) jvoid
+                                       :lambda-list (array elems &optional=
 (mode 0)))
+           (release-long-array-elements ((array jlong-array)              =
 ;196
+                                         (elems (:ptr jlong))
+                                         (mode jint)) jvoid
+                                        :lambda-list (array elems &optiona=
l (mode 0)))
+           (release-float-array-elements ((array jfloat-array)            =
 ;197
+                                          (elems (:ptr jfloat))
+                                          (mode jint)) jvoid
+                                         :lambda-list (array elems &option=
al (mode 0)))
+           (release-double-array-elements ((array jdouble-array)          =
 ;198
+                                           (elems (:ptr jdouble))
+                                           (mode jint)) jvoid
+                                          :lambda-list (array elems &optio=
nal (mode 0)))
+
+           (get-boolean-array-region ((array jboolean-array)              =
 ;199
+                                      (start jsize)
+                                      (len jsize)
+                                      (buf (:ptr jboolean))) jvoid)
+           (get-byte-array-region ((array jbyte-array)                    =
 ;200
+                                   (start jsize)
+                                   (len jsize)
+                                   (buf (:ptr jbyte))) jvoid)
+           (get-char-array-region ((array jchar-array)                    =
 ;201
+                                   (start jsize)
+                                   (len jsize)
+                                   (buf (:ptr jchar))) jvoid)
+           (get-short-array-region ((array jshort-array)                  =
 ;202
+                                    (start jsize)
+                                    (len jsize)
+                                    (buf (:ptr jshort))) jvoid)
+           (get-int-array-region ((array jint-array)                      =
 ;203
+                                  (start jsize)
+                                  (len jsize)
+                                  (buf (:ptr jint))) jvoid)
+           (get-long-array-region ((array jlong-array)                    =
 ;204
+                                   (start jsize)
+                                   (len jsize)
+                                   (buf (:ptr jlong))) jvoid)
+           (get-float-array-region ((array jfloat-array)                  =
 ;205
+                                    (start jsize)
+                                    (len jsize)
+                                    (buf (:ptr jfloat))) jvoid)
+           (get-double-array-region ((array jdouble-array)                =
 ;206
+                                     (start jsize)
+                                     (len jsize)
+                                     (buf (:ptr jdouble))) jvoid)
+
+           (set-boolean-array-region ((array jboolean-array)              =
 ;207
+                                      (start jsize)
+                                      (len jsize)
+                                      (buf (:ptr jboolean))) jvoid)
+           (set-byte-array-region ((array jbyte-array)                    =
 ;208
+                                   (start jsize)
+                                   (len jsize)
+                                   (buf (:ptr jbyte))) jvoid)
+           (set-char-array-region ((array jchar-array)                    =
 ;209
+                                   (start jsize)
+                                   (len jsize)
+                                   (buf (:ptr jchar))) jvoid)
+           (set-short-array-region ((array jshort-array)                  =
 ;210
+                                    (start jsize)
+                                    (len jsize)
+                                    (buf (:ptr jshort))) jvoid)
+           (set-int-array-region ((array jint-array)                      =
 ;211
+                                  (start jsize)
+                                  (len jsize)
+                                  (buf (:ptr jint))) jvoid)
+           (set-long-array-region ((array jlong-array)                    =
 ;212
+                                   (start jsize)
+                                   (len jsize)
+                                   (buf (:ptr jlong))) jvoid)
+           (set-float-array-region ((array jfloat-array)                  =
 ;213
+                                    (start jsize)
+                                    (len jsize)
+                                    (buf (:ptr jfloat))) jvoid)
+           (set-double-array-region ((array jdouble-array)                =
 ;214
+                                     (start jsize)
+                                     (len jsize)
+                                     (buf (:ptr jdouble))) jvoid)
+
+           (register-natives ((clazz jclass)                              =
 ;215
+                              (methods (:ptr jni-native-method))
+                              (n-methods jsize)) jint)
+           (unregister-natives ((clazz jclass)) jint)                     =
 ;216
+           (monitor-enter ((obj jobject)) jint)                           =
 ;217
+           (monitor-exit ((obj jobject)) jint)                            =
 ;218
+
+           (get-java-vm ((vm (:reference-return pvm))) jint               =
 ;219
+                        :lambda-list (&optional (vm t)))
+
+           (get-string-region ((str jstring)                              =
 ;220
+                               (start jsize)
+                               (len jsize)
+                               (buf (:ptr jchar))) jvoid)
+           (get-string-utf-region ((str jstring)                          =
 ;221
+                               (start jsize)
+                               (len jsize)
+                               (buf (:ptr :char))) jvoid)
+
+           (get-primitive-array-critical ((array jarray)                  =
 ;222
+                                          (is-copy (:reference-return jboo=
lean))) pvoid
+                                         :lambda-list (array &optional is-=
copy))
+           (release-primitive-array-critical ((array jarray)              =
 ;223
+                                            (carray pvoid)
+                                            (mode jint)) jvoid
+                                           :lambda-list (array carray &opt=
ional (mode 0)))
+           =

+           (get-string-critical ((str jstring)                            =
 ;224
+                                 (is-copy (:reference-return jboolean)))
+                                (:ptr jchar)
+                                :lambda-list (str &optional is-copy))
+           (release-string-critical ((str jstring)                        =
 ;225
+                                     (cstring (:ptr jchar))) jvoid)
+           (new-weak-global-ref ((obj jobject)) jweak)                    =
 ;226
+           (delete-weak-global-ref ((ref jweak)) jvoid)                   =
 ;227
+           (exception-check () jboolean)                                  =
 ;228
+           )
+
+(defun get-pvm ()
+  (or *pvm*
+      (error "JVM not loaded")))
+
+(defvtable java-vm (get-pvm)
+  (reserved-0 pvoid)
+  (reserved-1 pvoid)
+  (reserved-2 pvoid)
+#+:MACOSX  (cfm-padding (:foreign-array pvoid (4)))
+  (destroy-java-vm () jint)
+  (attach-current-thread ((penv (:reference-return penv)) (args pvoid)) ji=
nt
+                         :lambda-list (&optional args (penv t)))
+  (detach-current-thread () jint)
+  (get-env ((penv (:reference-return penv)) (interface-id jint)) jint
+           :lambda-list (interface-id &optional (penv t))))
+  =

+(fli:define-c-struct java-vm-option
+  (option-string (:ptr :char))
+  (extra-info pvoid))
+
+(fli:define-c-struct jdk-1-1-init-args
+  (version jint)
+  (properties (:ptr (:ptr char)))
+  (check-source jint)
+  (native-stack-size jint)
+  (java-stack-size jint)
+  (min-heap-size jint)
+  (max-heap-size jint)
+  (verify-mode jint)
+  (class-path (:ptr :char))
+  (vprintf pvoid)
+  (exit pvoid)
+  (abort pvoid)
+  (enable-class-gc jint)
+  (enable-verbose-gc jint)
+  (disable-async-gc jint)
+  (reserved-0 jint)
+  (reserved-1 jint)
+  (reserved-2 jint))
+  =

+(fli:define-foreign-function (jni-get-default-java-vm-init-args "JNI_GetDe=
faultJavaVMInitArgs")
+    ((init-args (:ptr jdk-1-1-init-args)))
+  :result-type jint)
+
+(fli:define-c-struct java-vm-init-args
+  (version jint)
+  (n-options jint)
+  (options (:ptr java-vm-option))
+  (ignore-unrecognized jboolean))
+
+(fli:define-foreign-function (jni-create-java-vm "JNI_CreateJavaVM" :sourc=
e)
+    ((pvm (:reference-return pvm))
+     (penv (:reference-return penv))
+     (vm-args (:ptr java-vm-init-args)))
+  :result-type jint
+  :lambda-list (vm-args &optional (pvm t) (penv t))
+;  :module :jni-lib ;refused on Mac OSX, even though register-module is su=
pported
+  )
+
+(fli:define-foreign-function (jni-get-created-java-vms "JNI_GetCreatedJava=
VMs" :source)
+    ((vm-buf (:c-array pvm))
+     (buf-len jsize)
+     (n-vms (:reference-return jsize)))
+  :result-type jint)
+
+(defun cleanup-jni-gref (gref)
+  "set as a special free action to free java classes when no longer used b=
y Lisp"
+  (when (java-ref-p gref)
+    (delete-global-ref gref)))
+
+(defun create-jvm (&rest option-strings)
+  "Creates the JVM, this can only be done once.
+The option strings can be used to control the JVM, esp. the classpath:
+\"-Djava.class.path=3D/Users/rich/Lisp/jfli.jar\""
+  (when *pvm*
+    (error "JVM already created, can only be started once"))
+  (load-jni-lib)
+  (let ((nopts (length option-strings))
+         (option-array nil))
+    (fli:with-dynamic-foreign-objects ((ia java-vm-init-args))
+      (when option-strings
+        (setf option-array (fli:allocate-dynamic-foreign-object :type 'jav=
a-vm-option :nelems nopts))
+        (dotimes (n nopts)
+          (setf (fli:foreign-slot-value (fli:dereference option-array
+                                                         :index n
+                                                         :copy-foreign-obj=
ect nil) 'option-string)
+                (fli:convert-to-dynamic-foreign-string (nth n option-strin=
gs)))))
+      (fli:with-foreign-slots (VERSION N-OPTIONS OPTIONS IGNORE-UNRECOGNIZ=
ED) ia
+        (setf version JNI-VERSION-1-4
+              n-options nopts
+              OPTIONS option-array
+              IGNORE-UNRECOGNIZED nil)
+        (multiple-value-bind (ret vm env)
+            (jni-create-java-vm ia)
+          (setf *pvm* vm)
+          (add-special-free-action #'cleanup-jni-gref)
+          (values ret vm env))))))
+
+;this is the FLI side of proxy support
+
+(defvar *invocation-handler* nil
+  "this will be set by jfli:enable-java-proxies to a function of 3 args")
+
+;this will be set as the implementation of a native java function
+(fli:define-foreign-callable ("LispInvocationHandler_invoke" :result-type =
jobject)
+    ((env penv) (obj jobject) (proxy jobject) (method jobject) (args jobje=
ct))
+  (do-invoke env obj proxy method args))
+
+(defun do-invoke (env obj proxy method args)
+  ;(declare (ignore env))
+  (when *invocation-handler*
+    (let ((*penv* env))
+      (prog1
+          (funcall *invocation-handler* proxy method args)
+        ;(jfli::invocation-handler proxy method args)
+        (delete-local-ref obj)))))
+
+(defun register-invocation-handler (invocation-handler)
+  "sets up the Lisp handler and binds the native function - jfli.jar must =
be in the classpath"
+  (setf *invocation-handler* invocation-handler)
+  (fli:with-dynamic-foreign-objects ((method jni-native-method))
+    (let ((lih (try-null (jni-find-class "com/richhickey/jfli/LispInvocati=
onHandler"))))
+      (fli:with-foreign-slots (name signature fn-ptr) method
+        (setf name (fli:convert-to-dynamic-foreign-string "invoke")
+              signature (fli:convert-to-dynamic-foreign-string "(Ljava/lan=
g/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;")
+              fn-ptr (fli:make-pointer :symbol-name "LispInvocationHandler=
_invoke")))
+      (register-natives lih method 1))))
+
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;the code below provides for the generation of wrapper functions that use =
JNI to access
+;methods and fields. This low-level interface is unsafe, in that JNI will =
not =

+;check arg types etc on calls, and therefore should only be used to build =
safer high-level interfaces
+;i.e. use jfli!
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;found on c.l.l
+(eval-when (:compile-toplevel :load-toplevel)
+(defun replace-substrings (string substring replacement)
+  (declare (optimize (speed 3))
+           (type simple-string string substring replacement))
+  (assert (> (length substring) 0) (substring)
+    "Substring ~A must be of length ~D > 0"
+    substring (length substring))
+  (with-output-to-string (stream)
+    (loop with substring-length =3D (length substring)
+          for index =3D 0 then (+ match-index substring-length)
+          for match-index =3D (search substring string :start2 index)
+          do
+          (write-string string stream :start index :end match-index)
+          (when match-index
+            (write-string replacement stream))
+          while match-index)))
+
+
+(defun local-ref-to-global-ref (lref)
+  (when lref
+    (let ((gref (new-global-ref lref)))
+      (flag-special-free-action gref)
+      (delete-local-ref lref)
+      gref)))
+
+(defun local-ref-to-string (lref)
+  (prog1
+      (convert-from-java-string lref)
+    (delete-local-ref lref)))
+
+(defun convert-to-java-string (s)
+  (when s
+    (try-null (new-string-utf (string s)))))
+
+(defun convert-from-java-string (s)
+  (when s
+    (let ((chars (try-null (get-string-utf-chars s))))
+      (prog1
+          (fli:convert-from-foreign-string chars :external-format :utf-8)
+        (release-string-utf-chars s chars)))))
+
+(defun jaref (array index)
+  (try (get-object-array-element array index)))
+
+(defun (setf jaref) (val array index)
+  (try (set-object-array-element array index val)))
+
+(defun convert-string-arg (s)
+  "if s is stringp, make into java string, else presume it is a java strin=
g and return it"
+  ;presumably faster than checking if s is a foreign pointer?
+  (if (or (stringp s) (symbolp s))
+      (convert-to-java-string s)
+    s))
+
+(defun process-arg (val type)
+  (if (string-equal "java.lang.String" type)
+                 `(convert-string-arg ,val)
+                 val))
+
+(defmacro set-arg (args i val type)
+  `(setf (fli:foreign-slot-value (fli:dereference (fli:foreign-array-point=
er ,args ,i)
+                                                     :copy-foreign-object =
nil)
+                                    ',(slot-from-typename type))
+            ,(process-arg val type)))
+
+(defmacro with-arg-array (arg-array-name args &body body)
+  (let ((i -1))
+  `(fli:with-dynamic-foreign-objects ()
+     (let ((,arg-array-name
+            (fli:allocate-dynamic-foreign-object :type
+                                                 '(:c-array jvalue ,(lengt=
h args)))))
+       ,@(mapcar #'(lambda (arg)
+                     (list 'set-arg arg-array-name (incf i) (first arg) (s=
econd arg))) =

+                 args)
+
+       , at body))))
+
+(defun build-descriptor (params return-type)
+  (string-append
+   "("
+   (apply #'string-append (mapcar #'(lambda (p)
+                                      (type-descriptor-from-typename (seco=
nd p)))
+                                  params))
+   ")"
+   (type-descriptor-from-typename return-type)))
+
+(defun get-class-and-method-id (class-name method-name descriptor is-stati=
c)
+  (let ((class (local-ref-to-global-ref
+                (try-null (jni-find-class class-name)))))
+    (values class
+            (if is-static
+                (try-null (get-static-method-id class method-name descript=
or))
+              (try-null (get-method-id class method-name descriptor))))))
+
+
+(defun get-class-and-field-id (class-name field-name descriptor is-static)
+  (let ((class (local-ref-to-global-ref
+                (try-null (jni-find-class class-name)))))
+    (values class
+            (if is-static
+                (try-null (get-static-field-id class field-name descriptor=
))
+              (try-null (get-field-id class field-name descriptor))))))
+
+(defun is-name-of-primitive (s)
+  (member s '("boolean" "byte" "char" "short" "int" "long" "float" "double=
" "void")
+          :test #'string-equal))
+
+(defun package-qualified-name (classname packagename)
+  (cond
+   ((is-name-of-primitive (subseq classname 0 (position #\< classname))) c=
lassname)
+   ((find #\. classname) classname)     ;already qualified, presumably by =
another package
+   (t (string-append packagename "." classname)))) =

+
+(defun split-package-and-class (name)
+    (let ((p (position #\. name :from-end t)))
+      (unless p (error "must supply package-qualified classname"))
+      (values (subseq name 0 p)
+              (subseq name (1+ p)))))
+
+(defun slot-from-typename (tn)
+  (let ((prim (assoc tn
+                     '(("boolean" . :z)
+                       ("byte" . :b)
+                       ("char" . :c)
+                       ("short" . :s)
+                       ("int" . :i)
+                       ("long" . :j)
+                       ("float" . :f)
+                       ("double" . :d))
+                     :test #'string-equal)))
+    (if prim
+        (rest prim)
+      :l)))
+
+(defun name-component-from-typename (tn)
+  (if (is-name-of-primitive tn)
+      tn
+    "object"))
+
+(defun type-descriptor-from-typename (tn)
+  (let ((prim (assoc tn
+                     '(("boolean" . "Z")
+                       ("byte" . "B")
+                       ("char" . "C")
+                       ("short" . "S")
+                       ("int" . "I")
+                       ("long" . "J")
+                       ("float" . "F")
+                       ("double" . "D")
+                       ("void" . "V"))
+                     :test #'string-equal)))
+    (if prim
+        (rest prim)
+      (let ((array-depth (count #\< tn))
+            (tn-with-slashes (replace-substrings tn "." "/")))
+        (if (=3D 0 array-depth)
+            (string-append "L" tn-with-slashes ";")
+          (with-output-to-string (s)
+            (dotimes (x array-depth)
+              (write-string "[" s))
+            (write-string (type-descriptor-from-typename
+                           (subseq tn-with-slashes 0 (position #\< tn-with=
-slashes))) s)))))))
+
+;not an exact reciprocal of type-descriptor-from-typename since reflection=
 uses . not / as separator
+(defun typename-from-reflection-type-descriptor (tn)
+  (let ((prim (assoc tn
+                     '(("Z" . "boolean")
+                       ("B" . "byte")
+                       ("C" . "char")
+                       ("S" . "short")
+                       ("I" . "int")
+                       ("J" . "long")
+                       ("F" . "float")
+                       ("D" . "double")
+                       ("V" . "void"))
+                     :test #'string-equal)))
+    (if prim
+        (rest prim)
+      (let ((array-depth (count #\[ tn)))
+        (if (=3D 0 array-depth)
+            (subseq tn 1 (1- (length tn))) ;strip leading L and trailing ;
+          (with-output-to-string (s)
+            (write-string (typename-from-reflection-type-descriptor (subse=
q tn array-depth)) s)
+            (dotimes (x array-depth)
+              (write-string "<>" s))))))))
+
+(defun method-name-from-typename (tn static)
+    (find-symbol (string-upcase (string-append "call-"
+                                               (if static "static-" "")
+                                             (name-component-from-typename=
 tn)
+                                             "-method-a")) :jni))
+
+(defun field-get-name-from-typename (tn static)
+    (find-symbol (string-upcase (string-append "get-"
+                                               (if static "static-" "")
+                                             (name-component-from-typename=
 tn)
+                                             "-field")) :jni))
+
+(defun field-set-name-from-typename (tn static)
+    (find-symbol (string-upcase (string-append "set-"
+                                               (if static "static-" "")
+                                             (name-component-from-typename=
 tn)
+                                             "-field")) :jni))
+(defun process-return (return-type f &key raw-return)
+  (cond
+   ((or raw-return (is-name-of-primitive return-type)) f)
+   ((string-equal "java.lang.String" return-type) `(local-ref-to-string ,f=
))
+   (t `(local-ref-to-global-ref ,f))))
+
+;JNI wrapper generators - will create functions in current package
+;this needs more docs
+(defmacro define-java-function (fname class-name return-type method-name p=
arams &key static raw-return)
+  (let ((this (gensym))
+        (class (gensym))
+        (id (gensym))
+        (args (gensym)))
+    `(let (,class ,id)
+       (defun ,fname ,(if static (mapcar #'first params)
+                        (cons this (mapcar #'first params)))
+         (when (null ,class)
+           (multiple-value-setq (,class ,id)
+               (get-class-and-method-id ,(replace-substrings class-name ".=
" "/")
+                                        ,method-name ,(build-descriptor pa=
rams return-type) ,static)))
+         (with-arg-array ,args ,(mapcar #'(lambda (param)
+                                           (list (first param) (second par=
am)))
+                                       params)
+           ,(process-return return-type
+                            `(try (,(method-name-from-typename return-type=
 static)
+                                   ,(if static class this) ,id ,args))
+                            :raw-return raw-return))))))
+
+(defmacro define-java-field (getname class-name field-type field-name &key=
 static)
+  (let ((this (gensym))
+        (class (gensym))
+        (id (gensym))
+        (val (gensym)))
+    `(let (,class ,id)
+       (flet ((load-ids ()
+                (when (null ,class)
+                  (multiple-value-setq (,class ,id)
+                      (get-class-and-field-id ,(replace-substrings class-n=
ame "." "/")
+                                              ,field-name ,(type-descripto=
r-from-typename field-type)
+                                              ,static)))))
+         (defun ,getname ,(if static () (list this))
+           (load-ids)
+           ,(process-return field-type
+                            `(try (,(field-get-name-from-typename field-ty=
pe static)
+                                   ,(if static class this) ,id))))
+         (defun (setf ,getname) ,(if static (list val) (list this val))
+           (load-ids)
+           (try (,(field-set-name-from-typename field-type static)
+                 ,(if static class this) ,id ,(process-arg val field-type)=
))
+           ,val)))))
+
+(defmacro define-java-constructor (fname class-name params)
+  (let ((class (gensym))
+        (id (gensym))
+        (args (gensym)))
+    `(let (,class ,id)
+       (defun ,fname ,(mapcar #'first params)
+         (when (null ,class)
+           (multiple-value-setq (,class ,id)
+               (get-class-and-method-id ,(replace-substrings class-name ".=
" "/")
+                                        "<init>" ,(build-descriptor params=
 "void") nil)))
+         (with-arg-array ,args ,(mapcar #'(lambda (param)
+                                           (list (first param) (second par=
am)))
+                                       params)
+           (local-ref-to-global-ref (try-null (new-object-a ,class ,id ,ar=
gs))))))))
+
+(defun make-func-name (class method params append-param-types)
+  ;probably a format one-liner that can do this
+    (let ((base (string-append class "." method)))
+      (if append-param-types
+          (string-append base
+                         (let ((param-types (mapcar #'second params)))
+                           (if param-types
+                               (string-append "<"
+                                              (reduce #'(lambda (x y)
+                                                          (string-append x=
 "-" y)) param-types)
+                                              ">")
+                             "<>")))
+        base)))
+
+;these just do some name twiddling before calling define-java-xxx above
+(defmacro def-jni-function (package-and-class method params return-typename
+                                               &key static overloaded raw-=
return)
+  (multiple-value-bind (package class) (split-package-and-class package-an=
d-class)
+    (let* ((fname (make-func-name class method params overloaded))
+           (fsym (read-from-string fname)))
+      `(locally ,(list 'define-java-function
+                     fsym
+                     package-and-class
+                     (package-qualified-name return-typename package)
+                     method
+                     (mapcar #'(lambda (p)
+                                 (list (first p) (package-qualified-name (=
second p) package)))
+                             params)
+                     :static static :raw-return raw-return)))))
+
+(defmacro def-jni-functions (package-and-class &rest decls)
+  `(locally ,@(mapcar #'(lambda (decl)
+                          (list* 'def-jni-function package-and-class decl))
+                      decls)))
+
+(defmacro def-jni-constructor (package-and-class params &key overloaded)
+  (multiple-value-bind (package class) (split-package-and-class package-an=
d-class)
+    (let* ((fname (make-func-name class "new" params overloaded))
+           (fsym (read-from-string fname)))
+      `(locally ,(list 'define-java-constructor
+                     fsym =

+                     package-and-class =

+                     (mapcar #'(lambda (p)
+                                 (list (first p) (package-qualified-name (=
second p) package)))
+                             params))))))
+
+(defmacro def-jni-field (package-and-class field typename &key static)
+  (multiple-value-bind (package class) (split-package-and-class package-an=
d-class)
+    (let ((getsym (read-from-string (string-append class "." field
+                                                   (if static "-accessor" =
""))))
+          (macsym (read-from-string (string-append class "." field))))
+      `(locally =

+         ,(list 'define-java-field getsym package-and-class
+                (package-qualified-name typename package) field :static st=
atic)
+         ,(when static
+            `(define-symbol-macro ,macsym (,getsym)))))))
+
+;we're going to use a little Java to do exception handling below
+(def-jni-function "java.lang.Object"
+                   "toString" () "String")
+
+(def-jni-function "java.lang.reflect.InvocationTargetException"
+                  "getTargetException" () "java.lang.Throwable")
+
+(def-jni-functions "java.lang.Throwable"
+                   ("getMessage" () "String")
+                   ("getStackTrace" () "StackTraceElement<>"))
+
+(defmacro do-jarray ((x array) &body body)
+  (let ((gcount (gensym))
+        (gi (gensym))
+        (garray (gensym)))
+    `(let* ((,garray ,array)
+            (,gcount (get-array-length ,garray)))
+       (dotimes (,gi ,gcount)
+         (let ((,x (jaref ,garray ,gi)))
+           , at body)))))
+
+#|
+It is critical that if you call a JNI function that might throw an excepti=
on that you clear it,
+otherwise the next Java call you make will cause a crash
+|#
+(defun handle-exception ()
+  (let ((e (exception-occurred)))
+    (when (not (fli:null-pointer-p e)) ;allow for safe calling in non-exce=
ptional state
+      (exception-clear)
+      ;if the exception occurs in the reflection target, we really want th=
at
+      (when (is-instance-of e (jni-find-class "java/lang/reflect/Invocatio=
nTargetException"))
+        (setf e (invocationtargetexception.gettargetexception e)))
+      (error "~A" (with-output-to-string (s)
+                    (format s "~A~%" (object.tostring e))
+                    (do-jarray (x (throwable.getstacktrace e))
+                      (format s "~A~%" (object.tostring x))))))))
+
+(defun try (result)
+  (if (exception-check)
+      (handle-exception)
+    result))
+
+;JNI will sometimes indicate theere is an exception via a return value
+;so take advantage of that when possible vs. the call back to exception-ch=
eck
+(defun try-null (result)
+  (if (fli:null-pointer-p result)
+      (handle-exception)
+    result))
+
+(defun try-neg (result)
+  (if (minusp result)
+      (handle-exception)
+    result))
+
+
+)
+



More information about the Openmcl-cvs-notifications mailing list