├── .gitignore ├── .gitmodules ├── ADDITIONAL_LICENSE_INFO ├── ASSEMBLY_EXCEPTION ├── LICENSE ├── NOTICE ├── README.md ├── pom.xml ├── standalone-jdk11-test ├── .gitignore ├── NOTICE ├── pom.xml └── src │ ├── main │ └── java │ │ ├── com │ │ └── kohlschutter │ │ │ └── jdk │ │ │ └── test │ │ │ └── Placeholder.java │ │ └── module-info.java │ └── test │ └── java │ └── com │ └── kohlschutter │ └── jdk │ └── test │ └── CompilerTest.java ├── standalone-jdk11 ├── .gitignore ├── NOTICE ├── pom.xml └── src │ └── main │ ├── java │ └── resources │ └── META-INF │ └── native-image │ └── com.kohlschutter.jdk.compiler │ └── standalone-jdk11 │ └── resource-config.json ├── standalone-jdk21-test ├── .gitignore ├── NOTICE ├── pom.xml └── src │ ├── main │ └── java │ │ ├── com │ │ └── kohlschutter │ │ │ └── jdk │ │ │ └── test │ │ │ └── Placeholder.java │ │ └── module-info.java │ └── test │ └── java │ └── com │ └── kohlschutter │ └── jdk │ └── test │ └── CompilerTest.java ├── standalone-jdk21 ├── .gitignore ├── NOTICE ├── pom.xml └── src │ └── main │ ├── java │ ├── java14 │ ├── java15 │ ├── java16 │ ├── java18 │ ├── java21 │ └── resources │ └── META-INF │ └── native-image │ └── com.kohlschutter.jdk.compiler │ └── standalone-jdk21 │ └── resource-config.json └── standalone-util ├── .gitignore ├── NOTICE ├── pom.xml └── src └── main └── java ├── com └── kohlschutter │ └── jdk │ └── standaloneutil │ ├── JavaHomeLocator.java │ ├── ToolProvider.java │ ├── annotation │ ├── AnnotationInvocationHandler.java │ ├── AnnotationParser.java │ ├── AnnotationType.java │ ├── AnnotationTypeMismatchExceptionProxy.java │ ├── EnumConstantNotPresentExceptionProxy.java │ └── ExceptionProxy.java │ ├── jmod │ └── JmodFile.java │ └── misc │ └── VM.java └── module-info.java /.gitignore: -------------------------------------------------------------------------------- 1 | .project 2 | .settings 3 | .classpath 4 | /target 5 | /target-eclipse 6 | .DS_Store 7 | dependency-reduced-pom.xml 8 | *~ 9 | *~~ 10 | pom.xml.releaseBackup 11 | /release.properties 12 | /coding-style 13 | .pmd 14 | .pmdruleset.xml 15 | interpolated-pom.xml 16 | .eclipse-pmd 17 | .*.swp 18 | .*.swo 19 | .checkstyle 20 | hs_err_pid*.log 21 | .idea 22 | javacore.*.txt 23 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "jdk.compiler/jdk11"] 2 | path = jdk.compiler/jdk11 3 | url = https://github.com/kohlschutter/jdk.compiler.git 4 | [submodule "jdk.compiler/jdk21"] 5 | path = jdk.compiler/jdk21 6 | url = https://github.com/kohlschutter/jdk.compiler.git 7 | branch = standalone-jdk21 8 | -------------------------------------------------------------------------------- /ADDITIONAL_LICENSE_INFO: -------------------------------------------------------------------------------- 1 | ADDITIONAL INFORMATION ABOUT LICENSING 2 | 3 | Certain files distributed by Oracle America, Inc. and/or its affiliates are 4 | subject to the following clarification and special exception to the GPLv2, 5 | based on the GNU Project exception for its Classpath libraries, known as the 6 | GNU Classpath Exception. 7 | 8 | Note that Oracle includes multiple, independent programs in this software 9 | package. Some of those programs are provided under licenses deemed 10 | incompatible with the GPLv2 by the Free Software Foundation and others. 11 | For example, the package includes programs licensed under the Apache 12 | License, Version 2.0 and may include FreeType. Such programs are licensed 13 | to you under their original licenses. 14 | 15 | Oracle facilitates your further distribution of this package by adding the 16 | Classpath Exception to the necessary parts of its GPLv2 code, which permits 17 | you to use that code in combination with other independent modules not 18 | licensed under the GPLv2. However, note that this would not permit you to 19 | commingle code under an incompatible license with Oracle's GPLv2 licensed 20 | code by, for example, cutting and pasting such code into a file also 21 | containing Oracle's GPLv2 licensed code and then distributing the result. 22 | 23 | Additionally, if you were to remove the Classpath Exception from any of the 24 | files to which it applies and distribute the result, you would likely be 25 | required to license some or all of the other code in that distribution under 26 | the GPLv2 as well, and since the GPLv2 is incompatible with the license terms 27 | of some items included in the distribution by Oracle, removing the Classpath 28 | Exception could therefore effectively compromise your ability to further 29 | distribute the package. 30 | 31 | Failing to distribute notices associated with some files may also create 32 | unexpected legal consequences. 33 | 34 | Proceed with caution and we recommend that you obtain the advice of a lawyer 35 | skilled in open source matters before removing the Classpath Exception or 36 | making modifications to this package which may subsequently be redistributed 37 | and/or involve the use of third party software. 38 | -------------------------------------------------------------------------------- /ASSEMBLY_EXCEPTION: -------------------------------------------------------------------------------- 1 | 2 | OPENJDK ASSEMBLY EXCEPTION 3 | 4 | The OpenJDK source code made available by Oracle America, Inc. (Oracle) at 5 | openjdk.org ("OpenJDK Code") is distributed under the terms of the GNU 6 | General Public License version 2 7 | only ("GPL2"), with the following clarification and special exception. 8 | 9 | Linking this OpenJDK Code statically or dynamically with other code 10 | is making a combined work based on this library. Thus, the terms 11 | and conditions of GPL2 cover the whole combination. 12 | 13 | As a special exception, Oracle gives you permission to link this 14 | OpenJDK Code with certain code licensed by Oracle as indicated at 15 | https://openjdk.org/legal/exception-modules-2007-05-08.html 16 | ("Designated Exception Modules") to produce an executable, 17 | regardless of the license terms of the Designated Exception Modules, 18 | and to copy and distribute the resulting executable under GPL2, 19 | provided that the Designated Exception Modules continue to be 20 | governed by the licenses under which they were offered by Oracle. 21 | 22 | As such, it allows licensees and sublicensees of Oracle's GPL2 OpenJDK Code 23 | to build an executable that includes those portions of necessary code that 24 | Oracle could not provide under GPL2 (or that Oracle has provided under GPL2 25 | with the Classpath exception). If you modify or add to the OpenJDK code, 26 | that new GPL2 code may still be combined with Designated Exception Modules 27 | if the new code is made subject to this exception by its copyright holder. 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The GNU General Public License (GPL) 2 | 3 | Version 2, June 1991 4 | 5 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 6 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this license 9 | document, but changing it is not allowed. 10 | 11 | Preamble 12 | 13 | The licenses for most software are designed to take away your freedom to share 14 | and change it. By contrast, the GNU General Public License is intended to 15 | guarantee your freedom to share and change free software--to make sure the 16 | software is free for all its users. This General Public License applies to 17 | most of the Free Software Foundation's software and to any other program whose 18 | authors commit to using it. (Some other Free Software Foundation software is 19 | covered by the GNU Library General Public License instead.) You can apply it to 20 | your programs, too. 21 | 22 | When we speak of free software, we are referring to freedom, not price. Our 23 | General Public Licenses are designed to make sure that you have the freedom to 24 | distribute copies of free software (and charge for this service if you wish), 25 | that you receive source code or can get it if you want it, that you can change 26 | the software or use pieces of it in new free programs; and that you know you 27 | can do these things. 28 | 29 | To protect your rights, we need to make restrictions that forbid anyone to deny 30 | you these rights or to ask you to surrender the rights. These restrictions 31 | translate to certain responsibilities for you if you distribute copies of the 32 | software, or if you modify it. 33 | 34 | For example, if you distribute copies of such a program, whether gratis or for 35 | a fee, you must give the recipients all the rights that you have. You must 36 | make sure that they, too, receive or can get the source code. And you must 37 | show them these terms so they know their rights. 38 | 39 | We protect your rights with two steps: (1) copyright the software, and (2) 40 | offer you this license which gives you legal permission to copy, distribute 41 | and/or modify the software. 42 | 43 | Also, for each author's protection and ours, we want to make certain that 44 | everyone understands that there is no warranty for this free software. If the 45 | software is modified by someone else and passed on, we want its recipients to 46 | know that what they have is not the original, so that any problems introduced 47 | by others will not reflect on the original authors' reputations. 48 | 49 | Finally, any free program is threatened constantly by software patents. We 50 | wish to avoid the danger that redistributors of a free program will 51 | individually obtain patent licenses, in effect making the program proprietary. 52 | To prevent this, we have made it clear that any patent must be licensed for 53 | everyone's free use or not licensed at all. 54 | 55 | The precise terms and conditions for copying, distribution and modification 56 | follow. 57 | 58 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 59 | 60 | 0. This License applies to any program or other work which contains a notice 61 | placed by the copyright holder saying it may be distributed under the terms of 62 | this General Public License. The "Program", below, refers to any such program 63 | or work, and a "work based on the Program" means either the Program or any 64 | derivative work under copyright law: that is to say, a work containing the 65 | Program or a portion of it, either verbatim or with modifications and/or 66 | translated into another language. (Hereinafter, translation is included 67 | without limitation in the term "modification".) Each licensee is addressed as 68 | "you". 69 | 70 | Activities other than copying, distribution and modification are not covered by 71 | this License; they are outside its scope. The act of running the Program is 72 | not restricted, and the output from the Program is covered only if its contents 73 | constitute a work based on the Program (independent of having been made by 74 | running the Program). Whether that is true depends on what the Program does. 75 | 76 | 1. You may copy and distribute verbatim copies of the Program's source code as 77 | you receive it, in any medium, provided that you conspicuously and 78 | appropriately publish on each copy an appropriate copyright notice and 79 | disclaimer of warranty; keep intact all the notices that refer to this License 80 | and to the absence of any warranty; and give any other recipients of the 81 | Program a copy of this License along with the Program. 82 | 83 | You may charge a fee for the physical act of transferring a copy, and you may 84 | at your option offer warranty protection in exchange for a fee. 85 | 86 | 2. You may modify your copy or copies of the Program or any portion of it, thus 87 | forming a work based on the Program, and copy and distribute such modifications 88 | or work under the terms of Section 1 above, provided that you also meet all of 89 | these conditions: 90 | 91 | a) You must cause the modified files to carry prominent notices stating 92 | that you changed the files and the date of any change. 93 | 94 | b) You must cause any work that you distribute or publish, that in whole or 95 | in part contains or is derived from the Program or any part thereof, to be 96 | licensed as a whole at no charge to all third parties under the terms of 97 | this License. 98 | 99 | c) If the modified program normally reads commands interactively when run, 100 | you must cause it, when started running for such interactive use in the 101 | most ordinary way, to print or display an announcement including an 102 | appropriate copyright notice and a notice that there is no warranty (or 103 | else, saying that you provide a warranty) and that users may redistribute 104 | the program under these conditions, and telling the user how to view a copy 105 | of this License. (Exception: if the Program itself is interactive but does 106 | not normally print such an announcement, your work based on the Program is 107 | not required to print an announcement.) 108 | 109 | These requirements apply to the modified work as a whole. If identifiable 110 | sections of that work are not derived from the Program, and can be reasonably 111 | considered independent and separate works in themselves, then this License, and 112 | its terms, do not apply to those sections when you distribute them as separate 113 | works. But when you distribute the same sections as part of a whole which is a 114 | work based on the Program, the distribution of the whole must be on the terms 115 | of this License, whose permissions for other licensees extend to the entire 116 | whole, and thus to each and every part regardless of who wrote it. 117 | 118 | Thus, it is not the intent of this section to claim rights or contest your 119 | rights to work written entirely by you; rather, the intent is to exercise the 120 | right to control the distribution of derivative or collective works based on 121 | the Program. 122 | 123 | In addition, mere aggregation of another work not based on the Program with the 124 | Program (or with a work based on the Program) on a volume of a storage or 125 | distribution medium does not bring the other work under the scope of this 126 | License. 127 | 128 | 3. You may copy and distribute the Program (or a work based on it, under 129 | Section 2) in object code or executable form under the terms of Sections 1 and 130 | 2 above provided that you also do one of the following: 131 | 132 | a) Accompany it with the complete corresponding machine-readable source 133 | code, which must be distributed under the terms of Sections 1 and 2 above 134 | on a medium customarily used for software interchange; or, 135 | 136 | b) Accompany it with a written offer, valid for at least three years, to 137 | give any third party, for a charge no more than your cost of physically 138 | performing source distribution, a complete machine-readable copy of the 139 | corresponding source code, to be distributed under the terms of Sections 1 140 | and 2 above on a medium customarily used for software interchange; or, 141 | 142 | c) Accompany it with the information you received as to the offer to 143 | distribute corresponding source code. (This alternative is allowed only 144 | for noncommercial distribution and only if you received the program in 145 | object code or executable form with such an offer, in accord with 146 | Subsection b above.) 147 | 148 | The source code for a work means the preferred form of the work for making 149 | modifications to it. For an executable work, complete source code means all 150 | the source code for all modules it contains, plus any associated interface 151 | definition files, plus the scripts used to control compilation and installation 152 | of the executable. However, as a special exception, the source code 153 | distributed need not include anything that is normally distributed (in either 154 | source or binary form) with the major components (compiler, kernel, and so on) 155 | of the operating system on which the executable runs, unless that component 156 | itself accompanies the executable. 157 | 158 | If distribution of executable or object code is made by offering access to copy 159 | from a designated place, then offering equivalent access to copy the source 160 | code from the same place counts as distribution of the source code, even though 161 | third parties are not compelled to copy the source along with the object code. 162 | 163 | 4. You may not copy, modify, sublicense, or distribute the Program except as 164 | expressly provided under this License. Any attempt otherwise to copy, modify, 165 | sublicense or distribute the Program is void, and will automatically terminate 166 | your rights under this License. However, parties who have received copies, or 167 | rights, from you under this License will not have their licenses terminated so 168 | long as such parties remain in full compliance. 169 | 170 | 5. You are not required to accept this License, since you have not signed it. 171 | However, nothing else grants you permission to modify or distribute the Program 172 | or its derivative works. These actions are prohibited by law if you do not 173 | accept this License. Therefore, by modifying or distributing the Program (or 174 | any work based on the Program), you indicate your acceptance of this License to 175 | do so, and all its terms and conditions for copying, distributing or modifying 176 | the Program or works based on it. 177 | 178 | 6. Each time you redistribute the Program (or any work based on the Program), 179 | the recipient automatically receives a license from the original licensor to 180 | copy, distribute or modify the Program subject to these terms and conditions. 181 | You may not impose any further restrictions on the recipients' exercise of the 182 | rights granted herein. You are not responsible for enforcing compliance by 183 | third parties to this License. 184 | 185 | 7. If, as a consequence of a court judgment or allegation of patent 186 | infringement or for any other reason (not limited to patent issues), conditions 187 | are imposed on you (whether by court order, agreement or otherwise) that 188 | contradict the conditions of this License, they do not excuse you from the 189 | conditions of this License. If you cannot distribute so as to satisfy 190 | simultaneously your obligations under this License and any other pertinent 191 | obligations, then as a consequence you may not distribute the Program at all. 192 | For example, if a patent license would not permit royalty-free redistribution 193 | of the Program by all those who receive copies directly or indirectly through 194 | you, then the only way you could satisfy both it and this License would be to 195 | refrain entirely from distribution of the Program. 196 | 197 | If any portion of this section is held invalid or unenforceable under any 198 | particular circumstance, the balance of the section is intended to apply and 199 | the section as a whole is intended to apply in other circumstances. 200 | 201 | It is not the purpose of this section to induce you to infringe any patents or 202 | other property right claims or to contest validity of any such claims; this 203 | section has the sole purpose of protecting the integrity of the free software 204 | distribution system, which is implemented by public license practices. Many 205 | people have made generous contributions to the wide range of software 206 | distributed through that system in reliance on consistent application of that 207 | system; it is up to the author/donor to decide if he or she is willing to 208 | distribute software through any other system and a licensee cannot impose that 209 | choice. 210 | 211 | This section is intended to make thoroughly clear what is believed to be a 212 | consequence of the rest of this License. 213 | 214 | 8. If the distribution and/or use of the Program is restricted in certain 215 | countries either by patents or by copyrighted interfaces, the original 216 | copyright holder who places the Program under this License may add an explicit 217 | geographical distribution limitation excluding those countries, so that 218 | distribution is permitted only in or among countries not thus excluded. In 219 | such case, this License incorporates the limitation as if written in the body 220 | of this License. 221 | 222 | 9. The Free Software Foundation may publish revised and/or new versions of the 223 | General Public License from time to time. Such new versions will be similar in 224 | spirit to the present version, but may differ in detail to address new problems 225 | or concerns. 226 | 227 | Each version is given a distinguishing version number. If the Program 228 | specifies a version number of this License which applies to it and "any later 229 | version", you have the option of following the terms and conditions either of 230 | that version or of any later version published by the Free Software Foundation. 231 | If the Program does not specify a version number of this License, you may 232 | choose any version ever published by the Free Software Foundation. 233 | 234 | 10. If you wish to incorporate parts of the Program into other free programs 235 | whose distribution conditions are different, write to the author to ask for 236 | permission. For software which is copyrighted by the Free Software Foundation, 237 | write to the Free Software Foundation; we sometimes make exceptions for this. 238 | Our decision will be guided by the two goals of preserving the free status of 239 | all derivatives of our free software and of promoting the sharing and reuse of 240 | software generally. 241 | 242 | NO WARRANTY 243 | 244 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR 245 | THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE 246 | STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE 247 | PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, 248 | INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 249 | FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND 250 | PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, 251 | YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 252 | 253 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL 254 | ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE 255 | PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY 256 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR 257 | INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA 258 | BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 259 | FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER 260 | OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 261 | 262 | END OF TERMS AND CONDITIONS 263 | 264 | How to Apply These Terms to Your New Programs 265 | 266 | If you develop a new program, and you want it to be of the greatest possible 267 | use to the public, the best way to achieve this is to make it free software 268 | which everyone can redistribute and change under these terms. 269 | 270 | To do so, attach the following notices to the program. It is safest to attach 271 | them to the start of each source file to most effectively convey the exclusion 272 | of warranty; and each file should have at least the "copyright" line and a 273 | pointer to where the full notice is found. 274 | 275 | One line to give the program's name and a brief idea of what it does. 276 | 277 | Copyright (C) 278 | 279 | This program is free software; you can redistribute it and/or modify it 280 | under the terms of the GNU General Public License as published by the Free 281 | Software Foundation; either version 2 of the License, or (at your option) 282 | any later version. 283 | 284 | This program is distributed in the hope that it will be useful, but WITHOUT 285 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 286 | FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 287 | more details. 288 | 289 | You should have received a copy of the GNU General Public License along 290 | with this program; if not, write to the Free Software Foundation, Inc., 291 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 292 | 293 | Also add information on how to contact you by electronic and paper mail. 294 | 295 | If the program is interactive, make it output a short notice like this when it 296 | starts in an interactive mode: 297 | 298 | Gnomovision version 69, Copyright (C) year name of author Gnomovision comes 299 | with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free 300 | software, and you are welcome to redistribute it under certain conditions; 301 | type 'show c' for details. 302 | 303 | The hypothetical commands 'show w' and 'show c' should show the appropriate 304 | parts of the General Public License. Of course, the commands you use may be 305 | called something other than 'show w' and 'show c'; they could even be 306 | mouse-clicks or menu items--whatever suits your program. 307 | 308 | You should also get your employer (if you work as a programmer) or your school, 309 | if any, to sign a "copyright disclaimer" for the program, if necessary. Here 310 | is a sample; alter the names: 311 | 312 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 313 | 'Gnomovision' (which makes passes at compilers) written by James Hacker. 314 | 315 | signature of Ty Coon, 1 April 1989 316 | 317 | Ty Coon, President of Vice 318 | 319 | This General Public License does not permit incorporating your program into 320 | proprietary programs. If your program is a subroutine library, you may 321 | consider it more useful to permit linking proprietary applications with the 322 | library. If this is what you want to do, use the GNU Library General Public 323 | License instead of this License. 324 | 325 | 326 | "CLASSPATH" EXCEPTION TO THE GPL 327 | 328 | Certain source files distributed by Oracle America and/or its affiliates are 329 | subject to the following clarification and special exception to the GPL, but 330 | only where Oracle has expressly included in the particular source file's header 331 | the words "Oracle designates this particular file as subject to the "Classpath" 332 | exception as provided by Oracle in the LICENSE file that accompanied this code." 333 | 334 | Linking this library statically or dynamically with other modules is making 335 | a combined work based on this library. Thus, the terms and conditions of 336 | the GNU General Public License cover the whole combination. 337 | 338 | As a special exception, the copyright holders of this library give you 339 | permission to link this library with independent modules to produce an 340 | executable, regardless of the license terms of these independent modules, 341 | and to copy and distribute the resulting executable under terms of your 342 | choice, provided that you also meet, for each linked independent module, 343 | the terms and conditions of the license of that module. An independent 344 | module is a module which is not derived from or based on this library. If 345 | you modify this library, you may extend this exception to your version of 346 | the library, but you are not obligated to do so. If you do not wish to do 347 | so, delete this exception statement from your version. 348 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kohlschutter/jdk.compiler.standalone/db16879ce7f4e0ca284c42f6c0f286dfb6cde2cd/NOTICE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Standalone jdk.compiler / Java Compiler Framework + Tree API 2 | 3 | ## What 4 | 5 | This repository builds standalone `jdk.compiler` artifacts (the Java code behind `javac`, etc.) 6 | that can be used just like other regular Maven dependencies. 7 | 8 | ## Why 9 | 10 | ### Motivation 11 | 12 | A typical way of using the Java Compiler API is to rely on the presence of such API in the Java VM 13 | that runs the code requiring it. 14 | 15 | Starting with Java 16, using the internals of the Java Compiler API requires a series of 16 | `--add-opens` incantations to the running VM, such as: 17 | 18 | ``` 19 | --add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED 20 | --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED 21 | --add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED 22 | --add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED 23 | ``` 24 | etc. 25 | 26 | The reason is clear: Eventually, we cannot rely on the presence of said API in the JDK, since 27 | `jdk.compiler` is an internal module that we really shouldn't touch. 28 | 29 | Since the Compiler Framework code is licensed as GPLv2+Classpath-Exception, let's make it 30 | a separate component that we can use regardless of what's available in the 31 | current VM! 32 | 33 | ### Benefits 34 | 35 | With this standalone compiler, you can rely on all Java 11 javac features to be available, 36 | even when using newer Java versions. 37 | 38 | Specifically, this is allows you to: 39 | 40 | - compile code even if your Java environment isn't a full JDK (Java JRE, for example!) 41 | - target Java 1.7 for compilation without any warnings or restrictions. 42 | - parse and compile Java 21 source code from Java 11 or newer 43 | - use the Compiler Tree API without resorting to `--add-opens` trickery that may eventually fail 44 | in newer Java releases 45 | - build a modified compiler with additional features or custom tweaks 46 | 47 | ### Examples 48 | 49 | See this [jsweet fork](https://github.com/kohlschutter/jsweet). jsweet makes exhaustive use of 50 | the Compiler Tree API, and now we can run it in Java 17 without prying open some JDK internals. 51 | 52 | ## How 53 | 54 | ### Usage 55 | 56 | First, add one of the following Maven dependencies (but not both) to your project: 57 | 58 | For the Java 11 compiler: 59 | 60 | ```xml 61 | 62 | com.kohlschutter.jdk.compiler 63 | standalone-jdk11 64 | 1.1.3 65 | 66 | ``` 67 | 68 | For the Java 21 compiler (which runs on Java 11 or newer, even with JRE-only): 69 | 70 | ```xml 71 | 72 | com.kohlschutter.jdk.compiler 73 | standalone-jdk11 74 | 1.1.3 75 | 76 | ``` 77 | 78 | If your project is modularized, also add the following statements to your `module-info.java`: 79 | 80 | ``` 81 | requires standalone.jdk.compiler; 82 | requires com.kohlschutter.jdk.standaloneutil; 83 | ``` 84 | 85 | This gives you access to all `com.sun.tools.*` and `com.sun.source.*` packages, however they are 86 | actually prefixed by `standalone.`, i.e., `standalone.com.sun.tools.*`, etc. 87 | 88 | So you need to change your code to use `standalone.com.sun.`... instead of `com.sun.`... 89 | For example use `standalone.com.sun.tools.javac.api.JavacTool` instead of 90 | `com.sun.tools.javac.api.JavacTool`. 91 | 92 | If you use `javax.tools.ToolProvider.getSystemJavaCompiler()`, change this to our own version: 93 | `com.kohlschutter.jdk.standaloneutil.ToolProvider.getSystemJavaCompiler()` (or just modify the 94 | `import` statement). 95 | 96 | The original Compiler framework refers to certain files from the JDK's home directory, specifically 97 | `lib/modules` (which contains all default `modules`), as well as `lib/ct.sym`, which contains the 98 | API fingerprints to support `-release` compatibility checks. 99 | 100 | The standalone compiler uses its own copies for both `lib/modules` contents as well as `lib/ct.sym` 101 | from a recent JDK 11 java home directory, which is automatically included via the 102 | `com.kohlschutter.jdk:standalone-home` dependency. 103 | 104 | ### Project setup and structure 105 | 106 | The code in this repository relies on copies of the "jdk.compiler" code 107 | obtained from Open Source Java JDKs (for example, Eclipse Temurin). 108 | 109 | These copies reside in a separate repository and are added as submodules to this project. 110 | 111 | Each submodule refers to a different JDK version, which allows the creation of 112 | multiple Maven artifacts, one for each major JDK version. 113 | 114 | ### Limitations 115 | 116 | To use this artifact, you currently need Java 11 or better. 117 | 118 | ### Building from source 119 | 120 | Clone this repository, initialize submodules and build: 121 | 122 | ``` 123 | git clone https://github.com/kohlschutter/jdk.compiler.standalone.git 124 | cd jdk.compiler.standalone.git 125 | git submodule update --init 126 | mvn clean install 127 | ``` 128 | 129 | Also see [jdk.compiler.home](https://github.com/kohlschutter/jdk.compiler.home) for the 130 | corresponding JDK home artifact. 131 | 132 | ### Executable jar with embedded dependencies 133 | 134 | Executable jar files (`java -jar ...`) are built with `-Djar-with-dependencies` enabled: 135 | 136 | ``` 137 | mvn clean install -Djar-with-dependencies 138 | ``` 139 | 140 | Executable jars are then placed in 141 | `standalone-jdk11/target/standalone-jdk11-VERSION-jar-with-dependencies.jar` (for JDK11) and 142 | `standalone-jdk21/target/standalone-jdk21-VERSION-jar-with-dependencies.jar` (for JDK21). 143 | 144 | ### GraalVM native image 145 | 146 | Executable [GraalVM native images](https://www.graalvm.org/22.0/reference-manual/native-image/) 147 | are built with `-Dnative` enabled (this expects `GRAALVM_HOME` to be set to the home directory of 148 | a recent GraalVM SDK): 149 | 150 | ``` 151 | mvn clean install -Dnative 152 | ``` 153 | 154 | Executables are then placed in 155 | `standalone-jdk11/target/standalone-jdk11-VERSION-javac` (for JDK11) and 156 | `standalone-jdk21/target/standalone-jdk21-VERSION-javac` (for JDK21). 157 | 158 | ### Compiling itself 159 | 160 | #### Using native-image 161 | Obtain the GraalVM native-image from the step above and copy it to an external path, e.g. 162 | `$HOME/standalone-jdk21-javac`. If you want to compile everything, you need the `jdk21` version, 163 | otherwise `jdk11` works as well. 164 | 165 | All you then need is to specify the path to this binary when building the project: 166 | 167 | ``` 168 | mvn clean install -Dcustom-javac=$HOME/standalone-jdk21-javac 169 | ``` 170 | 171 | #### Using jar-with-dependencies 172 | 173 | You can also use the regular jar-with-dependencies jars, but then you need to write a little 174 | wrapper script that can be executed by Maven: 175 | 176 | ``` 177 | #!/bin/sh 178 | 179 | /path/to/some/java-home-directory/bin/java -jar $HOME/standalone-jdk21-jar-with-dependencies.jar $@ 180 | ``` 181 | 182 | Save the script to an external place like `$HOME/standalone-jdk21-javac-jar-with-dependencies` 183 | and build the project: 184 | 185 | ``` 186 | mvn clean install -Dcustom-javac=$HOME/standalone-jdk21-javac-jar-with-dependencies 187 | ``` 188 | 189 | You can experiment with using different Java JDKs/JREs to host the compiler. Anything Java 11 or 190 | newer should work. 191 | 192 | ## When 193 | 194 | ### Future enhancements 195 | 196 | - We could support multiple different compiler versions to run side-by-side in the same VM. 197 | - We could build `javac` binaries (via GraalVM native-image) with custom configurations 198 | - This approach may be used for other jdk-internal components as well. 199 | 200 | If you have an idea, please reach out! 201 | 202 | ### Changelog 203 | 204 | #### _(2023-10-14)_ jdk.compiler.standalone 1.1.3 205 | 206 | - Fix Maven POM setup that would prevent getting the correct dependencies in other projects, take 2 207 | - No longer build jar-with-dependencies by default 208 | 209 | #### _(2023-10-14)_ jdk.compiler.standalone 1.1.2 210 | 211 | - Fix Maven POM setup that would prevent getting the correct dependencies in other projects 212 | 213 | #### _(2023-10-14)_ jdk.compiler.standalone 1.1.1 214 | 215 | - Open all exported packages 216 | 217 | #### _(2023-10-13)_ jdk.compiler.standalone 1.1.0 218 | 219 | - Add the compiler from JDK21, and backport it to Java 11 (!). 220 | - Add GraalVM native-image support. 221 | - Add self-contained jar-with-dependencies for both JDK11 and JDK21. 222 | - Various fixes. The standalone compiler can now compile itself. 223 | 224 | #### _(2023-10-10)_ jdk.compiler.standalone 1.0.0 225 | 226 | - Initial release. 227 | 228 | ## Who 229 | 230 | This repository has been put together by [Christian Kohlschütter](https://kohlschuetter.github.io/blog/). 231 | 232 | ### Getting involved 233 | 234 | If you encounter a bug, please file a [bug report](https://github.com/kohlschutter/jdk.compiler.standalone/issues). 235 | 236 | If you want to contribute, please open a [pull request](https://github.com/kohlschutter/jdk.compiler.standalone/pulls) 237 | or reach out directly. 238 | 239 | ### License 240 | 241 | The code itself carries the original license, GNU General Public License 242 | version 2 only, subject to the "Classpath" exception as provided in 243 | the LICENSE file that accompanies this code. 244 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | com.kohlschutter 8 | kohlschutter-parent 9 | 1.6.4 10 | 11 | 12 | com.kohlschutter.jdk.compiler 13 | standalone 14 | 1.1.3 15 | pom 16 | 17 | 18 | 19 | GPL-2.0-with-classpath-exception 20 | https://www.gnu.org/software/classpath/license.html 21 | repo 22 | 23 | 24 | 25 | 26 | https://github.com/kohlschutter/jdk.compiler.standalone 27 | 28 | scm:git:git@github.com:kohlschutter/jdk.compiler.standalone.git 29 | 30 | scm:git:git@github.com:kohlschutter/jdk.compiler.standalone.git 31 | 32 | 33 | 34 | ossrh 35 | https://oss.sonatype.org/content/repositories/snapshots 36 | 37 | 38 | github.com/kohlschutter/jdk.compiler.standalone 39 | gitsite:git@github.com/kohlschutter/jdk.compiler.standalone.git 40 | 41 | 42 | 43 | GitHub 44 | https://github.com/kohlschutter/jdk.compiler.standalone/issues 45 | 46 | 47 | 48 | com.kohlschutter 49 | compiler-annotations 50 | provided 51 | 52 | 53 | 54 | 55 | 56 | native 57 | 58 | 59 | native 60 | 61 | 62 | 63 | 64 | 65 | org.graalvm.buildtools 66 | native-maven-plugin 67 | true 68 | 69 | 70 | 71 | 72 | 73 | 74 | org.graalvm.buildtools 75 | native-maven-plugin 76 | 0.9.27 77 | 78 | 79 | 80 | 81 | 82 | 83 | custom-javac 84 | 85 | 86 | custom-javac 87 | 88 | 89 | 90 | 91 | 92 | maven-compiler-plugin 93 | 94 | true 95 | ${custom-javac} 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | com.kohlschutter.jdk 107 | standalone-util 108 | ${project.version} 109 | compile 110 | 111 | 112 | com.kohlschutter.jdk.compiler 113 | standalone-jdk11 114 | ${project.version} 115 | compile 116 | 117 | 118 | com.kohlschutter.jdk.compiler 119 | standalone-jdk21 120 | ${project.version} 121 | compile 122 | 123 | 124 | 125 | 126 | 127 | standalone-util 128 | standalone-jdk11 129 | standalone-jdk11-test 130 | standalone-jdk21 131 | standalone-jdk21-test 132 | 133 | 134 | -------------------------------------------------------------------------------- /standalone-jdk11-test/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /target-eclipse/ 3 | -------------------------------------------------------------------------------- /standalone-jdk11-test/NOTICE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kohlschutter/jdk.compiler.standalone/db16879ce7f4e0ca284c42f6c0f286dfb6cde2cd/standalone-jdk11-test/NOTICE -------------------------------------------------------------------------------- /standalone-jdk11-test/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | com.kohlschutter.jdk.compiler 8 | standalone 9 | 1.1.3 10 | ../pom.xml 11 | 12 | 13 | standalone-jdk11-test 14 | 15 | 11 16 | UTF-8 17 | UTF-8 18 | UTF-8 19 | UTF-8 20 | 21 | 22 | 23 | 24 | native 25 | 26 | 27 | native 28 | 29 | 30 | 31 | 32 | 33 | org.graalvm.buildtools 34 | native-maven-plugin 35 | true 36 | 37 | 38 | true 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | com.kohlschutter.jdk.compiler 50 | standalone-jdk11 51 | 52 | 53 | 54 | com.kohlschutter 55 | kohlschutter-test-util 56 | test 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /standalone-jdk11-test/src/main/java/com/kohlschutter/jdk/test/Placeholder.java: -------------------------------------------------------------------------------- 1 | package com.kohlschutter.jdk.test; 2 | 3 | interface Placeholder { 4 | // an empty interface to keep some plugins happy 5 | } 6 | -------------------------------------------------------------------------------- /standalone-jdk11-test/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module com.kohlschutter.jdk.standalonetest { 2 | requires standalone.jdk.compiler; 3 | } 4 | -------------------------------------------------------------------------------- /standalone-jdk11-test/src/test/java/com/kohlschutter/jdk/test/CompilerTest.java: -------------------------------------------------------------------------------- 1 | package com.kohlschutter.jdk.test; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | import java.io.PrintWriter; 10 | import java.nio.charset.StandardCharsets; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | import org.junit.jupiter.api.Test; 17 | 18 | import standalone.com.sun.tools.javac.main.JavaCompiler; 19 | 20 | public class CompilerTest { 21 | @Test 22 | public void testCompilerVersion() throws Exception { 23 | assertEquals("11", JavaCompiler.version().split("\\.")[0]); 24 | } 25 | 26 | private void testCompileSimple(String version, boolean release, int expectedRc) 27 | throws IOException { 28 | Path tmpDir = Files.createTempDirectory("standaloneJavacTest"); 29 | Path helloJava = tmpDir.resolve("Hello.java"); 30 | Path helloClass = tmpDir.resolve("Hello.class"); 31 | try { 32 | try (PrintWriter pw = new PrintWriter(new FileOutputStream(helloJava.toFile()), true, 33 | StandardCharsets.UTF_8)) { 34 | pw.println("public class Hello {"); 35 | pw.println(" public static void main(String[] args) throws Exception {"); 36 | pw.println(" System.out.println(\"Hello World\");"); 37 | pw.println(" }"); 38 | pw.println("}"); 39 | } 40 | 41 | System.out.println(tmpDir); 42 | 43 | List args = new ArrayList<>(); 44 | if (version != null) { 45 | if (release) { 46 | args.addAll(List.of("--release", version)); 47 | } else { 48 | args.addAll(List.of("-source", version, "-target", version)); 49 | } 50 | } 51 | args.add(helloJava.toAbsolutePath().toString()); 52 | 53 | int rc = standalone.com.sun.tools.javac.Main.compile(args.toArray(new String[0])); 54 | assertEquals(expectedRc, rc); 55 | 56 | if (rc == 0) { 57 | assertTrue(Files.exists(helloClass)); 58 | } else { 59 | assertFalse(Files.exists(helloClass)); 60 | } 61 | } finally { 62 | Files.deleteIfExists(helloClass); 63 | Files.deleteIfExists(helloJava); 64 | Files.delete(tmpDir); 65 | } 66 | } 67 | 68 | @Test 69 | public void testCompileDefault() throws Exception { 70 | testCompileSimple(null, false, 0); 71 | } 72 | 73 | @Test 74 | public void testCompileJava7() throws Exception { 75 | testCompileSimple("1.7", false, 0); 76 | } 77 | 78 | @Test 79 | public void testCompileJava8() throws Exception { 80 | testCompileSimple("1.8", false, 0); 81 | } 82 | 83 | @Test 84 | public void testCompileJava11() throws Exception { 85 | testCompileSimple("11", true, 0); 86 | } 87 | 88 | @Test 89 | public void testCompileJava12() throws Exception { 90 | testCompileSimple("12", true, 2); // not supported 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /standalone-jdk11/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /target-eclipse/ 3 | -------------------------------------------------------------------------------- /standalone-jdk11/NOTICE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kohlschutter/jdk.compiler.standalone/db16879ce7f4e0ca284c42f6c0f286dfb6cde2cd/standalone-jdk11/NOTICE -------------------------------------------------------------------------------- /standalone-jdk11/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | com.kohlschutter.jdk.compiler 8 | standalone 9 | 1.1.3 10 | ../pom.xml 11 | 12 | 13 | standalone-jdk11 14 | 15 | 16 | 17 | GPL-2.0-with-classpath-exception 18 | https://www.gnu.org/software/classpath/license.html 19 | repo 20 | 21 | 22 | 23 | 24 | 11 25 | UTF-8 26 | UTF-8 27 | UTF-8 28 | UTF-8 29 | -Xlint:none 30 | true 31 | false 32 | 33 | 34 | 35 | 36 | 37 | de.thetaphi 38 | forbiddenapis 39 | 40 | 41 | default 42 | 43 | check 44 | 45 | skip 46 | 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-checkstyle-plugin 52 | 53 | 54 | checkstyle-during-compile 55 | 56 | check 57 | 58 | skip 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-pmd-plugin 65 | 66 | 67 | default-check 68 | 69 | check 70 | 71 | skip 72 | 73 | 74 | default-cpd-check 75 | 76 | cpd-check 77 | 78 | skip 79 | 80 | 81 | 82 | 83 | com.github.spotbugs 84 | spotbugs-maven-plugin 85 | 86 | 87 | spotbugs-during-install 88 | 89 | check 90 | 91 | skip 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | native 101 | 102 | 103 | native 104 | 105 | 106 | 107 | 108 | 109 | org.graalvm.buildtools 110 | native-maven-plugin 111 | true 112 | 113 | 114 | build-native 115 | package 116 | 117 | compile-no-fork 118 | 119 | 120 | 121 | standalone.com.sun.tools.javac.Main 122 | 123 | 124 | 125 | 126 | false 127 | false 128 | ${project.name}-${project.version}-javac 129 | false 130 | 131 | 132 | 133 | 134 | 135 | 136 | jar-with-dependencies 137 | 138 | 139 | jar-with-dependencies 140 | 141 | 142 | 143 | 144 | 145 | maven-shade-plugin 146 | 147 | 148 | jar-with-dependencies 149 | package 150 | 151 | shade 152 | 153 | 154 | false 155 | true 156 | jar-with-dependencies 157 | 158 | 160 | 161 | standalone.com.sun.tools.javac.Main 162 | 163 | 165 | 166 | false 167 | 168 | 169 | *:* 170 | 171 | /logging.properties 172 | META-INF/LICENSE 173 | META-INF/*.md 174 | META-INF/*.MF 175 | META-INF/*.SF 176 | META-INF/*.DSA 177 | META-INF/*.RSA 178 | 179 | META-INF/*.kotlin_module 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | com.kohlschutter.jdk 195 | standalone-util 196 | 197 | 198 | com.kohlschutter.jdk 199 | standalone-home 200 | 1.1.0 201 | jdk11 202 | 203 | 204 | 205 | -------------------------------------------------------------------------------- /standalone-jdk11/src/main/java: -------------------------------------------------------------------------------- 1 | ../../../jdk.compiler/jdk11/java -------------------------------------------------------------------------------- /standalone-jdk11/src/main/resources/META-INF/native-image/com.kohlschutter.jdk.compiler/standalone-jdk11/resource-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources": { 3 | "includes": [ 4 | { 5 | "pattern": "\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" 6 | }, 7 | { 8 | "pattern": "\\QMETA-INF/services/java.nio.file.spi.FileSystemProvider\\E" 9 | }, 10 | { 11 | "pattern": "\\QMETA-INF/services/javax.annotation.processing.Processor\\E" 12 | }, 13 | { 14 | "pattern": "\\QMETA-INF/services/standalone.com.sun.tools.javac.platform.PlatformProvider\\E" 15 | } 16 | ] 17 | }, 18 | "bundles": [ 19 | { 20 | "name": "standalone.com.sun.tools.javac.resources.compiler", 21 | "classNames": [ 22 | "standalone.com.sun.tools.javac.resources.compiler" 23 | ] 24 | }, 25 | { 26 | "name": "standalone.com.sun.tools.javac.resources.ct", 27 | "classNames": [ 28 | "standalone.com.sun.tools.javac.resources.ct" 29 | ] 30 | }, 31 | { 32 | "name": "standalone.com.sun.tools.javac.resources.javac", 33 | "classNames": [ 34 | "standalone.com.sun.tools.javac.resources.javac" 35 | ] 36 | }, 37 | { 38 | "name": "standalone.com.sun.tools.javac.resources.version", 39 | "classNames": [ 40 | "standalone.com.sun.tools.javac.resources.version" 41 | ] 42 | } 43 | ] 44 | } -------------------------------------------------------------------------------- /standalone-jdk21-test/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /target-eclipse/ 3 | -------------------------------------------------------------------------------- /standalone-jdk21-test/NOTICE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kohlschutter/jdk.compiler.standalone/db16879ce7f4e0ca284c42f6c0f286dfb6cde2cd/standalone-jdk21-test/NOTICE -------------------------------------------------------------------------------- /standalone-jdk21-test/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | com.kohlschutter.jdk.compiler 8 | standalone 9 | 1.1.3 10 | ../pom.xml 11 | 12 | 13 | standalone-jdk21-test 14 | 15 | 11 16 | UTF-8 17 | UTF-8 18 | UTF-8 19 | UTF-8 20 | 21 | 22 | 23 | 24 | native 25 | 26 | 27 | native 28 | 29 | 30 | 31 | 32 | 33 | org.graalvm.buildtools 34 | native-maven-plugin 35 | true 36 | 37 | 38 | true 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | com.kohlschutter.jdk.compiler 50 | standalone-jdk21 51 | 52 | 53 | com.kohlschutter 54 | kohlschutter-test-util 55 | test 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /standalone-jdk21-test/src/main/java/com/kohlschutter/jdk/test/Placeholder.java: -------------------------------------------------------------------------------- 1 | package com.kohlschutter.jdk.test; 2 | 3 | interface Placeholder { 4 | // an empty interface to keep some plugins happy 5 | } 6 | -------------------------------------------------------------------------------- /standalone-jdk21-test/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module com.kohlschutter.jdk.standalonetest { 2 | requires standalone.jdk.compiler; 3 | } 4 | -------------------------------------------------------------------------------- /standalone-jdk21-test/src/test/java/com/kohlschutter/jdk/test/CompilerTest.java: -------------------------------------------------------------------------------- 1 | package com.kohlschutter.jdk.test; 2 | 3 | import static org.junit.jupiter.api.Assertions.assertEquals; 4 | import static org.junit.jupiter.api.Assertions.assertFalse; 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | import java.io.FileOutputStream; 8 | import java.io.IOException; 9 | import java.io.PrintWriter; 10 | import java.nio.charset.StandardCharsets; 11 | import java.nio.file.Files; 12 | import java.nio.file.Path; 13 | 14 | import org.junit.jupiter.api.Test; 15 | 16 | import standalone.com.sun.tools.javac.main.JavaCompiler; 17 | 18 | public class CompilerTest { 19 | @Test 20 | public void testCompilerVersion() throws Exception { 21 | assertEquals("21", JavaCompiler.version().split("\\.")[0]); 22 | } 23 | 24 | private void testCompileSimple(String version, int expectedRc) throws IOException { 25 | Path tmpDir = Files.createTempDirectory("standaloneJavacTest"); 26 | Path helloJava = tmpDir.resolve("Hello.java"); 27 | Path helloClass = tmpDir.resolve("Hello.class"); 28 | try { 29 | try (PrintWriter pw = new PrintWriter(new FileOutputStream(helloJava.toFile()), true, 30 | StandardCharsets.UTF_8)) { 31 | pw.println("public class Hello {"); 32 | pw.println(" public static void main(String[] args) throws Exception {"); 33 | pw.println(" System.out.println(\"Hello World\");"); 34 | pw.println(" }"); 35 | pw.println("}"); 36 | } 37 | 38 | System.out.println(tmpDir); 39 | 40 | int rc = standalone.com.sun.tools.javac.Main.compile(new String[] { 41 | "-source", version, // 42 | "-target", version, // 43 | helloJava.toAbsolutePath().toString()}); 44 | assertEquals(expectedRc, rc); 45 | 46 | if (rc == 0) { 47 | assertTrue(Files.exists(helloClass)); 48 | } else { 49 | assertFalse(Files.exists(helloClass)); 50 | } 51 | } finally { 52 | Files.deleteIfExists(helloClass); 53 | Files.deleteIfExists(helloJava); 54 | Files.delete(tmpDir); 55 | } 56 | } 57 | 58 | @Test 59 | public void testCompileJava7() throws Exception { 60 | testCompileSimple("1.7", 2); // no longer supported, so compilation should fail 61 | } 62 | 63 | @Test 64 | public void testCompileJava8() throws Exception { 65 | testCompileSimple("1.8", 0); 66 | } 67 | 68 | @Test 69 | public void testCompileJava21Unnamed() throws Exception { 70 | Path tmpDir = Files.createTempDirectory("standaloneJavacTest"); 71 | Path helloJava = tmpDir.resolve("Hello.java"); 72 | Path helloClass = tmpDir.resolve("Hello.class"); 73 | try { 74 | try (PrintWriter pw = new PrintWriter(new FileOutputStream(helloJava.toFile()), true, 75 | StandardCharsets.UTF_8)) { 76 | // Look ma, no package 77 | pw.println(" void main() {"); 78 | pw.println(" System.out.println(\"Hello World\");"); 79 | pw.println(" }"); 80 | } 81 | 82 | System.out.println(tmpDir); 83 | 84 | int rc = standalone.com.sun.tools.javac.Main.compile(new String[] { 85 | "--enable-preview", // 86 | "-source", "21", // 87 | "-target", "21", // 88 | helloJava.toAbsolutePath().toString()}); 89 | assertEquals(0, rc); 90 | 91 | assertTrue(Files.exists(helloClass)); 92 | } finally { 93 | Files.deleteIfExists(helloClass); 94 | Files.deleteIfExists(helloJava); 95 | Files.delete(tmpDir); 96 | } 97 | } 98 | 99 | @Test 100 | public void testCompileJava17Sealed() throws Exception { 101 | Path tmpDir = Files.createTempDirectory("standaloneJavacTest"); 102 | Path helloJava = tmpDir.resolve("Test.java"); 103 | Path helloClass = tmpDir.resolve("Test.class"); 104 | try { 105 | try (PrintWriter pw = new PrintWriter(new FileOutputStream(helloJava.toFile()), true, 106 | StandardCharsets.UTF_8)) { 107 | pw.println("public class Test {"); 108 | pw.println(" public sealed class Hello permits World {"); 109 | pw.println(" }"); 110 | pw.println(" public final class World extends Hello {"); 111 | pw.println(" }"); 112 | pw.println("}"); 113 | } 114 | 115 | System.out.println(tmpDir); 116 | 117 | int rc = standalone.com.sun.tools.javac.Main.compile(new String[] { 118 | "-source", "17", // 119 | "-target", "17", // 120 | helloJava.toAbsolutePath().toString()}); 121 | assertEquals(0, rc); 122 | 123 | assertTrue(Files.exists(helloClass)); 124 | } finally { 125 | Files.deleteIfExists(helloClass); 126 | Files.deleteIfExists(helloJava); 127 | Files.deleteIfExists(tmpDir.resolve("Test$Hello.class")); 128 | Files.deleteIfExists(tmpDir.resolve("Test$World.class")); 129 | Files.delete(tmpDir); 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /standalone-jdk21/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /target-eclipse/ 3 | -------------------------------------------------------------------------------- /standalone-jdk21/NOTICE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kohlschutter/jdk.compiler.standalone/db16879ce7f4e0ca284c42f6c0f286dfb6cde2cd/standalone-jdk21/NOTICE -------------------------------------------------------------------------------- /standalone-jdk21/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | com.kohlschutter.jdk.compiler 8 | standalone 9 | 1.1.3 10 | ../pom.xml 11 | 12 | 13 | standalone-jdk21 14 | 15 | 16 | 17 | GPL-2.0-with-classpath-exception 18 | https://www.gnu.org/software/classpath/license.html 19 | repo 20 | 21 | 22 | 23 | 24 | 11 25 | UTF-8 26 | UTF-8 27 | UTF-8 28 | UTF-8 29 | -Xlint:none 30 | true 31 | false 32 | 33 | 34 | 35 | 36 | 37 | de.thetaphi 38 | forbiddenapis 39 | 40 | 41 | default 42 | 43 | check 44 | 45 | skip 46 | 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-checkstyle-plugin 52 | 53 | 54 | checkstyle-during-compile 55 | 56 | check 57 | 58 | skip 59 | 60 | 61 | 62 | 63 | org.apache.maven.plugins 64 | maven-pmd-plugin 65 | 66 | 67 | default-check 68 | 69 | check 70 | 71 | skip 72 | 73 | 74 | default-cpd-check 75 | 76 | cpd-check 77 | 78 | skip 79 | 80 | 81 | 82 | 83 | com.github.spotbugs 84 | spotbugs-maven-plugin 85 | 86 | 87 | spotbugs-during-install 88 | 89 | check 90 | 91 | skip 92 | 93 | 94 | 95 | 96 | 97 | maven-compiler-plugin 98 | 99 | 100 | 101 | java21-compile 102 | 103 | compile 104 | 105 | 106 | 107 | ${project.build.outputDirectory}/META-INF/versions/21 108 | 21 109 | false 110 | 111 | 112 | ${project.basedir}/src/main/java21 113 | 114 | 115 | 116 | 117 | 118 | java18-compile 119 | 120 | compile 121 | 122 | 123 | 124 | ${project.build.outputDirectory}/META-INF/versions/18 125 | 18 126 | false 127 | 128 | 129 | ${project.basedir}/src/main/java18 130 | 131 | 132 | 133 | 134 | 135 | java16-compile 136 | 137 | compile 138 | 139 | 140 | 141 | ${project.build.outputDirectory}/META-INF/versions/16 142 | 16 143 | false 144 | 145 | 146 | ${project.basedir}/src/main/java16 147 | 148 | 149 | 150 | 151 | 152 | java15-compile 153 | 154 | compile 155 | 156 | 157 | 158 | ${project.build.outputDirectory}/META-INF/versions/15 159 | 15 160 | false 161 | 162 | 163 | ${project.basedir}/src/main/java15 164 | 165 | 166 | 167 | 168 | 169 | java14-compile 170 | 171 | compile 172 | 173 | 174 | 175 | ${project.build.outputDirectory}/META-INF/versions/14 176 | 14 177 | false 178 | 179 | 180 | ${project.basedir}/src/main/java14 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | native 192 | 193 | 194 | native 195 | 196 | 197 | 198 | 199 | 200 | org.graalvm.buildtools 201 | native-maven-plugin 202 | true 203 | 204 | 205 | build-native 206 | package 207 | 208 | compile-no-fork 209 | 210 | 211 | 212 | standalone.com.sun.tools.javac.Main 213 | 214 | 215 | 216 | 217 | false 218 | false 219 | ${project.name}-${project.version}-javac 220 | false 221 | 222 | 223 | 224 | 225 | 226 | 227 | jar-with-dependencies 228 | 229 | 230 | jar-with-dependencies 231 | 232 | 233 | 234 | 235 | 236 | maven-shade-plugin 237 | 238 | 239 | jar-with-dependencies 240 | package 241 | 242 | shade 243 | 244 | 245 | false 246 | true 247 | jar-with-dependencies 248 | 249 | 251 | 252 | standalone.com.sun.tools.javac.Main 253 | 254 | 256 | 257 | false 258 | 259 | 260 | *:* 261 | 262 | /logging.properties 263 | META-INF/LICENSE 264 | META-INF/*.md 265 | META-INF/*.MF 266 | META-INF/*.SF 267 | META-INF/*.DSA 268 | META-INF/*.RSA 269 | 270 | META-INF/*.kotlin_module 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | com.kohlschutter.jdk 286 | standalone-util 287 | 288 | 289 | com.kohlschutter.jdk 290 | standalone-home 291 | 1.1.0 292 | jdk21 293 | 294 | 295 | 296 | -------------------------------------------------------------------------------- /standalone-jdk21/src/main/java: -------------------------------------------------------------------------------- 1 | ../../../jdk.compiler/jdk21/java -------------------------------------------------------------------------------- /standalone-jdk21/src/main/java14: -------------------------------------------------------------------------------- 1 | ../../../jdk.compiler/jdk21/java14 -------------------------------------------------------------------------------- /standalone-jdk21/src/main/java15: -------------------------------------------------------------------------------- 1 | ../../../jdk.compiler/jdk21/java15 -------------------------------------------------------------------------------- /standalone-jdk21/src/main/java16: -------------------------------------------------------------------------------- 1 | ../../../jdk.compiler/jdk21/java16 -------------------------------------------------------------------------------- /standalone-jdk21/src/main/java18: -------------------------------------------------------------------------------- 1 | ../../../jdk.compiler/jdk21/java18 -------------------------------------------------------------------------------- /standalone-jdk21/src/main/java21: -------------------------------------------------------------------------------- 1 | ../../../jdk.compiler/jdk21/java21 -------------------------------------------------------------------------------- /standalone-jdk21/src/main/resources/META-INF/native-image/com.kohlschutter.jdk.compiler/standalone-jdk21/resource-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "resources": { 3 | "includes": [ 4 | { 5 | "pattern": "\\QMETA-INF/services/java.lang.System$LoggerFinder\\E" 6 | }, 7 | { 8 | "pattern": "\\QMETA-INF/services/java.nio.file.spi.FileSystemProvider\\E" 9 | }, 10 | { 11 | "pattern": "\\QMETA-INF/services/javax.annotation.processing.Processor\\E" 12 | }, 13 | { 14 | "pattern": "\\QMETA-INF/services/standalone.com.sun.tools.javac.platform.PlatformProvider\\E" 15 | }, 16 | { 17 | "pattern": "\\QMETA-INF/services/standalone.com.sun.source.util.Plugin\\E" 18 | }, 19 | { 20 | "pattern": "\\QMETA-INF/services/standalone.com.sun.tools.doclint.DocLint\\E" 21 | } 22 | ] 23 | }, 24 | "bundles": [ 25 | { 26 | "name": "standalone.com.sun.tools.javac.resources.compiler", 27 | "classNames": [ 28 | "standalone.com.sun.tools.javac.resources.compiler" 29 | ] 30 | }, 31 | { 32 | "name": "standalone.com.sun.tools.javac.resources.ct", 33 | "classNames": [ 34 | "standalone.com.sun.tools.javac.resources.ct" 35 | ] 36 | }, 37 | { 38 | "name": "standalone.com.sun.tools.javac.resources.javac", 39 | "classNames": [ 40 | "standalone.com.sun.tools.javac.resources.javac" 41 | ] 42 | }, 43 | { 44 | "name": "standalone.com.sun.tools.javac.resources.version", 45 | "classNames": [ 46 | "standalone.com.sun.tools.javac.resources.version" 47 | ] 48 | } 49 | ] 50 | } -------------------------------------------------------------------------------- /standalone-util/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | /target-eclipse/ 3 | -------------------------------------------------------------------------------- /standalone-util/NOTICE: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kohlschutter/jdk.compiler.standalone/db16879ce7f4e0ca284c42f6c0f286dfb6cde2cd/standalone-util/NOTICE -------------------------------------------------------------------------------- /standalone-util/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | 7 | com.kohlschutter.jdk.compiler 8 | standalone 9 | 1.1.3 10 | ../pom.xml 11 | 12 | 13 | com.kohlschutter.jdk 14 | standalone-util 15 | 16 | 17 | 18 | GPL-2.0-with-classpath-exception 19 | https://www.gnu.org/software/classpath/license.html 20 | repo 21 | 22 | 23 | 24 | 25 | 11 26 | UTF-8 27 | UTF-8 28 | UTF-8 29 | UTF-8 30 | -Xlint:none 31 | true 32 | 33 | 34 | 35 | 36 | 37 | de.thetaphi 38 | forbiddenapis 39 | 40 | 41 | default 42 | 43 | check 44 | 45 | skip 46 | 47 | 48 | 49 | 50 | org.apache.maven.plugins 51 | maven-pmd-plugin 52 | 53 | 54 | default-check 55 | 56 | check 57 | 58 | skip 59 | 60 | 61 | default-cpd-check 62 | 63 | cpd-check 64 | 65 | skip 66 | 67 | 68 | 69 | 70 | com.github.spotbugs 71 | spotbugs-maven-plugin 72 | 73 | ${spotbugs.exclude.file} 74 | 75 | 76 | 77 | spotbugs-during-install 78 | 79 | check 80 | 81 | skip 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | com.kohlschutter 91 | kohlschutter-test-util 92 | test 93 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /standalone-util/src/main/java/com/kohlschutter/jdk/standaloneutil/JavaHomeLocator.java: -------------------------------------------------------------------------------- 1 | package com.kohlschutter.jdk.standaloneutil; 2 | 3 | import java.io.IOException; 4 | import java.net.URI; 5 | import java.net.URISyntaxException; 6 | import java.net.URL; 7 | import java.nio.file.FileSystem; 8 | import java.nio.file.FileSystemNotFoundException; 9 | import java.nio.file.FileSystems; 10 | import java.nio.file.Path; 11 | import java.util.Collections; 12 | 13 | /** 14 | * Provides the path for "java.home", as far as the compiler is concerned. 15 | */ 16 | public class JavaHomeLocator { 17 | private static final Path COMPILER_JAVA_HOME = pathForResourceURL(); 18 | 19 | public static Path getPathToModules() { 20 | return getCompilerJavaHome().resolve("modules"); 21 | } 22 | 23 | public static Path getCompilerJavaHome() { 24 | return COMPILER_JAVA_HOME; 25 | } 26 | 27 | public static Path getCtSymPath() { 28 | return getCompilerJavaHome().resolve("lib").resolve("ct.sym"); 29 | } 30 | 31 | private static Path pathForResourceURL() { 32 | try { 33 | return pathForResourceURL(Class.forName("com.kohlschutter.jdk.home.JavaHome"), "."); 34 | } catch (ClassNotFoundException e) { 35 | throw new IllegalStateException("Could not find standalone-home jar in classpath", e); 36 | } 37 | } 38 | 39 | private static Path pathForResourceURL(Class classRef, String name) { 40 | if (".".equals(name) || name.isEmpty()) { 41 | name = "/" + classRef.getPackage().getName().replace('.', '/') + "/"; 42 | } 43 | 44 | URI uri; 45 | try { 46 | URL url = classRef.getResource(name); 47 | if (url == null) { 48 | throw new FileSystemNotFoundException("Cannot find resource " + name + " relative to " 49 | + classRef); 50 | } 51 | uri = url.toURI(); 52 | } catch (URISyntaxException e) { 53 | throw (FileSystemNotFoundException) new FileSystemNotFoundException(e.toString()).initCause( 54 | e); 55 | } 56 | 57 | Path p; 58 | try { 59 | p = Path.of(uri); 60 | } catch (FileSystemNotFoundException e) { 61 | String ssp = uri.getSchemeSpecificPart(); 62 | 63 | FileSystem fs; 64 | try { 65 | fs = FileSystems.newFileSystem(uri, Collections.emptyMap()); 66 | } catch (IOException e1) { 67 | throw (FileSystemNotFoundException) new FileSystemNotFoundException("Cannot find resource " 68 | + name + " relative to " + classRef).initCause(e1); 69 | } 70 | 71 | int exclp = ssp.indexOf("!/"); 72 | p = fs.getPath(exclp != -1 ? ssp.substring(exclp + 1) : ssp); 73 | } 74 | 75 | return p; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /standalone-util/src/main/java/com/kohlschutter/jdk/standaloneutil/ToolProvider.java: -------------------------------------------------------------------------------- 1 | package com.kohlschutter.jdk.standaloneutil; 2 | 3 | import java.lang.reflect.InvocationTargetException; 4 | import java.util.ServiceLoader; 5 | 6 | import javax.tools.JavaCompiler; 7 | 8 | /** 9 | * Helper class to mimic {@link javax.tools.ToolProvider}, for the purposes of providing a 10 | * {@link JavaCompiler} instance 11 | * 12 | * @author Christian Kohlschütter 13 | */ 14 | public class ToolProvider { 15 | private static final String STANDALONE_MODULE_NAME = "standalone.jdk.compiler"; 16 | 17 | /** 18 | * Being an intentional misnomer, this returns a new standalone Java Compiler instance, 19 | * not the actual JVM's compiler. 20 | * 21 | * @return A new {@link JavaCompiler} instance. 22 | */ 23 | public static JavaCompiler getSystemJavaCompiler() { 24 | ServiceLoader sl = ServiceLoader.load(JavaCompiler.class, ToolProvider.class 25 | .getClassLoader()); 26 | for (JavaCompiler tool : sl) { 27 | if (STANDALONE_MODULE_NAME.equals(tool.getClass().getModule().getName())) { 28 | return tool; 29 | } 30 | } 31 | 32 | Throwable suppressed = null; 33 | try { 34 | return (JavaCompiler) Class.forName("standalone.com.sun.tools.javac.api.JavacTool") 35 | .getConstructor().newInstance(); 36 | } catch (ClassNotFoundException | InstantiationException | IllegalAccessException 37 | | IllegalArgumentException | InvocationTargetException | NoSuchMethodException 38 | | SecurityException e) { 39 | suppressed = e; 40 | } 41 | 42 | IllegalStateException ise = new IllegalStateException("Module not found: " 43 | + STANDALONE_MODULE_NAME); 44 | if (suppressed != null) { 45 | ise.addSuppressed(suppressed); 46 | } 47 | throw ise; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /standalone-util/src/main/java/com/kohlschutter/jdk/standaloneutil/annotation/AnnotationInvocationHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.kohlschutter.jdk.standaloneutil.annotation; 27 | 28 | import java.io.ObjectInputStream; 29 | import java.io.Serializable; 30 | import java.lang.annotation.Annotation; 31 | import java.lang.annotation.AnnotationFormatError; 32 | import java.lang.annotation.IncompleteAnnotationException; 33 | import java.lang.reflect.AccessibleObject; 34 | import java.lang.reflect.Array; 35 | import java.lang.reflect.InvocationHandler; 36 | import java.lang.reflect.InvocationTargetException; 37 | import java.lang.reflect.Method; 38 | import java.lang.reflect.Modifier; 39 | import java.lang.reflect.Proxy; 40 | import java.security.AccessController; 41 | import java.security.PrivilegedAction; 42 | import java.util.ArrayList; 43 | import java.util.Arrays; 44 | import java.util.LinkedHashMap; 45 | import java.util.List; 46 | import java.util.Map; 47 | import java.util.Objects; 48 | import java.util.Set; 49 | import java.util.stream.Collectors; 50 | import java.util.stream.DoubleStream; 51 | import java.util.stream.IntStream; 52 | import java.util.stream.LongStream; 53 | import java.util.stream.Stream; 54 | 55 | /** 56 | * InvocationHandler for dynamic proxy implementation of Annotation. 57 | * 58 | * From {@code sun.reflect.annotation}. 59 | * 60 | * @author Josh Bloch 61 | * @since 1.5 62 | */ 63 | class AnnotationInvocationHandler implements InvocationHandler, Serializable { 64 | // @java.io.Serial 65 | private static final long serialVersionUID = 6182022883658399397L; 66 | private Class type; 67 | @SuppressWarnings("serial") // Not statically typed as Serializable 68 | private Map memberValues; 69 | 70 | AnnotationInvocationHandler(Class type, Map memberValues) { 71 | Class[] superInterfaces = type.getInterfaces(); 72 | if (!type.isAnnotation() || 73 | superInterfaces.length != 1 || 74 | superInterfaces[0] != java.lang.annotation.Annotation.class) 75 | throw new AnnotationFormatError("Attempt to create proxy for a non-annotation type: " + 76 | type.getName()); 77 | this.type = type; 78 | this.memberValues = memberValues; 79 | } 80 | 81 | public Object invoke(Object proxy, Method method, Object[] args) { 82 | String member = method.getName(); 83 | int parameterCount = method.getParameterCount(); 84 | 85 | // Handle Object and Annotation methods 86 | if (parameterCount == 1 && member == "equals" && 87 | method.getParameterTypes()[0] == Object.class) { 88 | return equalsImpl(proxy, args[0]); 89 | } 90 | if (parameterCount != 0) { 91 | throw new AssertionError("Too many parameters for an annotation method"); 92 | } 93 | 94 | if (member == "toString") { 95 | return toStringImpl(); 96 | } else if (member == "hashCode") { 97 | return hashCodeImpl(); 98 | } else if (member == "annotationType") { 99 | return type; 100 | } 101 | 102 | // Handle annotation member accessors 103 | Object result = memberValues.get(member); 104 | 105 | if (result == null) 106 | throw new IncompleteAnnotationException(type, member); 107 | 108 | if (result instanceof ExceptionProxy) 109 | throw ((ExceptionProxy) result).generateException(); 110 | 111 | if (result.getClass().isArray() && Array.getLength(result) != 0) 112 | result = cloneArray(result); 113 | 114 | return result; 115 | } 116 | 117 | /** 118 | * This method, which clones its array argument, would not be necessary 119 | * if Cloneable had a public clone method. 120 | */ 121 | private Object cloneArray(Object array) { 122 | Class type = array.getClass(); 123 | 124 | if (type == byte[].class) { 125 | byte[] byteArray = (byte[])array; 126 | return byteArray.clone(); 127 | } 128 | if (type == char[].class) { 129 | char[] charArray = (char[])array; 130 | return charArray.clone(); 131 | } 132 | if (type == double[].class) { 133 | double[] doubleArray = (double[])array; 134 | return doubleArray.clone(); 135 | } 136 | if (type == float[].class) { 137 | float[] floatArray = (float[])array; 138 | return floatArray.clone(); 139 | } 140 | if (type == int[].class) { 141 | int[] intArray = (int[])array; 142 | return intArray.clone(); 143 | } 144 | if (type == long[].class) { 145 | long[] longArray = (long[])array; 146 | return longArray.clone(); 147 | } 148 | if (type == short[].class) { 149 | short[] shortArray = (short[])array; 150 | return shortArray.clone(); 151 | } 152 | if (type == boolean[].class) { 153 | boolean[] booleanArray = (boolean[])array; 154 | return booleanArray.clone(); 155 | } 156 | 157 | Object[] objectArray = (Object[])array; 158 | return objectArray.clone(); 159 | } 160 | 161 | 162 | /** 163 | * Implementation of dynamicProxy.toString() 164 | */ 165 | private String toStringImpl() { 166 | StringBuilder result = new StringBuilder(128); 167 | result.append('@'); 168 | result.append(type.getName()); 169 | result.append('('); 170 | boolean firstMember = true; 171 | Set> entries = memberValues.entrySet(); 172 | boolean loneValue = entries.size() == 1; 173 | for (Map.Entry e : entries) { 174 | if (firstMember) 175 | firstMember = false; 176 | else 177 | result.append(", "); 178 | 179 | String key = e.getKey(); 180 | if (!loneValue || !"value".equals(key)) { 181 | result.append(key); 182 | result.append('='); 183 | } 184 | loneValue = false; 185 | result.append(memberValueToString(e.getValue())); 186 | } 187 | result.append(')'); 188 | return result.toString(); 189 | } 190 | 191 | /** 192 | * Translates a member value (in "dynamic proxy return form") into a string. 193 | */ 194 | private static String memberValueToString(Object value) { 195 | Class type = value.getClass(); 196 | if (!type.isArray()) { 197 | // primitive value, string, class, enum const, or annotation 198 | if (type == Class.class) 199 | return toSourceString((Class) value); 200 | else if (type == String.class) 201 | return toSourceString((String) value); 202 | if (type == Character.class) 203 | return toSourceString((char) value); 204 | else if (type == Double.class) 205 | return toSourceString((double) value); 206 | else if (type == Float.class) 207 | return toSourceString((float) value); 208 | else if (type == Long.class) 209 | return toSourceString((long) value); 210 | else if (type == Byte.class) 211 | return toSourceString((byte) value); 212 | else 213 | return value.toString(); 214 | } else { 215 | Stream stringStream; 216 | if (type == byte[].class) 217 | stringStream = convert((byte[]) value); 218 | else if (type == char[].class) 219 | stringStream = convert((char[]) value); 220 | else if (type == double[].class) 221 | stringStream = DoubleStream.of((double[]) value) 222 | .mapToObj(AnnotationInvocationHandler::toSourceString); 223 | else if (type == float[].class) 224 | stringStream = convert((float[]) value); 225 | else if (type == int[].class) 226 | stringStream = IntStream.of((int[]) value).mapToObj(String::valueOf); 227 | else if (type == long[].class) { 228 | stringStream = LongStream.of((long[]) value) 229 | .mapToObj(AnnotationInvocationHandler::toSourceString); 230 | } else if (type == short[].class) 231 | stringStream = convert((short[]) value); 232 | else if (type == boolean[].class) 233 | stringStream = convert((boolean[]) value); 234 | else if (type == Class[].class) 235 | stringStream = 236 | Arrays.stream((Class[]) value). 237 | map(AnnotationInvocationHandler::toSourceString); 238 | else if (type == String[].class) 239 | stringStream = 240 | Arrays.stream((String[])value). 241 | map(AnnotationInvocationHandler::toSourceString); 242 | else 243 | stringStream = Arrays.stream((Object[])value).map(Objects::toString); 244 | 245 | return stringStreamToString(stringStream); 246 | } 247 | } 248 | 249 | /** 250 | * Translates a Class value to a form suitable for use in the 251 | * string representation of an annotation. 252 | */ 253 | private static String toSourceString(Class clazz) { 254 | Class finalComponent = clazz; 255 | StringBuilder arrayBrackets = new StringBuilder(); 256 | 257 | while(finalComponent.isArray()) { 258 | finalComponent = finalComponent.getComponentType(); 259 | arrayBrackets.append("[]"); 260 | } 261 | 262 | return finalComponent.getName() + arrayBrackets.toString() + ".class"; 263 | } 264 | 265 | private static String toSourceString(float f) { 266 | if (Float.isFinite(f)) 267 | return Float.toString(f) + "f" ; 268 | else { 269 | if (Float.isInfinite(f)) { 270 | return (f < 0.0f) ? "-1.0f/0.0f": "1.0f/0.0f"; 271 | } else 272 | return "0.0f/0.0f"; 273 | } 274 | } 275 | 276 | private static String toSourceString(double d) { 277 | if (Double.isFinite(d)) 278 | return Double.toString(d); 279 | else { 280 | if (Double.isInfinite(d)) { 281 | return (d < 0.0f) ? "-1.0/0.0": "1.0/0.0"; 282 | } else 283 | return "0.0/0.0"; 284 | } 285 | } 286 | 287 | private static String toSourceString(char c) { 288 | StringBuilder sb = new StringBuilder(4); 289 | sb.append('\''); 290 | sb.append(quote(c)); 291 | return sb.append('\'') .toString(); 292 | } 293 | 294 | /** 295 | * Escapes a character if it has an escape sequence or is 296 | * non-printable ASCII. Leaves non-ASCII characters alone. 297 | */ 298 | private static String quote(char ch) { 299 | switch (ch) { 300 | case '\b': return "\\b"; 301 | case '\f': return "\\f"; 302 | case '\n': return "\\n"; 303 | case '\r': return "\\r"; 304 | case '\t': return "\\t"; 305 | case '\'': return "\\'"; 306 | case '\"': return "\\\""; 307 | case '\\': return "\\\\"; 308 | default: 309 | return (isPrintableAscii(ch)) 310 | ? String.valueOf(ch) 311 | : String.format("\\u%04x", (int) ch); 312 | } 313 | } 314 | 315 | /** 316 | * Is a character printable ASCII? 317 | */ 318 | private static boolean isPrintableAscii(char ch) { 319 | return ch >= ' ' && ch <= '~'; 320 | } 321 | 322 | private static String toSourceString(byte b) { 323 | return String.format("(byte)0x%02x", b); 324 | } 325 | 326 | private static String toSourceString(long ell) { 327 | return String.valueOf(ell) + "L"; 328 | } 329 | 330 | /** 331 | * Return a string suitable for use in the string representation 332 | * of an annotation. 333 | */ 334 | private static String toSourceString(String s) { 335 | StringBuilder sb = new StringBuilder(); 336 | sb.append('"'); 337 | for (int i = 0; i < s.length(); i++) { 338 | sb.append(quote(s.charAt(i))); 339 | } 340 | sb.append('"'); 341 | return sb.toString(); 342 | } 343 | 344 | private static Stream convert(byte[] values) { 345 | List list = new ArrayList<>(values.length); 346 | for (byte b : values) 347 | list.add(toSourceString(b)); 348 | return list.stream(); 349 | } 350 | 351 | private static Stream convert(char[] values) { 352 | List list = new ArrayList<>(values.length); 353 | for (char c : values) 354 | list.add(toSourceString(c)); 355 | return list.stream(); 356 | } 357 | 358 | private static Stream convert(float[] values) { 359 | List list = new ArrayList<>(values.length); 360 | for (float f : values) { 361 | list.add(toSourceString(f)); 362 | } 363 | return list.stream(); 364 | } 365 | 366 | private static Stream convert(short[] values) { 367 | List list = new ArrayList<>(values.length); 368 | for (short s : values) 369 | list.add(Short.toString(s)); 370 | return list.stream(); 371 | } 372 | 373 | private static Stream convert(boolean[] values) { 374 | List list = new ArrayList<>(values.length); 375 | for (boolean b : values) 376 | list.add(Boolean.toString(b)); 377 | return list.stream(); 378 | } 379 | 380 | private static String stringStreamToString(Stream stream) { 381 | return stream.collect(Collectors.joining(", ", "{", "}")); 382 | } 383 | 384 | /** 385 | * Implementation of dynamicProxy.equals(Object o) 386 | */ 387 | private Boolean equalsImpl(Object proxy, Object o) { 388 | if (o == proxy) 389 | return true; 390 | 391 | if (!type.isInstance(o)) 392 | return false; 393 | for (Method memberMethod : getMemberMethods()) { 394 | if (memberMethod.isSynthetic()) 395 | continue; 396 | String member = memberMethod.getName(); 397 | Object ourValue = memberValues.get(member); 398 | Object hisValue = null; 399 | AnnotationInvocationHandler hisHandler = asOneOfUs(o); 400 | if (hisHandler != null) { 401 | hisValue = hisHandler.memberValues.get(member); 402 | } else { 403 | try { 404 | hisValue = memberMethod.invoke(o); 405 | } catch (InvocationTargetException e) { 406 | return false; 407 | } catch (IllegalAccessException e) { 408 | throw new AssertionError(e); 409 | } 410 | } 411 | if (!memberValueEquals(ourValue, hisValue)) 412 | return false; 413 | } 414 | return true; 415 | } 416 | 417 | /** 418 | * Returns an object's invocation handler if that object is a dynamic 419 | * proxy with a handler of type AnnotationInvocationHandler. 420 | * Returns null otherwise. 421 | */ 422 | private AnnotationInvocationHandler asOneOfUs(Object o) { 423 | if (Proxy.isProxyClass(o.getClass())) { 424 | InvocationHandler handler = Proxy.getInvocationHandler(o); 425 | if (handler instanceof AnnotationInvocationHandler) 426 | return (AnnotationInvocationHandler) handler; 427 | } 428 | return null; 429 | } 430 | 431 | /** 432 | * Returns true iff the two member values in "dynamic proxy return form" 433 | * are equal using the appropriate equality function depending on the 434 | * member type. The two values will be of the same type unless one of 435 | * the containing annotations is ill-formed. If one of the containing 436 | * annotations is ill-formed, this method will return false unless the 437 | * two members are identical object references. 438 | */ 439 | private static boolean memberValueEquals(Object v1, Object v2) { 440 | Class type = v1.getClass(); 441 | 442 | // Check for primitive, string, class, enum const, annotation, 443 | // or ExceptionProxy 444 | if (!type.isArray()) 445 | return v1.equals(v2); 446 | 447 | // Check for array of string, class, enum const, annotation, 448 | // or ExceptionProxy 449 | if (v1 instanceof Object[] && v2 instanceof Object[]) 450 | return Arrays.equals((Object[]) v1, (Object[]) v2); 451 | 452 | // Check for ill formed annotation(s) 453 | if (v2.getClass() != type) 454 | return false; 455 | 456 | // Deal with array of primitives 457 | if (type == byte[].class) 458 | return Arrays.equals((byte[]) v1, (byte[]) v2); 459 | if (type == char[].class) 460 | return Arrays.equals((char[]) v1, (char[]) v2); 461 | if (type == double[].class) 462 | return Arrays.equals((double[]) v1, (double[]) v2); 463 | if (type == float[].class) 464 | return Arrays.equals((float[]) v1, (float[]) v2); 465 | if (type == int[].class) 466 | return Arrays.equals((int[]) v1, (int[]) v2); 467 | if (type == long[].class) 468 | return Arrays.equals((long[]) v1, (long[]) v2); 469 | if (type == short[].class) 470 | return Arrays.equals((short[]) v1, (short[]) v2); 471 | assert type == boolean[].class; 472 | return Arrays.equals((boolean[]) v1, (boolean[]) v2); 473 | } 474 | 475 | /** 476 | * Returns the member methods for our annotation type. These are 477 | * obtained lazily and cached, as they're expensive to obtain 478 | * and we only need them if our equals method is invoked (which should 479 | * be rare). 480 | */ 481 | private Method[] getMemberMethods() { 482 | Method[] value = memberMethods; 483 | if (value == null) { 484 | value = computeMemberMethods(); 485 | memberMethods = value; 486 | } 487 | return value; 488 | } 489 | 490 | @SuppressWarnings("removal") 491 | private Method[] computeMemberMethods() { 492 | return AccessController.doPrivileged( 493 | new PrivilegedAction() { 494 | public Method[] run() { 495 | final Method[] methods = type.getDeclaredMethods(); 496 | validateAnnotationMethods(methods); 497 | AccessibleObject.setAccessible(methods, true); 498 | return methods; 499 | }}); 500 | } 501 | 502 | private transient volatile Method[] memberMethods; 503 | 504 | /** 505 | * Validates that a method is structurally appropriate for an 506 | * annotation type. As of Java SE 8, annotation types cannot 507 | * contain static methods and the declared methods of an 508 | * annotation type must take zero arguments and there are 509 | * restrictions on the return type. 510 | */ 511 | private void validateAnnotationMethods(Method[] memberMethods) { 512 | /* 513 | * Specification citations below are from JLS 514 | * 9.6.1. Annotation Type Elements 515 | */ 516 | boolean valid = true; 517 | Method currentMethod = null; 518 | for(Method method : memberMethods) { 519 | currentMethod = method; 520 | int modifiers = method.getModifiers(); 521 | // Skip over methods that may be a static initializer or 522 | // similar construct. A static initializer may be used for 523 | // purposes such as initializing a lambda stored in an 524 | // interface field. 525 | if (method.isSynthetic() && 526 | (modifiers & (Modifier.STATIC | Modifier.PRIVATE)) != 0 && 527 | method.getParameterCount() == 0) { 528 | continue; 529 | } 530 | 531 | /* 532 | * "By virtue of the AnnotationTypeElementDeclaration 533 | * production, a method declaration in an annotation type 534 | * declaration cannot have formal parameters, type 535 | * parameters, or a throws clause. 536 | * 537 | * "By virtue of the AnnotationTypeElementModifier 538 | * production, a method declaration in an annotation type 539 | * declaration cannot be default or static." 540 | */ 541 | if (modifiers != (Modifier.PUBLIC | Modifier.ABSTRACT) || 542 | method.isDefault() || 543 | method.getParameterCount() != 0 || 544 | method.getExceptionTypes().length != 0) { 545 | valid = false; 546 | break; 547 | } 548 | 549 | /* 550 | * "It is a compile-time error if the return type of a 551 | * method declared in an annotation type is not one of the 552 | * following: a primitive type, String, Class, any 553 | * parameterized invocation of Class, an enum type 554 | * (section 8.9), an annotation type, or an array type 555 | * (chapter 10) whose element type is one of the preceding 556 | * types." 557 | */ 558 | Class returnType = method.getReturnType(); 559 | if (returnType.isArray()) { 560 | returnType = returnType.getComponentType(); 561 | if (returnType.isArray()) { // Only single dimensional arrays 562 | valid = false; 563 | break; 564 | } 565 | } 566 | 567 | if (!((returnType.isPrimitive() && returnType != void.class) || 568 | returnType == java.lang.String.class || 569 | returnType == java.lang.Class.class || 570 | returnType.isEnum() || 571 | returnType.isAnnotation())) { 572 | valid = false; 573 | break; 574 | } 575 | 576 | /* 577 | * "It is a compile-time error if any method declared in an 578 | * annotation type has a signature that is 579 | * override-equivalent to that of any public or protected 580 | * method declared in class Object or in the interface 581 | * java.lang.annotation.Annotation." 582 | * 583 | * The methods in Object or Annotation meeting the other 584 | * criteria (no arguments, contrained return type, etc.) 585 | * above are: 586 | * 587 | * String toString() 588 | * int hashCode() 589 | * Class annotationType() 590 | */ 591 | String methodName = method.getName(); 592 | if ((methodName.equals("toString") && returnType == java.lang.String.class) || 593 | (methodName.equals("hashCode") && returnType == int.class) || 594 | (methodName.equals("annotationType") && returnType == java.lang.Class.class)) { 595 | valid = false; 596 | break; 597 | } 598 | } 599 | if (valid) 600 | return; 601 | else 602 | throw new AnnotationFormatError("Malformed method on an annotation type: " + 603 | Objects.toString(currentMethod)); 604 | } 605 | 606 | /** 607 | * Implementation of dynamicProxy.hashCode() 608 | */ 609 | private int hashCodeImpl() { 610 | int result = 0; 611 | for (Map.Entry e : memberValues.entrySet()) { 612 | result += (127 * e.getKey().hashCode()) ^ 613 | memberValueHashCode(e.getValue()); 614 | } 615 | return result; 616 | } 617 | 618 | /** 619 | * Computes hashCode of a member value (in "dynamic proxy return form") 620 | */ 621 | private static int memberValueHashCode(Object value) { 622 | Class type = value.getClass(); 623 | if (!type.isArray()) // primitive, string, class, enum const, 624 | // or annotation 625 | return value.hashCode(); 626 | 627 | if (type == byte[].class) 628 | return Arrays.hashCode((byte[]) value); 629 | if (type == char[].class) 630 | return Arrays.hashCode((char[]) value); 631 | if (type == double[].class) 632 | return Arrays.hashCode((double[]) value); 633 | if (type == float[].class) 634 | return Arrays.hashCode((float[]) value); 635 | if (type == int[].class) 636 | return Arrays.hashCode((int[]) value); 637 | if (type == long[].class) 638 | return Arrays.hashCode((long[]) value); 639 | if (type == short[].class) 640 | return Arrays.hashCode((short[]) value); 641 | if (type == boolean[].class) 642 | return Arrays.hashCode((boolean[]) value); 643 | return Arrays.hashCode((Object[]) value); 644 | } 645 | 646 | // @java.io.Serial 647 | private void readObject(java.io.ObjectInputStream s) 648 | throws java.io.IOException, ClassNotFoundException { 649 | ObjectInputStream.GetField fields = s.readFields(); 650 | 651 | @SuppressWarnings("unchecked") 652 | Class t = (Class)fields.get("type", null); 653 | @SuppressWarnings("unchecked") 654 | Map streamVals = (Map)fields.get("memberValues", null); 655 | 656 | // Check to make sure that types have not evolved incompatibly 657 | 658 | AnnotationType annotationType = null; 659 | try { 660 | annotationType = AnnotationType.getInstance(t); 661 | } catch(IllegalArgumentException e) { 662 | // Class is no longer an annotation type; time to punch out 663 | throw new java.io.InvalidObjectException("Non-annotation type in annotation serial stream"); 664 | } 665 | 666 | Map> memberTypes = annotationType.memberTypes(); 667 | // consistent with runtime Map type 668 | Map mv = new LinkedHashMap<>(); 669 | 670 | // If there are annotation members without values, that 671 | // situation is handled by the invoke method. 672 | for (Map.Entry memberValue : streamVals.entrySet()) { 673 | String name = memberValue.getKey(); 674 | Object value = null; 675 | Class memberType = memberTypes.get(name); 676 | if (memberType != null) { // i.e. member still exists 677 | value = memberValue.getValue(); 678 | if (!(memberType.isInstance(value) || 679 | value instanceof ExceptionProxy)) { 680 | value = new AnnotationTypeMismatchExceptionProxy( 681 | objectToString(value)) 682 | .setMember(annotationType.members().get(name)); 683 | } 684 | } 685 | mv.put(name, value); 686 | } 687 | 688 | type = t; 689 | memberValues = mv; 690 | } 691 | 692 | /* 693 | * Create a textual representation of the argument without calling 694 | * any overridable methods of the argument. 695 | */ 696 | private static String objectToString(Object value) { 697 | return value.getClass().getName() + "@" + 698 | Integer.toHexString(System.identityHashCode(value)); 699 | } 700 | } 701 | -------------------------------------------------------------------------------- /standalone-util/src/main/java/com/kohlschutter/jdk/standaloneutil/annotation/AnnotationParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package com.kohlschutter.jdk.standaloneutil.annotation; 26 | 27 | import java.lang.annotation.Annotation; 28 | import java.lang.reflect.Proxy; 29 | import java.security.AccessController; 30 | import java.security.PrivilegedAction; 31 | import java.util.Map; 32 | 33 | /** 34 | * Simple {@code sun.reflect.annotation.AnnotationParser} excerpt for javac. 35 | */ 36 | public class AnnotationParser { 37 | /** 38 | * Returns an annotation of the given type backed by the given 39 | * member {@literal ->} value map. 40 | */ 41 | @SuppressWarnings("removal") 42 | public static Annotation annotationForMap(final Class type, 43 | final Map memberValues) 44 | { 45 | return AccessController.doPrivileged(new PrivilegedAction() { 46 | public Annotation run() { 47 | return (Annotation) Proxy.newProxyInstance( 48 | type.getClassLoader(), new Class[] { type }, 49 | new AnnotationInvocationHandler(type, memberValues)); 50 | }}); 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /standalone-util/src/main/java/com/kohlschutter/jdk/standaloneutil/annotation/AnnotationType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2003, 2021, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.kohlschutter.jdk.standaloneutil.annotation; 27 | 28 | import java.lang.annotation.*; 29 | import java.lang.reflect.*; 30 | import java.util.*; 31 | import java.security.AccessController; 32 | import java.security.PrivilegedAction; 33 | 34 | /** 35 | * Represents an annotation type at run time. Used to type-check annotations 36 | * and apply member defaults. 37 | * 38 | * @author Josh Bloch 39 | * @since 1.5 40 | */ 41 | public class AnnotationType { 42 | /** 43 | * Member name -> type mapping. Note that primitive types 44 | * are represented by the class objects for the corresponding wrapper 45 | * types. This matches the return value that must be used for a 46 | * dynamic proxy, allowing for a simple isInstance test. 47 | */ 48 | private final Map> memberTypes; 49 | 50 | /** 51 | * Member name -> default value mapping. 52 | */ 53 | private final Map memberDefaults; 54 | 55 | /** 56 | * Member name -> Method object mapping. This (and its associated 57 | * accessor) are used only to generate AnnotationTypeMismatchExceptions. 58 | */ 59 | private final Map members; 60 | 61 | /** 62 | * The retention policy for this annotation type. 63 | */ 64 | private final RetentionPolicy retention; 65 | 66 | /** 67 | * Whether this annotation type is inherited. 68 | */ 69 | private final boolean inherited; 70 | 71 | /** 72 | * Returns an AnnotationType instance for the specified annotation type. 73 | * 74 | * @throws IllegalArgumentException if the specified class object 75 | * does not represent a valid annotation type 76 | */ 77 | public static AnnotationType getInstance( 78 | Class annotationClass) 79 | { 80 | return null; 81 | // JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 82 | // AnnotationType result = jla.getAnnotationType(annotationClass); // volatile read 83 | // if (result == null) { 84 | // result = new AnnotationType(annotationClass); 85 | // // try to CAS the AnnotationType: null -> result 86 | // if (!jla.casAnnotationType(annotationClass, null, result)) { 87 | // // somebody was quicker -> read it's result 88 | // result = jla.getAnnotationType(annotationClass); 89 | // assert result != null; 90 | // } 91 | // } 92 | // 93 | // return result; 94 | } 95 | 96 | /** 97 | * Sole constructor. 98 | * 99 | * @param annotationClass the class object for the annotation type 100 | * @throws IllegalArgumentException if the specified class object for 101 | * does not represent a valid annotation type 102 | */ 103 | private AnnotationType(final Class annotationClass) { 104 | if (!annotationClass.isAnnotation()) 105 | throw new IllegalArgumentException("Not an annotation type"); 106 | 107 | @SuppressWarnings("removal") 108 | Method[] methods = 109 | AccessController.doPrivileged(new PrivilegedAction<>() { 110 | public Method[] run() { 111 | // Initialize memberTypes and defaultValues 112 | return annotationClass.getDeclaredMethods(); 113 | } 114 | }); 115 | 116 | memberTypes = new HashMap<>(methods.length+1, 1.0f); 117 | memberDefaults = new HashMap<>(0); 118 | members = new HashMap<>(methods.length+1, 1.0f); 119 | 120 | for (Method method : methods) { 121 | if (Modifier.isPublic(method.getModifiers()) && 122 | Modifier.isAbstract(method.getModifiers()) && 123 | !method.isSynthetic()) { 124 | if (method.getParameterCount() != 0) { 125 | throw new IllegalArgumentException(method + " has params"); 126 | } 127 | String name = method.getName(); 128 | Class type = method.getReturnType(); 129 | memberTypes.put(name, invocationHandlerReturnType(type)); 130 | members.put(name, method); 131 | 132 | Object defaultValue = method.getDefaultValue(); 133 | if (defaultValue != null) { 134 | memberDefaults.put(name, defaultValue); 135 | } 136 | } 137 | } 138 | 139 | // Initialize retention, & inherited fields. Special treatment 140 | // of the corresponding annotation types breaks infinite recursion. 141 | if (annotationClass != Retention.class && 142 | annotationClass != Inherited.class) { 143 | // JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); 144 | // Map, Annotation> metaAnnotations = 145 | // AnnotationParser.parseSelectAnnotations( 146 | // jla.getRawClassAnnotations(annotationClass), 147 | // jla.getConstantPool(annotationClass), 148 | // annotationClass, 149 | // Retention.class, Inherited.class 150 | // ); 151 | // Retention ret = (Retention) metaAnnotations.get(Retention.class); 152 | // retention = (ret == null ? RetentionPolicy.CLASS : ret.value()); 153 | // inherited = metaAnnotations.containsKey(Inherited.class); 154 | retention = RetentionPolicy.CLASS; 155 | inherited = false; 156 | } 157 | else { 158 | retention = RetentionPolicy.RUNTIME; 159 | inherited = false; 160 | } 161 | } 162 | 163 | /** 164 | * Returns the type that must be returned by the invocation handler 165 | * of a dynamic proxy in order to have the dynamic proxy return 166 | * the specified type (which is assumed to be a legal member type 167 | * for an annotation). 168 | */ 169 | public static Class invocationHandlerReturnType(Class type) { 170 | // Translate primitives to wrappers 171 | if (type == byte.class) 172 | return Byte.class; 173 | if (type == char.class) 174 | return Character.class; 175 | if (type == double.class) 176 | return Double.class; 177 | if (type == float.class) 178 | return Float.class; 179 | if (type == int.class) 180 | return Integer.class; 181 | if (type == long.class) 182 | return Long.class; 183 | if (type == short.class) 184 | return Short.class; 185 | if (type == boolean.class) 186 | return Boolean.class; 187 | 188 | // Otherwise, just return declared type 189 | return type; 190 | } 191 | 192 | /** 193 | * Returns member types for this annotation type 194 | * (member name {@literal ->} type mapping). 195 | */ 196 | public Map> memberTypes() { 197 | return memberTypes; 198 | } 199 | 200 | /** 201 | * Returns members of this annotation type 202 | * (member name {@literal ->} associated Method object mapping). 203 | */ 204 | public Map members() { 205 | return members; 206 | } 207 | 208 | /** 209 | * Returns the default values for this annotation type 210 | * (Member name {@literal ->} default value mapping). 211 | */ 212 | public Map memberDefaults() { 213 | return memberDefaults; 214 | } 215 | 216 | /** 217 | * Returns the retention policy for this annotation type. 218 | */ 219 | public RetentionPolicy retention() { 220 | return retention; 221 | } 222 | 223 | /** 224 | * Returns true if this annotation type is inherited. 225 | */ 226 | public boolean isInherited() { 227 | return inherited; 228 | } 229 | 230 | /** 231 | * For debugging. 232 | */ 233 | public String toString() { 234 | return "Annotation Type:\n" + 235 | " Member types: " + memberTypes + "\n" + 236 | " Member defaults: " + memberDefaults + "\n" + 237 | " Retention policy: " + retention + "\n" + 238 | " Inherited: " + inherited; 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /standalone-util/src/main/java/com/kohlschutter/jdk/standaloneutil/annotation/AnnotationTypeMismatchExceptionProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.kohlschutter.jdk.standaloneutil.annotation; 27 | import java.lang.annotation.*; 28 | import java.lang.reflect.Method; 29 | 30 | /** 31 | * ExceptionProxy for AnnotationTypeMismatchException. 32 | * 33 | * @author Josh Bloch 34 | * @since 1.5 35 | */ 36 | class AnnotationTypeMismatchExceptionProxy extends ExceptionProxy { 37 | // @java.io.Serial 38 | private static final long serialVersionUID = 7844069490309503934L; 39 | @SuppressWarnings("serial") // Not statically typed as Serializable 40 | private Method member; // Would be more robust to null-out in a writeObject method. 41 | private final String foundType; 42 | 43 | /** 44 | * It turns out to be convenient to construct these proxies in 45 | * two stages. Since this is a private implementation class, we 46 | * permit ourselves this liberty even though it's normally a very 47 | * bad idea. 48 | */ 49 | AnnotationTypeMismatchExceptionProxy(String foundType) { 50 | this.foundType = foundType; 51 | } 52 | 53 | AnnotationTypeMismatchExceptionProxy setMember(Method member) { 54 | this.member = member; 55 | return this; 56 | } 57 | 58 | protected RuntimeException generateException() { 59 | return new AnnotationTypeMismatchException(member, foundType); 60 | } 61 | 62 | @Override 63 | public String toString() { 64 | return "/* Warning type mismatch! \"" + foundType + "\" */" ; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /standalone-util/src/main/java/com/kohlschutter/jdk/standaloneutil/annotation/EnumConstantNotPresentExceptionProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.kohlschutter.jdk.standaloneutil.annotation; 27 | 28 | /** 29 | * ExceptionProxy for EnumConstantNotPresentException. 30 | * 31 | * @author Josh Bloch 32 | * @since 1.5 33 | */ 34 | public class EnumConstantNotPresentExceptionProxy extends ExceptionProxy { 35 | // @java.io.Serial 36 | private static final long serialVersionUID = -604662101303187330L; 37 | final Class> enumType; 38 | final String constName; 39 | 40 | public EnumConstantNotPresentExceptionProxy(Class> enumType, 41 | String constName) { 42 | this.enumType = enumType; 43 | this.constName = constName; 44 | } 45 | 46 | protected RuntimeException generateException() { 47 | return new EnumConstantNotPresentException(enumType, constName); 48 | } 49 | 50 | @Override 51 | public String toString() { 52 | return constName + " /* Warning: constant not present! */"; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /standalone-util/src/main/java/com/kohlschutter/jdk/standaloneutil/annotation/ExceptionProxy.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2004, 2019, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | 26 | package com.kohlschutter.jdk.standaloneutil.annotation; 27 | 28 | /** 29 | * An instance of this class is stored in an AnnotationInvocationHandler's 30 | * "memberValues" map in lieu of a value for an annotation member that 31 | * cannot be returned due to some exceptional condition (typically some 32 | * form of illegal evolution of the annotation class). The ExceptionProxy 33 | * instance describes the exception that the dynamic proxy should throw if 34 | * it is queried for this member. 35 | * 36 | * @author Josh Bloch 37 | * @since 1.5 38 | */ 39 | public abstract class ExceptionProxy implements java.io.Serializable { 40 | private static final long serialVersionUID = 7241930048386631401L; 41 | protected abstract RuntimeException generateException(); 42 | } 43 | -------------------------------------------------------------------------------- /standalone-util/src/main/java/com/kohlschutter/jdk/standaloneutil/jmod/JmodFile.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package com.kohlschutter.jdk.standaloneutil.jmod; 26 | 27 | import java.io.IOException; 28 | import java.io.InputStream; 29 | import java.nio.file.Files; 30 | import java.nio.file.Path; 31 | 32 | /** 33 | * Smaller version of {@code jdk.internal.jmod.JModFile}. 34 | * 35 | * @author Christian Kohlschütter 36 | */ 37 | public class JmodFile { 38 | //jmod magic number and version number 39 | private static final int JMOD_MAJOR_VERSION = 0x01; 40 | private static final int JMOD_MINOR_VERSION = 0x00; 41 | private static final byte[] JMOD_MAGIC_NUMBER = { 42 | 0x4A, 0x4D, /* JM */ 43 | JMOD_MAJOR_VERSION, JMOD_MINOR_VERSION, /* version 1.0 */ 44 | }; 45 | 46 | public static void checkMagic(Path file) throws IOException { 47 | try (InputStream in = Files.newInputStream(file)) { 48 | // validate the header 49 | byte[] magic = in.readNBytes(4); 50 | if (magic.length != 4) { 51 | throw new IOException("Invalid JMOD file: " + file); 52 | } 53 | if (magic[0] != JMOD_MAGIC_NUMBER[0] || 54 | magic[1] != JMOD_MAGIC_NUMBER[1]) { 55 | throw new IOException("Invalid JMOD file: " + file.toString()); 56 | } 57 | if (magic[2] > JMOD_MAJOR_VERSION || 58 | (magic[2] == JMOD_MAJOR_VERSION && magic[3] > JMOD_MINOR_VERSION)) { 59 | throw new IOException("Unsupported jmod version: " + 60 | magic[2] + "." + magic[3] + " in " + file.toString()); 61 | } 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /standalone-util/src/main/java/com/kohlschutter/jdk/standaloneutil/misc/VM.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. 3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 | * 5 | * This code is free software; you can redistribute it and/or modify it 6 | * under the terms of the GNU General Public License version 2 only, as 7 | * published by the Free Software Foundation. Oracle designates this 8 | * particular file as subject to the "Classpath" exception as provided 9 | * by Oracle in the LICENSE file that accompanied this code. 10 | * 11 | * This code is distributed in the hope that it will be useful, but WITHOUT 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 | * version 2 for more details (a copy is included in the LICENSE file that 15 | * accompanied this code). 16 | * 17 | * You should have received a copy of the GNU General Public License version 18 | * 2 along with this work; if not, write to the Free Software Foundation, 19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 | * 21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 | * or visit www.oracle.com if you need additional information or have any 23 | * questions. 24 | */ 25 | package com.kohlschutter.jdk.standaloneutil.misc; 26 | 27 | /** 28 | * Smaller/stubbed version of {@code jdk.internal.misc.VM}. 29 | * 30 | * @author Christian Kohlschütter 31 | */ 32 | public class VM { 33 | /** 34 | * Returns the VM arguments for this runtime environment. 35 | * 36 | * @implNote The HotSpot JVM processes the input arguments from multiple sources in the following 37 | * order: 1. JAVA_TOOL_OPTIONS environment variable 2. Options from JNI Invocation API 38 | * 3. _JAVA_OPTIONS environment variable 39 | * 40 | * If VM options file is specified via -XX:VMOptionsFile, the vm options file is read 41 | * and expanded in place of -XX:VMOptionFile option. 42 | */ 43 | public static String[] getRuntimeArguments() { 44 | System.err.println( 45 | "WARNING: VM.getRuntimeArguments is not yet implemented, returning empty array!"); 46 | return new String[0]; // FIXME 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /standalone-util/src/main/java/module-info.java: -------------------------------------------------------------------------------- 1 | module com.kohlschutter.jdk.standaloneutil { 2 | exports com.kohlschutter.jdk.standaloneutil; 3 | exports com.kohlschutter.jdk.standaloneutil.annotation; 4 | exports com.kohlschutter.jdk.standaloneutil.jmod; 5 | exports com.kohlschutter.jdk.standaloneutil.misc; 6 | 7 | requires transitive java.compiler; 8 | uses javax.tools.JavaCompiler; 9 | } 10 | --------------------------------------------------------------------------------