panama tools and demos
authorNot Zed <notzed@gmail.com>
Mon, 6 Jan 2020 05:20:35 +0000 (15:50 +1030)
committerNot Zed <notzed@gmail.com>
Mon, 6 Jan 2020 05:20:35 +0000 (15:50 +1030)
COPYING [new file with mode: 0644]
README [new file with mode: 0644]
src/Makefile [new file with mode: 0644]
src/export.cc [new file with mode: 0644]
src/generate [new file with mode: 0755]
src/list.h [new file with mode: 0644]
src/tree-codes.h [new file with mode: 0644]
test-api/Makefile [new file with mode: 0644]
test-api/api.c [new file with mode: 0644]
test-api/api.h [new file with mode: 0644]
test-api/api/test/TestAPI.java [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..94a9ed0
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/README b/README
new file mode 100644 (file)
index 0000000..1d38718
--- /dev/null
+++ b/README
@@ -0,0 +1,99 @@
+
+Introduction
+------------
+
+This is an alternative to jextract for the OpenJDK panama project
+which in turn is a project to implement Java to Native interfaces at
+runtime.
+
+It uses a gcc plugin for type extraction and a "simple" perl script to
+transform this into Java source files with the appropriate annotations
+for panama to do it's thing.
+
+Compile
+-------
+
+Edit the makefile to point to your gcc directory and make.
+
+$ cd src
+$ vi Makefile
+$ make
+
+generate is a perl script.
+
+See test-api/* for a simple example.  JAVA_HOME must point to
+a compatible panama-enabled jdk.
+
+Use dump.so
+-----------
+
+dump.so is a gcc plugin which captures structures, unions, enums,
+exported functions, and a few typedefs into a perl source file
+containing a single hash.
+
+Put all the include files required for your api in api.h.
+
+$ gcc -fplugin=src/export.so -fplugin-arg-export-output=api.pm api.h -o /dev/null
+
+api.pm is a text file of perl source-code which contains a descriptor
+of all supported types found.
+
+Use generate
+------------
+
+Quick and dirty:
+
+$ generate -d dir -t name.space -c APILib -lapi ./api.pm
+
+Generate all structures in api.pm (the ./ is required) and place all
+functions into dir/name/space/APILib.java which will link with
+libapi.so.
+
+The generator is quite flexible, sub-sets of functions can be placed
+in alternative *Lib.java files, types can be filtered.  If a function
+or type is specified explicitly, then only those types it requires are
+included in the generated output.
+
+See the top of the file for details, it's probably up to date.
+
+Status
+------
+
+This is currently just a 'quick and dirty' weekend hack.  The tools
+spew a lot of debug.  dump.cc is basic c-compatible-c++, because I
+don't know c++.
+
+dump.so works by hooking into the gcc pre-compiled headers function,
+it might work with a c source file as well.  It's only been used with
+gcc 9.
+
+The dumper will hard-fail if it sees defintions it doesn't understand,
+and currently it's only been tested with a small nubmer of libraries.
+
+enumerations are currently translated to integres.  The generator
+doesn't currently export any to Java.
+
+varags is not implemented.
+
+All function pointers are translated to a unique set of functional
+interfaces whose names are based on the function signature.  So they
+should remain stable.
+
+Anonymous type names are based on the structure member they belong to,
+but they may not be unique.
+
+It will only ever support c.
+
+Only tested against the panama 'openjdk-14-panama+1-15_linux'.
+
+License
+-------
+
+GNU General Public License, version 3 or later, but see individual
+file headers for specifics.
+
+Links
+-----
+
+ * https://www.zedzone/software/panamaz.html - project page.
+ * https://openjdk.java.net/projects/panama - openjdk panama page.
diff --git a/src/Makefile b/src/Makefile
new file mode 100644 (file)
index 0000000..7b8fdb6
--- /dev/null
@@ -0,0 +1,14 @@
+
+CXXFLAGS=-fPIC -Wall -Wno-switch -g
+CPPFLAGS=-I. -I/usr/lib64/gcc/x86_64-slackware-linux/9.2.0/plugin/include
+
+all: export.so
+
+export.o: export.cc list.h tree-codes.h
+export.so: export.o
+       $(CC) $(INCLUDE) -shared -fPIC -o $@ $^
+
+clean:
+       rm -f export.o export.so
+
+.PHONY: clean
diff --git a/src/export.cc b/src/export.cc
new file mode 100644 (file)
index 0000000..ed19501
--- /dev/null
@@ -0,0 +1,732 @@
+/*
+ * export c types and prototypes to perl file.
+ *
+ * Copyright (c) 2019 Yonatan Goldschmidt
+ * Copyright (c) 2020 Michael Zucchi
+ *
+ * The MIT License (MIT)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+/*
+   skeleton taken from structsizes.cc plugin by Richard W.M. Jones
+   https://rwmj.wordpress.com/2016/02/24/playing-with-gcc-plugins/
+   some other bits from
+   https://blog.adacore.com/bindings-gcc-plugins
+ */
+
+/*
+
+function declarations, i think
+
+                  chain            param type                 name              size
+TYPE_DECL         TYPE_ARG_TYPES   TYPE_VALUE(item):TREE_LIST  <empty>           TYPE_SIZE(item_type)
+ /POINTER
+ /FUNCTION_TYPE
+FUNCTION_DECL     DECL_ARGUENTS    TREE_TYPE(item):PARM_DECL   DECL_NAME(item)   DECL_SIZE(item)
+                                                                                 TYPE_SIZE(item_type)
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gcc-plugin.h>
+#include <tree.h>
+#include <print-tree.h>
+
+#include "tree-codes.h"
+#include "list.h"
+
+#define D(x) do { x; } while(0)
+
+//Enums have a type, otherwise they're just integers
+//#define TYPED_ENUMS
+
+// remove some of the debug
+//#define NDEBUG
+
+int plugin_is_GPL_compatible; // must be defined for the plugin to run
+
+static FILE *output_file;
+
+static void debug_tree_helper(tree t, const char *msg) {
+#ifndef NDEBUG
+       fflush(stdout);
+       fprintf(stderr, "dumping tree: '%s'\n", msg);
+       debug_tree(t);
+       fprintf(stderr, "\n\n");
+       fflush(stdout);
+#endif
+}
+
+static struct hash dumped;
+static struct list todump;
+static struct list context_stack;
+
+/*
+  Join all names in the stack, in reverse order.
+ */
+static char *stack_path(struct list *stack, const char *sep) {
+       size_t total = 1;
+
+       for (struct node *n = stack->tail; n; n=n->link)
+               total += strlen(n->name)+strlen(sep);
+
+       char *data = (char *)xmalloc(total);
+       char *p = data;
+
+       // FIXME: some other context name
+       for (struct node *n = stack->tail; n; n=n->link) {
+               p = stpcpy(p, n->name);
+               if (n->link)
+                       p = stpcpy(p, sep);
+       }
+
+       return data;
+}
+
+// returns 0 if type has no size (i.e VOID_TYPE)
+static bool is_struct_or_union(const_tree type) {
+    return RECORD_TYPE == TREE_CODE(type) || UNION_TYPE == TREE_CODE(type);
+}
+
+static void print_spaces(int n) {
+    for (int i = 0; i < n; ++i)
+        fputc('\t', output_file);
+}
+
+static int is_ref_type(tree type) {
+       switch (TREE_CODE(type)) {
+       case VECTOR_TYPE:
+       case ARRAY_TYPE:
+       case POINTER_TYPE:
+       case REFERENCE_TYPE:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static tree target_type(tree type) {
+       while (is_ref_type(type))
+               type = TREE_TYPE(type);
+       return type;
+}
+
+static size_t value_size(tree n) {
+       return n ? tree_to_uhwi(n) : 0;
+}
+
+static const char *value_name(tree type) {
+       tree test = TYPE_IDENTIFIER(type);
+       const char *value = test ? IDENTIFIER_POINTER(test) : NULL;
+
+       // __va_list_tag is the final type beneath __builtin_va_list.
+       // it behaves different from other types - it has a TYPE_MAIN_VARIANT, but the main TYPE_NAME seems to give
+       // an unexpected tree, and therefore ORIG_TYPE_NAME returns a garbage value.
+       // I think this patch is good enough.
+       if (value && 0 == strcmp("__va_list_tag", value))
+               return "__va_list_tag";
+
+       test = TYPE_MAIN_VARIANT(type);
+       test = test ? TYPE_NAME(test) : test;
+       test = test && TREE_CODE(test) == TYPE_DECL ? DECL_NAME(test) : test;
+
+       return test ? IDENTIFIER_POINTER(test) : value;
+}
+
+/*
+  Find a non-ref type in the type chain, i.e. skip pointers/arrays.
+ */
+static tree simple_type(tree t) {
+       while (is_ref_type(t))
+               t = TREE_TYPE(t);
+       return t;
+}
+
+/*
+  Create a 'panama' signature for a single type.
+ */
+static void export_desc(tree field, tree field_type, struct buffer *b) {
+       const size_t data_size = value_size(TYPE_SIZE(field_type));
+
+       switch (TREE_CODE(field_type)) {
+       case VOID_TYPE:
+               buffer_add(b, "v");
+               break;
+       case VECTOR_TYPE:
+       case ARRAY_TYPE: {
+               const size_t elem_size = tree_to_uhwi(TYPE_SIZE_UNIT(TREE_TYPE(field_type)));
+               size_t num_elem;
+
+               if (elem_size == 0 || NULL == TYPE_SIZE_UNIT(field_type)) {
+                       // it is a flexible array or empty types
+                       num_elem = 0;
+               } else {
+                       // it might be 0 / elem_size, in which case we also end up with num_elem = 0.
+                       num_elem = tree_to_uhwi(TYPE_SIZE_UNIT(field_type)) / elem_size;
+               }
+
+               buffer_room(b, 16);
+               b->pos += sprintf(b->data + b->pos, "[%zu", num_elem);
+               export_desc(field, TREE_TYPE(field_type), b);
+               buffer_add(b, "]");
+               break;
+       }
+       case POINTER_TYPE:
+       case REFERENCE_TYPE:
+               buffer_room(b, 16);
+               b->pos += sprintf(b->data + b->pos, "u%zu:", data_size);
+               export_desc(field, TREE_TYPE(field_type), b);
+               break;
+       case FUNCTION_TYPE: {
+               // TODO: handle void f() type -> null TYPE_ARG_TYPES()
+               tree return_type = TREE_TYPE(field_type);
+
+               buffer_add(b, "(");
+               for (tree param = TYPE_ARG_TYPES(field_type); param != NULL; param = TREE_CHAIN(param)) {
+                       tree param_type = TREE_VALUE(param);
+
+                       if (TREE_CODE(param_type) == VOID_TYPE)
+                               break;
+
+                       export_desc(param, param_type, b);
+               }
+               buffer_add(b, ")");
+               export_desc(field, return_type, b);
+               break;
+       }
+       case ENUMERAL_TYPE:
+#if defined(TYPED_ENUMS)
+               buffer_add(b, "${");
+               buffer_add(b, TYPE_IDENTIFIER(field_type) ? value_name(field_type) : "enum");
+               buffer_add(b, "}");
+#else
+               buffer_room(b, 16);
+               b->pos += sprintf(b->data + b->pos, "%c%zu",
+                                 TYPE_UNSIGNED(field_type) ? 'u' : 'i',
+                                 data_size);
+#endif
+               break;
+       case RECORD_TYPE:
+       case UNION_TYPE:
+               if (TYPE_IDENTIFIER(field_type)) {
+                       list_add(&todump, node_alloc(field_type, NULL));
+
+                       buffer_add(b, "${");
+                       buffer_add(b, value_name(field_type));
+                       buffer_add(b, "}");
+               } else {
+                       char *name = stack_path(&context_stack, "_");
+
+                       list_add(&todump, node_alloc(field_type, name));
+
+                       buffer_add(b, "${");
+                       buffer_add(b, name);
+                       buffer_add(b, "}");
+
+                       free(name);
+               }
+               break;
+       case REAL_TYPE:
+               buffer_room(b, 16);
+               b->pos += sprintf(b->data + b->pos, "f%zu", data_size);
+               break;
+       case INTEGER_TYPE:
+               buffer_room(b, 16);
+               b->pos += sprintf(b->data + b->pos, "%c%zu",
+                                 TYPE_UNSIGNED(field_type) ? 'u' : 'i',
+                                 data_size);
+               break;
+       default:
+               debug_tree_helper(field_type, "unknown type!");
+               gcc_unreachable();
+               break;
+       }
+}
+
+/*
+  Print a single parameter or field.
+ */
+static void export_param(tree field, tree field_type, size_t field_size) {
+       switch (TREE_CODE(field_type)) {
+       case VECTOR_TYPE:
+       case ARRAY_TYPE:
+       case POINTER_TYPE:
+       case REFERENCE_TYPE: {
+               struct buffer b;
+
+               buffer_init(&b, 256);
+               export_desc(field, field_type, &b);
+               fprintf(output_file, " deref => '%s',", b.data);
+               free(b.data);
+
+               field_type = simple_type(field_type);
+               field_size = value_size(TYPE_SIZE(field_type));
+
+               export_param(field, field_type, field_size);
+               break;
+       }
+       case VOID_TYPE:
+               fprintf(output_file, " type => 'void',");
+               break;
+       case ENUMERAL_TYPE: {
+#if defined(TYPED_ENUMS)
+               const char *names = TYPE_IDENTIFIER(field_type) ? value_name(field_type) : "enum";
+               fprintf(output_file, " type => 'enum:%s',", names);
+#else
+               fprintf(output_file, " type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', field_size);
+#endif
+               break;
+       }
+       case FUNCTION_TYPE: {
+               // If the function is a typedef we could use the name
+               // but it's a pain to find up the tree, so don't bother.
+               // Use the signature instead.  This is what jextract appears to use.
+               struct buffer b;
+
+               //char *name = stack_path(&context_stack);
+               //fprintf(output_file, " type => 'call:%s', ", name);
+               //free(name);
+
+               buffer_init(&b, 256);
+
+               export_desc(field, field_type, &b);
+               list_add(&todump, node_alloc(field_type, b.data));
+               fprintf(output_file, " type => 'call:%s', ", b.data);
+
+               free(b.data);
+
+               break;
+       }
+       case REAL_TYPE:
+               fprintf(output_file, " ctype => '%s',", value_name(field_type));
+               fprintf(output_file, " type => 'f%zu',", field_size);
+               break;
+       case INTEGER_TYPE:
+               if (TREE_CODE(field) == FIELD_DECL && DECL_BIT_FIELD(field)) {
+                       fprintf(output_file, " ctype => 'bitfield',");
+                       fprintf(output_file, " type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', value_size(DECL_SIZE(field)));
+               } else {
+                       fprintf(output_file, " ctype => '%s',", value_name(field_type));
+                       fprintf(output_file, " type => '%c%zu',", TYPE_UNSIGNED(field_type) ? 'u' : 'i', field_size);
+               }
+               break;
+       case RECORD_TYPE:
+       case UNION_TYPE: {
+               const char *us = TREE_CODE(field_type) == RECORD_TYPE ? "struct" : "union";
+
+               if (TYPE_IDENTIFIER(field_type)) {
+                       fprintf(output_file, " type => '%s:%s',", us, value_name(field_type));
+               } else {
+                       char *name = stack_path(&context_stack, "_");
+
+                       list_add(&todump, node_alloc(field_type, name));
+                       fprintf(output_file, " type => '%s:%s',", us, name);
+                       free(name);
+               }
+               break;
+       }
+       default:
+               printf("unknown param type: %s\n", ZTREE_CODE(field_type));
+               gcc_unreachable();
+       }
+}
+
+/*
+  Export a chain of parameters
+*/
+static void export_params(tree first_param, size_t indent_level) {
+       int id = 0;
+       char nameb[16];
+       const char *names = nameb;
+
+       for (tree param = first_param; param; param = TREE_CHAIN(param)) {
+               tree param_type = TREE_VALUE(param);
+               const size_t data_size = value_size(TYPE_SIZE(param_type));
+
+               // non-varags functions end in VOID_TYPE
+               if (!TREE_CHAIN(param) && TREE_CODE(param_type) == VOID_TYPE)
+                       break;
+
+               fprintf(output_file, "\t\t{");
+
+               // size: do we need it?
+               fprintf(output_file, " size => %zu,", data_size);
+
+               // name: none available, use position
+               sprintf(nameb, "arg_%d", id);
+
+               fprintf(output_file, " name => '%s',", names);
+               stack_push(&context_stack, node_alloc(param, names));
+
+               // value: details
+               export_param(param, param_type, data_size);
+
+               free(stack_pull(&context_stack));
+
+               fprintf(output_file, "},\n");
+               id++;
+       }
+}
+
+/*
+  Export a chain of fields.
+*/
+static void export_fields(tree first_field, size_t base_offset, int indent) {
+       for (tree field = first_field; field; field = TREE_CHAIN(field)) {
+               gcc_assert(TREE_CODE(field) == FIELD_DECL);
+
+               tree field_type = TREE_TYPE(field);
+               const size_t field_size = value_size(DECL_SIZE(field));
+               size_t offset = base_offset + tree_to_uhwi(DECL_FIELD_OFFSET(field)) * 8 + tree_to_uhwi(DECL_FIELD_BIT_OFFSET(field));
+
+               // name: if none, then inline
+               if (!DECL_NAME(field)) {
+                       if (is_struct_or_union(field_type))
+                               export_fields(TYPE_FIELDS(field_type), offset, indent);
+               } else {
+                       const char *names = IDENTIFIER_POINTER(DECL_NAME(field));
+
+                       printf("   name=%s\n", names);
+                       print_spaces(indent+1);
+                       fprintf(output_file, "{ name => '%s', size => %zu, offset => %zu,", names, field_size, offset);
+                       stack_push(&context_stack, node_alloc(field, names));
+
+                       // value: details
+                       export_param(field, field_type, field_size);
+
+                       free(stack_pull(&context_stack));
+
+                       fprintf(output_file, "},\n");
+               }
+       }
+}
+
+/*
+  Main entry point for exporting any type.
+*/
+static void export_type(tree type, const char *names) {
+       tree name;
+       tree deftype;
+       tree target;
+
+       D(printf("export: %s %s\n", names, ZTREE_CODE(type)));
+
+       switch (TREE_CODE(type)) {
+       case TYPE_DECL: {
+               if (!names) {
+                       name = DECL_NAME(type);
+                       if (!name)
+                               return;
+                       names = IDENTIFIER_POINTER(name);
+               }
+               if (hash_lookup(&dumped, names))
+                       return;
+               hash_put(&dumped, node_alloc(type, names));
+
+               deftype = TREE_TYPE(type);
+               target = target_type(deftype);
+               switch (TREE_CODE(target)) {
+               case FUNCTION_TYPE: {
+                       // I don't know if i even want this
+
+                       fprintf(output_file, "'call:%s' => { name => '%s',", names, names);
+
+                       // the deftype is always a pointer for a function_type
+
+                       struct buffer b;
+
+                       buffer_init(&b, 256);
+                       export_desc(type, deftype, &b);
+                       fprintf(output_file, " deref => '%s',", b.data);
+                       free(b.data);
+
+                       // TODO: cleanup
+                       {
+                               tree result_type = TREE_TYPE(target);
+                               const size_t data_size = value_size(TYPE_SIZE(result_type));
+
+                               fprintf(output_file, "\n\tresult => {");
+                               export_param(target, result_type, data_size);
+                               fprintf(output_file, " },");
+                       }
+
+                       fprintf(output_file, "\n\targuments => [\n");
+                       export_params(TYPE_ARG_TYPES(target), 0);
+                       fprintf(output_file, "]},\n");
+                       break;
+               }
+               case RECORD_TYPE:
+                       printf(" typedef record: %s\n", names);
+                       break;
+               case UNION_TYPE:
+                       printf(" typedef union: %s\n", names);
+                       break;
+               case ENUMERAL_TYPE: {
+                       // TODO: typedef of anonymous enumerations may be repeated
+                       // TODO: this could be detected in the frontend - e.g. don't include any
+                       // TODO: anon enum values if any are already there
+                       // TODO: or maybe duplicates could be detected here
+                       size_t size = tree_to_uhwi(TYPE_SIZE(target));
+
+                       if (size > 64) {
+                               fprintf(stderr, "Warning: enum '%s' requires too many bits (%zu)\n", names, size);
+                               return;
+                       }
+
+                       fprintf(output_file, "'enum:%s' => { name => '%s', type => 'enum', size => %zu, value_type => '%c%zu', values => [\n",
+                               names, names, size, TYPE_UNSIGNED(target) ? 'u' : 'i', size);
+
+                       for (tree v = TYPE_VALUES(target); v != NULL; v = TREE_CHAIN (v)) {
+                               fprintf(output_file, "\t{ label => '%s', value => '%ld' },\n",
+                                       IDENTIFIER_POINTER(TREE_PURPOSE(v)),
+                                       tree_to_shwi(TREE_VALUE(v)));
+                       }
+                       fprintf(output_file, "]},\n");
+                       break;
+               }
+               case VOID_TYPE:
+               case INTEGER_TYPE:
+               case REAL_TYPE:
+                       break;
+               default:
+                       printf("unknown type def: %s\n", ZTREE_CODE(target));
+                       gcc_unreachable();
+               }
+
+               break;
+       }
+       case FUNCTION_DECL: {
+               if (!names) {
+                       name = DECL_NAME(type);
+                       if (!name)
+                               return;
+                       names = IDENTIFIER_POINTER(name);
+               }
+               if (hash_lookup(&dumped, names))
+                       return;
+               hash_put(&dumped, node_alloc(type, names));
+               printf("export type func decl %s\n", names);
+
+               fprintf(output_file, "'func:%s' => { name => '%s', type => 'func',", names, names);
+
+               // FUNCTION_DECL -> FUNCTION_TYPE -> RESULT_TYPE, get FUNCTION_TYPE
+               type = TREE_TYPE(type);
+
+               // TODO: cleanup
+               debug_tree_helper(type, "function 1");
+               {
+                       tree result_type = TREE_TYPE(type);
+                       const size_t data_size = value_size(TYPE_SIZE(result_type));
+
+                       fprintf(output_file, "\n\tresult => {");
+                       export_param(type, result_type, data_size);
+                       fprintf(output_file, " },");
+               }
+
+               fprintf(output_file, "\n\targuments => [\n");
+               //export_decl_params(DECL_ARGUMENTS(type), 0);
+               export_params(TYPE_ARG_TYPES(type), 0);
+               fprintf(output_file, "]},\n");
+               break;
+       }
+       case FUNCTION_TYPE: {
+               if (!names) {
+                       name = TYPE_IDENTIFIER(type);
+                       if (!name)
+                               return;
+                       names = IDENTIFIER_POINTER(name);
+               }
+               if (hash_lookup(&dumped, names))
+                       return;
+               hash_put(&dumped, node_alloc(type, names));
+               printf("export type func %s\n", names);
+
+               fprintf(output_file, "'call:%s' => { name => '%s', type => 'call',", names, names);
+
+               debug_tree_helper(type, "function type");
+
+               // TODO: cleanup
+               // FUNCTION_TYPE -> RESULT_TYPE
+               {
+                       tree result = TREE_TYPE(type);
+                       //tree result_type = TREE_TYPE(result);
+                       //printf(" result type type %s\n", ZTREE_CODE(result_type));
+                       const size_t data_size = value_size(TYPE_SIZE(result));
+
+                       printf(" result size %zu\n", data_size);
+                       fprintf(output_file, "\n\tresult => {");
+                       export_param(type, result, data_size);
+                       fprintf(output_file, " },");
+               }
+
+               stack_push(&context_stack, node_alloc(type, names));
+               fprintf(output_file, "\n\targuments => [\n");
+               export_params(TYPE_ARG_TYPES(type), 0);
+               free(stack_pull(&context_stack));
+               fprintf(output_file, "]},\n");
+               break;
+       }
+       case RECORD_TYPE: // struct
+       case UNION_TYPE: {
+               const char *su = TREE_CODE(type) == RECORD_TYPE ? "struct" : "union";
+
+               // ignore empty types
+               if (!TYPE_FIELDS(type))
+                       return;
+
+               if (!names) {
+                       name = TYPE_IDENTIFIER(type);
+                       if (!name)
+                               return;
+                       names = IDENTIFIER_POINTER(name);
+               }
+               if (hash_lookup(&dumped, names))
+                       return;
+               hash_put(&dumped, node_alloc(type, names));
+
+               fprintf(output_file, "'%s:%s' => { name => '%s', type => '%s', size => %zu, fields => [\n",
+                       su, names, names, su, tree_to_uhwi(TYPE_SIZE(type)));
+
+               stack_push(&context_stack, node_alloc(type, names));
+               export_fields(TYPE_FIELDS(type), 0, 0);
+               free(stack_pull(&context_stack));
+
+               fprintf(output_file, "]},\n");
+               break;
+       }
+       case ENUMERAL_TYPE: {
+               // FIXME: see ENUMERAL_TYPE above regarding duplicate anon enums
+
+               // ignore empty enums
+               if (!TYPE_VALUES(type))
+                       return;
+
+               // We can only assume a non-named enum type isn't repeatedly sent here
+               if (!names) {
+                       name = TYPE_IDENTIFIER(type);
+                       if (name)
+                               names = IDENTIFIER_POINTER(name);
+               }
+               if (names) {
+                       if (hash_lookup(&dumped, names))
+                               return;
+                       hash_put(&dumped, node_alloc(type, names));
+               }
+
+               size_t size = tree_to_uhwi(TYPE_SIZE(type));
+
+               if (size > 64) {
+                       fprintf(stderr, "Warning: enum '%s' requires too many bits (%zu)\n", names, size);
+                       return;
+               }
+
+               // FIXME: choose a better anon name
+               char nameb[64];
+               static int namei;
+
+               if (!names) {
+                       sprintf(nameb, "enum$%d", namei++);
+                       names = nameb;
+               }
+               fprintf(output_file, "'enum:%s' => { name => '%s', type => 'enum', size => %zu, value_type => '%c%zu', values => [\n",
+                       names, names, size, TYPE_UNSIGNED(type) ? 'u' : 'i', size);
+
+               for (tree v = TYPE_VALUES(type); v != NULL; v = TREE_CHAIN (v)) {
+                       fprintf(output_file, "\t{ label => '%s', value => '%ld' },\n",
+                               IDENTIFIER_POINTER(TREE_PURPOSE(v)),
+                               tree_to_shwi(TREE_VALUE(v)));
+               }
+               fprintf(output_file, "]},\n");
+               break;
+       }
+       case FIELD_DECL:
+       case PARM_DECL:
+               break;
+       case VAR_DECL:
+               // global external variables, might want these
+               // well, if there's a way to resolve them
+               break;
+       default:
+               printf("unknown export: %s\n", ZTREE_CODE(type));
+               gcc_unreachable();
+       }
+}
+
+static void plugin_finish_type(void *event_data, void *user_data) {
+       tree type = (tree)event_data;
+
+       export_type(type, NULL);
+}
+
+static void plugin_finish_decl(void *event_data, void *user_data) {
+       tree type = (tree)event_data;
+
+       export_type(type, NULL);
+}
+
+static void plugin_finish(void *event_data, void *user_data) {
+       for (struct node *n = todump.head; n; n=n->next) {
+               if (COMPLETE_TYPE_P(n->type)) {
+                       if (n->name[0]) {
+                               export_type(n->type, n->name);
+                       } else {
+                               export_type(n->type, value_name(n->type));
+                       }
+               }
+       }
+
+       fprintf(output_file, "# dumped structs:\n");
+       for (struct node *n = dumped.list.head; n; n=n->next)
+               fprintf(output_file, "# %s\n", n->name);
+
+       fprintf(output_file, ");\n");
+       //fclose(output_file);
+}
+
+int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version) {
+    const char *output = NULL;
+
+    for (int i = 0; i < plugin_info->argc; ++i) {
+        if (0 == strcmp(plugin_info->argv[i].key, "output")) {
+            output = plugin_info->argv[i].value;
+        }
+    }
+
+    if (NULL == output) {
+        fprintf(stderr, "export plugin: missing parameter: -fplugin-arg-export-output=<output>\n");
+        exit(EXIT_FAILURE);
+    }
+
+    output_file = fopen(output, "w");
+    if (NULL == output_file) {
+        perror(output);
+        exit(EXIT_FAILURE);
+    }
+
+    fprintf(output_file, "%%data = (\n");
+
+    register_callback(plugin_info->base_name, PLUGIN_FINISH_DECL, plugin_finish_decl, NULL);
+    register_callback(plugin_info->base_name, PLUGIN_FINISH_TYPE, plugin_finish_type, NULL);
+    register_callback(plugin_info->base_name, PLUGIN_FINISH, plugin_finish, NULL);
+
+    return 0;
+}
diff --git a/src/generate b/src/generate
new file mode 100755 (executable)
index 0000000..bc7ee2b
--- /dev/null
@@ -0,0 +1,771 @@
+#!/usr/bin/perl
+
+# usage
+# generate [-d dir] [-t package] [--enclosing-type type ] [-s struct-root-pattern]* [--struct-file file]* [-c class [-llib]* [-f func-pattern]* [--func-file file]* ]*
+#  -d dir
+#    root output directory
+#  -t package
+#    output package
+#  --enclosing-type type
+#    If supplied, all structures and classes are written to an enclosing class
+#  -s struct-root-pattern
+#    provide one or more patterns for matching structure roots.
+#    all dependencies are automatically included.
+#    if no pattern is provided, all match.
+#  --struct-file file
+#    provide a filename with exact structure names in it.  These are
+#    used as roots(?)
+#  -c class
+#    specify class name to generate
+#  -llib
+#    specify link library used by class
+#  -f func-pattern
+#    function name pattern to include for the last class
+#  --func-file file
+#    point to a filename with exact function names in it, one per line ('#' is a comment).
+
+# TODO: scan all functions and include any types they use as struct roots
+# TODO: some way to specify external types
+
+@matchStruct = ();
+$meta = "";
+# @classes = ( { name => 'class', match => [ func-pattern, ... ], match_file => [ file, ... ] } )
+@classes = ();
+%lastclass = ();
+$output = ".";
+# map call signatures to a class name
+%callMap = ();
+$package = "";
+
+while (@ARGV) {
+    my $cmd = shift(@ARGV);
+
+    if ($cmd eq "-f") {
+       my $v = shift(@ARGV);
+       push @{$lastclass{match}}, qr/$v/;
+    } elsif ($cmd eq "--func-file") {
+       my $file = shift(@ARGV);
+       push @{$lastclass{match_file}}, $file;
+       push @{$lastclass{match}}, readMatchFile($file);
+    } elsif ($cmd eq "-s") {
+       my $v = shift(@ARGV);
+       push @matchStruct, qr/$v/;
+    } elsif ($cmd eq "--struct-file") {
+       my $file = shift(@ARGV);
+       push @matchStruct, readMatchFile($file);
+    } elsif ($cmd eq "-t") {
+       $package = shift(@ARGV);
+    } elsif ($cmd eq "-c") {
+       %lastclass = (
+           name => shift(@ARGV),
+           match => [],
+           match_file => [],
+           libs => []);
+       push @classes, \%lastclass;
+    } elsif ($cmd =~ m/^-l(.*)/) {
+       push @{$lastclass{libs}}, $1;
+    } elsif ($cmd eq "-d") {
+       $output = shift(@ARGV);
+    } elsif ($cmd eq "--enclosing-type") {
+       $enclosingType = shift(@ARGV);
+    } else {
+       $meta = $cmd;
+    }
+}
+
+use Data::Dumper;
+print Dumper(@classes);
+#exit 0;
+
+require $meta;
+
+# box types for primitives
+%map_box = (
+    "long" => "Long",
+    "int" => "Integer",
+    "short" => "Short",
+    "char" => "Character",
+    "float" => "Float",
+    "double" => "Double",
+    "byte" => "Byte",
+    "void" => "Void"
+    );
+
+sub readMatchFile {
+    my $path = shift @_;
+    my @lines = ();
+
+    open(my $f,"<$path");
+    while (<$f>) {
+       chop;
+       next if m/^#/;
+
+       #push @lines, qr/\^$_\$/;
+       push @lines, $_;
+    }
+    close($f);
+
+    my $all = join ('|', @lines);
+
+    return qr/^($all)$/;
+}
+
+sub camelCase {
+    my $name = shift @_;
+
+    $name =~ s/_(.)/uc($1)/eg;
+
+    return $name;
+}
+
+sub StudlyCaps {
+    my $name = shift @_;
+
+    $name =~ s/^(.)/uc($1)/e;
+    $name =~ s/_(.)/uc($1)/eg;
+
+    return $name;
+}
+
+
+sub structSignature {
+    my %struct = %{shift(@_)};
+    my $union = shift(@_);
+    my $sig = "";
+    my @fields = @{$struct{fields}};
+    my $offset = 0;
+
+    my $inbf = 0;
+    my $bfoffset = 0;
+    my $bfstart = 0;
+    my $bfsig = "";
+
+    for $fi (@fields) {
+       my %field = %{$fi};
+       my $off = $field{offset};
+
+       # bitfields, this only handles 1x u64 bitfield section
+       #  They need to: align to u32/u64
+       #  Group fields into one full u32/u64
+       # TODO: check alignment @ start?
+       # TODO: clean up and complete
+       # TODO: bitfields in unions are probably broken
+       if ($field{ctype} eq 'bitfield') {
+           if ($inbf) {
+               if ($off - $offset) {
+                   $bfsig .= "x";
+                   $bfsig .= ($off - $offset);
+               }
+               $bfsig .= $field{type};
+               $bfsig .= "($field{name})";
+               $offset = $off + $field{size};
+           } else {
+               $inbf = 1;
+               $bfsig = $field{type};
+               $bfsig .= "($field{name})";
+               $offset = $off + $field{size};
+               $bfstart = $field{offset};
+           }
+
+           if ($union) {
+               $inbf = 0;
+
+               if (($offset - $bfstart) == 32) {
+                   $bfsig = "u32=[$bfsig]";
+               } elsif (($offset - $bfstart) < 32) {
+                   $bfsig .= "x";
+                   $bfsig .= 32 - ($offset - $bfstart);
+                   $offset = $bfstart + 32;
+                   $bfsig = "u32=[$bfsig]";
+               } elsif (($offset - $bfstart) == 64) {
+                   $bfsig = "u64=[$bfsig]";
+               } elsif (($offset - $bfstart) < 64) {
+                   $bfsig .= "x";
+                   $bfsig .= 64 - ($offset - $bfstart);
+                   $offset = $bfstart + 64;
+                   $bfsig = "u64=[$bfsig]";
+               }
+
+               $sig .= $bfsig;
+               $sig .= "|" if ($union && $fi != @fields[$#fields]);
+           }
+           next;
+       } elsif ($inbf) {
+           if (($offset - $bfstart) == 32) {
+               $bfsig = "u32=[$bfsig]";
+           } elsif (($offset - $bfstart) < 32) {
+               $bfsig .= "x";
+               $bfsig .= 32 - ($offset - $bfstart);
+               $offset = $bfstart + 32;
+               $bfsig = "u32=[$bfsig]";
+           } elsif (($offset - $bfstart) == 64) {
+               $bfsig = "u64=[$bfsig]";
+           } elsif (($offset - $bfstart) < 64) {
+               $bfsig .= "x";
+               $bfsig .= 64 - ($offset - $bfstart);
+               $offset = $bfstart + 64;
+               $bfsig = "u64=[$bfsig]";
+           }
+           $sig .= $bfsig;
+           $inbf = 0;
+       }
+
+       # skip to next offset if necessary
+       if ($off > $offset) {
+           $sig .= "x";
+           $sig .= ($off - $offset);
+       }
+       $offset = $off + $field{size};
+
+       # normal field processing
+       if ($field{deref}) {
+           my $deref = $field{deref};
+
+           # HACK: function -> Void
+       #   if ($field{debug} eq 'function') {
+       #       $sig .= "u64($field{name}):v";
+       #    } els
+               if ($deref =~ m/^(u\d\d)(:.*)/) {
+               $sig .= "$1($field{name})$2";
+           } else {
+               $sig .= "$deref($field{name})";
+           }
+       } else {
+           if ($field{type} =~ m/(struct|union):(.*)/) {
+               $sig .= "\${$2}";
+           } elsif ($field{type} =~ m/([iuf])(\d+)/) {
+               $sig .= $1;
+               $sig .= $2;
+           } elsif ($field{type} eq 'void') {
+               $sig .= "v";
+           } elsif ($field{type} eq 'enum') {
+               # FIXME: set type in compiler
+               $sig .= "u32";
+           }
+
+           $sig .= "($field{name})";
+       }
+
+       $sig .= "|" if ($union && $fi != @fields[$#fields]);
+    }
+
+    # finish any trailing bitfield
+    # TODO: cleanup
+    if ($inbf) {
+       if (($offset - $bfstart) == 32) {
+           $bfsig = "u32=[$bfsig]";
+       } elsif (($offset - $bfstart) < 32) {
+           $bfsig .= "x";
+           $bfsig .= 32 - ($offset - $bfstart);
+           $offset = $bfstart + 32;
+           $bfsig = "u32=[$bfsig]";
+       } elsif (($offset - $bfstart) == 64) {
+           $bfsig = "u64=[$bfsig]";
+       } elsif (($offset - $bfstart) < 64) {
+           $bfsig .= "x";
+           $bfsig .= 64 - ($offset - $bfstart);
+           $offset = $bfstart + 64;
+           $bfsig = "u64=[$bfsig]";
+       }
+       #$bfsig .= "]";
+       $sig .= $bfsig;
+    }
+
+    return "[".$sig."]";
+}
+
+sub funcSignature {
+    my %func = %{shift(@_)};
+    my $sig = "";
+    my @params = @{$func{arguments}};
+
+    for $pi (@params) {
+       my %param = %{$pi};
+
+       if ($param{deref}) {
+           # HACK: function to void
+           if ($param{debug} eq "function") {
+               $sig .= "u64:v";
+           } else {
+               $sig .= $param{deref};
+           }
+       } else {
+           if ($param{type} =~ m/struct:(.*)/) {
+               $sig .= "\${$1}";
+           } elsif ($param{type} =~ m/([iuf])(\d*)/) {
+               $sig .= $1;
+               $sig .= $2;
+           } elsif ($param{type} eq "void") {
+               $sig .= "v";
+           }
+       }
+    }
+
+    my %result = %{$func{result}};
+    my $ret = "";
+
+    if ($result{deref}) {
+       $ret .= $result{deref};
+    } else {
+       if ($result{type} =~ m/^struct:(.*)/) {
+           $ret .= "\${$1}";
+       } elsif ($result{type} =~ m/^([iuf])(\d+)/) {
+           $ret .= $1;
+           $ret .= $2;
+       } elsif ($result{type} eq "void") {
+           $ret .= "v";
+       }
+    }
+
+    return "($sig)$ret";
+}
+
+sub deref {
+    my $type = shift @_;
+    my $ref = shift @_;
+
+    while ($ref) {
+       if ($ref =~ m/\[\d*(.*)\]/) {
+           my $sub = deref($type, $1);
+
+           return "Array<$sub>";
+       } elsif ($ref =~ m/^u64:(.*)/) {
+           $type = "Pointer<$type>";
+           $ref = $1;
+       } else {
+           last;
+       }
+    }
+    return $type;
+}
+
+sub typeToJava {
+    my %param = %{shift(@_)};
+    my $type = $param{type};
+    my $ref = $param{deref};
+
+    if ($type =~ m/^struct:(.*)/) {
+       $type = StudlyCaps($1);
+    } elsif ($type =~ m/call:/) {
+       # this re-writes ref to remove one pointer-to as the Callback absorbs it.
+       $type = "Callback<".$callMap{$type}.">";
+       $type || die ("No mapping for type ".Dumper(\%param));
+       $ref =~ s/^u(32|64)://;
+    } elsif ($type =~ m/^enum:(.*)/) {
+       # TODO: other enum options
+       $type = "int";
+    } elsif ($type eq "void") {
+       $type = "void";
+    } elsif ($type =~ m/^([iu])(\d*)/) {
+       my $sign = $1;
+       my $size = $2;
+
+       if ($size <= 8) {
+           $type = "byte";
+       } elsif ($size <= 16) {
+           if ($sign eq "i") {
+               $type = "short";
+           } else {
+               $type = "char";
+           }
+       } elsif ($size <= 32) {
+           $type = "int";
+       } else {
+           $type = "long";
+       }
+    } elsif ($type =~ m/^[f](\d*)$/) {
+       my $size = $1;
+
+       if ($size == 32) {
+           $type = "float";
+       } elsif ($size == 64) {
+           $type = "double";
+       }
+    }
+
+    if ($ref) {
+       $type = $map_box{$type} if ($map_box{$type});
+       $type = deref($type, $ref);
+    }
+
+    return $type;
+}
+
+sub testMatch {
+    my $name = shift @_;
+
+    if (@_) {
+       for $pat (@_) {
+           if ($name =~ /$pat/) {
+               return 1;
+           }
+       }
+       return 0;
+    } else {
+       return 1;
+    }
+}
+
+# find all matching structures and then all that they require
+sub findStructs {
+    my %all = %{shift @_};
+    my @match = @_;
+    my @stack = grep {
+       my %e = %{$all{$_}};
+       $e{type} =~ m/(struct|union)/ && testMatch($e{name}, @match);
+    } keys %all;
+    my %visit = ();
+
+    while (@stack) {
+       my $test = shift @stack;
+
+       if (!$visit{$test}) {
+           my %struct = %{$all{$test}};
+
+           $visit{$test} = 1;
+
+           if (%struct) {
+               print "class: $struct{name}\n";
+               # find all types this one uses
+               for $f (@{$struct{fields}}) {
+                   my %field = %{$f};
+
+                   if ($field{type} =~ m/^(struct|union):(.*)/) {
+                       if (!$set{$field{type}}) {
+                           $set{$field{type}} = $all{$field{type}};
+                           push @stack, $field{type};
+                       }
+                   }
+               }
+           } else {
+               # this is an anon type, typically used for handles
+               $test =~ m/^(struct|union):(.*)/;
+               print " anon: $2\n";
+               my %rec = (
+                   type => 'struct',
+                   name => $2,
+                   size => 0
+                   );
+               $data{$test} = \%rec;
+           }
+       }
+    }
+    return keys %visit;
+}
+
+sub findFuncs {
+    my %all = %{shift @_};
+    my @match = @_;
+    my @stack = grep {
+       my %e = %{$all{$_}};
+       $e{type} eq "func" && testMatch($e{name}, @match);
+    } keys %all;
+
+    return @stack;
+}
+
+# ######################################################################
+
+# setup section
+
+# find all classes used by functions, add them to the struct roots
+my %roots = ();
+for $c (@classes) {
+    my %class = %{$c};
+    my @libs = @{$class{libs}};
+    my @match = @{$class{match}};
+
+    for $k (findFuncs(\%data, @match)) {
+       my %func = %{$data{$k}};
+       my @params = @{$func{arguments}};
+
+       for $pi (@params) {
+           my %param = %{$pi};
+
+           if ($param{type} =~ m/^(struct|union):(.*)/) {
+               $roots{$2} = 1;
+           }
+       }
+
+       my %result = %{$func{result}};
+
+       if ($result{type} =~ m/^(struct|union):(.*)/) {
+           $roots{$2} = 1;
+       }
+    }
+}
+
+# FIXME: only include ones used elsewhere
+# add roots for any types used by calls
+for $k (grep { $_ =~ m/^call:/n } keys %data) {
+    my %func = %{$data{$k}};
+    my @params = @{$func{arguments}};
+
+    for $pi (@params) {
+       my %param = %{$pi};
+
+       if ($param{type} =~ m/^(struct|union):(.*)/) {
+           $roots{$2} = 1;
+       }
+    }
+
+    my %result = %{$func{result}};
+
+    if ($result{type} =~ m/^(struct|union):(.*)/) {
+       $roots{$2} = 1;
+    }
+}
+
+$all = join ('|', keys %roots);
+if ($all) {
+    push @matchStruct, qr/^($all)$/;
+}
+print "structures:\n";
+print Dumper(@matchStruct);
+
+# make a map for all callbacks (call: type) to generated names
+for $c (grep { $_ =~ m/^call:/n } keys %data) {
+    my $name = $c;
+
+    print "$c\n";
+    # enum maybe to int?
+
+    $name =~ s/^call:/Call/;
+    $name =~ s/\$\{[^\}]*\}/L/g;
+    $name =~ s/[ui](64|32):/p/g;
+    $name =~ s/[ui]64/J/g;
+    $name =~ s/[ui]32/I/g;
+    $name =~ s/[ui]8/B/g;
+    $name =~ s/f32/F/g;
+    $name =~ s/f64/D/g;
+    $name =~ s/[\[\]\(\)]/_/g;
+
+    $callMap{$c} = "$name";
+}
+
+print "call mappings\n";
+print Dumper(\%callMap);
+
+# ######################################################################
+# Start output
+my $dst;
+
+use File::Basename;
+use File::Path qw(make_path);
+
+if ($package ne "") {
+    $packagePrefix = $package.".";
+}
+
+if ($enclosingType) {
+    my $classname = $packagePrefix.$enclosingType;
+
+    $classname =~ s@\.@/@g;
+
+    my $path = $output."/".$classname.".java";
+    my $dir = dirname($path);
+    my $class = basename($path, ".java");
+
+    print "path $path\n";
+    print "dirname $dir\n";
+
+    make_path($dir);
+    open ($dst, ">$path");
+
+    if ($package ne "") {
+       print $dst "package $package;\n";
+    }
+
+    print $dst <<END;
+import java.foreign.Libraries;
+import java.foreign.annotations.*;
+import java.foreign.memory.*;
+import java.lang.invoke.MethodHandles;
+END
+    print $dst "public class $class {\n";
+}
+
+# Dump structures
+for $k (findStructs(\%data, @matchStruct)) {
+    my %struct = %{$data{$k}};
+    my @fields = @{$struct{fields}};
+    my $signature = structSignature(\%struct, ($struct{type} eq "union"));
+    my $name = StudlyCaps($struct{name});
+
+    if (!$enclosingType) {
+       my $classname = $packagePrefix.$name;
+
+       open ($dst, ">$path");
+       $classname =~ s@\.@/@g;
+
+       my $path = $output."/".$classname.".java";
+       my $dir = dirname($path);
+       my $class = basename($path, ".java");
+       make_path($dir);
+       open ($dst, ">$path");
+
+       if ($package ne "") {
+           print $dst "package $package;\n";
+       }
+       print $dst <<END;
+import java.foreign.annotations.*;
+import java.foreign.memory.*;
+END
+    }
+
+    # any in-line structures need to be added to the resolutionContext
+    # TODO: only include actual inline, not pointers
+    my %resolve = ();
+    for $fi (@fields) {
+       my %field = %{$fi};
+
+       if ($field{type} =~ m/^(struct|union):(.*)/) {
+           $resolve{StudlyCaps($2).".class"} = 1;
+       }
+    }
+    my $resolve = join (",", keys %resolve);
+
+    print $dst "\@NativeStruct(value=\"$signature($struct{name})\", resolutionContext={$resolve})\n";
+    print $dst "public interface $name extends Struct<$name> {\n";
+
+    for $fi (@fields) {
+       my %field = %{$fi};
+       my $type = typeToJava(\%field);
+       my $cc = StudlyCaps($field{name});
+
+       print $dst "\t\@NativeGetter(value=\"$field{name}\")\n";
+       print $dst "\tpublic $type get$cc();\n";
+
+       print $dst "\t\@NativeSetter(value=\"$field{name}\")\n";
+       print $dst "\tpublic void set$cc($type value);\n";
+    }
+
+    print $dst "}\n";
+
+    if (!$enclosingType) {
+       close($dst);
+    }
+}
+
+# Dump classes for library linkage
+for $c (@classes) {
+    my %class = %{$c};
+    my @libs = @{$class{libs}};
+    my @match = @{$class{match}};
+
+    if (!$enclosingType) {
+       my $classname = $packagePrefix.$class{name};
+
+       open ($dst, ">$path");
+       $classname =~ s@\.@/@g;
+
+       my $path = $output."/".$classname.".java";
+       my $dir = dirname($path);
+       my $class = basename($path, ".java");
+       make_path($dir);
+       open ($dst, ">$path");
+
+       if ($package ne "") {
+           print $dst "package $package;\n";
+       }
+       print $dst <<END;
+import java.foreign.Libraries;
+import java.foreign.annotations.*;
+import java.foreign.memory.*;
+import java.lang.invoke.MethodHandles;
+END
+    }
+
+    print $dst "\@NativeHeader(libraries={";
+    print $dst join(",", map { "\"$_\"" } @libs);
+    print $dst "})\n";
+    print $dst "public interface $class{name} {\n";
+
+    for $k (sort(findFuncs(\%data, @match))) {
+       my %func = %{$data{$k}};
+       my @params = @{$func{arguments}};
+       my $signature = funcSignature(\%func);
+       my $name = ($func{name});
+       my $result = typeToJava(\%{$func{result}});
+
+       print $dst "\n\t\@NativeFunction(value=\"$signature\")\n";
+       print $dst "\tpublic $result $name(";
+
+       for $pi (@params) {
+           my %param = %{$pi};
+           my $type = typeToJava($pi);
+
+           print $dst "$type $param{name}";
+           print $dst ", " if ($pi != $params[$#params]);
+       }
+
+       print $dst ");\n";
+    }
+
+    print $dst "\n";
+    print $dst "\tpublic static final $class{name} bind = Libraries.bind(MethodHandles.lookup(), $class{name}.class);\n";
+
+    print $dst "}\n";
+
+    if (!$enclosingType) {
+       close($dst);
+    }
+}
+
+# Dump callbacks
+# TODO: only those used by classes and functions that were exported
+for $c (keys %callMap) {
+    my %call = %{$data{$c}};
+    my $name = $callMap{$c};
+    my @params = @{$call{arguments}};
+    my $result = typeToJava(\%{$call{result}});
+
+    if (!$enclosingType) {
+       my $classname = $packagePrefix.$name;
+
+       open ($dst, ">$path");
+       $classname =~ s@\.@/@g;
+
+       my $path = $output."/".$classname.".java";
+       my $dir = dirname($path);
+       my $class = basename($path, ".java");
+       make_path($dir);
+       open ($dst, ">$path");
+
+       if ($package ne "") {
+           print $dst "package $package;\n";
+       }
+       print $dst <<END;
+import java.foreign.Libraries;
+import java.foreign.annotations.*;
+import java.foreign.memory.*;
+END
+    }
+
+    # FIXME: use something other than name to store this
+    print $dst "\@FunctionalInterface\n";
+    print $dst "\@NativeCallback(value=\"$call{name}\")\n";
+    print $dst "public interface $name {\n";
+    print $dst "\tpublic $result fn(";
+
+    for $pi (@params) {
+       my %param = %{$pi};
+       my $type = typeToJava($pi);
+
+       print $dst "$type $param{name}";
+       print $dst ", " if ($pi != $params[$#params]);
+    }
+
+    print $dst ");\n";
+    print $dst "}\n";
+
+    if (!$enclosingType) {
+       close($dst);
+    }
+}
+
+# Finish off
+if ($enclosingType) {
+    print $dst "}\n";
+    close($dst);
+}
diff --git a/src/list.h b/src/list.h
new file mode 100644 (file)
index 0000000..33afef7
--- /dev/null
@@ -0,0 +1,152 @@
+
+/*
+  Copyright (C) 2010,2019,2020 Michael Zucchi
+
+   This program is free software: you can redistribute it and/or
+   modify it under the terms of the GNU General Public License
+   as published by the Free Software Foundation, either version 3 of
+   the License, or (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public
+   License along with this program. If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+
+/* **********************************************************************
+ * a simple ordered hash/list/stack
+ */
+struct node {
+       // list/stack next
+       struct node *next;
+       // hash chain link/stack prev
+       struct node *link;
+
+       // payload
+       tree type;
+       char name[];
+};
+
+struct list {
+       struct node *head;
+       struct node *tail;
+};
+
+struct hash {
+       struct node *table[64];
+       struct list list;
+};
+
+static struct node *node_alloc(tree type, const char *name) {
+       struct node *n = (struct node *)xmalloc(sizeof(struct node) + (name ? strlen(name) + 1 : 1));
+
+       n->next = NULL;
+       n->link = NULL;
+       n->type = type;
+       if (name)
+               strcpy(n->name, name);
+       else
+               n->name[0] = 0;
+
+       return n;
+}
+
+static void stack_push(struct list *stack, struct node *node) {
+       if (stack->head) {
+               stack->head->link = node;
+               node->next = stack->head;
+               stack->head = node;
+       } else {
+               stack->head = node;
+               stack->tail = node;
+       }
+}
+
+static struct node *stack_pull(struct list *stack) {
+       struct node *n = stack->head;
+
+       if (n) {
+               stack->head = n->next;
+               if (stack->head)
+                       stack->head->link = NULL;
+               else
+                       stack->tail = NULL;
+       }
+
+       return n;
+}
+
+static unsigned int ez_hash_string(const char *s) {
+        unsigned int hash = 5381;
+
+        while (*s)
+                //hash = (*s++) + (hash << 5) + hash;
+                hash = hash * 33 + (*s++);
+
+        return hash;
+}
+
+static void list_add(struct list *list, struct node *node) {
+       if (list->head) {
+               list->tail->next = node;
+               list->tail = node;
+       } else {
+               list->head = node;
+               list->tail = node;
+       }
+}
+
+static void hash_put(struct hash *hash, struct node *node) {
+       int code = ez_hash_string(node->name) & 63;
+
+       list_add(&hash->list, node);
+
+       node->link = hash->table[code];
+       hash->table[code] = node;
+}
+
+struct node *hash_lookup(struct hash *hash, const char *name) {
+       int code = ez_hash_string(name) & 63;
+
+       for (struct node *n = hash->table[code]; n; n=n->link) {
+               if (strcmp(n->name, name) == 0)
+                       return n;
+       }
+
+       return NULL;
+}
+
+/* simple growable c buffer */
+struct buffer {
+       size_t size;
+       size_t pos;
+       char *data;
+};
+
+static void buffer_init(struct buffer *b, size_t size) {
+       b->size = size;
+       b->pos = 0;
+       b->data = (char *)xmalloc(size);
+}
+
+static void buffer_room(struct buffer *b, size_t len) {
+       if (b->pos + len >= b->size) {
+               do {
+                       b->size *= 2;
+               } while (b->pos + len >= b->size);
+
+               b->data = (char *)xrealloc(b->data, b->size);
+       }
+}
+
+static void buffer_add(struct buffer *b, const char *v) {
+       size_t len = strlen(v);
+
+       buffer_room(b, len);
+       strcpy(b->data + b->pos, v);
+       b->pos += len;
+}
diff --git a/src/tree-codes.h b/src/tree-codes.h
new file mode 100644 (file)
index 0000000..4b2cdf2
--- /dev/null
@@ -0,0 +1,374 @@
+/*
+Taken from gcc plugin/tree.defs
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
+
+const char *tree_codes[] = {
+       "ERROR_MARK",
+"IDENTIFIER_NODE",
+"TREE_LIST",
+"TREE_VEC",
+"BLOCK",
+"OFFSET_TYPE",
+"ENUMERAL_TYPE",
+"BOOLEAN_TYPE",
+"INTEGER_TYPE",
+"REAL_TYPE",
+"POINTER_TYPE",
+"REFERENCE_TYPE",
+"NULLPTR_TYPE",
+"FIXED_POINT_TYPE",
+"COMPLEX_TYPE",
+"VECTOR_TYPE",
+"ARRAY_TYPE",
+"RECORD_TYPE",
+"UNION_TYPE",
+"QUAL_UNION_TYPE",
+"VOID_TYPE",
+"FUNCTION_TYPE",
+"METHOD_TYPE",
+"LANG_TYPE",
+"VOID_CST",
+"INTEGER_CST",
+"POLY_INT_CST",
+"REAL_CST",
+"FIXED_CST",
+"COMPLEX_CST",
+"VECTOR_CST",
+"STRING_CST",
+"FUNCTION_DECL",
+"LABEL_DECL",
+"FIELD_DECL",
+"VAR_DECL",
+"CONST_DECL",
+"PARM_DECL",
+"TYPE_DECL",
+"RESULT_DECL",
+"DEBUG_EXPR_DECL",
+"DEBUG_BEGIN_STMT",
+"NAMESPACE_DECL",
+"IMPORTED_DECL",
+"NAMELIST_DECL",
+"TRANSLATION_UNIT_DECL",
+"COMPONENT_REF",
+"BIT_FIELD_REF",
+"ARRAY_REF",
+"ARRAY_RANGE_REF",
+"REALPART_EXPR",
+"IMAGPART_EXPR",
+"VIEW_CONVERT_EXPR",
+"INDIRECT_REF",
+"OBJ_TYPE_REF",
+"CONSTRUCTOR",
+"COMPOUND_EXPR",
+"MODIFY_EXPR",
+"INIT_EXPR",
+"TARGET_EXPR",
+"COND_EXPR",
+"VEC_DUPLICATE_EXPR",
+"VEC_SERIES_EXPR",
+"VEC_COND_EXPR",
+"VEC_PERM_EXPR",
+"BIND_EXPR",
+"CALL_EXPR",
+"WITH_CLEANUP_EXPR",
+"CLEANUP_POINT_EXPR",
+"PLACEHOLDER_EXPR",
+"PLUS_EXPR",
+"MINUS_EXPR",
+"MULT_EXPR",
+"POINTER_PLUS_EXPR",
+"POINTER_DIFF_EXPR",
+"MULT_HIGHPART_EXPR",
+"TRUNC_DIV_EXPR",
+"CEIL_DIV_EXPR",
+"FLOOR_DIV_EXPR",
+"ROUND_DIV_EXPR",
+"TRUNC_MOD_EXPR",
+"CEIL_MOD_EXPR",
+"FLOOR_MOD_EXPR",
+"ROUND_MOD_EXPR",
+"RDIV_EXPR",
+"EXACT_DIV_EXPR",
+"FIX_TRUNC_EXPR",
+"FLOAT_EXPR",
+"NEGATE_EXPR",
+"MIN_EXPR",
+"MAX_EXPR",
+"ABS_EXPR",
+"ABSU_EXPR",
+"LSHIFT_EXPR",
+"RSHIFT_EXPR",
+"LROTATE_EXPR",
+"RROTATE_EXPR",
+"BIT_IOR_EXPR",
+"BIT_XOR_EXPR",
+"BIT_AND_EXPR",
+"BIT_NOT_EXPR",
+"TRUTH_ANDIF_EXPR",
+"TRUTH_ORIF_EXPR",
+"TRUTH_AND_EXPR",
+"TRUTH_OR_EXPR",
+"TRUTH_XOR_EXPR",
+"TRUTH_NOT_EXPR",
+"LT_EXPR",
+"LE_EXPR",
+"GT_EXPR",
+"GE_EXPR",
+"EQ_EXPR",
+"NE_EXPR",
+"UNORDERED_EXPR",
+"ORDERED_EXPR",
+"UNLT_EXPR",
+"UNLE_EXPR",
+"UNGT_EXPR",
+"UNGE_EXPR",
+"UNEQ_EXPR",
+"LTGT_EXPR",
+"RANGE_EXPR",
+"PAREN_EXPR",
+"CONVERT_EXPR",
+"ADDR_SPACE_CONVERT_EXPR",
+"FIXED_CONVERT_EXPR",
+"NOP_EXPR",
+"NON_LVALUE_EXPR",
+"COMPOUND_LITERAL_EXPR",
+"SAVE_EXPR",
+"ADDR_EXPR",
+"FDESC_EXPR",
+"BIT_INSERT_EXPR",
+"COMPLEX_EXPR",
+"CONJ_EXPR",
+"PREDECREMENT_EXPR",
+"PREINCREMENT_EXPR",
+"POSTDECREMENT_EXPR",
+"POSTINCREMENT_EXPR",
+"VA_ARG_EXPR",
+"TRY_CATCH_EXPR",
+"TRY_FINALLY_EXPR",
+"DECL_EXPR",
+"LABEL_EXPR",
+"GOTO_EXPR",
+"RETURN_EXPR",
+"EXIT_EXPR",
+"LOOP_EXPR",
+"SWITCH_EXPR",
+"CASE_LABEL_EXPR",
+"ASM_EXPR",
+"SSA_NAME",
+"CATCH_EXPR",
+"EH_FILTER_EXPR",
+"SCEV_KNOWN",
+"SCEV_NOT_KNOWN",
+"POLYNOMIAL_CHREC",
+"STATEMENT_LIST",
+"ASSERT_EXPR",
+"TREE_BINFO",
+"WITH_SIZE_EXPR",
+"REALIGN_LOAD_EXPR",
+"TARGET_MEM_REF",
+"MEM_REF",
+"OACC_PARALLEL",
+"OACC_KERNELS",
+"OACC_DATA",
+"OACC_HOST_DATA",
+"OMP_PARALLEL",
+"OMP_TASK",
+"OMP_FOR",
+"OMP_SIMD",
+"OMP_DISTRIBUTE",
+"OMP_TASKLOOP",
+"OACC_LOOP",
+"OMP_TEAMS",
+"OMP_TARGET_DATA",
+"OMP_TARGET",
+"OMP_SECTIONS",
+"OMP_ORDERED",
+"OMP_CRITICAL",
+"OMP_SINGLE",
+"OMP_TASKGROUP",
+"OMP_SECTION",
+"OMP_MASTER",
+"OACC_CACHE",
+"OACC_DECLARE",
+"OACC_ENTER_DATA",
+"OACC_EXIT_DATA",
+"OACC_UPDATE",
+"OMP_TARGET_UPDATE",
+"OMP_TARGET_ENTER_DATA",
+"OMP_TARGET_EXIT_DATA",
+"OMP_ATOMIC",
+"OMP_ATOMIC_READ",
+"OMP_ATOMIC_CAPTURE_OLD",
+"OMP_ATOMIC_CAPTURE_NEW",
+"OMP_CLAUSE",
+"TRANSACTION_EXPR",
+"DOT_PROD_EXPR",
+"WIDEN_SUM_EXPR",
+"SAD_EXPR",
+"WIDEN_MULT_EXPR",
+"WIDEN_MULT_PLUS_EXPR",
+"WIDEN_MULT_MINUS_EXPR",
+"WIDEN_LSHIFT_EXPR",
+"VEC_WIDEN_MULT_HI_EXPR",
+"VEC_WIDEN_MULT_LO_EXPR",
+"VEC_WIDEN_MULT_EVEN_EXPR",
+"VEC_WIDEN_MULT_ODD_EXPR",
+"VEC_UNPACK_HI_EXPR",
+"VEC_UNPACK_LO_EXPR",
+"VEC_UNPACK_FLOAT_HI_EXPR",
+"VEC_UNPACK_FLOAT_LO_EXPR",
+"VEC_UNPACK_FIX_TRUNC_HI_EXPR",
+"VEC_UNPACK_FIX_TRUNC_LO_EXPR",
+"VEC_PACK_TRUNC_EXPR",
+"VEC_PACK_SAT_EXPR",
+"VEC_PACK_FIX_TRUNC_EXPR",
+"VEC_PACK_FLOAT_EXPR",
+"VEC_WIDEN_LSHIFT_HI_EXPR",
+"VEC_WIDEN_LSHIFT_LO_EXPR",
+"PREDICT_EXPR",
+"OPTIMIZATION_NODE",
+"TARGET_OPTION_NODE",
+"ANNOTATE_EXPR",
+"LAST_AND_UNUSED_TREE_CODE",
+"C_MAYBE_CONST_EXPR",
+"EXCESS_PRECISION_EXPR",
+"USERDEF_LITERAL",
+"SIZEOF_EXPR",
+"UNCONSTRAINED_ARRAY_TYPE",
+"UNCONSTRAINED_ARRAY_REF",
+"NULL_EXPR",
+"PLUS_NOMOD_EXPR",
+"MINUS_NOMOD_EXPR",
+"POWER_EXPR",
+"ATTR_ADDR_EXPR",
+"STMT_STMT",
+"LOOP_STMT",
+"EXIT_STMT",
+"OFFSET_REF",
+"PTRMEM_CST",
+"NEW_EXPR",
+"VEC_NEW_EXPR",
+"DELETE_EXPR",
+"VEC_DELETE_EXPR",
+"SCOPE_REF",
+"MEMBER_REF",
+"TYPE_EXPR",
+"AGGR_INIT_EXPR",
+"VEC_INIT_EXPR",
+"THROW_EXPR",
+"EMPTY_CLASS_EXPR",
+"BASELINK",
+"TEMPLATE_DECL",
+"TEMPLATE_PARM_INDEX",
+"TEMPLATE_TEMPLATE_PARM",
+"TEMPLATE_TYPE_PARM",
+"TYPENAME_TYPE",
+"TYPEOF_TYPE",
+"BOUND_TEMPLATE_TEMPLATE_PARM",
+"UNBOUND_CLASS_TEMPLATE",
+"USING_DECL",
+"USING_STMT",
+"DEFAULT_ARG",
+"DEFERRED_NOEXCEPT",
+"TEMPLATE_ID_EXPR",
+"OVERLOAD",
+"PSEUDO_DTOR_EXPR",
+"MODOP_EXPR",
+"CAST_EXPR",
+"REINTERPRET_CAST_EXPR",
+"CONST_CAST_EXPR",
+"STATIC_CAST_EXPR",
+"DYNAMIC_CAST_EXPR",
+"IMPLICIT_CONV_EXPR",
+"DOTSTAR_EXPR",
+"TYPEID_EXPR",
+"NOEXCEPT_EXPR",
+"NON_DEPENDENT_EXPR",
+"CTOR_INITIALIZER",
+"TRY_BLOCK",
+"EH_SPEC_BLOCK",
+"HANDLER",
+"MUST_NOT_THROW_EXPR",
+"CLEANUP_STMT",
+"IF_STMT",
+"FOR_STMT",
+"RANGE_FOR_STMT",
+"WHILE_STMT",
+"DO_STMT",
+"BREAK_STMT",
+"CONTINUE_STMT",
+"SWITCH_STMT",
+"EXPR_STMT",
+"TAG_DEFN",
+"OFFSETOF_EXPR",
+"ADDRESSOF_EXPR",
+"ARROW_EXPR",
+"ALIGNOF_EXPR",
+"AT_ENCODE_EXPR",
+"STMT_EXPR",
+"UNARY_PLUS_EXPR",
+"STATIC_ASSERT",
+"TYPE_ARGUMENT_PACK",
+"NONTYPE_ARGUMENT_PACK",
+"TYPE_PACK_EXPANSION",
+"EXPR_PACK_EXPANSION",
+"ARGUMENT_PACK_SELECT",
+"UNARY_LEFT_FOLD_EXPR",
+"UNARY_RIGHT_FOLD_EXPR",
+"BINARY_LEFT_FOLD_EXPR",
+"BINARY_RIGHT_FOLD_EXPR",
+"TRAIT_EXPR",
+"LAMBDA_EXPR",
+"DECLTYPE_TYPE",
+"UNDERLYING_TYPE",
+"BASES",
+"TEMPLATE_INFO",
+"OMP_DEPOBJ",
+"CONSTRAINT_INFO",
+"WILDCARD_DECL",
+"REQUIRES_EXPR",
+"SIMPLE_REQ",
+"TYPE_REQ",
+"COMPOUND_REQ",
+"NESTED_REQ",
+"PRED_CONSTR",
+"CHECK_CONSTR",
+"EXPR_CONSTR",
+"TYPE_CONSTR",
+"ICONV_CONSTR",
+"DEDUCT_CONSTR",
+"EXCEPT_CONSTR",
+"PARM_CONSTR",
+"CONJ_CONSTR",
+"DISJ_CONSTR",
+"UNSIGNED_RSHIFT_EXPR",
+"FLOAT_MOD_EXPR",
+"FUNCFRAME_INFO",
+"CLASS_INTERFACE_TYPE",
+"CLASS_IMPLEMENTATION_TYPE",
+"CATEGORY_INTERFACE_TYPE",
+"CATEGORY_IMPLEMENTATION_TYPE",
+"PROTOCOL_INTERFACE_TYPE",
+"KEYWORD_DECL",
+"INSTANCE_METHOD_DECL",
+"CLASS_METHOD_DECL",
+"PROPERTY_DECL",
+"MESSAGE_SEND_EXPR",
+"CLASS_REFERENCE_EXPR",
+"PROPERTY_REF",
+       };
+
+#define ZTREE_CODE(x) tree_codes[TREE_CODE(x)]
diff --git a/test-api/Makefile b/test-api/Makefile
new file mode 100644 (file)
index 0000000..d278024
--- /dev/null
@@ -0,0 +1,35 @@
+
+CFLAGS=-g -fPIC
+
+JAVA_HOME ?= /home/notzed/src/openjdk-panama-14
+JAVAC=$(JAVA_HOME)/bin/javac
+JAVA=$(JAVA_HOME)/bin/java
+
+all: bin/libapi.so bin/api.classes bin/classes/api/test/TestAPI.class
+
+bin/classes/api/test/TestAPI.class: api/test/TestAPI.java bin/api.classes
+       $(JAVAC) -cp bin/classes -d bin/classes $<
+
+bin/api.classes: bin/api.pm
+       ../src/generate -d bin/java -t api -c APILib -lapi ./bin/api.pm
+       $(JAVAC) -d bin/classes bin/java/api/*.java
+       touch $@
+
+bin/api.pm: api.h
+       gcc -fplugin=../src/export.so -fplugin-arg-export-output=$@ ./$< -o /dev/null
+
+bin/api.o: api.c api.h
+       $(CC) $(CFLAGS) -c -o $@ $<
+
+bin/libapi.so: bin/api.o
+       $(CC) -o $@ -shared $^
+
+check: all
+       $(JAVA) -Djava.library.path=bin -cp bin/classes api.test.TestAPI
+
+clean:
+       rm -rf bin
+
+.PHONY: check clean
+
+$(if $(filter clean,$(MAKECMDGOALS)),,$(shell mkdir -p bin))
diff --git a/test-api/api.c b/test-api/api.c
new file mode 100644 (file)
index 0000000..3458b4a
--- /dev/null
@@ -0,0 +1,13 @@
+
+#include <stdio.h>
+#include "api.h"
+
+void print_data(struct data *data) {
+       while (data) {
+               printf("%p: a=%d b=%d c=%d d=%d", data, data->a, data->b, data->c, data->d);
+               if (data->test_a)
+                       printf(" data->test_a()=%d", data->test_a());
+               printf("\n");
+               data = data->next;
+       }
+}
diff --git a/test-api/api.h b/test-api/api.h
new file mode 100644 (file)
index 0000000..8931b31
--- /dev/null
@@ -0,0 +1,12 @@
+
+struct data {
+       struct data *next;
+
+       int a;
+       int b;
+       int c:3;
+       unsigned d:5;
+       int (*test_a)(void);
+};
+
+void print_data(struct data *data);
diff --git a/test-api/api/test/TestAPI.java b/test-api/api/test/TestAPI.java
new file mode 100644 (file)
index 0000000..dc63a85
--- /dev/null
@@ -0,0 +1,46 @@
+
+package api.test;
+
+import java.foreign.*;
+import java.foreign.memory.*;
+import java.foreign.annotations.*;
+import java.util.function.IntFunction;
+
+import api.*;
+
+public class TestAPI {
+       public static void main(String[] args) {
+               APILib lib = APILib.bind;
+               LayoutType<Data> dataLayout = LayoutType.ofStruct(Data.class);
+
+               try (Scope s = Libraries.libraryScope(lib).fork()) {
+                       Pointer<Data> a = s.allocate(dataLayout);
+                       Pointer<Data> b = s.allocate(dataLayout);
+                       Callback<Call__I> cb = s.allocateCallback(Call__I.class,
+                               () -> {
+                                       return 56;
+                               });
+
+                       Data ad = a.get();
+                       Data bd = b.get();
+
+                       ad.setNext(b);
+                       ad.setA(1);
+                       ad.setB(2);
+                       ad.setC((byte)3);
+                       ad.setD((byte)4);
+
+                       bd.setA(5);
+                       bd.setB(6);
+                       bd.setC((byte)255);
+                       bd.setD((byte)255);
+
+                       bd.setTestA(cb);
+
+                       System.out.println("from a\n");
+                       lib.print_data(a);
+                       System.out.println("\bfrom b\n");
+                       lib.print_data(b);
+               }
+       }
+}