From 0457f87f1b9524cdc374e59b2900b11027298f8f Mon Sep 17 00:00:00 2001 From: xzl Date: Sun, 7 Apr 2024 16:33:03 +0800 Subject: [PATCH] feat: update asmtools to 7.0-b09 OpenJDK AsmTools Issue: https://github.com/deepin-community/sig-deepin-sysdev-team/issues/547 Log: update repo --- .hgignore | 10 + .hgtags | 7 + .jcheck/conf | 37 + LICENSE | 347 +++ README.html | 420 ++++ README.md | 22 +- build/build.properties | 32 + build/build.xml | 318 +++ build/productinfo.properties | 31 + debian/changelog | 12 +- debian/compat | 2 +- debian/control | 34 +- debian/copyright | 32 +- debian/libasmtools-java.javadoc | 1 + debian/libasmtools-java.jlibs | 1 + debian/rules | 11 +- debian/upstream/metadata.ex | 4 + debian/watch | 3 + src/org/openjdk/asmtools/Main.java | 125 + .../openjdk/asmtools/asmutils/HexUtils.java | 63 + .../asmtools/asmutils/StringUtils.java | 69 + src/org/openjdk/asmtools/common/Module.java | 440 ++++ src/org/openjdk/asmtools/common/Tool.java | 125 + src/org/openjdk/asmtools/i18n.properties | 33 + .../openjdk/asmtools/jasm/AnnotationData.java | 98 + src/org/openjdk/asmtools/jasm/Argument.java | 58 + src/org/openjdk/asmtools/jasm/AttrData.java | 63 + .../asmtools/jasm/BootstrapMethodData.java | 66 + src/org/openjdk/asmtools/jasm/CFVersion.java | 104 + src/org/openjdk/asmtools/jasm/CPXAttr.java | 48 + .../jasm/CheckedDataOutputStream.java | 64 + .../openjdk/asmtools/jasm/ClassArrayAttr.java | 69 + src/org/openjdk/asmtools/jasm/ClassData.java | 641 ++++++ src/org/openjdk/asmtools/jasm/CodeAttr.java | 529 +++++ .../openjdk/asmtools/jasm/ConstantPool.java | 1276 +++++++++++ src/org/openjdk/asmtools/jasm/Constants.java | 128 ++ src/org/openjdk/asmtools/jasm/Data.java | 43 + src/org/openjdk/asmtools/jasm/DataVector.java | 96 + .../openjdk/asmtools/jasm/DataVectorAttr.java | 112 + .../asmtools/jasm/DefaultAnnotationAttr.java | 62 + .../openjdk/asmtools/jasm/Environment.java | 454 ++++ src/org/openjdk/asmtools/jasm/FieldData.java | 67 + .../openjdk/asmtools/jasm/InnerClassData.java | 63 + src/org/openjdk/asmtools/jasm/Instr.java | 163 ++ src/org/openjdk/asmtools/jasm/JasmTokens.java | 476 ++++ src/org/openjdk/asmtools/jasm/Main.java | 330 +++ src/org/openjdk/asmtools/jasm/MemberData.java | 138 ++ src/org/openjdk/asmtools/jasm/MethodData.java | 228 ++ src/org/openjdk/asmtools/jasm/Modifiers.java | 465 ++++ src/org/openjdk/asmtools/jasm/ModuleAttr.java | 303 +++ .../asmtools/jasm/NestMembersAttr.java | 41 + .../openjdk/asmtools/jasm/OpcodeTables.java | 567 +++++ src/org/openjdk/asmtools/jasm/ParseBase.java | 62 + src/org/openjdk/asmtools/jasm/Parser.java | 2028 +++++++++++++++++ .../asmtools/jasm/ParserAnnotation.java | 1085 +++++++++ src/org/openjdk/asmtools/jasm/ParserCP.java | 638 ++++++ .../openjdk/asmtools/jasm/ParserInstr.java | 412 ++++ .../jasm/PermittedSubclassesAttr.java | 41 + src/org/openjdk/asmtools/jasm/RecordData.java | 100 + .../asmtools/jasm/RuntimeConstants.java | 90 + src/org/openjdk/asmtools/jasm/Scanner.java | 1209 ++++++++++ .../openjdk/asmtools/jasm/StackMapData.java | 281 +++ .../openjdk/asmtools/jasm/SwitchTable.java | 131 ++ src/org/openjdk/asmtools/jasm/Tables.java | 688 ++++++ .../asmtools/jasm/TypeAnnotationData.java | 98 + .../jasm/TypeAnnotationTargetInfoData.java | 546 +++++ .../jasm/TypeAnnotationTypePathData.java | 98 + .../asmtools/jasm/TypeAnnotationTypes.java | 312 +++ src/org/openjdk/asmtools/jasm/i18n.properties | 192 ++ src/org/openjdk/asmtools/jcdec/Main.java | 957 ++++++++ .../openjdk/asmtools/jcdec/i18n.properties | 34 + .../openjdk/asmtools/jcoder/ByteBuffer.java | 137 ++ .../openjdk/asmtools/jcoder/ErrorMessage.java | 41 + .../openjdk/asmtools/jcoder/JcodTokens.java | 387 ++++ src/org/openjdk/asmtools/jcoder/Jcoder.java | 766 +++++++ src/org/openjdk/asmtools/jcoder/Main.java | 197 ++ src/org/openjdk/asmtools/jcoder/Scanner.java | 905 ++++++++ .../openjdk/asmtools/jcoder/SourceFile.java | 440 ++++ .../openjdk/asmtools/jcoder/SyntaxError.java | 29 + .../openjdk/asmtools/jcoder/i18n.properties | 67 + src/org/openjdk/asmtools/jdec/ClassData.java | 1291 +++++++++++ src/org/openjdk/asmtools/jdec/Main.java | 133 ++ .../jdec/NestedByteArrayInputStream.java | 62 + src/org/openjdk/asmtools/jdec/i18n.properties | 41 + .../openjdk/asmtools/jdis/AnnotationData.java | 146 ++ .../asmtools/jdis/AnnotationElement.java | 390 ++++ src/org/openjdk/asmtools/jdis/AttrData.java | 65 + .../asmtools/jdis/BootstrapMethodData.java | 82 + .../openjdk/asmtools/jdis/ClassArrayData.java | 93 + src/org/openjdk/asmtools/jdis/ClassData.java | 529 +++++ src/org/openjdk/asmtools/jdis/CodeData.java | 743 ++++++ .../openjdk/asmtools/jdis/ConstantPool.java | 1073 +++++++++ src/org/openjdk/asmtools/jdis/FieldData.java | 126 + src/org/openjdk/asmtools/jdis/Indenter.java | 101 + .../openjdk/asmtools/jdis/InnerClassData.java | 84 + src/org/openjdk/asmtools/jdis/Main.java | 139 ++ src/org/openjdk/asmtools/jdis/MemberData.java | 235 ++ src/org/openjdk/asmtools/jdis/MethodData.java | 329 +++ src/org/openjdk/asmtools/jdis/ModuleData.java | 202 ++ .../openjdk/asmtools/jdis/NestHostData.java | 66 + .../asmtools/jdis/NestMembersData.java | 49 + src/org/openjdk/asmtools/jdis/Options.java | 93 + .../jdis/ParameterAnnotationData.java | 110 + .../jdis/PermittedSubclassesData.java | 49 + src/org/openjdk/asmtools/jdis/RecordData.java | 146 ++ .../openjdk/asmtools/jdis/SignatureData.java | 68 + .../openjdk/asmtools/jdis/StackMapData.java | 146 ++ src/org/openjdk/asmtools/jdis/TextLines.java | 56 + src/org/openjdk/asmtools/jdis/TraceUtils.java | 99 + src/org/openjdk/asmtools/jdis/TrapData.java | 55 + .../asmtools/jdis/TypeAnnotationData.java | 283 +++ src/org/openjdk/asmtools/jdis/Utils.java | 78 + src/org/openjdk/asmtools/jdis/i18n.properties | 47 + src/org/openjdk/asmtools/jdis/iAtt.java | 238 ++ src/org/openjdk/asmtools/jdis/uEscWriter.java | 77 + .../asmtools/util/I18NResourceBundle.java | 163 ++ .../openjdk/asmtools/util/ProductInfo.java | 83 + .../asmtools/util/productinfo.properties | 27 + 118 files changed, 28661 insertions(+), 38 deletions(-) create mode 100644 .hgignore create mode 100644 .hgtags create mode 100644 .jcheck/conf create mode 100644 LICENSE create mode 100644 README.html create mode 100644 build/build.properties create mode 100644 build/build.xml create mode 100644 build/productinfo.properties create mode 100644 debian/libasmtools-java.javadoc create mode 100644 debian/libasmtools-java.jlibs create mode 100644 debian/upstream/metadata.ex create mode 100644 debian/watch create mode 100644 src/org/openjdk/asmtools/Main.java create mode 100644 src/org/openjdk/asmtools/asmutils/HexUtils.java create mode 100644 src/org/openjdk/asmtools/asmutils/StringUtils.java create mode 100644 src/org/openjdk/asmtools/common/Module.java create mode 100644 src/org/openjdk/asmtools/common/Tool.java create mode 100644 src/org/openjdk/asmtools/i18n.properties create mode 100644 src/org/openjdk/asmtools/jasm/AnnotationData.java create mode 100644 src/org/openjdk/asmtools/jasm/Argument.java create mode 100644 src/org/openjdk/asmtools/jasm/AttrData.java create mode 100644 src/org/openjdk/asmtools/jasm/BootstrapMethodData.java create mode 100644 src/org/openjdk/asmtools/jasm/CFVersion.java create mode 100644 src/org/openjdk/asmtools/jasm/CPXAttr.java create mode 100644 src/org/openjdk/asmtools/jasm/CheckedDataOutputStream.java create mode 100644 src/org/openjdk/asmtools/jasm/ClassArrayAttr.java create mode 100644 src/org/openjdk/asmtools/jasm/ClassData.java create mode 100644 src/org/openjdk/asmtools/jasm/CodeAttr.java create mode 100644 src/org/openjdk/asmtools/jasm/ConstantPool.java create mode 100644 src/org/openjdk/asmtools/jasm/Constants.java create mode 100644 src/org/openjdk/asmtools/jasm/Data.java create mode 100644 src/org/openjdk/asmtools/jasm/DataVector.java create mode 100644 src/org/openjdk/asmtools/jasm/DataVectorAttr.java create mode 100644 src/org/openjdk/asmtools/jasm/DefaultAnnotationAttr.java create mode 100644 src/org/openjdk/asmtools/jasm/Environment.java create mode 100644 src/org/openjdk/asmtools/jasm/FieldData.java create mode 100644 src/org/openjdk/asmtools/jasm/InnerClassData.java create mode 100644 src/org/openjdk/asmtools/jasm/Instr.java create mode 100644 src/org/openjdk/asmtools/jasm/JasmTokens.java create mode 100644 src/org/openjdk/asmtools/jasm/Main.java create mode 100644 src/org/openjdk/asmtools/jasm/MemberData.java create mode 100644 src/org/openjdk/asmtools/jasm/MethodData.java create mode 100644 src/org/openjdk/asmtools/jasm/Modifiers.java create mode 100644 src/org/openjdk/asmtools/jasm/ModuleAttr.java create mode 100644 src/org/openjdk/asmtools/jasm/NestMembersAttr.java create mode 100644 src/org/openjdk/asmtools/jasm/OpcodeTables.java create mode 100644 src/org/openjdk/asmtools/jasm/ParseBase.java create mode 100644 src/org/openjdk/asmtools/jasm/Parser.java create mode 100644 src/org/openjdk/asmtools/jasm/ParserAnnotation.java create mode 100644 src/org/openjdk/asmtools/jasm/ParserCP.java create mode 100644 src/org/openjdk/asmtools/jasm/ParserInstr.java create mode 100644 src/org/openjdk/asmtools/jasm/PermittedSubclassesAttr.java create mode 100644 src/org/openjdk/asmtools/jasm/RecordData.java create mode 100644 src/org/openjdk/asmtools/jasm/RuntimeConstants.java create mode 100644 src/org/openjdk/asmtools/jasm/Scanner.java create mode 100644 src/org/openjdk/asmtools/jasm/StackMapData.java create mode 100644 src/org/openjdk/asmtools/jasm/SwitchTable.java create mode 100644 src/org/openjdk/asmtools/jasm/Tables.java create mode 100644 src/org/openjdk/asmtools/jasm/TypeAnnotationData.java create mode 100644 src/org/openjdk/asmtools/jasm/TypeAnnotationTargetInfoData.java create mode 100644 src/org/openjdk/asmtools/jasm/TypeAnnotationTypePathData.java create mode 100644 src/org/openjdk/asmtools/jasm/TypeAnnotationTypes.java create mode 100644 src/org/openjdk/asmtools/jasm/i18n.properties create mode 100644 src/org/openjdk/asmtools/jcdec/Main.java create mode 100644 src/org/openjdk/asmtools/jcdec/i18n.properties create mode 100644 src/org/openjdk/asmtools/jcoder/ByteBuffer.java create mode 100644 src/org/openjdk/asmtools/jcoder/ErrorMessage.java create mode 100644 src/org/openjdk/asmtools/jcoder/JcodTokens.java create mode 100644 src/org/openjdk/asmtools/jcoder/Jcoder.java create mode 100644 src/org/openjdk/asmtools/jcoder/Main.java create mode 100644 src/org/openjdk/asmtools/jcoder/Scanner.java create mode 100644 src/org/openjdk/asmtools/jcoder/SourceFile.java create mode 100644 src/org/openjdk/asmtools/jcoder/SyntaxError.java create mode 100644 src/org/openjdk/asmtools/jcoder/i18n.properties create mode 100644 src/org/openjdk/asmtools/jdec/ClassData.java create mode 100644 src/org/openjdk/asmtools/jdec/Main.java create mode 100644 src/org/openjdk/asmtools/jdec/NestedByteArrayInputStream.java create mode 100644 src/org/openjdk/asmtools/jdec/i18n.properties create mode 100644 src/org/openjdk/asmtools/jdis/AnnotationData.java create mode 100644 src/org/openjdk/asmtools/jdis/AnnotationElement.java create mode 100644 src/org/openjdk/asmtools/jdis/AttrData.java create mode 100644 src/org/openjdk/asmtools/jdis/BootstrapMethodData.java create mode 100644 src/org/openjdk/asmtools/jdis/ClassArrayData.java create mode 100644 src/org/openjdk/asmtools/jdis/ClassData.java create mode 100644 src/org/openjdk/asmtools/jdis/CodeData.java create mode 100644 src/org/openjdk/asmtools/jdis/ConstantPool.java create mode 100644 src/org/openjdk/asmtools/jdis/FieldData.java create mode 100644 src/org/openjdk/asmtools/jdis/Indenter.java create mode 100644 src/org/openjdk/asmtools/jdis/InnerClassData.java create mode 100644 src/org/openjdk/asmtools/jdis/Main.java create mode 100644 src/org/openjdk/asmtools/jdis/MemberData.java create mode 100644 src/org/openjdk/asmtools/jdis/MethodData.java create mode 100644 src/org/openjdk/asmtools/jdis/ModuleData.java create mode 100644 src/org/openjdk/asmtools/jdis/NestHostData.java create mode 100644 src/org/openjdk/asmtools/jdis/NestMembersData.java create mode 100644 src/org/openjdk/asmtools/jdis/Options.java create mode 100644 src/org/openjdk/asmtools/jdis/ParameterAnnotationData.java create mode 100644 src/org/openjdk/asmtools/jdis/PermittedSubclassesData.java create mode 100644 src/org/openjdk/asmtools/jdis/RecordData.java create mode 100644 src/org/openjdk/asmtools/jdis/SignatureData.java create mode 100644 src/org/openjdk/asmtools/jdis/StackMapData.java create mode 100644 src/org/openjdk/asmtools/jdis/TextLines.java create mode 100644 src/org/openjdk/asmtools/jdis/TraceUtils.java create mode 100644 src/org/openjdk/asmtools/jdis/TrapData.java create mode 100644 src/org/openjdk/asmtools/jdis/TypeAnnotationData.java create mode 100644 src/org/openjdk/asmtools/jdis/Utils.java create mode 100644 src/org/openjdk/asmtools/jdis/i18n.properties create mode 100644 src/org/openjdk/asmtools/jdis/iAtt.java create mode 100644 src/org/openjdk/asmtools/jdis/uEscWriter.java create mode 100644 src/org/openjdk/asmtools/util/I18NResourceBundle.java create mode 100644 src/org/openjdk/asmtools/util/ProductInfo.java create mode 100644 src/org/openjdk/asmtools/util/productinfo.properties diff --git a/.hgignore b/.hgignore new file mode 100644 index 0000000..2fc30fc --- /dev/null +++ b/.hgignore @@ -0,0 +1,10 @@ +syntax: regexp +data +data/.* +asmtools-7.0-build/.* +.*~$ +.*\.log$ +.*\.pyc$ +.*\.swp$ +.*\.DS_Store$ +.*\.iml$ diff --git a/.hgtags b/.hgtags new file mode 100644 index 0000000..afcc279 --- /dev/null +++ b/.hgtags @@ -0,0 +1,7 @@ +85d58e2683bfe2f70defd6ec10d377d84766ddd8 initial-openjdk +f201bb903e32f09e054f5fafc180508b9db67d00 6.0 +c52c3f68bfba5ca97378e9e9df5fc1268ac9e481 7.0 +f788f629bb40b3f3dd6d2cd25f3c9072568792de 7.0-b02 +2931522b78a4a4842396ad61300853911caf4169 7.0-b04 +b63cf0206a26c9cef5831aec217ed88a5ba09f5a 7.0-b05 +5d0dc302199d510f0fb44763fbca8118d12f8ad8 7.0-b06 diff --git a/.jcheck/conf b/.jcheck/conf new file mode 100644 index 0000000..362277d --- /dev/null +++ b/.jcheck/conf @@ -0,0 +1,37 @@ +; +; Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. +; DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +; +; This code is free software; you can redistribute it and/or modify it +; under the terms of the GNU General Public License version 2 only, as +; published by the Free Software Foundation. +; +; This code is distributed in the hope that it will be useful, but WITHOUT +; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +; version 2 for more details (a copy is included in the LICENSE file that +; accompanied this code). +; +; You should have received a copy of the GNU General Public License version +; 2 along with this work; if not, write to the Free Software Foundation, +; Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +; +; Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +; or visit www.oracle.com if you need additional information or have any +; questions. +; + +[general] +project=code-tools +repository=asmtools +jbs=codetools + +[checks] +error=author,committer,whitespace,executable,symlink + +[census] +version=0 +domain=openjdk.org + +[checks "whitespace"] +files=.*\.java$|.*\.c$|.*\.h$|.*\.cpp$|.*\.hpp$ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b40a0f4 --- /dev/null +++ b/LICENSE @@ -0,0 +1,347 @@ +The GNU General Public License (GPL) + +Version 2, June 1991 + +Copyright (C) 1989, 1991 Free Software Foundation, Inc. +59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Everyone is permitted to copy and distribute verbatim copies of this license +document, but changing it is not allowed. + +Preamble + +The licenses for most software are designed to take away your freedom to share +and change it. By contrast, the GNU General Public License is intended to +guarantee your freedom to share and change free software--to make sure the +software is free for all its users. This General Public License applies to +most of the Free Software Foundation's software and to any other program whose +authors commit to using it. (Some other Free Software Foundation software is +covered by the GNU Library General Public License instead.) You can apply it to +your programs, too. + +When we speak of free software, we are referring to freedom, not price. Our +General Public Licenses are designed to make sure that you have the freedom to +distribute copies of free software (and charge for this service if you wish), +that you receive source code or can get it if you want it, that you can change +the software or use pieces of it in new free programs; and that you know you +can do these things. + +To protect your rights, we need to make restrictions that forbid anyone to deny +you these rights or to ask you to surrender the rights. These restrictions +translate to certain responsibilities for you if you distribute copies of the +software, or if you modify it. + +For example, if you distribute copies of such a program, whether gratis or for +a fee, you must give the recipients all the rights that you have. You must +make sure that they, too, receive or can get the source code. And you must +show them these terms so they know their rights. + +We protect your rights with two steps: (1) copyright the software, and (2) +offer you this license which gives you legal permission to copy, distribute +and/or modify the software. + +Also, for each author's protection and ours, we want to make certain that +everyone understands that there is no warranty for this free software. If the +software is modified by someone else and passed on, we want its recipients to +know that what they have is not the original, so that any problems introduced +by others will not reflect on the original authors' reputations. + +Finally, any free program is threatened constantly by software patents. We +wish to avoid the danger that redistributors of a free program will +individually obtain patent licenses, in effect making the program proprietary. +To prevent this, we have made it clear that any patent must be licensed for +everyone's free use or not licensed at all. + +The precise terms and conditions for copying, distribution and modification +follow. + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +0. This License applies to any program or other work which contains a notice +placed by the copyright holder saying it may be distributed under the terms of +this General Public License. The "Program", below, refers to any such program +or work, and a "work based on the Program" means either the Program or any +derivative work under copyright law: that is to say, a work containing the +Program or a portion of it, either verbatim or with modifications and/or +translated into another language. (Hereinafter, translation is included +without limitation in the term "modification".) Each licensee is addressed as +"you". + +Activities other than copying, distribution and modification are not covered by +this License; they are outside its scope. The act of running the Program is +not restricted, and the output from the Program is covered only if its contents +constitute a work based on the Program (independent of having been made by +running the Program). Whether that is true depends on what the Program does. + +1. You may copy and distribute verbatim copies of the Program's source code as +you receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice and +disclaimer of warranty; keep intact all the notices that refer to this License +and to the absence of any warranty; and give any other recipients of the +Program a copy of this License along with the Program. + +You may charge a fee for the physical act of transferring a copy, and you may +at your option offer warranty protection in exchange for a fee. + +2. You may modify your copy or copies of the Program or any portion of it, thus +forming a work based on the Program, and copy and distribute such modifications +or work under the terms of Section 1 above, provided that you also meet all of +these conditions: + + a) You must cause the modified files to carry prominent notices stating + that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in whole or + in part contains or is derived from the Program or any part thereof, to be + licensed as a whole at no charge to all third parties under the terms of + this License. + + c) If the modified program normally reads commands interactively when run, + you must cause it, when started running for such interactive use in the + most ordinary way, to print or display an announcement including an + appropriate copyright notice and a notice that there is no warranty (or + else, saying that you provide a warranty) and that users may redistribute + the program under these conditions, and telling the user how to view a copy + of this License. (Exception: if the Program itself is interactive but does + not normally print such an announcement, your work based on the Program is + not required to print an announcement.) + +These requirements apply to the modified work as a whole. If identifiable +sections of that work are not derived from the Program, and can be reasonably +considered independent and separate works in themselves, then this License, and +its terms, do not apply to those sections when you distribute them as separate +works. But when you distribute the same sections as part of a whole which is a +work based on the Program, the distribution of the whole must be on the terms +of this License, whose permissions for other licensees extend to the entire +whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest your +rights to work written entirely by you; rather, the intent is to exercise the +right to control the distribution of derivative or collective works based on +the Program. + +In addition, mere aggregation of another work not based on the Program with the +Program (or with a work based on the Program) on a volume of a storage or +distribution medium does not bring the other work under the scope of this +License. + +3. You may copy and distribute the Program (or a work based on it, under +Section 2) in object code or executable form under the terms of Sections 1 and +2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable source + code, which must be distributed under the terms of Sections 1 and 2 above + on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three years, to + give any third party, for a charge no more than your cost of physically + performing source distribution, a complete machine-readable copy of the + corresponding source code, to be distributed under the terms of Sections 1 + and 2 above on a medium customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer to + distribute corresponding source code. (This alternative is allowed only + for noncommercial distribution and only if you received the program in + object code or executable form with such an offer, in accord with + Subsection b above.) + +The source code for a work means the preferred form of the work for making +modifications to it. For an executable work, complete source code means all +the source code for all modules it contains, plus any associated interface +definition files, plus the scripts used to control compilation and installation +of the executable. However, as a special exception, the source code +distributed need not include anything that is normally distributed (in either +source or binary form) with the major components (compiler, kernel, and so on) +of the operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the source +code from the same place counts as distribution of the source code, even though +third parties are not compelled to copy the source along with the object code. + +4. You may not copy, modify, sublicense, or distribute the Program except as +expressly provided under this License. Any attempt otherwise to copy, modify, +sublicense or distribute the Program is void, and will automatically terminate +your rights under this License. However, parties who have received copies, or +rights, from you under this License will not have their licenses terminated so +long as such parties remain in full compliance. + +5. You are not required to accept this License, since you have not signed it. +However, nothing else grants you permission to modify or distribute the Program +or its derivative works. These actions are prohibited by law if you do not +accept this License. Therefore, by modifying or distributing the Program (or +any work based on the Program), you indicate your acceptance of this License to +do so, and all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + +6. Each time you redistribute the Program (or any work based on the Program), +the recipient automatically receives a license from the original licensor to +copy, distribute or modify the Program subject to these terms and conditions. +You may not impose any further restrictions on the recipients' exercise of the +rights granted herein. You are not responsible for enforcing compliance by +third parties to this License. + +7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), conditions +are imposed on you (whether by court order, agreement or otherwise) that +contradict the conditions of this License, they do not excuse you from the +conditions of this License. If you cannot distribute so as to satisfy +simultaneously your obligations under this License and any other pertinent +obligations, then as a consequence you may not distribute the Program at all. +For example, if a patent license would not permit royalty-free redistribution +of the Program by all those who receive copies directly or indirectly through +you, then the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply and +the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any patents or +other property right claims or to contest validity of any such claims; this +section has the sole purpose of protecting the integrity of the free software +distribution system, which is implemented by public license practices. Many +people have made generous contributions to the wide range of software +distributed through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing to +distribute software through any other system and a licensee cannot impose that +choice. + +This section is intended to make thoroughly clear what is believed to be a +consequence of the rest of this License. + +8. If the distribution and/or use of the Program is restricted in certain +countries either by patents or by copyrighted interfaces, the original +copyright holder who places the Program under this License may add an explicit +geographical distribution limitation excluding those countries, so that +distribution is permitted only in or among countries not thus excluded. In +such case, this License incorporates the limitation as if written in the body +of this License. + +9. The Free Software Foundation may publish revised and/or new versions of the +General Public License from time to time. Such new versions will be similar in +spirit to the present version, but may differ in detail to address new problems +or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any later +version", you have the option of following the terms and conditions either of +that version or of any later version published by the Free Software Foundation. +If the Program does not specify a version number of this License, you may +choose any version ever published by the Free Software Foundation. + +10. If you wish to incorporate parts of the Program into other free programs +whose distribution conditions are different, write to the author to ask for +permission. For software which is copyrighted by the Free Software Foundation, +write to the Free Software Foundation; we sometimes make exceptions for this. +Our decision will be guided by the two goals of preserving the free status of +all derivatives of our free software and of promoting the sharing and reuse of +software generally. + +NO WARRANTY + +11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR +THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE +STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE +PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND +FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND +PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, +YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + +12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL +ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE +PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR +INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA +BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER +OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +END OF TERMS AND CONDITIONS + +How to Apply These Terms to Your New Programs + +If you develop a new program, and you want it to be of the greatest possible +use to the public, the best way to achieve this is to make it free software +which everyone can redistribute and change under these terms. + +To do so, attach the following notices to the program. It is safest to attach +them to the start of each source file to most effectively convey the exclusion +of warranty; and each file should have at least the "copyright" line and a +pointer to where the full notice is found. + + One line to give the program's name and a brief idea of what it does. + + Copyright (C) + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the Free + Software Foundation; either version 2 of the License, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., 59 + Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this when it +starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author Gnomovision comes + with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free + software, and you are welcome to redistribute it under certain conditions; + type 'show c' for details. + +The hypothetical commands 'show w' and 'show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may be +called something other than 'show w' and 'show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your school, +if any, to sign a "copyright disclaimer" for the program, if necessary. Here +is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + 'Gnomovision' (which makes passes at compilers) written by James Hacker. + + signature of Ty Coon, 1 April 1989 + + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General Public +License instead of this License. + + +"CLASSPATH" EXCEPTION TO THE GPL + +Certain source files distributed by Oracle America and/or its affiliates are +subject to the following clarification and special exception to the GPL, but +only where Oracle has expressly included in the particular source file's header +the words "Oracle designates this particular file as subject to the "Classpath" +exception as provided by Oracle in the LICENSE file that accompanied this code." + + Linking this library statically or dynamically with other modules is making + a combined work based on this library. Thus, the terms and conditions of + the GNU General Public License cover the whole combination. + + As a special exception, the copyright holders of this library give you + permission to link this library with independent modules to produce an + executable, regardless of the license terms of these independent modules, + and to copy and distribute the resulting executable under terms of your + choice, provided that you also meet, for each linked independent module, + the terms and conditions of the license of that module. An independent + module is a module which is not derived from or based on this library. If + you modify this library, you may extend this exception to your version of + the library, but you are not obligated to do so. If you do not wish to do + so, delete this exception statement from your version. diff --git a/README.html b/README.html new file mode 100644 index 0000000..7f1e11e --- /dev/null +++ b/README.html @@ -0,0 +1,420 @@ + + + + + + + + README - AsmTools - Version 7.0 + + + + + + + + + + + + + + + + +
 
+
+

Release Notes

+

AsmTools

+ +

April 2020

+
+
 
+


+[Skip TOC]

+

Table of Contents

+
+
Introduction
+
New in This Release
+
System Requirements
+
Release Contents
+
Installation
+
Using AsmTools
+
Known Bugs and Issues
+
Obtaining Sources
+
Building from Sources
+
Contribute
+
+ +

Introduction

+ +

+AsmTools is a +software package containing a collection of tools that you can use to +encode and decode a Java class file, without using a Javac compiler. +You can use AsmTools to create class files which can not normally be +produced by a compiler. These class files can be used during the +development process to test the compliance of a Virtual Machine (VM) to +its specification.

+

+These +release notes contain information about the ASM Tools version 7.0. +See the ( +CodeTools project) +AsmTools OpenJDK project page for more information about AsmTools. +

+ + + + +

New in This Release

+

New Features

+

+

+ + + + + + + + + + + + + + + +
Bug ID Description
+ CODETOOLS-7902525 + + Asmtools support: JEP 360 Sealed Types(Preview) +
+ CODETOOLS-7902524 + + Asmtools support: JEP 359 Records(Preview) +
+

+

+ +

Fixed Bugs

+

+

+ + + + + + + + + + + + + + + + + + + + + + + +
Bug ID Description
+ CODETOOLS-7902618 + + Asmtools does not support jasm output/input for TypeAnnotations. +
+ CODETOOLS-7902643 + + Asmtools(jasm) does not support signature attributes in field_info, component_info +
+ CODETOOLS-7902644 + + Asmtools (jcoder) fails if an jcod file contains CONSTANT_Utf8_info with non-normative char sequences +
+ CODETOOLS-7902648 + + jasm has no clues about how to interpret CONSTANT_MethodHandle_info:reference_kind in an invokedynamic instruction +
+ +

+

System Requirements

+

+Using ASM Tools requires a properly configured, certified Java +Platform, Standard Edition (Java SE™ platform) runtime environment +version 1.8 or later, or a Java SE platform 8.0 runtime environment. +

+ + +

Installation

+To install the AsmTools, simply unzip the  asmtools-7.0.zip +distribution file in the directory of your choice. Optionally, you may +wish to define an environment variable (ASMTOOLSHOME) to point to this +location for your convenience. + + +

Release Contents

+

+This section lists the components under the base subdirectory structure +(asmtools-7.0/) +that is created when you unzip the AsmTools distribution archive during +installation. +

+

Important Component Descriptions and Locations

+ + + + + + + + + + + + + + + +
ComponentDescription
README.htmlThe AsmTools release notes (this document).
lib/asmtools.jarThe AsmTools library.
+
+ + + +

Using AsmTools

+

AsmTools have the following synopsis:

+
+
java -jar ${ASMTOOLSHOME}/asmtools.jar TOOL FILE
[TOOL="jasm" | "jdis" | "jcoder" | jdec | jcdec]
[FILE=xxx.jasm (for jasm command) | xxx.jcod (for jcod command) | xxx.class for jdis, jdec, or jcdec command]

+
+

Examples:

+
+
+% java -jar ${ASMTOOLSHOME}/asmtools.jar jasm MyClass.jasm
+(produces MyClass.class)
+
+% java -jar ${ASMTOOLSHOME}/asmtools.jar jdis MyClass.class > +MyClass.jasm
+(produces MyClass.jasm)
+
+% java -jar ${ASMTOOLSHOME}/asmtools.jar jcoder MyClass.jcod
+(produces MyClass.class)
+
+% java -jar ${ASMTOOLSHOME}/asmtools.jar jdec MyClass.class > +MyClass.jcod
+(produces MyClass.jcod) +
+ +

+Assembler Syntax and tool usage is covered in the +AsmTools project wiki. +

+
+

Operating Modes

+

There are no operating modes.

+

[Top]

+

+

Known Bugs and Issues

+

+Many attributes (in the class file, as specified by the + +Java VM Specification, Chapter 4) + + are covered in the JASM syntax, but not all. +

+

[Top]

+

+

Contribute

+The AsmTools open source project was created in order to develop a community that will improve it, further the development of Java assembly tools, and use Java assemblers to develop test suites. We encourage you to browse, download, contribute, and get involved. +

+ +

[Top]

+

+

Obtaining the Sources

+

+The sources for ASM Tools 7.0 and later is available via the community +OpenJDK project. The sources are stored +and accessed through a Mercurial repository, which the public may access in a +read-only fashion. Committing to changes to the sources is accomplished by +first contributing through a proxy developer to establish ones self, which can +then lead to rights to contribute directly. More information can be found on +the OpenJDK Projects landing page. +

+ +

+ The general form for obtaining a copy of the current source tree is:

+
+% hg clone http://hg.openjdk.java.net/code-tools/asmtools asmtools-ro
+
+This places a copy of the entire source tree in the asmtools-ro directory. From +that directory, you can then view and build the sources, as described in the next +section. Note that this copy is of the development version of the sources, +you may need to extract a different revision if you want a copy from a specific +period of time. + + +

+At the time of writing, the current version of Mercurial (hg) +is 2.9.1. See the +OpenJDK ASM Tools main page for updated and more detailed information. +

+ +

[Top]

+

+

Building the Sources

+

+Building the sources requires Ant version 1.8 or later (1.10.1 is current at the +time of writing), and the built-in build is compatible with using an IDE. +The sources are designed for Java 8 and if you invoke ant with this +runtime, your chance of encountering an abnormal conditions due to your runtime +or compiler (or javadoc) version. +

+ +

+General requirements: +

+
    +
  • ant, version 1.8 or later +
  • Java 8 runtime and compiler (minimum and recommended) +
  • 10MB free disk space (3MB for source tree, 7MB for build output) +
+ +

+The general steps to do a build from source from the command line: +

+
    +
  1. Obtain Sources +
  2. Make your current directory <topdir>/build/ +
  3. Invoke ant:
    % ant +
  4. Output appears in a directory above (one above <topdir>), + such as: ../../asmtools-7.0-build/ +
+ +

+A common variation of the build command to force ant to use a particular +installation of Java on your machine: +

+
+# JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_144.jdk/Contents/Home ANTHOME=/usr/share/ant ant
+
+This example for common *nix shells (zsh, bash, sh), alter it as needed for your +command environment. You can test your invocation of ant by running it with +the -diagnostics flag, where it will indicate which Java it is +using. + + +

+The build file itself is build/build.xml, this is the file you can +specify in your IDE for building. +

+ + +


+

+ +
+ +
+

Copyright © 2008, 2020, Oracle and/or its affiliates. All rights reserved. + Use is subject to license terms.The majority of the Assember Tools project is released under the GNU + General Public License Version 2 (GPLv2)

+ + diff --git a/README.md b/README.md index 9ebb840..73f880e 100644 --- a/README.md +++ b/README.md @@ -1 +1,21 @@ -# template-repository \ No newline at end of file +# OpenJDK AsmTools + +The AsmTools open source project is used to develop tools for the production of proper and improper Java '.class' files. AsmTools are being opened in order to facilitate a community of Java .class file production for various testing and other OpenJDK development applications. + +AsmTools consist of a set of (Java class file) assembler/disassemblers: + ++ **Jasm/Jdis** - an assembler language that provides a Java-like declaration of member signatures, while providing Java VM specification compliant mnemonics for byte-code instructions. Jasm also provides high-level syntax for constructs often found within classfile attributes. Jasm encoded tests are useful for sequencing byte codes in a way that Javac compiled code might not normally sequence byte-codes. + ++ **JCod/JDec** - an assembler language that provides byte-code containers of class-file constructs. JCod encoded tests are useful for testing the well-formedness of class-files, as well as creating collections within a class-file construct that might be size-bounded by a normal Java compiler. JCod can also be used to 'fuzz' class files in a methodical way that respects class-file constructs. + +AsmTools are completely reflexive - Java binary (.class) files may be disassembled into textual representations, which in turn can be assembled back to the same binary file. + +AsmTools are developed to support the latest class file formats, in lock-step with JDK development. + +Other open source Java assembler tools and binary classfile frameworks exist. They can be used for the purpose of synthesizing classfiles, however: +- they typically are designed to enforce the limits imposed by the VM specification of the class file format. They are not designed to produce classes that violate those limits. +- other assembler tools may not necessarily follow strict Java mnemonics as defined in the Java VM spec. +- other assembler tools may not stay in lock-step with the current generation of the JDK and VM specifications. +- class file libraries are harder to use for simple manipulations of any given class file. Typically, one has to create a program in that framework to parse and modify a class for a specific change to a given class. + +The AsmTools open source project is part of the [Code Tools Project](http://openjdk.java.net/projects/code-tools/ "Code Tools Project"). It exists to promote a community that will improve it, further its development, and use it to develop test suites. We encourage you to browse, download, contribute, and get involved. diff --git a/build/build.properties b/build/build.properties new file mode 100644 index 0000000..f6ee37c --- /dev/null +++ b/build/build.properties @@ -0,0 +1,32 @@ +# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +# Compile in verbose mode +javac.verbose=false +# Compile in debug mode +javac.debug=true +# Compile with deprecation flag +javac.deprecation=false + +# path to output directory +BUILD_DIR = ../../asmtools-${PRODUCT_VERSION}-build diff --git a/build/build.xml b/build/build.xml new file mode 100644 index 0000000..a6137da --- /dev/null +++ b/build/build.xml @@ -0,0 +1,318 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Usage: + ant <TARGET> -D<OPTIONS> + +where TARGET is: + build (default): builds all binaries, doc and distributions. + clean: removes all generated files and directories. + clean-build: cleans then builds the project + help: prints a usage message (this message). + +Other values for TARGET are: + devbuild: produces binaries and javadocs. + javadoc: produces javadocs. + test: runs sanity tests. (not currently available) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/build/productinfo.properties b/build/productinfo.properties new file mode 100644 index 0000000..e8a1fe4 --- /dev/null +++ b/build/productinfo.properties @@ -0,0 +1,31 @@ +# Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. Oracle designates this +# particular file as subject to the "Classpath" exception as provided +# by Oracle in the LICENSE file that accompanied this code. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +# Path to JDK will be used to compile sources +# Product info +PRODUCT_NAME = asmtools +PRODUCT_JAR_NAME = asmtools.jar +PRODUCT_VERSION = 7.0 +PRODUCT_MILESTONE = ea +PRODUCT_BUILDNUMBER = b09 +PRODUCT_NAME_LONG = Java Assembler Tools diff --git a/debian/changelog b/debian/changelog index bad88e2..b97fd86 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,5 +1,11 @@ -template-repository (1.0-1) unstable; urgency=medium +asmtools (7.0-b09-2) unstable; urgency=medium - * Initial release + * Source only upload - -- Tsic404 Sat, 28 Jan 2023 13:46:49 +0800 + -- Emmanuel Bourg Mon, 06 Feb 2023 21:22:12 +0100 + +asmtools (7.0-b09-1) unstable; urgency=medium + + * Import asmtools 7.0-b09 (Closes: #1028366) + + -- Vladimir Petko Tue, 10 Jan 2023 10:44:47 +1300 diff --git a/debian/compat b/debian/compat index b4de394..ca7bf83 100644 --- a/debian/compat +++ b/debian/compat @@ -1 +1 @@ -11 +13 \ No newline at end of file diff --git a/debian/control b/debian/control index cb7c4a0..aa7ac87 100644 --- a/debian/control +++ b/debian/control @@ -1,15 +1,23 @@ -Source: template-repository -Section: unknown +Source: asmtools +Section: java Priority: optional -Maintainer: Tsic404 -Build-Depends: debhelper (>= 11) -Standards-Version: 4.1.3 -Homepage: https://github.com/deepin-community/template-repository -#Vcs-Browser: https://salsa.debian.org/debian/deepin-community-template-repository -#Vcs-Git: https://salsa.debian.org/debian/deepin-community-template-repository.git +Maintainer: Debian Java Maintainers +Build-Depends: + ant, + debhelper (>= 13), + default-jdk, + javahelper +Standards-Version: 4.6.2 +Vcs-Git: https://salsa.debian.org/java-team/asmtools.git +Vcs-Browser: https://salsa.debian.org/java-team/asmtools +Homepage: https://github.com/openjdk/asmtools +Rules-Requires-Root: no -Package: template-repository -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} -Description: - +Package: libasmtools-java +Architecture: all +Depends: ${java:Depends}, ${misc:Depends} +Description: OpenJDK AsmTools + The AsmTools open source project is used to develop tools for the production + of proper and improper Java '.class' files. AsmTools are being opened in order + to facilitate a community of Java .class file production for various testing + and other OpenJDK development applications. diff --git a/debian/copyright b/debian/copyright index f5c805e..7483236 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,22 +1,16 @@ Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ -Upstream-Name: template-repository -Source: https://github.com/deepin-community/template-repository +Upstream-Name: asmtools +Upstream-Contact: code-tools-dev@openjdk.org +Source: https://github.com/openjdk/asmtools/blob/master/LICENSE Files: * -Copyright: 2023 Tsic404 -License: GPL-2+ - This package is free software; you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 2 of the License, or - (at your option) any later version. - . - This package is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - . - You should have received a copy of the GNU General Public License - along with this program. If not, see - . - On Debian systems, the complete text of the GNU General - Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". +Copyright: 2014-2019, Oracle and/or its affiliates. +License: GPL-2.0 + On Debian systems the full text of the GPL-2.0 license can be found in + /usr/share/common-licenses/GPL-2.0. + +Files: debian/* +Copyright: 2023 Debian Java Maintainers +License: Apache-2.0 + On Debian systems the full text of the Apache-2.0 license can be found in + /usr/share/common-licenses/Apache-2.0. diff --git a/debian/libasmtools-java.javadoc b/debian/libasmtools-java.javadoc new file mode 100644 index 0000000..d49522d --- /dev/null +++ b/debian/libasmtools-java.javadoc @@ -0,0 +1 @@ +build/asmtools/javadoc \ No newline at end of file diff --git a/debian/libasmtools-java.jlibs b/debian/libasmtools-java.jlibs new file mode 100644 index 0000000..181aaab --- /dev/null +++ b/debian/libasmtools-java.jlibs @@ -0,0 +1 @@ +build/asmtools/release/lib/asmtools.jar \ No newline at end of file diff --git a/debian/rules b/debian/rules index 2d33f6a..694bf98 100755 --- a/debian/rules +++ b/debian/rules @@ -1,4 +1,13 @@ #!/usr/bin/make -f +include /usr/share/dpkg/pkg-info.mk + %: - dh $@ + dh $@ --buildsystem=ant --with javahelper + +override_dh_auto_clean: + dh_auto_clean -- -f build/build.xml -DBUILD_DIR=asmtools + +override_dh_auto_build: + JAVA_TOOL_OPTIONS=-Dfile.encoding=UTF-8 dh_auto_build -- -f build/build.xml build -DBUILD_DIR=asmtools + diff --git a/debian/upstream/metadata.ex b/debian/upstream/metadata.ex new file mode 100644 index 0000000..62da648 --- /dev/null +++ b/debian/upstream/metadata.ex @@ -0,0 +1,4 @@ +Bug-Database: https://github.com/openjdk/asmtools/issues +Bug-Submit: https://github.com/openjdk/asmtools/issues/new +Repository-Browse: https://github.com/openjdk/asmtools +Repository: https://github.com/openjdk/asmtools.git diff --git a/debian/watch b/debian/watch new file mode 100644 index 0000000..2c5d730 --- /dev/null +++ b/debian/watch @@ -0,0 +1,3 @@ +version=4 +opts="mode=git,compression=xz" \ +https://github.com/openjdk/asmtools.git refs/tags/([\d\.]+(?:-b[0-9]+)?) diff --git a/src/org/openjdk/asmtools/Main.java b/src/org/openjdk/asmtools/Main.java new file mode 100644 index 0000000..844c0ee --- /dev/null +++ b/src/org/openjdk/asmtools/Main.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools; + +import org.openjdk.asmtools.util.I18NResourceBundle; +import org.openjdk.asmtools.util.ProductInfo; + +/** + * Wrapper class that reads the first command line argument and invokes a corresponding + * tool. + */ +public class Main { + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + /** + * Parses the first argument and deligates execution to an appropriate tool + * + * @param args - command line arguments + */ + public static void main(String args[]) { + if (args.length == 0) { + usage(i18n.getString("main.error.no_arguments"), 1); + } + String cmd = args[0]; + if (cmd.equals("-?") || cmd.equals("-h") || cmd.equals("-help")) { + usage(null, 0); + } else if (cmd.equals("-version")) { + printVersion(); + } else { + String[] newArgs = new String[args.length - 1]; + System.arraycopy(args, 1, newArgs, 0, args.length - 1); + if (cmd.equals("jasm")) { + jasm(newArgs); + } else if (cmd.equals("jdis")) { + jdis(newArgs); + } else if (cmd.equals("jcoder")) { + jcoder(newArgs); + } else if (cmd.equals("jdec")) { + jdec(newArgs); + } else if (cmd.equals("jcdec")) { + jcdec(newArgs); + } else { + usage(i18n.getString("main.error.unknown_tool", cmd), 1); + } + } + } + + /** + * Prints usage info and error message, afterwards invokes System.exit() + * + * @param msg - error message to print, or null if no errors occurred + * @param exitCode - exit code to be returned by System.exit() + */ + public static void usage(String msg, int exitCode) { + System.err.println(i18n.getString("main.usage", "asmtools.jar")); + if (msg != null) { + System.err.println(msg); + } + System.exit(exitCode); + } + + /** + * Prints the tools version + */ + public static void printVersion() { + System.out.println(ProductInfo.FULL_VERSION); + } + + /** + * Invokes jasm main class with passed arguments + */ + public static void jasm(String[] args) { + org.openjdk.asmtools.jasm.Main.main(args); + } + + /** + * Invokes jcdec main class with passed arguments + */ + public static void jcdec(String[] args) { + org.openjdk.asmtools.jcdec.Main.main(args); + } + + /** + * Invokes jcoder main class with passed arguments + */ + public static void jcoder(String[] args) { + org.openjdk.asmtools.jcoder.Main.main(args); + } + + /** + * Invokes jdec main class with passed arguments + */ + public static void jdec(String[] args) { + org.openjdk.asmtools.jdec.Main.main(args); + } + + /** + * Invokes jdis main class with passed arguments + */ + public static void jdis(String[] args) { + org.openjdk.asmtools.jdis.Main.main(args); + } +} diff --git a/src/org/openjdk/asmtools/asmutils/HexUtils.java b/src/org/openjdk/asmtools/asmutils/HexUtils.java new file mode 100644 index 0000000..0d20b4a --- /dev/null +++ b/src/org/openjdk/asmtools/asmutils/HexUtils.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.asmutils; + +/** + * + */ +public class HexUtils { + /*======================================================== Hex */ + + private static final String hexString = "0123456789ABCDEF"; + private static final char hexTable[] = hexString.toCharArray(); + + public static String toHex(long val, int width) { + StringBuffer sb = new StringBuffer(); + for (int i = width - 1; i >= 0; i--) { + sb.append(hexTable[((int) (val >> (4 * i))) & 0xF]); + } + String s = sb.toString(); + return "0x" + (s.isEmpty() ? "0" : s); + } + + public static String toHex(long val) { + int width; + for (width = 16; width > 0; width--) { + if ((val >> (width - 1) * 4) != 0) { + break; + } + } + return toHex(val, width); + } + + public static String toHex(int val) { + int width; + for (width = 8; width > 0; width--) { + if ((val >> (width - 1) * 4) != 0) { + break; + } + } + return toHex(val, width); + } + +} diff --git a/src/org/openjdk/asmtools/asmutils/StringUtils.java b/src/org/openjdk/asmtools/asmutils/StringUtils.java new file mode 100644 index 0000000..598511a --- /dev/null +++ b/src/org/openjdk/asmtools/asmutils/StringUtils.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.asmutils; + +/** + * Utility class to share common tools/methods. + */ +public class StringUtils { + /** + * Converts CONSTANT_Utf8_info string to a printable string for jdis/jdes. + * @param utf8 UTF8 string taken from within ConstantPool of a class file + * @return output string for jcod/jasm + */ + public static String Utf8ToString(String utf8) { + StringBuilder sb = new StringBuilder("\""); + for (int k = 0; k < utf8.length(); k++) { + char c = utf8.charAt(k); + switch (c) { + case '\t': + sb.append('\\').append('t'); + break; + case '\n': + sb.append('\\').append('n'); + break; + case '\r': + sb.append('\\').append('r'); + break; + case '\b': + sb.append('\\').append('b'); + break; + case '\f': + sb.append('\\').append('f'); + break; + case '\"': + sb.append('\\').append('\"'); + break; + case '\'': + sb.append('\\').append('\''); + break; + case '\\': + sb.append('\\').append('\\'); + break; + default: + sb.append(c); + } + } + return sb.append('\"').toString(); + } +} diff --git a/src/org/openjdk/asmtools/common/Module.java b/src/org/openjdk/asmtools/common/Module.java new file mode 100644 index 0000000..a129475 --- /dev/null +++ b/src/org/openjdk/asmtools/common/Module.java @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.common; + +import org.openjdk.asmtools.jdis.Indenter; + +import java.util.*; +import java.util.stream.Collectors; + +import static java.lang.String.format; + +/** + * Internal presentation of a module + */ +public final class Module extends Indenter { + + //* A module name and module_flags + public final Header header; + //* A service dependence's of this module + public final Set uses; + //* Modules on which the current module has a dependence. + public final Set requires; + //* A module exports, may be qualified or unqualified. + public final Map> exports; + //* Packages, to be opened by the current module + public final Map> opens; + //* A service that a module provides one or more implementations of. + public final Map> provides; + + private Module(Builder builder) { + this.header = builder.header; + this.requires = Collections.unmodifiableSet(builder.requires); + this.exports = Collections.unmodifiableMap(builder.exports); + this.opens = Collections.unmodifiableMap(builder.opens); + this.uses = Collections.unmodifiableSet(builder.uses); + this.provides = Collections.unmodifiableMap(builder.provides); + } + + public String getModuleFlags () { + return Modifier.getModuleModifiers(header.getFlags()); + } + public String getModuleName () { return header.getModuleName(); } + public String getModuleVersion() { return header.getModuleVersion(); }; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + int l = 0; + requires.stream() + .sorted() + .forEach(d -> sb.append(getIndentString()).append(format("requires %s;%s%n", + d.toString(), + d.getModuleVersion() == null ? "" : " // @" + d.getModuleVersion()))); + // + l = newLine(sb,l); + exports.entrySet().stream() + .filter(e -> e.getValue().isEmpty()) + .sorted(Map.Entry.comparingByKey()) + .map(e -> format("%sexports %s;%n", getIndentString(), e.getKey().toString())) + .forEach(sb::append); + exports.entrySet().stream() + .filter(e -> !e.getValue().isEmpty()) + .sorted(Map.Entry.comparingByKey()) + .map(e -> format("%sexports %s to%n%s;%n", getIndentString(), e.getKey().toString(), + e.getValue().stream().sorted() + .map(mn -> format("%s %s", getIndentString(), mn)) + .collect(Collectors.joining(",\n")))) + .forEach(sb::append); + // + l = newLine(sb,l); + opens.entrySet().stream() + .filter(e -> e.getValue().isEmpty()) + .sorted(Map.Entry.comparingByKey()) + .map(e -> format("%sopens %s;%n", getIndentString(), e.getKey().toString())) + .forEach(sb::append); + opens.entrySet().stream() + .filter(e -> !e.getValue().isEmpty()) + .sorted(Map.Entry.comparingByKey()) + .map(e -> format("%sopens %s to%n%s;%n", getIndentString(), e.getKey().toString(), + e.getValue().stream().sorted() + .map(mn -> format("%s %s", getIndentString(), mn)) + .collect(Collectors.joining(",\n")))) + .forEach(sb::append); + // + l = newLine(sb,l); + uses.stream().sorted() + .map(s -> format("%suses %s;%n", getIndentString(), s)) + .forEach(sb::append); + // + l = newLine(sb,l); + provides.entrySet().stream() + .filter(e -> !e.getValue().isEmpty()) + .sorted(Map.Entry.comparingByKey()) + .map(e -> format("%sprovides %s with%n%s;%n", getIndentString(), e.getKey().toString(), + e.getValue().stream().sorted() + .map(mn -> format("%s %s", getIndentString(), mn)) + .collect(Collectors.joining(",\n")))) + .forEach(sb::append); + // + if( Character.isWhitespace(sb.charAt(sb.length()-1)) ) + sb.deleteCharAt(sb.length()-1); + return sb.toString(); + } + + private int newLine(StringBuilder sb, int length) { + if(sb.length() > length) { + sb.append("\n"); + return sb.length() + 1; + } + return length; + } + + /** + * Modules flags + */ + public enum Modifier { + ACC_NONE(0x0000, "", ""), + ACC_OPEN(0x0020, "open", "ACC_OPEN"), + ACC_TRANSITIVE(0x0020, "transitive", "ACC_TRANSITIVE"), + ACC_STATIC_PHASE(0x0040, "static", "ACC_STATIC_PHASE"), + ACC_SYNTHETIC(0x1000, "", "ACC_SYNTHETIC"), + ACC_MANDATED(0x8000, "", "ACC_MANDATED"); + private final int value; + private final String keyword; + private final String flag; + Modifier(int value, String keyword, String flagName) { + this.value = value; + this.keyword = keyword; + this.flag = flagName; + } + + public int asInt() { return value; } + + public static String getModuleModifiers(int flag) { + return asString(flag, false, ACC_TRANSITIVE); + } + + public static String getModuleFlags(int flag) { + return asString(flag, true, ACC_TRANSITIVE); + } + + public static String getStatementModifiers(int flag) { + return asString(flag, false, ACC_OPEN); + } + + public static String getStatementFlags(int flag) { + return asString(flag, true, ACC_OPEN); + } + + private static String asString(int value, boolean flagFormat, Modifier skipped ) { + String buf = ""; + for(Module.Modifier m : values()) { + if( m != skipped && (value & m.value) != 0) { + buf += ((flagFormat) ? m.flag : m.keyword) + " "; + value ^= m.value; + } + } + if( flagFormat && value != 0 ) + buf += String.format("0x%04X ", value); + return buf; + } + } + + // A module header consists of a module name and module flags + public final static class Header extends VersionedFlaggedTargetType{ + Header(String typeName, int flag) { this(typeName, flag, null); } + Header(String typeName, int flag, String moduleVersion) { super(typeName, flag, moduleVersion); } + public String getModuleName() { return getTypeName(); } + public int getModuleFlags() { return getFlags(); } + public String getModuleVersion() { return getVersion(); } + } + + //* A module on which the current module has a dependence. + public final static class Dependence extends VersionedFlaggedTargetType { + public Dependence(String moduleName, int flag) {this(moduleName, flag, null);} + public Dependence(String moduleName, int flag, String moduleVersion) {super(moduleName, flag, moduleVersion);} + public Dependence(String moduleName, boolean transitive, boolean staticPhase) { this(moduleName,transitive,staticPhase,null);} + public Dependence(String moduleName, boolean transitive, boolean staticPhase, String moduleVersion) { + this(moduleName, + (transitive ? Modifier.ACC_TRANSITIVE.value : Modifier.ACC_NONE.value) | + (staticPhase ? Modifier.ACC_STATIC_PHASE.value : Modifier.ACC_NONE.value), moduleVersion); + } + public String getModuleVersion() { return getVersion(); } + } + + public final static class Uses extends TargetType { + public Uses(String typeName) { super(typeName); } + } + + //* A provided type of the current module. + public final static class Provided extends TargetType { + public Provided(String typeName) { super(typeName); } + } + + //* An opened package of the current module. + public final static class Opened extends FlaggedTargetType { + public Opened(String typeName) { + super(typeName, 0); + } + public Opened(String typeName, int opensFlags) { + super(typeName, opensFlags); + } + } + + //* An exported package of the current module. + public final static class Exported extends FlaggedTargetType { + public Exported(String typeName) { + super(typeName, 0); + } + + public Exported(String typeName, int exportsFlags) { + super(typeName, exportsFlags); + } + } + + public static class VersionedFlaggedTargetType extends FlaggedTargetType { + private String version; + + VersionedFlaggedTargetType(String typeName, int flag) { + this(typeName,flag, null); + } + + VersionedFlaggedTargetType(String typeName, int flag, String version) { + super(typeName, flag); + this.version = version != null && !version.isEmpty() ? version : null; + } + public String getVersion() { return version; } + + @Override + public int hashCode() { + int code = version == null ? 0 : version.hashCode(); + return code + super.hashCode(); + } + } + + public static class FlaggedTargetType extends TargetType { + private int flag; + + FlaggedTargetType(String typeName, int flag) { + super(typeName); + this.flag = flag; + } + + public boolean isFlagged() { + return true; + } + + public int getFlags() { + return flag; + } + + public void setFlag(int value) { flag = value; } + + @Override + public int hashCode() { + return super.hashCode() + flag; + } + + @Override + public boolean equals(Object o) { + return super.equals(o) && ((FlaggedTargetType) o).flag == this.flag; + } + + @Override + public String toString() { + return Modifier.getStatementModifiers(this.flag)+ super.toString(); + } + } + + public static class TargetType implements Comparable { + private String typeName; + + TargetType(String typeName) { this.typeName = typeName; } + + public String getTypeName() { + return typeName; + } + + public void setTypeName(String value) { typeName = value; } + + public boolean isFlagged() { + return false; + } + + @Override + public int hashCode() { return typeName.hashCode() * 11; } + + @Override + public boolean equals(Object o) { + if (o instanceof TargetType) { + TargetType t = (TargetType) o; + return this.typeName.equals(t.getTypeName()); + } + return false; + } + + @Override + public int compareTo(TargetType t) { + return this.typeName.compareTo(t.getTypeName()); + } + + @Override + public String toString() { + return typeName; + } + } + + /** + * The module builder. + */ + public static final class Builder { + final Header header; + final Set requires = new HashSet<>(); + final Map> exports = new HashMap<>(); + final Map> opens = new HashMap<>(); + final Set uses = new HashSet<>(); + final Map> provides = new HashMap<>(); + + + public Builder() { + this("", Modifier.ACC_NONE.asInt(), null); + } + + public Builder(String moduleName, int moduleFlags, String moduleVersion) { + header = new Header( moduleName,moduleFlags, moduleVersion); + } + + public Builder setModuleFlags(int moduleFlags) { + header.setFlag(header.getFlags() | moduleFlags); + return this; + } + + public Builder setModuleFlags(Modifier... moduleFlags) { + for (Modifier m : moduleFlags) + setModuleFlags(m.value); + return this; + } + + public Builder setModuleName(String value) { + header.setTypeName(value); + return this; + } + + public Builder require(String d, boolean transitive, boolean staticPhase, String version) { + requires.add(new Dependence(d, transitive, staticPhase, version)); + return this; + } + + public Builder require(String d, int requiresFlag, String version) { + requires.add(new Dependence(d, requiresFlag, version)); + return this; + } + + public Builder require(String d, int requiresFlag) { + requires.add(new Dependence(d, requiresFlag, null)); + return this; + } + + public Builder opens(Opened p, Set ms) { + return add(opens, p, ms); + } + + public Builder opens(String packageName, int exportFlags, Set ms) { + return add(opens, new Opened(packageName, exportFlags), ms); + } + + public Builder opens(String packageName, int exportFlags) { + return add(opens, new Opened(packageName, exportFlags), new HashSet<>()); + } + + + public Builder exports(Exported p, Set ms) { + return add(exports, p, ms); + } + + public Builder exports(String packageName, int exportFlags, Set ms) { + return add(exports, new Exported(packageName, exportFlags), ms); + } + + public Builder exports(String packageName, int exportFlags) { + return add(exports, new Exported(packageName, exportFlags), new HashSet<>()); + } + + public Builder uses(String serviceName) { + uses.add(new Uses(serviceName)); + return this; + } + + + public Builder uses(Set serviceNames) { + uses.addAll(serviceNames.stream().map(Uses::new).collect(Collectors.toList())); + return this; + } + + public Builder provides(Provided t, Set implementations) { + return add(provides, t, implementations); + } + + public Builder provides(String serviceName, Set implementations) { + return add(provides, new Provided(serviceName), implementations); + } + + + /** + * @return The new module + */ + public Module build() { + return new Module(this); + } + + private Builder add( Map> collection, T source, Set target) { + Objects.requireNonNull(source); + Objects.requireNonNull(target); + if (!collection.containsKey(source)) + collection.put(source, new HashSet<>()); + collection.get(source).addAll(target); + return this; + } + } +} diff --git a/src/org/openjdk/asmtools/common/Tool.java b/src/org/openjdk/asmtools/common/Tool.java new file mode 100644 index 0000000..9ce9606 --- /dev/null +++ b/src/org/openjdk/asmtools/common/Tool.java @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.common; + + +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.net.URLConnection; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; + +public abstract class Tool { + + // Name of the program. + protected final String programName; + // Errors counter. + protected int nerrors = 0; + // The stream where error message are printed. + protected PrintWriter err; + + // Output stream + protected PrintWriter out; + + // A consumer to print a error message if the tool can't read a file + protected Consumer printCannotReadMsg; + + // A supplier to get a status of a debug flag + protected BooleanSupplier DebugFlag = () -> false; + + public Tool(PrintWriter out, String programName) { + this(out, out, programName); + } + + public Tool(PrintWriter out, PrintWriter err, String programName) { + this.out = out; + this.err = err; + this.programName = programName; + } + + + public String getError(String msg) { + return programName + ": " + msg; + } + + /** + * Top level error message + */ + public void error(String msg) { + err.println(getError(msg)); + err.flush(); + } + + /** + * Top level print message + */ + public void println(String msg) { + out.println(msg); + out.flush(); + } + + public void println() { + println(""); + } + + public void print(String msg) { + out.print(getError(msg)); + out.flush(); + } + + /** + * @param fname file name + * @return DataInputStream or null if the method can't read a file + */ + public DataInputStream getDataInputStream(String fname) { + try { + return new DataInputStream(new FileInputStream(fname)); + } catch (IOException ex) { + if (fname.matches("^[A-Za-z]+:.*")) { + try { + final URI uri = new URI(fname); + final URL url = uri.toURL(); + final URLConnection conn = url.openConnection(); + conn.setUseCaches(false); + return new DataInputStream(conn.getInputStream()); + } catch (URISyntaxException | IOException e) { + if (DebugFlag.getAsBoolean()) + e.printStackTrace(); + } + } + if (printCannotReadMsg != null) + printCannotReadMsg.accept(fname); + } + return null; + } + + /** + * Usage + */ + protected abstract void usage(); +} diff --git a/src/org/openjdk/asmtools/i18n.properties b/src/org/openjdk/asmtools/i18n.properties new file mode 100644 index 0000000..3969041 --- /dev/null +++ b/src/org/openjdk/asmtools/i18n.properties @@ -0,0 +1,33 @@ +# Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +main.usage=\ +Usage: \n\ +to run an assembly tool: \n\ +\ $ java -jar {0} toolName [args...] \n\ +\ where toolName one of: jasm, jdis, jcoder, jdec, jcdec \n\ +to get the version: \n\ +\ $ java -jar {0} -version \n\ +to get this message \n\ +\ $ java -jar {0} -?|-h|-help\n + +main.error.no_arguments=No arguments provided! See options above. +main.error.unknown_tool=Tool name ''{0}'' unrecognized. See usage above for possible tool choices. \ No newline at end of file diff --git a/src/org/openjdk/asmtools/jasm/AnnotationData.java b/src/org/openjdk/asmtools/jasm/AnnotationData.java new file mode 100644 index 0000000..60072a8 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/AnnotationData.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * JVMS 4.7.16. + * + * annotation { + * u2 type_index; + * u2 num_element_value_pairs; + * { u2 element_name_index; + * element_value value; + * } element_value_pairs[num_element_value_pairs]; + * } + */ +class AnnotationData implements Data { + + boolean invisible; + Argument typeCPX; + ArrayList elemValuePairs; + int annotationLength = 0; + + /** + * AnnotationElemValue + * + * Used to store Annotation Data + */ + static public class ElemValuePair implements Data { + + ConstantPool.ConstCell name; + Data value; + + public ElemValuePair(ConstantPool.ConstCell name, Data value) { + this.name = name; + this.value = value; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + name.write(out); + value.write(out); + } + + @Override + public int getLength() { + return 2 + value.getLength(); + } + } + + public AnnotationData(Argument typeCPX, boolean invisible) { + this.typeCPX = typeCPX; + this.elemValuePairs = new ArrayList<>(); + this.invisible = invisible; + } + + public void add(ElemValuePair elemValuePair) { + elemValuePairs.add(elemValuePair); + annotationLength += elemValuePair.getLength(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(typeCPX.arg); + out.writeShort(elemValuePairs.size()); + + for (Data pair : elemValuePairs) { + pair.write(out); + } + } + + @Override + public int getLength() { + return 4 + annotationLength; + } +} diff --git a/src/org/openjdk/asmtools/jasm/Argument.java b/src/org/openjdk/asmtools/jasm/Argument.java new file mode 100644 index 0000000..316c401 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Argument.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +/** + * + */ +class Argument { + + static final int NotSet = -1; + int arg; + + Argument() { + arg = NotSet; + } + + Argument(int arg) { + this.arg = arg; + } + + public int hashCode() { + return arg; + } + + /** + * Compares this object to the specified object. + * + * @param obj the object to compare with + * @return true if the objects are the same; false otherwise. + */ + public boolean equals(Object obj) { + throw new Parser.CompilerError("ConstCell.equals"); + } + + boolean isSet() { + return arg != NotSet; + } +} diff --git a/src/org/openjdk/asmtools/jasm/AttrData.java b/src/org/openjdk/asmtools/jasm/AttrData.java new file mode 100644 index 0000000..2b3c896 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/AttrData.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; + +/** + * AttrData + * + * AttrData is the base class for many attributes (or parts of attributes), and it is + * instantiated directly for simple attributes (like Synthetic or Deprecated). + */ +class AttrData implements Data { + + private final ClassData clsData; + private final Argument attrNameCPX; + + AttrData(ClassData cdata, String name) { + clsData = cdata; + attrNameCPX = cdata.pool.FindCellAsciz(name); + } + + protected ClassData getClassData() { + return clsData; + } + + // full length of the attribute + // declared in Data + public int getLength() { + return 6 + attrLength(); + } + + // subclasses must redefine this + public int attrLength() { + return 0; + } + + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(attrNameCPX.arg); + out.writeInt(attrLength()); // attr len + } +} // end class AttrData + diff --git a/src/org/openjdk/asmtools/jasm/BootstrapMethodData.java b/src/org/openjdk/asmtools/jasm/BootstrapMethodData.java new file mode 100644 index 0000000..31bdfe4 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/BootstrapMethodData.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * + */ +class BootstrapMethodData extends Argument implements Data { + + ConstantPool.ConstCell bootstrapMethodHandle; + ArrayList arguments; + public int placeholder_index = -1; + + public BootstrapMethodData(ConstantPool.ConstCell bsmHandle, ArrayList arguments) { + super(); + this.bootstrapMethodHandle = bsmHandle; + this.arguments = arguments; + } + + public BootstrapMethodData(int placeholder) { + super(); + this.bootstrapMethodHandle = null; + this.arguments = null; + this.placeholder_index = placeholder; + } + + public int getLength() { + return 4 + arguments.size() * 2; + } + + public boolean isPlaceholder() { + return placeholder_index > -1; + } + + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(bootstrapMethodHandle.arg); + out.writeShort(arguments.size()); + + for (ConstantPool.ConstCell argument : arguments) { + out.writeShort(argument.arg); + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/CFVersion.java b/src/org/openjdk/asmtools/jasm/CFVersion.java new file mode 100644 index 0000000..7d1944e --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/CFVersion.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +/* + * Class File Version + */ +public class CFVersion implements Cloneable{ + /** + * Default versions of class file + */ + public static final short DEFAULT_MAJOR_VERSION = 45; + public static final short DEFAULT_MINOR_VERSION = 3; + public static final short DEFAULT_MODULE_MAJOR_VERSION = 53; + public static final short DEFAULT_MODULE_MINOR_VERSION = 0; + public static final short UNDEFINED_VERSION = -1; + + private short major_version; + private short minor_version; + private boolean frozen; + private boolean isSet; + + public CFVersion() { + frozen = false; + isSet = false; + major_version = UNDEFINED_VERSION; + minor_version = UNDEFINED_VERSION; + } + + public CFVersion(boolean frozenCFV, short major_version, short minor_version) { + isSet = true; + frozen = frozenCFV; + this.major_version = major_version; + this.minor_version = minor_version; + } + + public void setMajorVersion(short major_version) { + if ( !frozen ) { + isSet = true; + this.major_version = major_version; + } + } + + public void setMinorVersion(short minor_version) { + if (!frozen) { + isSet = true; + this.minor_version = minor_version; + } + } + + public String asString() { + return (isSet) ? this.major_version + ":" +this.minor_version : "(undef):(undef)"; + } + + public void initModuleDefaults() { + if( ! isSet) { + major_version = DEFAULT_MODULE_MAJOR_VERSION; + minor_version = DEFAULT_MODULE_MINOR_VERSION; + } + } + + public void initClassDefaults() { + if( !isSet ) { + major_version = DEFAULT_MAJOR_VERSION; + minor_version = DEFAULT_MINOR_VERSION; + } + } + + public short minor_version() { + return this.minor_version; + } + + public short major_version() { + return this.major_version; + } + + public CFVersion clone() { + try { + return (CFVersion)super.clone(); + } catch (CloneNotSupportedException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/CPXAttr.java b/src/org/openjdk/asmtools/jasm/CPXAttr.java new file mode 100644 index 0000000..57aede1 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/CPXAttr.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; + +/** + * Constant Pool Index Attribute + */ +class CPXAttr extends AttrData { + + Argument cell; + + public CPXAttr(ClassData cls, String attrName, Argument cell) { + super(cls, attrName); + this.cell = cell; + } + + public int attrLength() { + return 2; + } + + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); // attr name, attr len + out.writeShort(cell.arg); + } +} // end class CPXAttr + diff --git a/src/org/openjdk/asmtools/jasm/CheckedDataOutputStream.java b/src/org/openjdk/asmtools/jasm/CheckedDataOutputStream.java new file mode 100644 index 0000000..abb0dfd --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/CheckedDataOutputStream.java @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2014, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * To change this template, choose Tools | Templates + * and open the template in the editor. + */ +package org.openjdk.asmtools.jasm; + +import java.io.DataOutput; +import java.io.IOException; + +/** + * + */ +public interface CheckedDataOutputStream { + + public void write(int b) throws IOException; + + public void write(byte b[], int off, int len) throws IOException; + + public void writeBoolean(boolean v) throws IOException; + + public void writeByte(int v) throws IOException; + + public void writeShort(int v) throws IOException; + + public void writeChar(int v) throws IOException; + + public void writeInt(int v) throws IOException; + + public void writeLong(long v) throws IOException; + + public void writeFloat(float v) throws IOException; + + public void writeDouble(double v) throws IOException; + + public void writeBytes(String s) throws IOException; + + public void writeChars(String s) throws IOException; + + public void writeUTF(String s) throws IOException; + +} diff --git a/src/org/openjdk/asmtools/jasm/ClassArrayAttr.java b/src/org/openjdk/asmtools/jasm/ClassArrayAttr.java new file mode 100644 index 0000000..6bd0d79 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ClassArrayAttr.java @@ -0,0 +1,69 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.List; + +/** + * Base class of the "classes[]" data of attributes + *

+ * JEP 181 (Nest-based Access Control): class file 55.0 + * NestMembers_attribute { + * u2 attribute_name_index; + * u4 attribute_length; + * u2 number_of_classes; + * u2 classes[number_of_classes]; + * } + *

+ * JEP 360 (Sealed types): class file 59.65535 + * PermittedSubclasses_attribute { + * u2 attribute_name_index; + * u4 attribute_length; + * u2 number_of_classes; + * u2 classes[number_of_classes]; + * } + */ +public class ClassArrayAttr extends AttrData { + + List classes; + + public ClassArrayAttr(String attributeName, ClassData cdata, List classes) { + super(cdata, attributeName); + this.classes = classes; + } + + @Override + public int attrLength() { + return 2 + classes.size() * 2; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + out.writeShort(classes.size()); + for (ConstantPool.ConstCell c : classes) { + out.writeShort(c.arg); + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/ClassData.java b/src/org/openjdk/asmtools/jasm/ClassData.java new file mode 100644 index 0000000..7f20d94 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ClassData.java @@ -0,0 +1,641 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.*; +import java.util.ArrayList; +import java.util.List; + +import static org.openjdk.asmtools.jasm.Tables.*; + +/** + * ClassData + * + * This is the main data structure for representing parsed class data. This structure + * renders directly to a class file. + * + */ +class ClassData extends MemberData { + + /* ClassData Fields */ + CFVersion cfv; + ConstantPool.ConstCell me, father; + String myClassName; + AttrData sourceFileNameAttr; + ArrayList interfaces; + ArrayList fields = new ArrayList<>(); + ArrayList methods = new ArrayList<>(); + DataVectorAttr innerClasses = null; + DataVectorAttr bootstrapMethodsAttr = null; + + // JEP 181 - NestHost, NestMembers attributes since class version 55.0 + CPXAttr nestHostAttr; + NestMembersAttr nestMembersAttr; + + // JEP 359 - Record attribute since class file 58.65535 + private RecordData recordData; + + // JEP 360 - PermittedSubclasses attribute since class file 59.65535 + private PermittedSubclassesAttr permittedSubclassesAttr; + + ModuleAttr moduleAttribute = null; + Environment env; + protected ConstantPool pool; + + private static final String DEFAULT_EXTENSION = ".class"; + String fileExtension = DEFAULT_EXTENSION; + public CDOutputStream cdos; + + /** + * Initializes the ClassData. + * + * @param me The constant pool reference to this class + * @param father The constant pool reference to the super class + * @param interfaces A list of interfaces that this class implements + */ + public final void init(int access, ConstantPool.ConstCell me, ConstantPool.ConstCell father, ArrayList interfaces) { + this.access = access; + + // normalize the modifiers to access flags + if (Modifiers.hasPseudoMod(access)) { + createPseudoMod(); + } + + this.me = me; + if (father == null) { + father = pool.FindCellClassByName("java/lang/Object"); + } + this.father = father; + this.interfaces = interfaces; + // Set default class file version if it is not set. + cfv.initClassDefaults(); + } + + public final void initAsModule() { + this.access = RuntimeConstants.ACC_MODULE; + // this_class" module-info + this.me = pool.FindCellClassByName("module-info"); + // super_class: zero + this.father = new ConstantPool.ConstCell(0); + cfv.initModuleDefaults(); + } + + /** + * canonical default constructor + * + * @param env The error reporting environment. + * @param cfv The class file version that this class file supports. + */ + public ClassData(Environment env, CFVersion cfv) { + super(null, 0); // for a class, these get inited in the super - later. + cls = this; + + this.env = env; + this.cfv = cfv; + + pool = new ConstantPool(env); + cdos = new CDOutputStream(); + } + + /** + * Predicate that describes if this class has an access flag indicating that it is an + * interface. + * + * @return True if the classes access flag indicates it is an interface. + */ + public final boolean isInterface() { + return Modifiers.isInterface(access); + } + + /* + * After a constant pool has been explicitly declared, + * this method links the Constant_InvokeDynamic Constant_Dynamic + * constants with any bootstrap methods that they index in the + * Bootstrap Methods Attribute + */ + protected void relinkBootstrapMethods() { + if (bootstrapMethodsAttr == null) { + return; + } + + env.traceln("relinkBootstrapMethods"); + + for (ConstantPool.ConstCell cell : pool) { + ConstantPool.ConstValue ref = null; + if (cell != null) { + ref = cell.ref; + } + if (ref != null + && (ref.tag == ConstType.CONSTANT_INVOKEDYNAMIC || ref.tag == ConstType.CONSTANT_DYNAMIC)) { + // Find only the Constant + ConstantPool.ConstValue_IndyOrCondyPair refval = (ConstantPool.ConstValue_IndyOrCondyPair) ref; + BootstrapMethodData bsmdata = refval.bsmData; + // only care about BSM Data that were placeholders + if (bsmdata != null && bsmdata.isPlaceholder()) { + // find the real BSM Data at the index + int bsmindex = bsmdata.placeholder_index; + if (bsmindex < 0 || bsmindex > bootstrapMethodsAttr.size()) { + // bad BSM index -- + // give a warning, but place the index in the arg anyway + env.traceln("Warning: (ClassData.relinkBootstrapMethods()): Bad bootstrapMethods index: " + bsmindex); + // env.error("const.bsmindex", bsmindex); + bsmdata.arg = bsmindex; + } else { + + BootstrapMethodData realbsmdata = bootstrapMethodsAttr.get(bsmindex); + // make the IndyPairs BSM Data point to the one from the attribute + refval.bsmData = realbsmdata; + } + } + } + } + } + + protected void numberBootstrapMethods() { + env.traceln("Numbering Bootstrap Methods"); + if (bootstrapMethodsAttr == null) { + return; + } + + int index = 0; + for (BootstrapMethodData data : bootstrapMethodsAttr) { + data.arg = index++; + } + } + + // API + // Record + public RecordData setRecord(int where) { + if( recordAttributeExists() ) { + env.error(where, "warn.record.repeated"); + } + this.recordData = new RecordData(cls); + return this.recordData; + } + + /** + * Rejects a record: removes the record attribute if there are no components + */ + public void rejectRecord() { + this.recordData = null; + } + + // Field + public ConstantPool.ConstValue_Pair mkNape(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { + return new ConstantPool.ConstValue_Pair(ConstType.CONSTANT_NAMEANDTYPE, name, sig); + } + + public ConstantPool.ConstValue_Pair mkNape(String name, String sig) { + return mkNape(pool.FindCellAsciz(name), pool.FindCellAsciz(sig)); + } + + public FieldData addFieldIfAbsent(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { + ConstantPool.ConstValue_Pair nape = mkNape(name, sig); + env.traceln(" [ClassData.addFieldIfAbsent]: #" + nape.left.arg + ":#" + nape.right.arg); + FieldData fd = getField(nape); + if( fd == null ) { + env.traceln(" [ClassData.addFieldIfAbsent]: new field."); + fd = addField(access,nape); + } + return fd; + } + + private FieldData getField(ConstantPool.ConstValue_Pair nape) { + for (FieldData fd : fields) { + if( fd.getNameDesc().equals(nape) ) { + return fd; + } + } + return null; + } + + public FieldData addField(int access, ConstantPool.ConstValue_Pair nape) { + env.traceln(" [ClassData.addField]: #" + nape.left.arg + ":#" + nape.right.arg); + FieldData res = new FieldData(this, access, nape); + fields.add(res); + return res; + } + + public FieldData addField(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { + return addField(access, mkNape(name, sig)); + } + + public FieldData addField(int access, String name, String type) { + return addField(access, pool.FindCellAsciz(name), pool.FindCellAsciz(type)); + } + + public ConstantPool.ConstCell LocalFieldRef(FieldData field) { + return pool.FindCell(ConstType.CONSTANT_FIELD, me, pool.FindCell(field.getNameDesc())); + } + + public ConstantPool.ConstCell LocalFieldRef(ConstantPool.ConstValue nape) { + return pool.FindCell(ConstType.CONSTANT_FIELD, me, pool.FindCell(nape)); + } + + public ConstantPool.ConstCell LocalFieldRef(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { + return LocalFieldRef(mkNape(name, sig)); + } + + public ConstantPool.ConstCell LocalFieldRef(String name, String sig) { + return LocalFieldRef(pool.FindCellAsciz(name), pool.FindCellAsciz(sig)); + } + + MethodData curMethod; + + public MethodData StartMethod(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell sig, ArrayList exc_table) { + EndMethod(); + env.traceln(" [ClassData.StartMethod]: #" + name.arg + ":#" + sig.arg); + curMethod = new MethodData(this, access, name, sig, exc_table); + methods.add(curMethod); + return curMethod; + } + + public void EndMethod() { + curMethod = null; + } + + public ConstantPool.ConstCell LocalMethodRef(ConstantPool.ConstValue nape) { + return pool.FindCell(ConstType.CONSTANT_METHOD, me, pool.FindCell(nape)); + } + + public ConstantPool.ConstCell LocalMethodRef(ConstantPool.ConstCell name, ConstantPool.ConstCell sig) { + return LocalMethodRef(mkNape(name, sig)); + } + + void addLocVarData(int opc, Argument arg) { + } + + public void addInnerClass(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell innerClass, ConstantPool.ConstCell outerClass) { + env.traceln("addInnerClass (with indexes: Name (" + name.toString() + "), Inner (" + innerClass.toString() + "), Outer (" + outerClass.toString() + ")."); + if (innerClasses == null) { + innerClasses = new DataVectorAttr<>(this, AttrTag.ATT_InnerClasses.parsekey()); + } + innerClasses.add(new InnerClassData(access, name, innerClass, outerClass)); + } + + public void addBootstrapMethod(BootstrapMethodData bsmData) { + env.traceln("addBootstrapMethod"); + if (bootstrapMethodsAttr == null) { + bootstrapMethodsAttr = new DataVectorAttr<>(this, AttrTag.ATT_BootstrapMethods.parsekey()); + } + bootstrapMethodsAttr.add(bsmData); + } + + public void addNestHost(ConstantPool.ConstCell hostClass) { + env.traceln("addNestHost"); + nestHostAttr = new CPXAttr(this, AttrTag.ATT_NestHost.parsekey(), hostClass); + } + + public void addNestMembers(List classes) { + env.traceln("addNestMembers"); + nestMembersAttr = new NestMembersAttr(this, classes); + } + + public void addPermittedSubclasses(List classes) { + env.traceln("addPermittedSubclasses"); + permittedSubclassesAttr = new PermittedSubclassesAttr(this, classes); + } + + + public void endClass() { + sourceFileNameAttr = new CPXAttr(this, + AttrTag.ATT_SourceFile.parsekey(), + pool.FindCellAsciz(env.getSimpleInputFileName())); + pool.NumberizePool(); + pool.CheckGlobals(); + numberBootstrapMethods(); + try { + me = pool.uncheckedGetCell(me.arg); + env.traceln("me=" + me); + ConstantPool.ConstValue_Cell me_value = (ConstantPool.ConstValue_Cell) me.ref; + ConstantPool.ConstCell ascicell = me_value.cell; + env.traceln("ascicell=" + ascicell); + ConstantPool.ConstValue_String me_str = (ConstantPool.ConstValue_String) ascicell.ref; + myClassName = me_str.value; + env.traceln("-------------------"); + env.traceln("-- Constant Pool --"); + env.traceln("-------------------"); + pool.printPool(); + env.traceln("-------------------"); + env.traceln(" "); + env.traceln(" "); + env.traceln("-------------------"); + env.traceln("-- Inner Classes --"); + env.traceln("-------------------"); + printInnerClasses(); + + } catch (Throwable e) { + env.traceln("check name:" + e); + env.error("no.classname"); + e.printStackTrace(); + } + } + + public void endModule(ModuleAttr moduleAttr) { + moduleAttribute = moduleAttr.build(); + pool.NumberizePool(); + pool.CheckGlobals(); + myClassName = "module-info"; + } + + private void printInnerClasses() { + if (innerClasses != null) { + int i = 1; + for (InnerClassData entry : innerClasses) { + env.trace(" InnerClass[" + i + "]: (" + Modifiers.toString(entry.access, CF_Context.CTX_INNERCLASS) + "]), "); + env.trace("Name: " + entry.name.toString() + " "); + env.trace("IC_info: " + entry.innerClass.toString() + " "); + env.trace("OC_info: " + entry.outerClass.toString() + " "); + env.traceln(" "); + i += 1; + } + } else { + env.traceln("<< NO INNER CLASSES >>"); + } + + } + + public void write(CheckedDataOutputStream out) throws IOException { + + // Write the header + out.writeInt(JAVA_MAGIC); + out.writeShort(cfv.minor_version()); + out.writeShort(cfv.major_version()); + + pool.write(out); + out.writeShort(access); // & MM_CLASS; // Q + out.writeShort(me.arg); + out.writeShort(father.arg); + + // Write the interface names + if (interfaces != null) { + out.writeShort(interfaces.size()); + for (Argument intf : interfaces) { + out.writeShort(intf.arg); + } + } else { + out.writeShort(0); + } + + // Write the fields + if (fields != null) { + out.writeShort(fields.size()); + for (FieldData field : fields) { + field.write(out); + } + } else { + out.writeShort(0); + } + + // Write the methods + if (methods != null) { + out.writeShort(methods.size()); + for (MethodData method : methods) { + method.write(out); + } + } else { + out.writeShort(0); + } + + // Write the attributes + DataVector attrs = getAttrVector(); + attrs.write(out); + } // end ClassData.write() + + @Override + protected DataVector getAttrVector() { + DataVector attrs = new DataVector(); + if( moduleAttribute != null ) { + if (annotAttrVis != null) + attrs.add(annotAttrVis); + if (annotAttrInv != null) + attrs.add(annotAttrInv); + attrs.add(moduleAttribute); + } else { + attrs.add(sourceFileNameAttr); + // JEP 359 since class file 58.65535 + if( recordData != null ) { + attrs.add(recordData); + } + if (innerClasses != null) + attrs.add(innerClasses); + if (syntheticAttr != null) + attrs.add(syntheticAttr); + if (deprecatedAttr != null) + attrs.add(deprecatedAttr); + if (annotAttrVis != null) + attrs.add(annotAttrVis); + if (annotAttrInv != null) + attrs.add(annotAttrInv); + if (type_annotAttrVis != null) + attrs.add(type_annotAttrVis); + if (type_annotAttrInv != null) + attrs.add(type_annotAttrInv); + if (bootstrapMethodsAttr != null) + attrs.add(bootstrapMethodsAttr); + // since class version 55.0 + if(nestHostAttributeExists()) + attrs.add(nestHostAttr); + if(nestMembersAttributesExist()) + attrs.add(nestMembersAttr); + // since class version 59.65535 (JEP 360) + if ( permittedSubclassesAttributesExist() ) + attrs.add(permittedSubclassesAttr); + } + return attrs; + } + + static char fileSeparator; //=System.getProperty("file.separator"); + + /** + * Writes to the directory passed with -d option + */ + public void write(File destdir) throws IOException { + File outfile; + if (destdir == null) { + int startofname = myClassName.lastIndexOf("/"); + if (startofname != -1) { + myClassName = myClassName.substring(startofname + 1); + } + outfile = new File(myClassName + fileExtension); + } else { + env.traceln("writing -d " + destdir.getPath()); + if (fileSeparator == 0) { + fileSeparator = System.getProperty("file.separator").charAt(0); + } + if (fileSeparator != '/') { + myClassName = myClassName.replace('/', fileSeparator); + } + outfile = new File(destdir, myClassName + fileExtension); + File outdir = new File(outfile.getParent()); + if (!outdir.exists() && !outdir.mkdirs()) { + env.error("cannot.write", outdir.getPath()); + return; + } + } + + DataOutputStream dos = new DataOutputStream( + new BufferedOutputStream(new FileOutputStream(outfile))); + cdos.setDataOutputStream(dos); + try { + write(cdos); + } finally { + dos.close(); + } + } // end write() + + public void setByteLimit(int bytelimit) { + cdos.enable(); + cdos.setLimit(bytelimit); + } + + public boolean nestHostAttributeExists() { + return nestHostAttr != null; + } + + public boolean nestMembersAttributesExist() { return nestMembersAttr != null; } + + public boolean permittedSubclassesAttributesExist() { return permittedSubclassesAttr != null; } + + public boolean recordAttributeExists() { return recordData != null; } + + /** + * This is a wrapper for DataOutputStream, used for debugging purposes. it allows + * writing the byte-stream of a class up to a given byte number. + */ + static private class CDOutputStream implements CheckedDataOutputStream { + + private int bytelimit; + private DataOutputStream dos; + public boolean enabled = false; + + public CDOutputStream() { + dos = null; + } + + public CDOutputStream(OutputStream out) { + setOutputStream(out); + } + + public final void setOutputStream(OutputStream out) { + dos = new DataOutputStream(out); + } + + public void setDataOutputStream(DataOutputStream dos) { + this.dos = dos; + } + + public void setLimit(int lim) { + bytelimit = lim; + } + + public void enable() { + enabled = true; + } + + private synchronized void check(String loc) throws IOException { + if (enabled && dos.size() >= bytelimit) { + throw new IOException(loc); + } + } + + @Override + public synchronized void write(int b) throws IOException { + dos.write(b); + check("Writing byte: " + b); + } + + @Override + public synchronized void write(byte b[], int off, int len) throws IOException { + dos.write(b, off, len); + check("Writing byte-array: " + b); + } + + @Override + public final void writeBoolean(boolean v) throws IOException { + dos.writeBoolean(v); + check("Writing writeBoolean: " + (v ? "true" : "false")); + } + + @Override + public final void writeByte(int v) throws IOException { + dos.writeByte(v); + check("Writing writeByte: " + v); + } + + @Override + public void writeShort(int v) throws IOException { + dos.writeShort(v); + check("Writing writeShort: " + v); + } + + @Override + public void writeChar(int v) throws IOException { + dos.writeChar(v); + check("Writing writeChar: " + v); + } + + @Override + public void writeInt(int v) throws IOException { + dos.writeInt(v); + check("Writing writeInt: " + v); + } + + @Override + public void writeLong(long v) throws IOException { + dos.writeLong(v); + check("Writing writeLong: " + v); + } + + @Override + public void writeFloat(float v) throws IOException { + dos.writeFloat(v); + check("Writing writeFloat: " + v); + } + + @Override + public void writeDouble(double v) throws IOException { + dos.writeDouble(v); + check("Writing writeDouble: " + v); + } + + @Override + public void writeBytes(String s) throws IOException { + dos.writeBytes(s); + check("Writing writeBytes: " + s); + } + + @Override + public void writeChars(String s) throws IOException { + dos.writeChars(s); + check("Writing writeChars: " + s); + } + + @Override + public void writeUTF(String s) throws IOException { + dos.writeUTF(s); + check("Writing writeUTF: " + s); + } + } +}// end class ClassData + diff --git a/src/org/openjdk/asmtools/jasm/CodeAttr.java b/src/org/openjdk/asmtools/jasm/CodeAttr.java new file mode 100644 index 0000000..7baa870 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/CodeAttr.java @@ -0,0 +1,529 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.jasm.OpcodeTables.Opcode; +import org.openjdk.asmtools.jasm.Tables.AttrTag; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +import static org.openjdk.asmtools.jasm.RuntimeConstants.SPLIT_VERIFIER_CFV; + +class CodeAttr extends AttrData { + + protected ClassData cls; + + protected MethodData mtd; + protected Environment env; + protected Argument max_stack, max_locals; + protected Instr zeroInstr, lastInstr; + protected int cur_pc = 0; + protected DataVector trap_table; // TrapData + protected DataVectorAttr lin_num_tb; // LineNumData + protected int lastln = 0; + protected DataVectorAttr loc_var_tb; // LocVarData + protected DataVector> attrs; + protected ArrayList slots; + protected HashMap locvarsHash; + protected HashMap labelsHash; + protected HashMap trapsHash; + protected StackMapData curMapEntry = null; + protected DataVectorAttr stackMap; + // type annotations + protected DataVectorAttr type_annotAttrVis = null; + protected DataVectorAttr type_annotAttrInv = null; + + public CodeAttr(MethodData mtd, int pos, int paramcnt, Argument max_stack, Argument max_locals) { + super(mtd.cls, AttrTag.ATT_Code.parsekey()); + this.mtd = mtd; + this.cls = mtd.cls; + this.env = cls.env; + this.max_stack = max_stack; + this.max_locals = max_locals; + lastInstr = zeroInstr = new Instr(); + trap_table = new DataVector<>(0); // TrapData + attrs = new DataVector<>(); + if (env.debugInfoFlag) { + lin_num_tb = new DataVectorAttr<>(cls, AttrTag.ATT_LineNumberTable.parsekey()); + attrs.add(lin_num_tb); + } + slots = new ArrayList<>(paramcnt); + for (int k = 0; k < paramcnt; k++) { + slots.add(k, 1); + } + } + + void endCode() { + checkTraps(); + checkLocVars(); + checkLabels(); + // + if (type_annotAttrVis != null) { + attrs.add(type_annotAttrVis); + } + if (type_annotAttrInv != null) { + attrs.add(type_annotAttrInv); + } + } + + public void addAnnotations(ArrayList list) { + for (AnnotationData item : list) { + boolean invisible = item.invisible; + if (item instanceof TypeAnnotationData) { + // Type Annotations + TypeAnnotationData ta = (TypeAnnotationData) item; + if (invisible) { + if (type_annotAttrInv == null) { + type_annotAttrInv = new DataVectorAttr(cls, + AttrTag.ATT_RuntimeInvisibleTypeAnnotations.parsekey()); + } + type_annotAttrInv.add(ta); + } else { + if (type_annotAttrVis == null) { + type_annotAttrVis = new DataVectorAttr(cls, + AttrTag.ATT_RuntimeVisibleTypeAnnotations.parsekey()); + } + type_annotAttrVis.add(ta); + } + } + } + } + + /* -------------------------------------- Traps */ + Trap trapDecl(int pos, String name) { + Trap local; + if (trapsHash == null) { + trapsHash = new HashMap<>(10); + local = null; + } else { + local = trapsHash.get(name); + } + if (local == null) { + local = new Trap(pos, name); + trapsHash.put(name, local); + } + return local; + } + + void beginTrap(int pos, String name) { + Trap trap = trapDecl(pos, name); + if (trap.start_pc != Argument.NotSet) { + env.error("trap.tryredecl", name); + return; + } + trap.start_pc = cur_pc; + } + + void endTrap(int pos, String name) { + Trap trap = trapDecl(pos, name); + if (trap.end_pc != Argument.NotSet) { + env.error("trap.endtryredecl", name); + return; + } + trap.end_pc = cur_pc; + } + + void trapHandler(int pos, String name, Argument type) { + Trap trap = trapDecl(pos, name); + trap.refd = true; + TrapData trapData = new TrapData(pos, trap, cur_pc, type); + trap_table.addElement(trapData); + } + + void checkTraps() { + if (trapsHash == null) { + return; + } + for (Trap trap : trapsHash.values()) { + if (!trap.refd) { + env.error(trap.pos, "warn.trap.notref", trap.name); + } + } + + for (TrapData trapData : trap_table) { + Trap trapLabel = trapData.trap; + if (trapLabel.start_pc == Argument.NotSet) { + env.error(trapData.pos, "trap.notry", trapLabel.name); + } + if (trapLabel.end_pc == Argument.NotSet) { + env.error(trapData.pos, "trap.noendtry", trapLabel.name); + } + } + } + + /* -------------------------------------- Labels */ + Label labelDecl(String name) { + Label local; + if (labelsHash == null) { + labelsHash = new HashMap<>(10); + local = null; + } else { + local = labelsHash.get(name); + } + if (local == null) { + local = new Label(name); + labelsHash.put(name, local); + } + return local; + } + + public Label LabelDef(int pos, String name) { + Label label = labelDecl(name); + if (label.defd) { + env.error(pos, "label.redecl", name); + return null; + } + label.defd = true; + label.arg = cur_pc; + return label; + } + + public Label LabelRef(String name) { + Label label = labelDecl(name); + label.refd = true; + return label; + } + + void checkLabels() { + if (labelsHash == null) { + return; + } + + for (Label local : labelsHash.values()) { + // check that every label is defined + if (!local.defd) { + env.error("label.undecl", local.name); + } + } + } + + /* -------------------------------------- Variables */ + LocVarData locvarDecl(String name) { + LocVarData local; + if (locvarsHash == null) { + locvarsHash = new HashMap<>(10); + local = null; + } else { + local = locvarsHash.get(name); + } + if (local == null) { + local = new LocVarData(name); + locvarsHash.put(name, local); + } + return local; + } + + public void LocVarDataDef(int slot) { + slots.set(slot, 1); + if ((max_locals != null) && (max_locals.arg < slots.size())) { + env.error("warn.illslot", Integer.toString(slot)); + } + } + + public void LocVarDataDef(String name, ConstantPool.ConstCell type) { + LocVarData locvar = locvarDecl(name); + if (locvar.defd) { + env.error("locvar.redecl", name); + return; + } + locvar.defd = true; + locvar.start_pc = (short) cur_pc; + locvar.name_cpx = cls.pool.FindCellAsciz(name); + locvar.sig_cpx = type; + int k; + findSlot: + { + for (k = 0; k < slots.size(); k++) { + if (slots.get(k) == 0) { + break findSlot; + } + } + k = slots.size(); + } + LocVarDataDef(k); + locvar.arg = k; + if (loc_var_tb == null) { + loc_var_tb = new DataVectorAttr<>(cls, AttrTag.ATT_LocalVariableTable.parsekey()); + attrs.add(loc_var_tb); + } + loc_var_tb.add(locvar); + } + + public Argument LocVarDataRef(String name) { + LocVarData locvar = locvarDecl(name); + if (!locvar.defd) { + env.error("locvar.undecl", name); + locvar.defd = true; // to avoid multiple error messages + } + locvar.refd = true; + return locvar; + } + + public void LocVarDataEnd(int slot) { + slots.set(slot, 0); + } + + public void LocVarDataEnd(String name) { + LocVarData locvar = locvarsHash.get(name); + if (locvar == null) { + env.error("locvar.undecl", name); + return; + } else if (!locvar.defd) { + env.error("locvar.undecl", name); + return; + } + locvar.length = (short) (cur_pc - locvar.start_pc); + + slots.set(locvar.arg, 0); + locvarsHash.put(name, new LocVarData(name)); + } + + void checkLocVars() { + if (locvarsHash == null) { + return; + } + for (LocVarData locvar : locvarsHash.values()) { + if (!locvar.defd) { + continue; + } // this is false locvar + // set end of scope, if not set + if (slots.get(locvar.arg) == 1) { + locvar.length = (short) (cur_pc - locvar.start_pc); + slots.set(locvar.arg, 0); + } + } + } + + /* -------------------------------------- StackMap */ + public StackMapData getStackMap() { + if (curMapEntry == null) { + curMapEntry = new StackMapData(env); + if (cls.cfv.major_version() >= SPLIT_VERIFIER_CFV) { + curMapEntry.setIsStackMapTable(true); + } + } + return curMapEntry; + } + + /*====================================================== Instr */ + void addInstr(int mnenoc_pos, Opcode opcode, Argument arg, Object arg2) { + Instr newInstr = new Instr(cur_pc, cls.env.pos, opcode, arg, arg2); + lastInstr.next = newInstr; + lastInstr = newInstr; + int len = opcode.length(); + switch (opcode) { + case opc_tableswitch: + len = ((SwitchTable) arg2).recalcTableSwitch(cur_pc); + break; + case opc_lookupswitch: + len = ((SwitchTable) arg2).calcLookupSwitch(cur_pc); + break; + case opc_ldc: + ((ConstantPool.ConstCell) arg).setRank(0); + break; + default: + if (arg instanceof ConstantPool.ConstCell) { + ((ConstantPool.ConstCell) arg).setRank(1); + } + } + if (env.debugInfoFlag) { + int ln = env.lineNumber(mnenoc_pos); + if (ln != lastln) { // only one entry in lin_num_tb per line + lin_num_tb.add(new LineNumData(cur_pc, ln)); + lastln = ln; + } + } + if (curMapEntry != null) { + curMapEntry.pc = cur_pc; + StackMapData prevStackFrame = null; + if (stackMap == null) { + if (cls.cfv.major_version() >= SPLIT_VERIFIER_CFV) { + stackMap = new DataVectorAttr<>(cls, AttrTag.ATT_StackMapTable.parsekey()); + } else { + stackMap = new DataVectorAttr<>(cls, AttrTag.ATT_StackMap.parsekey()); + } + attrs.add(stackMap); + } else if (stackMap.size() > 0) { + prevStackFrame = stackMap.get(stackMap.size() - 1); + } + curMapEntry.setOffset(prevStackFrame); + stackMap.add(curMapEntry); + curMapEntry = null; + } + + cur_pc += len; + } + + /*====================================================== Attr interface */ + // subclasses must redefine this + @Override + public int attrLength() { + return 2 + 2 + 4 // for max_stack, max_locals, and cur_pc + + cur_pc // + 2+trap_table.size()*8 + + trap_table.getLength() + attrs.getLength(); + } + + @Override + public void write(CheckedDataOutputStream out) + throws IOException, Parser.CompilerError { + int mxstck = (max_stack != null) ? max_stack.arg : 0; + int mxloc = (max_locals != null) ? max_locals.arg : slots.size(); + super.write(out); // attr name, attr len + out.writeShort(mxstck); + out.writeShort(mxloc); + out.writeInt(cur_pc); + for (Instr instr = zeroInstr.next; instr != null; instr = instr.next) { + instr.write(out, env); + } + + trap_table.write(out); + + attrs.write(out); + } + + /*-------------------------------------------------------- */ + /* CodeAttr inner classes */ + static public class Local extends Argument { + + String name; + boolean defd = false, refd = false; + + public Local(String name) { + this.name = name; + } + } + + /** + * + */ + static public class Label extends Local { + + public Label(String name) { + super(name); + } + } + + /** + * + */ + class LocVarData extends Local implements Data { + + // arg means slot + short start_pc, length; + ConstantPool.ConstCell name_cpx, sig_cpx; + + public LocVarData(String name) { + super(name); + } + + @Override + public int getLength() { + return 10; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(start_pc); + out.writeShort(length); + out.writeShort(name_cpx.arg); + out.writeShort(sig_cpx.arg); + out.writeShort(arg); + } + } + + /** + * + */ + class LineNumData implements Data { + + int start_pc, line_number; + + public LineNumData(int start_pc, int line_number) { + this.start_pc = start_pc; + this.line_number = line_number; + } + + @Override + public int getLength() { + return 4; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(start_pc); + out.writeShort(line_number); + } + } + + /** + * + */ + class Trap extends Local { + + int start_pc = Argument.NotSet, end_pc = Argument.NotSet; + int pos; + + Trap(int pos, String name) { + super(name); + this.pos = pos; + } + } + + /** + * + */ + class TrapData implements Data { + + int pos; + Trap trap; + int handler_pc; + Argument catchType; + + public TrapData(int pos, Trap trap, int handler_pc, Argument catchType) { + this.pos = pos; + this.trap = trap; + this.handler_pc = handler_pc; + this.catchType = catchType; + } + + @Override + public int getLength() { + return 8; // add the length of number of elements + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(trap.start_pc); + out.writeShort(trap.end_pc); + out.writeShort(handler_pc); + if (catchType.isSet()) { + out.writeShort(catchType.arg); + } else { + out.writeShort(0); + } + } + } // end TrapData +} // end CodeAttr + diff --git a/src/org/openjdk/asmtools/jasm/ConstantPool.java b/src/org/openjdk/asmtools/jasm/ConstantPool.java new file mode 100644 index 0000000..bc1ae1d --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ConstantPool.java @@ -0,0 +1,1276 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.jasm.Tables.ConstType; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.Hashtable; +import java.util.Iterator; + +/** + * ConstantPool + * + * ConstantPool is the class responsible for maintaining constants for a given class file. + * + */ +public class ConstantPool implements Iterable { + + + /*-------------------------------------------------------- */ + /* ConstantPool Inner Classes */ + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue { + + protected ConstType tag; + protected boolean isSet = false; + private boolean vizited = false; + + public ConstValue(ConstType tag) { + this.tag = tag; + } + + public int size() { + return 1; + } + + public boolean hasValue() { + return isSet; + } + + /** + * Compute the hash-code, based on the value of the native (_hashCode()) hashcode. + */ + @Override + public int hashCode() { + if (vizited) { + throw new Parser.CompilerError("CV hash:" + this); + } + vizited = true; + int res = _hashCode() + tag.value() * 1023; + vizited = false; + return res; + } + + // sub-classes override this. + // this is the default for getting a hash code. + protected int _hashCode() { + return 37; + } + + /** + * Compares this object to the specified object. + * + * Sub-classes must override this + * + * @param obj the object to compare with + * @return true if the objects are the same; false otherwise. + */ + @Override + public boolean equals(Object obj) { + return false; + } + + @Override + public String toString() { + String tagstr = tag.printval(); + String retval = ""; + if (tagstr == null) { + return "BOGUS_TAG:" + tag; + } + + String valueStr = _toString(); + if (valueStr != null) { + retval = "<" + tagstr + " " + valueStr + ">"; + } else { + retval = "<" + tagstr + ">"; + } + return retval; + } + + protected String _toString() { + return ""; + } + + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(tag.value()); + } + } // end ConstValue + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_Zero extends ConstValue { + + public ConstValue_Zero() { + super(ConstType.CONSTANT_ZERO); + isSet = false; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + throw new Parser.CompilerError("Trying to write Constant 0."); + } + } + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_String extends ConstValue { + + String value; + + public ConstValue_String(String value) { + super(ConstType.CONSTANT_UTF8); + this.value = value; + isSet = (value != null); + } + + @Override + protected String _toString() { + return value; + } + + @Override + protected int _hashCode() { + return value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ConstValue_String)) { + return false; + } + ConstValue_String dobj = (ConstValue_String) obj; + if (tag != dobj.tag) { + return false; + } + return value.equals(dobj.value); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + out.writeUTF(value); + } + } + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_Integer extends ConstValue { + + Integer value; + + public ConstValue_Integer(ConstType tag, Integer value) { + super(tag); + this.value = value; + isSet = (value != null); + } + + @Override + protected String _toString() { + return value.toString(); + } + + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ConstValue_Integer)) { + return false; + } + ConstValue_Integer dobj = (ConstValue_Integer) obj; + if (tag != dobj.tag) { + return false; + } + return value.equals(dobj.value); + } + + @Override + protected int _hashCode() { + return value.hashCode(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + out.writeInt(value.intValue()); + } + } + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_Long extends ConstValue { + + Long value; + + public ConstValue_Long(ConstType tag, Long value) { + super(tag); + this.value = value; + isSet = (value != null); + } + + @Override + public int size() { + return 2; + } + + @Override + protected String _toString() { + return value.toString(); + } + +// @Override +// public Object value() { return value; } + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ConstValue_Long)) { + return false; + } + ConstValue_Long dobj = (ConstValue_Long) obj; + if (tag != dobj.tag) { + return false; + } + return value.equals(dobj.value); + } + + @Override + protected int _hashCode() { + return value.hashCode(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + out.writeLong(value.longValue()); + } + } + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_Cell extends ConstValue { + + ConstCell cell; + + public ConstValue_Cell(ConstType tag, ConstCell cell) { + super(tag); + this.cell = cell; + isSet = (cell != null); + } + + @Override + protected String _toString() { + return cell.toString(); + } + + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ConstValue_Cell)) { + return false; + } + ConstValue_Cell dobj = (ConstValue_Cell) obj; + if (tag != dobj.tag) { + return false; + } + return cell.equals(dobj.cell); + } + + @Override + protected int _hashCode() { + return cell.hashCode(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + cell.write(out); + } + } + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_Pair extends ConstValue { + + ConstCell left, right; + + public ConstValue_Pair(ConstType tag, ConstCell left, ConstCell right) { + super(tag); + this.left = left; + this.right = right; + isSet = (left != null && right != null); + } + + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(obj instanceof ConstValue_Pair)) { + return false; + } + ConstValue_Pair dobj = (ConstValue_Pair) obj; + if (tag != dobj.tag) { + return false; + } + if (dobj.left != null) + if (!dobj.left.equals(left)) + return false; + if (dobj.right != null) + if (!dobj.right.equals(right)) + return false; + return true; + } + + @Override + public String toString() { + return super.toString() + "{" + left + "," + right + "}"; + } + + @Override + protected int _hashCode() { + return left.hashCode() * right.hashCode(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + if (tag == ConstType.CONSTANT_METHODHANDLE) { + out.writeByte(left.arg); // write subtag value + } else { + out.writeShort(left.arg); + } + out.writeShort(right.arg); + } + } + + static public class ConstValue_IndyOrCondyPair extends ConstValue { + BootstrapMethodData bsmData; + ConstantPool.ConstCell napeCell; + + protected ConstValue_IndyOrCondyPair(ConstType tag, BootstrapMethodData bsmdata, ConstCell napeCell) { + super(tag); + assert (tag == ConstType.CONSTANT_DYNAMIC && ConstValue_CondyPair.class.isAssignableFrom(getClass())) || + tag == ConstType.CONSTANT_INVOKEDYNAMIC && ConstValue_IndyPair.class.isAssignableFrom(getClass()); + + this.bsmData = bsmdata; + this.napeCell = napeCell; + isSet = (bsmdata != null && napeCell != null); + } + + @Override + public boolean equals(Object obj) { + if ((obj == null) || !(getClass().isInstance(obj))) { + return false; + } + + ConstValue_IndyOrCondyPair iobj = (ConstValue_IndyOrCondyPair) obj; + return (iobj.bsmData == bsmData) && (iobj.napeCell == napeCell); + } + + @Override + public String toString() { + return super.toString() + "{" + bsmData + "," + napeCell + "}"; + } + + @Override + protected int _hashCode() { + if (bsmData.isPlaceholder()) { + return napeCell.hashCode(); + } + return bsmData.hashCode() * napeCell.hashCode(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + out.writeShort(bsmData.arg); + out.writeShort(napeCell.arg); + } + } + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_CondyPair extends ConstValue_IndyOrCondyPair { + + public ConstValue_CondyPair(BootstrapMethodData bsmdata, ConstCell napeCell) { + super(ConstType.CONSTANT_DYNAMIC, bsmdata, napeCell); + } + } + + /** + * ConstValue + * + * A (typed) tagged value in the constant pool. + */ + static public class ConstValue_IndyPair extends ConstValue_IndyOrCondyPair { + + public ConstValue_IndyPair(BootstrapMethodData bsmdata, ConstCell napeCell) { + super(ConstType.CONSTANT_INVOKEDYNAMIC, bsmdata, napeCell); + } + } + + /*-------------------------------------------------------- */ + /* ConstantPool Inner Classes */ + /** + * ConstantCell + * + * ConstantCell is a type of data that can be in a constant pool. + */ + static public class ConstCell extends Argument implements Data { + + ConstValue ref; + int rank = 2; // 0 - highest - ref from ldc, 1 - any ref, 2 - no ref + + ConstCell(int arg, ConstValue ref) { + this.arg = arg; + this.ref = ref; + } + + ConstCell(ConstValue ref) { + this(NotSet, ref); + } + + ConstCell(int arg) { + this(arg, null); + } + + @Override + public int getLength() { + return 2; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(arg); + } + + public void setRank(int rank) { + this.rank = rank; + } + + @Override + public int hashCode() { + if (arg == NotSet) { + if (ref != null) { + return ref.hashCode(); + } else { + throw new Parser.CompilerError("Can't generate Hash Code, Null ConstCell Reference."); + } + } + return arg; + } + + @Override + public boolean equals(Object obj) { + if (obj == null) { + return false; + } + ConstCell cc = (ConstCell)obj; + if( cc.ref == null ) { + return this.ref == null && cc.rank == this.rank; + } + return cc.ref.equals(this.ref) && cc.rank == this.rank; + } + + public boolean isUnset() { + return (arg == NotSet) && (ref == null); + } + + @Override + public String toString() { + return "#" + arg + "=" + ref; + } + } + + /** + * CPVisitor + * + * CPVisitor base class defining a visitor for decoding constants. + */ + public static class CPTagVisitor implements Constants { + + public CPTagVisitor() { + } + + public final R visit(ConstType tag) { + R retVal = null; + switch (tag) { + case CONSTANT_UTF8: + retVal = visitUTF8(tag); + break; + case CONSTANT_INTEGER: + retVal = visitInteger(tag); + break; + case CONSTANT_FLOAT: + retVal = visitFloat(tag); + break; + case CONSTANT_DOUBLE: + retVal = visitDouble(tag); + break; + case CONSTANT_LONG: + retVal = visitLong(tag); + break; + case CONSTANT_METHODTYPE: + retVal = visitMethodtype(tag); + break; + case CONSTANT_STRING: + retVal = visitString(tag); + break; + case CONSTANT_CLASS: + retVal = visitClass(tag); + break; + case CONSTANT_METHOD: + retVal = visitMethod(tag); + break; + case CONSTANT_FIELD: + retVal = visitField(tag); + break; + case CONSTANT_INTERFACEMETHOD: + retVal = visitInterfacemethod(tag); + break; + case CONSTANT_NAMEANDTYPE: + retVal = visitNameandtype(tag); + break; + case CONSTANT_METHODHANDLE: + retVal = visitMethodhandle(tag); + break; + case CONSTANT_DYNAMIC: + retVal = visitDynamic(tag); + break; + case CONSTANT_INVOKEDYNAMIC: + retVal = visitInvokedynamic(tag); + break; + default: + visitDefault(tag); + } + return retVal; + } + + public R visitUTF8(ConstType tag) { + return null; + } + + ; + public R visitInteger(ConstType tag) { + return null; + } + + ; + public R visitFloat(ConstType tag) { + return null; + } + + ; + public R visitDouble(ConstType tag) { + return null; + } + + ; + public R visitLong(ConstType tag) { + return null; + } + + ; + public R visitMethodtype(ConstType tag) { + return null; + } + + ; + public R visitString(ConstType tag) { + return null; + } + + ; + public R visitClass(ConstType tag) { + return null; + } + + ; + public R visitMethod(ConstType tag) { + return null; + } + + ; + public R visitField(ConstType tag) { + return null; + } + + ; + public R visitInterfacemethod(ConstType tag) { + return null; + } + + ; + public R visitNameandtype(ConstType tag) { + return null; + } + + ; + public R visitMethodhandle(ConstType tag) { + return null; + } + + ; + public R visitDynamic(ConstType tag) { + return null; + } + + public R visitInvokedynamic(ConstType tag) { + return null; + } + ; + + public R visitModule(ConstType tag) { + return null; + } + ; + + public R visitPackage(ConstType tag) { + return null; + } + ; + + public void visitDefault(ConstType tag) { + } + ; + } + + /** + * CPVisitor + * + * CPVisitor base class defining a visitor for decoding constants. + */ + public static class CPVisitor implements Constants { + + public CPVisitor() { + } + + public final R visit(ConstValue val) { + R retVal = null; + ConstType tag = val.tag; + switch (tag) { + case CONSTANT_UTF8: + retVal = visitUTF8((ConstValue_String) val); + break; + case CONSTANT_INTEGER: + retVal = visitInteger((ConstValue_Integer) val); + break; + case CONSTANT_FLOAT: + retVal = visitFloat((ConstValue_Integer) val); + break; + case CONSTANT_DOUBLE: + retVal = visitDouble((ConstValue_Long) val); + break; + case CONSTANT_LONG: + retVal = visitLong((ConstValue_Long) val); + break; + case CONSTANT_METHODTYPE: + retVal = visitMethodtype((ConstValue_Cell) val); + break; + case CONSTANT_STRING: + retVal = visitString((ConstValue_Cell) val); + break; + case CONSTANT_CLASS: + retVal = visitClass((ConstValue_Cell) val); + break; + case CONSTANT_METHOD: + retVal = visitMethod((ConstValue_Pair) val); + break; + case CONSTANT_FIELD: + retVal = visitField((ConstValue_Pair) val); + break; + case CONSTANT_INTERFACEMETHOD: + retVal = visitInterfacemethod((ConstValue_Pair) val); + break; + case CONSTANT_NAMEANDTYPE: + retVal = visitNameandtype((ConstValue_Pair) val); + break; + case CONSTANT_METHODHANDLE: + retVal = visitMethodhandle((ConstValue_Pair) val); + break; + case CONSTANT_DYNAMIC: + retVal = visitDynamic((ConstValue_CondyPair) val); + break; + case CONSTANT_INVOKEDYNAMIC: + retVal = visitInvokedynamic((ConstValue_IndyPair) val); + break; + case CONSTANT_MODULE: + retVal = visitModule((ConstValue_Cell) val); + break; + case CONSTANT_PACKAGE: + retVal = visitPackage((ConstValue_Cell) val); + break; + default: + visitDefault(tag); + } + return retVal; + } + + public R visitUTF8(ConstValue_String p) { + return null; + } + + ; + public R visitInteger(ConstValue_Integer p) { + return null; + } + + ; + public R visitFloat(ConstValue_Integer p) { + return null; + } + + ; + public R visitDouble(ConstValue_Long p) { + return null; + } + + ; + public R visitLong(ConstValue_Long p) { + return null; + } + + ; + public R visitMethodtype(ConstValue_Cell p) { + return null; + } + + ; + public R visitString(ConstValue_Cell p) { + return null; + } + + ; + public R visitClass(ConstValue_Cell p) { + return null; + } + + ; + public R visitMethod(ConstValue_Pair p) { + return null; + } + + ; + public R visitField(ConstValue_Pair p) { + return null; + } + + ; + public R visitInterfacemethod(ConstValue_Pair p) { + return null; + } + + ; + public R visitNameandtype(ConstValue_Pair p) { + return null; + } + + ; + public R visitMethodhandle(ConstValue_Pair p) { + return null; + } + + ; + public R visitDynamic(ConstValue_CondyPair p) { return null;} + + ; + public R visitInvokedynamic(ConstValue_IndyPair p) { return null;} + + ; + public R visitModule(ConstValue_Cell p) { return null; } + + ; + public R visitPackage(ConstValue_Cell p) { return null; } + ; + + public void visitDefault(ConstType tag) {} + ; + + } + + + + /*-------------------------------------------------------- */ + /* Constant Pool Fields */ + + private ArrayList pool = new ArrayList<>(20); + + private final ConstValue ConstValue0 + = new ConstValue_String(""); +// private final ConstValue ConstValue0 = +// new ConstValue(CONSTANT_UTF8, ""); + private final ConstCell nullConst + = new ConstCell(null); + private final ConstCell constant_0 + = new ConstCell(new ConstValue_Zero()); +// private final ConstCell constant_0 = +// new ConstCell(new ConstValue(CONSTANT_ZERO, null)); + + // For hashing by value + Hashtable cpoolHashByValue + = new Hashtable<>(40); + + public Environment env; + + private static boolean debugCP = false; + + /*-------------------------------------------------------- */ + /** + * main constructor + * + * @param env The error reporting environment + */ + public ConstantPool(Environment env) { + this.env = env; + pool.add(constant_0); + + } + + public void debugStr(String s) { + if (debugCP) { + env.traceln(s); + } + } + + @Override + public Iterator iterator() { + return pool.iterator(); + } + + + /* + * Fix Refs in constant pool. + * + * This is used when scanning JASM files produced from JDis with the verbose + * option (eg. where the constant pool is declared in the jasm itself). In + * this scenario, we need two passes - the first pass to scan the entries + * (which creates constant references with indexes, but no reference values); + * and the second pass, which links references to existing constants. + * + */ + public void fixRefsInPool() { + // used to fix CP refs when a constant pool is constructed by refs alone. + env.traceln("Fixing CP for explicit Constant Entries."); + int i = 0; + // simply iterate through the pool. + for (ConstCell item : pool) { + i += 1; + // first item is always null + if (item == null) { + continue; + } + + checkAndFixCPRef(i, item); + } + } + + protected void CheckGlobals() { + env.traceln("Checking Globals"); + // + // This fn will put empty UTF8 string entries on any unset + // CP entries - before the last CP entry. + // + for (int cpx = 1; cpx < pool.size(); cpx++) { + ConstCell cell = pool.get(cpx); + if (cell == nullConst) { // gap + cell = new ConstCell(cpx, ConstValue0); + pool.set(cpx, cell); + } + ConstValue cval = cell.ref; + if ((cval == null) || !cval.hasValue()) { + String name = Integer.toString(cpx); + env.error("const.undecl", name); + } + } + } + + /* + * Helper function for "fixRefsInPool" + * + * Does recursive checking of references, + * using a locally-defined visitor. + */ + private void checkAndFixCPRef(int i, ConstCell item) { + ConstValue cv = item.ref; + if (cv != null) { + fixCPVstr.visit(cv); + } + } + + private CPVisitor fixCPVstr = new CPVisitor() { + @Override + public Void visitUTF8(ConstValue_String p) { + return null; + } + + ; + @Override + public Void visitInteger(ConstValue_Integer p) { + return null; + } + + ; + @Override + public Void visitFloat(ConstValue_Integer p) { + return null; + } + + ; + @Override + public Void visitDouble(ConstValue_Long p) { + return null; + } + + ; + @Override + public Void visitLong(ConstValue_Long p) { + return null; + } + + ; + @Override + public Void visitMethodtype(ConstValue_Cell p) { + handleClassRef(p); + return null; + } + + ; + @Override + public Void visitString(ConstValue_Cell p) { + handleClassRef(p); + return null; + } + + ; + @Override + public Void visitClass(ConstValue_Cell p) { + handleClassRef(p); + return null; + } + + ; + @Override + public Void visitMethod(ConstValue_Pair p) { + handleMemberRef(p); + return null; + } + + ; + @Override + public Void visitField(ConstValue_Pair p) { + handleMemberRef(p); + return null; + } + + ; + @Override + public Void visitInterfacemethod(ConstValue_Pair p) { + handleMemberRef(p); + return null; + } + + ; + @Override + public Void visitNameandtype(ConstValue_Pair p) { + handleMemberRef(p); + return null; + } + + ; + @Override + public Void visitMethodhandle(ConstValue_Pair p) { + handleMemberRef(p); + return null; + } + + ; + @Override + public Void visitDynamic(ConstValue_CondyPair p) { + return null; + } + + ; + @Override + public Void visitInvokedynamic(ConstValue_IndyPair p) { + return null; + } + ; + + @Override + public Void visitModule(ConstValue_Cell p) { + handleClassRef(p); + return null; + } + ; + + @Override + public Void visitPackage(ConstValue_Cell p) { + handleClassRef(p); + return null; + } + ; + + + public void handleClassRef(ConstValue_Cell cv) { + ConstCell clref = cv.cell; + if (clref.ref == null) { + ConstCell refval = cpool_get(clref.arg); + if (refval != null) { + checkAndFixCPRef(clref.arg, refval); + clref.ref = refval.ref; + } else { + clref.ref = null; + } + // env.traceln("FIXED ConstPool[" + i + "](" + cv.TagString(cv.tag) + ") = " + cv.value); + } + } + + public void handleMemberRef(ConstValue_Pair cv) { + // env.traceln("ConstPool[" + i + "](" + cv.TagString(cv.tag) + ") = " + cv.value); + ConstCell clref = cv.left; + ConstCell typref = cv.right; + if (clref.ref == null) { + ConstCell refval = cpool_get(clref.arg); + if (refval != null) { + checkAndFixCPRef(clref.arg, refval); + clref.ref = refval.ref; + } else { + clref.ref = null; + } + // env.traceln("FIXED ConstPool[" + i + "](" + cv.TagString(cv.tag) + ") = " + cv.value); + } + if (typref.ref == null) { + ConstCell refval = cpool_get(typref.arg); + if (refval != null) { + checkAndFixCPRef(typref.arg, refval); + typref.ref = refval.ref; + } else { + typref.ref = null; + } + // env.traceln("FIXED ConstPool[" + i + "](" + cv.TagString(cv.tag) + ") = " + cv.value); + } + } + + }; + + /* + * Help debug Constant Pools + */ + public void printPool() { + int i = 0; + for (ConstCell item : pool) { + env.traceln("^^^^^^^^^^^^^ const #" + i + ": " + item); + i += 1; + } + } + + private ConstCell cpool_get(int cpx) { + if (cpx >= pool.size()) { + return null; + } + return pool.get(cpx); + } + + private void cpool_set(int cpx, ConstCell cell, int sz) { + debugStr("cpool_set1: " + cpx + " " + cell); + debugStr("param_size: " + sz); + debugStr("pool_size: " + pool.size()); + cell.arg = cpx; + if (cpx + sz >= pool.size()) { + debugStr("calling ensureCapacity( " + (cpx + sz + 1) + ")"); + int low = pool.size(); + int high = cpx + sz; + for (int i = 0; i < high - low; i++) { + pool.add(nullConst); + } + } + pool.set(cpx, cell); + if (sz == 2) { + pool.set(cpx + 1, new ConstCell(cpx + 1, ConstValue0)); + } + debugStr(" cpool_set2: " + cpx + " " + cell); + } + + protected ConstCell uncheckedGetCell(int cpx) { // by index + return pool.get(cpx); + } + + public ConstCell getCell(int cpx) { // by index + ConstCell cell = cpool_get(cpx); + if (cell != null) { + return cell; + } + cell = new ConstCell(cpx, null); + return cell; + } + + public void setCell(int cpx, ConstCell cell) { + ConstValue value = cell.ref; + if (value == null) { + throw new Parser.CompilerError(env.errorStr("comperr.constcell.nullvalset")); + } + int sz = value.size(); + + if (cpx == 0) { + // It is correct to warn about redeclaring constant zero, + // since this value is never written out to a class file. + env.error("warn.const0.redecl"); + } else { + if ((cpool_get(cpx) != null) || ((sz == 2) && (cpool_get(cpx + 1) != null))) { + String name = "#" + cpx; + env.error("const.redecl", name); + return; + } + if (cell.isSet() && (cell.arg != cpx)) { + env.traceln("setCell: new ConstCell"); + cell = new ConstCell(value); + } + } + cpool_set(cpx, cell, sz); + } + + protected void NumberizePool() { + env.traceln("NumberizePool"); + Enumeration e; + for (int rank = 0; rank < 3; rank++) { + for (ConstCell cell : cpoolHashByValue.values()) { + if (cell.rank != rank) { + continue; + } + if (cell.isSet()) { + continue; + } + ConstValue value = cell.ref; + if (value == null) { + throw new Parser.CompilerError(env.errorStr("comperr.constcell.nullvalhash")); + } + int sz = value.size(), cpx; +find: + for (cpx = 1; cpx < pool.size(); cpx++) { + if ((pool.get(cpx) == nullConst) && ((sz == 1) || (pool.get(cpx + 1) == nullConst))) { + break find; + } + } + cpool_set(cpx, cell, sz); + } + } + + ConstCell firstCell = cpool_get(0); + firstCell.arg = 0; + } + + public ConstCell FindCell(ConstValue ref) { + if (ref == null) { + throw new Parser.CompilerError(env.errorStr("comperr.constcell.nullval")); + } + ConstCell pconst = null; + try { + pconst = cpoolHashByValue.get(ref); + } catch (Parser.CompilerError e) { + throw new Parser.CompilerError(env.errorStr("comperr.constcell.nullvalhash")); + } + // If we fund a cached ConstValue + if (pconst != null) { + ConstValue value = pconst.ref; + if (!value.equals(ref)) { + throw new Parser.CompilerError(env.errorStr("comperr.val.noteq")); + } + return pconst; + } + // If we didn't find a cached ConstValue + // Add it to the cache + pconst = new ConstCell(ref); + cpoolHashByValue.put(ref, pconst); + return pconst; + } + + public ConstCell FindCell(ConstType tag, String value) { + return FindCell(new ConstValue_String(value)); + } + + public ConstCell FindCell(ConstType tag, Integer value) { + return FindCell(new ConstValue_Integer(tag, value)); + } + + public ConstCell FindCell(ConstType tag, Long value) { + return FindCell(new ConstValue_Long(tag, value)); + } + + public ConstCell FindCell(ConstType tag, ConstCell value) { + return FindCell(new ConstValue_Cell(tag, value)); + } + + public ConstCell FindCell(ConstType tag, ConstCell left, ConstCell right) { + return FindCell(new ConstValue_Pair(tag, left, right)); + } + + public ConstCell FindCellAsciz(String str) { + return FindCell(ConstType.CONSTANT_UTF8, str); + } + + public ConstCell FindCellClassByName(String name) { return FindCell(ConstType.CONSTANT_CLASS, FindCellAsciz(name)); } + + public ConstCell FindCellModuleByName(String name) { return FindCell(ConstType.CONSTANT_MODULE, FindCellAsciz(name)); } + + public ConstCell FindCellPackageByName(String name) { return FindCell(ConstType.CONSTANT_PACKAGE, FindCellAsciz(name)); } + + public void write(CheckedDataOutputStream out) throws IOException { + // Write the constant pool + int length = pool.size(); + out.writeShort(length); + int i; + env.traceln("wr.pool:size=" + length); + for (i = 1; i < length;) { + ConstCell cell = pool.get(i); + ConstValue value = cell.ref; + if (cell.arg != i) { + throw new Parser.CompilerError(env.errorStr("comperr.constcell.invarg", Integer.toString(i), cell.arg)); + } + value.write(out); + i += value.size(); + } + } + + public static void setEnableDebug(boolean newState) { + debugCP = newState; + } +} diff --git a/src/org/openjdk/asmtools/jasm/Constants.java b/src/org/openjdk/asmtools/jasm/Constants.java new file mode 100644 index 0000000..0d93e03 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Constants.java @@ -0,0 +1,128 @@ +/* + * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +/** + * This interface defines constant that are used throughout the compiler. It inherits from + * RuntimeConstants, which is an autogenerated class that contains constants defined in + * the interpreter. + */ +public interface Constants extends RuntimeConstants { + + + /** + * End of input + */ + public static final int EOF = -1; + + /* + * Flags + */ + public static final int F_VERBOSE = 1 << 0; + public static final int F_DUMP = 1 << 1; + public static final int F_WARNINGS = 1 << 2; + public static final int F_DEBUG = 1 << 3; + public static final int F_OPTIMIZE = 1 << 4; + public static final int F_DEPENDENCIES = 1 << 5; + + /* + * Type codes + */ + public static final int TC_BOOLEAN = 0; + public static final int TC_BYTE = 1; + public static final int TC_CHAR = 2; + public static final int TC_SHORT = 3; + public static final int TC_INT = 4; + public static final int TC_LONG = 5; + public static final int TC_FLOAT = 6; + public static final int TC_DOUBLE = 7; + public static final int TC_NULL = 8; + public static final int TC_ARRAY = 9; + public static final int TC_CLASS = 10; + public static final int TC_VOID = 11; + public static final int TC_METHOD = 12; + public static final int TC_ERROR = 13; + + /* + * Type Masks + */ + public static final int TM_NULL = 1 << TC_NULL; + public static final int TM_VOID = 1 << TC_VOID; + public static final int TM_BOOLEAN = 1 << TC_BOOLEAN; + public static final int TM_BYTE = 1 << TC_BYTE; + public static final int TM_CHAR = 1 << TC_CHAR; + public static final int TM_SHORT = 1 << TC_SHORT; + public static final int TM_INT = 1 << TC_INT; + public static final int TM_LONG = 1 << TC_LONG; + public static final int TM_FLOAT = 1 << TC_FLOAT; + public static final int TM_DOUBLE = 1 << TC_DOUBLE; + public static final int TM_ARRAY = 1 << TC_ARRAY; + public static final int TM_CLASS = 1 << TC_CLASS; + public static final int TM_METHOD = 1 << TC_METHOD; + public static final int TM_ERROR = 1 << TC_ERROR; + + public static final int TM_INT32 = TM_BYTE | TM_SHORT | TM_CHAR | TM_INT; + public static final int TM_NUM32 = TM_INT32 | TM_FLOAT; + public static final int TM_NUM64 = TM_LONG | TM_DOUBLE; + public static final int TM_INTEGER = TM_INT32 | TM_LONG; + public static final int TM_REAL = TM_FLOAT | TM_DOUBLE; + public static final int TM_NUMBER = TM_INTEGER | TM_REAL; + public static final int TM_REFERENCE = TM_ARRAY | TM_CLASS | TM_NULL; + + /* + * Class status + */ + public static final int CS_UNDEFINED = 0; + public static final int CS_UNDECIDED = 1; + public static final int CS_BINARY = 2; + public static final int CS_SOURCE = 3; + public static final int CS_PARSED = 4; + public static final int CS_COMPILED = 5; + public static final int CS_NOTFOUND = 6; + + /* + * Attributes + */ + public static final int ATT_ALL = -1; + public static final int ATT_CODE = 1; + + /* + * Number of bits used in file offsets + */ + public static final int OFFSETBITS = 19; + public static final int MAXFILESIZE = (1 << OFFSETBITS) - 1; + public static final int MAXLINENUMBER = (1 << (32 - OFFSETBITS)) - 1; + + /* + * Operator precedence + */ + /* Who uses this???? + public static final int opPrecedence[] = { + 10, 11, 11, 11, 11, 11, 11, 11, 11, 11, + 11, 11, 11, 12, 13, 14, 15, 16, 17, 18, + 18, 19, 19, 19, 19, 19, 20, 20, 20, 21, + 21, 22, 22, 22, 23, 24, 24, 24, 24, 24, + 24, 25, 25, 26, 26, 26, 26, 26, 26 + }; + * */ +} diff --git a/src/org/openjdk/asmtools/jasm/Data.java b/src/org/openjdk/asmtools/jasm/Data.java new file mode 100644 index 0000000..b9feeeb --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Data.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; + +/** + * Base contract for writeable structures + */ +interface Data { + + void write(CheckedDataOutputStream out) throws IOException; + + int getLength(); + + default String tabString(int tabLevel) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < tabLevel; i++) { + sb.append('\t'); + } + return sb.toString(); + } +} diff --git a/src/org/openjdk/asmtools/jasm/DataVector.java b/src/org/openjdk/asmtools/jasm/DataVector.java new file mode 100644 index 0000000..a268e25 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/DataVector.java @@ -0,0 +1,96 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +/** + * + */ +public class DataVector implements Iterable { + + ArrayList elements; + + public DataVector(int initSize) { + elements = new ArrayList<>(initSize); + } + + public DataVector() { + this(12); + } + + public Iterator iterator() { + return elements.iterator(); + } + + public void add(T element) { + elements.add(element); + } + + public void addAll(List collection) { + elements.addAll(collection); + } + + // full length of the attribute conveyor + // declared in Data + public int getLength() { + int length = 0; + // calculate overall size here rather than in add() + // because it may not be available at the time of invoking of add() + for (T element : elements) { + length += element.getLength(); + } + + return 2 + length; // add the length of number of elements + } + + public void write(CheckedDataOutputStream out) + throws IOException { + out.writeShort(elements.size()); + writeElements(out); + } + + public void writeElements(CheckedDataOutputStream out) + throws IOException { + for (Data element : elements) { + element.write(out); + } + } + + /* for compatibility with Vector */ + public void addElement(T element) { + elements.add(element); + } + + public int size() { + return elements.size(); + } + + public Data elementAt(int k) { + return elements.get(k); + } +}// end class DataVector + diff --git a/src/org/openjdk/asmtools/jasm/DataVectorAttr.java b/src/org/openjdk/asmtools/jasm/DataVectorAttr.java new file mode 100644 index 0000000..711feb1 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/DataVectorAttr.java @@ -0,0 +1,112 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Iterator; + +/** + * + */ +// public class DataVectorAttr extends AttrData implements Constants { +// } +class DataVectorAttr extends AttrData implements Iterable { + + private ArrayList elements; + private boolean byteIndex; + + private DataVectorAttr(ClassData cls, String name, boolean byteIndex, ArrayList initialData) { + super(cls, name); + this.elements = initialData; + this.byteIndex = byteIndex; + } + + DataVectorAttr(ClassData cls, String name, ArrayList initialData) { + this(cls, name, false, initialData); + } + + DataVectorAttr(ClassData cls, String name) { + this(cls, name, false, new ArrayList<>()); + + } + + DataVectorAttr(ClassData cls, String name, boolean byteIndex) { + this(cls, name, byteIndex, new ArrayList<>()); + + } + + public T get(int index) { + return elements.get(index); + } + + public void add(T element) { + elements.add(element); + } + + public void put(int i, T element) { + elements.set(i, element); + } + + public int size() { + return elements.size(); + } + + @Override + public Iterator iterator() { + return elements.iterator(); + } + + @Override + public int attrLength() { + int length = 0; + // calculate overall size here rather than in add() + // because it may not be available at the time of invoking of add() + for (T elem : elements) { + length += elem.getLength(); + } + + // add the length of number of elements + if (byteIndex) { + length += 1; + } else { + length += 2; + } + + return length; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); // attr name, attr len + if (byteIndex) { + out.writeByte(elements.size()); + } else { + out.writeShort(elements.size()); + } // number of elements + for (T elem : elements) { + elem.write(out); + } + } + +} diff --git a/src/org/openjdk/asmtools/jasm/DefaultAnnotationAttr.java b/src/org/openjdk/asmtools/jasm/DefaultAnnotationAttr.java new file mode 100644 index 0000000..af81687 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/DefaultAnnotationAttr.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; + +/** + * DefaultAnnotationAttr + * + * Used to represent Default Annotation Attributes + * + */ +public class DefaultAnnotationAttr extends AttrData { + + Data element; // Data + + public DefaultAnnotationAttr(ClassData cls, String name, Data element) { + super(cls, name); + this.element = element; + } + + public DefaultAnnotationAttr(ClassData cls, String name) { + super(cls, name); + this.element = null; + } + + public void add(Data element) { + this.element = element; + } + + @Override + public int attrLength() { + return element.getLength(); // add the length of number of elements + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); // attr name, attr len + element.write(out); + } +}// end class DataVectorAttr + diff --git a/src/org/openjdk/asmtools/jasm/Environment.java b/src/org/openjdk/asmtools/jasm/Environment.java new file mode 100644 index 0000000..93ca278 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Environment.java @@ -0,0 +1,454 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.Constants.EOF; +import static org.openjdk.asmtools.jasm.Constants.OFFSETBITS; +import org.openjdk.asmtools.util.I18NResourceBundle; + +import java.io.*; +import java.nio.file.Paths; +import java.text.MessageFormat; + +/** + * An input stream for java programs. The stream treats either "\n", "\r" or "\r\n" as the + * end of a line, it always returns \n. It also parses UNICODE characters expressed as + * \uffff. However, if it sees "\\", the second slash cannot begin a unicode sequence. It + * keeps track of the current position in the input stream. + * + * An position consists of: ((linenr << OFFSETBITS) | offset) this means that both + * the line number and the exact offset into the file are encoded in each position + * value.

+ */ +public class Environment { + + /*-------------------------------------------------------- */ + /* Environment Inner Classes */ + /** + * A sorted list of error messages + */ + final class ErrorMessage { + + int where; + String message; + ErrorMessage next; + + /** + * Constructor + */ + ErrorMessage(int where, String message) { + this.where = where; + this.message = message; + } + } + + /*-------------------------------------------------------- */ + /* Environment Fields */ + static boolean traceFlag = false; + boolean debugInfoFlag = false; + + private String inputFileName; + private String simpleInputFileName; + public PrintWriter out; + private boolean nowarn; + private byte[] data; + private int bytepos; + private int linepos; + public int pos; + /*-------------------------------------------------------- */ + + public Environment(DataInputStream dis, String inputFileName, PrintWriter out, boolean nowarn) throws IOException { + this.out = out; + this.inputFileName = inputFileName; + this.nowarn = nowarn; + // Read the file + data = new byte[dis.available()]; + dis.read(data); + dis.close(); + bytepos = 0; + linepos = 1; + } + + public String getInputFileName() { + return inputFileName; + } + + public String getSimpleInputFileName() { + if( simpleInputFileName == null ) { + simpleInputFileName = Paths.get(inputFileName).getFileName().toString(); + } + return simpleInputFileName; + } + + int lookForward() { + try { + return data[bytepos]; + } catch (ArrayIndexOutOfBoundsException e) { + return EOF; + } + } + + int convertUnicode() { + int c; + try { + while ((c = data[bytepos]) == 'u') { + bytepos++; + } + int d = 0; + for (int i = 0; i < 4; i++) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + d = (d << 4) + c - '0'; + break; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + d = (d << 4) + 10 + c - 'a'; + break; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + d = (d << 4) + 10 + c - 'A'; + break; + default: + error(pos, "invalid.escape.char"); + return d; + } + ++bytepos; + c = data[bytepos]; + } + return d; + } catch (ArrayIndexOutOfBoundsException e) { + error(pos, "invalid.escape.char"); + return EOF; + } + } + + public int read() { + int c; + pos = (linepos << OFFSETBITS) | bytepos; + try { + c = data[bytepos]; + } catch (ArrayIndexOutOfBoundsException e) { + return EOF; + } + bytepos++; + + // parse special characters + switch (c) { + /* case '\\': + if (lookForward() != 'u') { + return '\\'; + } + // we have a unicode sequence + return convertUnicode();*/ + case '\n': + linepos++; + return '\n'; + + case '\r': + if (lookForward() == '\n') { + bytepos++; + } + linepos++; + return '\n'; + + default: + return c; + } + } + + int lineNumber(int lcpos) { + return lcpos >>> OFFSETBITS; + } + + int lineNumber() { + return lineNumber(pos); + } + + int lineOffset(int lcpos) { + return lcpos & ((1 << OFFSETBITS) - 1); + } + + int lineOffset() { + return lineOffset(pos); + } + + /*============================================================== Environment */ + /** + * The number of errors and warnings + */ + public int nerrors; + public int nwarnings; + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + /** + * Error String + */ + String errorString(String err, Object arg1, Object arg2, Object arg3) { + String str = null; + + //str = getProperty(err); + str = i18n.getString(err); + if (str == null) { + return "error message '" + err + "' not found"; + } + + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if ((c == '%') && (i + 1 < str.length())) { + switch (str.charAt(++i)) { + case 's': + String arg = arg1.toString(); + for (int j = 0; j < arg.length(); j++) { + switch (c = arg.charAt(j)) { + case ' ': + case '\t': + case '\n': + case '\r': + buf.append((char) c); + break; + + default: + if ((c > ' ') && (c <= 255)) { + buf.append((char) c); + } else { + buf.append('\\'); + buf.append('u'); + buf.append(Integer.toString(c, 16)); + } + } + } + arg1 = arg2; + arg2 = arg3; + break; + + case '%': + buf.append('%'); + break; + + default: + buf.append('?'); + break; + } + } else { + buf.append((char) c); + } + } + // KTL + // Need to do message format to substitute args + String msg = buf.toString(); + MessageFormat form = new MessageFormat(msg); + Object args[] = {arg1, arg2, arg3}; + msg = form.format(args); + + return msg; + + } + + /** + * List of outstanding error messages + */ + ErrorMessage errors; + + /** + * Insert an error message in the list of outstanding error messages. The list is + * sorted on input position. + */ + void insertError(int where, String message) { + //output("ERR = " + message); + ErrorMessage msg = new ErrorMessage(where, message); + if (errors == null) { + errors = msg; + } else if (errors.where > where) { + msg.next = errors; + errors = msg; + } else { + ErrorMessage m = errors; + for (; (m.next != null) && (m.next.where <= where); m = m.next); + msg.next = m.next; + m.next = msg; + } + } + + /** + * Flush outstanding errors + */ + public void flushErrors() { + if (errors == null) { + traceln("flushErrors: errors == null"); + return; + } + + // Report the errors + for (ErrorMessage msg = errors; msg != null; msg = msg.next) { + int off = lineOffset(msg.where); + + int i, j; + for (i = off; (i > 0) && (data[i - 1] != '\n') && (data[i - 1] != '\r'); i--); + for (j = off; (j < data.length) && (data[j] != '\n') && (data[j] != '\r'); j++); + + outputln( String.format( "%s (%d:%d) %s", getSimpleInputFileName(), lineNumber(msg.where), off - i, msg.message)); + outputln(new String(data, i, j - i)); + + char strdata[] = new char[(off - i) + 1]; + for (j = i; j < off; j++) { + strdata[j - i] = (data[j] == '\t') ? '\t' : ' '; + } + strdata[off - i] = '^'; + outputln(new String(strdata)); + } + errors = null; + } + + /** + * Output a string. This can either be an error message or something for debugging. + * This should be used instead of print. + */ + public void output(String msg) { + int len = msg.length(); + for (int i = 0; i < len; i++) { + out.write(msg.charAt(i)); + } + out.flush(); + } + + /** + * Output a string. This can either be an error message or something for debugging. + * This should be used instead of println. + */ + public void outputln(String msg) { + output((msg == null ? "" : msg) + "\n"); + } + + /** + * Issue an error. source - the input source, usually a file name string offset - the + * offset in the source of the error err - the error number (as defined in this + * interface) arg1 - an optional argument to the error (null if not applicable) arg2 - + * a second optional argument to the error (null if not applicable) arg3 - a third + * optional argument to the error (null if not applicable) + */ + /** + * Issue an error + */ + public void error(int where, String err, Object arg1, Object arg2, Object arg3) { + String msg; + if (err.startsWith("warn.")) { + if (nowarn) { + return; + } + nwarnings++; + msg = "Warning: "; + } else { + err = "err." + err; + nerrors++; + msg = "Error: "; + } + msg = msg + errorString(err, arg1, arg2, arg3); + traceln(msg); + insertError(where, msg); + } + + public final void error(int where, String err, Object arg1, Object arg2) { + error(where, err, arg1, arg2, null); + } + + public final void error(int where, String err, Object arg1) { + error(where, err, arg1, null, null); + } + + public final void error(int where, String err) { + error(where, err, null, null, null); + } + + public final void error(String err, Object arg1, Object arg2, Object arg3) { + error(pos, err, arg1, arg2, arg3); + } + + public final void error(String err, Object arg1, Object arg2) { + error(pos, err, arg1, arg2, null); + } + + public final void error(String err, Object arg1) { + error(pos, err, arg1, null, null); + } + + public final void error(String err) { + error(pos, err, null, null, null); + } + + public final String errorStr(String err, Object arg1, Object arg2, Object arg3) { + return errorString(err, arg1, arg2, arg3); + } + + public final String errorStr(String err, Object arg1, Object arg2) { + return errorStr(err, arg1, arg2, null); + } + + public final String errorStr(String err, Object arg1) { + return errorStr(err, arg1, null, null); + } + + public final String errorStr(String err) { + return errorStr(err, null, null, null); + } + + /*============================================================== trace */ + public boolean isTraceEnabled() { + return traceFlag; + } + + public boolean isDebugEnabled() { + return debugInfoFlag; + } + + void trace(String message) { + if (traceFlag) { + output(message); + } + } + + void traceln(String message) { + if (traceFlag) { + outputln(message); + } + } + +} // end Environment diff --git a/src/org/openjdk/asmtools/jasm/FieldData.java b/src/org/openjdk/asmtools/jasm/FieldData.java new file mode 100644 index 0000000..c4a6c26 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/FieldData.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.jasm.Tables.AttrTag; +import java.io.IOException; + +/** + * field_info + */ +class FieldData extends MemberData { + + /* FieldData Fields */ + private ConstantPool.ConstValue_Pair nape; + private AttrData initValue; + + public FieldData(ClassData cls, int acc, ConstantPool.ConstValue_Pair nape) { + super(cls, acc); + this.nape = nape; + if (Modifiers.hasPseudoMod(acc)) { + createPseudoMod(); + } + } + + public ConstantPool.ConstValue_Pair getNameDesc() { + return nape; + } + + public void SetValue(Argument value_cpx) { + initValue = new CPXAttr(cls, AttrTag.ATT_ConstantValue.parsekey(), + value_cpx); + } + + @Override + protected DataVector getAttrVector() { + return getDataVector(initValue, syntheticAttr, deprecatedAttr, signatureAttr); + } + + public void write(CheckedDataOutputStream out) throws IOException, Parser.CompilerError { + out.writeShort(access); + out.writeShort(nape.left.arg); + out.writeShort(nape.right.arg); + DataVector attrs = getAttrVector(); + attrs.write(out); + } +} // end FieldData + diff --git a/src/org/openjdk/asmtools/jasm/InnerClassData.java b/src/org/openjdk/asmtools/jasm/InnerClassData.java new file mode 100644 index 0000000..0afef3e --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/InnerClassData.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; + +/** + * + */ +class InnerClassData implements Data { + + int access; + ConstantPool.ConstCell name, innerClass, outerClass; + + public InnerClassData(int access, ConstantPool.ConstCell name, ConstantPool.ConstCell innerClass, ConstantPool.ConstCell outerClass) { + this.access = access; + this.name = name; + this.innerClass = innerClass; + this.outerClass = outerClass; + } + + @Override + public int getLength() { + return 8; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(innerClass.arg); + if (outerClass.isSet()) { + out.writeShort(outerClass.arg); + } else { + out.writeShort(0); + } + if (name.isSet()) { + out.writeShort(name.arg); + } else { + out.writeShort(0); + } + out.writeShort(access); + } +}// end class InnerClassData + diff --git a/src/org/openjdk/asmtools/jasm/Instr.java b/src/org/openjdk/asmtools/jasm/Instr.java new file mode 100644 index 0000000..0035308 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Instr.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; + +import static org.openjdk.asmtools.jasm.OpcodeTables.*; + +/** + * + */ +class Instr { + + Instr next = null; + int pc; + int pos; + Opcode opc; + Argument arg; + Object arg2; // second or unusual argument + + public Instr(int pc, int pos, Opcode opc, Argument arg, Object arg2) { + this.pc = pc; + this.pos = pos; + this.opc = opc; + this.arg = arg; + this.arg2 = arg2; + } + + public Instr() { + } + + public void write(CheckedDataOutputStream out, Environment env) throws IOException { + OpcodeType type = opc.type(); + switch (type) { + case NORMAL: { + if (opc == Opcode.opc_bytecode) { + out.writeByte(arg.arg); + return; + } + out.writeByte(opc.value()); + int opcLen = opc.length(); + if (opcLen == 1) { + return; + } + + switch (opc) { + case opc_tableswitch: + ((SwitchTable) arg2).writeTableSwitch(out); + return; + case opc_lookupswitch: + ((SwitchTable) arg2).writeLookupSwitch(out); + return; + } + + int iarg; + try { + iarg = arg.arg; + } catch (NullPointerException e) { + throw new Parser.CompilerError(env.errorStr("comperr.instr.nullarg", opc.parsekey())); + } +//env.traceln("instr:"+opcNamesTab[opc]+" len="+opcLen+" arg:"+iarg); + switch (opc) { + case opc_jsr: + case opc_goto: + case opc_ifeq: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + case opc_iflt: + case opc_ifne: + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_icmplt: + case opc_if_acmpeq: + case opc_if_acmpne: + case opc_ifnull: + case opc_ifnonnull: + case opc_jsr_w: + case opc_goto_w: + iarg = iarg - pc; + break; + case opc_iinc: + iarg = (iarg << 8) | (((Argument) arg2).arg & 0xFF); + break; + case opc_invokeinterface: + iarg = ((iarg << 8) | (((Argument) arg2).arg & 0xFF)) << 8; + break; + case opc_invokedynamic: // JSR-292 + iarg = (iarg << 16); + break; + case opc_ldc: + if ((iarg & 0xFFFFFF00) != 0) { + throw new Parser.CompilerError( + env.errorStr("comperr.instr.arglong", opc.parsekey(), iarg)); + } + break; + } + switch (opcLen) { + case 1: + return; + case 2: + out.writeByte(iarg); + return; + case 3: + out.writeShort(iarg); + return; + case 4: // opc_multianewarray only + out.writeShort(iarg); + iarg = ((Argument) arg2).arg; + out.writeByte(iarg); + return; + case 5: + out.writeInt(iarg); + return; + default: + throw new Parser.CompilerError( + env.errorStr("comperr.instr.opclen", opc.parsekey())); + } + } + case WIDE: + out.writeByte(Opcode.opc_wide.value()); + out.writeByte(opc.value() & 0xFF); + out.writeShort(arg.arg); + if (opc == Opcode.opc_iinc_w) { + out.writeShort(((Argument) arg2).arg); + } + return; + case PRIVELEGED: + case NONPRIVELEGED: + out.writeByte(opc.value() >> 8); + out.writeByte(opc.value() & 0xFF); + return; + default: + throw new Parser.CompilerError( + env.errorStr("comperr.instr.opclen", opc.parsekey())); + } // end writeSpecCode + + } +} // end Instr + diff --git a/src/org/openjdk/asmtools/jasm/JasmTokens.java b/src/org/openjdk/asmtools/jasm/JasmTokens.java new file mode 100644 index 0000000..8471b1c --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/JasmTokens.java @@ -0,0 +1,476 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.util.EnumSet; +import java.util.Optional; + +/** + * + * JasmTokens + * + * This class contains tokens specific to parsing JASM syntax. + * + * The classes in JasmTokens are following a Singleton Pattern. These classes are Enums, + * and they are contained in private hash maps (lookup tables and reverse lookup tables). + * These hash maps all have public accessors, which clients use to look-up enums. + * + * Tokens in this table carry no external state, and are typically treated as constants. + * They do not need to be reset. + */ +public class JasmTokens { + + /*-------------------------------------------------------- */ + /* Marker: describes the type of Keyword */ + static public enum KeywordType { + TOKEN (0, "TOKEN"), + VALUE (1, "VALUE"), + JASMIDENTIFIER (2, "JASM"), + KEYWORD (3, "KEYWORD"); + + private final Integer value; + private final String printval; + + KeywordType(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + } + + + /*-------------------------------------------------------- */ + /* Marker - describes the type of token */ + /* this is rather cosmetic, no function currently. */ + static public enum TokenType { + MODIFIER (1, "Modifier"), + OPERATOR (2, "Operator"), + VALUE (3, "Value"), + TYPE (4, "Type"), + EXPRESSION (5, "Expression"), + STATEMENT (6, "Statement"), + DECLARATION (7, "Declaration"), + PUNCTUATION (8, "Punctuation"), + SPECIAL (9, "Special"), + JASM (10, "Jasm"), + MISC (11, "Misc"), + JASM_IDENT (12, "Jasm identifier"), + MODULE_NAME (13, "Module Name"), + TYPE_PATH_KIND (14, "Type path kind") // Table 4.7.20.2-A Interpretation of type_path_kind values + ; + + private final Integer value; + private final String printval; + + TokenType(Integer val, String print) { + value = val; + printval = print; + } + public String printval() { + return printval; + } + } + + public enum AnnotationType { + Visible("@+"), + Invisible("@-"), + VisibleType("@T+"), + InvisibleType("@T-"); + + private final String jasmPrefix; + + AnnotationType(String jasmPrefix) { + this.jasmPrefix = jasmPrefix; + } + + /** + * isAnnotationToken + * + * examines the beginning of a string to see if it starts with an annotation + * characters ('@+' = visible annotation, '@-' = invisible). + * + * @param str String to be analyzed + * @return True if the string starts with an annotation char. + */ + static public boolean isAnnotationToken(String str) { + return (str.startsWith(AnnotationType.Invisible.jasmPrefix) || + str.startsWith(AnnotationType.Visible.jasmPrefix)); + } + + /** + * isTypeAnnotationToken + * + * examines the beginning of a string to see if it starts with type annotation + * characters ('@T+' = visible type annotation, '@T-' = invisible). + * + * @param str String to be analyzed + * @return True if the string starts with an annotation char. + */ + static public boolean isTypeAnnotationToken(String str) { + return (str.startsWith(AnnotationType.InvisibleType.jasmPrefix) || + str.startsWith(AnnotationType.VisibleType.jasmPrefix)); + } + + /** + * isAnnotation + * + * examines the beginning of a string to see if it starts with an annotation character + * + * @param str String to be analyzed + * @return True if the string starts with an annotation char. + */ + static public boolean isAnnotation(String str) { + return (str.startsWith("@")); + } + + /** + * isInvisibleAnnotationToken + * + * examines the end of an annotation token to determine visibility ('+' = visible + * annotation, '-' = invisible). + * + * @param str String to be analyzed + * @return True if the token implies invisible annotation. + */ + static public boolean isInvisibleAnnotationToken(String str) { + return (str.endsWith("-")); + } + } + + /** + * Scanner Tokens (Definitive List) + */ + public enum Token { + EOF (-1, "EOF", "EOF", EnumSet.of(TokenType.MISC)), + COMMA (0, "COMMA", ",", EnumSet.of(TokenType.OPERATOR)), + ASSIGN (1, "ASSIGN", "=", EnumSet.of(TokenType.OPERATOR)), + + ASGMUL (2, "ASGMUL", "*=", EnumSet.of(TokenType.OPERATOR)), + ASGDIV (3, "ASGDIV", "/=", EnumSet.of(TokenType.OPERATOR)), + ASGREM (4, "ASGREM", "%=", EnumSet.of(TokenType.OPERATOR)), + ASGADD (5, "ASGADD", "+=", EnumSet.of(TokenType.OPERATOR)), + ASGSUB (6, "ASGSUB", "-=", EnumSet.of(TokenType.OPERATOR)), + ASGLSHIFT (7, "ASGLSHIFT", "<<=", EnumSet.of(TokenType.OPERATOR)), + ASGRSHIFT (8, "ASGRSHIFT", ">>=", EnumSet.of(TokenType.OPERATOR)), + ASGURSHIFT (9, "ASGURSHIFT", "<<<=", EnumSet.of(TokenType.OPERATOR)), + ASGBITAND (10, "ASGBITAND", "&=", EnumSet.of(TokenType.OPERATOR)), + ASGBITOR (11, "ASGBITOR", "|=", EnumSet.of(TokenType.OPERATOR)), + ASGBITXOR (12, "ASGBITXOR", "^=", EnumSet.of(TokenType.OPERATOR)), + + COND (13, "COND", "?:", EnumSet.of(TokenType.OPERATOR)), + OR (14, "OR", "||", EnumSet.of(TokenType.OPERATOR)), + AND (15, "AND", "&&", EnumSet.of(TokenType.OPERATOR)), + BITOR (16, "BITOR", "|", EnumSet.of(TokenType.OPERATOR)), + BITXOR (17, "BITXOR", "^", EnumSet.of(TokenType.OPERATOR)), + BITAND (18, "BITAND", "&", EnumSet.of(TokenType.OPERATOR)), + NE (19, "NE", "!=", EnumSet.of(TokenType.OPERATOR)), + EQ (20, "EQ", "==", EnumSet.of(TokenType.OPERATOR)), + GE (21, "GE", ">=", EnumSet.of(TokenType.OPERATOR)), + GT (22, "GT", ">", EnumSet.of(TokenType.OPERATOR)), + LE (23, "LE", "<=", EnumSet.of(TokenType.OPERATOR)), + LT (24, "LT", "<", EnumSet.of(TokenType.OPERATOR)), + INSTANCEOF (25, "INSTANCEOF", "instanceof", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)), + LSHIFT (26, "LSHIFT", "<<", EnumSet.of(TokenType.OPERATOR)), + RSHIFT (27, "RSHIFT", ">>", EnumSet.of(TokenType.OPERATOR)), + URSHIFT (28, "URSHIFT", "<<<", EnumSet.of(TokenType.OPERATOR)), + ADD (29, "ADD", "+", EnumSet.of(TokenType.OPERATOR)), + SUB (30, "SUB", "-", EnumSet.of(TokenType.OPERATOR)), + DIV (31, "DIV", "/", EnumSet.of(TokenType.OPERATOR)), + REM (32, "REM", "%", EnumSet.of(TokenType.OPERATOR)), + MUL (33, "MUL", "*", EnumSet.of(TokenType.OPERATOR)), + CAST (34, "CAST", "cast", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)), + POS (35, "POS", "+", EnumSet.of(TokenType.OPERATOR)), + NEG (36, "NEG", "-", EnumSet.of(TokenType.OPERATOR)), + NOT (37, "NOT", "!", EnumSet.of(TokenType.OPERATOR)), + BITNOT (38, "BITNOT", "~", EnumSet.of(TokenType.OPERATOR)), + PREINC (39, "PREINC", "++", EnumSet.of(TokenType.OPERATOR)), + PREDEC (40, "PREDEC", "--", EnumSet.of(TokenType.OPERATOR)), + NEWARRAY (41, "NEWARRAY", "new", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)), + NEWINSTANCE (42, "NEWINSTANCE", "new", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)), + NEWFROMNAME (43, "NEWFROMNAME", "new", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)), + POSTINC (44, "POSTINC", "++", EnumSet.of(TokenType.OPERATOR)), + POSTDEC (45, "POSTDEC", "--", EnumSet.of(TokenType.OPERATOR)), + FIELD (46, "FIELD", "field", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)), + METHOD (47, "METHOD", "method", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)), + ARRAYACCESS (48, "ARRAYACCESS", "[]", EnumSet.of(TokenType.OPERATOR)), + NEW (49, "NEW", "new", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)), + INC (50, "INC", "++", EnumSet.of(TokenType.OPERATOR)), + DEC (51, "DEC", "--", EnumSet.of(TokenType.OPERATOR)), + + CONVERT (55, "CONVERT", "convert", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)), + EXPR (56, "EXPR", "expr", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)), + ARRAY (57, "ARRAY", "array", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)), + GOTO (58, "GOTO", "goto", EnumSet.of(TokenType.OPERATOR, TokenType.MODULE_NAME)), + + /* + * Value tokens + */ + IDENT (60, "IDENT", "Identifier", EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME, TokenType.JASM_IDENT), KeywordType.VALUE), + BOOLEANVAL (61, "BOOLEANVAL", "Boolean", EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME), KeywordType.VALUE), + BYTEVAL (62, "BYTEVAL", "Byte", EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME)), + CHARVAL (63, "CHARVAL", "Char", EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME)), + SHORTVAL (64, "SHORTVAL", "Short", EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME)), + INTVAL (65, "INTVAL", "Integer", EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME), KeywordType.VALUE), + LONGVAL (66, "LONGVAL", "Long", EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME), KeywordType.VALUE), + FLOATVAL (67, "FLOATVAL", "Float", EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME), KeywordType.VALUE), + DOUBLEVAL (68, "DOUBLEVAL", "Double", EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME), KeywordType.VALUE), + STRINGVAL (69, "STRINGVAL", "String", EnumSet.of(TokenType.VALUE, TokenType.MODULE_NAME), KeywordType.VALUE), + + /* + * Type keywords + */ + BYTE (70, "BYTE", "byte", EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME )), + CHAR (71, "CHAR", "char", EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME )), + SHORT (72, "SHORT", "short", EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME )), + INT (73, "INT", "int", EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME )), + LONG (74, "LONG", "long", EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME )), + FLOAT (75, "FLOAT", "float", EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME)), + DOUBLE (76, "DOUBLE", "double", EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME)), + VOID (77, "VOID", "void", EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME)), + BOOLEAN (78, "BOOLEAN", "boolean", EnumSet.of(TokenType.TYPE, TokenType.MODULE_NAME)), + + /* + * Expression keywords + */ + TRUE (80, "TRUE", "true", EnumSet.of(TokenType.EXPRESSION, TokenType.MODULE_NAME )), + FALSE (81, "FALSE", "false", EnumSet.of(TokenType.EXPRESSION, TokenType.MODULE_NAME )), + THIS (82, "THIS", "this", EnumSet.of(TokenType.EXPRESSION, TokenType.MODULE_NAME )), + SUPER (83, "SUPER", "super", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + NULL (84, "NULL", "null", EnumSet.of(TokenType.EXPRESSION, TokenType.MODULE_NAME )), + + /* + * Statement keywords + */ + IF (90, "IF", "if", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + ELSE (91, "ELSE", "else", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + FOR (92, "FOR", "for", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + WHILE (93, "WHILE", "while", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + DO (94, "DO", "do", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + SWITCH (95, "SWITCH", "switch", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + CASE (96, "CASE", "case", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + DEFAULT (97, "DEFAULT", "default", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + BREAK (98, "BREAK", "break", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + CONTINUE (99, "CONTINUE", "continue", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + RETURN (100, "RETURN", "return", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + TRY (101, "TRY", "try", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + + CATCH (102, "CATCH", "catch", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + FINALLY (103, "FINALLY", "finally", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + THROW (104, "THROW", "throw", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + STAT (105, "STAT", "stat", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + EXPRESSION (106, "EXPRESSION", "expression", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + DECLARATION (107, "DECLARATION", "declaration", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + VARDECLARATION (108, "VARDECLARATION", "vdeclaration", EnumSet.of(TokenType.STATEMENT, TokenType.MODULE_NAME )), + + /* + * Declaration keywords + */ + IMPORT (110, "IMPORT", "import", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME )), + CLASS (111, "CLASS", "class", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + EXTENDS (112, "EXTENDS", "extends", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + IMPLEMENTS (113, "IMPLEMENTS", "implements", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + INTERFACE (114, "INTERFACE", "interface", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + PACKAGE (115, "PACKAGE", "package", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + ENUM (116, "ENUM", "enum", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + MANDATED (117, "MANDATED", "mandated", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + THROWS (118, "THROWS", "throws", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + + /* + * Modifier keywords + */ + ANNOTATION_ACCESS (119, "ANNOTATION_ACCESS", "annotation", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + PRIVATE (120, "PRIVATE", "private", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + PUBLIC (121, "PUBLIC", "public", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + PROTECTED (122, "PROTECTED", "protected", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + CONST (123, "CONST", "const", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME), KeywordType.KEYWORD), + STATIC (124, "STATIC", "static", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + TRANSIENT (125, "TRANSIENT", "transient", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + SYNCHRONIZED (126, "SYNCHRONIZED", "synchronized", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + NATIVE (127, "NATIVE", "native", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + FINAL (128, "FINAL", "final", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + VOLATILE (129, "VOLATILE", "volatile", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + ABSTRACT (130, "ABSTRACT", "abstract", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + TRANSITIVE (131, "TRANSITIVE", "transitive", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + OPEN (132, "OPEN", "open", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + + /* + * Punctuation + */ + AT_SIGN (133, "AT", ";", EnumSet.of(TokenType.PUNCTUATION), KeywordType.VALUE), + SEMICOLON (134, "SEMICOLON", ";", EnumSet.of(TokenType.PUNCTUATION), KeywordType.VALUE), + COLON (135, "COLON", ":", EnumSet.of(TokenType.PUNCTUATION), KeywordType.VALUE), + QUESTIONMARK (136, "QUESTIONMARK", "?", EnumSet.of(TokenType.PUNCTUATION)), + LBRACE (137, "LBRACE", "{", EnumSet.of(TokenType.PUNCTUATION), KeywordType.VALUE), + RBRACE (138, "RBRACE", "}", EnumSet.of(TokenType.PUNCTUATION), KeywordType.VALUE), + LPAREN (139, "LPAREN", "(", EnumSet.of(TokenType.PUNCTUATION)), + RPAREN (140, "RPAREN", ")", EnumSet.of(TokenType.PUNCTUATION)), + LSQBRACKET (141, "LSQBRACKET", "[", EnumSet.of(TokenType.PUNCTUATION)), + RSQBRACKET (142, "RSQBRACKET", "]", EnumSet.of(TokenType.PUNCTUATION)), + + ESCAPED_COLON (201, "ESCCOLON", "\\:", EnumSet.of(TokenType.PUNCTUATION, TokenType.MODULE_NAME)), + ESCAPED_ATSIGH (202, "ESCATSIGH", "\\@", EnumSet.of(TokenType.PUNCTUATION, TokenType.MODULE_NAME)), + ESCAPED_BACKSLASH (203, "ESCBACKSLASH", "\\\\", EnumSet.of(TokenType.PUNCTUATION, TokenType.MODULE_NAME)), + /* + * Special tokens + */ + ERROR (145, "ERROR", "error", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME)), + COMMENT (146, "COMMENT", "comment", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME)), + TYPE (147, "TYPE", "type", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME)), + LENGTH (148, "LENGTH", "length", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME )), + INLINERETURN (149, "INLINERETURN", "inline-return", EnumSet.of(TokenType.MODIFIER)), + INLINEMETHOD (150, "INLINEMETHOD", "inline-method", EnumSet.of(TokenType.MODIFIER)), + INLINENEWINSTANCE (151, "INLINENEWINSTANCE", "inline-new",EnumSet.of(TokenType.MODIFIER)), + + /* + * Added for jasm + */ + METHODREF (152, "METHODREF", "Method", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + FIELDREF (153, "FIELD", "Field", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + STACK (154, "STACK", "stack", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + LOCAL (155, "LOCAL", "locals", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + CPINDEX (156, "CPINDEX", "CPINDEX", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME )), + CPNAME (157, "CPNAME", "CPName", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME )), + SIGN (158, "SIGN", "SIGN", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME )), + BITS (159, "BITS", "bits", EnumSet.of(TokenType.MISC, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + + INF (160, "INF", "Inf", "Infinity", EnumSet.of(TokenType.MISC, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + NAN (161, "NAN", "NaN", EnumSet.of(TokenType.MISC, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + + INNERCLASS (162, "INNERCLASS", "InnerClass", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + OF (163, "OF", "of", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + SYNTHETIC (164, "SYNTHETIC", "synthetic", EnumSet.of(TokenType.MODIFIER, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + STRICT (165, "STRICT", "strict", EnumSet.of(TokenType.MODIFIER, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + DEPRECATED (166, "DEPRECATED", "deprecated", EnumSet.of(TokenType.MODIFIER, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + VERSION (167, "VERSION", "version", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + MODULE (168, "MODULE", "module", EnumSet.of(TokenType.DECLARATION, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + ANNOTATION (169, "ANNOTATION", "@", EnumSet.of(TokenType.MISC, TokenType.MODULE_NAME )), + PARAM_NAME (173, "PARAM_NAME", "#", EnumSet.of(TokenType.MISC, TokenType.MODULE_NAME )), + + VARARGS (170, "VARARGS", "varargs", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + BRIDGE (171, "BRIDGE", "bridge", EnumSet.of(TokenType.MODIFIER, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + + // Declaration keywords + BOOTSTRAPMETHOD (172, "BOOTSTRAPMETHOD", "BootstrapMethod", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + NESTHOST (173, "NESTHOST", "NestHost", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + NESTMEMBERS (174, "NESTMEMBERS", "NestMembers", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + // + RECORD (175, "RECORD", "Record", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + COMPONENT (176, "COMPONENT", "Component", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + // + PERMITTEDSUBCLASSES (177, "PERMITTEDSUBCLASSES", "PermittedSubclasses", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + + //Module statements + REQUIRES (180, "REQUIRES", "requires", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + EXPORTS (182, "EXPORTS", "exports", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + TO (183, "TO", "to", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + USES (184, "USES", "uses", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + PROVIDES (185, "PROVIDES", "provides", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + WITH (186, "WITH", "with", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + OPENS (187, "OPENS", "opens", EnumSet.of(TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + + // Table 4.7.20.2-1 type_path_kind + ARRAY_TYPEPATH (188, TypeAnnotationTypes.EPathKind.ARRAY.parseKey(), TypeAnnotationTypes.EPathKind.ARRAY.parseKey(), + EnumSet.of(TokenType.TYPE_PATH_KIND, TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + INNER_TYPE_TYPEPATH (189, TypeAnnotationTypes.EPathKind.INNER_TYPE.parseKey(), TypeAnnotationTypes.EPathKind.INNER_TYPE.parseKey(), + EnumSet.of(TokenType.TYPE_PATH_KIND, TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + WILDCARD_TYPEPATH (190, TypeAnnotationTypes.EPathKind.WILDCARD.parseKey(), TypeAnnotationTypes.EPathKind.WILDCARD.parseKey(), + EnumSet.of(TokenType.TYPE_PATH_KIND, TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD), + TYPE_ARGUMENT_TYPEPATH (191, TypeAnnotationTypes.EPathKind.TYPE_ARGUMENT.parseKey(), TypeAnnotationTypes.EPathKind.TYPE_ARGUMENT.parseKey(), + EnumSet.of(TokenType.TYPE_PATH_KIND, TokenType.DECLARATION, TokenType.JASM_IDENT, TokenType.MODULE_NAME ), KeywordType.KEYWORD); + + final static EnumSet ALL_TOKENS = EnumSet.allOf(Token.class); + // Misc Keywords + final private Integer value; // 160 + final private String printval; // INF + final private String parsekey; // inf + final private String alias; // Infinity + final private EnumSet tokenType; // TokenType.MISC, TokenType.MODULE_NAME + final private KeywordType key_type; // KeywordType.KEYWORD + + public static Optional get(String parsekey, KeywordType ktype) { + return ALL_TOKENS.stream(). + filter(t->t.key_type == ktype). + filter(t->t.parsekey.equals(parsekey) || ( t.alias != null && t.alias.equals(parsekey))). + findFirst(); + } + + // By default, if a KeywordType is not specified, it has the value 'TOKEN' + Token(Integer val, String print, String parsekey, EnumSet ttype) { + this(val, print, parsekey, null, ttype, KeywordType.TOKEN); + } + + Token(Integer val, String print, String parsekey, String als, EnumSet ttype) { + this(val, print, parsekey, als, ttype, KeywordType.TOKEN); + } + + Token(Integer val, String print, String parsekey, EnumSet ttype, KeywordType ktype) { + this(val, print, parsekey, null, ttype, ktype); + } + + Token(Integer val, String print, String parsekey, String als, EnumSet ttype, KeywordType ktype) { + this.value = val; + this.printval = print; + this.parsekey = parsekey; + this.tokenType = ttype; + this.key_type = ktype; + this.alias = als; + } + + public String printValue() { + return printval; + } + + public String parseKey() { + return parsekey; + } + + public int value() { + return value; + } + + public boolean possibleJasmIdentifier() { + return tokenType.contains(TokenType.JASM_IDENT); + } + + public boolean possibleModuleName() { return tokenType.contains(TokenType.MODULE_NAME) && !tokenType.contains(TokenType.PUNCTUATION); } + + /** + * Checks a token belonging to the table: Table 4.7.20.2-A. Interpretation of type_path_kind values + * + * @return true if token is ARRAY, INNER_TYPE, WILDCARD or TYPE_ARGUMENT + */ + public boolean possibleTypePathKind() { return tokenType.contains(TokenType.TYPE_PATH_KIND); } + + @Override + public String toString() { + return "<" + printval + "> [" + value + "]"; + } + } + + public static Token keyword_token_ident(String idValue) { + return Token.get(idValue,KeywordType.KEYWORD).orElse(Token.IDENT); + } +} diff --git a/src/org/openjdk/asmtools/jasm/Main.java b/src/org/openjdk/asmtools/jasm/Main.java new file mode 100644 index 0000000..2b99f6e --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Main.java @@ -0,0 +1,330 @@ +/* + * Copyright (c) 1996, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.CFVersion.DEFAULT_MAJOR_VERSION; +import static org.openjdk.asmtools.jasm.CFVersion.DEFAULT_MINOR_VERSION; + +import org.openjdk.asmtools.common.Tool; +import org.openjdk.asmtools.util.I18NResourceBundle; +import org.openjdk.asmtools.util.ProductInfo; + +import java.io.*; +import java.util.ArrayList; + +/** + * + * + */ +public class Main extends Tool { + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + private File destDir = null; + private boolean traceFlag = false; + private long tm = System.currentTimeMillis(); + private ArrayList v = new ArrayList<>(); + private boolean nowrite = false; + private boolean nowarn = false; + private boolean strict = false; + private String props = null; + private int nwarnings = 0; + private CFVersion cfv = new CFVersion(); + private int bytelimit = 0; + private boolean debugScanner = false; + private boolean debugMembers = false; + private boolean debugCP = false; + private boolean debugAnnot = false; + private boolean debugInstr = false; + + + public Main(PrintWriter out, String programName) { + super(out, programName); + printCannotReadMsg = (fname) -> + error( i18n.getString("jasm.error.cannot_read", fname)); + } + + public Main(PrintStream out, String program) { + this(new PrintWriter(out), program); + } + + @Override + public void usage() { + println(i18n.getString("jasm.usage")); + println(i18n.getString("jasm.opt.d")); + println(i18n.getString("jasm.opt.g")); + println(i18n.getString("jasm.opt.v")); + println(i18n.getString("jasm.opt.nowrite")); + println(i18n.getString("jasm.opt.nowarn")); + println(i18n.getString("jasm.opt.strict")); + println(i18n.getString("jasm.opt.cv", DEFAULT_MAJOR_VERSION, DEFAULT_MINOR_VERSION)); + println(i18n.getString("jasm.opt.version")); + } + + /** + * Run the compiler + */ + private synchronized boolean parseArgs(String argv[]) { + // Parse arguments + boolean frozenCFV = false; + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + switch (arg) { + case "-v": + traceFlag = true; + break; + case "-g": + super.DebugFlag = () -> true; + break; + case "-nowrite": + nowrite = true; + break; + case "-strict": + strict = true; + break; + case "-nowarn": + nowarn = true; + break; + case "-version": + println(ProductInfo.FULL_VERSION); + break; + case "-d": + if ((i + 1) >= argv.length) { + error(i18n.getString("jasm.error.d_requires_argument")); + usage(); + return false; + } + destDir = new File(argv[++i]); + if (!destDir.exists()) { + error(i18n.getString("jasm.error.does_not_exist", destDir.getPath())); + return false; + } + break; + // non-public options + case "-XdScanner": + debugScanner = true; + break; + case "-XdMember": + debugMembers = true; + break; + case "-XdCP": + debugCP = true; + break; + case "-XdInstr": + debugInstr = true; + break; + case "-XdAnnot": + debugAnnot = true; + break; + case "-XdAll": + debugScanner = true; + debugMembers = true; + debugCP = true; + debugInstr = true; + debugAnnot = true; + break; + case "-Xdlimit": + // parses file until the specified byte number + if (i + 1 > argv.length) { + println(" Error: Unspecified byte-limit"); + return false; + } else { + i++; + String bytelimstr = argv[i]; + bytelimit = 0; + try { + bytelimit = Integer.parseInt(bytelimstr); + } catch (NumberFormatException e) { + println(" Error: Unspecified byte-limit"); + return false; + } + } + break; + case "-fixcv": + // overrides cf version if it's defined in the source file. + frozenCFV = true; + // public options + case "-cv": + if ((i + 1) >= argv.length) { + error(i18n.getString("jasm.error.cv_requires_arg")); + usage(); + return false; + } + String[] versions = {"", ""}; // workaround for String.split() + int index = argv[++i].indexOf("."); // + if (index != -1) { // + versions[0] = argv[i].substring(0, index); // + versions[1] = argv[i].substring(index + 1); // + } // + if (versions.length != 2) { + error(i18n.getString("jasm.error.invalid_major_minor_param")); + usage(); + return false; + } + try { + cfv = new CFVersion(frozenCFV, Short.parseShort(versions[0]), Short.parseShort(versions[1]) ); + } catch (NumberFormatException e) { + error(i18n.getString("jasm.error.invalid_major_minor_param")); + usage(); + return false; + } + break; + default: + if (arg.startsWith("-")) { + error(i18n.getString("jasm.error.invalid_option", arg)); + usage(); + return false; + } else { + v.add(argv[i]); + } + break; + } + } + if (v.size() == 0) { + usage(); + return false; + } + if (strict) { + nowarn = false; + } + return true; + } + + private void reset() { + destDir = null; + traceFlag = false; + super.DebugFlag = () -> false; + System.currentTimeMillis(); + v = new ArrayList<>(); + nowrite = false; + nowarn = false; + strict = false; + props = null; + nwarnings = 0; + bytelimit = 0; + } + + /** + * Run the compiler + */ + public synchronized boolean compile(String argv[]) { + // Reset the state of all objs + reset(); + + boolean validArgs = parseArgs(argv); + if (!validArgs) { + return false; + } + // compile all input files + Environment sf = null; + try { + for (String inpname : v) { + Parser p; + + DataInputStream dataInputStream = getDataInputStream(inpname); + if( dataInputStream == null ) { + nerrors++; + continue; + } + sf = new Environment(dataInputStream, inpname, out, nowarn); + sf.traceFlag = traceFlag; + sf.debugInfoFlag = DebugFlag.getAsBoolean(); + p = new Parser(sf, cfv.clone() ); + p.setDebugFlags(debugScanner, debugMembers, debugCP, debugAnnot, debugInstr); + p.parseFile(); + + nerrors += sf.nerrors; + nwarnings += sf.nwarnings; + if (nowrite || (nerrors > 0)) { + sf.flushErrors(); + continue; + } + try { + ClassData[] clsData = p.getClassesData(); + for (int i = 0; i < clsData.length; i++) { + ClassData cd = clsData[i]; + if (bytelimit > 0) { + cd.setByteLimit(bytelimit); + } + cd.write(destDir); + } + } catch (IOException ex) { + if (bytelimit > 0) { + // IO Error thrown from user-specified byte count + ex.printStackTrace(); + error("UserSpecified byte-limit at byte[" + bytelimit + "]: " + + ex.getMessage() + "\n" + + inpname + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]"); + } else { + String er = i18n.getString("jasm.error.cannot_write", ex.getMessage()); + error(er + "\n" + inpname + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]"); + } + } + sf.flushErrors(); // possible errors from write() + } + } catch (Error ee) { + if (DebugFlag.getAsBoolean()) { + ee.printStackTrace(); + } + String er = ee.getMessage() + "\n" + i18n.getString("jasm.error.fatal_error"); + error(er + "\n" + sf.getInputFileName() + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]"); + } catch (Exception ee) { + if (DebugFlag.getAsBoolean()) { + ee.printStackTrace(); + } + String er = ee.getMessage() + "\n" + ee.getMessage() + "\n" + i18n.getString("jasm.error.fatal_exception"); + error(er + "\n" + sf.getInputFileName() + ": [" + sf.lineNumber() + ", " + sf.lineOffset() + "]"); + } + + boolean errs = nerrors > 0; + boolean warns = (nwarnings > 0) && (!nowarn); + boolean errsOrWarns = errs || warns; + if (!errsOrWarns) { + return true; + } + if (errs) { + out.print(nerrors > 1 ? (nerrors + " errors") : "1 error"); + } + if (errs && warns) { + out.print(", "); + } + if (warns) { + out.print(nwarnings > 1 ? (nwarnings + " warnings") : "1 warning"); + } + println(); + if (strict) { + return !errsOrWarns; + } else { + return !errs; + } + } + + /** + * main program + */ + public static void main(String argv[]) { + Main compiler = new Main(new PrintWriter(System.out), "jasm"); + System.exit(compiler.compile(argv) ? 0 : 1); + } +} diff --git a/src/org/openjdk/asmtools/jasm/MemberData.java b/src/org/openjdk/asmtools/jasm/MemberData.java new file mode 100644 index 0000000..17aece6 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/MemberData.java @@ -0,0 +1,138 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.RuntimeConstants.DEPRECATED_ATTRIBUTE; +import static org.openjdk.asmtools.jasm.RuntimeConstants.SYNTHETIC_ATTRIBUTE; +import org.openjdk.asmtools.jasm.Tables.AttrTag; +import java.util.ArrayList; + +/** + * The common base structure for field_info, method_info, and component_info + */ +abstract public class MemberData { + + protected int access; + protected AttrData syntheticAttr, deprecatedAttr; + protected DataVectorAttr annotAttrVis = null; + protected DataVectorAttr annotAttrInv = null; + protected DataVectorAttr type_annotAttrVis = null; + protected DataVectorAttr type_annotAttrInv = null; + protected ClassData cls; + protected AttrData signatureAttr; + + public MemberData(ClassData cls, int access) { + this.cls = cls; + init(access); + } + + public MemberData(ClassData cls) { + this.cls = cls; + } + + public void init(int access) { + this.access = access; + } + + public void createPseudoMod() { + // If a member has a Pseudo-modifier + + // create the appropriate marker attributes, + // and clear the PseudoModifiers from the access flags. + if (Modifiers.isSyntheticPseudoMod(access)) { + syntheticAttr = new AttrData(cls, AttrTag.ATT_Synthetic.parsekey()); + access &= ~SYNTHETIC_ATTRIBUTE; + } + if (Modifiers.isDeprecatedPseudoMod(access)) { + deprecatedAttr = new AttrData(cls, AttrTag.ATT_Deprecated.parsekey()); + access &= ~DEPRECATED_ATTRIBUTE; + } + } + + public void setSignatureAttr(ConstantPool.ConstCell value_cpx) { + signatureAttr = new CPXAttr(cls, Tables.AttrTag.ATT_Signature.parsekey(), value_cpx); + } + + protected abstract DataVector getAttrVector(); + + protected final DataVector getDataVector(Data... extraAttrs) { + DataVector attrs = new DataVector(); + for( Data extra : extraAttrs ) { + if (extra != null) { + attrs.add(extra); + } + } + // common set for [ FieldData, MethodData, RecordData ] + if (annotAttrVis != null) { + attrs.add(annotAttrVis); + } + if (annotAttrInv != null) { + attrs.add(annotAttrInv); + } + if (type_annotAttrVis != null) { + attrs.add(type_annotAttrVis); + } + if (type_annotAttrInv != null) { + attrs.add(type_annotAttrInv); + } + return attrs; + } + + public void addAnnotations(ArrayList list) { + for (AnnotationData item : list) { + boolean invisible = item.invisible; + if (item instanceof TypeAnnotationData) { + // Type Annotations + TypeAnnotationData ta = (TypeAnnotationData) item; + if (invisible) { + if (type_annotAttrInv == null) { + type_annotAttrInv = new DataVectorAttr(cls, + AttrTag.ATT_RuntimeInvisibleTypeAnnotations.parsekey()); + } + type_annotAttrInv.add(ta); + } else { + if (type_annotAttrVis == null) { + type_annotAttrVis = new DataVectorAttr(cls, + AttrTag.ATT_RuntimeVisibleTypeAnnotations.parsekey()); + } + type_annotAttrVis.add(ta); + } + } else { + // Regular Annotations + if (invisible) { + if (annotAttrInv == null) { + annotAttrInv = new DataVectorAttr(cls, + AttrTag.ATT_RuntimeInvisibleAnnotations.parsekey()); + } + annotAttrInv.add(item); + } else { + if (annotAttrVis == null) { + annotAttrVis = new DataVectorAttr(cls, + AttrTag.ATT_RuntimeVisibleAnnotations.parsekey()); + } + annotAttrVis.add(item); + } + } + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/MethodData.java b/src/org/openjdk/asmtools/jasm/MethodData.java new file mode 100644 index 0000000..f988471 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/MethodData.java @@ -0,0 +1,228 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.jasm.Tables.AttrTag; +import org.openjdk.asmtools.jasm.ConstantPool.ConstCell; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.TreeMap; + +/** + * + */ +class MethodData extends MemberData { + + /** + * MethodParamData + */ + class ParamNameData implements Data { + + int access; + ConstCell name; + + public ParamNameData(int access, ConstCell name) { + this.access = access; + this.name = name; + } + + @Override + public int getLength() { + return 4; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + int nm = 0; + int ac = 0; + if (name != null) { + nm = name.arg; + ac = access; + } + out.writeShort(nm); + out.writeShort(ac); + } + }// end class MethodParamData + + /** + * Used to store Parameter Arrays (as attributes) + */ + static public class DataPArrayAttr extends AttrData implements Constants { + + TreeMap> elements; // Data + int paramsTotal; + + public DataPArrayAttr(ClassData cls, String name, int paramsTotal, TreeMap> elements) { + super(cls, name); + this.paramsTotal = paramsTotal; + this.elements = elements; + } + + public DataPArrayAttr(ClassData cls, String name, int paramsTotal) { + this(cls, name, paramsTotal, new TreeMap>()); + } + + public void put(int paramNum, T element) { + ArrayList v = get(paramNum); + if (v == null) { + v = new ArrayList<>(); + elements.put(paramNum, v); + } + + v.add(element); + } + + public ArrayList get(int paramNum) { + return elements.get(paramNum); + } + + @Override + public int attrLength() { + int length = 1; // One byte for the parameter count + + // calculate overall size here rather than in add() + // because it may not be available at the time of invoking of add() + for (int i = 0; i < paramsTotal; i++) { + ArrayList attrarray = get(i); + if (attrarray != null) { + for (Data item : attrarray) { + length += item.getLength(); + } + } + length += 2; // 2 bytes for the annotation count for each parameter + } + + return length; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); // attr name, attr len + out.writeByte(paramsTotal); // number of parameters total (in byte) + + for (int i = 0; i < paramsTotal; i++) { + ArrayList attrarray = get(i); + if (attrarray != null) { + // write out the number of annotations for the current param + out.writeShort(attrarray.size()); + for (T item : attrarray) { + item.write(out); // write the current annotation + } + } else { + out.writeShort(0); + // No annotations to write out + } + } + } + }// end class DataPArrayAttr + + + /* Method Data Fields */ + protected Environment env; + protected ConstCell nameCell, sigCell; + protected CodeAttr code; + protected DataVectorAttr exceptions = null; + protected DataVectorAttr paramNames = null; + protected DataPArrayAttr pannotAttrVis = null; + protected DataPArrayAttr pannotAttrInv = null; + protected DefaultAnnotationAttr defaultAnnot = null; + + public MethodData(ClassData cls, int acc, + ConstCell name, ConstCell sig, ArrayList exc_table) { + super(cls, acc); + this.env = cls.env; + nameCell = name; + sigCell = sig; + if ((exc_table != null) && (!exc_table.isEmpty())) { + exceptions = new DataVectorAttr<>(cls, + AttrTag.ATT_Exceptions.parsekey(), + exc_table); + } + // Normalize the modifiers to access flags + if (Modifiers.hasPseudoMod(acc)) { + createPseudoMod(); + } + } + + public void addMethodParameter(int totalParams, int paramNum, ConstCell name, int access) { + env.traceln("addMethodParameter Param[" + paramNum + "] (name: " + name.toString() + ", Flags (" + access + ")."); + if (paramNames == null) { + paramNames = new DataVectorAttr<>(cls, AttrTag.ATT_MethodParameters.parsekey(), true); + for (int i = 0; i < totalParams; i++) { + // initialize the paramName array (in case the name is not given in Jasm syntax) + paramNames.add(new ParamNameData(0, null)); + } + } + paramNames.put(paramNum, new ParamNameData(access, name)); + } + + public CodeAttr startCode(int pos, int paramcnt, Argument max_stack, Argument max_locals) { + code = new CodeAttr(this, pos, paramcnt, max_stack, max_locals); + return code; + } + + public void addDefaultAnnotation(DefaultAnnotationAttr data) { + defaultAnnot = data; + } + + public void addParamAnnotation(int totalParams, int paramNum, AnnotationData data) { + if (!data.invisible) { + if (pannotAttrVis == null) { + pannotAttrVis = new DataPArrayAttr<>(cls, + AttrTag.ATT_RuntimeVisibleParameterAnnotations.parsekey(), + totalParams); + } + pannotAttrVis.put(paramNum, data); + + } else { + if (pannotAttrInv == null) { + pannotAttrInv = new DataPArrayAttr<>(cls, + AttrTag.ATT_RuntimeInvisibleParameterAnnotations.parsekey(), + totalParams); + } + pannotAttrInv.put(paramNum, data); + } + } + + @Override + protected DataVector getAttrVector() { + DataVector dv = getDataVector( exceptions, syntheticAttr, deprecatedAttr, paramNames, code, defaultAnnot); + if (pannotAttrVis != null) { + dv.add(pannotAttrVis); + } + if (pannotAttrInv != null) { + dv.add(pannotAttrInv); + } + return dv; + } + + /*====================================================== Write */ + public void write(CheckedDataOutputStream out) throws IOException, Parser.CompilerError { + out.writeShort(access); + out.writeShort(nameCell.arg); + out.writeShort(sigCell.arg); + getAttrVector().write(out); + } +} // end MethodData + diff --git a/src/org/openjdk/asmtools/jasm/Modifiers.java b/src/org/openjdk/asmtools/jasm/Modifiers.java new file mode 100644 index 0000000..9147ec5 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Modifiers.java @@ -0,0 +1,465 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.JasmTokens.Token; +import static org.openjdk.asmtools.jasm.RuntimeConstants.*; +import static org.openjdk.asmtools.jasm.Tables.CF_Context; + +/** + * + * + */ +public class Modifiers { + + /* + * Modifier masks + */ + public static final int MM_ATTR = SYNTHETIC_ATTRIBUTE | DEPRECATED_ATTRIBUTE; + + public static final int MM_ACCESS = ACC_PUBLIC | ACC_PRIVATE | ACC_PROTECTED; + + public static final int MM_INTRF = MM_ACCESS | ACC_ABSTRACT | ACC_INTERFACE | MM_ATTR | ACC_ANNOTATION; + + public static final int MM_CLASS = MM_ACCESS | ACC_FINAL | ACC_SUPER | ACC_ABSTRACT | ACC_ENUM | + MM_ATTR | ACC_MODULE ; + + public static final int MM_FIELD = MM_ACCESS | ACC_STATIC | ACC_FINAL | ACC_VOLATILE | ACC_TRANSIENT | + ACC_SYNTHETIC | ACC_ENUM | + ACC_MANDATED | // JEP 359 Record + MM_ATTR; + + public static final int MM_I_METHOD = ACC_ABSTRACT | ACC_PUBLIC | ACC_PRIVATE | ACC_STATIC | ACC_VARARGS | + ACC_BRIDGE | ACC_SYNTHETIC ; // interface method + + public static final int MM_A_METHOD = MM_ACCESS | ACC_ABSTRACT | MM_ATTR; + + public static final int MM_N_METHOD = MM_ACCESS | ACC_STRICT | ACC_VARARGS | ACC_SYNTHETIC | MM_ATTR; // + + public static final int MM_METHOD = MM_ACCESS | ACC_STATIC | ACC_FINAL | ACC_SYNCHRONIZED | ACC_BRIDGE | + ACC_VARARGS | ACC_NATIVE | ACC_ABSTRACT | ACC_STRICT | ACC_SYNTHETIC | + ACC_MANDATED | // JEP 359 Record + MM_ATTR ; + + public static final int MM_INNERCLASS = MM_ACCESS | ACC_STATIC | ACC_FINAL | ACC_SUPER | ACC_INTERFACE | + ACC_ABSTRACT | ACC_SYNTHETIC | ACC_ANNOTATION | ACC_ENUM | MM_ATTR ; + + public static final int MM_REQUIRES = ACC_TRANSITIVE | ACC_STATIC_PHASE | ACC_SYNTHETIC | ACC_MANDATED ; + + public static final int MM_EXPORTS = ACC_SYNTHETIC | ACC_MANDATED ; + + private Modifiers() { + } + + public static boolean validRequires(int mod) { + return (mod & ~MM_REQUIRES) == 0; + } + + public static boolean validExports(int mod) { return (mod & ~MM_EXPORTS) == 0; } + + public static boolean validInnerClass(int mod) { + return (mod & ~MM_INNERCLASS) == 0; + } + + public static boolean validField(int mod) { + return (mod & ~MM_FIELD) == 0; + } + + public static boolean validMethod(int mod) { + return (mod & ~MM_METHOD) == 0; + } + + public static boolean validInterface(int mod) { + return (mod & ~MM_INTRF) == 0; + } + + public static int getInvalidModifiers4Interface(int mod) { + return mod & ~MM_INTRF; + } + + public static boolean validClass(int mod) { + return (mod & ~MM_CLASS) == 0; + } + + public static int getInvalidModifiers4Class(int mod) { + return (mod & ~MM_CLASS); + } + + public static boolean validAbstractMethod(int mod) { + return (mod & ~MM_A_METHOD) == 0; + } + + public static boolean validInitMethod(int mod) { + return (mod & ~MM_N_METHOD) == 0; + } + + public static boolean validInterfaceMethod(int mod, ClassData cd) { + return ((mod & ~MM_I_METHOD) == 0) && + (cd.cfv.major_version() >= 52 || isPublic(mod) && isAbstract(mod) && !isStatic(mod)); + } + + public static boolean validInterfaceField(int mod) { + return mod == (ACC_STATIC | ACC_PUBLIC | ACC_FINAL); + } + + public static boolean isPublic(int mod) { + return (mod & ACC_PUBLIC) != 0; + } + + public static boolean isPrivate(int mod) { + return (mod & ACC_PRIVATE) != 0; + } + + public static boolean isProtected(int mod) { + return (mod & ACC_PROTECTED) != 0; + } + + public static boolean isInterface(int mod) { + return (mod & ACC_INTERFACE) != 0; + } + + public static boolean isAbstract(int mod) { + return (mod & ACC_ABSTRACT) != 0; + } + + public static boolean isFinal(int mod) { + return (mod & ACC_FINAL) != 0; + } + + public static boolean isStatic(int mod) { + return (mod & ACC_STATIC) != 0; + } + + public static boolean isSynthetic(int mod) { + return (mod & ACC_SYNTHETIC) != 0; + } + + public static boolean isDeprecated(int mod) { + return (mod & DEPRECATED_ATTRIBUTE) != 0; + } + + public static boolean isTransient(int mod) { + return (mod & ACC_TRANSIENT) != 0; + } + + public static boolean isAnnotation(int mod) { + return (mod & ACC_ANNOTATION) != 0; + } + + public static boolean isNative(int mod) { + return (mod & ACC_NATIVE) != 0; + } + + public static boolean isStrict(int mod) { + return (mod & ACC_STRICT) != 0; + } + + public static boolean isEnum(int mod) { + return (mod & ACC_ENUM) != 0; + } + + public static boolean isSuper(int mod) { + return (mod & ACC_SUPER) != 0; + } + + public static boolean isModule(int mod) { return (mod & ACC_MODULE)!=0; } + + public static boolean isMandated(int mod) { return (mod & ACC_MANDATED) != 0; } + + public static boolean isSynchronized(int mod) { + return (mod & ACC_SYNCHRONIZED) != 0; + } + + public static boolean isBridge(int mod) { + return (mod & ACC_BRIDGE) != 0; + } + + public static boolean isVolatile(int mod) { + return (mod & ACC_VOLATILE) != 0; + } + + public static boolean isVarArgs(int mod) { + return (mod & ACC_VARARGS) != 0; + } + + public static boolean isSyntheticPseudoMod(int mod) { + return (mod & SYNTHETIC_ATTRIBUTE) != 0; + } + + public static boolean isDeprecatedPseudoMod(int mod) { + return (mod & DEPRECATED_ATTRIBUTE) != 0; + } + + public static boolean hasPseudoMod(int mod) { + return isSyntheticPseudoMod(mod) || isDeprecatedPseudoMod(mod); + } + + public static boolean isTransitive(int mod) { return (mod & ACC_TRANSITIVE) != 0; } + + public static boolean isStaticPhase(int mod) { return (mod & ACC_STATIC_PHASE) != 0; } + + /* + * Checks that only one (or none) of the Access flags are set. + */ + public static boolean validAccess(int mod) { + boolean retval = true; + switch (mod & MM_ACCESS) { + case 0: + case ACC_PUBLIC: + case ACC_PRIVATE: + case ACC_PROTECTED: + break; + default: + retval = false; + } + return retval; + } + + /* + * Are both flags set + * + */ + public static boolean both(int mod, int flagA, int flagB) { + return (mod & (flagA | flagB)) == (flagA | flagB); + } + + /** + * Check the modifier flags for the class + * + * @param env The error reporting environment. + * @param mod The modifier flags being checked + * @param scanner The file parser + */ + public static void checkClassModifiers(Environment env, int mod, Scanner scanner) { + if (isInterface(mod)) { + if( isEnum(mod) ) { + env.error(scanner.pos, "warn.invalid.modifier.class.intenum"); + } else if ( !validInterface(mod) ) { + env.error(scanner.pos, "warn.invalid.modifier.int", + toString(mod & ~MM_INTRF, CF_Context.CTX_CLASS)); + } + if (!isAbstract(mod)) { + env.error(scanner.pos, "warn.invalid.modifier.int.abs"); + } + } else { + if ( scanner.token != Token.CLASS && !isEnum(mod) && scanner.token != Token.ANNOTATION) { + env.error(scanner.pos, "warn.missing.modifier.class"); + } + if (! validClass(mod)) { + env.error(scanner.pos, "warn.invalid.modifier.class", + toString(mod & ~MM_CLASS, CF_Context.CTX_CLASS)); + } + if (isAbstract(mod) && Modifiers.isFinal(mod)) { + env.error(scanner.pos, "warn.invalid.modifier.class.finabs"); + } + } + } + + /** + * Check the modifier flags for the field + * + * @param cd The ClassData for the current class + * @param mod The modifier flags being checked + * @param pos the position of the parser in the file + */ + public static void checkFieldModifiers(ClassData cd, int mod, int pos) { + Environment env = cd.env; + if (cd.isInterface()) { + // For interfaces + if (!validInterfaceField(mod)) { + env.error(pos, "warn.invalid.modifier.intfield"); + } + } else { + // For non-interfaces + if (!validField(mod)) { + env.error(pos, "warn.invalid.modifier.field", + toString(mod & ~MM_FIELD, CF_Context.CTX_METHOD)); + } + if (both(mod, ACC_FINAL, ACC_VOLATILE)) { + env.error(pos, "warn.invalid.modifier.fiva"); + } + if (!validAccess(mod)) { + env.error(pos, "warn.invalid.modifier.acc"); + } + } + + } + + /** + * Check the modifier flags for the method + * + * @param cd The ClassData for the current class + * @param mod The modifier flags being checked + * @param pos the position of the parser in the file + */ + public static void checkMethodModifiers(ClassData cd, int mod, int pos, boolean is_init, boolean is_clinit) { + Environment env = cd.env; + if (!is_clinit) { + if (cd.isInterface()) { + if (is_init) { + env.error(pos, "warn.init.in_int"); + } else if (!validInterfaceMethod(mod, cd)) { + int badflags = (mod & ~MM_I_METHOD); + env.error(pos, "warn.invalid.modifier.intmth", toString(badflags, CF_Context.CTX_METHOD) + + " *****" + toString(mod, CF_Context.CTX_METHOD) + "*****"); + } + } else { + if (is_init && !validInitMethod(mod)) { + int badflags = (mod & ~MM_N_METHOD); + env.error(pos, "warn.invalid.modifier.init", toString(badflags, CF_Context.CTX_METHOD) + + " *****" + toString(mod, CF_Context.CTX_METHOD) + "*****"); + } else if (isAbstract(mod)) { + if (!validAbstractMethod(mod)) { + int badflags = (mod & ~MM_A_METHOD); + env.error(pos, "warn.invalid.modifier.abst", toString(badflags, CF_Context.CTX_METHOD) + + " *****" + toString(mod, CF_Context.CTX_METHOD) + "*****"); + } + } else { + if (!validMethod(mod)) { + env.error(pos, "warn.invalid.modifier.mth", + toString(mod & ~MM_METHOD, CF_Context.CTX_METHOD)); + } + } + if (!validAccess(mod)) { + env.error(pos, "warn.invalid.modifier.acc"); + } + } + } + } + + /** + * Check the modifier flags for the inner-class + * + * @param cd The ClassData for the current class + * @param mod The modifier flags being checked + * @param pos the position of the parser in the file + */ + public static void checkInnerClassModifiers(ClassData cd, int mod, int pos) { + Environment env = cd.env; + + if (!validInnerClass(mod)) { + int badflags = (mod & ~MM_INNERCLASS); + env.error(pos, "warn.invalid.modifier.innerclass", + toString(badflags, CF_Context.CTX_INNERCLASS) + + " *****" + toString(mod, CF_Context.CTX_INNERCLASS) + "*****"); + } + + } + + private static StringBuffer _accessString(int mod, CF_Context context) { + StringBuffer sb = new StringBuffer(); + if (context == CF_Context.CTX_CLASS && isModule(mod)) { + sb.append(Token.MODULE.parseKey() + " "); + } + if (isPublic(mod)) { + sb.append(Token.PUBLIC.parseKey() + " "); + } + if (isPrivate(mod)) { + sb.append(Token.PRIVATE.parseKey() + " "); + } + if (isProtected(mod)) { + sb.append(Token.PROTECTED.parseKey() + " "); + } + if (isStatic(mod)) { + sb.append(Token.STATIC.parseKey() + " "); + } + if (context == CF_Context.CTX_METHOD && isFinal(mod)) { + sb.append(Token.FINAL.parseKey() + " "); + } + if (context == CF_Context.CTX_FIELD && isTransient(mod)) { + sb.append(Token.TRANSIENT.parseKey() + " "); + } + if (context == CF_Context.CTX_CLASS && isSuper(mod)) { + sb.append(Token.SUPER.parseKey() + " "); + } + if (context == CF_Context.CTX_METHOD && isSynchronized(mod)) { + sb.append(Token.SYNCHRONIZED.parseKey() + " "); + } + if (context == CF_Context.CTX_METHOD) { + if (isBridge(mod)) { + sb.append(Token.BRIDGE.parseKey() + " "); + } + if (isVarArgs(mod)) { + sb.append(Token.VARARGS.parseKey() + " "); + } + if (isNative(mod)) { + sb.append(Token.NATIVE.parseKey() + " "); + } + } + if (isAbstract(mod)) { + if ((context != CF_Context.CTX_CLASS) || !isInterface(mod)) { + sb.append(Token.ABSTRACT.parseKey() + " "); + } + } + if ( context.isOneOf(CF_Context.CTX_CLASS, CF_Context.CTX_INNERCLASS, CF_Context.CTX_FIELD) && isFinal(mod)) { + sb.append(Token.FINAL.parseKey() + " "); + } + if (context.isOneOf(CF_Context.CTX_CLASS, CF_Context.CTX_INNERCLASS) && isInterface(mod)) { + if (isAnnotation(mod)) { + sb.append(Token.ANNOTATION_ACCESS.parseKey() + " "); + } + sb.append(Token.INTERFACE.parseKey() + " "); + } + if (isStrict(mod)) { + sb.append(Token.STRICT.parseKey() + " "); + } + if (isSynthetic(mod)) { + sb.append(Token.SYNTHETIC.parseKey() + " "); + } + if (context == CF_Context.CTX_FIELD && isVolatile(mod)) { + sb.append(Token.VOLATILE.parseKey() + " "); + } + if (isEnum(mod)) { + sb.append(Token.ENUM.parseKey() + " "); + } + if (context.isOneOf(CF_Context.CTX_METHOD, CF_Context.CTX_FIELD) && isMandated(mod)) { + sb.append(Token.MANDATED.parseKey() + " "); + } + + return sb; + } + + public static String toString(int mod, CF_Context context) { + StringBuffer sb = _accessString(mod, context); + + if (isSyntheticPseudoMod(mod)) { + sb.append("Synthetic(Pseudo) "); + } + if (isDeprecatedPseudoMod(mod)) { + sb.append("Deprecated(Pseudo) "); + } + + return sb.toString().trim(); + } + + public static String moduleFlags( int flags ) { + return ""; + } + + public static String accessString(int mod, CF_Context context) { + return (context == CF_Context.CTX_MODULE) ? + moduleFlags(mod) : + _accessString(mod, context).toString(); + } + +} diff --git a/src/org/openjdk/asmtools/jasm/ModuleAttr.java b/src/org/openjdk/asmtools/jasm/ModuleAttr.java new file mode 100644 index 0000000..0dec054 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ModuleAttr.java @@ -0,0 +1,303 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.common.Module; + +import java.io.IOException; +import java.util.*; +import java.util.function.BiConsumer; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * The module attribute + */ +class ModuleAttr extends AttrData { + // shared data + private Module.Builder builder; + private final ClassData clsData; + private final Function findCellAsciz; + private final Function findCellClassByName; + private final Function findCellModuleByName; + private final Function findCellPackageByName; + + // entries to populate tables of the module attribute + BiConsumer requires = (mn, f) -> this.builder.require(mn, f); + BiConsumer> exports = (pn, ms) -> this.builder.exports(new Module.Exported(pn), ms); + BiConsumer> opens = (pn, ms) -> this.builder.opens(new Module.Opened(pn), ms); + BiConsumer> provides = (tn, ts) -> this.builder.provides(new Module.Provided(tn), ts); + Consumer> uses = (ts) -> this.builder.uses(ts); + + ModuleAttr(ClassData cdata) { + super(cdata, Tables.AttrTag.ATT_Module.parsekey()); + builder = new Module.Builder(); + clsData = cdata; + findCellAsciz = (name) -> clsData.pool.FindCellAsciz(name); + findCellClassByName = (name) -> clsData.pool.FindCellClassByName(name); + findCellModuleByName = (name) -> clsData.pool.FindCellModuleByName(name); + findCellPackageByName = (name) -> clsData.pool.FindCellPackageByName(name); + } + + void openModule() { + builder.setModuleFlags(Module.Modifier.ACC_OPEN); + } + void setModuleName(String value) { builder.setModuleName(value);} + + ModuleAttr build() { + Module module = builder.build(); + Content.instance.header = new HeaderStruct(module.header, findCellModuleByName, findCellAsciz); + Content.instance.requiresStruct = new SetStruct<>(module.requires, findCellModuleByName, findCellAsciz); + Content.instance.exportsMapStruct = new MapStruct<>(module.exports, findCellPackageByName, findCellModuleByName ); + Content.instance.opensMapStruct = new MapStruct<>(module.opens,findCellPackageByName, findCellModuleByName ); + Content.instance.usesStruct = new SetStruct<>(module.uses, findCellClassByName, null); + Content.instance.providesMapStruct = new MapStruct<>(module.provides, findCellClassByName, findCellClassByName); + return this; + } + + @Override + public int attrLength() { + return Content.instance.getLength(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + Content.instance.write(out); + } + + private enum Content implements Data { + instance { + @Override + public int getLength() { + return header.getLength() + + requiresStruct.getLength() + + exportsMapStruct.getLength() + + opensMapStruct.getLength() + + usesStruct.getLength() + + providesMapStruct.getLength(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + // keep order! + header.write(out); + requiresStruct.write(out); + exportsMapStruct.write(out); + opensMapStruct.write(out); + usesStruct.write(out); + providesMapStruct.write(out); + } + }; + + HeaderStruct header ; + SetStruct requiresStruct; + MapStruct exportsMapStruct; + MapStruct opensMapStruct; + SetStruct usesStruct; + MapStruct providesMapStruct; + } + + /** + * u2 {exports|opens}_count; + * { u2 {exports|opens}_index; + * u2 {exports|opens}_flags; + * u2 {exports|opens}_to_count; + * u2 {exports|opens}_to_index[{exports|opens}_to_count]; + * } {exports|opens}[{exports|opens}_count]; + * or + * u2 provides_count; + * { u2 provides_index; + * u2 provides_with_count; + * u2 provides_with_index[provides_with_count]; + * } provides[provides_count]; + */ + private class MapStruct implements Data { + final List>> exportsOpensList = new ArrayList<>(); + final List>> providesList = new ArrayList<>(); + + MapStruct(Map> source, + Function nameFinder, + Function targetFinder) { + Objects.requireNonNull(source); + source.entrySet().stream() + .sorted(Map.Entry.comparingByKey()) + .forEach(e -> { + ArrayList to = new ArrayList<>(); + e.getValue().forEach(mn -> to.add(targetFinder.apply(mn))); + if (e.getKey().isFlagged()) { + exportsOpensList.add(new Triplet<> + ( nameFinder.apply(e.getKey().getTypeName()), + ((Module.FlaggedTargetType) e.getKey()).getFlags(), + to)); + } else { + providesList.add(new Pair<>(nameFinder.apply(e.getKey().getTypeName()), + to)); + } + } + ); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + if (providesList.isEmpty()) { + out.writeShort(exportsOpensList.size()); // u2 {exports|opens}_count; + for (Triplet> triplet : exportsOpensList) { + out.writeShort(triplet.first.arg); // { u2 {exports|opens}_index; + out.writeShort(triplet.second); // u2 {exports|opens}_flags; + out.writeShort(triplet.third.size()); // u2 {exports|opens}_to_count; + for (ConstantPool.ConstCell to : triplet.third) + out.writeShort(to.arg); // u2 {exports|opens}_to_index[{exports|opens}_to_count]; } + } + } else { + out.writeShort(providesList.size()); // u2 provides_count; + for (Pair> pair : providesList) { + out.writeShort(pair.first.arg); // { u2 provides_index; + out.writeShort(pair.second.size()); // u2 provides_with_count; + for (ConstantPool.ConstCell to : pair.second) + out.writeShort(to.arg); // u2 provides_with_index[provides_with_count]; } + } + } + } + + @Override + public int getLength() { + if (providesList.isEmpty()) { + // (u2:{exports|opens}_count) + (u2:{exports|opens}_index + u2:{exports|opens}_flags u2:{exports|opens}_to_count) * {exports|opens}_count + + return 2 + 6 * exportsOpensList.size() + + // (u2:{exports|opens}_to_index) * {exports|opens}_to_count + exportsOpensList.stream().mapToInt(p -> p.third.size()).filter(s -> s > 0).sum() * 2; + } else { + // (u2 : provides_count) + (u2:provides_index + u2:provides_with_count) * provides_count + + return 2 + 4 * providesList.size() + + // (u2:provides_with_index) * provides_with_count + providesList.stream().mapToInt(p -> p.second.size()).filter(s -> s > 0).sum() * 2; + } + } + } + + private class HeaderStruct implements Data { + final ConstantPool.ConstCell index; + final int flags; + final ConstantPool.ConstCell versionIndex; + + HeaderStruct(Module.Header source, + Function nameFinder, + Function versionFinder) { + index = nameFinder.apply(source.getModuleName()); + versionIndex = (source.getModuleVersion() == null ) ? null : versionFinder.apply(source.getModuleVersion()); + flags = source.getModuleFlags(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(index.arg); // u2 module_name_index; + out.writeShort(flags); // u2 module_flags; + out.writeShort(versionIndex == null ? 0 : versionIndex.arg); // u2 module_version_index; + } + + @Override + public int getLength() { + // u2:module_name_index) + u2:module_flags +u2:module_version_index + return 6; + } + } + + /** + * u2 uses_count; + * u2 uses_index[uses_count]; + * or + * u2 requires_count; + * { u2 requires_index; + * u2 requires_flags; + * u2 requires_version_index; + * } requires[requires_count]; + */ + private class SetStruct implements Data { + final List usesList = new ArrayList<>(); + final List> requiresList = new ArrayList<>(); + + SetStruct(Set source, + Function nameFinder, + Function versionFinder) { + Objects.requireNonNull(source); + source.forEach(e -> { + if (e.isFlagged()) { + requiresList.add(new Triplet<>( + nameFinder.apply(e.getTypeName()), + ((Module.FlaggedTargetType) e).getFlags(), + (((Module.VersionedFlaggedTargetType) e).getVersion() == null) ? + null : + versionFinder.apply(((Module.VersionedFlaggedTargetType) e).getVersion()))); + } else { + usesList.add(nameFinder.apply((e.getTypeName()))); + } + }); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + if (usesList.isEmpty()) { + out.writeShort(requiresList.size()); // u2 requires_count; + for (Triplet r : requiresList) { + out.writeShort(r.first.arg); // u2 requires_index; + out.writeShort(r.second); // u2 requires_flags; + out.writeShort(r.third == null ? 0 : r.third.arg); // u2 requires_version_index; + } + } else { + out.writeShort(usesList.size()); // u2 uses_count; + for (ConstantPool.ConstCell u : usesList) + out.writeShort(u.arg); // u2 uses_index[uses_count]; + } + } + + @Override + public int getLength() { + return usesList.isEmpty() ? + // (u2:requires_count) + (u2:requires_index + u2:requires_flags + u2:requires_version_index) * requires_count + 2 + 6 * requiresList.size() : + // (u2:uses_count) + (u2:uses_index) * uses_count + 2 + 2 * usesList.size(); + } + } + + // Helper classes + private class Pair { + final F first; + final S second; + + Pair(F first, S second) { + this.first = first; + this.second = second; + } + } + + public class Triplet extends Pair { + private final T third; + Triplet(F first, S second, T third) { + super(first,second); + this.third = third; + } + } + +} diff --git a/src/org/openjdk/asmtools/jasm/NestMembersAttr.java b/src/org/openjdk/asmtools/jasm/NestMembersAttr.java new file mode 100644 index 0000000..143c743 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/NestMembersAttr.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.util.List; + +/** + * The "classes[]" data of attributes + * JEP 181 (Nest-based Access Control): class file 55.0 + * NestMembers_attribute { + * u2 attribute_name_index; + * u4 attribute_length; + * u2 number_of_classes; + * u2 classes[number_of_classes]; + * } + */ +public class NestMembersAttr extends ClassArrayAttr { + public NestMembersAttr(ClassData cdata, List classes) { + super(Tables.AttrTag.ATT_NestMembers.parsekey(), cdata, classes); + } +} diff --git a/src/org/openjdk/asmtools/jasm/OpcodeTables.java b/src/org/openjdk/asmtools/jasm/OpcodeTables.java new file mode 100644 index 0000000..c7cc522 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/OpcodeTables.java @@ -0,0 +1,567 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.util.HashMap; + +/** + * + * OpcodeTables + * + * The OpcodeTables class follows a Singleton Pattern. This class contains Enums, that are + * contained in private hash maps (lookup tables and reverse lookup tables). These hash + * maps all have public accessors, which clients use to look-up opcodes. + * + * Tokens in this table carry no external state, and are typically treated as constants. + * They do not need to be reset. + * + */ +public class OpcodeTables { + + /** + * Initialized keyword and token Hash Maps (and Reverse Tables) + */ + static private final int MaxOpcodes = 301; + static private HashMap IntToNormalOpcodes = new HashMap<>(MaxOpcodes); + static private HashMap IntToAllOpcodes = new HashMap<>(MaxOpcodes); + static private HashMap mnemocodes = new HashMap<>(MaxOpcodes); + + static private HashMap IntToPrivOpcode = new HashMap<>(MaxOpcodes); + static private HashMap PrivMnemocodes = new HashMap<>(MaxOpcodes); + + static private HashMap IntToNonPrivOpcode = new HashMap<>(MaxOpcodes); + static private HashMap NonPrivMnemocodes = new HashMap<>(MaxOpcodes); + + static { + // register all of the tokens + for (Opcode opc : Opcode.values()) { + registerOpcode(opc); + } + + } + + private static void registerOpcode(Opcode opc) { + IntToAllOpcodes.put(opc.value, opc); + mnemocodes.put(opc.parsekey, opc); + if (opc.alias != null) { + mnemocodes.put(opc.alias, opc); + } + + if (opc.type == OpcodeType.PRIVELEGED) { + PrivMnemocodes.put(opc.parsekey, opc); + IntToPrivOpcode.put(opc.baseVal, opc); + } else if (opc.type == OpcodeType.NONPRIVELEGED) { + NonPrivMnemocodes.put(opc.parsekey, opc); + IntToNonPrivOpcode.put(opc.baseVal, opc); + } + + } + + public static Opcode opcode(String mnemonic) { + return mnemocodes.get(mnemonic); + } + + public static Opcode opcode(Integer mnem_code) { + return IntToAllOpcodes.get(mnem_code); + } + + /*-------------------------------------------------------- */ + /** + * Marker: describes the type of Opcode. + * + * certain types of Opcodes will be added to specific lookup tables. + */ + static public enum OpcodeType { + NORMAL (0, "Normal"), + NONPRIVELEGED (1, "NonPriv"), + PRIVELEGED (2, "Priv"), + WIDE (3, "Wide"); + + private final Integer value; + private final String printval; + + OpcodeType(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + + } + + /*-------------------------------------------------------- */ + /* Opcode Enums */ + static public enum Opcode { + /* Opcodes */ + opc_dead (-2, " opc_dead", 0), + opc_label (-1, "opc_label", 0), + opc_nop (0, "nop", 1), + opc_aconst_null (1, "aconst_null", 1), + opc_iconst_m1 (2, "iconst_m1", 1), + opc_iconst_0 (3, "iconst_0", 1), + opc_iconst_1 (4, "iconst_1", 1), + opc_iconst_2 (5, "iconst_2", 1), + opc_iconst_3 (6, "iconst_3", 1), + opc_iconst_4 (7, "iconst_4", 1), + opc_iconst_5 (8, "iconst_5", 1), + opc_lconst_0 (9, "lconst_0", 1), + opc_lconst_1 (10, "lconst_1", 1), + opc_fconst_0 (11, "fconst_0", 1), + opc_fconst_1 (12, "fconst_1", 1), + opc_fconst_2 (13, "fconst_2", 1), + opc_dconst_0 (14, "dconst_0", 1), + opc_dconst_1 (15, "dconst_1", 1), + opc_bipush (16, "bipush", 2), + opc_sipush (17, "sipush", 3), + opc_ldc (18, "ldc", 2), + opc_ldc_w (19, "ldc_w", 3), + opc_ldc2_w (20, "ldc2_w", 3), + opc_iload (21, "iload", 2), + opc_lload (22, "lload", 2), + opc_fload (23, "fload", 2), + opc_dload (24, "dload", 2), + opc_aload (25, "aload", 2), + opc_iload_0 (26, "iload_0", 1), + opc_iload_1 (27, "iload_1", 1), + opc_iload_2 (28, "iload_2", 1), + opc_iload_3 (29, "iload_3", 1), + opc_lload_0 (30, "lload_0", 1), + opc_lload_1 (31, "lload_1", 1), + opc_lload_2 (32, "lload_2", 1), + opc_lload_3 (33, "lload_3", 1), + opc_fload_0 (34, "fload_0", 1), + opc_fload_1 (35, "fload_1", 1), + opc_fload_2 (36, "fload_2", 1), + opc_fload_3 (37, "fload_3", 1), + opc_dload_0 (38, "dload_0", 1), + opc_dload_1 (39, "dload_1", 1), + opc_dload_2 (40, "dload_2", 1), + opc_dload_3 (41, "dload_3", 1), + opc_aload_0 (42, "aload_0", 1), + opc_aload_1 (43, "aload_1", 1), + opc_aload_2 (44, "aload_2", 1), + opc_aload_3 (45, "aload_3", 1), + opc_iaload (46, "iaload", 1), + opc_laload (47, "laload", 1), + opc_faload (48, "faload", 1), + opc_daload (49, "daload", 1), + opc_aaload (50, "aaload", 1), + opc_baload (51, "baload", 1), + opc_caload (52, "caload", 1), + opc_saload (53, "saload", 1), + opc_istore (54, "istore", 2), + opc_lstore (55, "lstore", 2), + opc_fstore (56, "fstore", 2), + opc_dstore (57, "dstore", 2), + opc_astore (58, "astore", 2), + opc_istore_0 (59, "istore_0", 1), + opc_istore_1 (60, "istore_1", 1), + opc_istore_2 (61, "istore_2", 1), + opc_istore_3 (62, "istore_3", 1), + opc_lstore_0 (63, "lstore_0", 1), + opc_lstore_1 (64, "lstore_1", 1), + opc_lstore_2 (65, "lstore_2", 1), + opc_lstore_3 (66, "lstore_3", 1), + opc_fstore_0 (67, "fstore_0", 1), + opc_fstore_1 (68, "fstore_1", 1), + opc_fstore_2 (69, "fstore_2", 1), + opc_fstore_3 (70, "fstore_3", 1), + opc_dstore_0 (71, "dstore_0", 1), + opc_dstore_1 (72, "dstore_1", 1), + opc_dstore_2 (73, "dstore_2", 1), + opc_dstore_3 (74, "dstore_3", 1), + opc_astore_0 (75, "astore_0", 1), + opc_astore_1 (76, "astore_1", 1), + opc_astore_2 (77, "astore_2", 1), + opc_astore_3 (78, "astore_3", 1), + opc_iastore (79, "iastore", 1), + opc_lastore (80, "lastore", 1), + opc_fastore (81, "fastore", 1), + opc_dastore (82, "dastore", 1), + opc_aastore (83, "aastore", 1), + opc_bastore (84, "bastore", 1), + opc_castore (85, "castore", 1), + opc_sastore (86, "sastore", 1), + opc_pop (87, "pop", 1), + opc_pop2 (88, "pop2", 1), + opc_dup (89, "dup", 1), + opc_dup_x1 (90, "dup_x1", 1), + opc_dup_x2 (91, "dup_x2", 1), + opc_dup2 (92, "dup2", 1), + opc_dup2_x1 (93, "dup2_x1", 1), + opc_dup2_x2 (94, "dup2_x2", 1), + opc_swap (95, "swap", 1), + opc_iadd (96, "iadd", 1), + opc_ladd (97, "ladd", 1), + opc_fadd (98, "fadd", 1), + opc_dadd (99, "dadd", 1), + opc_isub (100, "isub", 1), + opc_lsub (101, "lsub", 1), + opc_fsub (102, "fsub", 1), + opc_dsub (103, "dsub", 1), + opc_imul (104, "imul", 1), + opc_lmul (105, "lmul", 1), + opc_fmul (106, "fmul", 1), + opc_dmul (107, "dmul", 1), + opc_idiv (108, "idiv", 1), + opc_ldiv (109, "ldiv", 1), + opc_fdiv (110, "fdiv", 1), + opc_ddiv (111, "ddiv", 1), + opc_irem (112, "irem", 1), + opc_lrem (113, "lrem", 1), + opc_frem (114, "frem", 1), + opc_drem (115, "drem", 1), + opc_ineg (116, "ineg", 1), + opc_lneg (117, "lneg", 1), + opc_fneg (118, "fneg", 1), + opc_dneg (119, "dneg", 1), + opc_ishl (120, "ishl", 1), + opc_lshl (121, "lshl", 1), + opc_ishr (122, "ishr", 1), + opc_lshr (123, "lshr", 1), + opc_iushr (124, "iushr", 1), + opc_lushr (125, "lushr", 1), + opc_iand (126, "iand", 1), + opc_land (127, "land", 1), + opc_ior (128, "ior", 1), + opc_lor (129, "lor", 1), + opc_ixor (130, "ixor", 1), + opc_lxor (131, "lxor", 1), + opc_iinc (132, "iinc", 3), + opc_i2l (133, "i2l", 1), + opc_i2f (134, "i2f", 1), + opc_i2d (135, "i2d", 1), + opc_l2i (136, "l2i", 1), + opc_l2f (137, "l2f", 1), + opc_l2d (138, "l2d", 1), + opc_f2i (139, "f2i", 1), + opc_f2l (140, "f2l", 1), + opc_f2d (141, "f2d", 1), + opc_d2i (142, "d2i", 1), + opc_d2l (143, "d2l", 1), + opc_d2f (144, "d2f", 1), + opc_i2b (145, "i2b", 1), + opc_i2c (146, "i2c", 1), + opc_i2s (147, "i2s", 1), + opc_lcmp (148, "lcmp", 1), + opc_fcmpl (149, "fcmpl", 1), + opc_fcmpg (150, "fcmpg", 1), + opc_dcmpl (151, "dcmpl", 1), + opc_dcmpg (152, "dcmpg", 1), + opc_ifeq (153, "ifeq", 3), + opc_ifne (154, "ifne", 3), + opc_iflt (155, "iflt", 3), + opc_ifge (156, "ifge", 3), + opc_ifgt (157, "ifgt", 3), + opc_ifle (158, "ifle", 3), + opc_if_icmpeq (159, "if_icmpeq", 3), + opc_if_icmpne (160, "if_icmpne", 3), + opc_if_icmplt (161, "if_icmplt", 3), + opc_if_icmpge (162, "if_icmpge", 3), + opc_if_icmpgt (163, "if_icmpgt", 3), + opc_if_icmple (164, "if_icmple", 3), + opc_if_acmpeq (165, "if_acmpeq", 3), + opc_if_acmpne (166, "if_acmpne", 3), + opc_goto (167, "goto", 3), + opc_jsr (168, "jsr", 3), + opc_ret (169, "ret", 2), + opc_tableswitch (170, "tableswitch", 99), + opc_lookupswitch (171, "lookupswitch", 99), + opc_ireturn (172, "ireturn", 1), + opc_lreturn (173, "lreturn", 1), + opc_freturn (174, "freturn", 1), + opc_dreturn (175, "dreturn", 1), + opc_areturn (176, "areturn", 1), + opc_return (177, "return", 1), + opc_getstatic (178, "getstatic", 3), + opc_putstatic (179, "putstatic", 3), + opc_getfield (180, "getfield", 3), + opc_putfield (181, "putfield", 3), + opc_invokevirtual (182, "invokevirtual", 3), + opc_invokespecial (183, "invokespecial", "invokenonvirtual", 3), + opc_invokestatic (184, "invokestatic", 3), + opc_invokeinterface (185, "invokeinterface", 5), + opc_invokedynamic (186, "invokedynamic", 5), + opc_new (187, "new", 3), + opc_newarray (188, "newarray", 2), + opc_anewarray (189, "anewarray", 3), + opc_arraylength (190, "arraylength", 1), + opc_athrow (191, "athrow", 1), + opc_checkcast (192, "checkcast", 3), + opc_instanceof (193, "instanceof", 3), + opc_monitorenter (194, "monitorenter", 1), + opc_monitorexit (195, "monitorexit", 1), + + // Wide Marker (not really an opcode) + opc_wide (196, null, 0), + opc_multianewarray (197, "multianewarray", 4), + opc_ifnull (198, "ifnull", 3), + opc_ifnonnull (199, "ifnonnull", 3), + opc_goto_w (200, "goto_w", 5), + opc_jsr_w (201, "jsr_w", 5), +// opc_bytecode 202 (202, "bytecode 202", 1), + /* Pseudo-instructions */ + opc_bytecode (203, "bytecode", 1), + opc_try (204, "try", 0), + opc_endtry (205, "endtry", 0), + opc_catch (206, "catch", 0), + opc_var (207, "var", 0), + opc_endvar (208, "endvar", 0), + opc_locals_map (209, "locals_map", 0), + opc_stack_map (210, "stack_map", 0), + opc_stack_frame_type (211, "stack_frame_type", 0), + + + // Priv/NonPriv Marker (not really an opcode) + opc_nonpriv (254, "priv", 0), + opc_priv (255, "nonpriv", 0), + + + /* Wide instructions */ + opc_iload_w (opc_iload.value, "iload_w", 4, OpcodeType.WIDE), + opc_lload_w (opc_lload.value, "lload_w", 4, OpcodeType.WIDE), + opc_fload_w (opc_fload.value, "fload_w", 4, OpcodeType.WIDE), + opc_dload_w (opc_dload.value, "dload_w", 4, OpcodeType.WIDE), + opc_aload_w (opc_aload.value, "aload_w", 4, OpcodeType.WIDE), + opc_istore_w (opc_istore.value, "istore_w", 4, OpcodeType.WIDE), + opc_lstore_w (opc_lstore.value, "lstore_w", 4, OpcodeType.WIDE), + opc_fstore_w (opc_fstore.value, "fstore_w", 4, OpcodeType.WIDE), + opc_dstore_w (opc_dstore.value, "dstore_w", 4, OpcodeType.WIDE), + opc_astore_w (opc_astore.value, "astore_w", 4, OpcodeType.WIDE), + opc_ret_w (opc_ret.value, "ret_w", 4, OpcodeType.WIDE), + opc_iinc_w (opc_iinc.value, "iinc_w", 6, OpcodeType.WIDE), + + + /* Priveleged instructions */ + opc_load_ubyte (0, "load_ubyte", OpcodeType.NONPRIVELEGED), + opc_priv_load_ubyte (0, "priv_load_ubyte", OpcodeType.PRIVELEGED), + opc_load_byte (1, "load_byte", OpcodeType.NONPRIVELEGED), + opc_priv_load_byte (1, "priv_load_byte", OpcodeType.PRIVELEGED), + opc_load_char (2, "load_char", OpcodeType.NONPRIVELEGED), + opc_priv_load_char (2, "priv_load_char", OpcodeType.PRIVELEGED), + opc_load_short (3, "load_short", OpcodeType.NONPRIVELEGED), + opc_priv_load_short (3, "priv_load_short", OpcodeType.PRIVELEGED), + opc_load_word (4, "load_word", OpcodeType.NONPRIVELEGED), + opc_priv_load_word (4, "priv_load_word", OpcodeType.PRIVELEGED), + opc_load_char_oe (10, "load_char_oe", OpcodeType.NONPRIVELEGED), + opc_priv_load_char_oe (10, "priv_load_char_oe", OpcodeType.PRIVELEGED), + opc_load_short_oe (11, "load_short_oe", OpcodeType.NONPRIVELEGED), + opc_priv_load_short_oe (11, "priv_load_short_oe", OpcodeType.PRIVELEGED), + opc_load_word_oe (12, "load_word_oe", OpcodeType.NONPRIVELEGED), + opc_priv_load_word_oe (12, "priv_load_word_oe", OpcodeType.PRIVELEGED), + opc_ncload_ubyte (16, "ncload_ubyte", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_ubyte (16, "priv_ncload_ubyte", OpcodeType.PRIVELEGED), + opc_ncload_byte (17, "ncload_byte", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_byte (17, "priv_ncload_byte", OpcodeType.PRIVELEGED), + opc_ncload_char (18, "ncload_char", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_char (18, "priv_ncload_char", OpcodeType.PRIVELEGED), + opc_ncload_short (19, "ncload_short", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_short (19, "priv_ncload_short", OpcodeType.PRIVELEGED), + opc_ncload_word (20, "ncload_word", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_word (20, "priv_ncload_word", OpcodeType.PRIVELEGED), + opc_ncload_char_oe (26, "ncload_char_oe", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_char_oe (26, "priv_ncload_char_oe", OpcodeType.PRIVELEGED), + opc_ncload_short_oe (27, "ncload_short_oe", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_short_oe (27, "priv_ncload_short_oe", OpcodeType.PRIVELEGED), + opc_ncload_word_oe (28, "ncload_word_oe", OpcodeType.NONPRIVELEGED), + opc_priv_ncload_word_oe (28, "priv_ncload_word_oe", OpcodeType.PRIVELEGED), + opc_cache_flush (30, "cache_flush", OpcodeType.NONPRIVELEGED), + opc_priv_cache_flush (30, "priv_cache_flush", OpcodeType.PRIVELEGED), + opc_store_byte (32, "store_byte", OpcodeType.NONPRIVELEGED), + opc_priv_store_byte (32, "priv_store_byte", OpcodeType.PRIVELEGED), + opc_store_short (34, "store_short", OpcodeType.NONPRIVELEGED), + opc_priv_store_short (34, "priv_store_short", OpcodeType.PRIVELEGED), + opc_store_word (36, "store_word", OpcodeType.NONPRIVELEGED), + opc_priv_store_word (36, "priv_store_word", OpcodeType.PRIVELEGED), + opc_store_short_oe (42, "store_short_oe", OpcodeType.NONPRIVELEGED), + opc_priv_store_short_oe (42, "priv_store_short_oe", OpcodeType.PRIVELEGED), + opc_store_word_oe (44, "store_word_oe", OpcodeType.NONPRIVELEGED), + opc_priv_store_word_oe (44, "priv_store_word_oe", OpcodeType.PRIVELEGED), + opc_ncstore_byte (48, "ncstore_byte", OpcodeType.NONPRIVELEGED), + opc_priv_ncstore_byte (48, "priv_ncstore_byte", OpcodeType.PRIVELEGED), + opc_ncstore_short (50, "ncstore_short", OpcodeType.NONPRIVELEGED), + opc_priv_ncstore_short (50, "priv_ncstore_short", OpcodeType.PRIVELEGED), + opc_ncstore_word (52, "ncstore_word", OpcodeType.NONPRIVELEGED), + opc_priv_ncstore_word (52, "priv_ncstore_word", OpcodeType.PRIVELEGED), + opc_ncstore_short_oe (58, "ncstore_short_oe", OpcodeType.NONPRIVELEGED), + opc_priv_ncstore_short_oe (58, "priv_ncstore_short_oe", OpcodeType.PRIVELEGED), + opc_ncstore_word_oe (60, "ncstore_word_oe", OpcodeType.NONPRIVELEGED), + opc_priv_ncstore_word_oe (60, "priv_ncstore_word_oe", OpcodeType.PRIVELEGED), + opc_zero_line (62, "zero_line", OpcodeType.NONPRIVELEGED), + opc_priv_zero_line (62, "priv_zero_line", OpcodeType.PRIVELEGED), + opc_ret_from_sub (5, "ret_from_sub", OpcodeType.NONPRIVELEGED), + opc_enter_sync_method (63, "enter_sync_method", OpcodeType.NONPRIVELEGED), + opc_priv_ret_from_trap (5, "priv_ret_from_trap", OpcodeType.PRIVELEGED), + opc_priv_read_dcache_tag (6, "priv_read_dcache_tag", OpcodeType.PRIVELEGED), + opc_priv_read_dcache_data (7, "priv_read_dcache_data", OpcodeType.PRIVELEGED), + opc_priv_read_icache_tag (14, "priv_read_icache_tag", OpcodeType.PRIVELEGED), + opc_priv_read_icache_data (15, "priv_read_icache_data", OpcodeType.PRIVELEGED), + opc_priv_powerdown (22, "priv_powerdown", OpcodeType.PRIVELEGED), + opc_priv_read_scache_data (23, "priv_read_scache_data", OpcodeType.PRIVELEGED), + opc_priv_cache_index_flush (31, "priv_cache_index_flush", OpcodeType.PRIVELEGED), + opc_priv_write_dcache_tag (38, "priv_write_dcache_tag", OpcodeType.PRIVELEGED), + opc_priv_write_dcache_data (39, "priv_write_dcache_data", OpcodeType.PRIVELEGED), + opc_priv_write_icache_tag (46, "priv_write_icache_tag", OpcodeType.PRIVELEGED), + opc_priv_write_icache_data (47, "priv_write_icache_data", OpcodeType.PRIVELEGED), + opc_priv_reset (54, "priv_reset", OpcodeType.PRIVELEGED), + opc_priv_write_scache_data (55, "priv_write_scache_data", OpcodeType.PRIVELEGED), + opc_priv_read_reg_0 (64, "priv_read_reg_0", OpcodeType.PRIVELEGED), + opc_priv_read_reg_1 (65, "priv_read_reg_1", OpcodeType.PRIVELEGED), + opc_priv_read_reg_2 (66, "priv_read_reg_2", OpcodeType.PRIVELEGED), + opc_priv_read_reg_3 (67, "priv_read_reg_3", OpcodeType.PRIVELEGED), + opc_priv_read_reg_4 (68, "priv_read_reg_4", OpcodeType.PRIVELEGED), + opc_priv_read_reg_5 (69, "priv_read_reg_5", OpcodeType.PRIVELEGED), + opc_priv_read_reg_6 (70, "priv_read_reg_6", OpcodeType.PRIVELEGED), + opc_priv_read_reg_7 (71, "priv_read_reg_7", OpcodeType.PRIVELEGED), + opc_priv_read_reg_8 (72, "priv_read_reg_8", OpcodeType.PRIVELEGED), + opc_priv_read_reg_9 (73, "priv_read_reg_9", OpcodeType.PRIVELEGED), + opc_priv_read_reg_10 (74, "priv_read_reg_10", OpcodeType.PRIVELEGED), + opc_priv_read_reg_11 (75, "priv_read_reg_11", OpcodeType.PRIVELEGED), + opc_priv_read_reg_12 (76, "priv_read_reg_12", OpcodeType.PRIVELEGED), + opc_priv_read_reg_13 (77, "priv_read_reg_13", OpcodeType.PRIVELEGED), + opc_priv_read_reg_14 (78, "priv_read_reg_14", OpcodeType.PRIVELEGED), + opc_priv_read_reg_15 (79, "priv_read_reg_15", OpcodeType.PRIVELEGED), + opc_priv_read_reg_16 (80, "priv_read_reg_16", OpcodeType.PRIVELEGED), + opc_priv_read_reg_17 (81, "priv_read_reg_17", OpcodeType.PRIVELEGED), + opc_priv_read_reg_18 (82, "priv_read_reg_18", OpcodeType.PRIVELEGED), + opc_priv_read_reg_19 (83, "priv_read_reg_19", OpcodeType.PRIVELEGED), + opc_priv_read_reg_20 (84, "priv_read_reg_20", OpcodeType.PRIVELEGED), + opc_priv_read_reg_21 (85, "priv_read_reg_21", OpcodeType.PRIVELEGED), + opc_priv_read_reg_22 (86, "priv_read_reg_22", OpcodeType.PRIVELEGED), + opc_priv_read_reg_23 (87, "priv_read_reg_23", OpcodeType.PRIVELEGED), + opc_priv_read_reg_24 (88, "priv_read_reg_24", OpcodeType.PRIVELEGED), + opc_priv_read_reg_25 (89, "priv_read_reg_25", OpcodeType.PRIVELEGED), + opc_priv_read_reg_26 (90, "priv_read_reg_26", OpcodeType.PRIVELEGED), + opc_priv_read_reg_27 (91, "priv_read_reg_27", OpcodeType.PRIVELEGED), + opc_priv_read_reg_28 (92, "priv_read_reg_28", OpcodeType.PRIVELEGED), + opc_priv_read_reg_29 (93, "priv_read_reg_29", OpcodeType.PRIVELEGED), + opc_priv_read_reg_30 (94, "priv_read_reg_30", OpcodeType.PRIVELEGED), + opc_priv_read_reg_31 (95, "priv_read_reg_31", OpcodeType.PRIVELEGED), + opc_priv_write_reg_0 (96, "priv_write_reg_0", OpcodeType.PRIVELEGED), + opc_priv_write_reg_1 (97, "priv_write_reg_1", OpcodeType.PRIVELEGED), + opc_priv_write_reg_2 (98, "priv_write_reg_2", OpcodeType.PRIVELEGED), + opc_priv_write_reg_3 (99, "priv_write_reg_3", OpcodeType.PRIVELEGED), + opc_priv_write_reg_4 (100, "priv_write_reg_4", OpcodeType.PRIVELEGED), + opc_priv_write_reg_5 (101, "priv_write_reg_5", OpcodeType.PRIVELEGED), + opc_priv_write_reg_6 (102, "priv_write_reg_6", OpcodeType.PRIVELEGED), + opc_priv_write_reg_7 (103, "priv_write_reg_7", OpcodeType.PRIVELEGED), + opc_priv_write_reg_8 (104, "priv_write_reg_8", OpcodeType.PRIVELEGED), + opc_priv_write_reg_9 (105, "priv_write_reg_9", OpcodeType.PRIVELEGED), + opc_priv_write_reg_10 (106, "priv_write_reg_10", OpcodeType.PRIVELEGED), + opc_priv_write_reg_11 (107, "priv_write_reg_11", OpcodeType.PRIVELEGED), + opc_priv_write_reg_12 (108, "priv_write_reg_12", OpcodeType.PRIVELEGED), + opc_priv_write_reg_13 (109, "priv_write_reg_13", OpcodeType.PRIVELEGED), + opc_priv_write_reg_14 (110, "priv_write_reg_14", OpcodeType.PRIVELEGED), + opc_priv_write_reg_15 (111, "priv_write_reg_15", OpcodeType.PRIVELEGED), + opc_priv_write_reg_16 (112, "priv_write_reg_16", OpcodeType.PRIVELEGED), + opc_priv_write_reg_17 (113, "priv_write_reg_17", OpcodeType.PRIVELEGED), + opc_priv_write_reg_18 (114, "priv_write_reg_18", OpcodeType.PRIVELEGED), + opc_priv_write_reg_19 (115, "priv_write_reg_19", OpcodeType.PRIVELEGED), + opc_priv_write_reg_20 (116, "priv_write_reg_20", OpcodeType.PRIVELEGED), + opc_priv_write_reg_21 (117, "priv_write_reg_21", OpcodeType.PRIVELEGED), + opc_priv_write_reg_22 (118, "priv_write_reg_22", OpcodeType.PRIVELEGED), + opc_priv_write_reg_23 (119, "priv_write_reg_23", OpcodeType.PRIVELEGED), + opc_priv_write_reg_24 (120, "priv_write_reg_24", OpcodeType.PRIVELEGED), + opc_priv_write_reg_25 (121, "priv_write_reg_25", OpcodeType.PRIVELEGED), + opc_priv_write_reg_26 (122, "priv_write_reg_26", OpcodeType.PRIVELEGED), + opc_priv_write_reg_27 (123, "priv_write_reg_27", OpcodeType.PRIVELEGED), + opc_priv_write_reg_28 (124, "priv_write_reg_28", OpcodeType.PRIVELEGED), + opc_priv_write_reg_29 (125, "priv_write_reg_29", OpcodeType.PRIVELEGED), + opc_priv_write_reg_30 (126, "priv_write_reg_30", OpcodeType.PRIVELEGED), + opc_priv_write_reg_31 (127, "priv_write_reg_31", OpcodeType.PRIVELEGED); + + private Integer value; + private String parsekey; + private String alias; + private Integer length; + private Integer baseVal; + private OpcodeType type; + + Opcode(Integer val, String parse, OpcodeType tp) { + init(val, parse, null, 2, tp); + } + + Opcode(Integer val, String parse, int len, OpcodeType tp) { + init(val, parse, null, len, tp); + } + + Opcode(Integer val, String parse) { + init(val, parse, null, 2, OpcodeType.NORMAL); + } + + Opcode(Integer val, String parse, int len) { + init(val, parse, null, len, OpcodeType.NORMAL); + } + + Opcode(Integer val, String parse, String als, int len) { + init(val, parse, als, len, OpcodeType.NORMAL); + } + + Opcode(Integer val, String parse, String als, int len, OpcodeType tp) { + init(val, parse, als, len, tp); + } + + private void init(Integer val, String parse, String als, int len, OpcodeType tp) { + type = tp; + baseVal = null; + switch (tp) { + case NORMAL: + value = val; + break; + case WIDE: + value = (opc_wide.value << 8) | val; + break; + case PRIVELEGED: + value = (opc_priv.value * 0xFF) + val; + baseVal = val; + break; + case NONPRIVELEGED: + value = (opc_nonpriv.value * 0xFF) + val; + baseVal = val; + break; + } + parsekey = parse; + alias = als; + length = len; + } + + public Integer value() { + return value; + } + + public int length() { + return length; + } + + public String parsekey() { + return parsekey; + } + + public OpcodeType type() { + return type; + } + } + +} diff --git a/src/org/openjdk/asmtools/jasm/ParseBase.java b/src/org/openjdk/asmtools/jasm/ParseBase.java new file mode 100644 index 0000000..5e7dc96 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ParseBase.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +/** + * + */ +public class ParseBase { + + protected boolean debugFlag; + protected Scanner scanner; + protected Parser parser; + protected Environment env; + + public ParseBase() { + init(null, null, null); + } + + public void init(Scanner scnr, Parser prsr, Environment envr) { + debugFlag = false; + scanner = scnr; + parser = prsr; + env = envr; + } + + public void enableDebug(boolean debState) { + debugFlag = debState; + } + + protected void debugStr(String str) { + if (debugFlag) { + env.traceln(str); + } + } + + protected void debugScan(String str) { + if (debugFlag) { + scanner.debugScan(str); + } + } + +} diff --git a/src/org/openjdk/asmtools/jasm/Parser.java b/src/org/openjdk/asmtools/jasm/Parser.java new file mode 100644 index 0000000..5655437 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Parser.java @@ -0,0 +1,2028 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.common.Module; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Set; +import java.util.function.BiConsumer; +import java.util.function.Consumer; + +import static org.openjdk.asmtools.jasm.ConstantPool.*; +import static org.openjdk.asmtools.jasm.JasmTokens.Token; +import static org.openjdk.asmtools.jasm.JasmTokens.Token.COMMA; +import static org.openjdk.asmtools.jasm.JasmTokens.Token.SEMICOLON; +import static org.openjdk.asmtools.jasm.RuntimeConstants.*; +import static org.openjdk.asmtools.jasm.Tables.*; + +/** + * This class is used to parse Jasm statements and expressions. + * The result is a parse tree.

+ *

+ * This class implements an operator precedence parser. Errors are + * reported to the Environment object, if the error can't be + * resolved immediately, a SyntaxError exception is thrown.

+ *

+ * Error recovery is implemented by catching Scanner.SyntaxError exceptions + * and discarding input scanner.tokens until an input token is reached that + * is possibly a legal continuation.

+ *

+ * The parse tree that is constructed represents the input + * exactly (no rewrites to simpler forms). This is important + * if the resulting tree is to be used for code formatting in + * a programming environment. Currently only documentation comments + * are retained.

+ *

+ * A parser owns several components (scanner, constant-parser, + * instruction-parser, annotations-parser) to which it delegates certain + * parsing responsibilities. This parser contains functions to parse the + * overall form of a class, and any members (fields, methods, inner-classes). + *

+ *

+ * Syntax errors, should always be caught inside the + * parser for error recovery. + */ +class Parser extends ParseBase { + + /* Parser Fields */ + protected ConstantPool pool = null; + + ClassData cd = null; + + CodeAttr curCode; + + private ArrayList clsDataList = new ArrayList<>(); + private String pkg = null; + private String pkgPrefix = ""; + private ArrayList pkgAnnttns = null; + private ArrayList clsAnnttns = null; + private ArrayList memberAnnttns = null; + private boolean explicitcp = false; + private ModuleAttr moduleAttribute; + private CFVersion currentCFV; + /** + * other parser components + */ + private ParserAnnotation annotParser; // For parsing Annotations + private ParserCP cpParser; // for parsing Constants + private ParserInstr instrParser; // for parsing Instructions + + + /** + * Create a parser + */ + protected Parser(Environment sf, CFVersion cfVersion) throws IOException { + super.init(new Scanner(sf), this, sf); + this.currentCFV = cfVersion; + this.annotParser = new ParserAnnotation(scanner, this, env); + this.cpParser = new ParserCP(scanner, this, env); + this.instrParser = new ParserInstr(scanner, this, cpParser, env); + } + + void setDebugFlags(boolean debugScanner, boolean debugMembers, + boolean debugCP, boolean debugAnnot, boolean debugInstr) { + + enableDebug(debugMembers); + scanner.enableDebug(debugScanner); + cpParser.enableDebug(debugCP); + annotParser.enableDebug(debugAnnot); + instrParser.enableDebug(debugInstr); + } + + String encodeClassString(String classname) { + return "L" + classname + ";"; + } + + + /*-------------------------------------------------------- */ + + /** + * Parses version in package statements + */ + + private void parseVersionPkg() throws IOException { + if (scanner.token == SEMICOLON) { + return; + } + parse_ver: + { + if (scanner.token != Token.VERSION) { + break parse_ver; + } + scanner.scan(); + if (scanner.token != Token.INTVAL) { + break parse_ver; + } + currentCFV.setMajorVersion((short) scanner.intValue); + scanner.scan(); + if (scanner.token != Token.COLON) { + break parse_ver; + } + scanner.scan(); + if (scanner.token != Token.INTVAL) { + break parse_ver; + } + currentCFV.setMinorVersion((short) scanner.intValue); + scanner.scan(); + debugScan(" [Parser.parseVersionPkg]: " + currentCFV.asString()); + return; + } + env.error(scanner.pos, "version.expected"); + throw new Scanner.SyntaxError(); + } + + private void parseVersion() throws IOException { + if (scanner.token == Token.LBRACE) { + return; + } + parse_ver: + { + if (scanner.token != Token.VERSION) { + break parse_ver; + } + scanner.scan(); + if (scanner.token != Token.INTVAL) { + break parse_ver; + } + cd.cfv.setMajorVersion((short) scanner.intValue); + scanner.scan(); + if (scanner.token != Token.COLON) { + break parse_ver; + } + scanner.scan(); + if (scanner.token != Token.INTVAL) { + break parse_ver; + } + cd.cfv.setMinorVersion((short) scanner.intValue); + scanner.scan(); + debugStr("parseVersion: " + cd.cfv.asString()); + return; + } + env.error(scanner.pos, "version.expected"); + throw new Scanner.SyntaxError(); + } + + + /*---------------------------------------------*/ + + /** + * Parse an internal name: identifier. + */ + String parseIdent() throws Scanner.SyntaxError, IOException { + String v = scanner.idValue; + scanner.expect(Token.IDENT); + return v; + } + + /** + * Parse a local variable + */ + void parseLocVarDef() throws Scanner.SyntaxError, IOException { + if (scanner.token == Token.INTVAL) { + int v = scanner.intValue; + scanner.scan(); + curCode.LocVarDataDef(v); + } else { + String name = scanner.stringValue, type; + scanner.expect(Token.IDENT); + if (scanner.token == Token.COLON) { + scanner.scan(); + type = parseIdent(); + } else { + type = "I"; // TBD + } + curCode.LocVarDataDef(name, pool.FindCellAsciz(type)); + } + } + + Argument parseLocVarRef() throws Scanner.SyntaxError, IOException { + if (scanner.token == Token.INTVAL) { + int v = scanner.intValue; + scanner.scan(); + return new Argument(v); + } else { + String name = scanner.stringValue; + scanner.expect(Token.IDENT); + return curCode.LocVarDataRef(name); + } + } + + void parseLocVarEnd() throws Scanner.SyntaxError, IOException { + if (scanner.token == Token.INTVAL) { + int v = scanner.intValue; + scanner.scan(); + curCode.LocVarDataEnd(v); + } else { + String name = scanner.stringValue; + scanner.expect(Token.IDENT); + curCode.LocVarDataEnd(name); + } + } + + void parseMapItem(DataVector map) throws Scanner.SyntaxError, IOException { + StackMapType itemType = stackMapType(scanner.intValue, null); + ConstType tag = null; + Argument arg = null; + Token ptoken = scanner.token; + int iValue = scanner.intValue; + String sValue = scanner.stringValue; + scanner.scan(); + resolve: + { + switch (ptoken) { + case INTVAL: + break resolve; + case CLASS: + itemType = StackMapType.ITEM_Object; + tag = ConstType.CONSTANT_CLASS; + break resolve; + case CPINDEX: + itemType = StackMapType.ITEM_Object; + arg = pool.getCell(iValue); + break resolve; + case IDENT: + itemType = stackMapType(sValue); + tag = Tables.tag(sValue); + if (itemType != null) { // itemType OK + if ((tag != null) // ambiguity: "int," or "int 77,"? + && (scanner.token != SEMICOLON) + && (scanner.token != COMMA)) { + itemType = StackMapType.ITEM_Object; + } + break resolve; + } else if (tag != null) { // tag OK + itemType = StackMapType.ITEM_Object; + break resolve; + } + } + // resolution failed: + itemType = StackMapType.ITEM_Bogus; + env.error("itemtype.expected", "<" + ptoken.printValue() + ">"); + } + switch (itemType) { + case ITEM_Object: // followed by CP index + if (arg == null) { + arg = pool.FindCell(cpParser.parseConstValue(tag)); + } + map.addElement(new StackMapData.StackMapItem2(itemType, arg)); + break; + case ITEM_NewObject: // followed by label + arg = instrParser.parseLabelRef(); + map.addElement(new StackMapData.StackMapItem2(itemType, arg)); + break; + default: + map.addElement(new StackMapData.StackMapItem1(itemType)); + } + } + + /** + * Parse an external name: CPINDEX, string, or identifier. + */ + ConstCell parseName() throws Scanner.SyntaxError, IOException { + debugScan("------- [Parser.parseName]: "); + String v; + switch (scanner.token) { + case CPINDEX: { + int cpx = scanner.intValue; + scanner.scan(); + return pool.getCell(cpx); + } + case STRINGVAL: + v = scanner.stringValue; + scanner.scan(); + return pool.FindCellAsciz(v); + + // In many cases, Identifiers can correctly have the same + // names as keywords. We need to allow these. + case OPEN: + case MODULE: + case VARARGS: + case REQUIRES: + case EXPORTS: + case TO: + case USES: + case PROVIDES: + case WITH: + case OPENS: + + case ARRAY_TYPEPATH: + case INNER_TYPE_TYPEPATH: + case WILDCARD_TYPEPATH: + case TYPE_ARGUMENT_TYPEPATH: + case PERMITTEDSUBCLASSES: + case INF: + case NAN: + case COMPONENT: + + case SYNTHETIC: + case DEPRECATED: + case VERSION: + case BITS: + case STACK: + case LOCAL: + case OF: + case INNERCLASS: + case STRICT: + case FIELDREF: + case METHODREF: + case IDENT: + case BRIDGE: + v = scanner.idValue; + scanner.scan(); + return pool.FindCellAsciz(v); + default: + env.error(scanner.pos, "name.expected", scanner.token); + throw new Scanner.SyntaxError(); + } + } + + /** + * Parses a field or method reference for method handle. + */ + ConstCell parseMethodHandle(SubTag subtag) throws Scanner.SyntaxError, IOException { + ConstCell refCell; + switch (subtag) { + // If the value of the reference_kind item is + // 1 (REF_getField), 2 (REF_getStatic), 3 (REF_putField) or 4 (REF_putStatic), + // then the constant_pool entry at that index must be a CONSTANT_Fieldref_info structure (4.4.2) + // representing a field for which a method handle is to be created. jvms-4.4.8-200-C-A + case REF_GETFIELD: + case REF_GETSTATIC: + case REF_PUTFIELD: + case REF_PUTSTATIC: + refCell = pool.FindCell(cpParser.parseConstValue(ConstType.CONSTANT_FIELD)); + break; + // If the value of the reference_kind item is + // 5 (REF_invokeVirtual) or 8 (REF_newInvokeSpecial), + // then the constant_pool entry at that index must be a CONSTANT_Methodref_info structure (4.4.2) + // representing a class's method or constructor (2.9.1) for which a method handle is to be created. + // jvms-4.4.8-200-C-B + case REF_INVOKEVIRTUAL: + case REF_NEWINVOKESPECIAL: + refCell = pool.FindCell(cpParser.parseConstValue(ConstType.CONSTANT_METHOD)); + break; + case REF_INVOKESTATIC: + case REF_INVOKESPECIAL: + // CODETOOLS-7902333 + // 4.4.8. The CONSTANT_MethodHandle_info Structure + // reference_index + // The value of the reference_index item must be a valid index into the constant_pool table. + // The constant_pool entry at that index must be as follows: + // If the value of the reference_kind item is 6 (REF_invokeStatic) or 7 (REF_invokeSpecial), + // then if the class file version number is less than 52.0, the constant_pool entry at that index must be + // a CONSTANT_Methodref_info structure representing a class's method for which a method handle is to be created; + // if the class file version number is 52.0 or above, the constant_pool entry at that index must be + // either a CONSTANT_Methodref_info structure or a CONSTANT_InterfaceMethodref_info structure (4.4.2) + // representing a class's or interface's method for which a method handle is to be created. + ConstType ctype01 = ConstType.CONSTANT_METHOD; + ConstType ctype02 = ConstType.CONSTANT_INTERFACEMETHOD; + if (this.cd.cfv.major_version() >= 52 && Modifiers.isInterface(this.cd.access)) { + ctype01 = ConstType.CONSTANT_INTERFACEMETHOD; + ctype02 = ConstType.CONSTANT_METHOD; + } + refCell = cpParser.parseConstRef(ctype01, ctype02); + break; + case REF_INVOKEINTERFACE: + refCell = pool.FindCell(cpParser.parseConstValue(ConstType.CONSTANT_INTERFACEMETHOD)); + break; + default: + // should not reach + throw new Scanner.SyntaxError(); + } + return refCell; + } + + /** + * Parses a sub-tag value in method handle. + */ + SubTag parseSubtag() throws Scanner.SyntaxError, IOException { + SubTag subtag = null; + switch (scanner.token) { + case IDENT: + subtag = subtag(scanner.stringValue); + break; + case INTVAL: + subtag = subtag(scanner.intValue); + break; + } + if (subtag == null) { + env.error("subtag.expected"); + throw new Scanner.SyntaxError(); + } + scanner.scan(); + return subtag; + } + + ConstCell parseClassName(boolean uncond) throws Scanner.SyntaxError, IOException { + String v; + switch (scanner.token) { + case CPINDEX: { + int cpx = scanner.intValue; + scanner.scan(); + return pool.getCell(cpx); + } + case STRINGVAL: + v = scanner.stringValue; + scanner.scan(); + v = prependPackage(v, uncond); + return pool.FindCellAsciz(v); + // Some identifiers might coincide with token names. + // these should be OK to use as identifier names. + case OPEN: + case MODULE: + case VARARGS: + case REQUIRES: + case EXPORTS: + case TO: + case USES: + case PROVIDES: + case WITH: + case OPENS: + + case ARRAY_TYPEPATH: + case INNER_TYPE_TYPEPATH: + case WILDCARD_TYPEPATH: + case TYPE_ARGUMENT_TYPEPATH: + case PERMITTEDSUBCLASSES: + case INF: + case NAN: + case COMPONENT: + + case SYNTHETIC: + case DEPRECATED: + case VERSION: + case BITS: + case STACK: + case LOCAL: + case OF: + case INNERCLASS: + case STRICT: + case FIELDREF: + case METHODREF: + case BRIDGE: + case IDENT: + v = scanner.idValue; + scanner.scan(); + v = prependPackage(v, uncond); + return pool.FindCellAsciz(v); + default: + ConstType key = Tables.tag(scanner.token.value()); + env.traceln("%%%%% Unrecognized token [" + scanner.token + "]: '" + (key == null ? "null" : key.parseKey()) + "'."); + env.error(scanner.prevPos, "name.expected", "\"" + scanner.token.parseKey() + "\""); + throw new Scanner.SyntaxError(); + } + } + + private String prependPackage(String className, boolean uncond) { + if (uncond || (scanner.token == Token.FIELD)) { + if ((!className.contains("/")) // class identifier doesn't contain "/" + && (!className.contains("["))) { // class identifier doesn't contain "[" + className = pkgPrefix + className; // add package + } + } + return className; + } + + /** + * Parse a signed integer of size bytes long. + * size = 1 or 2 + */ + Argument parseInt(int size) throws Scanner.SyntaxError, IOException { + if (scanner.token == Token.BITS) { + scanner.scan(); + } + if (scanner.token != Token.INTVAL) { + env.error(scanner.pos, "int.expected"); + throw new Scanner.SyntaxError(); + } + int arg = scanner.intValue * scanner.sign; + switch (size) { + case 1: +// if ((arg>127)||(arg<-128)) { // 0xFF not allowed + if ((arg > 255) || (arg < -128)) { // to allow 0xFF + env.error(scanner.pos, "value.large", "1 byte"); + throw new Scanner.SyntaxError(); + } + break; + case 2: +// if ((arg > 32767) || (arg < -32768)) { //this seems +// natural but is not backward compatible. Some tests contain +// expressions like: +// sipush 0x8765; + + if ((arg > 65535) || (arg < -32768)) { + env.error(scanner.pos, "value.large", "2 bytes"); + throw new Scanner.SyntaxError(); + } + break; + default: + throw new InternalError("parseInt(" + size + ")"); + } + scanner.scan(); + return new Argument(arg); + } + + /** + * Parse an unsigned integer of size bytes long. + * size = 1 or 2 + */ + Argument parseUInt(int size) throws Scanner.SyntaxError, IOException { + if (scanner.token != Token.INTVAL) { + env.error(scanner.pos, "int.expected"); + throw new Scanner.SyntaxError(); + } + if (scanner.sign == -1) { + env.error(scanner.pos, "neg.forbidden"); + throw new Scanner.SyntaxError(); + } + int arg = scanner.intValue; + switch (size) { + case 1: + if (arg > 255) { + env.error(scanner.pos, "value.large", "1 byte"); + throw new Scanner.SyntaxError(); + } + break; + case 2: + if (arg > 65535) { + env.error(scanner.pos, "value.large", "2 bytes"); + throw new Scanner.SyntaxError(); + } + break; + default: + throw new InternalError("parseUInt(" + size + ")"); + } + scanner.scan(); + return new Argument(arg); + } + + /** + * Parse constant declaration + */ + private void parseConstDef() throws IOException { + for (; ; ) { + if (scanner.token == Token.CPINDEX) { + int cpx = scanner.intValue; + scanner.scan(); + scanner.expect(Token.ASSIGN); + env.traceln("parseConstDef:" + cpx); + pool.setCell(cpx, cpParser.parseConstRef(null)); + } else { + env.error("const.def.expected"); + throw new Scanner.SyntaxError(); + } + if (scanner.token != COMMA) { + scanner.expect(SEMICOLON); + return; + } + scanner.scan(); // COMMA + } + } + + /** + * Parse the modifiers + */ + private int scanModifier(int mod) throws IOException { + int nextmod, prevpos; + + while (true) { + nextmod = 0; + switch (scanner.token) { + case PUBLIC: + nextmod = ACC_PUBLIC; + break; + case PRIVATE: + nextmod = ACC_PRIVATE; + break; + case PROTECTED: + nextmod = ACC_PROTECTED; + break; + case STATIC: + nextmod = ACC_STATIC; + break; + case FINAL: + nextmod = ACC_FINAL; + break; + case SYNCHRONIZED: + nextmod = ACC_SYNCHRONIZED; + break; + case SUPER: + nextmod = ACC_SUPER; + break; + case VOLATILE: + nextmod = ACC_VOLATILE; + break; + case BRIDGE: + nextmod = ACC_BRIDGE; + break; + case TRANSIENT: + nextmod = ACC_TRANSIENT; + break; + case VARARGS: + nextmod = ACC_VARARGS; + break; + case NATIVE: + nextmod = ACC_NATIVE; + break; + case INTERFACE: + nextmod = ACC_INTERFACE; + break; + case ABSTRACT: + nextmod = ACC_ABSTRACT; + break; + case STRICT: + nextmod = ACC_STRICT; + break; + case ENUM: + nextmod = ACC_ENUM; + break; + case SYNTHETIC: + nextmod = ACC_SYNTHETIC; + break; + case ANNOTATION_ACCESS: + nextmod = ACC_ANNOTATION; + break; + + case DEPRECATED: + nextmod = DEPRECATED_ATTRIBUTE; + break; + case MANDATED: + nextmod = ACC_MANDATED; + break; + default: + return nextmod; + } + prevpos = scanner.pos; + scanner.scan(); + if ((mod & nextmod) == 0) { + return nextmod; + } + env.error(prevpos, "warn.repeated.modifier"); + } + } + + int scanModifiers() throws IOException { + int mod = 0, nextmod; + + while (true) { + nextmod = scanModifier(mod); + if (nextmod == 0) { + return mod; + } + mod = mod | nextmod; + } + } + + /** + * Parse a field. + */ + private void parseField(int mod) throws Scanner.SyntaxError, IOException { + debugStr(" [Parser.parseField]: <<>>"); + // check access modifiers: + Modifiers.checkFieldModifiers(cd, mod, scanner.pos); + + while (true) { + ConstCell nameCell = parseName(); + scanner.expect(Token.COLON); + ConstCell typeCell = parseName(); + + // Define the variable + FieldData fld = cd.addField(mod, nameCell, typeCell); + + if (memberAnnttns != null) { + fld.addAnnotations(memberAnnttns); + } + + // Parse the optional attribute: signature + if (scanner.token == Token.COLON) { + scanner.scan(); + ConstCell signatureCell = parseName(); + fld.setSignatureAttr(signatureCell); + } + + // Parse the optional initializer + if (scanner.token == Token.ASSIGN) { + scanner.scan(); + fld.SetValue(cpParser.parseConstRef(null)); + } + + // If the next scanner.token is a comma, then there is more + debugScan(" [Parser.parseField]: Field: " + fld + " "); + + if (scanner.token != COMMA) { + scanner.expect(SEMICOLON); + return; + } + scanner.scan(); + } // end while + } // end parseField + + /** + * Scan method's signature to determine size of parameters. + */ + private int countParams(ConstCell sigCell) throws Scanner.SyntaxError { + String sig; + try { + ConstValue_String strConst = (ConstValue_String) sigCell.ref; + sig = strConst.value; + } catch (NullPointerException | ClassCastException e) { + return 0; // ??? TBD + } + int siglen = sig.length(), k = 0, loccnt = 0, errparam = 0; + boolean arraytype = false; + scan: + { + if (k >= siglen) { + break scan; + } + if (sig.charAt(k) != '(') { + errparam = 1; + break scan; + } + for (k = 1; k < siglen; k++) { + switch (sig.charAt(k)) { + case ')': + if (arraytype) { + errparam = 2; + break scan; + } + return loccnt; + case '[': + arraytype = true; + break; + case 'B': + case 'C': + case 'F': + case 'I': + case 'S': + case 'Z': + loccnt++; + arraytype = false; + break; + case 'D': + case 'J': + loccnt++; + if (arraytype) { + arraytype = false; + } else { + loccnt++; + } + break; + case 'L': + for (; ; k++) { + if (k >= siglen) { + errparam = 3; + break scan; + } + if (sig.charAt(k) == ';') { + break; + } + } + loccnt++; + arraytype = false; + break; + default: + errparam = 4; + break scan; + } + } + } + env.error(scanner.pos, "msig.malformed", Integer.toString(k), Integer.toString(errparam)); + return loccnt; + } + + /** + * Parse a method. + */ + private void parseMethod(int mod) throws Scanner.SyntaxError, IOException { + + // The start of the method + int posa = scanner.pos; + debugStr(" [Parser.parseMethod]: <<>>"); + + ConstCell nameCell = parseName(); + ConstValue_String strConst = (ConstValue_String) nameCell.ref; + String name = strConst.value; + boolean is_clinit = name.equals(""); + boolean is_init = name.equals(""); + DefaultAnnotationAttr defAnnot = null; + + // check access modifiers: + Modifiers.checkMethodModifiers(cd, mod, posa, is_init, is_clinit); + + scanner.expect(Token.COLON); + ConstCell typeCell = parseName(); + int paramcnt = countParams(typeCell); + if ((!Modifiers.isStatic(mod)) && !is_clinit) { + paramcnt++; + } + if (paramcnt > 255) { + env.error(scanner.pos, "warn.msig.more255", Integer.toString(paramcnt)); + } + // Parse throws clause + ArrayList exc_table = null; + if (scanner.token == Token.THROWS) { + scanner.scan(); + exc_table = new ArrayList<>(); + for (; ; ) { + posa = scanner.pos; + ConstCell exc = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + if (exc_table.contains(exc)) { + env.error(posa, "warn.exc.repeated"); + } else { + exc_table.add(exc); + env.traceln("THROWS:" + exc.arg); + } + if (scanner.token != COMMA) { + break; + } + scanner.scan(); + } + } + if (scanner.token == Token.DEFAULT) { + // need to scan the annotation value + defAnnot = annotParser.parseDefaultAnnotation(); + } + + MethodData curMethod = cd.StartMethod(mod, nameCell, typeCell, exc_table); + Argument max_stack = null, max_locals = null; + + if (scanner.token == Token.STACK) { + scanner.scan(); + max_stack = parseUInt(2); + } + if (scanner.token == Token.LOCAL) { + scanner.scan(); + max_locals = parseUInt(2); + } + if (scanner.token == Token.INTVAL) { + annotParser.parseParamAnnots(paramcnt, curMethod); + } + + if (scanner.token == SEMICOLON) { + if ((max_stack != null) || (max_locals != null)) { + env.error("token.expected", "{"); + } + scanner.scan(); + } else { + scanner.expect(Token.LBRACE); + curCode = curMethod.startCode(posa, paramcnt, max_stack, max_locals); + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + instrParser.parseInstr(); + if (scanner.token == Token.RBRACE) { + break; + } + // code's type annotation(s) + if (scanner.token == Token.ANNOTATION) { + curCode.addAnnotations(annotParser.scanAnnotations()); + break; + } + scanner.expect(SEMICOLON); + } + curCode.endCode(); + scanner.expect(Token.RBRACE); + } + + if (defAnnot != null) { + curMethod.addDefaultAnnotation(defAnnot); + } + if (memberAnnttns != null) { + curMethod.addAnnotations(memberAnnttns); + } + cd.EndMethod(); + debugStr(" [Parser.parseMethod]: Method: " + curMethod); + + } // end parseMethod + + /** + * Parse a (CPX based) BootstrapMethod entry. + */ + private void parseCPXBootstrapMethod() throws Scanner.SyntaxError, IOException { + // Parses in the form: + // BOOTSTRAPMETHOD CPX_MethodHandle (CPX_Arg)* ; + if (scanner.token == Token.CPINDEX) { + // CPX can be a CPX to an MethodHandle constant, + int cpx = scanner.intValue; + ConstCell MHCell = pool.getCell(cpx); + scanner.scan(); + ArrayList bsm_args = new ArrayList<>(256); + + while (scanner.token != SEMICOLON) { + if (scanner.token == Token.CPINDEX) { + bsm_args.add(pool.getCell(scanner.intValue)); + + } else { + // throw error, bootstrap method is not recognizable + env.error(scanner.pos, "invalid.bootstrapmethod"); + throw new Scanner.SyntaxError(); + } + scanner.scan(); + } + BootstrapMethodData bsmData = new BootstrapMethodData(MHCell, bsm_args); + cd.addBootstrapMethod(bsmData); + } else { + // throw error, bootstrap method is not recognizable + env.error(scanner.pos, "invalid.bootstrapmethod"); + throw new Scanner.SyntaxError(); + } + } + + /** + * Parse a NestHost entry + */ + private void parseNestHost() throws Scanner.SyntaxError, IOException { + // Parses in the form: + // NESTHOST IDENT; + debugStr(" [Parser.parseNestHost]: <<>>"); + String className = prependPackage(parseIdent(), true); + ConstCell hostClass = pool.FindCellClassByName(className); + debugScan(" [Parser.parseNestHost]: NestHost: class " + className); + scanner.expect(SEMICOLON); + cd.addNestHost(hostClass); + } + + /** + * Parse a list of classes belonging to the [NestMembers | PermittedSubclasses] entry + */ + private void parseClasses(Consumer> classesConsumer) + throws Scanner.SyntaxError, IOException { + ArrayList classes = new ArrayList<>(); + // Parses in the form: + // (NESTMEMBERS|PERMITTEDSUBCLASSES)? IDENT(, IDENT)*; + debugStr(" [Parser.parseClasses]: <<>>"); + while (true) { + String className = prependPackage(parseIdent(), true); + classes.add(pool.FindCellClassByName(className)); + debugScan(" [Parser.parseClasses]: class " + className); + if (scanner.token != COMMA) { + scanner.expect(SEMICOLON); + classesConsumer.accept(classes); + return; + } + scanner.scan(); + } + } + + /** + * Parse the Record entry + */ + private void parseRecord() throws Scanner.SyntaxError, IOException { + // Parses in the form: + // RECORD { (COMPONENT)+ } + // where + // COMPONENT Component (ANNOTATION)* NAME:DESCRIPTOR(:SIGNATURE)? (,|;) + // NAME = (CPINDEX | IDENT) + // DESCRIPTOR = (CPINDEX | STRING) + // SIGNATURE = (CPINDEX | STRING) + debugScan("[Parser.parseRecord]: Begin"); + scanner.expect(Token.LBRACE); + + ArrayList componentAnntts = null; + boolean grouped = false; + RecordData rd = cd.setRecord(scanner.pos); + + while (true) { + if (scanner.token == Token.RBRACE) { + if (rd.isEmpty()) { + env.error(scanner.pos, "warn.no.components.in.record.attribute"); + cd.rejectRecord(); + } else if (grouped) { + env.error(scanner.pos, "grouped.component.expected"); + } + scanner.scan(); + break; + } + + ConstCell nameCell, descCell, signatureCell = null; + if (scanner.token == Token.ANNOTATION) { + componentAnntts = annotParser.scanAnnotations(); + } + + scanner.expect(Token.COMPONENT); + + nameCell = parseName(); + scanner.expect(Token.COLON); + descCell = parseName(); + // Parse the optional attribute: signature + if (scanner.token == Token.COLON) { + scanner.scan(); + signatureCell = parseName(); + } + + rd.addComponent(nameCell, descCell, signatureCell, componentAnntts); + + switch (scanner.token) { + case COMMA: + grouped = true; + break; + case SEMICOLON: + grouped = false; + componentAnntts = null; + break; + default: + env.error(scanner.pos, "one.of.two.token.expected", + "<" + SEMICOLON.printValue() + ">", + "<" + COMMA.printValue() + ">"); + break; + } + // next component + scanner.scan(); + } // end while + debugScan("[Parser.parseRecord]: End"); + } + + /** + * Parse an inner class. + */ + private void parseInnerClass(int mod) throws Scanner.SyntaxError, IOException { + // Parses in the form: + // MODIFIERS (INNERCLASSNAME =)? (INNERCLASS) (OF OUTERCLASS)? ; + // + // where + // INNERCLASSNAME = (IDENT | CPX_IN-CL-NM) + // INNERCLASS = (CLASS IDENT | CPX_IN-CL) (S2) + // OUTERCLASS = (CLASS IDENT | CPX_OT-CL) (S3) + // + // Note: + // If a class reference cannot be identified using IDENT, CPX indexes must be used. + + // check access modifiers: + debugScan("[Parser.parseInnerClass]: Begin "); + Modifiers.checkInnerClassModifiers(cd, mod, scanner.pos); + + ConstCell nameCell; + ConstCell innerClass = null; + ConstCell outerClass = null; + + + if (scanner.token == Token.CLASS) { + nameCell = pool.getCell(0); // no NameIndex + parseInnerClass_s2(mod, nameCell, innerClass, outerClass); + } else { + if ((scanner.token == Token.IDENT) || scanner.checkTokenIdent()) { + // Got a Class Name + nameCell = parseName(); + parseInnerClass_s1(mod, nameCell, innerClass, outerClass); + } else if (scanner.token == Token.CPINDEX) { + // CPX can be either a CPX to an InnerClassName, + // or a CPX to an InnerClassInfo + int cpx = scanner.intValue; + nameCell = pool.getCell(cpx); + ConstValue nameCellValue = nameCell.ref; + + if (nameCellValue instanceof ConstValue_String) { + // got a name cell + scanner.scan(); + parseInnerClass_s1(mod, nameCell, innerClass, outerClass); + } else { + // got a CPRef cell + nameCell = pool.getCell(0); // no NameIndex + parseInnerClass_s2(mod, nameCell, innerClass, outerClass); + } + } else { + pic_error(); + } + + } + } + + private void parseInnerClass_s1(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) throws IOException { + // next scanner.token must be '=' + if (scanner.token == Token.ASSIGN) { + scanner.scan(); + parseInnerClass_s2(mod, nameCell, innerClass, outerClass); + } else { + pic_error(); + } + + } + + private void parseInnerClass_s2(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) throws IOException { + // scanner.token is either "CLASS IDENT" or "CPX_Class" + if ((scanner.token == Token.CPINDEX) || (scanner.token == Token.CLASS)) { + if (scanner.token == Token.CPINDEX) { + innerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + } + + if (scanner.token == Token.CLASS) { + // next symbol needs to be InnerClass + scanner.scan(); + innerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + } + + // See if declaration is terminated + if (scanner.token == SEMICOLON) { + // InnerClass is complete, no OUTERINFO; + outerClass = pool.getCell(0); + pic_tracecreate(mod, nameCell, innerClass, outerClass); + cd.addInnerClass(mod, nameCell, innerClass, outerClass); + } else if (scanner.token == Token.OF) { + // got an outer class reference + parseInnerClass_s3(mod, nameCell, innerClass, outerClass); + } else { + pic_error(); + } + + } else { + pic_error(); + } + + } + + private void parseInnerClass_s3(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) throws IOException { + scanner.scan(); + if ((scanner.token == Token.CLASS) || (scanner.token == Token.CPINDEX)) { + if (scanner.token == Token.CLASS) { + // next symbol needs to be InnerClass + scanner.scan(); + outerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + } + if (scanner.token == Token.CPINDEX) { + outerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + } + + if (scanner.token == SEMICOLON) { + pic_tracecreate(mod, nameCell, innerClass, outerClass); + cd.addInnerClass(mod, nameCell, innerClass, outerClass); + } else { + pic_error(); + } + } else { + pic_error(); + } + } + + private void pic_tracecreate(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) { + // throw error, IC is not recognizable + env.trace(" Creating InnerClass: [" + Modifiers.toString(mod, CF_Context.CTX_INNERCLASS) + "], "); + + if (nameCell != pool.getCell(0)) { + ConstValue value = nameCell.ref; + if (value != null) { + env.trace(value.toString() + " = "); + } + } + + ConstValue_Cell ici_val = (ConstValue_Cell) innerClass.ref; + ConstCell ici_ascii = ici_val.cell; + // Constant pool may not be numberized yet. + // + // check values before dereference on a trace. + if (ici_ascii.ref == null) { + env.trace("<#cpx-unresolved> "); + } else { + ConstValue_String cval = (ConstValue_String) ici_ascii.ref; + if (cval.value == null) { + env.trace("<#cpx-0> "); + } else { + env.trace(cval.value + " "); + } + } + + if (outerClass != pool.getCell(0)) { + if (outerClass.arg != 0) { + ConstValue_Cell oci_val = (ConstValue_Cell) outerClass.ref; + ConstCell oci_ascii = oci_val.cell; + if (oci_ascii.ref == null) { + env.trace(" of <#cpx-unresolved> "); + } else { + ConstValue_String cval = (ConstValue_String) oci_ascii.ref; + if (cval.value == null) { + env.trace(" of <#cpx-0> "); + } else { + env.trace(" of " + cval.value); + } + } + } + } + + env.traceln(""); + } + + private void pic_error() { + // throw error, IC is not recognizable + env.error(scanner.pos, "invalid.innerclass"); + throw new Scanner.SyntaxError(); + } + + /** + * The match() method is used to quickly match opening + * brackets (ie: '(', '{', or '[') with their closing + * counter part. This is useful during error recovery.

+ *

+ * Scan to a matching '}', ']' or ')'. The current scanner.token must be + * a '{', '[' or '('; + */ + private void match(Token open, Token close) throws IOException { + int depth = 1; + + while (true) { + scanner.scan(); + if (scanner.token == open) { + depth++; + } else if (scanner.token == close) { + if (--depth == 0) { + return; + } + } else if (scanner.token == Token.EOF) { + env.error(scanner.pos, "unbalanced.paren"); + return; + } + } + } + + /** + * Recover after a syntax error in a field. This involves + * discarding scanner.tokens until an EOF or a possible legal + * continuation is encountered. + */ + private void recoverField() throws Scanner.SyntaxError, IOException { + while (true) { + switch (scanner.token) { + case EOF: + case STATIC: + case FINAL: + case PUBLIC: + case PRIVATE: + case SYNCHRONIZED: + case TRANSIENT: + case PROTECTED: + case VOLATILE: + case NATIVE: +// case INTERFACE: see below + case ABSTRACT: + case ANNOTATION_ACCESS: + // possible begin of a field, continue + return; + + case LBRACE: + match(Token.LBRACE, Token.RBRACE); + scanner.scan(); + break; + + case LPAREN: + match(Token.LPAREN, Token.RPAREN); + scanner.scan(); + break; + + case LSQBRACKET: + match(Token.LSQBRACKET, Token.RSQBRACKET); + scanner.scan(); + break; + + case RBRACE: + case INTERFACE: + case CLASS: + case IMPORT: + case PACKAGE: + // begin of something outside a class, panic more + endClass(); + scanner.debugStr(" [Parser.recoverField]: pos: [" + scanner.pos + "]: "); + throw new Scanner.SyntaxError(); + + default: + // don't know what to do, skip + scanner.scan(); + break; + } + } + } + + /** + * Parse a class or interface declaration. + */ + private void parseClass(int mod) throws IOException { + int posa = scanner.pos; + debugStr(" [Parser.parseClass]: Begin "); + // check access modifiers: + Modifiers.checkClassModifiers(env, mod, scanner); + + if (cd == null) { + cd = new ClassData(env, currentCFV.clone()); + pool = cd.pool; + } + + if (clsAnnttns != null) { + cd.addAnnotations(clsAnnttns); + } + + // move the tokenizer to the identifier: + if (scanner.token == Token.CLASS) { + scanner.scan(); + } else if (scanner.token == Token.ANNOTATION) { + scanner.scan(); + if (scanner.token == Token.INTERFACE) { + mod |= ACC_ANNOTATION | ACC_INTERFACE; + scanner.scan(); + } else { + env.error(scanner.prevPos, "token.expected", Token.ANNOTATION.parseKey() + Token.INTERFACE.parseKey()); + throw new Scanner.SyntaxError(); + } + } + + // Parse the class name + ConstCell nm = cpParser.parseConstRef(ConstType.CONSTANT_CLASS, null, true); + + if (scanner.token == Token.FIELD) { // DOT + String fileExtension; + scanner.scan(); + switch (scanner.token) { + case STRINGVAL: + fileExtension = scanner.stringValue; + break; + case IDENT: + fileExtension = scanner.idValue; + break; + default: + env.error(scanner.pos, "name.expected"); + throw new Scanner.SyntaxError(); + } + scanner.scan(); + cd.fileExtension = "." + fileExtension; + } else if (scanner.token == Token.MODULE) { + env.error(scanner.prevPos, "token.expected", Token.OPEN.parseKey()); + throw new Scanner.SyntaxError(); + } else if (scanner.token == SEMICOLON) { + // drop the semi-colon following a name + scanner.scan(); + } + + // Parse extends clause + ConstCell sup = null; + if (scanner.token == Token.EXTENDS) { + scanner.scan(); + sup = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + while (scanner.token == COMMA) { + scanner.scan(); + env.error(posa, "multiple.inherit"); + cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + } + } + + // Parse implements clause + ArrayList impl = new ArrayList<>(); + if (scanner.token == Token.IMPLEMENTS) { + do { + scanner.scan(); + Argument intf = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + if (impl.contains(intf)) { + env.error(posa, "warn.intf.repeated", intf); + } else { + impl.add(intf); + } + } while (scanner.token == COMMA); + } + parseVersion(); + scanner.expect(Token.LBRACE); + + // Begin a new class + cd.init(mod, nm, sup, impl); + + // Parse constant declarations + + // Parse class members + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + switch (scanner.token) { + case SEMICOLON: + // Empty fields are allowed + scanner.scan(); + break; + case CONST: + scanner.scan(); + parseConstDef(); + explicitcp = true; + break; + default: // scanner.token is some member. + parseClassMembers(); + } // end switch + } // while + scanner.expect(Token.RBRACE); + // End the class + endClass(); + } // end parseClass + + /** + * Parses a package or type name in a module statement(s) + */ + private String parseTypeName() throws IOException { + String name = "", field = ""; + while (true) { + if (scanner.token.possibleModuleName()) { + name = name + field + scanner.idValue; + scanner.scan(); + } else { + env.error(scanner.pos, "name.expected", "\"" + scanner.token.parseKey() + "\""); + throw new Scanner.SyntaxError(); + } + if (scanner.token == Token.FIELD) { + env.error(scanner.pos, "warn.dot.will.be.converted"); + field = "/"; + scanner.scan(); + } else { + break; + } + } + return name; + } + + /** + * Parses a module name in a module statement(s) + */ + private String parseModuleName() throws IOException { + String name = "", field = ""; + while (true) { + if (scanner.token.possibleModuleName()) { + name = name + field + scanner.idValue; + scanner.scanModuleStatement(); + } else { + env.error(scanner.pos, "module.name.expected", "\"" + scanner.token.parseKey() + "\""); + throw new Scanner.SyntaxError().Fatal(); + } + if (scanner.token == Token.FIELD) { + field = Character.toString((char) scanner.token.value()); + scanner.scanModuleStatement(); + } else { + break; + } + } + return name; + } + + /** + * Parse a module declaration. + */ + private void parseModule() throws IOException { + debugStr(" [Parser.parseModule]: Begin "); + if (cd == null) { + cd = new ClassData(env, currentCFV.clone()); + pool = cd.pool; + } + if (clsAnnttns != null) { + cd.addAnnotations(clsAnnttns); + } + moduleAttribute = new ModuleAttr(cd); + + if (scanner.token == Token.OPEN) { + moduleAttribute.openModule(); + scanner.scan(); + } + + // move the tokenizer to the identifier: + if (scanner.token == Token.MODULE) { + scanner.scanModuleStatement(); + // scanner.scan(); + } else { + env.error(scanner.pos, "token.expected", Token.MODULE.parseKey()); + throw new Scanner.SyntaxError().Fatal(); + } + // Parse the module name + String moduleName = parseModuleName(); + if (moduleName.isEmpty()) { + env.error(scanner.pos, "name.expected"); + throw new Scanner.SyntaxError().Fatal(); + } + moduleAttribute.setModuleName(moduleName); + + parseVersion(); + scanner.expect(Token.LBRACE); + + // Begin a new class as module + cd.initAsModule(); + + // Parse module statement(s) + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + switch (scanner.token) { + case REQUIRES: + scanRequires(moduleAttribute.requires); + break; + case EXPORTS: + scanStatement(moduleAttribute.exports, + this::parseTypeName, + this::parseModuleName, + Token.TO, + true, + "exports.expected"); + break; + case OPENS: + scanStatement(moduleAttribute.opens, + this::parseTypeName, + this::parseModuleName, + Token.TO, true, "opens.expected"); + break; + case USES: + scanStatement(moduleAttribute.uses, "uses.expected"); + break; + case PROVIDES: + scanStatement(moduleAttribute.provides, + this::parseTypeName, + this::parseTypeName, + Token.WITH, + false, + "provides.expected"); + break; + case SEMICOLON: + // Empty fields are allowed + scanner.scan(); + break; + default: + env.error(scanner.pos, "module.statement.expected"); + throw new Scanner.SyntaxError().Fatal(); + } // end switch + } // while + scanner.expect(Token.RBRACE); + // End the module + endModule(); + } // end parseModule + + /** + * Scans ModuleStatement: requires [transitive] [static] ModuleName ; + */ + private void scanRequires(BiConsumer action) throws IOException { + int flags = 0; + String mn = ""; + scanner.scanModuleStatement(); + while (scanner.token != SEMICOLON) { + switch (scanner.token) { + case STATIC: + if (((flags & (1 << Module.Modifier.ACC_STATIC_PHASE.asInt())) != 0) || !mn.isEmpty()) { + env.error(scanner.pos, "requires.expected"); + throw new Scanner.SyntaxError().Fatal(); + } + flags |= Module.Modifier.ACC_STATIC_PHASE.asInt(); + break; + case TRANSITIVE: + if (((flags & (1 << Module.Modifier.ACC_TRANSITIVE.asInt())) != 0) || !mn.isEmpty()) { + env.error(scanner.pos, "requires.expected"); + throw new Scanner.SyntaxError().Fatal(); + } + flags |= Module.Modifier.ACC_TRANSITIVE.asInt(); + break; + case IDENT: + if (!mn.isEmpty()) { + env.error(scanner.pos, "requires.expected"); + throw new Scanner.SyntaxError().Fatal(); + } + mn = parseModuleName(); + continue; + default: + if (mn.isEmpty() && scanner.token.possibleModuleName()) { + mn = parseModuleName(); + continue; + } else { + env.error(scanner.pos, "requires.expected"); + throw new Scanner.SyntaxError().Fatal(); + } + } + scanner.scanModuleStatement(); + } + // Token.SEMICOLON + if (mn.isEmpty()) { + env.error(scanner.pos, "requires.expected"); + throw new Scanner.SyntaxError().Fatal(); + } + action.accept(mn, flags); + scanner.scanModuleStatement(); + } + + /** + * Scans ModuleStatement: uses TypeName; + */ + private void scanStatement(Consumer> action, String err) throws IOException { + HashSet names = scanList(() -> scanner.scan(), this::parseTypeName, err, true); + // Token.SEMICOLON + if (names.size() != 1) { + env.error(scanner.pos, err); + throw new Scanner.SyntaxError().Fatal(); + } + action.accept(names); + scanner.scan(); + } + + /** + * Scans Module Statement(s): + * exports packageName [to ModuleName {, ModuleName}] ; + * opens packageName [to ModuleName {, ModuleName}] ; + * provides TypeName with TypeName [,typeName] ; + */ + private void scanStatement(BiConsumer> action, + NameSupplier source, + NameSupplier target, + Token startList, + boolean emptyListAllowed, + String err) throws IOException { + String typeName = ""; + HashSet names = new HashSet<>(); + scanner.scan(); + while (scanner.token != SEMICOLON) { + if (scanner.token == Token.IDENT) { + if (typeName.isEmpty()) { + typeName = source.get(); + continue; + } + env.error(scanner.pos, err); + throw new Scanner.SyntaxError().Fatal(); + } + if (scanner.token == startList) { + if (typeName.isEmpty()) { + env.error(scanner.pos, err); + throw new Scanner.SyntaxError().Fatal(); + } + names = scanList(scanner.token == Token.TO ? () -> scanner.scanModuleStatement() : () -> scanner.scan(), target, err, false); + break; + } else { + env.error(scanner.pos, err); + throw new Scanner.SyntaxError().Fatal(); + } + } + // Token.SEMICOLON + if (typeName.isEmpty() || (names.isEmpty() && !emptyListAllowed)) { + env.error(scanner.pos, err); + throw new Scanner.SyntaxError().Fatal(); + } + action.accept(typeName, names); + scanner.scan(); + } + + /** + * Scans the "to" or "with" part of ModuleStatement: exports PackageName [to ModuleName {, ModuleName}] ;, + * opens packageName [to ModuleName {, ModuleName}] ; + * provides TypeName with TypeName [,typeName] ; + * uses TypeName; + * : [ModuleName {, ModuleName}]; , [TypeName [,typeName]]; or TypeName; + */ + private HashSet scanList(Method scanMethod, NameSupplier target, String err, boolean onlyOneElement) throws IOException { + HashSet names = new HashSet<>(); + boolean comma = false, first = true; + scanMethod.call(); + while (scanner.token != SEMICOLON) { + switch (scanner.token) { + case COMMA: + if (comma || first || onlyOneElement) { + env.error(scanner.pos, err); + throw new Scanner.SyntaxError().Fatal(); + } + comma = true; + break; + case IDENT: + if (!first && !comma) { + env.error(scanner.pos, err); + throw new Scanner.SyntaxError().Fatal(); + } + names.add(target.get()); + comma = false; + first = false; + continue; + default: + env.error(scanner.pos, err); + throw new Scanner.SyntaxError().Fatal(); + } + scanner.scan(); + } + // Token.SEMICOLON + if (names.isEmpty() || comma) { + env.error(scanner.pos, err); + throw new Scanner.SyntaxError().Fatal(); + } + return names; + } + + private void parseClassMembers() throws IOException { + debugScan("[Parser.parseClassMembers]: Begin "); + // Parse annotations + if (scanner.token == Token.ANNOTATION) { + memberAnnttns = annotParser.scanAnnotations(); + } + // Parse modifiers + int mod = scanModifiers(); + try { + switch (scanner.token) { + case FIELDREF: + scanner.scan(); + parseField(mod); + break; + case METHODREF: + scanner.scan(); + parseMethod(mod); + break; + case INNERCLASS: + scanner.scan(); + parseInnerClass(mod); + break; + case BOOTSTRAPMETHOD: + scanner.scan(); + parseCPXBootstrapMethod(); + break; + case NESTHOST: + if (cd.nestHostAttributeExists()) { + env.error(scanner.pos, "extra.nesthost.attribute"); + throw new Scanner.SyntaxError(); + } else if (cd.nestMembersAttributesExist()) { + env.error(scanner.pos, "both.nesthost.nestmembers.found"); + throw new Scanner.SyntaxError(); + } + scanner.scan(); + parseNestHost(); + break; + case NESTMEMBERS: + if (cd.nestMembersAttributesExist()) { + env.error(scanner.pos, "extra.nestmembers.attribute"); + throw new Scanner.SyntaxError(); + } else if (cd.nestHostAttributeExists()) { + env.error(scanner.pos, "both.nesthost.nestmembers.found"); + throw new Scanner.SyntaxError(); + } + scanner.scan(); + parseClasses(list -> cd.addNestMembers(list)); + break; + case PERMITTEDSUBCLASSES: // JEP 360 + if (cd.nestMembersAttributesExist()) { + env.error(scanner.pos, "extra.permittedsubclasses.attribute"); + throw new Scanner.SyntaxError(); + } + scanner.scan(); + parseClasses(list -> cd.addPermittedSubclasses(list)); + break; + case RECORD: // JEP 359 + if (cd.recordAttributeExists()) { + env.error(scanner.pos, "extra.record.attribute"); + throw new Scanner.SyntaxError(); + } + scanner.scan(); + parseRecord(); + break; + default: + env.error(scanner.pos, "field.expected"); + throw new Scanner.SyntaxError(); + } // end switch + } catch (Scanner.SyntaxError e) { + recoverField(); + } + memberAnnttns = null; + } + + /** + * Recover after a syntax error in the file. + * This involves discarding scanner.tokens until an EOF + * or a possible legal continuation is encountered. + */ + private void recoverFile() throws IOException { + while (true) { + env.traceln("recoverFile: scanner.token=" + scanner.token); + switch (scanner.token) { + case CLASS: + case INTERFACE: + // Start of a new source file statement, continue + return; + + case LBRACE: + match(Token.LBRACE, Token.RBRACE); + scanner.scan(); + break; + + case LPAREN: + match(Token.LPAREN, Token.RPAREN); + scanner.scan(); + break; + + case LSQBRACKET: + match(Token.LSQBRACKET, Token.RSQBRACKET); + scanner.scan(); + break; + + case EOF: + return; + + default: + // Don't know what to do, skip + scanner.scan(); + break; + } + } + } + + /** + * End class + */ + private void endClass() { + if (explicitcp) { + // Fix references in the constant pool (for explicitly coded CPs) + pool.fixRefsInPool(); + // Fix any bootstrap Method references too + cd.relinkBootstrapMethods(); + } + cd.endClass(); + clsDataList.add(cd); + cd = null; + } + + /** + * End module + */ + private void endModule() { + cd.endModule(moduleAttribute); + clsDataList.add(cd); + cd = null; + } + + final ClassData[] getClassesData() { + return ((ClassData[]) clsDataList.toArray(new ClassData[0])); + } + + /** + * Determines whether the JASM file is for a package-info class + * or for a module-info class. + *

+ * creates the correct kind of ClassData accordingly. + * + * @throws IOException + */ + private void parseJasmPackages() throws IOException { + try { + // starting annotations could either be + // a package annotation, or a class annotation + if (scanner.token == Token.ANNOTATION) { + if (cd == null) { + cd = new ClassData(env, currentCFV.clone()); + pool = cd.pool; + } + pkgAnnttns = annotParser.scanAnnotations(); + } + if (scanner.token == Token.PACKAGE) { + // Package statement + scanner.scan(); + int where = scanner.pos; + String id = parseIdent(); + parseVersionPkg(); + scanner.expect(SEMICOLON); + + if (pkg == null) { + pkg = id; + pkgPrefix = id + "/"; + } else { + env.error(where, "package.repeated"); + } + debugScan("[Parser.parseJasmPackages] {PARSED} package-prefix: " + pkgPrefix + " "); + } + } catch (Scanner.SyntaxError e) { + recoverFile(); + } + // skip bogus semi colons + while (scanner.token == SEMICOLON) { + scanner.scan(); + } + + // checks that we compile module or package compilation unit + if (scanner.token == Token.EOF) { + env.traceln("Scanner: EOF"); + String sourceName = env.getSimpleInputFileName(); + int mod = ACC_INTERFACE | ACC_ABSTRACT; + + // package-info + if (sourceName.endsWith("package-info.jasm")) { + env.traceln("Creating \"package-info.jasm\": package: " + pkg + " " + currentCFV.asString()); + + if (cd == null) { + cd = new ClassData(env, currentCFV.clone()); + pool = cd.pool; + } else { + cd.cfv = currentCFV.clone(); + } + ConstCell me = pool.FindCellClassByName(pkgPrefix + "package-info"); + + // Interface package-info should be marked synthetic and abstract + if (currentCFV.major_version() > 49) { + mod |= SYNTHETIC_ATTRIBUTE; + } + cd.init(mod, me, new ConstCell(0), null); + + if (pkgAnnttns != null) { + cd.addAnnotations(pkgAnnttns); + } + + endClass(); + } + return; + } + + if (pkg == null && pkgAnnttns != null) { // RemoveModules + clsAnnttns = pkgAnnttns; + pkgAnnttns = null; + } + } + + /** + * Parse an Jasm file. + */ + void parseFile() { + try { + // First, parse any package identifiers (and associated package annotations) + parseJasmPackages(); + + while (scanner.token != Token.EOF) { + // Second, parse any class identifiers (and associated class annotations) + try { + // Parse annotations + if (scanner.token == Token.ANNOTATION) { + if (cd == null) { + cd = new ClassData(env, currentCFV.clone()); + pool = cd.pool; + } else { + cd.cfv = currentCFV.clone(); + } + clsAnnttns = annotParser.scanAnnotations(); + } + + // Parse class modifiers + int mod = scanModifiers(); + if (mod == 0) { + switch (scanner.token) { + case OPEN: + case MODULE: + case CLASS: + case CPINDEX: + case STRINGVAL: + case IDENT: + // this is a class declaration anyway + break; + case SEMICOLON: + // Bogus semi colon + scanner.scan(); + continue; + default: + // no class declaration found + debugScan(" [Parser.parseFile]: "); + env.error(scanner.pos, "toplevel.expected"); + throw new Scanner.SyntaxError(); + } + } else if (Modifiers.isInterface(mod) && (scanner.token != Token.CLASS)) { + // rare syntactic sugar: + // interface == abstract interface class + mod |= ACC_ABSTRACT; + } + if (scanner.token == Token.MODULE || scanner.token == Token.OPEN) + parseModule(); + else + parseClass(mod); + clsAnnttns = null; + + } catch (Scanner.SyntaxError e) { + // KTL + env.traceln("^^^^^^^ Syntax Error ^^^^^^^^^^^^"); + if (scanner.debugFlag) + e.printStackTrace(); + if (e.isFatal()) { + break; + } + recoverFile(); + } + } + } catch (IOException e) { + env.error(scanner.pos, "io.exception", env.getSimpleInputFileName()); + } catch (Error er) { + er.printStackTrace(); + } + } //end parseFile + + @FunctionalInterface + interface NameSupplier { + String get() throws IOException; + } + + @FunctionalInterface + interface Method { + void call() throws IOException; + } + + /** + * The main compile error for the parser + */ + static class CompilerError extends Error { + + CompilerError(String message) { + super(message); + } + } +} //end Parser diff --git a/src/org/openjdk/asmtools/jasm/ParserAnnotation.java b/src/org/openjdk/asmtools/jasm/ParserAnnotation.java new file mode 100644 index 0000000..d9683fe --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ParserAnnotation.java @@ -0,0 +1,1085 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.jasm.TypeAnnotationTargetInfoData.*; + +import static org.openjdk.asmtools.jasm.JasmTokens.AnnotationType.isInvisibleAnnotationToken; +import static org.openjdk.asmtools.jasm.TypeAnnotationTypes.*; +import static org.openjdk.asmtools.jasm.JasmTokens.*; +import static org.openjdk.asmtools.jasm.ConstantPool.*; +import static org.openjdk.asmtools.jasm.Tables.*; +import java.io.IOException; +import java.util.ArrayList; +import java.util.TreeMap; + +/** + * ParserAnnotation + * + * ParserAnnotation is a parser class owned by Parser.java. It is primarily responsible + * for parsing Annotations (for classes, methods or fields). + * + * ParserAnnotation can parse the different types of Annotation Attributes: + * Runtime(In)Visible Annotations (JDK 6+) Default Annotations (JDK 6+) + * Runtime(In)VisibleParameter Annotations (JDK 7+) Runtime(In)VisibleType Annotations + * (JSR308, JDK8+) + */ +public class ParserAnnotation extends ParseBase { + + /*-------------------------------------------------------- */ + /* Annotation Inner Classes */ + /** + * AnnotationElemValue + * + * Used to store Annotation values + */ + static class AnnotationElemValue implements Data { + + AnnotationData annotation; + + AnnotationElemValue(AnnotationData annotation) { + this.annotation = annotation; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte('@'); + annotation.write(out); + } + + @Override + public int getLength() { + return 1 + annotation.getLength(); + } + } + + /** + * ClassElemValue + * + * Annotation Element value referring to a class + */ + static class ClassElemValue implements Data { + + ConstCell indx; + + ClassElemValue(ConstCell indx) { + this.indx = indx; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte('c'); + indx.write(out); + } + + @Override + public int getLength() { + return 3; + } + } + + /** + * ArrayElemValue + * + * Annotation Element value referring to an Array + */ + static class ArrayElemValue implements Data { + + ArrayList elemValues; + int arrayLength = 0; + + ArrayElemValue() { + this.elemValues = new ArrayList<>(); + } + + void add(Data elemValue) { + elemValues.add(elemValue); + arrayLength += elemValue.getLength(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte('['); + out.writeShort(elemValues.size()); + + for (Data eval : elemValues) { + eval.write(out); + } + } + + @Override + public int getLength() { + return 3 + arrayLength; + } + } + + /** + * ConstElemValue + * + * Annotation Element value referring to a Constant + */ + static class ConstElemValue implements Data { + + char tag; + ConstCell indx; + + ConstElemValue(char tag, ConstCell indx) { + this.tag = tag; + this.indx = indx; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(tag); + indx.write(out); + } + + @Override + public int getLength() { + return 3; + } + } + + /** + * EnumElemValue + * + * Element Value for Enums + */ + static class EnumElemValue implements Data { + + ConstCell type; + ConstCell value; + + EnumElemValue(ConstCell type, ConstCell value) { + this.type = type; + this.value = value; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte('e'); + type.write(out); + value.write(out); + } + + @Override + public int getLength() { + return 5; + } + } + + /** + * local handles on the scanner, main parser, and the error reporting env + */ + private static TTVis ttVisitor; + + protected ParserAnnotation(Scanner scanner, Parser parser, Environment env) { + super.init(scanner, parser, env); + ttVisitor = new TTVis(); + ttVisitor.init(env, scanner); + } + + protected void scanParamName(int totalParams, int paramNum, MethodData curMethod) throws IOException { + debugScan(" - - - > [ParserAnnotation.scanParamName]: Begin "); + scanner.scan(); + scanner.expect(Token.LBRACE); + // First scan the Name (String, or CPX to name) + ConstCell nameCell; + if ((scanner.token == Token.IDENT) || scanner.checkTokenIdent()) { + // Got a Class Name + nameCell = parser.parseName(); + } else if (scanner.token == Token.CPINDEX) { + int cpx = scanner.intValue; + nameCell = parser.pool.getCell(cpx); + // check the constant + ConstValue nameCellValue = nameCell.ref; + if (!(nameCellValue instanceof ConstValue_String)) { + // throw an error + env.error(scanner.pos, "paramname.constnum.invaltype", cpx); + throw new Scanner.SyntaxError(); + } + + } else { + // throw scan error - unexpected token + env.error(scanner.pos, "paramname.token.unexpected", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + + // Got the name cell. Next, scan the access flags + int mod = parser.scanModifiers(); + + scanner.expect(Token.RBRACE); + + curMethod.addMethodParameter(totalParams, paramNum, nameCell, mod); + + debugScan(" - - - > [ParserAnnotation.scanParamName]: End "); + } + + /** + * The main entry for parsing an annotation list. + * + * @return An ArrayList of parsed annotations + * @throws IOException + */ + ArrayList scanAnnotations() throws IOException { + ArrayList list = new ArrayList<>(); + while (scanner.token == Token.ANNOTATION) { + if ( JasmTokens.AnnotationType.isAnnotationToken(scanner.stringValue)) { + list.add(parseAnnotation()); + } else if (JasmTokens.AnnotationType.isTypeAnnotationToken(scanner.stringValue)) { + list.add(parseTypeAnnotation()); + } else { + return null; + } + } + if (list.size() > 0) { + return list; + } else { + return null; + } + } + + /** + * parseDefaultAnnotation + * + * parses a default Annotation attribute + * + * @return the parsed Annotation Attribute + * @throws org.openjdk.asmtools.jasm.Scanner.SyntaxError + * @throws IOException + */ + protected DefaultAnnotationAttr parseDefaultAnnotation() throws Scanner.SyntaxError, IOException { + scanner.scan(); + DefaultAnnotationAttr attr = null; + Data value = null; + scanner.expect(Token.LBRACE); + + if ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + value = scanAnnotationData("default"); + } + scanner.expect(Token.RBRACE); + attr = new DefaultAnnotationAttr(parser.cd, + AttrTag.ATT_AnnotationDefault.parsekey(), + value); + return attr; + } + + /** + * parseParamAnnots + * + * Parses Parameter Annotations attributes. + * + * @param _totalParams + * @param curMethod + * @throws org.openjdk.asmtools.jasm.Scanner.SyntaxError + * @throws IOException + */ + protected void parseParamAnnots(int _totalParams, MethodData curMethod) throws Scanner.SyntaxError, IOException { + debugScan(" - - - > [ParserAnnotation.parseParamAnnots]: Begin, totalParams = " + _totalParams + " "); + // The _method thinks there are N+1 params in the signature + // (N = total params in the call list) + 1 (return value) + int totalParams = _totalParams - 1; + TreeMap> pAnnots = new TreeMap<>(); + + while (scanner.token == Token.INTVAL) { + // Create the Parameter Array for Param Annotations + + // Do something with Parameter annotations + // -------------------- + // First - validate that the parameter number (integer) + // (eg >= 0, < numParams, and param num is not previously set) + int paramNum = scanner.intValue; + Integer iParamNum = Integer.valueOf(paramNum); + if (paramNum < 0 || paramNum >= totalParams) { + //invalid Parameter number. Throw an error. + env.error(scanner.pos, "invalid.paramnum", paramNum); + } + if (pAnnots.get(iParamNum) != null) { + // paramter is already populated with annotations/pnames, Throw an error. + env.error(scanner.pos, "duplicate.paramnum", paramNum); + } + // 2nd - Parse the COLON (invalid if not present) + scanner.scan(); + scanner.expect(Token.COLON); + + // 3rd - parse either an optional ParamName, or a list of annotations + if (scanner.token == Token.PARAM_NAME) { + //parse the ParamName + scanParamName(totalParams, iParamNum, curMethod); + } + + // 4th - parse each Annotation (followed by comma, followed by annotation + // assign array of annotations to param array + if (scanner.token == Token.ANNOTATION) { + ArrayList pAnnot = scanAnnotations(); + pAnnots.put(iParamNum, pAnnot); + + for (AnnotationData data : pAnnot) { + curMethod.addParamAnnotation(totalParams, paramNum, data); + } + } + + } + } + + /* ************************* Private Members *************************** */ + /** + * parseTypeAnnotation + * + * parses an individual annotation. + * + * @return a parsed annotation. + * @throws IOException + */ + private AnnotationData parseTypeAnnotation() throws Scanner.SyntaxError, IOException { + boolean invisible = isInvisibleAnnotationToken(scanner.stringValue); + scanner.scan(); + debugScan(" [ParserAnnotation.parseTypeAnnotation]: id = " + scanner.stringValue + " "); + String annoName = "L" + scanner.stringValue + ";"; + TypeAnnotationData anno = new TypeAnnotationData(parser.pool.FindCellAsciz(annoName), invisible); + scanner.scan(); + debugScan(" [ParserAnnotation.parseTypeAnnotation]:new type annotation: " + annoName + " "); + + scanner.expect(Token.LBRACE); + + // Scan the usual annotation data + _scanAnnotation(anno); + + // scan the Target (u1: target_type, union{...}: target_info) + _scanTypeTarget(anno); + + if( scanner.token != Token.RBRACE ) { + // scan the Location (type_path: target_path) + _scanTargetPath(anno); + } + + scanner.expect(Token.RBRACE); + return anno; + } + + /** + * scanAnnotation + * + * parses an individual annotation. + * + * @return a parsed annotation. + * @throws IOException + */ + private AnnotationData parseAnnotation() throws Scanner.SyntaxError, IOException { + debugScan(" - - - > [ParserAnnotation.parseAnnotation]: Begin "); + boolean invisible = isInvisibleAnnotationToken(scanner.stringValue); + scanner.scan(); + String annoName = "L" + scanner.stringValue + ";"; + + AnnotationData anno = new AnnotationData(parser.pool.FindCellAsciz(annoName), invisible); + scanner.scan(); + debugScan("[ParserAnnotation.parseAnnotation]: new annotation: " + annoName); + _scanAnnotation(anno); + + return anno; + } + + /** + * _scanAnnotation + * + * parses an individual annotation-data. + * + * @return a parsed annotation. + * @throws IOException + */ + private void _scanAnnotation(AnnotationData annotData) throws Scanner.SyntaxError, IOException { + debugScan(" - - - > [ParserAnnotation._scanAnnotation]: Begin"); + scanner.expect(Token.LBRACE); + + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + ConstCell nameCell = parser.parseName(); + scanner.expect(Token.ASSIGN); + + ConstValue cellref = nameCell.ref; + if (cellref.tag != ConstType.CONSTANT_UTF8) { + throw new Scanner.SyntaxError(); + } + String name = ((ConstValue_String) cellref)._toString(); + debugScan(" [ParserAnnotation._scanAnnotation]: Annot - Field Name: " + name); + Data data = scanAnnotationData(name); + annotData.add(new AnnotationData.ElemValuePair(nameCell, data)); + + // consume tokens inbetween annotation fields + if (scanner.token == Token.COMMA) { + scanner.scan(); + } + } + scanner.expect(Token.RBRACE); + } + + /** + * _scanAnnotation + * + * parses an individual annotation-data. + * + * @return a parsed annotation. + * @throws IOException + */ + private void _scanTypeTarget(TypeAnnotationData annotData) throws Scanner.SyntaxError, IOException { + debugScan(" [ParserAnnotation._scanTypeTarget]: Begin "); + scanner.expect(Token.LBRACE); + + //Scan the target_type and the target_info + scanner.expect(Token.IDENT); + debugScan(" [ParserAnnotation._scanTypeTarget]: TargetType: " + scanner.idValue); + ETargetType targetType = ETargetType.getTargetType(scanner.idValue); + if (targetType == null) { + env.error(scanner.pos, "incorrect.typeannot.target", scanner.idValue); + throw new Scanner.SyntaxError(); + } + + debugScan(" [ParserAnnotation._scanTypeTarget]: Got TargetType: " + targetType); + + if (ttVisitor.scanner == null) { + ttVisitor.scanner = scanner; + } + ttVisitor.visitExcept(targetType); + + annotData.targetInfo = ttVisitor.getTargetInfo(); + annotData.targetType = targetType; + debugScan(" [ParserAnnotation._scanTypeTarget]: Got TargetInfo: " + annotData.targetInfo); + + scanner.expect(Token.RBRACE); + } + + /** + * TTVis + * + * Target Type visitor, used for constructing the target-info within a type + * annotation. visitExcept() is the entry point. ti is the constructed target info. + */ + private static class TTVis extends TypeAnnotationTypes.TypeAnnotationTargetVisitor { + + private TypeAnnotationTargetInfoData ti; + private IOException IOProb; + private Scanner.SyntaxError SyProb; + private Scanner scanner; + private Environment env; + + public TTVis() { + super(); + reset(); + } + + public void init(Environment en, Scanner scn) { + if (scanner == null) { + scanner = scn; + } + if (env == null) { + env = en; + } + } + + public final void reset() { + ti = null; + IOProb = null; + SyProb = null; + } + + //This is the entry point for a visitor that tunnels exceptions + public void visitExcept(ETargetType tt) throws IOException, Scanner.SyntaxError { + IOProb = null; + SyProb = null; + ti = null; + + visit(tt); + + if (IOProb != null) { + throw IOProb; + } + + if (SyProb != null) { + throw SyProb; + } + } + + public TypeAnnotationTargetInfoData getTargetInfo() { + return ti; + } + + // this fn gathers intvals, and tunnels any exceptions thrown by + // the scanner + private int scanIntVal(ETargetType tt) { + int ret = -1; + if (scanner.token == Token.INTVAL) { + ret = scanner.intValue; + try { + scanner.scan(); + } catch (IOException e) { + IOProb = e; + } catch (Scanner.SyntaxError e) { + SyProb = e; + } + } else { + env.error(scanner.pos, "incorrect.typeannot.targtype.int", tt.parseKey(), scanner.token); + SyProb = new Scanner.SyntaxError(); + } + return ret; + } + + // this fn gathers intvals, and tunnels any exceptions thrown by + // the scanner + private String scanStringVal(ETargetType tt) { + String ret = ""; + if (scanner.token == Token.STRINGVAL) { + ret = scanner.stringValue; + try { + scanner.scan(); + } catch (IOException e) { + IOProb = e; + } catch (Scanner.SyntaxError e) { + SyProb = e; + } + } else { + env.error(scanner.pos, "incorrect.typeannot.targtype.string", tt.parseKey(), scanner.token); + SyProb = new Scanner.SyntaxError(); + } + return ret; + } + + // this fn gathers intvals, and tunnels any exceptions thrown by + // the scanner + private void scanBrace(boolean left) { + try { + scanner.expect(left ? Token.LBRACE : Token.RBRACE); + } catch (IOException e) { + IOProb = e; + } catch (Scanner.SyntaxError e) { + SyProb = e; + } + } + + private boolean error() { + return IOProb != null || SyProb != null; + } + + @Override + public void visit_type_param_target(ETargetType tt) { + env.traceln("Type Param Target: "); + int byteval = scanIntVal(tt); // param index + if (!error()) { + ti = new TypeAnnotationTargetInfoData.type_parameter_target(tt, byteval); + } + } + + @Override + public void visit_supertype_target(ETargetType tt) { + env.traceln("SuperType Target: "); + int shortval = scanIntVal(tt); // type index + if (!error()) { + ti = new TypeAnnotationTargetInfoData.supertype_target(tt, shortval); + } + } + + @Override + public void visit_typeparam_bound_target(ETargetType tt) { + env.traceln("TypeParam Bound Target: "); + int byteval1 = scanIntVal(tt); // param index + if (error()) { + return; + } + int byteval2 = scanIntVal(tt); // bound index + if (error()) { + return; + } + ti = new TypeAnnotationTargetInfoData.type_parameter_bound_target(tt, byteval1, byteval2); + } + + @Override + public void visit_empty_target(ETargetType tt) { + env.traceln("Empty Target: "); + if (!error()) { + ti = new TypeAnnotationTargetInfoData.empty_target(tt); + } + } + + @Override + public void visit_methodformalparam_target(ETargetType tt) { + env.traceln("MethodParam Target: "); + int byteval = scanIntVal(tt); // param index + if (!error()) { + ti = new formal_parameter_target(tt, byteval); + } + } + + @Override + public void visit_throws_target(ETargetType tt) { + env.traceln("Throws Target: "); + int shortval = scanIntVal(tt); // exception index + if (!error()) { + ti = new throws_target(tt, shortval); + } + } + + @Override + public void visit_localvar_target(ETargetType tt) { + env.traceln("LocalVar Target: "); + localvar_target locvartab = new localvar_target(tt, 0); + ti = locvartab; + + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + // consume the left brace + scanBrace(true); + if (error()) { + return; + } + // scan the local var triple + int shortval1 = scanIntVal(tt); // startPC + if (error()) { + return; + } + int shortval2 = scanIntVal(tt); // length + if (error()) { + return; + } + int shortval3 = scanIntVal(tt); // CPX + locvartab.addEntry(shortval1, shortval2, shortval3); + scanBrace(false); + if (error()) { + return; + } + } + } + + @Override + public void visit_catch_target(ETargetType tt) { + env.traceln("Catch Target: "); + int shortval = scanIntVal(tt); // catch index + + ti = new catch_target(tt, shortval); + } + + @Override + public void visit_offset_target(ETargetType tt) { + env.traceln("Offset Target: "); + int shortval = scanIntVal(tt); // offset index + if (!error()) { + ti = new offset_target(tt, shortval); + } + } + + @Override + public void visit_typearg_target(ETargetType tt) { + env.traceln("TypeArg Target: "); + int shortval = scanIntVal(tt); // offset + if (error()) { + return; + } + int byteval = scanIntVal(tt); // type index + if (error()) { + return; + } + ti = new type_argument_target(tt, shortval, byteval); + } + + } + + /** + * _scanTargetPath + * + * parses and fills the type_path structure (4.7.20.2) + * + * type_path { + * u1 path_length; + * { u1 type_path_kind; + * u1 type_argument_index; + * } path[path_length]; + * } + * + * @throws Scanner.SyntaxError, IOException + */ + private void _scanTargetPath(TypeAnnotationData annotData) throws Scanner.SyntaxError, IOException { + // parse the location info + scanner.expect(Token.LBRACE); + + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + TypePathEntry tpe = _scanTypePathEntry(); + annotData.addTypePathEntry(tpe); + // throw away comma + if (scanner.token == Token.COMMA) { + scanner.scan(); + } + } + + scanner.expect(Token.RBRACE); + } + + /** + * _scanTypeLocation + * + * parses a path entry of the type_path. + * + * { u1 type_path_kind; + * u1 type_argument_index; + * } + * + * @return a parsed type path. + * @throws Scanner.SyntaxError, IOException + */ + private TypePathEntry _scanTypePathEntry() throws Scanner.SyntaxError, IOException { + TypePathEntry tpe; + + if ( (scanner.token != Token.EOF) && scanner.token.possibleTypePathKind() ) { + EPathKind pathKind = EPathKind.getPathKind(scanner.stringValue); + if (pathKind == EPathKind.TYPE_ARGUMENT) { + scanner.scan(); + // need to scan the index + // Take the form: TYPE_ARGUMENT{#} + scanner.expect(Token.LBRACE); + int index = 0; + if ((scanner.token != Token.EOF) && (scanner.token == Token.INTVAL)) { + index = scanner.intValue; + scanner.scan(); + } else { + // incorrect Arg index + env.error(scanner.pos, "incorrect.typeannot.pathentry.argindex", scanner.token); + throw new Scanner.SyntaxError(); + } + tpe = new TypePathEntry(pathKind, index); + scanner.expect(Token.RBRACE); + } else { + tpe = new TypePathEntry(pathKind, 0); + scanner.scan(); + } + } else { + // unexpected Type Path + env.error(scanner.pos, "incorrect.typeannot.pathentry", scanner.token); + throw new Scanner.SyntaxError(); + } + + return tpe; + } + + /** + * scanAnnotationArray + * + * Scans an Array of annotations. + * + * @param name Name of the annotation + * @return Array Element + * @throws IOException if scanning errors exist + */ + private ArrayElemValue scanAnnotationArray(String name) throws IOException { + scanner.scan(); + ArrayElemValue arrayElem = new ArrayElemValue(); + + while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) { + Data data = scanAnnotationData(name + " {}"); + arrayElem.add(data); + + // consume tokens inbetween annotation fields + if (scanner.token == Token.COMMA) { + scanner.scan(); + } + } + + scanner.expect(Token.RBRACE); + return arrayElem; + } + + /** + * scanAnnotationEnum + * + * Scans an annotation enum val. + * + * @param name Annotation Name + * @return Constant element value for the Class Annotation. + * @throws IOException + */ + private Data scanAnnotationClass(String name) throws IOException { + Data constVal = null; + // scan the next identifier. + // if it is an Ident, consume it as the class name. + scanner.scan(); + switch (scanner.token) { + case IDENT: + env.traceln("[AnnotationParser.scanAnnotationData]:: Constant Class Field: " + name + " = " + scanner.stringValue); + //need to encode the stringval as an (internal) descriptor. + String desc = parser.encodeClassString(scanner.stringValue); + + // note: for annotations, a class field points to a string with the class descriptor. + constVal = new ConstElemValue('c', parser.pool.FindCellAsciz(desc)); + scanner.scan(); + break; + case CPINDEX: + // could be a reference to a class name + env.traceln("[AnnotationParser.scanAnnotationData]:: Constant Class Field: " + name + " = " + scanner.stringValue); + Integer ConstNmCPX = Integer.valueOf(scanner.stringValue); + constVal = new ClassElemValue(parser.pool.getCell(ConstNmCPX)); + scanner.scan(); + break; + default: + env.error(scanner.pos, "incorrect.annot.class", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + + return constVal; + } + + /** + * scanAnnotationEnum + * + * Scans an annotation enum val. + * + * @param name Annotation Name + * @return Enumeration Element Value + * @throws IOException for scanning errors. + */ + private EnumElemValue scanAnnotationEnum(String name) throws IOException { + scanner.scan(); + EnumElemValue enumval = null; + switch (scanner.token) { + case IDENT: + // could be a string identifying enum class and name + String enumClassName = scanner.stringValue; + scanner.scan(); + // could be a string identifying enum class and name + switch (scanner.token) { + case IDENT: + // could be a string identifying enum class and name + String enumTypeName = scanner.stringValue; + env.traceln("[AnnotationParser.scanAnnotationEnum]:: Constant Enum Field: " + name + " = " + enumClassName + " " + enumTypeName); + String encodedClass = parser.encodeClassString(enumClassName); + ConstElemValue classConst = new ConstElemValue('s', parser.pool.FindCellAsciz(encodedClass)); + ConstElemValue typeConst = new ConstElemValue('s', parser.pool.FindCellAsciz(enumTypeName)); + enumval = new EnumElemValue(classConst.indx, typeConst.indx); + scanner.scan(); + break; + + default: + env.error(scanner.pos, "incorrect.annot.enum", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + break; + case CPINDEX: + Integer typeNmCPX = Integer.valueOf(scanner.stringValue); + scanner.scan(); + //need two indexes to form a proper enum + switch (scanner.token) { + case CPINDEX: + Integer ConstNmCPX = Integer.valueOf(scanner.stringValue); + env.traceln("[AnnotationParser.scanAnnotationEnum]:: Enumeration Field: " + name + " = #" + typeNmCPX + " #" + ConstNmCPX); + enumval = new EnumElemValue(parser.pool.getCell(typeNmCPX), parser.pool.getCell(ConstNmCPX)); + scanner.scan(); + break; + default: + env.error(scanner.pos, "incorrect.annot.enum.cpx"); + throw new Scanner.SyntaxError(); + } + break; + } + + return enumval; + } + + /** + * scanAnnotationData + * + * parses the internals of an annotation. + * + * @param name Annotation Name + * @return a Data data structure containing the annotation data. + * @throws IOException for scanning errors. + */ + private Data scanAnnotationData(String name) throws IOException { + Data data = null; + switch (scanner.token) { + // This handles the Annotation types (as normalized in the constant pool) + // Some primitive types (Boolean, char, short, byte) are identified by a keyword. + case INTVAL: + env.traceln("[AnnotationParser.scanAnnotationData]:: Integer Field: " + name + " = " + scanner.intValue); + data = new ConstElemValue('I', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, scanner.intValue)); + scanner.scan(); + break; + case DOUBLEVAL: + env.traceln("[AnnotationParser.scanAnnotationData]:: Double Field: " + name + " = " + scanner.doubleValue); + double dval = scanner.doubleValue; + long ivdal = Double.doubleToLongBits(dval); + Long val = ivdal; + data = new ConstElemValue('D', parser.pool.FindCell(ConstType.CONSTANT_DOUBLE, val)); + scanner.scan(); + break; + case FLOATVAL: + env.traceln("[AnnotationParser.scanAnnotationData]:: Float Field: " + name + " = " + scanner.floatValue); + float fval = scanner.floatValue; + int ifval = Float.floatToIntBits(fval); + Integer val1 = ifval; + data = new ConstElemValue('F', parser.pool.FindCell(ConstType.CONSTANT_FLOAT, val1)); + scanner.scan(); + break; + case LONGVAL: + env.traceln("[AnnotationParser.scanAnnotationData]:: Long Field: " + name + " = " + scanner.longValue); + data = new ConstElemValue('J', parser.pool.FindCell(ConstType.CONSTANT_LONG, scanner.longValue)); + scanner.scan(); + break; + case STRINGVAL: + env.traceln("[AnnotationParser.scanAnnotationData]:: String Field: " + name + " = " + scanner.stringValue); + data = new ConstElemValue('s', parser.pool.FindCellAsciz(scanner.stringValue)); + scanner.scan(); + break; + case CLASS: + env.traceln("[AnnotationParser.scanAnnotationData]:: Class) keyword: " + scanner.stringValue); + data = scanAnnotationClass(name); + break; + case ENUM: + // scan the next two identifiers (eg ident.ident), or 2 CPRefs. + // if it is an Ident, use consume it as the class name. + env.traceln("[AnnotationParser.scanAnnotationData]:: Enum) keyword: " + scanner.stringValue); + data = scanAnnotationEnum(name); + break; + case IDENT: + env.traceln("[AnnotationParser.scanAnnotationData]:: JASM Keyword: (annotation field name: " + name + ") keyword: " + scanner.stringValue); + data = scanAnnotationIdent(scanner.stringValue, name); + break; + case ANNOTATION: + env.traceln("[AnnotationParser.scanAnnotationData]:: Annotation Field: " + name + " = " + scanner.stringValue); + data = new AnnotationElemValue(parseAnnotation()); + break; + case LBRACE: + env.traceln("[AnnotationParser.scanAnnotationData]:: Annotation Array Field: " + name); + data = scanAnnotationArray(name); + break; + default: + env.error(scanner.pos, "incorrect.annot.token", scanner.token); + throw new Scanner.SyntaxError(); + } + + return data; + } + + /** + * scanAnnotationIdent + * + * parses the identifier of an annotation. + * + * @param ident Basic Type identifier + * @param name Annotation Name + * @return Basic Type Annotation data + * @throws IOException if scanning errors occur + */ + private Data scanAnnotationIdent(String ident, String name) throws IOException { + // Handle JASM annotation Keyword Identifiers + Data data; + BasicType type = basictype(ident); + switch (type) { + + case T_BOOLEAN: + // consume the keyword, get the value + scanner.scan(); + switch (scanner.token) { + case INTVAL: + // Handle Boolean value in integer form + env.traceln("Boolean Field: " + name + " = " + scanner.intValue); + Integer val = scanner.intValue; + if (val > 1 || val < 0) { + env.traceln("Warning: Boolean Field: " + name + " value is not 0 or 1, value = " + scanner.intValue); + } + data = new ConstElemValue('Z', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val)); + scanner.scan(); + break; + case IDENT: + // handle boolean value with true/false keywords + int val1; + switch (scanner.stringValue) { + case "true": + val1 = 1; + break; + case "false": + val1 = 0; + break; + default: + throw new IOException("Incorrect Annotation (boolean), expected true/false), got \"" + scanner.stringValue + "\"."); + } + env.traceln("Boolean Field: " + name + " = " + scanner.stringValue); + data = new ConstElemValue('Z', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val1)); + scanner.scan(); + break; + default: + env.error(scanner.pos, "incorrect.annot.bool", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + break; + case T_BYTE: + // consume the keyword, get the value + scanner.scan(); + switch (scanner.token) { + case INTVAL: + env.traceln("Byte Field: " + name + " = " + scanner.intValue); + Integer val = scanner.intValue; + if (val > 0xFF) { + env.traceln("Warning: Byte Field: " + name + " value is greater than 0xFF, value = " + scanner.intValue); + } + data = new ConstElemValue('B', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val)); + scanner.scan(); + break; + default: + env.error(scanner.pos, "incorrect.annot.byte", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + break; + case T_CHAR: + // consume the keyword, get the value + scanner.scan(); + switch (scanner.token) { + case INTVAL: + env.traceln("Char Field: " + name + " = " + scanner.intValue); + Integer val = scanner.intValue; + // Bounds check? + data = new ConstElemValue('C', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val)); + scanner.scan(); + break; + default: + env.error(scanner.pos, "incorrect.annot.char", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + break; + case T_SHORT: + // consume the keyword, get the value + scanner.scan(); + switch (scanner.token) { + case INTVAL: + env.traceln("Short Field: " + name + " = " + scanner.intValue); + Integer val = scanner.intValue; + if (val > 0xFFFF) { + env.traceln("Warning: Short Field: " + name + " value is greater than 0xFFFF, value = " + scanner.intValue); + } + data = new ConstElemValue('S', parser.pool.FindCell(ConstType.CONSTANT_INTEGER, val)); + scanner.scan(); + break; + default: + env.error(scanner.pos, "incorrect.annot.short", scanner.stringValue); + throw new Scanner.SyntaxError(); + } + break; + default: + env.error(scanner.pos, "incorrect.annot.keyword", ident); + throw new Scanner.SyntaxError(); + } + return data; + } +} diff --git a/src/org/openjdk/asmtools/jasm/ParserCP.java b/src/org/openjdk/asmtools/jasm/ParserCP.java new file mode 100644 index 0000000..756c041 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ParserCP.java @@ -0,0 +1,638 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.function.BiFunction; + +import static org.openjdk.asmtools.jasm.JasmTokens.Token; +import static org.openjdk.asmtools.jasm.Tables.*; + +/** + * ParserCP + * + * ParseCP is a parser class owned by Parser.java. It is primarily responsible for parsing + * the constant pool and constant declarations. + */ +public class ParserCP extends ParseBase { + + /** + * local handles on the scanner, main parser, and the error reporting env + */ + /** + * Visitor object + */ + private ParserCPVisitor pConstVstr; + /** + * counter of left braces + */ + private int lbrace = 0; + + + /** + * main constructor + * + * @param scanner + * @param parser + * @param env + */ + protected ParserCP(Scanner scanner, Parser parser, Environment env) { + super.init(scanner, parser, env); + pConstVstr = new ParserCPVisitor(); + } + + /** + * ParserCPVisitor + * + * This inner class overrides a constant pool visitor to provide specific parsing + * instructions (per method) for each type of Constant. + * + * Note: since the generic visitor throws no exceptions, this derived class tunnels + * the exceptions, rethrown in the visitEcept method. + */ + class ParserCPVisitor extends ConstantPool.CPTagVisitor { + + private IOException IOProb; + private Scanner.SyntaxError SyProb; + + + public ParserCPVisitor() { + IOProb = null; + SyProb = null; + } + + //This is the entry point for a visitor that tunnels exceptions + public ConstantPool.ConstValue visitExcept(ConstType tag) throws IOException, Scanner.SyntaxError { + IOProb = null; + SyProb = null; + debugStr("------- [ParserCPVisitor.visitExcept]: "); + ConstantPool.ConstValue ret = visit(tag); + + if (IOProb != null) { + throw IOProb; + } + + if (SyProb != null) { + throw SyProb; + } + + return ret; + } + + @Override + public ConstantPool.ConstValue visitUTF8(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitUTF8]: "); + try { + scanner.expect(Token.STRINGVAL); + } catch (IOException e) { + IOProb = e; + } + ConstantPool.ConstValue_String obj + = new ConstantPool.ConstValue_String(scanner.stringValue); + return obj; + } + + @Override + public ConstantPool.ConstValue visitInteger(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitInteger]: "); + ConstantPool.ConstValue_Integer obj; + int v = 0; + try { + if (scanner.token == Token.BITS) { + scanner.scan(); + scanner.inBits = true; + } + v = scanner.intValue * scanner.sign; + scanner.expect(Token.INTVAL); + } catch (IOException e) { + IOProb = e; + } + obj = new ConstantPool.ConstValue_Integer(tag, v); + return obj; + } + + @Override + public ConstantPool.ConstValue visitLong(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitLong]: "); + ConstantPool.ConstValue_Long obj = null; + try { + long v; + if (scanner.token == Token.BITS) { + scanner.scan(); + scanner.inBits = true; + } + switch (scanner.token) { + case INTVAL: + v = scanner.intValue; + break; + case LONGVAL: + v = scanner.longValue; + break; + default: + env.error(scanner.prevPos, "token.expected", "Integer"); + throw new Scanner.SyntaxError(); + } + obj = new ConstantPool.ConstValue_Long(tag, v * scanner.sign); + scanner.scan(); + } catch (IOException e) { + IOProb = e; + } catch (Scanner.SyntaxError e) { + SyProb = e; + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitFloat(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitFloat]: "); + ConstantPool.ConstValue_Integer obj = null; + try { + int v; + float f; + scanner.inBits = false; // this needs to be initialized for each float! + if (scanner.token == Token.BITS) { + scanner.scan(); + scanner.inBits = true; + } +i2f: { + switch (scanner.token) { + case INTVAL: + if (scanner.inBits) { + v = scanner.intValue; + break i2f; + } else { + f = (float) scanner.intValue; + break; + } + case FLOATVAL: + f = scanner.floatValue; + break; + case DOUBLEVAL: + f = (float) scanner.doubleValue; // to be excluded? + break; + case INF: + f = Float.POSITIVE_INFINITY; + break; + case NAN: + f = Float.NaN; + break; + default: + env.traceln("token=" + scanner.token); + env.error(scanner.pos, "token.expected", ""); + throw new Scanner.SyntaxError(); + } + v = Float.floatToIntBits(f); + } + if (scanner.sign == -1) { + v = v ^ 0x80000000; + } + obj = new ConstantPool.ConstValue_Integer(tag, v); + scanner.scan(); + } catch (IOException e) { + IOProb = e; + } catch (Scanner.SyntaxError e) { + SyProb = e; + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitDouble(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitDouble]: "); + ConstantPool.ConstValue_Long obj = null; + try { + long v; + double d; + if (scanner.token == Token.BITS) { + scanner.scan(); + scanner.inBits = true; + } +d2l: { + switch (scanner.token) { + case INTVAL: + if (scanner.inBits) { + v = scanner.intValue; + break d2l; + } else { + d = scanner.intValue; + break; + } + case LONGVAL: + if (scanner.inBits) { + v = scanner.longValue; + break d2l; + } else { + d = (double) scanner.longValue; + break; + } + case FLOATVAL: + d = scanner.floatValue; + break; + case DOUBLEVAL: + d = scanner.doubleValue; + break; + case INF: + d = Double.POSITIVE_INFINITY; + break; + case NAN: + d = Double.NaN; + break; + default: + env.error(scanner.pos, "token.expected", "Double"); + throw new Scanner.SyntaxError(); + } + v = Double.doubleToLongBits(d); + } + if (scanner.sign == -1) { + v = v ^ 0x8000000000000000L; + } + obj = new ConstantPool.ConstValue_Long(tag, v); + scanner.scan(); + } catch (IOException e) { + IOProb = e; + } catch (Scanner.SyntaxError e) { + SyProb = e; + } + return obj; + } + + private ConstantPool.ConstCell visitName(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitName]: "); + ConstantPool.ConstCell obj = null; + try { + obj = parser.parseName(); + } catch (IOException e) { + IOProb = e; + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitMethodtype(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitMethodtype]: "); + ConstantPool.ConstValue_Cell obj = null; + ConstantPool.ConstCell cell = visitName(tag); + if (IOProb == null) { + obj = new ConstantPool.ConstValue_Cell(tag, cell); + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitString(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitString]: "); + ConstantPool.ConstValue_Cell obj = null; + ConstantPool.ConstCell cell = visitName(tag); + if (IOProb == null) { + obj = new ConstantPool.ConstValue_Cell(tag, cell); + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitClass(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitClass]: "); + ConstantPool.ConstValue_Cell obj = null; + try { + ConstantPool.ConstCell cell = parser.parseClassName(true); + obj = new ConstantPool.ConstValue_Cell(tag, cell); + } catch (IOException e) { + IOProb = e; + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitMethodhandle(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitMethodHandle]: "); + ConstantPool.ConstValue_Pair obj = null; + try { + ConstantPool.ConstCell refCell; + ConstantPool.ConstCell subtagCell; + SubTag subtag; + // MethodHandle [INVOKESUBTAG|INVOKESUBTAG_INDEX] : CONSTANT_FIELD | [FIELDREF|METHODREF|INTERFACEMETHODREF] + if (scanner.token == Token.INTVAL) { + // INVOKESUBTAG_INDEX + // Handle explicit constant pool form + subtag = subtag(scanner.intValue); + subtagCell = new ConstantPool.ConstCell(subtag.value()); + scanner.scan(); + scanner.expect(Token.COLON); + if (scanner.token == Token.CPINDEX) { + // CONSTANT_FIELD + int cpx = scanner.intValue; + refCell = parser.pool.getCell(cpx); + scanner.scan(); + } else { + // [FIELDREF|METHODREF|INTERFACEMETHODREF] + refCell = parser.parseMethodHandle(subtag); + } + } else { + // INVOKESUBTAG : REF_INVOKEINTERFACE, REF_NEWINVOKESPECIAL, ... + // normal JASM + subtag = parser.parseSubtag(); + subtagCell = new ConstantPool.ConstCell(subtag.value()); + scanner.expect(Token.COLON); + if (scanner.token == Token.CPINDEX) { + // CODETOOLS-7901522: Jasm doesn't allow to create REF_invoke* referring an InterfaceMethod + // Parsing the case when refCell is CP index (#1) + // const #1 = InterfaceMethod m:"()V"; + // const #2 = MethodHandle REF_invokeSpecial:#1; + int cpx = scanner.intValue; + refCell = parser.pool.getCell(cpx); + scanner.scan(); + } else { + refCell = parser.parseMethodHandle(subtag); + } + } + obj = new ConstantPool.ConstValue_Pair(tag, subtagCell, refCell); + } catch (IOException e) { + IOProb = e; + } + return obj; + } + + private ConstantPool.ConstValue_Pair visitMember(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitMember]: "); + ConstantPool.ConstValue_Pair obj = null; + try { + Token prevtoken = scanner.token; + ConstantPool.ConstCell firstName, ClassCell, NameCell, NapeCell; + firstName = parser.parseClassName(false); + if (scanner.token == Token.FIELD) { // DOT + scanner.scan(); + if (prevtoken == Token.CPINDEX) { + ClassCell = firstName; + } else { + ClassCell = parser.pool.FindCell(ConstType.CONSTANT_CLASS, firstName); + } + NameCell = parser.parseName(); + } else { + // no class provided - assume current class + ClassCell = parser.cd.me; + NameCell = firstName; + } + if (scanner.token == Token.COLON) { + // name and type separately + scanner.scan(); + NapeCell = parser.pool.FindCell(ConstType.CONSTANT_NAMEANDTYPE, NameCell, parser.parseName()); + } else { + // name and type as single name + NapeCell = NameCell; + } + obj = new ConstantPool.ConstValue_Pair(tag, ClassCell, NapeCell); + } catch (IOException e) { + IOProb = e; + } + return obj; + } + + @Override + public ConstantPool.ConstValue visitField(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitField]: "); + return visitMember(tag); + } + + @Override + public ConstantPool.ConstValue visitMethod(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitMethod]: "); + return visitMember(tag); + } + + @Override + public ConstantPool.ConstValue visitInterfacemethod(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitInterfacemethod]: "); + return visitMember(tag); + } + + @Override + public ConstantPool.ConstValue visitNameandtype(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitNameandtype]: "); + ConstantPool.ConstValue_Pair obj = null; + try { + ConstantPool.ConstCell NameCell = parser.parseName(), TypeCell; + scanner.expect(Token.COLON); + TypeCell = parser.parseName(); + obj = new ConstantPool.ConstValue_Pair(tag, NameCell, TypeCell); + } catch (IOException e) { + IOProb = e; + } + return obj; + } + + @Override + public ConstantPool.ConstValue_IndyPair visitInvokedynamic(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitInvokeDynamic]: "); + final BiFunction ctor = + (bsmData, napeCell) -> new ConstantPool.ConstValue_IndyPair(bsmData, napeCell); + return visitBsm(ctor); + } + + @Override + public ConstantPool.ConstValue_CondyPair visitDynamic(ConstType tag) { + debugStr("------- [ParserCPVisitor.visitDynamic]: "); + final BiFunction ctor = + (bsmData, napeCell) -> new ConstantPool.ConstValue_CondyPair(bsmData, napeCell); + return visitBsm(ctor); + } + + private E visitBsm(BiFunction ctor) { + E obj = null; + try { + if (scanner.token == Token.INTVAL) { + // Handle explicit constant pool form + int bsmIndex = scanner.intValue; + scanner.scan(); + scanner.expect(Token.COLON); + if (scanner.token != Token.CPINDEX) { + env.traceln("token=" + scanner.token); + env.error(scanner.pos, "token.expected", ""); + throw new Scanner.SyntaxError(); + } + int cpx = scanner.intValue; + scanner.scan(); + // Put a placeholder in place of BSM. + // resolve placeholder after the attributes are scanned. + BootstrapMethodData bsmData = new BootstrapMethodData(bsmIndex); + obj = ctor.apply(bsmData, parser.pool.getCell(cpx)); + } else { + // Handle full form + ConstantPool.ConstCell MHCell = parser.pool.FindCell(parseConstValue(ConstType.CONSTANT_METHODHANDLE)); + scanner.expect(Token.COLON); + ConstantPool.ConstCell NapeCell = parser.pool.FindCell(parseConstValue(ConstType.CONSTANT_NAMEANDTYPE)); + if(scanner.token == Token.LBRACE) { + ParserCP.this.lbrace++; + scanner.scan(); + } + ArrayList bsm_args = new ArrayList<>(256); + while(true) { + if( ParserCP.this.lbrace > 0 ) { + if(scanner.token == Token.RBRACE ) { + ParserCP.this.lbrace--; + scanner.scan(); + break; + } else if(scanner.token == Token.SEMICOLON) { + scanner.expect(Token.RBRACE); + } + } else if(scanner.token == Token.SEMICOLON) { + break; + } + if (scanner.token == Token.COMMA) { + scanner.scan(); + } + bsm_args.add(parseConstRef(null)); + } + if( ParserCP.this.lbrace == 0 ) { + scanner.check(Token.SEMICOLON); + } + BootstrapMethodData bsmData = new BootstrapMethodData(MHCell, bsm_args); + parser.cd.addBootstrapMethod(bsmData); + obj = ctor.apply(bsmData, NapeCell); + } + } catch (IOException e) { + IOProb = e; + } + return obj; + } + } // End Visitor + + /** + * Parse CONSTVALUE + */ + protected ConstantPool.ConstValue parseConstValue(ConstType tag) throws IOException, Scanner.SyntaxError { + return pConstVstr.visitExcept(tag); + } + + /** + * Parse [TAG] CONSTVALUE + */ + protected ConstantPool.ConstValue parseTagConstValue(ConstType defaultTag) throws Scanner.SyntaxError, IOException { + return parseTagConstValue(defaultTag, null, false); + } + + private ConstType scanConstByID(boolean ignoreKeywords) { + ConstType tag = null; + if (!ignoreKeywords) { + ConstType tg = Tables.tag(scanner.idValue); + if (tg != null) { + tag = tg; + } + debugStr(" *^*^*^*^ [ParserCP.scanConst]: {TAG = " + (tg == null ? "null" : tg.toString()) + " "); + } + return tag; + } + + private ConstType scanConstPrimVal() throws Scanner.SyntaxError, IOException { + ConstType tag = null; + switch (scanner.token) { + case INTVAL: + tag = ConstType.CONSTANT_INTEGER; + break; + case LONGVAL: + tag = ConstType.CONSTANT_LONG; + break; + case FLOATVAL: + tag = ConstType.CONSTANT_FLOAT; + break; + case DOUBLEVAL: + tag = ConstType.CONSTANT_DOUBLE; + break; + case STRINGVAL: + case BITS: + case IDENT: + tag = ConstType.CONSTANT_STRING; + break; + default: + // problem - no constant value + System.err.println("NEAR: " + scanner.token.printValue()); + env.error(scanner.pos, "value.expected"); + throw new Scanner.SyntaxError(); + } + return tag; + } + + private void checkWrongTag(ConstType tag, ConstType defaultTag, ConstType default2Tag) throws Scanner.SyntaxError, IOException { + if (defaultTag != null) { + if (tag != defaultTag) { + if (default2Tag == null) { + env.error("warn.wrong.tag", defaultTag.parseKey()); + } else if (tag != default2Tag) { + env.error("warn.wrong.tag2", defaultTag.parseKey(), default2Tag.parseKey()); + } + } + } + } + + protected ConstantPool.ConstValue parseTagConstValue(ConstType defaultTag, ConstType default2Tag, boolean ignoreKeywords) throws Scanner.SyntaxError, IOException { + debugScan(" *^*^*^*^ [ParserCP.parseTagConstValue]: Begin default_tag: ignoreKeywords: " + (ignoreKeywords ? "true" : "false")); + // Lookup the Tag from the scanner + ConstType tag = scanConstByID(ignoreKeywords); + debugStr(" *^*^*^*^ [ParserCP.parseTagConstValue]: {tag = " + tag + ", defaulttag = " + defaultTag + "} "); + + // If the scanned tag is null + if (tag == null) { + // and, if the expected tag is null + if (defaultTag == null) { + // return some other type of constant as the tag + tag = scanConstPrimVal(); + } else { + // otherwise, make the scanned-tag the same constant-type + // as the expected tag. + tag = defaultTag; + } + } else { + // If the scanned tag is some constant type + // and the scanned type does not equal the expected type + checkWrongTag(tag, defaultTag, default2Tag); + scanner.scan(); + } + return parseConstValue(tag); + } // end parseTagConstValue + + protected ConstantPool.ConstCell parseConstRef(ConstType defaultTag) throws Scanner.SyntaxError, IOException { + return parseConstRef(defaultTag, null, false); + } + + protected ConstantPool.ConstCell parseConstRef(ConstType defaultTag, ConstType default2Tag) throws Scanner.SyntaxError, IOException { + return parseConstRef(defaultTag, default2Tag, false); + } + + /** + * Parse an instruction argument, one of: * #NUMBER, #NAME, [TAG] CONSTVALUE + */ + protected ConstantPool.ConstCell parseConstRef(ConstType defaultTag, + ConstType default2Tag, + boolean ignoreKeywords) throws Scanner.SyntaxError, IOException { + if (scanner.token == Token.CPINDEX) { + int cpx = scanner.intValue; + scanner.scan(); + return parser.pool.getCell(cpx); + } else { + ConstantPool.ConstValue ref = null; + ref = parseTagConstValue(defaultTag, default2Tag, ignoreKeywords); + return parser.pool.FindCell(ref); + } + } // end parseConstRef + +} diff --git a/src/org/openjdk/asmtools/jasm/ParserInstr.java b/src/org/openjdk/asmtools/jasm/ParserInstr.java new file mode 100644 index 0000000..d40af58 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/ParserInstr.java @@ -0,0 +1,412 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.JasmTokens.*; +import static org.openjdk.asmtools.jasm.Tables.*; +import static org.openjdk.asmtools.jasm.OpcodeTables.*; +import java.io.IOException; +import java.lang.reflect.Modifier; + +/** + * ParserInstr + * + * ParserInstr is a parser class owned by Parser.java. It is primarily responsible for + * parsing instruction byte codes. + */ +public class ParserInstr extends ParseBase { + + /** + * local handle for the constant parser - needed for parsing constants during + * instruction construction. + */ + private ParserCP cpParser = null; + + /** + * main constructor + * + * @param scanner + * @param parser + * @param env + */ + protected ParserInstr(Scanner scanner, Parser parser, ParserCP cpParser, Environment env) { + super.init(scanner, parser, env); + this.cpParser = cpParser; + } + + /** + * Parse an instruction. + */ + protected void parseInstr() throws Scanner.SyntaxError, IOException { + // ignore possible line numbers after java disassembler + if (scanner.token == Token.INTVAL) { + scanner.scan(); + } + // ignore possible numeric labels after java disassembler + if (scanner.token == Token.INTVAL) { + scanner.scan(); + } + if (scanner.token == Token.COLON) { + scanner.scan(); + } + + String mnemocode; + int mnenoc_pos; + for (;;) { // read labels + if (scanner.token != Token.IDENT) { + return; + } + mnemocode = scanner.idValue; + mnenoc_pos = scanner.pos; + scanner.scan(); + if (scanner.token != Token.COLON) { + break; + } + // actually it was a label + scanner.scan(); + parser.curCode.LabelDef(mnenoc_pos, mnemocode); + } + + Opcode opcode = OpcodeTables.opcode(mnemocode); + if (opcode == null) { + debugScan(" Error: mnemocode = '" + mnemocode + "'. "); + } + OpcodeType optype = opcode.type(); + + Argument arg = null; + Object arg2 = null; + StackMapData sMap = null; + + debugScan(" --IIIII---[ParserInstr:[parseInstr]: (Pos: " + mnenoc_pos + ") mnemocode: '" + opcode.parsekey() + "' "); + + switch (optype) { + case NORMAL: + switch (opcode) { + + // pseudo-instructions: + case opc_bytecode: + for (;;) { + parser.curCode.addInstr(mnenoc_pos, Opcode.opc_bytecode, parser.parseUInt(1), null); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_try: + for (;;) { + parser.curCode.beginTrap(scanner.pos, parser.parseIdent()); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_endtry: + for (;;) { + parser.curCode.endTrap(scanner.pos, parser.parseIdent()); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_catch: + parser.curCode.trapHandler(scanner.pos, parser.parseIdent(), + cpParser.parseConstRef(ConstType.CONSTANT_CLASS)); + return; + case opc_var: + for (;;) { + parser.parseLocVarDef(); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_endvar: + for (;;) { + parser.parseLocVarEnd(); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_locals_map: + sMap = parser.curCode.getStackMap(); + if (sMap.localsMap != null) { + env.error(scanner.pos, "localsmap.repeated"); + } + ; + DataVector localsMap = new DataVector(); + sMap.localsMap = localsMap; + if (scanner.token == Token.SEMICOLON) { + return; // empty locals_map allowed + } + for (;;) { + parser.parseMapItem(localsMap); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_stack_map: + sMap = parser.curCode.getStackMap(); + if (sMap.stackMap != null) { + env.error(scanner.pos, "stackmap.repeated"); + } + ; + DataVector stackMap = new DataVector(); + sMap.stackMap = stackMap; + if (scanner.token == Token.SEMICOLON) { + return; // empty stack_map allowed + } + for (;;) { + parser.parseMapItem(stackMap); + if (scanner.token != Token.COMMA) { + return; + } + scanner.scan(); + } + case opc_stack_frame_type: + sMap = parser.curCode.getStackMap(); + if (sMap.stackFrameType != null) { + env.error(scanner.pos, "frametype.repeated"); + } + ; + sMap.setStackFrameType(parser.parseIdent()); + return; + + // normal instructions: + case opc_aload: + case opc_astore: + case opc_fload: + case opc_fstore: + case opc_iload: + case opc_istore: + case opc_lload: + case opc_lstore: + case opc_dload: + case opc_dstore: + case opc_ret: + case opc_aload_w: + case opc_astore_w: + case opc_fload_w: + case opc_fstore_w: + case opc_iload_w: + case opc_istore_w: + case opc_lload_w: + case opc_lstore_w: + case opc_dload_w: + case opc_dstore_w: + case opc_ret_w: + // loc var + arg = parser.parseLocVarRef(); + break; + case opc_iinc: // loc var, const + arg = parser.parseLocVarRef(); + scanner.expect(Token.COMMA); + arg2 = parser.parseInt(1); + break; + case opc_tableswitch: + case opc_lookupswitch: + arg2 = parseSwitchTable(); + break; + case opc_newarray: { + int type; + if (scanner.token == Token.INTVAL) { + type = scanner.intValue; + } else if ((type = Tables.basictypeValue(scanner.idValue)) == -1) { + env.error(scanner.pos, "type.expected"); + throw new Scanner.SyntaxError(); + } + scanner.scan(); + arg = new Argument(type); + break; + } + case opc_new: + case opc_anewarray: + case opc_instanceof: + case opc_checkcast: + arg = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + break; + case opc_bipush: + arg = parser.parseInt(1); + break; + case opc_sipush: + arg = parser.parseInt(2); + break; + case opc_ldc: + case opc_ldc_w: + case opc_ldc2_w: + arg = cpParser.parseConstRef(null); + break; + case opc_putstatic: + case opc_getstatic: + case opc_putfield: + case opc_getfield: + arg = cpParser.parseConstRef(ConstType.CONSTANT_FIELD); + break; + case opc_invokevirtual: + arg = cpParser.parseConstRef(ConstType.CONSTANT_METHOD); + break; + case opc_invokestatic: + case opc_invokespecial: + ConstType ctype01 = ConstType.CONSTANT_METHOD; + ConstType ctype02 = ConstType.CONSTANT_INTERFACEMETHOD; + if(Modifier.isInterface(this.parser.cd.access)) { + ctype01 = ConstType.CONSTANT_INTERFACEMETHOD; + ctype02 = ConstType.CONSTANT_METHOD; + } + arg = cpParser.parseConstRef(ctype01, ctype02); + break; + case opc_jsr: + case opc_goto: + case opc_ifeq: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + case opc_iflt: + case opc_ifne: + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_icmplt: + case opc_if_acmpeq: + case opc_if_acmpne: + case opc_ifnull: + case opc_ifnonnull: + case opc_jsr_w: + case opc_goto_w: + arg = parseLabelRef(); + break; + + case opc_invokeinterface: + arg = cpParser.parseConstRef(ConstType.CONSTANT_INTERFACEMETHOD); + scanner.expect(Token.COMMA); + arg2 = parser.parseUInt(1); + break; + case opc_invokedynamic: + arg = cpParser.parseConstRef(ConstType.CONSTANT_INVOKEDYNAMIC); + break; + + case opc_multianewarray: + arg = cpParser.parseConstRef(ConstType.CONSTANT_CLASS); + scanner.expect(Token.COMMA); + arg2 = parser.parseUInt(1); + break; + case opc_wide: + case opc_nonpriv: + case opc_priv: + int opc2 = (opcode.value() << 8) | parser.parseUInt(1).arg; + opcode = opcode(opc2); + break; + } + break; + case WIDE: + arg = parser.parseLocVarRef(); + if (opcode == Opcode.opc_iinc_w) { // loc var, const + scanner.expect(Token.COMMA); + arg2 = parser.parseInt(2); + } + break; + case NONPRIVELEGED: + case PRIVELEGED: + break; + default: + env.error(scanner.prevPos, "wrong.mnemocode", mnemocode); + throw new Scanner.SyntaxError(); + } + // env.traceln(" [ParserInstr.parseInstr] ===============> Adding Instruction: [" + mnenoc_pos + "]: instr: "+ mnemocode /* opcNamesTab[opc] */); + parser.curCode.addInstr(mnenoc_pos, opcode, arg, arg2); + } //end parseInstr + + /** + * Parse a Switch Table. return value: SwitchTable. + */ + protected SwitchTable parseSwitchTable() throws Scanner.SyntaxError, IOException { + scanner.expect(Token.LBRACE); + Argument label; + int numpairs = 0, key; + SwitchTable table = new SwitchTable(env); +tableScan: + { + while (numpairs < 1000) { +// env.traceln("start tableScan:" + token); + switch (scanner.token) { + case INTVAL: +// env.traceln("enter tableScan:" + token); + key = scanner.intValue * scanner.sign; + scanner.scan(); + scanner.expect(Token.COLON); + table.addEntry(key, parseLabelRef()); + numpairs++; + if (scanner.token != Token.SEMICOLON) { +// env.traceln("break tableScan1:" + token); + break tableScan; + } + scanner.scan(); + break; + case DEFAULT: + scanner.scan(); + scanner.expect(Token.COLON); + if (table.deflabel != null) { + env.error("default.redecl"); + } + table.deflabel = parseLabelRef(); + if (scanner.token != Token.SEMICOLON) { +// env.traceln("break tableScan2:" + token); + break tableScan; + } + scanner.scan(); + break; + default: +// env.traceln("break tableScan3:" + token + "val=" + intValue); + break tableScan; + } // end switch + } // while (numpairs<1000) + env.error("long.switchtable", "1000"); + } // end tableScan + scanner.expect(Token.RBRACE); + return table; + } // end parseSwitchTable + + /** + * Parse a label instruction argument + */ + protected Argument parseLabelRef() throws Scanner.SyntaxError, IOException { + switch (scanner.token) { + case INTVAL: { + int v = scanner.intValue * scanner.sign; + scanner.scan(); + return new Argument(v); + } + case IDENT: { + String label = scanner.stringValue; + scanner.scan(); + return parser.curCode.LabelRef(label); + } + } + env.error("label.expected"); + throw new Scanner.SyntaxError(); + } + +} diff --git a/src/org/openjdk/asmtools/jasm/PermittedSubclassesAttr.java b/src/org/openjdk/asmtools/jasm/PermittedSubclassesAttr.java new file mode 100644 index 0000000..436bba8 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/PermittedSubclassesAttr.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.util.List; + +/** + * The "classes[]" data of attributes + * JEP 360 (Sealed types): class file 59.65535 + * PermittedSubclasses_attribute { + * u2 attribute_name_index; + * u4 attribute_length; + * u2 number_of_classes; + * u2 classes[number_of_classes]; + * } + */ +public class PermittedSubclassesAttr extends ClassArrayAttr { + public PermittedSubclassesAttr(ClassData cdata, List classes) { + super(Tables.AttrTag.ATT_PermittedSubclasses.parsekey(), cdata, classes); + } +} diff --git a/src/org/openjdk/asmtools/jasm/RecordData.java b/src/org/openjdk/asmtools/jasm/RecordData.java new file mode 100644 index 0000000..c24308d --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/RecordData.java @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static org.openjdk.asmtools.jasm.RuntimeConstants.*; + +/** + * The record attribute (JEP 359 since class file 58.65535) + */ +public class RecordData extends AttrData { + private List components = new ArrayList<>(); + + public RecordData(ClassData cls) { + super(cls, Tables.AttrTag.ATT_Record.parsekey()); + } + + public void addComponent(ConstantPool.ConstCell nameCell, + ConstantPool.ConstCell descCell, + ConstantPool.ConstCell signature, + ArrayList annotations) { + // Define a field if absent + FieldData fd = getClassData().addFieldIfAbsent(ACC_MANDATED & ACC_PRIVATE & ACC_FINAL, nameCell, descCell); + ComponentData cd = new ComponentData(fd); + if( annotations != null ) { + cd.addAnnotations(annotations); + } + if( signature != null ) { + cd.setSignatureAttr(signature); + } + components.add(cd); + } + + public boolean isEmpty() { + return components.isEmpty(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + super.write(out); + out.writeShort(components.size()); + for (ComponentData cd : components) { + cd.write(out); + } + } + + @Override + public int attrLength() { + int compsLength = components.stream().mapToInt(c -> c.getLength()).sum(); + return 2 + compsLength; + } + + class ComponentData extends MemberData { + private FieldData field; + + public ComponentData(FieldData field) { + super(getClassData()); + this.field = field; + } + + @Override + protected DataVector getAttrVector() { + return getDataVector(signatureAttr); + } + + public void write(CheckedDataOutputStream out) throws IOException, Parser.CompilerError { + out.writeShort(field.getNameDesc().left.arg); + out.writeShort(field.getNameDesc().right.arg); + DataVector attrs = getAttrVector(); + attrs.write(out); + } + + public int getLength() { + return 4 + getAttrVector().getLength(); + } + } +} \ No newline at end of file diff --git a/src/org/openjdk/asmtools/jasm/RuntimeConstants.java b/src/org/openjdk/asmtools/jasm/RuntimeConstants.java new file mode 100644 index 0000000..7afb26e --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/RuntimeConstants.java @@ -0,0 +1,90 @@ +/* + * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.util.HashMap; +import java.util.Map; + +/** + * + */ +public interface RuntimeConstants { + /* Access Flags */ + + int ACC_NONE = 0x0000; // <> + int ACC_PUBLIC = 0x0001; // class, inner, field, method + int ACC_PRIVATE = 0x0002; // inner, field, method + int ACC_PROTECTED = 0x0004; // inner, field, method + int ACC_STATIC = 0x0008; // inner, field, method + int ACC_FINAL = 0x0010; // class, inner, field, method + int ACC_TRANSITIVE = 0x0010; // requires(module) + int ACC_SUPER = 0x0020; // class + int ACC_STATIC_PHASE = 0x0020; // requires(module) + int ACC_SYNCHRONIZED = 0x0020; // method + int ACC_OPEN = 0x0020; // module + int ACC_VOLATILE = 0x0040; // field + int ACC_BRIDGE = 0x0040; // method + int ACC_TRANSIENT = 0x0080; // field + int ACC_VARARGS = 0x0080; // method + int ACC_NATIVE = 0x0100; // method + int ACC_INTERFACE = 0x0200; // class, inner + int ACC_ABSTRACT = 0x0400; // class, inner, method + int ACC_STRICT = 0x0800; // method + int ACC_SYNTHETIC = 0x1000; // class, inner, field, method, module requires(module) exports(module) + int ACC_ANNOTATION = 0x2000; // class, inner + int ACC_ENUM = 0x4000; // class, inner, field + int ACC_MODULE = 0x8000; // class + int ACC_MANDATED = 0x8000; // method module requires(module) exports(module) + + /* Attribute codes */ + int SYNTHETIC_ATTRIBUTE = 0x00010000; // actually, this is an attribute + int DEPRECATED_ATTRIBUTE = 0x00020000; // actually, this is an attribute + + Map ACC_NAMES = new HashMap() {{ + put(ACC_PUBLIC ,"public"); + put(ACC_PRIVATE ,"private"); + put(ACC_PROTECTED ,"protected"); + put(ACC_STATIC ,"static"); + put(ACC_FINAL ,"final"); + put(ACC_SUPER ,"super"); + put(ACC_SYNCHRONIZED ,"synchronized"); + put(ACC_VOLATILE ,"volatile"); + put(ACC_BRIDGE ,"bridge"); + put(ACC_TRANSIENT ,"transient"); + put(ACC_VARARGS ,"varargs"); + put(ACC_NATIVE ,"native"); + put(ACC_INTERFACE ,"interface"); + put(ACC_ABSTRACT ,"abstract"); + put(ACC_STRICT ,"strict"); + put(ACC_SYNTHETIC ,"synthetic"); + put(ACC_ANNOTATION ,"annotation"); + put(ACC_ENUM ,"enum"); + put(ACC_MODULE ,"module"); + put(ACC_MANDATED ,"mandated"); + put(SYNTHETIC_ATTRIBUTE ,"synthetic"); + }}; + + /* The version of a class file since which the compact format of stack map is necessary */ + int SPLIT_VERIFIER_CFV = 50; + +} diff --git a/src/org/openjdk/asmtools/jasm/Scanner.java b/src/org/openjdk/asmtools/jasm/Scanner.java new file mode 100644 index 0000000..91d1c4b --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Scanner.java @@ -0,0 +1,1209 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static java.lang.String.format; +import static org.openjdk.asmtools.jasm.JasmTokens.*; +import static org.openjdk.asmtools.jasm.Constants.EOF; +import static org.openjdk.asmtools.jasm.Constants.OFFSETBITS; +import java.io.IOException; +import java.util.function.Predicate; + +/** + * A Scanner for Jasm tokens. Errors are reported to the environment object.

+ * + * The scanner keeps track of the current token, the value of the current token (if any), + * and the start position of the current token.

+ * + * The scan() method advances the scanner to the next token in the input.

+ * + * The match() method is used to quickly match opening brackets (ie: '(', '{', or '[') + * with their closing counter part. This is useful during error recovery.

+ * + * The compiler treats either "\n", "\r" or "\r\n" as the end of a line.

+ */ +public class Scanner extends ParseBase { + + /** + * SyntaxError is the generic error thrown for parsing problems. + */ + protected static class SyntaxError extends Error { + boolean fatalError = false; + SyntaxError Fatal() { fatalError = true; return this; } + boolean isFatal() {return fatalError;} + } + + /** + * Input stream + */ + protected Environment in; + + /** + * The current character + */ + protected int ch; + + /** + * Current token + */ +// protected int token; + protected Token token; + + /** + * The position of the current token + */ + protected int pos; + + /* + * Token values. + */ + protected char charValue; + protected int intValue; + protected long longValue; + protected float floatValue; + protected double doubleValue; + protected String stringValue; + protected String idValue; + protected int radix; // Radix, when reading int or long + + /* doc comment preceding the most recent token */ + protected String docComment; + + /* A growable character buffer. */ + private int count; + private char buffer[] = new char[32]; + // + private Predicate escapingAllowed; + /** + * The position of the previous token + */ + protected int prevPos; + protected int sign; // sign, when reading number + protected boolean inBits; // inBits prefix, when reading number + + /** + * main constructor. + * + * Create a scanner to scan an input stream. + */ + protected Scanner(Environment env) throws IOException { + super.init(this, null, env); + escapingAllowed = noFunc; + this.in = env; + ch = env.read(); + xscan(); + } + + protected void scanModuleStatement() throws IOException { + try { + escapingAllowed = yesAndProcessFunc; + scan(); + } finally { + escapingAllowed = noFunc; + } + } + + /** + * scan + * + * Scan the next token. + * + * @throws IOException + */ + protected void scan() throws IOException { + int signloc = 1, cnt = 0; + prevPos = pos; +prefix: + for (;;) { + xscan(); + switch (token) { + case SIGN: + signloc = signloc * intValue; + break; + default: + break prefix; + } + cnt++; + } + switch (token) { + case INTVAL: + case LONGVAL: + case FLOATVAL: + case DOUBLEVAL: + case INF: + case NAN: + sign = signloc; + break; + default: + } + } + + /** + * Check the token may be identifier + */ + protected final boolean checkTokenIdent() { + return token.possibleJasmIdentifier(); + } + + static String readableConstant(int t) { + return "<" + Tables.tag(t) + "> [" + t + "]"; + } + + /** + * Expects a token, scans the next token or throws an exception. + */ + protected final void expect(Token t) throws SyntaxError, IOException { + check(t); + scan(); + } + + /** + * Checks a token, throws an exception if not the same + */ + protected final void check(Token t) throws SyntaxError, IOException { + if (token != t) { + if ((t != Token.IDENT) || !checkTokenIdent()) { + env.traceln("expect:" + t + " instead of " + token); + switch (t) { + case IDENT: + env.error(pos, "identifier.expected"); + break; + default: + env.error(pos, "token.expected", "<" + t.printValue() + ">"); + break; + } + + if (debugFlag) { + debugStr("<<<<>>>>>>: "); + throw new Error("<<<<>>>>>>"); + } else { + throw new SyntaxError(); + } + } + } + } + + private void putCh(int ch) { + if (count == buffer.length) { + char newBuffer[] = new char[buffer.length * 2]; + System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); + buffer = newBuffer; + } + buffer[count++] = (char) ch; + } + + private String bufferString() { + char buf[] = new char[count]; + System.arraycopy(buffer, 0, buf, 0, count); + return new String(buf); + } + + /** + * Returns true if the character is a unicode digit. + * + * @param ch the character to be checked + */ + public static boolean isUCDigit(int ch) { + if ((ch >= '0') && (ch <= '9')) { + return true; + } + switch (ch >> 8) { + case 0x06: + return ((ch >= 0x0660) && (ch <= 0x0669)) || // Arabic-Indic + ((ch >= 0x06f0) && (ch <= 0x06f9)); // Eastern Arabic-Indic + case 0x07: + case 0x08: + default: + return false; + case 0x09: + return ((ch >= 0x0966) && (ch <= 0x096f)) || // Devanagari + ((ch >= 0x09e6) && (ch <= 0x09ef)); // Bengali + case 0x0a: + return ((ch >= 0x0a66) && (ch <= 0x0a6f)) || // Gurmukhi + ((ch >= 0x0ae6) && (ch <= 0x0aef)); // Gujarati + case 0x0b: + return ((ch >= 0x0b66) && (ch <= 0x0b6f)) || // Oriya + ((ch >= 0x0be7) && (ch <= 0x0bef)); // Tamil + case 0x0c: + return ((ch >= 0x0c66) && (ch <= 0x0c6f)) || // Telugu + ((ch >= 0x0ce6) && (ch <= 0x0cef)); // Kannada + case 0x0d: + return ((ch >= 0x0d66) && (ch <= 0x0d6f)); // Malayalam + case 0x0e: + return ((ch >= 0x0e50) && (ch <= 0x0e59)) || // Thai + ((ch >= 0x0ed0) && (ch <= 0x0ed9)); // Lao + case 0x0f: + return false; + case 0x10: + return ((ch >= 0x1040) && (ch <= 0x1049)); // Tibetan + } + } + + /** + * Returns true if the character is a Unicode letter. + * + * @param ch the character to be checked + */ + public static boolean isUCLetter(int ch) { + // fast check for Latin capitals and small letters + if (((ch >= 'A') && (ch <= 'Z')) + || ((ch >= 'a') && (ch <= 'z'))) { + return true; + } + // rest of ISO-LATIN-1 + if (ch < 0x0100) { + // fast check + if (ch < 0x00c0) { + return (ch == '_') || (ch == '$'); + } + // various latin letters and diacritics, + // but *not* the multiplication and division symbols + return ((ch >= 0x00c0) && (ch <= 0x00d6)) + || ((ch >= 0x00d8) && (ch <= 0x00f6)) + || ((ch >= 0x00f8) && (ch <= 0x00ff)); + } + // other non CJK alphabets and symbols, but not digits + if (ch <= 0x1fff) { + return !isUCDigit(ch); + } + // rest are letters only in five ranges: + // Hiragana, Katakana, Bopomofo and Hangul + // CJK Squared Words + // Korean Hangul Symbols + // Han (Chinese, Japanese, Korean) + // Han compatibility + return ((ch >= 0x3040) && (ch <= 0x318f)) + || ((ch >= 0x3300) && (ch <= 0x337f)) + || ((ch >= 0x3400) && (ch <= 0x3d2d)) + || ((ch >= 0x4e00) && (ch <= 0x9fff)) + || ((ch >= 0xf900) && (ch <= 0xfaff)); + } + + /** + * Scan a comment. This method should be called once the initial /, * and the next + * character have been read. + */ + private void skipComment() throws IOException { + while (true) { + switch (ch) { + case EOF: + env.error(pos, "eof.in.comment"); + return; + case '*': + if ((ch = in.read()) == '/') { + ch = in.read(); + return; + } + break; + default: + ch = in.read(); + break; + } + } + } + + /** + * Scan a doc comment. This method should be called once the initial /, * and * have + * been read. It gathers the content of the comment (without leading spaces and '*'s) + * in the string buffer. + */ + @SuppressWarnings("empty-statement") + private String scanDocComment() throws IOException { + count = 0; + + if (ch == '*') { + do { + ch = in.read(); + } while (ch == '*'); + if (ch == '/') { + ch = in.read(); + return ""; + } + } + switch (ch) { + case '\n': + case ' ': + ch = in.read(); + break; + } + + boolean seenstar = false; + int c = count; + while (true) { + switch (ch) { + case EOF: + env.error(pos, "eof.in.comment"); + return bufferString(); + case '\n': + putCh('\n'); + ch = in.read(); + seenstar = false; + c = count; + break; + case ' ': + case '\t': + putCh(ch); + ch = in.read(); + break; + case '*': + if (seenstar) { + if ((ch = in.read()) == '/') { + ch = in.read(); + count = c; + return bufferString(); + } + putCh('*'); + } else { + seenstar = true; + count = c; + while ((ch = in.read()) == '*'); + switch (ch) { + case ' ': + ch = in.read(); + break; + case '/': + ch = in.read(); + count = c; + return bufferString(); + } + } + break; + default: + if (!seenstar) { + seenstar = true; + } + putCh(ch); + ch = in.read(); + c = count; + break; + } + } + } + + /** + * Scan a decimal at this point + */ + private void scanCPRef() throws IOException { + switch (ch = in.read()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + boolean overflow = false; + long value = ch - '0'; + count = 0; + putCh(ch); // save character in buffer +numberLoop: + for (;;) { + switch (ch = in.read()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + putCh(ch); + if (overflow) { + break; + } + value = (value * 10) + (ch - '0'); + overflow = (value > 0xFFFF); + break; + default: + break numberLoop; + } + } // while true + intValue = (int) value; + stringValue = bufferString(); + token = Token.CPINDEX; + if (overflow) { + env.error(pos, "overflow"); + } + break; + } + default: + stringValue = Character.toString((char)ch); + env.error(in.pos, "invalid.number", stringValue); + intValue = 0; + token = Token.CPINDEX; + ch = in.read(); + } + } // scanCPRef() + + /** + * Scan a number. The first digit of the number should be the current character. We + * may be scanning hex, decimal, or octal at this point + */ + private void scanNumber() throws IOException { + boolean seenNonOctal = false; + boolean overflow = false; + radix = (ch == '0' ? 8 : 10); + long value = ch - '0'; + count = 0; + putCh(ch); // save character in buffer +numberLoop: + for (;;) { + switch (ch = in.read()) { + case '.': + if (radix == 16) { + break numberLoop; // an illegal character + } + scanReal(); + return; + + case '8': + case '9': + // We can't yet throw an error if reading an octal. We might + // discover we're really reading a real. + seenNonOctal = true; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + putCh(ch); + if (radix == 10) { + overflow = overflow || (value * 10) / 10 != value; + value = (value * 10) + (ch - '0'); + overflow = overflow || (value - 1 < -1); + } else if (radix == 8) { + overflow = overflow || (value >>> 61) != 0; + value = (value << 3) + (ch - '0'); + } else { + overflow = overflow || (value >>> 60) != 0; + value = (value << 4) + (ch - '0'); + } + break; + case 'd': + case 'D': + case 'e': + case 'E': + case 'f': + case 'F': + if (radix != 16) { + scanReal(); + return; + } + // fall through + case 'a': + case 'A': + case 'b': + case 'B': + case 'c': + case 'C': + putCh(ch); + if (radix != 16) { + break numberLoop; // an illegal character + } + overflow = overflow || (value >>> 60) != 0; + value = (value << 4) + 10 + + Character.toLowerCase((char) ch) - 'a'; + break; + case 'l': + case 'L': + ch = in.read(); // skip over 'l' + longValue = value; + token = Token.LONGVAL; + break numberLoop; + case 'x': + case 'X': + // if the first character is a '0' and this is the second + // letter, then read in a hexadecimal number. Otherwise, error. + if (count == 1 && radix == 8) { + radix = 16; + break; + } else { + // we'll get an illegal character error + break numberLoop; + } + default: + intValue = (int) value; + token = Token.INTVAL; + break numberLoop; + } + } // while true + // we have just finished reading the number. The next thing better + // not be a letter or digit. + if (isUCDigit(ch) || isUCLetter(ch) || ch == '.') { + env.error(in.pos, "invalid.number", Character.toString((char)ch)); + do { + ch = in.read(); + } while (isUCDigit(ch) || isUCLetter(ch) || ch == '.'); + intValue = 0; + token = Token.INTVAL; + } else if (radix == 8 && seenNonOctal) { + intValue = 0; + token = Token.INTVAL; + env.error(in.pos, "invalid.octal.number"); + } else if (overflow + || (token == Token.INTVAL + && ((radix == 10) ? (intValue - 1 < -1) + : ((value & 0xFFFFFFFF00000000L) != 0)))) { + intValue = 0; // so we don't get second overflow in Parser + longValue = 0; + env.error(pos, "overflow"); + } + } // scanNumber() + + /** + * Scan a float. We are either looking at the decimal, or we have already seen it and + * put it into the buffer. We haven't seen an exponent. Scan a float. Should be called + * with the current character is either the 'e', 'E' or '.' + */ + private void scanReal() throws IOException { + boolean seenExponent = false; + boolean isSingleFloat = false; + char lastChar; + if (ch == '.') { + putCh(ch); + ch = in.read(); + } + +numberLoop: + for (;; ch = in.read()) { + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + putCh(ch); + break; + case 'e': + case 'E': + if (seenExponent) { + break numberLoop; // we'll get a format error + } + putCh(ch); + seenExponent = true; + break; + case '+': + case '-': + lastChar = buffer[count - 1]; + if (lastChar != 'e' && lastChar != 'E') { + break numberLoop; // this isn't an error, though! + } + putCh(ch); + break; + case 'f': + case 'F': + ch = in.read(); // skip over 'f' + isSingleFloat = true; + break numberLoop; + case 'd': + case 'D': + ch = in.read(); // skip over 'd' + // fall through + default: + break numberLoop; + } // sswitch + } // loop + + // we have just finished reading the number. The next thing better + // not be a letter or digit. + if (isUCDigit(ch) || isUCLetter(ch) || ch == '.') { + env.error(in.pos, "invalid.number", Character.toString((char)ch)); + do { + ch = in.read(); + } while (isUCDigit(ch) || isUCLetter(ch) || ch == '.'); + doubleValue = 0; + token = Token.DOUBLEVAL; + } else { + token = isSingleFloat ? Token.FLOATVAL : Token.DOUBLEVAL; + try { + lastChar = buffer[count - 1]; + if (lastChar == 'e' || lastChar == 'E' + || lastChar == '+' || lastChar == '-') { + env.error(in.pos - 1, "float.format"); + } else if (isSingleFloat) { + floatValue = Float.valueOf(bufferString()); + if (Float.isInfinite(floatValue)) { + env.error(pos, "overflow"); + } + } else { + doubleValue = Double.valueOf(bufferString()); + if (Double.isInfinite(doubleValue)) { + env.error(pos, "overflow"); + env.error(pos, "overflow"); + } + } + } catch (NumberFormatException ee) { + env.error(pos, "float.format"); + doubleValue = 0; + floatValue = 0; + } + } + } // scanReal + + /** + * Scan an escape character. + * + * @return the character or '\\' + */ + private int scanEscapeChar() throws IOException { + int p = in.pos; + + switch (ch = in.read()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + int n = ch - '0'; + for (int i = 2; i > 0; i--) { + switch (ch = in.read()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + n = (n << 3) + ch - '0'; + break; + default: + if (n > 0xFF) { + env.error(p, "invalid.escape.char"); + } + return n; + } + } + ch = in.read(); + if (n > 0xFF) { + env.error(p, "invalid.escape.char"); + } + return n; + } + case 'r': + ch = in.read(); + return '\r'; + case 'n': + ch = in.read(); + return '\n'; + case 'f': + ch = in.read(); + return '\f'; + case 'b': + ch = in.read(); + return '\b'; + case 't': + ch = in.read(); + return '\t'; + case '\\': + ch = in.read(); + return '\\'; + case '\"': + ch = in.read(); + return '\"'; + case '\'': + ch = in.read(); + return '\''; + case 'u': + int unich = in.convertUnicode(); + ch = in.read(); + return unich; + } + return '\\'; + } + + /** + * Scan a string. The current character should be the opening " of the string. + */ + private void scanString() throws IOException { + token = Token.STRINGVAL; + count = 0; + ch = in.read(); + + // Scan a String + while (true) { + switch (ch) { + case EOF: + env.error(pos, "eof.in.string"); + stringValue = bufferString(); + return; + case '\n': + ch = in.read(); + env.error(pos, "newline.in.string"); + stringValue = bufferString(); + return; + case '"': + ch = in.read(); + stringValue = bufferString(); + return; + case '\\': { + int c = scanEscapeChar(); + if (c >= 0) { + putCh((char) c); + } + break; + } + default: + putCh(ch); + ch = in.read(); + break; + } + } + } + + + /** + * Scan an Identifier. The current character should be the first character of the + * identifier. + */ + private void scanIdentifier(char[] prefix) throws IOException { + int firstChar; + count = 0; + if(prefix != null) { + for(;;) { + for (int i = 0; i < prefix.length; i++) + putCh(prefix[i]); + ch = in.read(); + if (ch == '\\') { + ch = in.read(); + if (ch == 'u') { + ch = in.convertUnicode(); + if (!isUCLetter(ch) && !isUCDigit(ch)) { + prefix = new char[]{(char)ch}; + continue; + } + } else if (escapingAllowed.test(ch)) { + prefix = new char[]{(char)ch}; + continue; + } + int p = in.pos; + env.error(p, "invalid.escape.char"); + } + break; + } + } + firstChar = ch; + boolean firstIteration = true; +scanloop: + while (true) { + putCh(ch); + ch = in.read(); + + // Check to see if the annotation marker is at + // the front of the identifier. + if (firstIteration && firstChar == '@') { + // May be a type annotation + if (ch == 'T') { // type annotation + putCh(ch); + ch = in.read(); + } + + // is either a runtime visible or invisible annotation + if (ch == '+' || ch == '-') { // regular annotation + // possible annotation - + // need to eat up the '@+' or '@-' + putCh(ch); + ch = in.read(); + } + idValue = bufferString(); + stringValue = idValue; + token = Token.ANNOTATION; + return; + } + + firstIteration = false; + switch (ch) { + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '$': + case '_': + case '-': + case '[': + case ']': + case '(': + case ')': + case '<': + case '>': + break; + case '/': {// may be comment right after identifier + int c = in.lookForward(); + if ((c == '*') || (c == '/')) { + break scanloop; // yes, comment + } + break; // no, continue to parse identifier + } + case '\\': + ch = in.read(); + if ( ch == 'u') { + ch = in.convertUnicode(); + if (isUCLetter(ch) || isUCDigit(ch)) { + break; + } + } else if( escapingAllowed.test(ch)) { + break; + } + int p = in.pos; + env.error(p, "invalid.escape.char"); + default: +// if ((!isUCDigit(ch)) && (!isUCLetter(ch))) { + break scanloop; +// } + } // end switch + } // end scanloop + idValue = bufferString(); + stringValue = idValue; + token = keyword_token_ident(idValue); + debugStr(format("##### SCANNER (scanIdent) ######## token = %s value = \"%s\"\n", token, idValue)); + } // end scanIdentifier + +//============================== + @SuppressWarnings("empty-statement") + protected final void xscan() throws IOException { + docComment = null; +loop: + for (;;) { + pos = in.pos; + switch (ch) { + case EOF: + token = Token.EOF; + break loop; + case '\n': + case ' ': + case '\t': + case '\f': + ch = in.read(); + break; + case '/': + switch (ch = in.read()) { + case '/': + // Parse a // comment + while (((ch = in.read()) != EOF) && (ch != '\n')); + break; + case '*': + ch = in.read(); + if (ch == '*') { + docComment = scanDocComment(); + } else { + skipComment(); + } + break; + default: + token = Token.DIV; + break loop; + } + break; + case '"': + scanString(); + break loop; + case '-': + intValue = -1; + token = Token.SIGN; + ch = in.read(); + break loop; + case '+': + intValue = +1; + ch = in.read(); + token = Token.SIGN; + break loop; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + scanNumber(); + break loop; + case '.': + switch (ch = in.read()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + count = 0; + putCh('.'); + scanReal(); + break; + default: + token = Token.FIELD; + } + break loop; + case '{': + ch = in.read(); + token = Token.LBRACE; + break loop; + case '}': + ch = in.read(); + token = Token.RBRACE; + break loop; + case ',': + ch = in.read(); + token = Token.COMMA; + break loop; + case ';': + ch = in.read(); + token = Token.SEMICOLON; + break loop; + case ':': + ch = in.read(); + token = Token.COLON; + break loop; + case '=': + if ((ch = in.read()) == '=') { + ch = in.read(); + token = Token.EQ; + break loop; + } + token = Token.ASSIGN; + break loop; + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '$': + case '_': + case '@': + case '[': + case ']': + case '(': + case ')': + case '<': + case '>': + scanIdentifier(null); + break loop; + case '\u001a': + // Our one concession to DOS. + if ((ch = in.read()) == EOF) { + token = Token.EOF; + break loop; + } + env.error(pos, "funny.char"); + ch = in.read(); + break; + case '#': + int c = in.lookForward(); + if (c == '{') { + // '#' char denotes a "paramMethod name" token + ch = in.read(); + token = Token.PARAM_NAME; + break loop; + } + // otherwise, it is a normal cpref + scanCPRef(); + break loop; + case '\\': + ch = in.read(); + if ( ch == 'u') { + ch = in.convertUnicode(); + if (isUCLetter(ch)) { + scanIdentifier(null); + break loop; + } + } else if( escapingAllowed.test(ch)) { + scanIdentifier(new char[]{'\\', (char)ch}); + break loop; + } +// if ((ch = in.read()) == 'u') { +// ch = in.convertUnicode(); +// if (isUCLetter(ch)) { +// scanIdentifier(); +// break loop; +// } +// } + default: + env.out.println("funny.char:" + env.lineNumber(pos) + "/" + (pos & ((1 << OFFSETBITS) - 1))); + env.error(pos, "funny.char"); + ch = in.read(); + } + } + } + + @Override + protected void debugScan(String dbstr) { + if (token == null) { + env.traceln(dbstr + "<<>>"); + return; + } + env.trace(dbstr + token); + switch (token) { + case IDENT: + env.traceln(" = '" + stringValue + "' {idValue = '" + idValue + "'}"); + break; + case STRINGVAL: + env.traceln(" = {stringValue}: \"" + stringValue + "\""); + break; + case INTVAL: + env.traceln(" = {intValue}: " + intValue + "}"); + break; + case FLOATVAL: + env.traceln(" = {floatValue}: " + floatValue); + break; + case DOUBLEVAL: + env.traceln(" = {doubleValue}: " + doubleValue); + break; + default: + env.traceln(""); + } + } + + private Predicate noFunc = (ch)-> false; + private Predicate yesAndProcessFunc = (ch) -> { + boolean res = ((ch == '\\') || (ch == ':') || (ch == '@')); + if (res) + putCh('\\'); + return res; + }; +} diff --git a/src/org/openjdk/asmtools/jasm/StackMapData.java b/src/org/openjdk/asmtools/jasm/StackMapData.java new file mode 100644 index 0000000..68ea32b --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/StackMapData.java @@ -0,0 +1,281 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import static org.openjdk.asmtools.jasm.Tables.*; +import java.io.IOException; + +/** + * + */ +public class StackMapData implements Data { + + /** + * + */ + static public class StackMapItem1 implements Data { + + StackMapType itemType; + + StackMapItem1(StackMapType itemType) { + this.itemType = itemType; + } + + @Override + public int getLength() { + return 1; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(itemType.value()); + } + } + + /** + * + */ + static public class StackMapItem2 implements Data { + + StackMapType itemType; + Argument arg; + + StackMapItem2(StackMapType itemType, Argument arg) { + this.itemType = itemType; + this.arg = arg; + } + + @Override + public int getLength() { + return 3; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(itemType.value()); + out.writeShort(arg.arg); + } + } + + int pc; + int offset; + int type; + String stackFrameType = null; + boolean isStackMapTable = false; + DataVector localsMap, stackMap; + Environment env; + + StackMapData(Environment env) { + this.env = env; + } + + void setPC(int pc) { + this.pc = pc; + } + + void setOffset(int offset) { + this.offset = offset; + } + + void setOffset(StackMapData prevFrame) { + offset = (prevFrame == null) ? pc : (pc - prevFrame.pc - 1); + } + + void setStackFrameType(String stackFrameType) { + this.stackFrameType = stackFrameType; + + if (stackFrameType != null) { + type = stackMapFrameTypeValue(stackFrameType); + } + + if (stackFrameType == null || type == -1) { + env.error(pc, "invalid.stack.frame.type", stackFrameType, "" + type); + } + } + + void setIsStackMapTable(boolean isStackMapTable) { + this.isStackMapTable = isStackMapTable; + } + + void setLocalsMap(DataVector localsMap) { + this.localsMap = localsMap; + } + + void setStackMap(DataVector stackMap) { + this.stackMap = stackMap; + } + + @Override + public int getLength() { + int res = 0; + StackMapFrameType frame_type = StackMapFrameType.FULL_FRAME; + // int frame_type = FULL_FRAME; + + if (isStackMapTable) { + if (stackFrameType != null) { + frame_type = stackMapFrameType(type); + } + res += 1; + } + + switch (frame_type) { + case SAME_FRAME: + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME: + res += stackMap.getLength() - 2; + break; + case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME: + res += stackMap.getLength(); + break; + case CHOP_1_FRAME: + case CHOP_2_FRAME: + case CHOP_3_FRAME: + res += 2; + break; + case SAME_FRAME_EX: + res += 2; + break; + case APPEND_FRAME: + res += 2 + (localsMap == null ? 0 : (localsMap.getLength() - 2)); + break; + case FULL_FRAME: + res += 2; + res += (localsMap == null ? 2 : localsMap.getLength()); + res += (stackMap == null ? 2 : stackMap.getLength()); + break; + default: + ; + } + return res; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + StackMapFrameType frame_type = StackMapFrameType.FULL_FRAME; + + if (isStackMapTable) { + if (stackFrameType != null) { + frame_type = stackMapFrameType(type); + } + } + + switch (frame_type) { + case SAME_FRAME: + if (offset >= 64) { + env.error(pc, "invalid.offset.same.frame", "" + offset); + } + out.writeByte(offset); + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME: + if (stackMap == null) { + env.error(pc, "no.stack.map.same.locals"); + break; + } + + if (stackMap.elements.size() != 1) { + env.error(pc, "should.be.only.one.stack.map.element"); + break; + } + + if (offset >= 64) { + env.error(pc, "invalid.offset.same.locals", "" + offset); + break; + } + out.writeByte(frame_type.value() + offset); + stackMap.writeElements(out); + break; + case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME: + if (stackMap == null) { + env.error(pc, "no.stack.map.same.locals"); + break; + } + + if (stackMap.elements.size() != 1) { + env.error(pc, "should.be.only.one.stack.map.element"); + break; + } + out.writeByte(frame_type.value()); + out.writeShort(offset); + stackMap.writeElements(out); + break; + case CHOP_1_FRAME: + case CHOP_2_FRAME: + case CHOP_3_FRAME: + case SAME_FRAME_EX: + boolean error = false; + + if (stackMap != null) { + env.error(pc, "unexpected.stack.maps"); + error = true; + } + + if (localsMap != null) { + env.error(pc, "unexpected.locals.maps"); + error = true; + } + + if (error) { + break; + } + out.writeByte(frame_type.value()); + out.writeShort(offset); + break; + case APPEND_FRAME: + if (localsMap == null) { + env.error(pc, "no.locals.map.append"); + break; + } + + if (localsMap.elements.size() > 3) { + env.error(pc, "more.locals.map.elements"); + break; + } + out.writeByte(frame_type.value() + localsMap.elements.size() - 1); + out.writeShort(offset); + localsMap.writeElements(out); + break; + case FULL_FRAME: + if (isStackMapTable) { + out.writeByte(frame_type.value()); + out.writeShort(offset); + } else { + out.writeShort(pc); + } + + if (localsMap == null) { + out.writeShort(0); + } else { + localsMap.write(out); + } + + if (stackMap == null) { + out.writeShort(0); + } else { + stackMap.write(out); + } + break; + default: + env.error(pc, "invalid.stack.frame.type", "" + frame_type); + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/SwitchTable.java b/src/org/openjdk/asmtools/jasm/SwitchTable.java new file mode 100644 index 0000000..402ee60 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/SwitchTable.java @@ -0,0 +1,131 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.util.ArrayList; + +/** + * + */ +class SwitchTable { + + Argument deflabel = null; + ArrayList labels = new ArrayList<>(); + ArrayList keys = new ArrayList<>(); + +// for tableswitch: + Argument[] resLabels; + int high, low; + + int pc, pad; + Environment env; + + SwitchTable(Environment env) { + this.env = env; + } + + void addEntry(int key, Argument label) { + keys.add(key); + labels.add(label); + } + +// for lookupswitch: + int calcLookupSwitch(int pc) { + this.pc = pc; + pad = ((3 - pc) & 0x3); + int len = 1 + pad + (keys.size() + 1) * 8; + if (deflabel == null) { + deflabel = new Argument(pc + len); + } + return len; + } + + void writeLookupSwitch(CheckedDataOutputStream out) throws IOException { + env.traceln(" writeLookupSwitch: pc=" + pc + " pad=" + pad + " deflabel=" + deflabel.arg); + int k; + for (k = 0; k < pad; k++) { + out.writeByte(0); + } + out.writeInt(deflabel.arg - pc); + out.writeInt(keys.size()); + for (k = 0; k < keys.size(); k++) { + out.writeInt(keys.get(k)); + out.writeInt((labels.get(k)).arg - pc); + } + } + + int recalcTableSwitch(int pc) { + int k; + int numpairs = keys.size(); + int high1 = Integer.MIN_VALUE, low1 = Integer.MAX_VALUE; + int numslots = 0; + if (numpairs > 0) { + for (k = 0; k < numpairs; k++) { + int key = keys.get(k); + if (key > high1) { + high1 = key; + } + if (key < low1) { + low1 = key; + } + } + numslots = high1 - low1 + 1; + } +// if (numslots>2000) env.error("long.switchtable", "2000"); + env.traceln(" recalcTableSwitch: low=" + low1 + " high=" + high1); + this.pc = pc; + pad = ((3 - pc) & 0x3); + int len = 1 + pad + (numslots + 3) * 4; + if (deflabel == null) { + deflabel = new Argument(pc + len); + } + Argument[] resLabels1 = new Argument[numslots]; + for (k = 0; k < numslots; k++) { + resLabels1[k] = deflabel; + } + for (k = 0; k < numpairs; k++) { + env.traceln(" keys.data[" + k + "]=" + keys.get(k)); + resLabels1[keys.get(k) - low1] = labels.get(k); + } + this.resLabels = resLabels1; + this.labels = null; + this.keys = null; + this.high = high1; + this.low = low1; + return len; + } + + void writeTableSwitch(CheckedDataOutputStream out) throws IOException { + int k; + for (k = 0; k < pad; k++) { + out.writeByte(0); + } + out.writeInt(deflabel.arg - pc); + out.writeInt(low); + out.writeInt(high); + for (k = 0; k < resLabels.length; k++) { + out.writeInt(resLabels[k].arg - pc); + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/Tables.java b/src/org/openjdk/asmtools/jasm/Tables.java new file mode 100644 index 0000000..c90bdc0 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/Tables.java @@ -0,0 +1,688 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.PrintWriter; +import java.util.HashMap; + +/** + * + * Tables + * + * The classes in Tables are following a Singleton Pattern. These classes are Enums, and + * they are contained in private hash maps (lookup tables and reverse lookup tables). + * These hash maps all have public accessors, which clients use to look-up enums. + * + * Tokens in this table carry no external state, and are typically treated as constants. + * They do not need to be reset. + * + */ +public class Tables { + + public static final int JAVA_MAGIC = 0xCAFEBABE; + /** + * Lookup-tables for various types. + */ + private static HashMap NameToAttrTag = new HashMap<>(9); + private static HashMap AttrTags = new HashMap<>(9); + + private static HashMap NameToSubTag = new HashMap<>(9); + private static HashMap SubTags = new HashMap<>(9); + + private static HashMap NameToBasicType = new HashMap<>(10); + private static HashMap BasicTypes = new HashMap<>(10); + + private static HashMap NameToAnnotElemType = new HashMap<>(10); + private static HashMap AnnotElemTypes = new HashMap<>(10); + + private static HashMap KeyToStackMapType = new HashMap<>(10); + private static HashMap NameToStackMapType = new HashMap<>(10); + private static HashMap StackMapTypes = new HashMap<>(10); + + private static HashMap NameToStackMapFrameType = new HashMap<>(10); + private static HashMap StackMapFrameTypes = new HashMap<>(10); + + private static HashMap NameToConstantType = new HashMap<>(ConstType.maxTag); + private static HashMap ConstantTypes = new HashMap<>(ConstType.maxTag); + + static { + // register all of the tokens + for (ConstType ct : ConstType.values()) { + registerConstantType(ct); + } + + /* Type codes for SubTags */ + for (AttrTag at : AttrTag.values()) { + registerAttrtag(at); + } + + /* Type codes for SubTags */ + for (SubTag st : SubTag.values()) { + registerSubtag(st); + } + + /* Type codes for BasicTypes */ + for (BasicType bt : BasicType.values()) { + registerBasicType(bt); + } + + /* Type codes for BasicTypes */ + for (AnnotElemType aet : AnnotElemType.values()) { + registerAnnotElemType(aet); + } + + /* Type codes for StackMapTypes */ + for (StackMapType smt : StackMapType.values()) { + registerStackMapType(smt); + } + + /* Type codes for StackMapFrame attribute */ + for (StackMapFrameType smft : StackMapFrameType.values()) { + registerStackMapFrameType(smft); + } + + } + + /** + * ConstType + * + * A (typed) tag (constant) representing the type of Constant in the Constant Pool. + */ + static public enum ConstType { + CONSTANT_ZERO (-3, "CONSTANT_ZERO", ""), + CONSTANT_UTF8 (1, "CONSTANT_UTF8", "Asciz"), + CONSTANT_UNICODE (2, "CONSTANT_UNICODE", ""), + CONSTANT_INTEGER (3, "CONSTANT_INTEGER", "int"), + CONSTANT_FLOAT (4, "CONSTANT_FLOAT", "float"), + CONSTANT_LONG (5, "CONSTANT_LONG", "long"), + CONSTANT_DOUBLE (6, "CONSTANT_DOUBLE", "double"), + CONSTANT_CLASS (7, "CONSTANT_CLASS", "class"), + CONSTANT_STRING (8, "CONSTANT_STRING", "String"), + CONSTANT_FIELD (9, "CONSTANT_FIELD", "Field"), + CONSTANT_METHOD (10, "CONSTANT_METHOD", "Method"), + CONSTANT_INTERFACEMETHOD (11, "CONSTANT_INTERFACEMETHOD", "InterfaceMethod"), + CONSTANT_NAMEANDTYPE (12, "CONSTANT_NAMEANDTYPE", "NameAndType"), + // Constant 13 reserved + // Constant 14 reserved + CONSTANT_METHODHANDLE (15, "CONSTANT_METHODHANDLE", "MethodHandle"), + CONSTANT_METHODTYPE (16, "CONSTANT_METHODTYPE", "MethodType"), + CONSTANT_DYNAMIC (17, "CONSTANT_DYNAMIC", "Dynamic"), + CONSTANT_INVOKEDYNAMIC (18, "CONSTANT_INVOKEDYNAMIC", "InvokeDynamic"), + CONSTANT_MODULE (19, "CONSTANT_MODULE", "Module"), + CONSTANT_PACKAGE (20, "CONSTANT_PACKAGE", "Package"); + + static final public int maxTag = 20; + + private final int value; + private final String parseKey; + private final String printval; + + ConstType(int val, String print, String parse) { + value = val; + parseKey = parse; + printval = print; + } + + public int value() { + return value; + } + + public String parseKey() { + return parseKey; + } + + public String printval() { + return printval; + } + + public void print(PrintWriter out) { + out.print(parseKey); + } + + @Override + public String toString() { + return "<" + printval + "> [" + Integer.toString(value) + "]"; + } + }; + + static public ConstType tag(int i) { + return ConstantTypes.get(i); + } + + static public ConstType tag(String parsekey) { + return NameToConstantType.get(parsekey); + } + + private static void registerConstantType(ConstType tt) { + NameToConstantType.put(tt.parseKey, tt); + ConstantTypes.put(tt.value, tt); + } + + /** + * Attribute descriptor enums + */ + static public enum AttrTag { + + // Constant for ME Spec (StackMap does not appear in SE VM Spec) + ATT_Unrecognized (0, "ATT_Unrecognized", ""), + ATT_StackMap (1, "ATT_StackMap", "StackMap"), + // Numbers corespond to VM spec (chapter 4.7.X) + ATT_ConstantValue (2, "ATT_ConstantValue", "ConstantValue"), + ATT_Code (3, "ATT_Code", "Code"), + ATT_StackMapTable (4, "ATT_StackMapTable", "StackMapTable"), + ATT_Exceptions (5, "ATT_Exceptions", "Exceptions"), + ATT_InnerClasses (6, "ATT_InnerClasses", "InnerClasses"), + ATT_EnclosingMethod (7, "ATT_EnclosingMethod", "EnclosingMethod"), + ATT_Synthetic (8, "ATT_Synthetic", "Synthetic"), + ATT_Signature (9, "ATT_Signature", "Signature"), + ATT_SourceFile (10, "ATT_SourceFile", "SourceFile"), + ATT_SourceDebugExtension (11, "ATT_SourceDebugExtension", "SourceDebugExtension"), + ATT_LineNumberTable (12, "ATT_LineNumberTable", "LineNumberTable"), + ATT_LocalVariableTable (13, "ATT_LocalVariableTable", "LocalVariableTable"), + ATT_LocalVariableTypeTable (14, "ATT_LocalVariableTypeTable", "LocalVariableTypeTable"), + ATT_Deprecated (15, "ATT_Deprecated", "Deprecated"), + ATT_RuntimeVisibleAnnotations (16, "ATT_RuntimeVisibleAnnotations", "RuntimeVisibleAnnotations"), + ATT_RuntimeInvisibleAnnotations (17, "ATT_RuntimeInvisibleAnnotations", "RuntimeInvisibleAnnotations"), + ATT_RuntimeVisibleParameterAnnotations (18, "ATT_RuntimeVisibleParameterAnnotations", "RuntimeVisibleParameterAnnotations"), + ATT_RuntimeInvisibleParameterAnnotations (19, "ATT_RuntimeInvisibleParameterAnnotations", "RuntimeInvisibleParameterAnnotations"), + ATT_AnnotationDefault (20, "ATT_AnnotationDefault", "AnnotationDefault"), + ATT_BootstrapMethods (21, "ATT_BootstrapMethods", "BootstrapMethods"), + ATT_RuntimeVisibleTypeAnnotations (22, "ATT_RuntimeVisibleTypeAnnotations", "RuntimeVisibleTypeAnnotations"), + ATT_RuntimeInvisibleTypeAnnotations (23, "ATT_RuntimeInvisibleTypeAnnotations", "RuntimeInvisibleTypeAnnotations"), + ATT_MethodParameters (24, "ATT_MethodParameters", "MethodParameters"), + ATT_Module (25, "ATT_Module", "Module"), + ATT_Version (26, "ATT_Version", "Version"), + ATT_TargetPlatform (27, "ATT_TargetPlatform", "TargetPlatform"), + ATT_MainClass (28, "ATT_MainClass", "MainClass"), + ATT_ModulePackages (29, "ATT_ModulePackages", "ModulePackages"), + ATT_ModuleMainClass (30, "ATT_ModuleMainClass", "ModuleMainClass"), + ATT_ModuleTarget (31, "ATT_ModuleTarget", "ModuleTarget"), + // JEP 181: class file 55.0 + ATT_NestHost (32, "ATT_NestHost", "NestHost"), + ATT_NestMembers (33, "ATT_NestMembers", "NestMembers"), + // JEP 359 Record(Preview): class file 58.65535 + // Record_attribute { + // u2 attribute_name_index; + // u4 attribute_length; + // u2 components_count; + // component_info components[components_count]; + // } + ATT_Record (34, "ATT_Record", "Record"), + // JEP 360 (Sealed types): class file 59.65535 + // PermittedSubclasses_attribute { + // u2 attribute_name_index; + // u4 attribute_length; + // u2 number_of_classes; + // u2 classes[number_of_classes]; + // } + ATT_PermittedSubclasses (35, "ATT_PermittedSubclasses", "PermittedSubclasses"); + + private final Integer value; + private final String printval; + private final String parsekey; + + AttrTag(Integer val, String print, String parse) { + value = val; + printval = print; + parsekey = parse; + } + + public String printval() { + return printval; + } + + public String parsekey() { + return parsekey; + } + } + + private static void registerAttrtag(AttrTag tg) { + NameToAttrTag.put(tg.parsekey, tg); + AttrTags.put(tg.value, tg); + } + + public static AttrTag attrtag(int val) { + AttrTag tg = AttrTags.get(val); + if (tg == null) { + tg = AttrTag.ATT_Unrecognized; + } + return tg; + } + + public static AttrTag attrtag(String idValue) { + AttrTag tg = NameToAttrTag.get(idValue); + if (tg == null) { + tg = AttrTag.ATT_Unrecognized; + } + return tg; + } + + public static String attrtagName(int subtag) { + AttrTag tg = AttrTags.get(subtag); + return tg.parsekey; + } + + public static int attrtagValue(String idValue) { + AttrTag tg = attrtag(idValue); + return tg.value; + } + + + /*-------------------------------------------------------- */ + /** + * SubTag enums + */ + static public enum SubTag { + REF_GETFIELD (1, "REF_getField"), + REF_GETSTATIC (2, "REF_getStatic"), + REF_PUTFIELD (3, "REF_putField"), + REF_PUTSTATIC (4, "REF_putStatic"), + REF_INVOKEVIRTUAL (5, "REF_invokeVirtual"), + REF_INVOKESTATIC (6, "REF_invokeStatic"), + REF_INVOKESPECIAL (7, "REF_invokeSpecial"), + REF_NEWINVOKESPECIAL (8, "REF_newInvokeSpecial"), + REF_INVOKEINTERFACE (9, "REF_invokeInterface"); + + private final Integer value; + private final String printval; + + SubTag(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + + public Integer value() { + return value; + } + } + + private static void registerSubtag(SubTag tg) { + NameToSubTag.put(tg.printval, tg); + SubTags.put(tg.value, tg); + } + + public static SubTag subtag(String subtag) { + return NameToSubTag.get(subtag); + } + + public static SubTag subtag(int subtag) { + return SubTags.get(subtag); + } + + public static String subtagName(int subtag) { + String retval = null; + SubTag tg = SubTags.get(subtag); + if (tg != null) { + retval = tg.printval; + } + return retval; + } + + public static int subtagValue(String idValue) { + int retval = 0; + SubTag tg = NameToSubTag.get(idValue); + if (tg != null) { + retval = tg.value; + } + return retval; + } + + /*-------------------------------------------------------- */ + /** + * BasicType enums + */ + static public enum BasicType { + T_INT (0x0000000a, "int"), + T_LONG (0x0000000b, "long"), + T_FLOAT (0x00000006, "float"), + T_DOUBLE (0x00000007, "double"), + T_CLASS (0x00000002, "class"), + T_BOOLEAN (0x00000004, "boolean"), + T_CHAR (0x00000005, "char"), + T_BYTE (0x00000008, "byte"), + T_SHORT (0x00000009, "short"); + + private final Integer value; + private final String printval; + + BasicType(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + } + + private static void registerBasicType(BasicType typ) { + NameToBasicType.put(typ.printval, typ); + BasicTypes.put(typ.value, typ); + } + + public static BasicType basictype(String idValue) { + return NameToBasicType.get(idValue); + } + + public static BasicType basictype(int subtag) { + return BasicTypes.get(subtag); + } + + public static String basictypeName(int subtag) { + String retval = null; + BasicType tg = BasicTypes.get(subtag); + if (tg != null) { + retval = tg.printval; + } + return retval; + } + + public static int basictypeValue(String idValue) { + int retval = -1; + BasicType tg = NameToBasicType.get(idValue); + if (tg != null) { + retval = tg.value; + } + return retval; + } + + /*-------------------------------------------------------- */ + /** + * AnnotElemType enums + */ + static public enum AnnotElemType { + + AE_BYTE ('B', "byte"), + AE_CHAR ('C', "char"), + AE_SHORT ('S', "short"), + AE_INT ('I', "int"), + AE_LONG ('J', "long"), + AE_FLOAT ('F', "float"), + AE_DOUBLE ('D', "double"), + AE_BOOLEAN ('Z', "boolean"), + AE_STRING ('s', "string"), + AE_ENUM ('e', "enum"), + AE_CLASS ('c', "class"), + AE_ANNOTATION ('@', "annotation"), + AE_ARRAY ('[', "array"), + AE_UNKNOWN ((char)0, "unknown"); + + private char value; + private final String printval; + + AnnotElemType(char val, String print) { + value = val; + printval = print; + } + + public char val() { + return value; + } + + public String printval() { + return printval; + } + } + + private static void registerAnnotElemType(AnnotElemType typ) { + NameToAnnotElemType.put(typ.printval, typ); + AnnotElemTypes.put(typ.value, typ); + } + + public static AnnotElemType annotElemType(String idValue) { + return NameToAnnotElemType.get(idValue); + } + + public static AnnotElemType annotElemType(char subtag) { + AnnotElemType type = AnnotElemTypes.get(subtag); + if ( type == null ) { + type = AnnotElemType.AE_UNKNOWN; + } + return type; + } + + public static String annotElemTypeName(char subtag) { + String retval = null; + AnnotElemType tg = AnnotElemTypes.get(subtag); + if (tg != null) { + retval = tg.printval; + } + return retval; + } + + public static char annotElemTypeVal(String idValue) { + char retval = 0; + AnnotElemType tg = NameToAnnotElemType.get(idValue); + if (tg != null) { + retval = tg.value; + } + return retval; + } + + + /*-------------------------------------------------------- */ + /** + * MapTypes table. These constants are used in stackmap pseudo-instructions only. + */ + static public enum StackMapType { + /* Type codes for StackMap attribute */ + ITEM_Bogus (0, "bogus", "B"), // an unknown or uninitialized value + ITEM_Integer (1, "int", "I"), // a 32-bit integer + ITEM_Float (2, "float", "F"), // not used + ITEM_Double (3, "double", "D"), // not used + ITEM_Long (4, "long", "L"), // a 64-bit integer + ITEM_Null (5, "null", "N"), // the type of null + ITEM_InitObject (6, "this", "IO"), // "this" in constructor + ITEM_Object (7, "CP", "O"), // followed by 2-byte index of class name + ITEM_NewObject (8, "at", "NO"), // followed by 2-byte ref to "new" + ITEM_UNKNOWN (null, "UNKNOWN", "UNKNOWN"); // placeholder for wrong types + + private Integer value; + private final String printval; + private final String parsekey; + + StackMapType(Integer val, String print, String parse) { + value = val; + printval = print; + parsekey = parse; + } + + public String parsekey() { + return parsekey; + } + + public String printval() { + return printval; + } + + public Integer value() { + return value; + } + } + + private static void registerStackMapType(StackMapType typ) { + KeyToStackMapType.put(typ.parsekey, typ); + NameToStackMapType.put(typ.printval, typ); + StackMapTypes.put(typ.value, typ); + } + + public static StackMapType stackMapType(int subtag, PrintWriter out) { + StackMapType type = StackMapTypes.get(subtag); + if (type == null || type == StackMapType.ITEM_UNKNOWN) { + if (out != null) + out.println("// Unknown StackMap type " + subtag); + type = StackMapType.ITEM_UNKNOWN; + type.value = subtag; + } + return type; + } + + public static StackMapType stackMapType(String subtag) { + return NameToStackMapType.get(subtag); + } + + public static StackMapType stackMapTypeKey(String subtag) { + return KeyToStackMapType.get(subtag); + } + + public static String stackMapTypeName(int subtag) { + String retval = null; + StackMapType tg = StackMapTypes.get(subtag); + if (tg != null) { + retval = tg.printval; + } + return retval; + } + + public static int stackMapTypeValue(String idValue) { + int retval = 0; + StackMapType tg = NameToStackMapType.get(idValue); + if (tg != null) { + retval = tg.value; + } + return retval; + } + + + /*-------------------------------------------------------- */ + /** + * StackMap-FrameType table. These constants are used in stackmap pseudo-instructions + * only. + */ + static public enum StackMapFrameType { + /* Type codes for StackMapFrame attribute */ + SAME_FRAME (0, "same"), + SAME_LOCALS_1_STACK_ITEM_FRAME (64, "stack1"), + SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME (247, "stack1_ex"), + CHOP_1_FRAME (250, "chop1"), + CHOP_2_FRAME (249, "chop2"), + CHOP_3_FRAME (248, "chop3"), + SAME_FRAME_EX (251, "same_ex"), + APPEND_FRAME (252, "append"), + FULL_FRAME (255, "full"); + + private final Integer value; + private final String parsekey; + + StackMapFrameType(Integer val, String print) { + value = val; + parsekey = print; + } + + public String parsekey() { + return parsekey; + } + + public Integer value() { + return value; + } + } + + private static void registerStackMapFrameType(StackMapFrameType typ) { + NameToStackMapFrameType.put(typ.parsekey, typ); + StackMapFrameTypes.put(typ.value, typ); + } + + public static StackMapFrameType stackMapFrameTypeVal(int subtag) { + return StackMapFrameTypes.get(subtag); + } + + public static String stackMapFrameTypeName(int subtag) { + String retval = null; + StackMapFrameType tg = StackMapFrameTypes.get(subtag); + if (tg != null) { + retval = tg.parsekey; + } + return retval; + } + + public static StackMapFrameType stackMapFrameType(int subtag) { + StackMapFrameType frametype; + if (subtag < StackMapFrameType.SAME_LOCALS_1_STACK_ITEM_FRAME.value()) { + // type is same_frame; + frametype = StackMapFrameType.SAME_FRAME; + } else if (subtag >= StackMapFrameType.SAME_LOCALS_1_STACK_ITEM_FRAME.value() + && subtag <= 127) { + // type is same_locals_1_stack_item_frame + frametype = StackMapFrameType.SAME_LOCALS_1_STACK_ITEM_FRAME; + + } else if (subtag >= StackMapFrameType.APPEND_FRAME.value() + && subtag < StackMapFrameType.FULL_FRAME.value()) { + // type is append_frame + frametype = StackMapFrameType.APPEND_FRAME; + } else { + frametype = StackMapFrameTypes.get(subtag); + } + return frametype; + } + + public static int stackMapFrameTypeValue(String idValue) { + int retval = 0; + StackMapFrameType tg = NameToStackMapFrameType.get(idValue); + if (tg != null) { + retval = tg.value; + } + return retval; + } + + /** + * CF_Context enums + */ + public enum CF_Context { + + CTX_CLASS (0, "class"), + CTX_FIELD (1, "field"), + CTX_METHOD (2, "method"), + CTX_INNERCLASS (3, "inner-class"), + CTX_MODULE (4, "module") ; + + private final int value; + private final String printval; + + CF_Context(int val, String print) { + value = val; + printval = print; + } + + boolean isOneOf(CF_Context... items) { + for(CF_Context item : items) { + if(item.value == value) { + return true; + } + } + return false; + } + + public int val() { + return value; + } + + public String printval() { + return printval; + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/TypeAnnotationData.java b/src/org/openjdk/asmtools/jasm/TypeAnnotationData.java new file mode 100644 index 0000000..4988362 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/TypeAnnotationData.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.jasm.TypeAnnotationTypes.ETargetType; +import org.openjdk.asmtools.jasm.TypeAnnotationTypes.TypePathEntry; + +import java.io.IOException; + +/** + * JVMS 4.7.20. + * type_annotation { + * u1 target_type; + * union { + * type_parameter_target; + * supertype_target; + * type_parameter_bound_target; + * empty_target; + * formal_parameter_target; + * throws_target; + * localvar_target; + * catch_target; + * offset_target; + * type_argument_target; + * } target_info; + * type_path target_path; + * u2 type_index; + * // + * // + * u2 num_element_value_pairs; + * { u2 element_name_index; + * element_value value; + * } element_value_pairs[num_element_value_pairs]; + * } + */ +public class TypeAnnotationData extends AnnotationData { + + protected ETargetType targetType; + protected TypeAnnotationTargetInfoData targetInfo; + protected TypeAnnotationTypePathData typePath; + + public TypeAnnotationData(Argument typeCPX, boolean invisible) { + super(typeCPX, invisible); + typePath = new TypeAnnotationTypePathData(); + } + + @Override + public int getLength() { + // lengthOf(annotations[]) + lengthOf(targetType) + lengthOf(targetInfo) + lengthOf(targetInfo) + return super.getLength() + 1 + targetInfo.getLength() + typePath.getLength(); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(targetType.value); + targetInfo.write(out); + typePath.write(out); + super.write(out); + } + + public void addTypePathEntry(TypePathEntry path) { + typePath.addTypePathEntry(path); + } + + @Override + public String toString() { + return toString(0); + } + + public String toString(int tabLevel) { + StringBuilder sb = new StringBuilder(tabString(tabLevel)); + sb.append(targetType.toString()). + append(' '). + append(targetInfo.toString(tabLevel)). + append(typePath.toString(tabLevel)); + return sb.toString(); + } +} diff --git a/src/org/openjdk/asmtools/jasm/TypeAnnotationTargetInfoData.java b/src/org/openjdk/asmtools/jasm/TypeAnnotationTargetInfoData.java new file mode 100644 index 0000000..10db33a --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/TypeAnnotationTargetInfoData.java @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * TargetInfo (4.7.20.1. The target_info union) + * + * BaseClass for any Type Annotation Target-Info. + */ +public abstract class TypeAnnotationTargetInfoData implements Data { + + protected TypeAnnotationTypes.ETargetType targettype = null; + + public TypeAnnotationTargetInfoData(TypeAnnotationTypes.ETargetType tt) { + targettype = tt; + } + + public TypeAnnotationTypes.ETargetType getTargetType() { + return targettype; + } + + public void print(PrintWriter out, String tab) { + // print the TargetType and TargetInfo + out.print(tab + " {"); + targettype.print(out); + _print(out, tab); + out.print(tab + "} "); + } + + public abstract void _print(PrintWriter out, String tab); + + public abstract void write(CheckedDataOutputStream out) throws IOException; + + @Override + public String toString() { + return toString(0); + } + + protected abstract void _toString(StringBuilder sb, int tabLevel); + + public String toString(int tabLevel) { + StringBuilder sb = new StringBuilder(tabString(tabLevel)); + // first print the target info name ( + sb.append(targettype.targetInfo().printValue()).append("_target "); + // get the sub-classes parts + _toString(sb, tabLevel); + return sb.toString(); + } + + /** + * type_parameter_target (4.7.20.1. The target_info union) + * + * The type_parameter_target item indicates that an annotation appears on the declaration of the i'th type parameter + * of a generic class, generic interface, generic method, or generic constructor. + * + * type_parameter_target { + * u1 type_parameter_index; + * } + */ + public static class type_parameter_target extends TypeAnnotationTargetInfoData { + + int typeParamIndex; + + public type_parameter_target(TypeAnnotationTypes.ETargetType tt, int index) { + super(tt); + typeParamIndex = index; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(typeParamIndex); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(typeParamIndex); + } + + @Override + public int getLength() { + return 1; + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + sb.append(tabString(tabLevel)).append(String.format("{ type_parameter_index: %d; }",typeParamIndex)); + } + } + + /** + * supertype_target (4.7.20.1. The target_info union) + * + * The supertype_target item indicates that an annotation appears on a type in the extends or implements clause of + * a class or interface declaration. + * + * supertype_target { + * u2 supertype_index; + * } + */ + public static class supertype_target extends TypeAnnotationTargetInfoData { + + int superTypeIndex; + + public supertype_target(TypeAnnotationTypes.ETargetType tt, int index) { + super(tt); + superTypeIndex = index; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(superTypeIndex); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(superTypeIndex); + } + + @Override + public int getLength() { + return 2; + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + sb.append(tabString(tabLevel)).append(String.format("{ supertype_index: %d; }",superTypeIndex)); + } + } + + /** + * type_parameter_bound_target (4.7.20.1. The target_info union) + * + * The type_parameter_bound_target item indicates that an annotation appears on the i'th bound of the j'th type parameter + * declaration of a generic class, interface, method, or constructor. + * + * type_parameter_bound_target { + * u1 type_parameter_index; + * u1 bound_index; + * } + */ + public static class type_parameter_bound_target extends TypeAnnotationTargetInfoData { + + int typeParamIndex; + int boundIndex; + + public type_parameter_bound_target(TypeAnnotationTypes.ETargetType tt, int pindx, int bindx) { + super(tt); + typeParamIndex = pindx; + boundIndex = bindx; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(typeParamIndex); + out.writeByte(boundIndex); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(typeParamIndex); + out.print(" "); + out.print(boundIndex); + } + + @Override + public int getLength() { + return 2; + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + sb.append(tabString(tabLevel)).append(String.format("{ type_parameter_index: %d; bound_index: %d; }", + typeParamIndex, boundIndex)); + } + } + + /** + * empty_target (4.7.20.1. The target_info union) + * + * The empty_target item indicates that an annotation appears on either the type in a field declaration, + * the return type of a method, the type of a newly constructed object, or the receiver type of a method or constructor. + * + * empty_target { + * } + */ + public static class empty_target extends TypeAnnotationTargetInfoData { + + public empty_target(TypeAnnotationTypes.ETargetType tt) { + super(tt); + } + + @Override + public void _print(PrintWriter out, String tab) { + // do nothing + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + // do nothing + } + + @Override + public int getLength() { return 0; } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + sb.append(tabString(tabLevel)).append("{ }"); + } + } + + /** + * formal_parameter_target (4.7.20.1. The target_info union) + * + * The formal_parameter_target item indicates that an annotation appears on the type in a formal parameter + * declaration of a method, constructor, or lambda expression. + * + * formal_parameter_target { + * u1 formal_parameter_index; + * } + */ + public static class formal_parameter_target extends TypeAnnotationTargetInfoData { + + int formalParamIndex; + + public formal_parameter_target(TypeAnnotationTypes.ETargetType tt, int index) { + super(tt); + formalParamIndex = index; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(formalParamIndex); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(formalParamIndex); + } + + @Override + public int getLength() { + return 1; + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + sb.append(tabString(tabLevel)).append(String.format("{ formal_parameter_index: %d; }",formalParamIndex)); + } + } + + /** + * throws_target (4.7.20.1. The target_info union) + * + * The throws_target item indicates that an annotation appears on the i'th type in the throws clause of a method or + * constructor declaration. + * + * throws_target { + * u2 throws_type_index; + * } + */ + public static class throws_target extends TypeAnnotationTargetInfoData { + + int throwsTypeIndex; + + public throws_target(TypeAnnotationTypes.ETargetType tt, int index) { + super(tt); + throwsTypeIndex = index; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(throwsTypeIndex); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(throwsTypeIndex); + } + + @Override + public int getLength() { + return 2; + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + sb.append(tabString(tabLevel)).append(String.format("{ throws_type_index: %d; }",throwsTypeIndex)); + } + } + + /** + * localvar_target (4.7.20.1. The target_info union) + * + * The localvar_target item indicates that an annotation appears on the type in a local variable declaration, + * including a variable declared as a resource in a try-with-resources statement. + * + * localvar_target { + * u2 table_length; + * { u2 start_pc; + * u2 length; + * u2 index; + * } table[table_length]; + * } + */ + public static class localvar_target extends TypeAnnotationTargetInfoData { + + public class LocalVar_Entry { + + public int startPC; + public int length; + public int cpx; + + public LocalVar_Entry(int st, int len, int index) { + startPC = st; + length = len; + cpx = index; + } + + void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(startPC); + out.writeShort(length); + out.writeShort(cpx); + } + + public void _print(PrintWriter out, String tab) { + out.print(tab + "{"); + out.print(startPC); + out.print(" "); + out.print(length); + out.print(" "); + out.print(cpx); + out.print("}"); + } + + public String toString() { + return String.format("start_pc: %d, length: %d, index: %d", startPC, length, cpx); + } + } + + ArrayList table = null; + + public localvar_target(TypeAnnotationTypes.ETargetType tt, int size) { + super(tt); + table = new ArrayList<>(size); + } + + public void addEntry(int startPC, int length, int cpx) { + LocalVar_Entry entry = new LocalVar_Entry(startPC, length, cpx); + table.add(entry); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(table.size()); + for (LocalVar_Entry entry : table) { + entry.write(out); + } + } + + @Override + public void _print(PrintWriter out, String tab) { + String innerTab = tab + " "; + for (LocalVar_Entry entry : table) { + entry._print(out, innerTab); + } + out.print(tab); + } + + @Override + public int getLength() { + return 2 + // U2 for table size + (6 * table.size()); // (3 * U2) for each table entry + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + int i = 0; + sb.append(tabString(tabLevel)).append(String.format("{ %d {", table.size())); + for (LocalVar_Entry entry : table) { + sb.append(String.format(" [%d]: %s;", i++, entry.toString())); + } + sb.append(" } }"); + } + } + + /** + * catch_target (4.7.20.1. The target_info union) + * + * The catch_target item indicates that an annotation appears on the i'th type in an exception parameter declaration. + * + * catch_target { + * u2 exception_table_index; + * } + */ + public static class catch_target extends TypeAnnotationTargetInfoData { + + int exceptionTableIndex; + + public catch_target(TypeAnnotationTypes.ETargetType tt, int index) { + super(tt); + exceptionTableIndex = index; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(exceptionTableIndex); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(exceptionTableIndex); + } + + @Override + public int getLength() { + return 2; + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + sb.append(tabString(tabLevel)).append(String.format("{ exception_table_index: %d; }",exceptionTableIndex)); + } + } + + /** + * offset_target (4.7.20.1. The target_info union) + * + * The offset_target item indicates that an annotation appears on either the type in an instanceof expression or + * a new expression, or the type before the :: in a method reference expression. + * + * offset_target { + * u2 offset; + * } + */ + public static class offset_target extends TypeAnnotationTargetInfoData { + + int offset; + + public offset_target(TypeAnnotationTypes.ETargetType tt, int offset) { + super(tt); + this.offset = offset; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(offset); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(offset); + } + + @Override + public int getLength() { + return 2; + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + sb.append(tabString(tabLevel)).append(String.format("{ offset: %d; }", offset)); + } + } + + /** + * type_argument_target (4.7.20.1. The target_info union) + * + * The type_argument_target item indicates that an annotation appears either on the i'th type in a cast expression, + * or on the i'th type argument in the explicit type argument list for any of the following: a new expression, + * an explicit constructor invocation statement, a method invocation expression, or a method reference expression + * + * type_argument_target { + * u2 offset; + * u1 type_argument_index; + * } + */ + public static class type_argument_target extends TypeAnnotationTargetInfoData { + + int offset; + int typeArgumentIndex; + + public type_argument_target(TypeAnnotationTypes.ETargetType tt, int offset, int index) { + super(tt); + this.offset = offset; + typeArgumentIndex = index; + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeShort(offset); + out.writeByte(typeArgumentIndex); + } + + @Override + public void _print(PrintWriter out, String tab) { + out.print(" "); + out.print(offset); + out.print(" "); + out.print(typeArgumentIndex); + } + + @Override + public int getLength() { + return 3; + } + + @Override + protected void _toString(StringBuilder sb, int tabLevel) { + sb.append(tabString(tabLevel)).append(String.format("{ offset: %d; type_argument_index: %d; }", + offset, typeArgumentIndex)); + } + } + +} + diff --git a/src/org/openjdk/asmtools/jasm/TypeAnnotationTypePathData.java b/src/org/openjdk/asmtools/jasm/TypeAnnotationTypePathData.java new file mode 100644 index 0000000..a4a3813 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/TypeAnnotationTypePathData.java @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import org.openjdk.asmtools.jasm.TypeAnnotationTypes.TypePathEntry; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * JVMS 4.7.20.2. The type_path structure + * + * type_path { + * u1 path_length; + * { u1 type_path_kind; + * u1 type_argument_index; + * } path[path_length]; + * } + */ +public class TypeAnnotationTypePathData implements Data { + + private ArrayList typePathEntries = new ArrayList<>(); + + public void addTypePathEntry( TypePathEntry entry) { + typePathEntries.add(entry); + } + + @Override + public void write(CheckedDataOutputStream out) throws IOException { + out.writeByte(typePathEntries.size()); + for (TypePathEntry entry : typePathEntries) { + out.writeByte(entry.getTypePathKind()); + out.writeByte(entry.getTypeArgumentIndex()); + } + } + + @Override + public int getLength() { + return 1 + typePathEntries.size() * 2; + } + + public String toString(int tabLevel) { + String buffer = ""; + if( typePathEntries.size() > 0 ) { + StringBuilder sb = new StringBuilder(tabString(tabLevel)); + sb.append(" [ "); + boolean first = true; + for (TypePathEntry entry : typePathEntries) { + if (!first) + sb.append(", "); + first = false; + sb.append(entry.toString()); + } + sb.append("]"); + buffer = sb.toString(); + } + return buffer; + } + + /** + * jdis: print the type_path structure + */ + public void print(PrintWriter out, String tab) { + if( typePathEntries.size() > 0 ) { + out.print(tab + " {"); + boolean first = true; + for (TypePathEntry entry : typePathEntries) { + if (!first) { + out.print(", "); + } + first = false; + out.print(entry.toString()); + } + out.print(tab + "} "); + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/TypeAnnotationTypes.java b/src/org/openjdk/asmtools/jasm/TypeAnnotationTypes.java new file mode 100644 index 0000000..50d398e --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/TypeAnnotationTypes.java @@ -0,0 +1,312 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jasm; + +import java.io.PrintWriter; + +/** + * Type annotation types: target_type, target_info && target_path + */ +public class TypeAnnotationTypes { + + /** + * Interpretation of type_path_kind values (Table 4.7.20.2-A) + */ + public enum EPathKind { + ARRAY(0), + INNER_TYPE(1), + WILDCARD(2), + TYPE_ARGUMENT(3); + + private final int tag; + public static final int maxLen = 3; + + EPathKind(int tag) { + this.tag = tag; + } + + public int tag() { + return tag; + } + + public String parseKey() { + return this.toString(); + } + + static EPathKind getPathKind(String token) { + for (EPathKind pk : values()) { + if( pk.parseKey().equals(token)) { + return pk; + } + } + return null; + } + } + + // will throw ArrayIndexOutOfBounds if i < 0 or i > 3 + static public EPathKind getPathKind(int i) { + return EPathKind.values()[i]; + } + + static public class TypePathEntry { + + private final EPathKind kind; + private final int typeArgumentIndex; + + public TypePathEntry(int kind, int typeArgumentIndex) { + this.kind = getPathKind(kind); + this.typeArgumentIndex = typeArgumentIndex; + } + + public TypePathEntry(EPathKind kind, int typeArgumentIndex) { + this.kind = kind; + this.typeArgumentIndex = typeArgumentIndex; + } + + public int getTypePathKind() { + return kind.tag(); + } + + public int getTypeArgumentIndex() { + return typeArgumentIndex; + } + + @Override + public String toString() { + // Chapter 4.7.20.2 The type_path structure + // if the value of the type_path_kind is 0,1, or 2, thebn the value of the + // type_argument_index item is 0. + return kind.parseKey() + ( kind.tag == 3 ? + JasmTokens.Token.LBRACE.parseKey() + typeArgumentIndex + JasmTokens.Token.RBRACE.parseKey() : + ""); + } + } + + /** + * union { + * type_parameter_target; + * supertype_target; + * type_parameter_bound_target; + * empty_target; + * method_formal_parameter_target; + * throws_target; + * localvar_target; + * catch_target; + * offset_target; + * type_argument_target; + * } target_info; + */ + public enum ETargetInfo { + TYPEPARAM ("TYPEPARAM", "type_parameter"), + SUPERTYPE ("SUPERTYPE", "supertype"), + TYPEPARAM_BOUND ("TYPEPARAM_BOUND", "type_parameter_bound"), + EMPTY ("EMPTY", "empty"), + METHODPARAM ("METHODPARAM", "formal_parameter"), + EXCEPTION ("EXCEPTION", "throws"), + LOCALVAR ("LOCALVAR", "localvar"), + CATCH ("CATCH", "catch"), + OFFSET ("OFFSET", "offset"), + TYPEARG ("TYPEARG", "type_argument"); + + private final String parseKey; + private final String printValue; + + ETargetInfo(String parse, String printValue) { + parseKey = parse; + this.printValue = printValue; + } + public String parseKey() { + return this.parseKey; + } + + public String printValue() { + return this.printValue; + } + } + + /** + * Interpretation of target_type values (Table 4.7.20-A./B.) + */ + static public enum ETargetType { + class_type_param (0x00, "CLASS_TYPE_PARAMETER", ETargetInfo.TYPEPARAM, "class/interface type parameter"), + meth_type_param (0x01, "METHOD_TYPE_PARAMETER", ETargetInfo.TYPEPARAM, "method/constructor type parameter"), + class_exts_impls (0x10, "CLASS_EXTENDS", ETargetInfo.SUPERTYPE, "class extends/implements"), + class_type_param_bnds (0x11, "CLASS_TYPE_PARAMETER_BOUND", ETargetInfo.TYPEPARAM_BOUND, "class/interface type parameter bounds"), + meth_type_param_bnds (0x12, "METHOD_TYPE_PARAMETER_BOUND", ETargetInfo.TYPEPARAM_BOUND, "method/constructor type parameter bounds"), + field (0x13, "FIELD", ETargetInfo.EMPTY, "field"), + meth_ret_type (0x14, "METHOD_RETURN", ETargetInfo.EMPTY, "method return type"), + meth_receiver (0x15, "METHOD_RECEIVER", ETargetInfo.EMPTY, "method receiver"), + meth_formal_param (0x16, "METHOD_FORMAL_PARAMETER", ETargetInfo.METHODPARAM, "method formal parameter type"), + throws_type (0x17, "THROWS", ETargetInfo.EXCEPTION, "exception type in throws"), + + local_var (0x40, "LOCAL_VARIABLE", ETargetInfo.LOCALVAR, "local variable"), + resource_var (0x41, "RESOURCE_VARIABLE", ETargetInfo.LOCALVAR, "resource variable"), + exception_param (0x42, "EXCEPTION_PARAM", ETargetInfo.CATCH, "exception parameter"), + type_test (0x43, "INSTANCEOF", ETargetInfo.OFFSET, "type test (instanceof)"), + obj_creat (0x44, "NEW", ETargetInfo.OFFSET, "object creation (new)"), + constr_ref_receiver (0x45, "CONSTRUCTOR_REFERENCE_RECEIVER", ETargetInfo.OFFSET, "constructor reference receiver"), + meth_ref_receiver (0x46, "METHOD_REFERENCE_RECEIVER", ETargetInfo.OFFSET, "method reference receiver"), + cast (0x47, "CAST", ETargetInfo.TYPEARG, "cast"), + constr_invoc_typearg (0x48, "CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT", ETargetInfo.TYPEARG, "type argument in constructor call"), + meth_invoc_typearg (0x49, "METHOD_INVOCATION_TYPE_ARGUMENT", ETargetInfo.TYPEARG, "type argument in method call"), + constr_ref_typearg (0x4A, "CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT", ETargetInfo.TYPEARG, "type argument in constructor reference"), + meth_ref_typearg (0x4B, "METHOD_REFERENCE_TYPE_ARGUMENT", ETargetInfo.TYPEARG, "type argument in method reference"); + + public static final int maxTag = 0x9A; + public static final int maxLen = 36; + + public final int value; + private final String parseKey; + private final ETargetInfo targetInfo; + private final String printVal; + + ETargetType(int val, String parse, ETargetInfo targetInfo, String printVal) { + value = val; + parseKey = parse; + this.targetInfo = targetInfo; + this.printVal = printVal; + } + + public String parseKey() { + return parseKey; + } + + public String infoKey() { + return targetInfo.parseKey(); + } + + public ETargetInfo targetInfo() { + return targetInfo; + } + + public void print(PrintWriter out) { + out.print(parseKey); + } + + @Override + public String toString() { + return String.format("%s[%#x]", parseKey, value); + } + + public static ETargetType getTargetType(int typeCode) { + for( ETargetType type: ETargetType.values() ) { + if (type.value == typeCode) { + return type; + } + } + return null; + } + + static public ETargetType getTargetType(String typeName) { + for( ETargetType type: ETargetType.values() ) { + if (type.parseKey.equals(typeName)) { + return type; + } + } + return null; + } + }; + + /* TypeAnnotationVisitor Methods */ + public static class TypeAnnotationTargetVisitor { + + public final void visit(ETargetType tt) { + switch (tt) { + case class_type_param: + case meth_type_param: + visit_type_param_target(tt); + break; + case class_exts_impls: + visit_supertype_target(tt); + break; + case class_type_param_bnds: + case meth_type_param_bnds: + visit_typeparam_bound_target(tt); + break; + case field: + case meth_ret_type: + case meth_receiver: + visit_empty_target(tt); + break; + case meth_formal_param: + visit_methodformalparam_target(tt); + break; + case throws_type: + visit_throws_target(tt); + break; + case local_var: + case resource_var: + visit_localvar_target(tt); + break; + case exception_param: + visit_catch_target(tt); + break; + case type_test: + case obj_creat: + case constr_ref_receiver: + case meth_ref_receiver: + visit_offset_target(tt); + break; + + case cast: + case constr_invoc_typearg: + case meth_invoc_typearg: + case constr_ref_typearg: + case meth_ref_typearg: + + visit_typearg_target(tt); + break; + } + } + + public void visit_type_param_target(ETargetType tt) { + } + + public void visit_supertype_target(ETargetType tt) { + } + + public void visit_typeparam_bound_target(ETargetType tt) { + } + + public void visit_empty_target(ETargetType tt) { + } + + public void visit_methodformalparam_target(ETargetType tt) { + } + + public void visit_throws_target(ETargetType tt) { + } + + public void visit_localvar_target(ETargetType tt) { + } + + public void visit_catch_target(ETargetType tt) { + } + + public void visit_offset_target(ETargetType tt) { + } + + public void visit_typearg_target(ETargetType tt) { + } + } +} diff --git a/src/org/openjdk/asmtools/jasm/i18n.properties b/src/org/openjdk/asmtools/jasm/i18n.properties new file mode 100644 index 0000000..ea92546 --- /dev/null +++ b/src/org/openjdk/asmtools/jasm/i18n.properties @@ -0,0 +1,192 @@ +# Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +jasm.usage=\ +Usage: java -jar asmtools.jar jasm [options] file.jasm...\n\ +where possible options include: + +jasm.opt.d=\ +\ -d destdir directory to place resulting .class files +jasm.opt.v=\ +\ -v add trace information +jasm.opt.g=\ +\ -g add debug information +jasm.opt.version=\ +\ -version prints the program version +jasm.opt.nowrite=\ +\ -nowrite do not write resulting .class files +jasm.opt.strict=\ +\ -strict consider warnings as errors +jasm.opt.nowarn=\ +\ -nowarn do not print warnings +jasm.opt.cv=\ +\ -cv major.minor set operating class file version (by default {0}.{1}) + + +jasm.error.d_requires_argument=-d requires argument +jasm.error.does_not_exist={0} does not exist +jasm.error.cv_requires_arg=-cv requires argument +jasm.error.invalid_major_minor_param=invalid parameter major.minor +jasm.error.invalid_option=invalid option: {0} +jasm.error.cannot_read=cannot read {0} +jasm.error.cannot_write=cannot write {0} +jasm.error.fatal_error=fatal error +jasm.error.fatal_exception=fatal exception + + +# Scanner: +err.invalid.escape.char=Invalid escape character. +err.eof.in.comment=Comment not terminated at end of input. +err.invalid.number=Invalid character "{0}" in number. +err.invalid.octal.number=Invalid character in octal number. +err.overflow=Numeric overflow. +err.float.format=Invalid floating point format. +err.eof.in.string=String not terminated at end of input. +err.newline.in.string=String not terminated at end of line. +err.funny.char=Invalid character in input. +err.unbalanced.paren=Unbalanced parentheses. +# Parser: +err.package.repeated=Package statement repeated. +warn.intf.repeated=Interface {0} repeated. +warn.exc.repeated=Exception repeated in throws clause. +warn.record.repeated=Record attribute repeated. +err.multiple.inherit=Multiple inheritance is not supported. +err.toplevel.expected=Class, module or interface declaration expected. +err.const.def.expected=Constant declaration expected. +err.const.undecl=Constant #{0} not declared. +err.const.redecl=Constant {0} redeclared. +warn.const0.redecl=Re-declaration of Constant #0 cannot be written to the class file. +err.field.expected=Field, Method, NestMembers, NestHost or Record declaration expected. +err.token.expected={0} expected. +err.identifier.expected=Identifier expected. +err.extra.nesthost.attribute=There may be at most one NestHost attribute. +err.extra.nestmembers.attribute=There may be at most one NestMembers attribute. +err.extra.permittedsubclasses.attribute=There may be at most one PermittedSubclasses attribute. +err.extra.record.attribute=There may be at most one Record attribute. +err.grouped.component.expected=Either an annotation or a record component expected. +warn.no.components.in.record.attribute=Record should have at least one component. +err.one.of.two.token.expected=Either #{0} or #{1} token expected. + +err.both.nesthost.nestmembers.found=The attributes table of a ClassFile structure must not contain both a NestMembers attribute and a NestHost attribute. +err.name.expected=Name expected, got {0}. +err.module.name.expected=Module name expected, got {0}. +err.int.expected=Integer expected. +err.neg.forbidden=Negative integer is not allowed here. +err.value.large=Value doesn't fit in {0}. +err.value.expected=Value expected. +err.wrong.mnemocode=Invalid mnemocode ({0}). +err.default.redecl=Default statement already declared in this table. +err.long.switchtable=Switchtable too long: > {0}. +err.io.exception=I/O error in {0}. +warn.wrong.tag=Wrong tag: {0} expected. +warn.wrong.tag2=Wrong tags: {0} or {1} expected. +# Code Gen: +err.locvar.redecl=Local variable {0} redeclared. +err.locvar.undecl=Local variable {0} not declared. +#err.locvar.expected=Local variable expected. +err.label.redecl=Label {0} redeclared. +err.label.undecl=Label {0} not declared. +err.label.expected=Label expected. +err.subtag.expected=Subtag expected. +err.type.expected=Type expected. +err.trap.tryredecl= redeclared. +err.trap.endtryredecl= redeclared. +err.trap.notry=No found. +err.trap.noendtry=No found. +warn.trap.notref=No declared. +err.cannot.write=Cannot write to {0}. +err.msig.malformed=Malformed method signature at char {0}. [err={1}] +err.no.classname=Class name not defined. +warn.msig.more255=Number of parameters too large ({0}>255). +warn.illslot=Local variable at Illegal slot {0}. +warn.repeated.modifier=Repeated modifier. +warn.invalid.modifier.init=invalid modifier for method \"{0}\". +warn.invalid.modifier.fiva=at most one of final and volatile modifiers can be used for a field +warn.invalid.modifier.intfield=interface field must be public static final only +warn.init.in_int= method cannot be placed in an interface. +warn.invalid.modifier.intmth=interface method must be abstract public only \"{0}\". +warn.invalid.modifier.abst=invalid modifier for abstract method. +# +warn.invalid.modifier.field=invalid modifier(s) for a field \"{0}\" +warn.invalid.modifier.mth=invalid modifier(s) for a method \"{0}\" +warn.invalid.modifier.innerclass=invalid modifier for an inner class \"{0}\" +# +warn.invalid.modifier.class=invalid modifier(s) for a class \"{0}\" +warn.invalid.modifier.int=invalid modifier(s) for an interface \"{0}\" +# +warn.invalid.modifier.acc=at most one of public, protected, and private modifiers can be used. +warn.invalid.modifier.int.abs=interface class must have abstract modifier. +warn.missing.modifier.class=class or enum declaration missing. +warn.invalid.modifier.class.finabs=class cannot be both abstract and final. +warn.invalid.modifier.class.intenum=cannot be both interface and enum. +err.itemtype.expected=StackMap item type expected instead of {0}. +err.localsmap.repeated=locals_map redeclared. +err.invalid.stack.frame.type=invalid stack frame type. +err.invalid.offset.same.frame=offset value more than 64 for the 'same_frame' type frame. +err.no.stack.map.same.locals=stack map element for the 'same_locals_1_stack_item_frame' type frame is absent. +err.should.be.only.one.stack.map.element=should be only one stack map element for the 'same_locals_1_stack_item_frame' type frame. +err.invalid.offset.same.locals=offset value more than 64 for the 'same_locals_1_stack_item_frame' type frame. +err.unexpected.stack.maps=there are unexpected stack maps. +err.unexpected.locals.maps=there are unexpected locals maps. +err.no.locals.map.append=locals map element for the 'append_frame' type frame is absent. +err.more.locals.map.elements=there are more than 3 locals map element for the 'append_frame' type frame. +err.stackmap.repeated=stack_map redeclared. +err.version.expected=class file version expected +err.invalid.innerclass=Invalid declaration of Inner Class +err.invalid.bootstrapmethod=Invalid declaration of BootstrapMethod Entry +err.frametype.repeated=Frametype repeated +err.invalid.paramnum=Invalid Parameter Number: {0}. +err.duplicate.paramnum=Duplicate Parameter Number: {0}. +err.paramname.constnum.invaltype=ParameterName CPX at {0} is not a ConstantString. +err.paramname.token.unexpected=Incorrect ParamName, unrecognized token: \"{0}\". +# +# annotations Errors +# +err.incorrect.annot.class=Incorrect Annotation (class), expected class name or CPX), got \"{0}\". +err.incorrect.annot.enum=Incorrect Annotation (enum), expected type field IDENT, \"{0}\". +err.incorrect.annot.enum.cpx==Incorrect Annotation (enum), expected type field CPX. +err.incorrect.annot.token=Incorrect Annotation, unrecognized token: \"{0}\". +err.incorrect.annot.bool=Incorrect Annotation (boolean), expected Integer), got \"{0}\". +err.incorrect.annot.byte=Incorrect Annotation (byte), expected Integer), got \"{0}\". +err.incorrect.annot.char=Incorrect Annotation (char), expected Integer), got \"{0}\". +err.incorrect.annot.short=Incorrect Annotation (short), expected Integer), got \"{0}\". +err.incorrect.annot.keyword=Incorrect Annotation keyword \"{0}\". +err.incorrect.typeannot.target=Incorrect TypeAnnotation target \"{0}\". +err.incorrect.typeannot.targtype.string=Incorrect TypeAnnotation \"{0}\" argument: (expected String), \"{1}\". +err.incorrect.typeannot.targtype.int=Incorrect TypeAnnotation \"{0}\" argument: (expected Integer), \"{1}\". +err.incorrect.typeannot.pathentry=Incorrect TypeAnnotation TargetPath PathEntry \"{0}\". +err.incorrect.typeannot.pathentry.argindex=Incorrect TypeAnnotation TargetPath PathEntry ArgIndex (expected Integer), \"{0}\". +# +# module Errors +err.module.statement.expected= Module statement expected. +err.requires.expected=Module statement \"requires [transitive] [static] ModuleName;\" expected. +warn.dot.will.be.converted=Forward slash \"/\" expected instead of dot \".\". The dot is replaced by \"/\". +# +# Compiler Errors +# +comperr.constcell.nullvalset="Cell without value in setCell" +comperr.constcell.nullvalhash="Cell without value in cpoolHashByValue" +comperr.constcell.invarg="Cell[{0}] has #{1}" +comperr.constcell.nullval="ConstCell.value=null??" +comperr.val.noteq="Values not eq" +comperr.instr.nullarg="null arg for {0}" +comperr.instr.arglong="Too long argument of {0}: {1}" +comperr.instr.opclen="Wrong opcLength({0})" diff --git a/src/org/openjdk/asmtools/jcdec/Main.java b/src/org/openjdk/asmtools/jcdec/Main.java new file mode 100644 index 0000000..013cf25 --- /dev/null +++ b/src/org/openjdk/asmtools/jcdec/Main.java @@ -0,0 +1,957 @@ +/* + * Copyright (c) 2009, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcdec; + +import static org.openjdk.asmtools.jcoder.JcodTokens.*; +import org.openjdk.asmtools.jdis.uEscWriter; +import org.openjdk.asmtools.util.I18NResourceBundle; +import org.openjdk.asmtools.util.ProductInfo; +import java.io.DataInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Main program of the JavaCard DeCoder + * + */ +public class Main { + + /*-------------------------------------------------------- */ + /* Main Fields */ + /** + * Name of the program. + */ + String program; + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + /** + * The stream where error message are printed. + */ + PrintWriter out; + boolean DebugFlag = false; + boolean printDetails = false; + int shift = 0; + private static final char hexTable[] = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + /*-------------------------------------------------------- */ + + static String toHex(long val, int width) { + StringBuffer s = new StringBuffer(); + for (int i = width * 2 - 1; i >= 0; i--) { + s.append(hexTable[((int) (val >> (4 * i))) & 0xF]); + } + return "0x" + s.toString(); + } + + static String toHex(long val) { + int width; + for (width = 8; width > 0; width--) { + if ((val >> (width - 1) * 8) != 0) { + break; + } + } + return toHex(val, width); + } + + void printByteHex(PrintWriter out, int b) { + out.print(hexTable[(b >> 4) & 0xF]); + out.print(hexTable[b & 0xF]); + } + + /*========================================================*/ + void out_begin(String s) { + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.println(s); + shift++; + } + + void out_print(String s) { + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.print(s); + } + + void out_println(String s) { + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.println(s); + } + + void out_end(String s) { + shift--; + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.println(s); + } + + String startArray(int length) { + return "[" + (printDetails ? Integer.toString(length) : "") + "]"; + } + + void printBytes(DataInputStream in, int len) throws IOException { + try { + for (int i = 0; i < len; i++) { + if (i % 8 == 0) { + out_print("0x"); + } + printByteHex(out, in.readByte()); + if (i % 8 == 7) { + out.println(";"); + } + } + } finally { + if (len % 8 != 0) { + out.println(";"); + } + } + } + + /*========================================================*/ + static final int EXPORT_MAGIC = 0x00FACADE; + static final int HEADER_MAGIC = 0xDECAFFED; + static String[] compNames = { + "Header", + "Directory", + "Applet", + "Import", + "ConstantPool", + "Class", + "Method", + "StaticField", + "RefLocation", + "Export", + "Descriptor" + }; + + static String compName(int compNum) { + try { + return compNames[compNum - 1]; + } catch (ArrayIndexOutOfBoundsException e) { + return "tag " + compNum + "???"; + } + } + String[] cPoolStrings; + + void decodeAttr(DataInputStream in) throws IOException { + int name_cpx = in.readUnsignedShort(), len = in.readInt(); + String AttrName = null; + String endingComment = "Attr(#" + name_cpx + ")"; + try { + endingComment = AttrName = cPoolStrings[name_cpx]; + } catch (ArrayIndexOutOfBoundsException e) { + } + if (printDetails) { + out_begin("Attr(#" + name_cpx + ", " + len + ") { // " + AttrName); + } else { + out_begin("Attr(#" + name_cpx + ") { // " + AttrName); + } + if (AttrName == null) { + printBytes(in, len); + } else if (AttrName.equals("ConstantValue")) { + if (len != 2) { + out_println("// invalid length of ConstantValue attr: " + len + " (should be 2)"); + printBytes(in, len); + } else { + out_println("#" + in.readUnsignedShort() + ";"); + } + } else { + printBytes(in, len); + } + out_end("} // end " + endingComment); + } + + void decodeExp(String inpName) throws IOException { + DataInputStream in = new DataInputStream(new FileInputStream(inpName)); + out_println("file " + inpName); + out_begin("{ // export file"); + + int magic = in.readInt(); + out_print(toHex(magic, 4) + "; // "); + if (magic != EXPORT_MAGIC) { + out.print("wrong magic: 0x" + Integer.toString(EXPORT_MAGIC, 16) + " expected"); + } else { + out_print("magic"); + } + out.println(); + out_println(in.readUnsignedByte() + "b; // minor version"); + out_println(in.readUnsignedByte() + "b; // major version"); + + int cp_count = in.readUnsignedShort(); + cPoolStrings = new String[cp_count]; + out_begin(startArray(cp_count) + " { // Constant Pool"); + for (int i = 0; i < cp_count; i++) { + int tag = in.readUnsignedByte(); + ConstType tg = constType(tag); + switch (tg) { + case CONSTANT_UTF8: + out_print("Utf8 \""); + + StringBuffer sb = new StringBuffer(); + String s = in.readUTF(); + cPoolStrings[i] = s; + for (int k = 0; k < s.length(); k++) { + char c = s.charAt(k); + switch (c) { + case '\t': + sb.append('\\').append('t'); + break; + case '\n': + sb.append('\\').append('n'); + break; + case '\r': + sb.append('\\').append('r'); + break; + case '\"': + sb.append('\\').append('\"'); + break; + default: + sb.append(c); + } + } + out.println(sb.append("\"; // #").append(i).toString()); + break; + + case CONSTANT_INTEGER: + out_println("int " + toHex(in.readInt(), 4) + "; // #" + i); + break; + + case CONSTANT_CLASS: + out_println("class #" + in.readUnsignedShort() + "; // #" + i); + break; + + case CONSTANT_JAVACARD_PACKAGE: + out_begin("package { // #" + i); + out_println(toHex(in.readUnsignedByte(), 1) + "; // flags"); + out_println("#" + in.readUnsignedShort() + "; // name"); + out_println(in.readUnsignedByte() + "b; // minor version"); + out_println(in.readUnsignedByte() + "b; // major version"); + int aid_len = in.readUnsignedByte(); + out_begin("Bytes" + startArray(aid_len) + "b {"); + printBytes(in, aid_len); + out_end("};"); // Bytes[] + out_end("};"); // package info + break; + + default: + throw new Error("invalid constant type: " + (int) tag); + } + } + ; + out_end("} // Constant pool"); + out_println("#" + in.readUnsignedShort() + "; // this package"); + int class_count = in.readUnsignedByte(); + out_begin(startArray(class_count) + "b { // classes"); + for (int i = 0; i < class_count; i++) { + out_begin("{ // class " + i); + + out_println(in.readUnsignedByte() + "b; // token"); + + int flags = in.readUnsignedShort(); + out_print("0x"); + printByteHex(out, flags >> 8); + printByteHex(out, flags); + out.println("; // flags"); + + out_println("#" + in.readUnsignedShort() + "; // this class"); + + int sup_count = in.readUnsignedShort(); + out_begin(startArray(sup_count) + " { // supers"); + for (int k = 0; k < sup_count; k++) { + out_println("#" + in.readUnsignedShort() + ";"); + } + out_end("} // supers"); + + int int_count = in.readUnsignedByte(); + out_begin(startArray(int_count) + "b { // interfaces"); + for (int k = 0; k < int_count; k++) { + out_println("#" + in.readUnsignedShort() + ";"); + } + out_end("} // interfaces"); + + int field_count = in.readUnsignedShort(); + out_begin(startArray(field_count) + " { // fields"); + for (int k = 0; k < field_count; k++) { + out_begin("{ // field " + k); + out_println(in.readUnsignedByte() + "b; // token"); + + int f_flags = in.readUnsignedShort(); + out_print("0x"); + printByteHex(out, f_flags >> 8); + printByteHex(out, f_flags); + out.println("; // flags"); + + out_println("#" + in.readUnsignedShort() + "; // this field name"); + out_println("#" + in.readUnsignedShort() + "; // this field descriptor"); + + int attr_count = in.readUnsignedShort(); + out_begin(startArray(attr_count) + " { // Attributes"); + for (int ai = 0; ai < attr_count; ai++) { + decodeAttr(in); + } + out_end("} // Attributes"); + out_end("};"); + } + out_end("} // fields"); + + int mth_count = in.readUnsignedShort(); + out_begin(startArray(mth_count) + " { // methods"); + for (int k = 0; k < mth_count; k++) { + out_begin("{ // method " + k); + out_println(in.readUnsignedByte() + "b; // token"); + + int mth_flags = in.readUnsignedShort(); + out_print("0x"); + printByteHex(out, mth_flags >> 8); + printByteHex(out, mth_flags); + out.println("; // flags"); + + out_println("#" + in.readUnsignedShort() + "; // this method name"); + out_println("#" + in.readUnsignedShort() + "; // this method descriptor"); + out_end("};"); + } + out_end("} // methods"); + out_end("};"); + } + out_end("} // classes"); + endComponent(in); + } + + DataInputStream beginComponent(String inpName) throws IOException { + DataInputStream in = new DataInputStream(new FileInputStream(inpName)); + out_println("file " + inpName); + + int tag = in.readUnsignedByte(); + out_print("Component(" + tag); + int size = in.readUnsignedShort(); + if (printDetails) { + out.print(", " + size); + } + out_begin(") { // " + compName(tag)); + return in; + } + + void endComponent(DataInputStream in) throws IOException { + out_end("};"); // Component + int avail = in.available(); + if (avail > 0) { + out.println("=========== extra bytes:"); + for (int k = 0; k < 8; k++) { + printBytes(in, avail >= 8 ? 8 : avail); + avail = in.available(); + if (avail == 0) { + break; + } + } + if (avail > 0) { + out.println(" there is also " + avail + " bytes available"); + } + } + in.close(); + } + + ArrayList methodsLengths = null; + ArrayList methodsOffsets = null; + + void decodeHeader(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int magic = in.readInt(); + out_print(toHex(magic, 4) + "; // "); + if (magic != HEADER_MAGIC) { + out.print("wrong magic: 0x" + Integer.toString(HEADER_MAGIC, 16) + " expected"); + } else { + out_print("magic"); + } + out.println(); + out_println(in.readUnsignedByte() + "b; // minor version"); + out_println(in.readUnsignedByte() + "b; // major version"); + out_println(toHex(in.readUnsignedByte(), 1) + "; // flags"); + + out_begin("{ // package info"); + out_println(in.readUnsignedByte() + "b; // minor version"); + out_println(in.readUnsignedByte() + "b; // major version"); + int aid_len = in.readUnsignedByte(); + out_begin("Bytes" + startArray(aid_len) + "b {"); + printBytes(in, aid_len); + out_end("};"); // Bytes[] + out_end("};"); // package info + endComponent(in); + } + + void decodeDirectory(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int i; + out_begin("{ // component sizes"); + for (i = 0; i < 11; i++) { + out_println(in.readUnsignedShort() + "; // " + (i + 1)); + } + out_end("};"); + + out_begin("{ // static field size"); + out_println(in.readUnsignedShort() + "; // image size"); + out_println(in.readUnsignedShort() + "; // array init count"); + out_println(in.readUnsignedShort() + "; // array init size"); + out_end("};"); + + out_println(in.readUnsignedByte() + "b; // import count"); + out_println(in.readUnsignedByte() + "b; // applet count"); + + int custom_count = in.readUnsignedByte(); + out_begin(startArray(custom_count) + "b { // custom components"); + for (i = 0; i < custom_count; i++) { + out_print("Comp(" + in.readUnsignedByte()); // tag; + int size2 = in.readUnsignedShort(); + if (printDetails) { + out_print(", " + size2); + } + out_begin(") {"); + int aid_len = in.readUnsignedByte(); + out_begin("Bytes" + startArray(aid_len) + "b {"); + printBytes(in, aid_len); + out_end("};"); + out_end("};"); + } + out_end("};"); + + endComponent(in); + } + + void decodeApplet(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int applet_count = in.readUnsignedByte(); + out_begin(startArray(applet_count) + "b { // applets"); + for (int i = 0; i < applet_count; i++) { + out_begin("{ // applet " + i); + int aid_len = in.readUnsignedByte(); + out_begin("Bytes" + startArray(aid_len) + "b {"); + printBytes(in, aid_len); + out_end("};"); // Bytes[] + out_println(in.readUnsignedShort() + "; // install method offset"); + out_end("};"); // applet + } + out_end("};"); // applets + endComponent(in); + } + + void decodeImport(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int package_count = in.readUnsignedByte(); + out_begin(startArray(package_count) + "b { // packages"); + for (int i = 0; i < package_count; i++) { + out_begin("{ // package " + i); + out_println(in.readUnsignedByte() + "b; // minor version"); + out_println(in.readUnsignedByte() + "b; // major version"); + int aid_len = in.readUnsignedByte(); + out_begin("Bytes" + startArray(aid_len) + "b {"); + printBytes(in, aid_len); + out_end("};"); // Bytes[] + out_end("};"); // package info + } + out_end("};"); // package info + endComponent(in); + } + + static String[] refNames = { + "Classref", + "InstanceFieldref", + "VirtualMethodref", + "SuperMethodref", + "StaticFieldref", + "StaticMethodref" + }; + + void decodeConstantPool(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int items_count = in.readUnsignedShort(); + out_begin(startArray(items_count) + " { // items"); + for (int i = 0; i < items_count; i++) { + int tag = in.readUnsignedByte(); + int info1 = in.readUnsignedByte(), + info2 = in.readUnsignedByte(), + info3 = in.readUnsignedByte(); + out_print(tag + "b "); + if ((tag > 0) && (tag <= 6)) { + if ((info1 & 0x80) == 0) { + if (tag <= 4) { + out_print(((info1 << 8) | info2) + " " + info3 + "b;"); + } else { + out_print(info1 + "b " + ((info2 << 8) | info3) + ";"); + } + out.print(" // internal "); + } else { + out.print(info1 + "b " + info2 + "b " + info3 + "b;"); + out.print(" // external "); + } + out.println(refNames[tag - 1]); + } else { + out.print(info1 + "b " + info2 + "b " + info3 + "b;"); + out.println(" // unknown tag "); + } + } + out_end("};"); // CP array + endComponent(in); + } + + void printClassref(DataInputStream in) throws IOException { + int info1 = in.readUnsignedByte(), + info2 = in.readUnsignedByte(); + if ((info1 & 0x80) == 0) { + out_print(((info1 << 8) | info2) + ";"); + out_print(" // internal "); + } else { + out_print(info1 + "b " + info2 + "b;"); + out_print(" // external "); + } + out_println(" Classref "); + } + + void decodeClass(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + for (int i = 0; in.available() > 0; i++) { + out_begin("{ // class " + i); + int bitfield = in.readUnsignedByte(); + int interface_count = bitfield & 0x0F; + out_print("0x"); + printByteHex(out, bitfield); + out.println("; // bitfield"); + if ((bitfield & 0x80) != 0) { + // interface + for (int k = 0; k < interface_count; k++) { + printClassref(in); + } + } else { + // class + printClassref(in); + out_println(in.readUnsignedByte() + "b; // declared instance size"); + out_println(in.readUnsignedByte() + "b; // first reference token"); + out_println(in.readUnsignedByte() + "b; // reference count"); + out_println(in.readUnsignedByte() + "b; // public method table base"); + int pumrc = in.readUnsignedByte(); + out_println(pumrc + "b; // public method table count"); + out_println(in.readUnsignedByte() + "b; // package method table base"); + int pamrc = in.readUnsignedByte(); + out_println(pamrc + "b; // package method table count"); + out_begin("{ // public method table"); + for (int k = 0; k < pumrc; k++) { + out_println(in.readUnsignedShort() + ";"); + } + out_end("};"); + out_begin("{ // package method table"); + for (int k = 0; k < pamrc; k++) { + out_println(in.readUnsignedShort() + ";"); + } + out_end("};"); + out_begin("{ // implemented interfaces"); + for (int k = 0; k < interface_count; k++) { + out_begin("{ // interface " + k); + printClassref(in); + int count = in.readUnsignedByte(); + out_begin("Bytes" + startArray(count) + "b {"); + printBytes(in, count); + out_end("};"); // Bytes[] + out_end("};"); + } + out_end("};"); + } + out_end("};"); + } + endComponent(in); + } + + void decodeDescriptor(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + methodsLengths = new ArrayList<>(); + methodsOffsets = new ArrayList<>(); + int class_count = in.readUnsignedByte(); + out_begin(startArray(class_count) + "b { // classes"); + for (int c = 0; c < class_count; c++) { + out_begin("{ // class " + c); + out_println(in.readUnsignedByte() + "b; // token"); + out_print("0x"); + printByteHex(out, in.readUnsignedByte()); + out.println("; // flags"); + printClassref(in); + int icount = in.readUnsignedByte(); + out_println(icount + "b; // interface count"); + int fcount = in.readUnsignedShort(); + out_println(fcount + "; // field count"); + int mcount = in.readUnsignedShort(); + out_println(mcount + "; // method count"); + if (icount != 0) { + out_begin("{ // interfaces"); + for (int i = 0; i < icount; i++) { + printClassref(in); + } + out_end("};"); + } + for (int i = 0; i < fcount; i++) { + out_begin("{ // field " + i); + out_println(in.readUnsignedByte() + "b; // token"); + int flags = in.readUnsignedByte(); + out_print("0x"); + printByteHex(out, flags); + out.println("; // flags"); + if ((flags & 0x08) == 0) { + printClassref(in); + out_println(in.readUnsignedByte() + "b; // token"); + } else { // static field + int info1 = in.readUnsignedByte(), + info2 = in.readUnsignedByte(), + info3 = in.readUnsignedByte(); + if ((info1 & 0x80) == 0) { + out_print(info1 + "b " + ((info2 << 8) | info3) + ";"); + out.println(" // internal field"); + } else { + out.print(info1 + "b " + info2 + "b " + info3 + "b;"); + out.println(" // external field"); + } + } + int type = in.readUnsignedShort(); + if ((type & 0x8000) == 0) { + out_println(type + "; // reference type"); + } else { + out_print("0x"); + printByteHex(out, type >> 8); + printByteHex(out, type); + out.println("; // primitive type"); + } + out_end("};"); + } + for (int i = 0; i < mcount; i++) { + int token = in.readUnsignedByte(); + int flags = in.readUnsignedByte(); + int m_offset = in.readUnsignedShort(); + int t_offset = in.readUnsignedShort(); + int bytecode_count = in.readUnsignedShort(); + if (m_offset != 0) { + out_begin("{ // method " + i + " (" + methodsLengths.size() + ")"); + methodsLengths.add(bytecode_count); + methodsOffsets.add(m_offset); + } else { + out_begin("{ // method " + i); + } + out_println(token + "b; // token"); + out_print("0x"); + printByteHex(out, flags); + out.println("; // flags"); + out_println(m_offset + "; // method offset"); + out_println(t_offset + "; // type offset"); + out_println(bytecode_count + "; // bytecode count"); + out_println(in.readUnsignedShort() + "; // exception handler count"); + out_println(in.readUnsignedShort() + "; // exception handler index"); + out_end("};"); + } + out_end("};"); // class i + } + out_end("}; // classes"); + + int cp_count = in.readUnsignedShort(); + out_begin(startArray(cp_count) + " { // constant pool types"); + for (int i = 0; i < cp_count; i++) { + int type = in.readUnsignedShort(); + if (type == 0xFFFF) { + out_println("0xFFFF;"); + } else { + out_println(type + "; "); + } + } + out_end("}; // constant pool types"); + + out_begin("{ // type descriptors"); + for (int i = 0; in.available() > 0; i++) { + int nibble_count = in.readUnsignedByte(); + out_print(nibble_count + "b; "); + printBytes(in, (nibble_count + 1) / 2); + } + out_end("}; // type descriptors"); + endComponent(in); + } + + void decodeMethod(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int handler_count = in.readUnsignedByte(); + out_begin(startArray(handler_count) + "b { // exception handlers"); + for (int i = 0; i < handler_count; i++) { + out_print(in.readUnsignedShort() + ", "); + int bitfield = in.readUnsignedShort(); + out.print("0x"); + printByteHex(out, bitfield >> 8); + printByteHex(out, bitfield); + out.print(", " + in.readUnsignedShort() + ", "); + out.println(in.readUnsignedShort() + "; // handler " + i); + } + out_end("};"); // handlers + + if (methodsLengths == null) { + out.println("// Descriptor.cap absent - methods not printed"); + } else { + int f_offset = 1 + handler_count * 8; + for (int i = 0; i < methodsLengths.size(); i++) { + out_begin("{ // method " + i); + int m_offset = methodsOffsets.get(i); + if (m_offset != f_offset) { + out.println("file offset=" + f_offset + " but m_offset=" + m_offset); + break; + } + int bitfield = in.readUnsignedByte(); + if ((bitfield & 0x80) == 0) { + out_print("0x"); + printByteHex(out, bitfield); + out.println("; // flags, max_stack"); + out_print("0x"); + printByteHex(out, in.readUnsignedByte()); + out.println("; // nargs, max_locals"); + f_offset += 2; + } else { + out_print("0x"); + printByteHex(out, bitfield); + out.println("; // flags, padding"); + out_println(in.readUnsignedByte() + "b; // max_stack"); + out_println(in.readUnsignedByte() + "b; // nargs"); + out_println(in.readUnsignedByte() + "b; // max_locals"); + f_offset += 4; + } + int bytecode_count = methodsLengths.get(i); + out_begin("{ // bytecodes"); + printBytes(in, bytecode_count); + f_offset += bytecode_count; + out_end("};"); + out_end("};"); + } + } + + endComponent(in); + } + + void decodeStaticField(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int image_size = in.readUnsignedShort(); + out_println(image_size + "; // image size"); + int reference_count = in.readUnsignedShort(); + out_println(reference_count + "; // reference count"); + int array_init_count = in.readUnsignedShort(); + out_begin(startArray(array_init_count) + " { // array_init_info"); + for (int i = 0; i < array_init_count; i++) { + out_println(in.readUnsignedByte() + "b // type "); + int count = in.readUnsignedShort(); + out_begin("Bytes" + startArray(count) + "s { // values"); + printBytes(in, count); + out_end("};"); // Bytes[] + } + out_end("};"); // array_init_info + int default_value_count = in.readUnsignedShort(); + out_println(default_value_count + "; // default value count"); + int non_default_value_count = in.readUnsignedShort(); + out_begin("Bytes" + startArray(non_default_value_count) + "s { // non default values"); + printBytes(in, non_default_value_count); + out_end("};"); // Bytes[] + + endComponent(in); + } + + void decodeRefLocation(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + + int byte_index_count = in.readUnsignedShort(); + out_begin("Bytes" + startArray(byte_index_count) + "s { // offsets to byte indices"); + printBytes(in, byte_index_count); + out_end("};"); // Bytes[] + + byte_index_count = in.readUnsignedShort(); + out_begin("Bytes" + startArray(byte_index_count) + "s { // offsets to byte2 indices"); + printBytes(in, byte_index_count); + out_end("};"); // Bytes[] + + endComponent(in); + } + + void decodeExport(String inpName) throws IOException { + DataInputStream in = beginComponent(inpName); + int class_count = in.readUnsignedByte(); + out_begin(startArray(class_count) + "b { // classes"); + for (int i = 0; i < class_count; i++) { + out_begin("{ // class " + i); + out_println(in.readUnsignedShort() + "; // class offset"); + int fcount = in.readUnsignedByte(); + out_println(fcount + "b; // static field count"); + int mcount = in.readUnsignedByte(); + out_println(mcount + "b; // static method count"); + out_begin("{ // static field offsets"); + for (int j = 0; j < fcount; j++) { + out_println(in.readUnsignedShort() + "; // field " + j + " offset"); + } + out_end("};"); + out_begin("{ // static method offsets"); + for (int j = 0; j < mcount; j++) { + out_println(in.readUnsignedShort() + "; // method " + j + " offset"); + } + out_end("};"); + out_end("};"); // class i + } + out_end("};"); // classes + endComponent(in); + } + /*========================================================*/ + + /** + * Constructor. + */ + public Main(PrintWriter out, String program) { + this.out = out; + this.program = program; + } + + public void error(String msg) { + out.println(program + ": " + msg); + } + + /** + * Usage + */ + public void usage() { + out.println(i18n.getString("jcdec.usage")); + out.println(i18n.getString("jcdec.opt.g")); + out.println(i18n.getString("jcdec.opt.version")); + } + + /** + * Run the decoder + */ + public synchronized boolean decode(String argv[]) { +// int flags = F_WARNINGS; + long tm = System.currentTimeMillis(); + ArrayList vargs = new ArrayList<>(); + ArrayList vj = new ArrayList<>(); + boolean nowrite = false; + int addOptions = 0; + + // Parse arguments + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + if (arg.equals("-g")) { + printDetails = true; + vargs.add(arg); + } else if (arg.equals("-v")) { + DebugFlag = true; + vargs.add(arg); + out.println("arg[" + i + "]=" + argv[i] + "/verbose"); + } else if (arg.equals("-version")) { + out.println(ProductInfo.FULL_VERSION); + } else if (arg.startsWith("-")) { +//out.println("arg["+i+"]="+argv[i]+"/invalid flag"); + error(i18n.getString("jcdec.error.invalid_flag", arg)); + usage(); + return false; + } else { + vargs.add(arg); + vj.add(arg); + } + } + + if (vj.isEmpty()) { + usage(); + return false; + } + +// String[] names = new String[vj.size()]; +// vj.copyInto(names); + String[] names = null; + names = vj.toArray(names); +decode: + for (int k = 0; k < names.length; k++) { + String inpname = names[k]; + try { + if (inpname.endsWith(".cap")) { + String shortName = inpname.substring(0, inpname.length() - 4); + if (shortName.endsWith("Header")) { + decodeHeader(inpname); + } else if (shortName.endsWith("Directory")) { + decodeDirectory(inpname); + } else if (shortName.endsWith("Applet")) { + decodeApplet(inpname); + } else if (shortName.endsWith("Import")) { + decodeImport(inpname); + } else if (shortName.endsWith("ConstantPool")) { + decodeConstantPool(inpname); + } else if (shortName.endsWith("Class")) { + decodeClass(inpname); + } else if (shortName.endsWith("Descriptor")) { + decodeDescriptor(inpname); + } else if (shortName.endsWith("Method")) { + decodeMethod(inpname); + } else if (shortName.endsWith("StaticField")) { + decodeStaticField(inpname); + } else if (shortName.endsWith("RefLocation")) { + decodeRefLocation(inpname); + } else if (shortName.endsWith("Export")) { + decodeExport(inpname); + } else { + continue decode; + } + out.println(""); + } else if (inpname.endsWith(".exp")) { + decodeExp(inpname); + out.println(""); + } + continue decode; + } catch (FileNotFoundException ee) { + error(i18n.getString("jcdec.error.cannot_read", inpname)); + } catch (Error ee) { + ee.printStackTrace(); + error(i18n.getString("jcdec.error.fatal_error")); + } catch (Exception ee) { + ee.printStackTrace(); + error(i18n.getString("jcdec.error.fatal_exception")); + } + return false; + } + return true; + } + + /** + * Main program + */ + public static void main(String argv[]) { + Main decoder = new Main(new PrintWriter(new uEscWriter(System.out)), "jcdec"); + System.exit(decoder.decode(argv) ? 0 : 1); + } +} diff --git a/src/org/openjdk/asmtools/jcdec/i18n.properties b/src/org/openjdk/asmtools/jcdec/i18n.properties new file mode 100644 index 0000000..88560b2 --- /dev/null +++ b/src/org/openjdk/asmtools/jcdec/i18n.properties @@ -0,0 +1,34 @@ +# Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +jcdec.error.invalid_flag=invalid flag: " {0} +jcdec.error.fatal_error=fatal error +jcdec.error.fatal_exception=fatal exception +jcdec.error.cannot_read=cannot read {0} +jcdec.usage=\ +Usage: java -jar asmtools.jar jcdec [options] FILE.class... > FILE.jcod\n\ +where possible options include: + +jcdec.opt.g=\ +\ -g: detailed output format +jcdec.opt.version=\ +\ -version: print version number and date + diff --git a/src/org/openjdk/asmtools/jcoder/ByteBuffer.java b/src/org/openjdk/asmtools/jcoder/ByteBuffer.java new file mode 100644 index 0000000..6f8142f --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/ByteBuffer.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +/** + * Compiles just 1 source file + */ +class ByteBuffer extends java.io.OutputStream { + + String myname; + /** + * The buffer where elements are stored. + */ + byte data[]; + /** + * The number of elements in the buffer. + */ + int length; + /** + * The size of the increment. If it is 0 the size of the the buffer is doubled + * everytime it needs to grow. + */ + protected int capacityIncrement; + + /** + * Constructs an empty vector with the specified storage capacity and the specified + * capacityIncrement. + * + * @param initialCapacity the initial storage capacity of the vector + * @param capacityIncrement how much to increase the element's size by. + */ + public ByteBuffer(int initialCapacity, int capacityIncrement) { +// super(); + this.data = new byte[initialCapacity]; + this.capacityIncrement = capacityIncrement; + } + + /** + * Constructs an empty vector with the specified storage capacity. + * + * @param initialCapacity the initial storage capacity of the vector + */ + public ByteBuffer(int initialCapacity) { + this(initialCapacity, 0); + } + + /** + * Constructs an empty vector. + */ + public ByteBuffer() { + this(30); + } + + /** + * Constructs a full vector. + */ + public ByteBuffer(byte data[], int capacityIncrement) { + this.length = data.length; + this.data = data; + this.capacityIncrement = capacityIncrement; + } + + /** + * Constructs a full vector. + */ + public ByteBuffer(byte data[]) { + this(data, 0); + } + + /** + * Returns the number of elements in the vector. Note that this is not the same as the + * vector's capacity. + */ + public final int size() { + return length; + } + + /** + * Ensures that the vector has at least the specified capacity. + * + * @param minCapacity the desired minimum capacity + */ + public final synchronized void ensureCapacity(int minCapacity) { + int oldCapacity = data.length; + if (minCapacity <= oldCapacity) { + return; + } + byte oldData[] = data; + int newCapacity = (capacityIncrement > 0) ? (oldCapacity + capacityIncrement) : (oldCapacity * 2); + if (newCapacity < minCapacity) { + newCapacity = minCapacity; + } + data = new byte[newCapacity]; + System.arraycopy(oldData, 0, data, 0, length); + } + + /*======================================*/ + public void write(int val) { + ensureCapacity(length + 1); + data[length++] = (byte) val; + } + + public void writeAt(int index, long val, int width) { + for (int i = 0; i < width; i++) { + data[index + i] = (byte) (val >> (width - 1 - i) * 8); + } + } + + public void append(long val, int width) { + ensureCapacity(length + width); + writeAt(length, val, width); + length += width; + } + + /*======================================================*/ +} // end ByteBuffer + diff --git a/src/org/openjdk/asmtools/jcoder/ErrorMessage.java b/src/org/openjdk/asmtools/jcoder/ErrorMessage.java new file mode 100644 index 0000000..019ef8e --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/ErrorMessage.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +/** + * A sorted list of error messages + */ +final class ErrorMessage { + + int where; + String message; + ErrorMessage next; + + /** + * Constructor + */ + ErrorMessage(int where, String message) { + this.where = where; + this.message = message; + } +} diff --git a/src/org/openjdk/asmtools/jcoder/JcodTokens.java b/src/org/openjdk/asmtools/jcoder/JcodTokens.java new file mode 100644 index 0000000..829eb1a --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/JcodTokens.java @@ -0,0 +1,387 @@ +/* + * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +import static org.openjdk.asmtools.jasm.Tables.*; + +import java.io.PrintWriter; +import java.util.HashMap; + +/** + * + * JcodTokens + * + * This class contains tokens specific to parsing JCOD syntax. + * + * The classes in JcodTokens are following a Singleton Pattern. These classes are Enums, + * and they are contained in private hash maps (lookup tables and reverse lookup tables). + * These hash maps all have public accessors, which clients use to look-up enums. + * + * Tokens in this table carry no external state, and are typically treated as constants. + * They do not need to be reset. + */ +public class JcodTokens { + + /*-------------------------------------------------------- */ + /* Marker: describes the type of Keyword */ + static public enum KeywordType { + + TOKEN(0, "TOKEN"), + KEYWORD(3, "KEYWORD"); + + private final Integer value; + private final String printval; + + KeywordType(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + } + + /*-------------------------------------------------------- */ + /* Marker - describes the type of token */ + /* this is rather cosmetic, no function currently. */ + static public enum TokenType { + VALUE (0, "Value"), + KEYWORDS (1, "Keywords"), + PUNCTUATION (2, "Punctuation"), + JDEC (3, "JDec"), + STACKMAP (4, "StackMap"), + MISC (5, "Misc"); + + private final Integer value; + private final String printval; + + TokenType(Integer val, String print) { + value = val; + printval = print; + } + + public String printval() { + return printval; + } + } + + /*-------------------------------------------------------- */ + /** Scanner Tokens (Definitive List) */ + static public enum Token { + EOF (-1, "EOF", "EOF", TokenType.MISC), + IDENT (60, "IDENT", "IDENT", TokenType.VALUE), + LONGSTRINGVAL (61, "LONGSTRINGVAL", "LONGSTRING", TokenType.VALUE), + INTVAL (65, "INTVAL", "INT", TokenType.VALUE), + LONGVAL (66, "LONGVAL", "LONG", TokenType.VALUE), + STRINGVAL (69, "STRINGVAL", "STRING", TokenType.VALUE), + + CLASS (70, "CLASS", "class", TokenType.KEYWORDS, KeywordType.KEYWORD), + INTERFACE (71, "INTERFACE", "interface", TokenType.KEYWORDS, KeywordType.KEYWORD), + DIV (72, "DIV", "div", TokenType.KEYWORDS), + EQ (73, "EQ", "eq", TokenType.KEYWORDS), + ASSIGN (74, "ASSIGN", "assign", TokenType.KEYWORDS), + MODULE (75, "MODULE", "module", TokenType.KEYWORDS, KeywordType.KEYWORD), + + COLON (134, "COLON", ":", TokenType.PUNCTUATION), + SEMICOLON (135, "SEMICOLON", ";", TokenType.PUNCTUATION, KeywordType.KEYWORD), + COMMA (0, "COMMA", ",", TokenType.PUNCTUATION, KeywordType.KEYWORD), + LBRACE (138, "LBRACE", "{", TokenType.PUNCTUATION, KeywordType.KEYWORD), + RBRACE (139, "RBRACE", "}", TokenType.PUNCTUATION, KeywordType.KEYWORD), + LPAREN (140, "LPAREN", "(", TokenType.PUNCTUATION, KeywordType.KEYWORD), + RPAREN (141, "RPAREN", ")", TokenType.PUNCTUATION, KeywordType.KEYWORD), + LSQBRACKET (142, "LSQBRACKET", "[", TokenType.PUNCTUATION, KeywordType.KEYWORD), + RSQBRACKET (143, "RSQBRACKET", "]", TokenType.PUNCTUATION, KeywordType.KEYWORD), + + + BYTEINDEX (156, "BYTEINDEX", "b", TokenType.JDEC, KeywordType.KEYWORD), + SHORTINDEX (157, "SHORTINDEX", "s", TokenType.JDEC, KeywordType.KEYWORD), + ATTR (158, "ATTR", "Attr", TokenType.JDEC, KeywordType.KEYWORD), + BYTES (159, "BYTES", "Bytes", TokenType.JDEC, KeywordType.KEYWORD), + MACRO (160, "MACRO", "Attr", TokenType.JDEC), + COMP (161, "COMP", "Component", TokenType.JDEC, KeywordType.KEYWORD), + FILE (162, "FILE", "file", TokenType.JDEC, KeywordType.KEYWORD), + + ZEROINDEX (163, "ZEROINDEX", "z", TokenType.STACKMAP, KeywordType.KEYWORD); + + private Integer value; + private String printval; + private String parsekey; + private TokenType tk_type; + private KeywordType key_type; + + // By default, if a KeywordType is not specified, it has the value 'TOKEN' + Token(Integer val, String print, String op) { + init(val, print, op, TokenType.VALUE, KeywordType.TOKEN); + } + + Token(Integer val, String print, String op, TokenType tt) { + init(val, print, op, tt, KeywordType.TOKEN); + } + + Token(Integer val, String print, String op, TokenType tt, KeywordType kt) { + init(val, print, op, tt, kt); + } + + private void init(Integer val, String print, String op, TokenType tt, KeywordType kt) { + value = val; + printval = print; + parsekey = op; + tk_type = tt; + key_type = kt; + } + + public String printval() { + return printval; + } + + public String parsekey() { + return parsekey; + } + + public int value() { + return value; + } + + @Override + public String toString() { + return "<" + printval + "> [" + value + "]"; + } + + } + + /** + * Initialized keyword and token Hash Maps (and Reverse Tables) + */ + protected static final int MaxTokens = 172; + private static HashMap TagToTokens = new HashMap<>(MaxTokens); + private static HashMap SymbolToTokens = new HashMap<>(MaxTokens); + private static HashMap ParsekeyToTokens = new HashMap<>(MaxTokens); + + protected static final int MaxKeywords = 40; + private static HashMap TagToKeywords = new HashMap<>(MaxKeywords); + private static HashMap SymbolToKeywords = new HashMap<>(MaxKeywords); + private static HashMap ParsekeyToKeywords = new HashMap<>(MaxKeywords); + + static { + + // register all of the tokens + for (Token tk : Token.values()) { + registerToken(tk); + } + + SymbolToKeywords.put(Token.INTVAL.printval(), Token.INTVAL); + ParsekeyToKeywords.put(Token.INTVAL.parsekey(), Token.INTVAL); + SymbolToKeywords.put(Token.STRINGVAL.printval(), Token.STRINGVAL); + ParsekeyToKeywords.put(Token.STRINGVAL.parsekey(), Token.STRINGVAL); + } + + private static void registerToken(Token tk) { + // Tag is a keyword + if (tk.key_type == KeywordType.KEYWORD) { + TagToKeywords.put(tk.value, tk); + SymbolToKeywords.put(tk.printval, tk); + if (tk.parsekey != null) { + ParsekeyToKeywords.put(tk.parsekey, tk); + } + } + + // Finally, register all tokens + TagToTokens.put(tk.value, tk); + SymbolToTokens.put(tk.printval, tk); + ParsekeyToTokens.put(tk.printval, tk); + } + + /* Token accessors */ + public static Token token(int tk) { + return TagToTokens.get(tk); + } + + public static Token keyword_token(int tk) { + return TagToKeywords.get(tk); + } + + /* Reverse lookup accessors */ + public static Token token(String parsekey) { + return ParsekeyToTokens.get(parsekey); + } + + public static Token keyword_token(String parsekey) { + return ParsekeyToKeywords.get(parsekey); + } + + /* Reverse lookup by ID accessors */ + public static Token token_ID(String ID) { + return ParsekeyToTokens.get(ID); + } + + public static Token keyword_token_ID(String ID) { + return ParsekeyToKeywords.get(ID); + } + + public static String keywordName(int token) { + String retval = ""; + if (token > TagToTokens.size()) { + retval = null; + } else { + Token tk = keyword_token(token); + if (tk != null) { + retval = tk.parsekey; + } + } + return retval; + } + + public static Token keyword_token_ident(String idValue) { + Token kwd = keyword_token(idValue); + + if (kwd == null) { + kwd = Token.IDENT; + } + return kwd; + } + + public static int keyword_token_int(String idValue) { + return keyword_token_ident(idValue).value(); + } + + private static HashMap NameToConstantType = new HashMap<>(ConstType.maxTag); + private static HashMap ConstantTypes = new HashMap<>(ConstType.maxTag); + + static { + // register all of the tokens + for (ConstType ct : ConstType.values()) { + registerConstantType(ct); + } + } + + /** + * ConstType + * + * A (typed) tag (constant) representing the type of Constant in the Constant Pool. + * + * This is more-or-less a copy of jasm.ConstType. Unfortunately, there's no way to + * sub-class (or slightly alter) the members of an enum. This enum set is slightly + * modified from the Jasm one. + */ + static public enum ConstType { +// CONSTANT_ZERO (-3, "CONSTANT_ZERO", ""), + CONSTANT_UTF8 (1, "CONSTANT_UTF8", "Asciz", "Utf8"), + CONSTANT_UNICODE (2, "CONSTANT_UNICODE", ""), + CONSTANT_INTEGER (3, "CONSTANT_INTEGER", "int", "u4"), + CONSTANT_FLOAT (4, "CONSTANT_FLOAT", "float"), + CONSTANT_LONG (5, "CONSTANT_LONG", "long"), + CONSTANT_DOUBLE (6, "CONSTANT_DOUBLE", "double"), + // Class is removed for JavaCard (???) + CONSTANT_CLASS (7, "CONSTANT_CLASS", "class"), + CONSTANT_STRING (8, "CONSTANT_STRING", "String"), + CONSTANT_FIELD (9, "CONSTANT_FIELD", "Field"), + CONSTANT_METHOD (10, "CONSTANT_METHOD", "Method"), + CONSTANT_INTERFACEMETHOD (11, "CONSTANT_INTERFACEMETHOD", "InterfaceMethod"), + CONSTANT_NAMEANDTYPE (12, "CONSTANT_NAMEANDTYPE", "NameAndType"), + // added for JavaCard + CONSTANT_JAVACARD_PACKAGE (13, "CONSTANT_PACKAGE", "package"), // in javacard export file + // Constant 14 reserved + CONSTANT_METHODHANDLE (15, "CONSTANT_METHODHANDLE", "MethodHandle"), + CONSTANT_METHODTYPE (16, "CONSTANT_METHODTYPE", "MethodType"), + CONSTANT_DYNAMIC (17, "CONSTANT_DYNAMIC", "Dynamic"), + CONSTANT_INVOKEDYNAMIC (18, "CONSTANT_INVOKEDYNAMIC", "InvokeDynamic"), + CONSTANT_MODULE (19, "CONSTANT_MODULE", "Module"), + CONSTANT_MODULE_PACKAGE (20, "CONSTANT_PACKAGE", "Package"); + + public static final int maxTag = 20; + + private final int value; + private final String parseKey; + private final String printval; + private final String alias; + + ConstType(int val, String print, String parse) { + value = val; + parseKey = parse; + printval = print; + alias = null; + } + + ConstType(int val, String print, String parse, String als) { + value = val; + parseKey = parse; + printval = print; + alias = als; + } + + public int value() { + return value; + } + + public String parseKey() { + return parseKey; + } + + public String printval() { + return printval; + } + + public void print(PrintWriter out) { + out.print(parseKey); + } + + @Override + public String toString() { + return "<" + printval + "> [" + Integer.toString(value) + "]"; + } + }; + + static public ConstType constType(int i) { + return ConstantTypes.get(i); + } + + static public ConstType constType(String parsekey) { + return NameToConstantType.get(parsekey); + } + + private static void registerConstantType(ConstType tt) { + NameToConstantType.put(tt.parseKey, tt); + if (tt.alias != null) { + NameToConstantType.put(tt.alias, tt); + } + ConstantTypes.put(tt.value, tt); + } + + public static int constValue(String stringValue) { + ConstType Val = constType(stringValue); + int val = -1; + + if (Val != null) { + val = Val.value(); + } else { + StackMapType smt = stackMapTypeKey(stringValue); + + if (smt != null) { + val = smt.value(); + } + } + return val; + } + +} diff --git a/src/org/openjdk/asmtools/jcoder/Jcoder.java b/src/org/openjdk/asmtools/jcoder/Jcoder.java new file mode 100644 index 0000000..a8dccd8 --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/Jcoder.java @@ -0,0 +1,766 @@ +/* + * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +import java.io.*; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Stack; + +import static org.openjdk.asmtools.jcoder.JcodTokens.ConstType; +import static org.openjdk.asmtools.jcoder.JcodTokens.Token; + +/** + * Compiles just 1 source file + */ +class Jcoder { + + /*-------------------------------------------------------- */ + /* Jcoder Fields */ + private ArrayList Classes = new ArrayList<>(); + private ByteBuffer buf; + private DataOutputStream bufstream; + private int depth = 0; + private String tabStr = ""; + private Context context = null; + protected SourceFile env; + protected Scanner scanner; + + /*-------------------------------------------------------- */ + /* Jcoder inner classes */ + + /*-------------------------------------------------------- */ + /* ContextTag (marker) - describes the type of token */ + /* this is rather cosmetic, no function currently. */ + private enum ContextTag { + NULL ( ""), + CLASS ( "Class"), + CONSTANTPOOL ( "Constant-Pool"), + INTERFACES ( "Interfaces"), + INTERFACE ( "Interface"), + METHODS ( "Methods"), + METHOD ( "Method"), + FIELDS ( "Fields"), + FIELD ( "Field"), + ATTRIBUTE ( "Attribute"); + + private final String printValue; + + ContextTag(String value) { + printValue = value; + } + + public String printval() { + return printValue; + } + } + + /*-------------------------------------------------------- */ + /* ContextVal (marker) - Specific value on a context stack */ + private class ContextVal { + + public ContextTag tag; + int compCount; + ContextVal owner; + + ContextVal(ContextTag tg) { + tag = tg; + compCount = 0; + owner = null; + } + + ContextVal(ContextTag tg, ContextVal ownr) { + tag = tg; + compCount = 0; + owner = ownr; + } + } + + + /*-------------------------------------------------------- */ + /* Context - Context stack */ + public class Context { + + Stack stack; + + private boolean hasCP; + private boolean hasMethods; + private boolean hasInterfaces; + private boolean hasFields; + + Context() { + stack = new Stack<>(); + init(); + } + + boolean isConstantPool() { + return !stack.empty() && (stack.peek().tag == ContextTag.CONSTANTPOOL); + } + + public void init() { + stack.removeAllElements(); + hasCP = false; + hasMethods = false; + hasInterfaces = false; + hasFields = false; + } + + void update() { + if (stack.empty()) { + stack.push(new ContextVal(ContextTag.CLASS)); + return; + } + + ContextVal currentCtx = stack.peek(); + switch (currentCtx.tag) { + case CLASS: + if (!hasCP) { + stack.push(new ContextVal(ContextTag.CONSTANTPOOL)); + hasCP = true; + } else if (!hasInterfaces) { + stack.push(new ContextVal(ContextTag.INTERFACES)); + hasInterfaces = true; + } else if (!hasFields) { + stack.push(new ContextVal(ContextTag.FIELDS)); + hasFields = true; + } else if (!hasMethods) { + stack.push(new ContextVal(ContextTag.METHODS)); + hasMethods = true; + } else { + // must be class attributes + currentCtx.compCount += 1; + stack.push(new ContextVal(ContextTag.ATTRIBUTE, currentCtx)); + } + break; + case INTERFACES: + currentCtx.compCount += 1; + stack.push(new ContextVal(ContextTag.INTERFACE, currentCtx)); + break; + case FIELDS: + currentCtx.compCount += 1; + stack.push(new ContextVal(ContextTag.FIELD, currentCtx)); + break; + case METHODS: + currentCtx.compCount += 1; + stack.push(new ContextVal(ContextTag.METHOD, currentCtx)); + break; + case FIELD: + case METHOD: + case ATTRIBUTE: + currentCtx.compCount += 1; + stack.push(new ContextVal(ContextTag.ATTRIBUTE, currentCtx)); + break; + default: + break; + } + } + + void exit() { + if (!stack.isEmpty()) { + stack.pop(); + } + } + + public String toString() { + if (stack.isEmpty()) { + return ""; + } + ContextVal currentCtx = stack.peek(); + String retval = currentCtx.tag.printval(); + switch (currentCtx.tag) { + case INTERFACE: + case METHOD: + case FIELD: + case ATTRIBUTE: + if (currentCtx.owner != null) { + retval += "[" + currentCtx.owner.compCount + "]"; + } + } + + return retval; + } + } + + + /*-------------------------------------------------------- */ + /* Jcoder */ + /** + * Create a parser + */ + Jcoder(SourceFile sf, HashMap macros) throws IOException { + scanner = new Scanner(sf, macros); + env = sf; + context = new Context(); + + } + /*-------------------------------------------------------- */ + + /** + * Expect a token, return its value, scan the next token or throw an exception. + */ + private void expect(Token t) throws SyntaxError, IOException { + if (scanner.token != t) { + env.traceln("expect:" + t + " instead of " + scanner.token); + switch (t) { + case IDENT: + env.error(scanner.pos, "identifier.expected"); + break; + default: + env.error(scanner.pos, "token.expected", t.toString()); + break; + } + throw new SyntaxError(); + } + scanner.scan(); + } + + private void recoverField() throws SyntaxError, IOException { + while (true) { + switch (scanner.token) { + case LBRACE: + scanner.match(Token.LBRACE, Token.RBRACE); + scanner.scan(); + break; + + case LPAREN: + scanner.match(Token.LPAREN, Token.RPAREN); + scanner.scan(); + break; + + case LSQBRACKET: + scanner.match(Token.LSQBRACKET, Token.RSQBRACKET); + scanner.scan(); + break; + + case RBRACE: + case EOF: + case INTERFACE: + case CLASS: + // begin of something outside a class, panic more + throw new SyntaxError(); + + default: + // don't know what to do, skip + scanner.scan(); + break; + } + } + } + + /** + * Parse an array of struct. + */ + private void parseArray() throws IOException { + scanner.scan(); + int length0 = buf.length, pos0 = scanner.pos; + int num_expected; + if (scanner.token == Token.INTVAL) { + num_expected = scanner.intValue; + scanner.scan(); + } else { + num_expected = -1; + } + expect(Token.RSQBRACKET); + int numSize; + switch (scanner.token) { + case BYTEINDEX: + scanner.scan(); + numSize = 1; + break; + case SHORTINDEX: + scanner.scan(); + numSize = 2; + break; + case ZEROINDEX: + scanner.scan(); + numSize = 0; + break; + default: + numSize = 2; + } + + // skip array size + if (numSize > 0) { + buf.append(num_expected, numSize); + } + + int num_present = parseStruct(); + if (num_expected == -1) { + env.trace(" buf.writeAt(" + length0 + ", " + num_present + ", " + numSize + "); "); + // skip array size + if (numSize > 0) { + buf.writeAt(length0, num_present, numSize); + } + } else if ( num_expected != num_present) { + if (context.isConstantPool() && num_expected == num_present +1) return; + env.error(pos0, "warn.array.wronglength", num_expected, num_present); + } + } + + /** + * Parse a byte array. + */ + private void parseByteArray() throws IOException { + scanner.scan(); + expect(Token.LSQBRACKET); + int length0 = buf.length, pos0 = scanner.pos; + int len_expected; + if (scanner.token == Token.INTVAL) { + len_expected = scanner.intValue; + scanner.scan(); + } else { + len_expected = -1; + } + expect(Token.RSQBRACKET); + int lenSize; + switch (scanner.token) { + case BYTEINDEX: + scanner.scan(); + lenSize = 1; + break; + case SHORTINDEX: + scanner.scan(); + lenSize = 2; + break; + case ZEROINDEX: + scanner.scan(); + lenSize = 0; + break; + default: + lenSize = 4; + } + + // skip array size + if (lenSize > 0) { + buf.append(len_expected, lenSize); + } + int length1 = buf.length; + parseStruct(); + int len_present = buf.length - length1; + if (len_expected == -1) { + env.trace(" buf.writeAt(" + length0 + ", " + len_present + ", " + lenSize + "); "); + // skip array size + if (lenSize > 0) { + buf.writeAt(length0, len_present, lenSize); + } + } else if (len_expected != len_present) { + env.error(pos0, "warn.array.wronglength", len_expected, len_present); + } + } + + /** + * Parse an Attribute. + */ + private void parseAttr() throws IOException { + scanner.scan(); + expect(Token.LPAREN); + int cpx; // index int const. pool + if (scanner.token == Token.INTVAL) { + cpx = scanner.intValue; + scanner.scan(); + + /* } else if (token==STRINGVAL) { + Integer Val=(Integer)(CP_Strings.get(stringValue)); + if (Val == null) { + env.error(pos, "attrname.notfound", stringValue); + throw new SyntaxError(); + } + cpx=Val.intValue(); + */ } else { + env.error(scanner.pos, "attrname.expected"); + throw new SyntaxError(); + } + buf.append(cpx, 2); + int pos0 = scanner.pos, length0 = buf.length; + int len_expected; + if (scanner.token == Token.COMMA) { + scanner.scan(); + len_expected = scanner.intValue; + expect(Token.INTVAL); + } else { + len_expected = -1; + } + buf.append(len_expected, 4); + expect(Token.RPAREN); + parseStruct(); + int len_present = buf.length - (length0 + 4); + if (len_expected == -1) { + buf.writeAt(length0, len_present, 4); + } else if (len_expected != len_present) { + env.error(pos0, "warn.attr.wronglength", len_expected, len_present); + } + } // end parseAttr + + /** + * Parse a Component of JavaCard .cap file. + */ + private void parseComp() throws IOException { + scanner.scan(); + expect(Token.LPAREN); + int tag = scanner.intValue; // index int const. pool + expect(Token.INTVAL); + buf.append(tag, 1); + int pos0 = scanner.pos, length0 = buf.length; + int len_expected; + if (scanner.token == Token.COMMA) { + scanner.scan(); + len_expected = scanner.intValue; + expect(Token.INTVAL); + } else { + len_expected = -1; + } + buf.append(len_expected, 2); + expect(Token.RPAREN); + parseStruct(); + int len_present = buf.length - (length0 + 2); + if (len_expected == -1) { + buf.writeAt(length0, len_present, 2); + } else if (len_expected != len_present) { + env.error(pos0, "warn.attr.wronglength", len_expected, len_present); + } + } // end parseComp + + private void adjustDepth(boolean up) { + if (up) { + depth += 1; + context.update(); + scanner.setDebugCP(context.isConstantPool()); + } else { + depth -= 1; + context.exit(); + } + StringBuilder bldr = new StringBuilder(); + int tabAmt = 4; + int len = depth * tabAmt; + for (int i = 0; i < len; i++) { + bldr.append(" "); + } + tabStr = bldr.toString(); + } + + /** + * Parse a structure. + */ + private int parseStruct() throws IOException { + adjustDepth(true); + env.traceln(" "); + env.traceln(tabStr + "MapStruct { <" + context + "> "); + expect(Token.LBRACE); + int num = 0; + int addElem = 0; + while (true) { + try { + switch (scanner.token) { + case COMMA: // ignored + scanner.scan(); + break; + case SEMICOLON: + num++; + addElem = 0; + scanner.scan(); + break; + case CLASS: + scanner.addConstDebug(ConstType.CONSTANT_CLASS); + env.trace("class "); + scanner.longValue = ConstType.CONSTANT_CLASS.value(); + scanner.intSize = 1; + case INTVAL: + env.trace("int [" + scanner.longValue + "] "); + buf.append(scanner.longValue, scanner.intSize); + scanner.scan(); + addElem = 1; + break; + case STRINGVAL: + scanner.scan(); + scanner.addConstDebug(ConstType.CONSTANT_UTF8); + env.trace("UTF8 [\"" + scanner.stringValue + "\"] "); + bufstream.writeUTF(scanner.stringValue); + addElem = 1; + break; + case LONGSTRINGVAL: + scanner.scan(); + env.traceln("LongString [\"" + Arrays.toString(scanner.longStringValue.data) + "\"] "); + buf.write(scanner.longStringValue.data, 0, scanner.longStringValue.length); + addElem = 1; + break; + case LBRACE: + parseStruct(); + addElem = 1; + break; + case LSQBRACKET: + parseArray(); + addElem = 1; + break; + case BYTES: + env.trace("bytes "); + parseByteArray(); + addElem = 1; + break; + case ATTR: + env.trace("attr "); + parseAttr(); + addElem = 1; + break; + case COMP: + env.trace("comp "); + parseComp(); + addElem = 1; + break; + case RBRACE: + scanner.scan(); + env.traceln(" "); + env.traceln(tabStr + "} // MapStruct <" + context + "> ["); + adjustDepth(false); + return num + addElem; + default: + env.traceln("unexp token=" + scanner.token); + env.traceln(" scanner.stringval = \"" + scanner.stringValue + "\""); + env.error(scanner.pos, "element.expected"); + throw new SyntaxError(); + } + } catch (SyntaxError e) { + recoverField(); + } + } + } // end parseStruct + + /** + * Recover after a syntax error in the file. This involves discarding tokens until an + * EOF or a possible legal continuation is encountered. + */ + private void recoverFile() throws IOException { + while (true) { + switch (scanner.token) { + case CLASS: + case INTERFACE: + // Start of a new source file statement, continue + return; + + case LBRACE: + scanner.match(Token.LBRACE, Token.RBRACE); + scanner.scan(); + break; + + case LPAREN: + scanner.match(Token.LPAREN, Token.RPAREN); + scanner.scan(); + break; + + case LSQBRACKET: + scanner.match(Token.LSQBRACKET, Token.RSQBRACKET); + scanner.scan(); + break; + + case EOF: + return; + + default: + // Don't know what to do, skip + scanner.scan(); + break; + } + } + } + + /** + * Parse module declaration + */ + private void parseModule() throws IOException { + // skip module name as a redundant element + scanner.skipTill(Scanner.LBRACE); + buf = new ByteBuffer(); + bufstream = new DataOutputStream(buf); + buf.myname = "module-info.class"; + scanner.scan(); + env.traceln("starting " + buf.myname); + // Parse the clause + parseClause(); + env.traceln("ending " + buf.myname); + } + + /** + * Parse a class or interface declaration. + */ + private void parseClass(Token prev) throws IOException { + scanner.scan(); + buf = new ByteBuffer(); + bufstream = new DataOutputStream(buf); + // Parse the class name + switch (scanner.token) { + case STRINGVAL: + buf.myname = scanner.stringValue; + break; + case BYTEINDEX: + case SHORTINDEX: + case ATTR: + case BYTES: + case MACRO: + case COMP: + case FILE: + case IDENT: + if (prev == Token.FILE) { + buf.myname = scanner.stringValue; + } else { + buf.myname = scanner.stringValue + ".class"; + } + break; + default: + env.error(scanner.prevPos, "name.expected"); + throw new SyntaxError(); + } + scanner.scan(); + env.traceln("starting class " + buf.myname); + // Parse the clause + parseClause(); + env.traceln("ending class " + buf.myname); + + } // end parseClass + + private void parseClause() throws IOException { + switch (scanner.token) { + case LBRACE: + parseStruct(); + break; + case LSQBRACKET: + parseArray(); + break; + case BYTES: + parseByteArray(); + break; + case ATTR: + parseAttr(); + break; + case COMP: + parseComp(); + break; + default: + env.error(scanner.pos, "struct.expected"); + } + } + + /** + * Parse an Jcoder file. + */ + void parseFile() { + env.traceln("PARSER"); + context.init(); + try { + while (scanner.token != Token.EOF) { + try { + switch (scanner.token) { + case CLASS: + case MODULE: + case INTERFACE: + case FILE: + Token t = scanner.token; + if ( t == Token.MODULE) { + parseModule(); + } else { + parseClass(t); + } + // End of the class,interface or module + env.flushErrors(); + Classes.add(buf); + break; + case SEMICOLON: + // Bogus semi colon + scanner.scan(); + break; + + case EOF: + // The end + return; + + default: + env.traceln("unexpected token=" + scanner.token.toString()); + env.error(scanner.pos, "toplevel.expected"); + throw new SyntaxError(); + } + } catch (SyntaxError e) { + String msg = e.getMessage(); + env.traceln("SyntaxError " + (msg == null ? "" : msg)); + if( env.debugInfoFlag ) { + e.printStackTrace(); + } + recoverFile(); + } + } + } catch (IOException e) { + env.error(scanner.pos, "io.exception", env.getInputFileName()); + } + } //end parseFile + + /*---------------------------------------------*/ + private static char fileSeparator; //=System.getProperty("file.separator"); + + /** + * write to the directory passed with -d option + */ + public void write(ByteBuffer cls, File destdir) throws IOException { + String myname = cls.myname; + if (myname == null) { + env.error("cannot.write", null); + return; + } + + env.traceln("writing " + myname); + File outfile; + if (destdir == null) { + int startofname = myname.lastIndexOf('/'); + if (startofname != -1) { + myname = myname.substring(startofname + 1); + } + outfile = new File(myname); + } else { + env.traceln("writing -d " + destdir.getPath()); + if (fileSeparator == 0) { + fileSeparator = System.getProperty("file.separator").charAt(0); + } + if (fileSeparator != '/') { + myname = myname.replace('/', fileSeparator); + } + outfile = new File(destdir, myname); + File outdir = new File(outfile.getParent()); + if (!outdir.exists() && !outdir.mkdirs()) { + env.error("cannot.write", outdir.getPath()); + return; + } + } + + BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outfile)); + out.write(cls.data, 0, cls.length); + try { + out.close(); + } catch (IOException ignored) { } + } + + /** + * Writes the classes + */ + public void write(File destdir) throws IOException { + for (ByteBuffer cls : Classes) { + write(cls, destdir); + } + } // end write() +} // end Jcoder diff --git a/src/org/openjdk/asmtools/jcoder/Main.java b/src/org/openjdk/asmtools/jcoder/Main.java new file mode 100644 index 0000000..c348600 --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/Main.java @@ -0,0 +1,197 @@ +/* + * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +import org.openjdk.asmtools.common.Tool; +import org.openjdk.asmtools.util.I18NResourceBundle; +import org.openjdk.asmtools.util.ProductInfo; + +import java.io.*; +import java.util.ArrayList; +import java.util.HashMap; + +/** + * + * + */ +public class Main extends Tool { + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + public Main(PrintWriter out, String programName) { + super(out, programName); + printCannotReadMsg = (fname) -> error(i18n.getString("jcoder.error.cannot_read", fname)); + } + + public Main(PrintStream out, String program) { + this(new PrintWriter(out), program); + } + + @Override + public void usage() { + println(i18n.getString("jcoder.usage")); + println(i18n.getString("jcoder.opt.nowrite")); + println(i18n.getString("jcoder.opt.ignore")); + println(i18n.getString("jcoder.opt.d")); + println(i18n.getString("jcoder.opt.version")); + } + + /** + * Run the compiler + */ + public synchronized boolean compile(String argv[]) { + File destDir = null; + boolean traceFlag = false; + DebugFlag = () -> false; + long tm = System.currentTimeMillis(); + ArrayList v = new ArrayList<>(); + boolean nowrite = false; + boolean ignore = false; + int nwarnings = 0; + HashMap macros = new HashMap<>(); + macros.put("VERSION", "3;45"); + + // Parse arguments + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + if (!arg.startsWith("-")) { + v.add(arg); + } else if (arg.startsWith("-D")) { + int argLength = arg.length(); + if (argLength == 2) { + error(i18n.getString("jcoder.error.D_needs_macro")); + return false; + } + int index = arg.indexOf('='); + if (index == -1) { + error(i18n.getString("jcoder.error.D_needs_macro")); + return false; + } + String macroId = arg.substring(2, index); + index++; + if (argLength == index) { + error(i18n.getString("jcoder.error.D_needs_macro")); + return false; + } + String macro; + if (arg.charAt(index) == '"') { + index++; + if (argLength == index || arg.charAt(argLength - 1) != '"') { + error(i18n.getString("jcoder.error.no_closing_quota")); + return false; + } + macro = arg.substring(index, argLength - 1); + } else { + macro = arg.substring(index, argLength); + } + macros.put(macroId, macro); + } else if (arg.equals("-vv")) { + DebugFlag = () -> true; + traceFlag = true; + } else if (arg.equals("-v")) { + traceFlag = true; + } else if (arg.equals("-nowrite")) { + nowrite = true; + } else if (arg.equals("-ignore")) { + ignore = true; + } else if (arg.equals("-d")) { + if ((i + 1) == argv.length) { + error(i18n.getString("jcoder.error.d_requires_argument")); + usage(); + return false; + } + destDir = new File(argv[++i]); + if (!destDir.exists()) { + error(i18n.getString("jcoder.error.does_not_exist", destDir)); + return false; + } + } else if (arg.equals("-version")) { + println(ProductInfo.FULL_VERSION); + } else { + error(i18n.getString("jcoder.error.invalid_option", arg)); + usage(); + return false; + } + } + if (v.isEmpty()) { + usage(); + return false; + } + // compile all input files + try { + for (String inpname : v) { + SourceFile env; + Jcoder p; + + DataInputStream dataInputStream = getDataInputStream(inpname); + if( dataInputStream == null ) { + nerrors++; + continue; + } + env = new SourceFile(this, dataInputStream, inpname, out); + env.traceFlag = traceFlag; + env.debugInfoFlag = DebugFlag.getAsBoolean(); + p = new Jcoder(env, macros); + p.parseFile(); + env.traceln("END PARSER"); + env.closeInp(); + + nerrors += env.nerrors; + nwarnings += env.nwarnings; + if (nowrite || (nerrors > 0 & !ignore)) { + continue; + } + try { + env.traceln("WRITE"); + p.write(destDir); + } catch (FileNotFoundException ex) { + error(i18n.getString("jcoder.error.cannot_write", ex.getMessage())); + } + } + } catch (Error ee) { + ee.printStackTrace(); + error(i18n.getString("jcoder.error.fatal_error")); + } catch (Exception ee) { + ee.printStackTrace(); + error(i18n.getString("jcoder.error.fatal_exception")); + } + + boolean errs = nerrors > 0; + boolean warns = nwarnings > 0; + if (!errs && !warns) { + return true; + } + println(errs ? (nerrors > 1 ? (nerrors + " errors") : "1 error") + : "" + ((errs && warns) ? ", " : "") + (warns ? (nwarnings > 1 ? (nwarnings + " warnings") : "1 warning") : "")); + return !errs; + } + + /** + * main program + */ + public static void main(String[] argv) { + Main compiler = new Main(new PrintWriter(System.out), "jcoder"); + System.exit(compiler.compile(argv) ? 0 : 1); + } +} diff --git a/src/org/openjdk/asmtools/jcoder/Scanner.java b/src/org/openjdk/asmtools/jcoder/Scanner.java new file mode 100644 index 0000000..86b8f27 --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/Scanner.java @@ -0,0 +1,905 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +import static org.openjdk.asmtools.jcoder.JcodTokens.*; + +import java.io.IOException; +import java.util.HashMap; + +/** + * A Scanner for Jcoder tokens. Errors are reported to the environment object.

+ * + * The scanner keeps track of the current token, the value of the current token (if any), + * and the start position of the current token.

+ * + * The scan() method advances the scanner to the next token in the input.

+ * + * The match() method is used to quickly match opening brackets (ie: '(', '{', or '[') + * with their closing counter part. This is useful during error recovery.

+ * + * The compiler treats either "\n", "\r" or "\r\n" as the end of a line.

+ */ +public class Scanner { + /*-------------------------------------------------------- */ + /* Scanner Fields */ + + /** + * End of input + */ + public static final int EOF = -1; + public static final int LBRACE = 123; // "{" + private boolean debugCP = false; + private int numCPentrs = 0; + + /** + * Where errors are reported + */ + protected SourceFile env; + + /** + * Input stream + */ + protected SourceFile in; + HashMap macros; + + /** + * The current character + */ + protected int ch, prevCh = -1; + protected String macro; + protected int indexMacro; + + /** + * Current token + */ + protected Token token; + + /** + * The position of the current token + */ + protected int pos; + + /** + * The position of the previous token + */ + protected int prevPos; + + /* Token values. */ + protected long longValue; + protected int intValue; + protected int intSize; + protected String stringValue; + protected ByteBuffer longStringValue; + protected int sign; // sign, when reading number + + /* A doc comment preceding the most recent token */ + protected String docComment; + + /** + * A growable character buffer. + */ + private int count; + private char[] buffer = new char[32]; + + /*-------------------------------------------------------- */ + /** + * Create a scanner to scan an input stream. + */ + protected Scanner(SourceFile sf, HashMap macros) + throws IOException { + this.env = sf; + this.in = sf; + this.macros = macros; + + ch = sf.read(); + prevPos = sf.pos; + + scan(); + } + + /** + * for use in jcfront. + */ + protected Scanner(SourceFile sf) + throws IOException { + this.env = sf; + this.in = sf; + this.macros = new HashMap<>(); + + ch = sf.read(); + prevPos = sf.pos; + + scan(); + } + + /* *********************************************** */ + void setDebugCP(boolean enable) { + if (enable) { + numCPentrs = 0; + } + debugCP = enable; + + } + + void addConstDebug(ConstType ct) { + numCPentrs += 1; + env.traceln("\n Const[" + numCPentrs + "] = " + ct.printval()); + } + + void setMacro(String macro) { + this.macro = macro; + indexMacro = 0; + prevCh = ch; + } + + void readCh() throws IOException { + if (macro != null) { + if (indexMacro < macro.length()) { + ch = macro.charAt(indexMacro); + } + macro = null; + } + if (prevCh >= 0) { + ch = prevCh; + prevCh = -1; + } else { + ch = in.read(); + } + } + + private void putc(int ch) { + if (count == buffer.length) { + char[] newBuffer = new char[buffer.length * 2]; + System.arraycopy(buffer, 0, newBuffer, 0, buffer.length); + buffer = newBuffer; + } + buffer[count++] = (char) ch; + } + + private String bufferString() { + char[] buf = new char[count]; + System.arraycopy(buffer, 0, buf, 0, count); + return new String(buf); + } + + /** + * Scan a comment. This method should be called once the initial /, * and the next + * character have been read. + */ + private void skipComment() throws IOException { + while (true) { + switch (ch) { + case EOF: + env.error(pos, "eof.in.comment"); + return; + + case '*': + readCh(); + if (ch == '/') { + readCh(); + return; + } + break; + + default: + readCh(); + break; + } + } + } + + /** + * Scan a doc comment. This method should be called once the initial /, * and * have + * been read. It gathers the content of the comment (witout leading spaces and '*'s) + * in the string buffer. + */ + private String scanDocComment() throws IOException { + count = 0; + + if (ch == '*') { + do { + readCh(); + } while (ch == '*'); + if (ch == '/') { + readCh(); + return ""; + } + } + switch (ch) { + case '\n': + case ' ': + readCh(); + break; + } + + boolean seenstar = false; + int c = count; + while (true) { + switch (ch) { + case EOF: + env.error(pos, "eof.in.comment"); + return bufferString(); + + case '\n': + putc('\n'); + readCh(); + seenstar = false; + c = count; + break; + + case ' ': + case '\t': + putc(ch); + readCh(); + break; + + case '*': + if (seenstar) { + readCh(); + if (ch == '/') { + readCh(); + count = c; + return bufferString(); + } + putc('*'); + } else { + seenstar = true; + count = c; + do { + readCh(); + } while (ch == '*'); + switch (ch) { + case ' ': + readCh(); + break; + + case '/': + readCh(); + count = c; + return bufferString(); + } + } + break; + + default: + if (!seenstar) { + seenstar = true; + } + putc(ch); + readCh(); + c = count; + break; + } + } + } + + /** + * Scan a decimal number + */ + private void scanDecNumber() throws IOException { + boolean overflow = false; + long value = ch - '0'; + count = 0; + token = Token.INTVAL; + intSize = 2; // default + putc(ch); // save character in buffer +numberLoop: + for (;;) { + readCh(); + switch (ch) { + case '8': + case '9': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + putc(ch); + overflow = overflow || (value * 10) / 10 != value; + value = (value * 10) + (ch - '0'); + overflow = overflow || (value - 1 < -1); + break; + case 'b': + readCh(); + intSize = 1; + break numberLoop; + case 's': + readCh(); + intSize = 2; + break numberLoop; + case 'i': + readCh(); + intSize = 4; + break numberLoop; + case 'l': + readCh(); + intSize = 8; + break numberLoop; + default: + break numberLoop; + } + } + longValue = value; + intValue = (int) value; + // we have just finished reading the number. The next thing better + // not be a letter or digit. + if (Character.isJavaIdentifierPart((char) ch) || ch == '.') { + env.error(in.pos, "invalid.number", Character.toString((char)ch)); + do { + readCh(); + } while (Character.isJavaIdentifierPart((char) ch) || ch == '.'); + return; + } + if (overflow) { + env.error(pos, "overflow"); + } + } // scanNumber() + + /** + * Scan a hex number. + */ + private void scanHexNumber() throws IOException { + boolean overflow = false; + long value = 0; + int cypher; + count = 0; + token = Token.INTVAL; + intSize = 2; // default + putc(ch); // save character in buffer +numberLoop: + for (int k = 0;; k++) { + readCh(); + switch (ch) { + case '8': + case '9': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + cypher = (char) ch - '0'; + break; + case 'd': + case 'D': + case 'e': + case 'E': + case 'f': + case 'F': + case 'a': + case 'A': + case 'b': + case 'B': + case 'c': + case 'C': + cypher = 10 + Character.toLowerCase((char) ch) - 'a'; + break; + + default: + break numberLoop; + } + putc(ch); + overflow = overflow || ((value >>> 60) != 0); + value = (value << 4) + cypher; + intSize = (k + 1) / 2; + } + longValue = value; + intValue = (int) value; + // we have just finished reading the number. The next thing better + // not be a letter or digit. + if (Character.isJavaIdentifierPart((char) ch) || ch == '.') { + env.error(in.pos, "invalid.number", Character.toString((char)ch)); + do { + readCh(); + } while (Character.isJavaIdentifierPart((char) ch) || ch == '.'); + intValue = 0; +// } else if ( overflow || (intValue - 1 < -1) ) { + } else if (overflow) { + intValue = 0; // so we don't get second overflow in Parser + env.error(pos, "overflow"); + } + } // scanNumber() + + /** + * Scan an escape character. + * + * @return the character or -1 if it escaped an end-of-line. + */ + private int scanEscapeChar() throws IOException { + int p = in.pos; + + readCh(); + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': { + int n = ch - '0'; + for (int i = 2; i > 0; i--) { + readCh(); + switch (ch) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + n = (n << 3) + ch - '0'; + break; + + default: + if (n > 0xFF) { + env.error(p, "invalid.escape.char"); + } + return n; + } + } + readCh(); + if (n > 0xFF) { + env.error(p, "invalid.escape.char"); + } + return n; + } + case 'r': + readCh(); + return '\r'; + case 'n': + readCh(); + return '\n'; + case 'f': + readCh(); + return '\f'; + case 'b': + readCh(); + return '\b'; + case 't': + readCh(); + return '\t'; + case '\\': + readCh(); + return '\\'; + case '\"': + readCh(); + return '\"'; + case '\'': + readCh(); + return '\''; + } + + env.error(p, "invalid.escape.char"); + readCh(); + return -1; + } + + /** + * Scan a string. The current character should be the opening " of the string. + */ + private void scanString() throws IOException { + token = Token.STRINGVAL; + count = 0; + readCh(); + +loop: + for (;;) { + switch (ch) { + case EOF: + env.error(pos, "eof.in.string"); + break loop; + + case '\n': + readCh(); + env.error(pos, "newline.in.string"); + break loop; + + case '"': + readCh(); + break loop; + + case '\\': { + int c = scanEscapeChar(); + if (c >= 0) { + putc((char)c); + } + break; + } + + default: + putc(ch); + readCh(); + break; + } + } + stringValue = bufferString(); + } + + /** + * Scan a character array. The current character should be the opening ' of the array. + */ + private void scanCharArray() throws IOException { + token = Token.LONGSTRINGVAL; + ByteBuffer buf = new ByteBuffer(); + count = 0; + readCh(); + +loop: + for (;;) { + int c = ch; + switch (ch) { + case EOF: + env.error(pos, "eof.in.string"); + break loop; + + case '\n': + readCh(); + env.error(pos, "newline.in.string"); + break loop; + + case '\'': + readCh(); + break loop; + + case '\\': + c = scanEscapeChar(); + if (c < 0) { + break; + } + // no break - continue + default: + // see description of java.io.DataOutput.writeUTF() + if ((c > 0) && (c <= 0x7F)) { + buf.write(c); + } else if ((c == 0) || ((c >= 0x80) && (c <= 0x7FF))) { + buf.write(0xC0 | (0x1F & (c >> 6))); + buf.write(0x80 | (0x3f & c)); + } else { + buf.write(0xc0 | (0x0f & (c >> 12))); + buf.write(0x80 | (0x3f & (c >> 6))); + buf.write(0x80 | (0x3f & c)); + } + readCh(); + } + } + longStringValue = buf; + } + + /** + * Scan an Identifier. The current character should be the first character of the + * identifier. + */ + private void scanIdentifier() throws IOException { + count = 0; + boolean compound = false; + for (;;) { + putc(ch); + readCh(); + if ((ch == '/') || (ch == '.') || (ch == '-')) { + compound = true; + } else if (!Character.isJavaIdentifierPart((char) ch)) { + break; + } + } + stringValue = bufferString(); + if (compound) { + token = Token.IDENT; + } else { + token = keyword_token_ident(stringValue); + if (token == Token.IDENT) { + intValue = constValue(stringValue); + if (intValue != -1) { + // this is a constant + if (debugCP) { + ConstType ct = constType(stringValue); + if (ct != null) { + addConstDebug(ct); + } + } + token = Token.INTVAL; + intSize = 1; + longValue = intValue; + } + } + } + } // end scanIdentifier + + // skip till symbol + protected void skipTill(int sym) throws IOException { + while (true) { + if( ch == EOF ) { + env.error(pos, "eof.in.comment"); + return; + } else if (ch == sym) { + return; + } + readCh(); + } + } + + protected int xscan() throws IOException { + int retPos = pos; + prevPos = in.pos; + docComment = null; + sign = 1; + for (;;) { + pos = in.pos; + + switch (ch) { + case EOF: + token = Token.EOF; + return retPos; + + case '\n': + case ' ': + case '\t': + case '\f': + readCh(); + break; + + case '/': + readCh(); + switch (ch) { + case '/': + // Parse a // comment + do { + readCh(); + } while ((ch != EOF) && (ch != '\n')); + break; + + case '*': + readCh(); + if (ch == '*') { + docComment = scanDocComment(); + } else { + skipComment(); + } + break; + + default: + token = Token.DIV; + return retPos; + } + break; + + case '"': + scanString(); + return retPos; + + case '\'': + scanCharArray(); + return retPos; + + case '-': + sign = -sign; // hack: no check that numbers only are allowed after + case '+': + readCh(); + break; + + case '0': + readCh(); + token = Token.INTVAL; + longValue = intValue = 0; + switch (ch) { + case 'x': + case 'X': + scanHexNumber(); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + scanDecNumber(); + break; + case 'b': + readCh(); + intSize = 1; + break; + case 's': + readCh(); + intSize = 2; + break; + case 'i': + readCh(); + intSize = 4; + break; + case 'l': + readCh(); + intSize = 8; + break; + default: + intSize = 2; + } + return retPos; + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + scanDecNumber(); + return retPos; + + case '{': + readCh(); + token = Token.LBRACE; + return retPos; + + case '}': + readCh(); + token = Token.RBRACE; + return retPos; + + case '(': + readCh(); + token = Token.LPAREN; + return retPos; + + case ')': + readCh(); + token = Token.RPAREN; + return retPos; + + case '[': + readCh(); + token = Token.LSQBRACKET; + return retPos; + + case ']': + readCh(); + token = Token.RSQBRACKET; + return retPos; + + case ',': + readCh(); + token = Token.COMMA; + return retPos; + + case ';': + readCh(); + token = Token.SEMICOLON; + return retPos; + + case ':': + readCh(); + token = Token.COLON; + return retPos; + + case '=': + readCh(); + if (ch == '=') { + readCh(); + token = Token.EQ; + return retPos; + } + token = Token.ASSIGN; + return retPos; + + case '\u001a': + // Our one concession to DOS. + readCh(); + if (ch == EOF) { + token = Token.EOF; + return retPos; + } + env.error(pos, "funny.char"); + readCh(); + break; + + case '#': + readCh(); + scanDecNumber(); + return retPos; + + case '&': { + readCh(); + retPos = pos; + if (!Character.isJavaIdentifierStart((char) ch)) { + env.error(pos, "identifier.expected"); + } + scanIdentifier(); + String macroId = stringValue; + String macro = (String) macros.get(macroId); + if (macro == null) { + env.error(pos, "macro.undecl", macroId); + throw new SyntaxError(); + } + setMacro(macro); + readCh(); + } + break; + + default: + if (Character.isJavaIdentifierStart((char) ch)) { + scanIdentifier(); + return retPos; + } + env.error(pos, "funny.char"); + readCh(); + break; + } + } + } + + /** + * Scan to a matching '}', ']' or ')'. The current token must be a '{', '[' or '('; + */ + protected void match(Token open, Token close) throws IOException { + int depth = 1; + + while (true) { + scan(); + if (token == open) { + depth++; + } else if (token == close) { + if (--depth == 0) { + return; + } + } else if (token == Token.EOF) { + env.error(pos, "unbalanced.paren"); + return; + } + } + } + + /** + * Scan the next token. + * + * @return the position of the previous token. + */ + protected int scan() throws IOException { + int retPos = xscan(); +//env.traceln("scanned:"+token+" ("+keywordName(token)+")"); + return retPos; + } + + /** + * Scan the next token. + * + * @return the position of the previous token. + */ + protected int scanMacro() throws IOException { + int retPos = xscan(); +//env.traceln("scanned:"+token+" ("+keywordName(token)+")"); + return retPos; + } +} diff --git a/src/org/openjdk/asmtools/jcoder/SourceFile.java b/src/org/openjdk/asmtools/jcoder/SourceFile.java new file mode 100644 index 0000000..a03a7fb --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/SourceFile.java @@ -0,0 +1,440 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +import java.io.*; +import java.util.Hashtable; + +import org.openjdk.asmtools.common.Tool; +import org.openjdk.asmtools.util.I18NResourceBundle; + +/** + * An input stream for java programs. The stream treats either "\n", "\r" or "\r\n" as the + * end of a line, it always returns \n. It also parses UNICODE characters expressed as + * \uffff. However, if it sees "\\", the second slash cannot begin a unicode sequence. It + * keeps track of the current position in the input stream. + * + * An position consists of: ((linenr << OFFSETBITS) | offset) this means that both + * the line number and the exact offset into the file are encoded in each postion + * value.

+ */ +public class SourceFile implements org.openjdk.asmtools.jasm.Constants { + + Tool tool; + + boolean traceFlag = false; + boolean debugInfoFlag = false; + /** + * The increment for each character. + */ + static final int OFFSETINC = 1; + /** + * The increment for each line. + */ + static final int LINEINC = 1 << OFFSETBITS; + String inputFileName; + InputStream in; + PrintWriter out; + int pos; + private int chpos; + private int pushBack = -1; + + public SourceFile(Tool tool, DataInputStream dataInputStream, String inputFileName, PrintWriter out) { + this.tool = tool; + this.inputFileName = inputFileName; + this.in = new BufferedInputStream(dataInputStream); + chpos = LINEINC; + this.out = out; + } + + public String getInputFileName() { + return inputFileName; + } + + public void closeInp() { + try { + in.close(); + } catch (IOException e) { + } + flushErrors(); + } + + public int read() throws IOException { + pos = chpos; + chpos += OFFSETINC; + + int c = pushBack; + if (c == -1) { + c = in.read(); + } else { + pushBack = -1; + } + + // parse special characters + switch (c) { + case -2: + // -2 is a special code indicating a pushback of a backslash that + // definitely isn't the start of a unicode sequence. + return '\\'; + + case '\\': + if ((c = in.read()) != 'u') { + pushBack = (c == '\\' ? -2 : c); + return '\\'; + } + // we have a unicode sequence + chpos += OFFSETINC; + while ((c = in.read()) == 'u') { + chpos += OFFSETINC; + } + + // unicode escape sequence + int d = 0; + for (int i = 0; i < 4; i++, chpos += OFFSETINC, c = in.read()) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + d = (d << 4) + c - '0'; + break; + + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + d = (d << 4) + 10 + c - 'a'; + break; + + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + d = (d << 4) + 10 + c - 'A'; + break; + + default: + error(pos, "invalid.escape.char"); + pushBack = c; + return d; + } + } + pushBack = c; + return d; + + case '\n': + chpos += LINEINC; + return '\n'; + + case '\r': + if ((c = in.read()) != '\n') { + pushBack = c; + } else { + chpos += OFFSETINC; + } + chpos += LINEINC; + return '\n'; + + default: + return c; + } + } + + public int lineNumber(int pos) { + return pos >>> OFFSETBITS; + } + + public int lineNumber() { + return pos >>> OFFSETBITS; + } + + /*============================================================== Environment */ + /** + * The number of errors and warnings + */ + public int nerrors; + public int nwarnings; + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + /* + * Until place for jasm.properties is defind, + * I have to keep them right here + * + static Hashtable properties = new Hashtable(40); + + static { + // Scanner: + properties.put("err.eof.in.comment", "Comment not terminated at end of input."); + properties.put("err.invalid.number", "Invalid character \'%s\' in number."); + properties.put("err.invalid.octal.number", "Invalid character in octal number."); + properties.put("err.overflow", "Numeric overflow."); + properties.put("err.float.format", "Invalid floating point format."); + properties.put("err.eof.in.string", "String not terminated at end of input."); + properties.put("err.newline.in.string", "String not terminated at end of line."); + properties.put("err.funny.char", "Invalid character in input."); + properties.put("err.unbalanced.paren", "Unbalanced parentheses."); + // Parser: + properties.put("err.toplevel.expected", "Class or interface declaration expected."); + properties.put("err.token.expected", "'%s' expected."); + properties.put("err.identifier.expected", "Identifier expected."); + properties.put("err.name.expected", "Name expected."); + properties.put("err.io.exception", "I/O error in %s."); + properties.put("err.cannot.write", "Cannot write to %s."); + properties.put("warn.array.wronglength", "expected array length %s do not match real length %s; expected length written"); + properties.put("warn.attr.wronglength", "expected attribute length %s do not match real length %s; expected length written"); + properties.put("attrname.notfound", "Cannot find \"%s\" in constant pool"); + properties.put("err.attrname.expected", "Attribute's name or index expected."); + properties.put("err.element.expected", "Primary data item expected."); + properties.put("err.struct.expected", "Structured data item expected."); + properties.put("err.macro.undecl", "Macro %s undefined."); + } + static String getProperty(String nm) { + return (String) properties.get(nm); + } + */ + /** + * Error String + */ + String errorString(String err, Object arg1, Object arg2, Object arg3) { + String str = null; + + if (!err.startsWith("warn.")) { + err = "err." + err; + } + //str = getProperty(err); + str = i18n.getString(err); + + if (str == null) { + return "error message '" + err + "' not found"; + } + + StringBuffer buf = new StringBuffer(); + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if ((c == '%') && (i + 1 < str.length())) { + switch (str.charAt(++i)) { + case 's': + String arg = arg1.toString(); + for (int j = 0; j < arg.length(); j++) { + switch (c = arg.charAt(j)) { + case ' ': + case '\t': + case '\n': + case '\r': + buf.append((char) c); + break; + + default: + if ((c > ' ') && (c <= 255)) { + buf.append((char) c); + } else { + buf.append('\\'); + buf.append('u'); + buf.append(Integer.toString(c, 16)); + } + } + } + arg1 = arg2; + arg2 = arg3; + break; + + case '%': + buf.append('%'); + break; + + default: + buf.append('?'); + break; + } + } else { + buf.append((char) c); + } + } + return buf.toString(); + } + + /** + * List of outstanding error messages + */ + ErrorMessage errors; + + /** + * Insert an error message in the list of outstanding error messages. The list is + * sorted on input position. + */ + void insertError(int where, String message) { + //output("ERR = " + message); + ErrorMessage msg = new ErrorMessage(where, message); + if (errors == null) { + errors = msg; + } else if (errors.where > where) { + msg.next = errors; + errors = msg; + } else { + ErrorMessage m = errors; + for (; (m.next != null) && (m.next.where <= where); m = m.next) { + ; + } + msg.next = m.next; + m.next = msg; + } + } + + /** + * Flush outstanding errors + */ + public void flushErrors() { + if (errors == null) { + return; + } + + try { + // Read the file + DataInputStream dataInputStream = tool.getDataInputStream(inputFileName); + if (dataInputStream == null) + return; + + byte data[] = new byte[dataInputStream.available()]; + dataInputStream.read(data); + dataInputStream.close(); + + // Report the errors + for (ErrorMessage msg = errors; msg != null; msg = msg.next) { + int ln = msg.where >>> OFFSETBITS; + int off = msg.where & ((1 << OFFSETBITS) - 1); + + int i, j; + for (i = off; (i > 0) && (data[i - 1] != '\n') && (data[i - 1] != '\r'); i--) { + ; + } + for (j = off; (j < data.length) && (data[j] != '\n') && (data[j] != '\r'); j++) { + ; + } + + String prefix = inputFileName + ":" + ln + ":"; + outputln(prefix + " " + msg.message); + outputln(new String(data, i, j - i)); + + char strdata[] = new char[(off - i) + 1]; + for (j = i; j < off; j++) { + strdata[j - i] = (data[j] == '\t') ? '\t' : ' '; + } + strdata[off - i] = '^'; + outputln(new String(strdata)); + } + } catch (IOException e) { + outputln("I/O exception"); + } + errors = null; + } + + /** + * Output a string. This can either be an error message or something for debugging. + * This should be used instead of print. + */ + public void output(String msg) { + int len = msg.length(); + for (int i = 0; i < len; i++) { + out.write(msg.charAt(i)); + } + out.flush(); + } + + /** + * Output a string. This can either be an error message or something for debugging. + * This should be used instead of println. + */ + public void outputln(String msg) { + output(msg); + out.write('\n'); + out.flush(); + } + + /** + * Issue an error. + * @param where Offset in the source for the error + * @param err Error number (as defined in this interface) + * @param arg1 Optional argument to the error (null if not applicable) + * @param arg2 Optional argument to the error (null if not applicable) + * @param arg3 Optional argument to the error (null if not applicable) + */ + /** + * Issue an error + */ + public void error(int where, String err, Object arg1, Object arg2, Object arg3) { + String msg = errorString(err, arg1, arg2, arg3); + if (err.startsWith("warn.")) { + nwarnings++; + } else { + nerrors++; + } + traceln("error:" + msg); + insertError(where, msg); + } + + public final void error(int where, String err, Object arg1, Object arg2) { + error(where, err, arg1, arg2, null); + } + + public final void error(int where, String err, Object arg1) { + error(where, err, arg1, null, null); + } + + public final void error(int where, String err) { + error(where, err, null, null, null); + } + + public final void error(String err) { + error(pos, err, null, null, null); + } + + public final void error(String err, Object arg1) { + error(pos, err, arg1, null, null); + } + + /*============================================================== trace */ + public void trace(String message) { + if (traceFlag) { + output(message); + } + } + + public void traceln(String message) { + if (traceFlag) { + outputln(message); + } + } +} // end SourceFile + diff --git a/src/org/openjdk/asmtools/jcoder/SyntaxError.java b/src/org/openjdk/asmtools/jcoder/SyntaxError.java new file mode 100644 index 0000000..6d8784e --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/SyntaxError.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jcoder; + +/** + * Syntax errors, should always be caught inside the parser for error recovery. + */ +class SyntaxError extends Error { +} diff --git a/src/org/openjdk/asmtools/jcoder/i18n.properties b/src/org/openjdk/asmtools/jcoder/i18n.properties new file mode 100644 index 0000000..99c1f03 --- /dev/null +++ b/src/org/openjdk/asmtools/jcoder/i18n.properties @@ -0,0 +1,67 @@ +# Copyright (c) 2014, 2019, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +jcoder.usage=\ +Usage: java -jar asmtools.jar jcoder [options] file.jcod...\n\ +where possible options include: + +jcoder.opt.nowrite=\ +\ -nowrite do not write resulting .class files +jcoder.opt.ignore=\ +\ -ignore ingore non-fatal error(s) that suppress writing .class files +jcoder.opt.d=\ +\ -d destdir directory to place resulting .class files +jcoder.opt.version=\ +\ -version prints the program version + +jcoder.error.D_needs_macro=-D needs macro declaration +jcoder.error.no_closing_quota=no closing quota in macro definition +jcoder.error.d_requires_argument=-d requires argument +jcoder.error.does_not_exist={0} does not exist +jcoder.error.invalid_option=invalid option: {0} +jcoder.error.cannot_read=cannot read {0} +jcoder.error.cannot_write=cannot write {0} +jcoder.error.fatal_error=fatal error +jcoder.error.fatal_exception=fatal exception + +err.eof.in.comment=Comment not terminated at end of input. +err.invalid.number=Invalid character '%s' in number. +#err.invalid.octal.number=Invalid character in octal number. +err.overflow=Numeric overflow. +#err.float.format=Invalid floating point format. +err.eof.in.string=String not terminated at end of input. +err.newline.in.string=String not terminated at end of line. +err.funny.char=Invalid character in input. +err.unbalanced.paren=Unbalanced parentheses. +err.toplevel.expected=Class or interface declaration expected. +err.token.expected='{0}' expected. +err.identifier.expected=Identifier expected. +err.name.expected=Name expected. +err.io.exception=I/O error in {0}. +err.cannot.write=Cannot write to {0}. +warn.array.wronglength=expected array length %s do not match real length %s; expected length written +warn.attr.wronglength=expected attribute length %s do not match real length %s; expected length written +#attrname.notfound=Cannot find "{0}" in constant pool +err.attrname.expected=Attribute's name or index expected. +err.element.expected=Primary data item expected. +err.struct.expected=Structured data item expected. +err.macro.undecl=Macro {0} undefined. +err.invalid.escape.char=Invalid escape char diff --git a/src/org/openjdk/asmtools/jdec/ClassData.java b/src/org/openjdk/asmtools/jdec/ClassData.java new file mode 100644 index 0000000..e66538b --- /dev/null +++ b/src/org/openjdk/asmtools/jdec/ClassData.java @@ -0,0 +1,1291 @@ +/* + * Copyright (c) 2009, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdec; + +import org.openjdk.asmtools.common.Module; +import org.openjdk.asmtools.asmutils.StringUtils; +import org.openjdk.asmtools.jasm.Modifiers; +import org.openjdk.asmtools.jcoder.JcodTokens; +import org.openjdk.asmtools.util.I18NResourceBundle; + +import java.awt.event.KeyEvent; +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; +import java.io.PrintWriter; + +import static java.lang.String.format; +import static org.openjdk.asmtools.jasm.Tables.*; +import static org.openjdk.asmtools.jasm.Tables.AnnotElemType.AE_UNKNOWN; +import static org.openjdk.asmtools.jasm.TypeAnnotationTypes.*; + +/** + * Class data of the Java Decoder + */ +class ClassData { + + private byte[] types; + private Object[] cpool; + private int CPlen; + private NestedByteArrayInputStream countedin; + private DataInputStream in; + private PrintWriter out; + private int[] cpe_pos; + private boolean printDetails; + private String entityType = ""; + private String entityName = ""; + + public static I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + ClassData(DataInputStream dis, int printFlags, PrintWriter out) throws IOException { + byte[] buf = new byte[dis.available()]; + try { + if (dis.read(buf) <= 0) + throw new IOException("The file is empty"); + } finally { + dis.close(); + } + countedin = new NestedByteArrayInputStream(buf); + in = new DataInputStream(countedin); + this.out = out; + printDetails = ((printFlags & 1) == 1); + } + + /*========================================================*/ + private static final char[] hexTable = { + '0', '1', '2', '3', '4', '5', '6', '7', + '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + }; + + private String toHex(long val, int width) { + StringBuilder s = new StringBuilder(); + for (int i = width * 2 - 1; i >= 0; i--) { + s.append(hexTable[((int) (val >> (4 * i))) & 0xF]); + } + return "0x" + s.toString(); + } + + private String toHex(long val) { + int width; + for (width = 8; width > 0; width--) { + if ((val >> (width - 1) * 8) != 0) { + break; + } + } + return toHex(val, width); + } + + private void printByteHex(PrintWriter out, int b) { + out.print(hexTable[(b >> 4) & 0xF]); + out.print(hexTable[b & 0xF]); + } + + private void printBytes(PrintWriter out, DataInputStream in, int len) + throws IOException { + try { + for (int i = 0; i < len; i++) { + if (i % 8 == 0) { + out_print("0x"); + } + printByteHex(out, in.readByte()); + if (i % 8 == 7) { + out.println(";"); + } + } + } finally { + if (len % 8 != 0) { + out.println(";"); + } + } + } + + private void printRestOfBytes() { + for (int i = 0; ; i++) { + try { + byte b = in.readByte(); + if (i % 8 == 0) { + out_print("0x"); + } + printByteHex(out, b); + if (i % 8 == 7) { + out.print(";\n"); + } + } catch (IOException e) { + return; + } + } + } + + private void printUtf8InfoIndex(int index, String indexName) { + String name = (String) cpool[index]; + out_print("#" + index + "; // "); + if (printDetails) { + out.println(String.format("%-16s",indexName) + " : " + name); + } else { + out.println(indexName); + } + } + + /*========================================================*/ + private int shift = 0; + + private void out_begin(String s) { + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.println(s); + shift++; + } + + private void out_print(String s) { + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.print(s); + } + + private void out_println(String s) { + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.println(s); + } + + private void out_end(String s) { + shift--; + for (int i = 0; i < shift; i++) { + out.print(" "); + } + out.println(s); + } + + private String startArray(int length) { + return "[" + (printDetails ? Integer.toString(length) : "") + "]"; + } + + private void startArrayCmt(int length, String comment) { + out_begin(startArray(length) + format(" {%s", comment == null ? "" : " // " + comment)); + } + + private void startArrayCmtB(int length, String comment) { + out_begin(startArray(length) + format("b {%s", comment == null ? "" : " // " + comment)); + } + + /*========================================================*/ + private void readCP(DataInputStream in) throws IOException { + int length = in.readUnsignedShort(); + CPlen = length; + traceln(i18n.getString("jdec.trace.CP_len", length)); + types = new byte[length]; + cpool = new Object[length]; + cpe_pos = new int[length]; + for (int i = 1; i < length; i++) { + byte btag; + int v1; + long lv; + cpe_pos[i] = countedin.getPos(); + btag = in.readByte(); + traceln(i18n.getString("jdec.trace.CP_entry", i, btag)); + types[i] = btag; + ConstType tg = tag(btag); + switch (tg) { + case CONSTANT_UTF8: + cpool[i] = in.readUTF(); + break; + case CONSTANT_INTEGER: + v1 = in.readInt(); + cpool[i] = v1; + break; + case CONSTANT_FLOAT: + v1 = Float.floatToIntBits(in.readFloat()); + cpool[i] = v1; + break; + case CONSTANT_LONG: + lv = in.readLong(); + cpool[i] = lv; + i++; + break; + case CONSTANT_DOUBLE: + lv = Double.doubleToLongBits(in.readDouble()); + cpool[i] = lv; + i++; + break; + case CONSTANT_CLASS: + case CONSTANT_STRING: + case CONSTANT_MODULE: + case CONSTANT_PACKAGE: + v1 = in.readUnsignedShort(); + cpool[i] = v1; + break; + case CONSTANT_INTERFACEMETHOD: + case CONSTANT_FIELD: + case CONSTANT_METHOD: + case CONSTANT_NAMEANDTYPE: + cpool[i] = "#" + in.readUnsignedShort() + " #" + in.readUnsignedShort(); + break; + case CONSTANT_DYNAMIC: + case CONSTANT_INVOKEDYNAMIC: + cpool[i] = in.readUnsignedShort() + "s #" + in.readUnsignedShort(); + break; + case CONSTANT_METHODHANDLE: + cpool[i] = in.readUnsignedByte() + "b #" + in.readUnsignedShort(); + break; + case CONSTANT_METHODTYPE: + cpool[i] = "#" + in.readUnsignedShort(); + break; + default: + CPlen = i; + printCP(out); + out_println(toHex(btag, 1) + "; // invalid constant type: " + (int) btag + " for element " + i); + throw new ClassFormatError(); + } + } + } + + private void printCP(PrintWriter out) { + int length = CPlen; + startArrayCmt(length, "Constant Pool"); + out_println("; // first element is empty"); + try { + int size; + for (int i = 1; i < length; i = i + size) { + size = 1; + byte btag = types[i]; + ConstType tg = tag(btag); + int pos = cpe_pos[i]; + String tagstr; + String valstr; + int v1; + long lv; + if (tg != null) { + tagstr = tg.parseKey(); + } else { + throw new Error("Can't get a tg representing the type of Constant in the Constant Pool at: " + i); + } + switch (tg) { + case CONSTANT_UTF8: { + tagstr = "Utf8"; + valstr = StringUtils.Utf8ToString((String) cpool[i]); + } + break; + case CONSTANT_FLOAT: + case CONSTANT_INTEGER: + v1 = (Integer) cpool[i]; + valstr = toHex(v1, 4); + break; + case CONSTANT_DOUBLE: + case CONSTANT_LONG: + lv = (Long) cpool[i]; + valstr = toHex(lv, 8) + ";"; + size = 2; + break; + case CONSTANT_CLASS: + case CONSTANT_MODULE: + case CONSTANT_PACKAGE: + case CONSTANT_STRING: + v1 = (Integer) cpool[i]; + valstr = "#" + v1; + break; + case CONSTANT_INTERFACEMETHOD: + case CONSTANT_FIELD: + case CONSTANT_METHOD: + case CONSTANT_NAMEANDTYPE: + case CONSTANT_METHODHANDLE: + case CONSTANT_METHODTYPE: + case CONSTANT_DYNAMIC: + case CONSTANT_INVOKEDYNAMIC: + valstr = (String) cpool[i]; + break; + default: + throw new Error("invalid constant type: " + (int) btag); + } + out_print(tagstr + " " + valstr + "; // #" + i); + if (printDetails) { + out_println(" at " + toHex(pos)); + } else { + out.println(); + } + } + } finally { + out_end("} // Constant Pool"); + out.println(); + } + } + + private String getStringPos() { + return " at " + toHex(countedin.getPos()); + } + + private String getCommentPosCond() { + if (printDetails) { + return " // " + getStringPos(); + } else { + return ""; + } + } + + private void decodeCPXAttr(DataInputStream in, int len, String attrname, PrintWriter out) throws IOException { + decodeCPXAttrM(in, len, attrname, out, 1); + } + + private void decodeCPXAttrM(DataInputStream in, int len, String attrname, PrintWriter out, int expectedIndices) throws IOException { + if (len != expectedIndices * 2) { + out_println("// invalid length of " + attrname + " attr: " + len + " (should be " + (expectedIndices * 2) + ") > "); + printBytes(out, in, len); + } else { + StringBuilder outputString = new StringBuilder(); + for (int k = 1; k <= expectedIndices; k++) { + outputString.append("#").append(in.readUnsignedShort()).append("; "); + if (k % 16 == 0) { + out_println(outputString.toString().replaceAll("\\s+$","")); + outputString = new StringBuilder(); + } + } + if (outputString.length() > 0) { + out_println(outputString.toString().replaceAll("\\s+$","")); + } + } + } + + private void printStackMap(DataInputStream in, int elementsNum) throws IOException { + int num; + if (elementsNum > 0) { + num = elementsNum; + } else { + num = in.readUnsignedShort(); + } + out.print(startArray(num) + (elementsNum > 0 ? "z" : "") + "{"); + try { + for (int k = 0; k < num; k++) { + int maptype = in.readUnsignedByte(); + StackMapType mptyp = stackMapType(maptype, out); + String maptypeImg; + if (printDetails) { + maptypeImg = maptype + "b"; + } else { + try { + maptypeImg = mptyp.parsekey(); + } catch (ArrayIndexOutOfBoundsException e) { + maptypeImg = "/* BAD TYPE: */ " + maptype + "b"; + } + } + switch (mptyp) { + case ITEM_Object: + case ITEM_NewObject: + maptypeImg = maptypeImg + "," + in.readUnsignedShort(); + break; + case ITEM_UNKNOWN: + maptypeImg = maptype + "b"; + break; + default: + } + out.print(maptypeImg); + if (k < num - 1) { + out.print("; "); + } + } + } finally { + out.print("}"); + } + } + + /** + * Processes 4.7.20 The RuntimeVisibleTypeAnnotations Attribute, 4.7.21 The RuntimeInvisibleTypeAnnotations Attribute + * type_annotation structure. + */ + private void decodeTargetTypeAndRefInfo(DataInputStream in) throws IOException { + int tt = in.readUnsignedByte(); // [4.7.20] annotations[], type_annotation { u1 target_type; ...} + ETargetType targetType = ETargetType.getTargetType(tt); + if( targetType == null ) { + throw new Error("Type annotation: invalid target_type(u1) " + tt); + } + ETargetInfo targetInfo = targetType.targetInfo(); + out_println(toHex(tt, 1) + "; // target_type: " + targetType.parseKey()); + switch (targetInfo) { + case TYPEPARAM: //[3.3.1] meth_type_param, class_type_param: + out_println(toHex(in.readUnsignedByte(), 1) + "; // param_index"); + break; + case SUPERTYPE: //[3.3.2] class_exts_impls + out_println(toHex(in.readUnsignedShort(), 2) + "; // type_index"); + break; + case TYPEPARAM_BOUND: //[3.3.3] class_type_param_bnds, meth_type_param_bnds + out_println(toHex(in.readUnsignedByte(), 1) + "; // param_index"); + out_println(toHex(in.readUnsignedByte(), 1) + "; // bound_index"); + break; + case EMPTY: //[3.3.4] meth_receiver, meth_ret_type, field + // NOTE: reference_info is empty for this annotation's target + break; + case METHODPARAM: //[3.3.5] meth_formal_param: + out_println(toHex(in.readUnsignedByte(), 1) + "; // parameter_index"); + break; + case EXCEPTION: //[3.3.61] throws_type + //KTL: Updated index to UShort for JSR308 change + out_println(in.readUnsignedShort() + "; // type_index"); + break; + case LOCALVAR: //[3.3.7] local_var, resource_var + { + int lv_num = in.readUnsignedShort(); + startArrayCmt(lv_num, "local_variables"); + try { + for (int i = 0; i < lv_num; i++) { + out_println(in.readUnsignedShort() + " " + in.readUnsignedShort() + + " " + in.readUnsignedShort() + ";" + getCommentPosCond()); + } + } finally { + out_end("}"); + } + } + break; + case CATCH: //[3.3.8] exception_param + out_println(in.readUnsignedShort() + "; // exception_table_index"); + break; + case OFFSET: //[3.3.9] type_test (instanceof), obj_creat (new) + // constr_ref_receiver, meth_ref_receiver + out_println(in.readUnsignedShort() + "; // offset"); + break; + case TYPEARG: //[3.3.10] cast, constr_ref_typearg, meth_invoc_typearg + // constr_invoc_typearg, meth_ref_typearg + out_println(in.readUnsignedShort() + "; // offset"); + out_println(toHex(in.readUnsignedByte(), 1) + "; // type_index"); + break; + default: // should never happen + out_println(toHex(tt, 1) + "; // invalid target_info: " + tt); + throw new ClassFormatError(); + } + // [4.7.20.2] + int path_length = in.readUnsignedByte(); // type_path { u1 path_length; ...} + startArrayCmtB(path_length, "type_paths"); + try { + for (int i = 0; i < path_length; i++) { + // print the type_path elements + out_println("{ " + toHex(in.readUnsignedByte(), 1) // { u1 type_path_kind; + + "; " + toHex(in.readUnsignedByte(), 1) // u1 type_argument_index; } + + "; } // type_path[" + i + "]"); // path[i] + } + } finally { + out_end("}"); + } + } + + private void decodeElementValue(DataInputStream in, PrintWriter out) throws IOException { + out_begin("{ // element_value"); + try { + char tg = (char) in.readByte(); + AnnotElemType tag = annotElemType(tg); + if (tag != AE_UNKNOWN) { + out_println("'" + tg + "';"); + } + switch (tag) { + case AE_BYTE: + case AE_CHAR: + case AE_DOUBLE: + case AE_FLOAT: + case AE_INT: + case AE_LONG: + case AE_SHORT: + case AE_BOOLEAN: + case AE_STRING: + decodeCPXAttr(in, 2, "const_value_index", out); + break; + case AE_ENUM: + out_begin("{ // enum_const_value"); + decodeCPXAttr(in, 2, "type_name_index", out); + decodeCPXAttr(in, 2, "const_name_index", out); + out_end("} // enum_const_value"); + break; + case AE_CLASS: + decodeCPXAttr(in, 2, "class_info_index", out); + break; + case AE_ANNOTATION: + decodeAnnotation(in, out); + break; + case AE_ARRAY: + int ev_num = in.readUnsignedShort(); + startArrayCmt(ev_num, "array_value"); + try { + for (int i = 0; i < ev_num; i++) { + decodeElementValue(in, out); + if (i < ev_num - 1) { + out_println(";"); + } + } + } finally { + out_end("} // array_value"); + } + break; + case AE_UNKNOWN: + default: + String msg = "invalid element_value" + (isPrintableChar(tg) ? " tag type : " + tg : ""); + out_println(toHex(tg, 1) + "; // " + msg); + throw new ClassFormatError(msg); + } + } finally { + out_end("} // element_value"); + } + } + + public boolean isPrintableChar(char c) { + Character.UnicodeBlock block = Character.UnicodeBlock.of(c); + return (!Character.isISOControl(c)) && + c != KeyEvent.CHAR_UNDEFINED && + block != null && + block != Character.UnicodeBlock.SPECIALS; + } + + private void decodeAnnotation(DataInputStream in, PrintWriter out) throws IOException { + out_begin("{ // annotation"); + try { + decodeCPXAttr(in, 2, "field descriptor", out); + int evp_num = in.readUnsignedShort(); + decodeElementValuePairs(evp_num, in, out); + } finally { + out_end("} // annotation"); + } + } + + private void decodeElementValuePairs(int count, DataInputStream in, PrintWriter out) throws IOException { + startArrayCmt(count, "element_value_pairs"); + try { + for (int i = 0; i < count; i++) { + out_begin("{ // element value pair"); + try { + decodeCPXAttr(in, 2, "name of the annotation type element", out); + decodeElementValue(in, out); + } finally { + out_end("} // element value pair"); + if (i < count - 1) { + out_println(";"); + } + } + } + } finally { + out_end("} // element_value_pairs"); + } + } + + /** + * component_info { JEP 359 Record(Preview): class file 58.65535 + * u2 name_index; + * u2 descriptor_index; + * u2 attributes_count; + * attribute_info attributes[attributes_count]; + * } + * + * or + * field_info { + * u2 access_flags; + * u2 name_index; + * u2 descriptor_index; + * u2 attributes_count; + * attribute_info attributes[attributes_count]; + * } + * or + * method_info { + * u2 access_flags; + * u2 name_index; + * u2 descriptor_index; + * u2 attributes_count; + * attribute_info attributes[attributes_count]; + * } + * + */ + private void decodeInfo(DataInputStream in, PrintWriter out, String elementName, boolean hasAccessFlag) throws IOException { + out_begin("{ // " + elementName + (printDetails ? getStringPos() : "")); + try { + if(hasAccessFlag) { + // u2 access_flags; + out_println(toHex(in.readShort(), 2) + "; // access"); + } + // u2 name_index + printUtf8InfoIndex(in.readUnsignedShort(), "name_index"); + // u2 descriptor_index + printUtf8InfoIndex(in.readUnsignedShort(), "descriptor_index"); + // u2 attributes_count; + // attribute_info attributes[attributes_count] + decodeAttrs(in, out); + } finally { + out_end("}"); + } + } + + private void decodeTypeAnnotation(DataInputStream in, PrintWriter out) throws IOException { + out_begin("{ // type_annotation"); + try { + decodeTargetTypeAndRefInfo(in); + decodeCPXAttr(in, 2, "field descriptor", out); + int evp_num = in.readUnsignedShort(); + decodeElementValuePairs(evp_num, in, out); + } finally { + out_end("} // type_annotation"); + } + } + + private void decodeBootstrapMethod(DataInputStream in) throws IOException { + out_begin("{ // bootstrap_method"); + try { + out_println("#" + in.readUnsignedShort() + "; // bootstrap_method_ref"); + int bm_args_cnt = in.readUnsignedShort(); + startArrayCmt(bm_args_cnt, "bootstrap_arguments"); + try { + for (int i = 0; i < bm_args_cnt; i++) { + out_println("#" + in.readUnsignedShort() + ";" + getCommentPosCond()); + } + } finally { + out_end("} // bootstrap_arguments"); + } + } finally { + out_end("} // bootstrap_method"); + } + } + + private void decodeAttr(DataInputStream in, PrintWriter out) throws IOException { + // Read one attribute + String posComment = getStringPos(); + int name_cpx = in.readUnsignedShort(), btag, len; + + String AttrName = ""; + try { + btag = types[name_cpx]; + ConstType tag = tag(btag); + + if (tag == ConstType.CONSTANT_UTF8) { + AttrName = (String) cpool[name_cpx]; + } + } catch (ArrayIndexOutOfBoundsException ignored) { + } + AttrTag tg = attrtag(AttrName); + String endingComment = AttrName; + len = in.readInt(); + countedin.enter(len); + try { + if (printDetails) { + out_begin("Attr(#" + name_cpx + ", " + len + ") { // " + AttrName + posComment); + } else { + out_begin("Attr(#" + name_cpx + ") { // " + AttrName); + } + + switch (tg) { + case ATT_Code: + out_println(in.readUnsignedShort() + "; // max_stack"); + out_println(in.readUnsignedShort() + "; // max_locals"); + int code_len = in.readInt(); + out_begin("Bytes" + startArray(code_len) + "{"); + try { + printBytes(out, in, code_len); + } finally { + out_end("}"); + } + int trap_num = in.readUnsignedShort(); + startArrayCmt(trap_num, "Traps"); + try { + for (int i = 0; i < trap_num; i++) { + out_println(in.readUnsignedShort() + " " + + in.readUnsignedShort() + " " + + in.readUnsignedShort() + " " + + in.readUnsignedShort() + ";" + + getCommentPosCond()); + } + } finally { + out_end("} // end Traps"); + } + // Read the attributes + decodeAttrs(in, out); + break; + + case ATT_Exceptions: + int count = in.readUnsignedShort(); + startArrayCmt(count, AttrName); + try { + for (int i = 0; i < count; i++) { + out_println("#" + in.readUnsignedShort() + ";" + + getCommentPosCond()); + } + } finally { + out_end("}"); + } + break; + case ATT_LineNumberTable: + int ll_num = in.readUnsignedShort(); + startArrayCmt(ll_num, "line_number_table"); + try { + for (int i = 0; i < ll_num; i++) { + out_println(in.readUnsignedShort() + " " + + in.readUnsignedShort() + ";" + + getCommentPosCond()); + } + } finally { + out_end("}"); + } + break; + case ATT_LocalVariableTable: + case ATT_LocalVariableTypeTable: + int lvt_num = in.readUnsignedShort(); + startArrayCmt(lvt_num, AttrName); + try { + for (int i = 0; i < lvt_num; i++) { + out_println(in.readUnsignedShort() + " " + + in.readUnsignedShort() + " " + + in.readUnsignedShort() + " " + + in.readUnsignedShort() + " " + + in.readUnsignedShort() + ";" + + getCommentPosCond()); + } + } finally { + out_end("}"); + } + break; + case ATT_InnerClasses: + int ic_num = in.readUnsignedShort(); + startArrayCmt(ic_num, "classes"); + try { + for (int i = 0; i < ic_num; i++) { + out_println("#" + in.readUnsignedShort() + " #" + + in.readUnsignedShort() + " #" + + in.readUnsignedShort() + " " + + in.readUnsignedShort() + ";" + getCommentPosCond()); + } + } finally { + out_end("}"); + } + break; + case ATT_StackMap: + int e_num = in.readUnsignedShort(); + startArrayCmt(e_num, ""); + try { + for (int k = 0; k < e_num; k++) { + int start_pc = in.readUnsignedShort(); + out_print("" + start_pc + ", "); + printStackMap(in, 0); + out.print(", "); + printStackMap(in, 0); + out.println(";"); + } + } finally { + out_end("}"); + } + break; + case ATT_StackMapTable: + int et_num = in.readUnsignedShort(); + startArrayCmt(et_num, ""); + try { + for (int k = 0; k < et_num; k++) { + int frame_type = in.readUnsignedByte(); + StackMapFrameType ftype = stackMapFrameType(frame_type); + switch (ftype) { + case SAME_FRAME: + // type is same_frame; + out_print("" + frame_type + "b"); + out.println("; // same_frame"); + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME: + // type is same_locals_1_stack_item_frame + out_print("" + frame_type + "b, "); + // read additional single stack element + printStackMap(in, 1); + out.println("; // same_locals_1_stack_item_frame"); + break; + case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME: + // type is same_locals_1_stack_item_frame_extended + int noffset = in.readUnsignedShort(); + out_print("" + frame_type + "b, " + noffset + ", "); + // read additional single stack element + printStackMap(in, 1); + out.println("; // same_locals_1_stack_item_frame_extended"); + break; + case CHOP_1_FRAME: + case CHOP_2_FRAME: + case CHOP_3_FRAME: + // type is chop_frame + int coffset = in.readUnsignedShort(); + out_print("" + frame_type + "b, " + coffset); + out.println("; // chop_frame " + (251 - frame_type)); + break; + case SAME_FRAME_EX: + // type is same_frame_extended; + int xoffset = in.readUnsignedShort(); + out_print("" + frame_type + "b, " + xoffset); + out.println("; // same_frame_extended"); + break; + case APPEND_FRAME: + // type is append_frame + int aoffset = in.readUnsignedShort(); + out_print("" + frame_type + "b, " + aoffset + ", "); + // read additional locals + printStackMap(in, frame_type - 251); + out.println("; // append_frame " + (frame_type - 251)); + break; + case FULL_FRAME: + // type is full_frame + int foffset = in.readUnsignedShort(); + out_print("" + frame_type + "b, " + foffset + ", "); + printStackMap(in, 0); + out.print(", "); + printStackMap(in, 0); + out.println("; // full_frame"); + break; + } + } + } finally { + out_end("}"); + } + break; + case ATT_EnclosingMethod: + decodeCPXAttrM(in, len, AttrName, out, 2); + break; + case ATT_AnnotationDefault: + decodeElementValue(in, out); + break; + case ATT_RuntimeInvisibleAnnotations: + case ATT_RuntimeVisibleAnnotations: + int an_num = in.readUnsignedShort(); + startArrayCmt(an_num, "annotations"); + try { + for (int i = 0; i < an_num; i++) { + decodeAnnotation(in, out); + if (i < an_num - 1) { + out_println(";"); + } + } + } finally { + out_end("}"); + } + break; + // 4.7.20 The RuntimeVisibleTypeAnnotations Attribute + // 4.7.21 The RuntimeInvisibleTypeAnnotations Attribute + case ATT_RuntimeInvisibleTypeAnnotations: + case ATT_RuntimeVisibleTypeAnnotations: + int ant_num = in.readUnsignedShort(); + startArrayCmt(ant_num, "annotations"); + try { + for (int i = 0; i < ant_num; i++) { + decodeTypeAnnotation(in, out); + if (i < ant_num - 1) { + out_println(";"); + } + } + } finally { + out_end("}"); + } + break; + case ATT_RuntimeInvisibleParameterAnnotations: + case ATT_RuntimeVisibleParameterAnnotations: + int pm_num = in.readUnsignedByte(); + startArrayCmtB(pm_num, "parameters"); + try { + for (int k = 0; k < pm_num; k++) { + int anp_num = in.readUnsignedShort(); + startArrayCmt(anp_num, "annotations"); + try { + for (int i = 0; i < anp_num; i++) { + decodeAnnotation(in, out); + if (k < anp_num - 1) { + out_println(";"); + } + } + } finally { + out_end("}"); + } + if (k < pm_num - 1) { + out_println(";"); + } + } + } finally { + out_end("}"); + } + break; + case ATT_BootstrapMethods: + int bm_num = in.readUnsignedShort(); + startArrayCmt(bm_num, "bootstrap_methods"); + try { + for (int i = 0; i < bm_num; i++) { + decodeBootstrapMethod(in); + if (i < bm_num - 1) { + out_println(";"); + } + } + } finally { + out_end("}"); + } + break; + case ATT_Module: + decodeModule(in); + break; + case ATT_TargetPlatform: + decodeCPXAttrM(in, len, AttrName, out, 3); + break; + case ATT_ModulePackages: + int p_num = in.readUnsignedShort(); + startArrayCmt(p_num, null); + try { + decodeCPXAttrM(in, len - 2, AttrName, out, p_num); + } finally { + out_end("}"); + } + break; + // MethodParameters_attribute { + // u2 attribute_name_index; + // u4 attribute_length; + // u1 parameters_count; + // { u2 name_index; + // u2 access_flags; + // } parameters[parameters_count]; + // } + case ATT_MethodParameters: + int pcount = in.readUnsignedByte(); + startArrayCmtB(pcount, AttrName); + try { + for (int i = 0; i < pcount; i++) { + out_println("#" + in.readUnsignedShort() + " " + + toHex(in.readUnsignedShort(), 2) + ";" + + getCommentPosCond()); + } + } finally { + out_end("}"); + } + break; + // JEP 359 Record(Preview): class file 58.65535 + // Record_attribute { + // u2 attribute_name_index; + // u4 attribute_length; + // u2 components_count; + // component_info components[components_count]; + // } + case ATT_Record: + int ncomps = in.readUnsignedShort(); + startArrayCmt(ncomps, "components"); + try { + for (int i = 0; i < ncomps; i++) { + decodeInfo(in,out,"component",false); + if (i < ncomps - 1) { + out_println(";"); + } + } + } finally { + out_end("}"); + } + break; + case ATT_ConstantValue: + case ATT_Signature: + case ATT_SourceFile: + decodeCPXAttr(in, len, AttrName, out); + break; + // JEP 181 (Nest-based Access Control): class file 55.0 + // NestHost_attribute { + // u2 attribute_name_index; + // u4 attribute_length; + // u2 host_class_index; + // } + case ATT_NestHost: + decodeTypes(in, out, 1); + break; + // JEP 181 (Nest-based Access Control): class file 55.0 + // NestMembers_attribute { + // u2 attribute_name_index; + // u4 attribute_length; + // u2 number_of_classes; + // u2 classes[number_of_classes]; + // } + case ATT_NestMembers: + // JEP 360 (Sealed types): class file 59.65535 + // PermittedSubclasses_attribute { + // u2 attribute_name_index; + // u4 attribute_length; + // u2 number_of_classes; + // u2 classes[number_of_classes]; + // } + case ATT_PermittedSubclasses: + int nsubtypes = in.readUnsignedShort(); + startArrayCmt(nsubtypes, "classes"); + try { + decodeTypes(in, out, nsubtypes); + } finally { + out_end("}"); + } + break; + default: + printBytes(out, in, len); + if (AttrName == null) { + endingComment = "Attr(#" + name_cpx + ")"; + } + } + + } catch (EOFException e) { + out.println("// ======== unexpected end of attribute array"); + } finally { + int rest = countedin.available(); + if (rest > 0) { + out.println("// ======== attribute array started " + posComment + " has " + rest + " bytes more:"); + printBytes(out, in, rest); + } + out_end("} // end " + endingComment); + countedin.leave(); + } + } + + private void decodeModuleStatement(String statementName, DataInputStream in) throws IOException { + // u2 {exports|opens}_count + int count = in.readUnsignedShort(); + startArrayCmt(count, statementName); + try { + for (int i = 0; i < count; i++) { + // u2 {exports|opens}_index; u2 {exports|opens}_flags + int index = in.readUnsignedShort(); + int nFlags = in.readUnsignedShort(); + String sFlags = printDetails ? Module.Modifier.getStatementFlags(nFlags) : ""; + out_println("#" + index + " " + toHex(nFlags, 2) + (sFlags.isEmpty() ? "" : " // [ " + sFlags + " ]")); + int exports_to_count = in.readUnsignedShort(); + startArrayCmt(exports_to_count, null); + try { + for (int j = 0; j < exports_to_count; j++) { + out_println("#" + in.readUnsignedShort() + ";"); + } + } finally { + out_end("};"); + } + } + } finally { + out_end("} // " + statementName + "\n"); + } + } + + private void decodeModule(DataInputStream in) throws IOException { + //u2 module_name_index + int index = in.readUnsignedShort(); + entityName = (String) cpool[(Integer) cpool[index]]; + out_print("#" + index + "; // "); + if (printDetails) { + out.println(String.format("%-16s","name_index") + " : " + entityName); + } else { + out.println("name_index"); + } + + // u2 module_flags + int moduleFlags = in.readUnsignedShort(); + out_print(toHex(moduleFlags, 2) + "; // flags"); + if (printDetails) { + out_print(" " + Module.Modifier.getModuleFlags(moduleFlags)); + } + out.println(); + + //u2 module_version + int versionIndex = in.readUnsignedShort(); + out_println("#" + versionIndex + "; // version"); + + // u2 requires_count + int count = in.readUnsignedShort(); + startArrayCmt(count, "requires"); + try { + for (int i = 0; i < count; i++) { + // u2 requires_index; u2 requires_flags; u2 requires_version_index + index = in.readUnsignedShort(); + int nFlags = in.readUnsignedShort(); + versionIndex = in.readUnsignedShort(); + String sFlags = printDetails ? Module.Modifier.getStatementFlags(nFlags) : ""; + out_println("#" + index + " " + toHex(nFlags, 2) + " #" + versionIndex + ";" + (sFlags.isEmpty() ? "" : " // " + sFlags)); + } + } finally { + out_end("} // requires\n"); + } + + decodeModuleStatement("exports", in); + + decodeModuleStatement("opens", in); + // u2 uses_count + count = in.readUnsignedShort(); + startArrayCmt(count, "uses"); + try { + for (int i = 0; i < count; i++) { + // u2 uses_index + out_println("#" + in.readUnsignedShort() + ";"); + } + } finally { + out_end("} // uses\n"); + } + count = in.readUnsignedShort(); // u2 provides_count + startArrayCmt(count, "provides"); + try { + for (int i = 0; i < count; i++) { + // u2 provides_index + out_println("#" + in.readUnsignedShort()); + int provides_with_count = in.readUnsignedShort(); + // u2 provides_with_count + startArrayCmt(provides_with_count, null); + try { + for (int j = 0; j < provides_with_count; j++) { + // u2 provides_with_index; + out_println("#" + in.readUnsignedShort() + ";"); + } + } finally { + out_end("};"); + } + } + } finally { + out_end("} // provides\n"); + } + } + + private void decodeAttrs(DataInputStream in, PrintWriter out) throws IOException { + // Read the attributes + int attr_num = in.readUnsignedShort(); + startArrayCmt(attr_num, "Attributes"); + try { + for (int i = 0; i < attr_num; i++) { + decodeAttr(in, out); + if (i + 1 < attr_num) { + out_println(";"); + } + } + } finally { + out_end("} // Attributes"); + } + } + + private void decodeMembers(DataInputStream in, PrintWriter out, String groupName, String elementName) throws IOException { + int count = in.readUnsignedShort(); + traceln(groupName + "=" + count); + startArrayCmt(count, groupName); + try { + for (int i = 0; i < count; i++) { + decodeInfo(in,out,elementName,true); + if (i + 1 < count) { + out_println(";"); + } + } + } finally { + out_end("} // " + groupName); + out.println(); + } + } + + void decodeClass(String fileName) throws IOException { + // Read the header + try { + int magic = in.readInt(); + int min_version = in.readUnsignedShort(); + int version = in.readUnsignedShort(); + + // Read the constant pool + readCP(in); + short access = in.readShort(); // don't care about sign + int this_cpx = in.readUnsignedShort(); + + try { + entityName = (String) cpool[(Integer) cpool[this_cpx]]; + if (entityName.equals("module-info")) { + entityType = "module"; + entityName = ""; + } else { + entityType = "class"; + } + if (!entityName.isEmpty() && (JcodTokens.keyword_token_ident(entityName) != JcodTokens.Token.IDENT || JcodTokens.constValue(entityName) != -1)) { + // JCod can't parse a entityName matching a keyword or a constant value, + // then use the filename instead: + out_begin(String.format("file \"%s.class\" {", entityName)); + } else { + out_begin(format("%s %s {", entityType, entityName)); + } + } catch (Exception e) { + entityName = fileName; + out.println("// " + e.getMessage() + " while accessing entityName"); + out_begin(format("%s %s { // source file name", entityType, entityName)); + } + + out_print(toHex(magic, 4) + ";"); + if (magic != JAVA_MAGIC) { + out.print(" // wrong magic: 0x" + Integer.toString(JAVA_MAGIC, 16) + " expected"); + } + out.println(); + out_println(min_version + "; // minor version"); + out_println(version + "; // version"); + + // Print the constant pool + printCP(out); + out_println(toHex(access, 2) + "; // access" + + (printDetails ? " [" + (" " + Modifiers.accessString(access, CF_Context.CTX_CLASS).toUpperCase()).replaceAll(" (\\S)", " ACC_$1") + "]" : "")); + out_println("#" + this_cpx + ";// this_cpx"); + int super_cpx = in.readUnsignedShort(); + out_println("#" + super_cpx + ";// super_cpx"); + traceln(i18n.getString("jdec.trace.access_thisCpx_superCpx", access, this_cpx, super_cpx)); + out.println(); + + // Read the interfaces + int numinterfaces = in.readUnsignedShort(); + traceln(i18n.getString("jdec.trace.numinterfaces", numinterfaces)); + startArrayCmt(numinterfaces, "Interfaces"); + try { + decodeTypes(in, out, numinterfaces); + } finally { + out_end("} // Interfaces\n"); + } + // Read the fields + decodeMembers(in, out, "Fields", "field"); + + // Read the methods + decodeMembers(in, out, "Methods", "method"); + + // Read the attributes + decodeAttrs(in, out); + } catch (EOFException ignored) { + } catch (ClassFormatError err) { + String msg = err.getMessage(); + out.println("//------- ClassFormatError" + + (msg == null || msg.isEmpty() ? "" : ": " + msg)); + printRestOfBytes(); + } finally { + out_end(format("} // end %s %s", entityType, entityName)); + } + } // end decodeClass() + + private void decodeTypes(DataInputStream in, PrintWriter out, int count) throws IOException { + for (int i = 0; i < count; i++) { + int type_cpx = in.readUnsignedShort(); + traceln(i18n.getString("jdec.trace.type", i, type_cpx)); + out_print("#" + type_cpx + ";"); + if (printDetails) { + String name = (String) cpool[(int)cpool[type_cpx]]; + out.println(" // " + name + getStringPos()); + } else { + out.println(); + } + } + } + + /* ====================================================== */ + boolean DebugFlag = false; + + public void trace(String s) { + if (!DebugFlag) { + return; + } + System.out.print(s); + } + + public void traceln(String s) { + if (!DebugFlag) { + return; + } + System.out.println(s); + } +}// end class ClassData + diff --git a/src/org/openjdk/asmtools/jdec/Main.java b/src/org/openjdk/asmtools/jdec/Main.java new file mode 100644 index 0000000..f1dedd8 --- /dev/null +++ b/src/org/openjdk/asmtools/jdec/Main.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdec; + +import org.openjdk.asmtools.common.Tool; +import org.openjdk.asmtools.jdis.uEscWriter; +import org.openjdk.asmtools.util.I18NResourceBundle; +import org.openjdk.asmtools.util.ProductInfo; + +import java.io.DataInputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Main program of the Java DECoder :: class to jcod + */ +public class Main extends Tool { + + int printFlags = 0; + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + public Main(PrintWriter out, PrintWriter err, String programName) { + super(out, err, programName); + printCannotReadMsg = (fname) -> + error( i18n.getString("jdec.error.cannot_read", fname)); + } + + public Main(PrintStream out, String program) { + this(new PrintWriter(out), new PrintWriter(System.err), program); + } + + @Override + public void usage() { + println(i18n.getString("jdec.usage")); + println(i18n.getString("jdec.opt.g")); + println(i18n.getString("jdec.opt.version")); + } + + /** + * Run the decoder + */ + public synchronized boolean decode(String argv[]) { + long tm = System.currentTimeMillis(); + ArrayList vargs = new ArrayList<>(); + ArrayList vj = new ArrayList<>(); + boolean nowrite = false; + int addOptions = 0; + + // Parse arguments + int i = 0; + for (String arg : argv) { + // + if (arg.equals("-g")) { + printFlags = printFlags | 1; + vargs.add(arg); + } else if (arg.equals("-v")) { + DebugFlag = () -> true; + vargs.add(arg); + out.println("arg[" + i + "]=" + argv[i] + "/verbose"); + } else if (arg.equals("-version")) { + out.println(ProductInfo.FULL_VERSION); + } else if (arg.startsWith("-")) { + error(i18n.getString("jdec.error.invalid_flag", arg)); + usage(); + return false; + } else { + vargs.add(arg); + vj.add(arg); + } + i += 1; + } + + if (vj.isEmpty()) { + usage(); + return false; + } + + String[] names = new String[0]; + names = vj.toArray(names); + for (String inpname : names) { + try { + DataInputStream dataInputStream = getDataInputStream(inpname); + if( dataInputStream == null ) + return false; + ClassData cc = new ClassData(dataInputStream, printFlags, out); + cc.DebugFlag = DebugFlag.getAsBoolean(); + cc.decodeClass(inpname); + continue; + } catch (Error ee) { + if (DebugFlag.getAsBoolean()) + ee.printStackTrace(); + error(i18n.getString("jdec.error.fatal_error")); + } catch (Exception ee) { + if (DebugFlag.getAsBoolean()) + ee.printStackTrace(); + error(i18n.getString("jdec.error.fatal_exception")); + } + return false; + } + return true; + } + + /** + * Main program + */ + public static void main(String argv[]) { + Main decoder = new Main(new PrintWriter(new uEscWriter(System.out)), new PrintWriter(System.err), "jdec"); + System.exit(decoder.decode(argv) ? 0 : 1); + } +} diff --git a/src/org/openjdk/asmtools/jdec/NestedByteArrayInputStream.java b/src/org/openjdk/asmtools/jdec/NestedByteArrayInputStream.java new file mode 100644 index 0000000..c8e86d8 --- /dev/null +++ b/src/org/openjdk/asmtools/jdec/NestedByteArrayInputStream.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2009, 2019, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdec; + +import java.io.ByteArrayInputStream; +import java.util.Stack; + +/** + * this class provides functionality needed to read class files: + *

    + *
  • methods to read unsigned integers of various length + *
  • counts bytes read so far + *
+ */ +public class NestedByteArrayInputStream extends ByteArrayInputStream { + + NestedByteArrayInputStream(byte buf[]) { + super(buf); + } + + NestedByteArrayInputStream(byte buf[], int offset, int length) { + super(buf, offset, length); + } + + public int getPos() { + return pos; + } + Stack savedStates = new Stack(); + + public void enter(int range) { + savedStates.push(count); + if (pos + range < count) { + count = pos + range; + } + } + + public void leave() { + pos = count; + count = ((Integer) savedStates.pop()).intValue(); + } +} // end class NestedByteArrayInputStream + diff --git a/src/org/openjdk/asmtools/jdec/i18n.properties b/src/org/openjdk/asmtools/jdec/i18n.properties new file mode 100644 index 0000000..a27299c --- /dev/null +++ b/src/org/openjdk/asmtools/jdec/i18n.properties @@ -0,0 +1,41 @@ +# Copyright (c) 2014, 2020, Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +jdec.error.invalid_flag=invalid flag: " {0} +jdec.error.fatal_error=fatal error +jdec.error.fatal_exception=fatal exception +jdec.error.cannot_read=cannot read {0} +jdec.usage=\ +Usage: java -jar asmtools.jar jdec [options] FILE.class... > FILE.jcod\n\ +where possible options include: + +jdec.opt.g=\ +\ -g: detailed output format +jdec.opt.version=\ +\ -version: print version number and date + + +jdec.trace.CP_len=CP len= {0} +jdec.trace.CP_entry=CP entry # {0} tag= {1} +jdec.trace.access_thisCpx_superCpx=access={0} this_cpx={1} super_cpx={2} +jdec.trace.numinterfaces=numinterfaces={0} +jdec.trace.type=\ +\ type_cpx[{0}]={1} diff --git a/src/org/openjdk/asmtools/jdis/AnnotationData.java b/src/org/openjdk/asmtools/jdis/AnnotationData.java new file mode 100644 index 0000000..d21a2b5 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/AnnotationData.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +import static java.lang.String.format; + +/** + * + */ +public class AnnotationData { + /*-------------------------------------------------------- */ + /* AnnotData Fields */ + + protected String visAnnotToken = "@+"; + protected String invAnnotToken = "@-"; + protected String dataName = "AnnotationData"; + private boolean invisible = false; + private int type_cpx = 0; //an index into the constant pool indicating the annotation type for this annotation. + private ArrayList array = new ArrayList<>(); + private ClassData cls; + /*-------------------------------------------------------- */ + + public AnnotationData(boolean invisible, ClassData cls) { + this.cls = cls; + this.invisible = invisible; + } + + public void read(DataInputStream in) throws IOException { + type_cpx = in.readShort(); + int elemValueLength = in.readShort(); + TraceUtils.traceln(3, format(" %s: name[%d]=%s", dataName, type_cpx, cls.pool.getString(type_cpx)), + format(" %s: %s num_elems: %d", dataName, cls.pool.getString(type_cpx), elemValueLength)); + for (int evc = 0; evc < elemValueLength; evc++) { + AnnotationElement elem = new AnnotationElement(cls); + TraceUtils.traceln(3, format(" %s: %s reading [%d]", dataName, cls.pool.getString(type_cpx), evc)); + elem.read(in, invisible); + array.add(elem); + } + } + + public void print(PrintWriter out, String tab) { + printHeader(out, tab); + printBody(out, ""); + } + + protected void printHeader(PrintWriter out, String tab) { + //Print annotation Header, which consists of the + // Annotation Token ('@'), visibility ('+', '-'), + // and the annotation name (type index, CPX). + + // Mark whether it is invisible or not. + if (invisible) { + out.print(tab + invAnnotToken); + } else { + out.print(tab + visAnnotToken); + } + String annoName = cls.pool.getString(type_cpx); + + // converts class type to java class name + if (annoName.startsWith("L") && annoName.endsWith(";")) { + annoName = annoName.substring(1, annoName.length() - 1); + } + + out.print(annoName); + } + + protected void printBody(PrintWriter out, String tab) { + // For a standard annotation, print out brackets, + // and list the name/value pairs. + out.print(" { "); + int i = 0; + for (AnnotationElement elem : array) { + elem.print(out, tab); + if (i++ < array.size() - 1) { + out.print(", "); + } + } + out.print(" }"); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + String annoName = cls.pool.getString(type_cpx); + + // converts class type to java class name + if (annoName.startsWith("L") && annoName.endsWith(";")) { + annoName = annoName.substring(1, annoName.length() - 1); + } + + //Print annotation + // Mark whether it is invisible or not. + if (invisible) { + sb.append(invAnnotToken); + } else { + sb.append(visAnnotToken); + } + + sb.append(annoName); + sb.append(" { "); + + int i = 0; + for (AnnotationElement elem : array) { + sb.append(elem.toString()); + + if (i++ < array.size() - 1) { + sb.append(", "); + } + } + + _toString(sb); + + sb.append("}"); + return sb.toString(); + } + + protected void _toString(StringBuilder sb) { + // sub-classes override this + } +} + diff --git a/src/org/openjdk/asmtools/jdis/AnnotationElement.java b/src/org/openjdk/asmtools/jdis/AnnotationElement.java new file mode 100644 index 0000000..c47492b --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/AnnotationElement.java @@ -0,0 +1,390 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import static java.lang.String.format; +import static org.openjdk.asmtools.jasm.Tables.*; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Base class of all AnnotationElement entries + */ +public class AnnotationElement { + + /** + * + * CPX_AnnotElem + * + * base class for an annotation value. + * + */ + public static class AnnotValue { + + /** + * tag the descriptor for the constant + */ + public AnnotElemType tag; + + // internal references + protected ClassData cls; + + public AnnotValue(AnnotElemType tagval, ClassData cls) { + tag = tagval; + this.cls = cls; + } + + public String stringVal() { + return ""; + } + + public void print(PrintWriter out, String tab) { + out.print(tag.val() + " "); + } + + @Override + public String toString() { + return ""; + } + } + + /** + * + * CPX_AnnotElem + * + * Annotation value which is described by a single CPX entry (ie. String, byte, char, + * int, short, boolean, float, long, double, class reference). + * + */ + public static class CPX_AnnotValue extends AnnotValue { + + /** + * tag the descriptor for the constant + */ + public int cpx; + + public CPX_AnnotValue(AnnotElemType tag, ClassData cls, int cpx) { + super(tag, cls); + this.cpx = cpx; + } + + @Override + public String stringVal() { + StringBuilder sb = new StringBuilder(); + switch (tag) { + case AE_STRING: // String + sb.append('"' + cls.pool.getString(cpx) + '"'); + break; + case AE_BYTE: // Byte + sb.append("byte " + cls.pool.getConst(cpx).stringVal()); + break; + case AE_CHAR: // Char + sb.append("char " + cls.pool.getConst(cpx).stringVal()); + break; + case AE_INT: // Int (no need to add keyword) + sb.append(cls.pool.getConst(cpx).stringVal()); + break; + case AE_SHORT: // Short + sb.append("short " + cls.pool.getConst(cpx).stringVal()); + break; + case AE_BOOLEAN: // Boolean + ConstantPool.CP_Int cns = (ConstantPool.CP_Int) cls.pool.getConst(cpx); + sb.append("boolean " + (cns.value == 0 ? "false" : "true")); + break; + case AE_FLOAT: // Float + sb.append(cls.pool.getConst(cpx).stringVal()); // + "f"); + break; + case AE_DOUBLE: // Double + sb.append(cls.pool.getConst(cpx).stringVal()); // + "d"); + break; + case AE_LONG: // Long + sb.append(cls.pool.getConst(cpx).stringVal()); // + "l"); + break; + case AE_CLASS: // Class + sb.append("class " + cls.pool.decodeClassDescriptor(cpx)); + break; + default: + break; + } + return sb.toString(); + } + + @Override + public void print(PrintWriter out, String tab) { + out.print(tab + stringVal()); + } + + @Override + public String toString() { + return ""; + } + } + + /** + * + * CPX_AnnotElem + * + * AnnotElements that contain 2 cpx indices (ie. enums). + * + */ + public static class CPX2_AnnotValue extends AnnotValue { + + /** + * tag the descriptor for the constant + */ + public int cpx1; + public int cpx2; + + public CPX2_AnnotValue(AnnotElemType tag, ClassData cls, int cpx1, int cpx2) { + super(tag, cls); + this.cpx1 = cpx1; + this.cpx2 = cpx2; + } + + @Override + public String stringVal() { + StringBuilder sb = new StringBuilder(); + switch (tag) { + + case AE_ENUM: // Enum + // print the enum type and constant name + sb.append("enum " + cls.pool.decodeClassDescriptor(cpx1) + + " " + cls.pool.getName(cpx2)); + break; + default: + break; + } + return sb.toString(); + } + + @Override + public void print(PrintWriter out, String tab) { + out.print(tab + stringVal()); + } + + @Override + public String toString() { + return ""; + } + } + + /** + * + * Array_AnnotElem + * + * Annotation value that is an array of annotation elements. + * + */ + public static class Array_AnnotValue extends AnnotValue { + + /** + * tag the descriptor for the constant + */ + public ArrayList array = new ArrayList<>(); + + public Array_AnnotValue(AnnotElemType tagval, ClassData cls) { + super(tagval, cls); + } + + @Override + public String stringVal() { + StringBuilder sb = new StringBuilder(); + sb.append(super.stringVal() + " = "); + sb.append("{"); + int i = 0; + int cnt = array.size(); + for (AnnotValue arrayelem : array) { + sb.append(arrayelem.toString()); + if (i < cnt - 1) { + sb.append(","); + } + } + sb.append("}"); + return sb.toString(); + } + + public void add(AnnotValue elem) { + array.add(elem); + } + + @Override + public void print(PrintWriter out, String tab) { + out.println("{"); + int i = 0; + int cnt = array.size(); + for (AnnotValue arrayelem : array) { + arrayelem.print(out, tab + " "); + if (i < cnt - 1) { + out.println(","); + } + i += 1; + } + out.println("}"); + } + + @Override + public String toString() { + return ""; + } + } + + /** + * + * Annot_AnnotValue + * + * Annotation value that is a reference to an annotation. + * + */ + public static class Annot_AnnotValue extends AnnotValue { + + /** + * tag the descriptor for the constant + */ + AnnotationData annot; + + public Annot_AnnotValue(AnnotElemType tagval, ClassData cls, AnnotationData annot) { + super(tagval, cls); + this.annot = annot; + } + + @Override + public String stringVal() { + return annot.toString(); + } + + @Override + public void print(PrintWriter out, String tab) { +// out.print(tag + "\t"); + annot.print(out, tab); + } + + @Override + public String toString() { + return ""; + } + } + + /*========================================================*/ + /* Factory Method */ + /** + * + * read + * + * Static factory - creates Annotation Elements. + * + */ + public static AnnotValue readValue(DataInputStream in, ClassData cls, boolean invisible) throws IOException { + AnnotValue val = null; + char tg = (char) in.readByte(); + AnnotElemType tag = annotElemType(tg); + + switch (tag) { + case AE_STRING: // String + case AE_BYTE: // Byte + case AE_CHAR: // Char + case AE_INT: // Int (no need to add keyword) + case AE_SHORT: // Short + case AE_BOOLEAN: // Boolean + case AE_FLOAT: // Float + case AE_DOUBLE: // Double + case AE_LONG: // Long + case AE_CLASS: // Class + // CPX based Annotation + int CPX = in.readShort(); + val = new CPX_AnnotValue(tag, cls, CPX); + break; + case AE_ENUM: // Enum + // CPX2 based Annotation + int CPX1 = in.readShort(); + int CPX2 = in.readShort(); + val = new CPX2_AnnotValue(tag, cls, CPX1, CPX2); + break; + case AE_ANNOTATION: // Annotation + AnnotationData ad = new AnnotationData(invisible, cls); + ad.read(in); + val = new Annot_AnnotValue(tag, cls, ad); + break; + case AE_ARRAY: // Array + Array_AnnotValue aelem = new Array_AnnotValue(tag, cls); + val = aelem; + int cnt = in.readShort(); + for (int i = 0; i < cnt; i++) { + aelem.add(readValue(in, cls, invisible)); + } + break; + default: + throw new IOException("Unknown tag in annotation '" + tg + "' [" + Integer.toHexString(tg) + "]"); + } + + return val; + } + + /*========================================================*/ + + /*-------------------------------------------------------- */ + /* AnnotElem Fields */ + /** + * constant pool index for the name of the Annotation Element + */ + public int name_cpx; + + public AnnotValue value = null; + + // internal references + protected ClassData cls; + /*-------------------------------------------------------- */ + + public AnnotationElement(ClassData cls) { + this.cls = cls; + } + + /** + * + * read + * + * read and resolve the method data called from ClassData. precondition: NumFields has + * already been read from the stream. + * + */ + public void read(DataInputStream in, boolean invisible) throws IOException { + name_cpx = in.readShort(); + value = readValue(in, cls, invisible); + TraceUtils.traceln(format(" AnnotElem: name[%d]=%s value=%s", name_cpx, cls.pool.getString(name_cpx), value.toString())); + } + + public String stringVal() { + return cls.pool.getName(name_cpx); + } + + public void print(PrintWriter out, String tab) { + out.print(stringVal() + " = "); + value.print(out, ""); + } + + @Override + public String toString() { + return ""; + } +} diff --git a/src/org/openjdk/asmtools/jdis/AttrData.java b/src/org/openjdk/asmtools/jdis/AttrData.java new file mode 100644 index 0000000..b94ed01 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/AttrData.java @@ -0,0 +1,65 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.Tables; + +import java.io.DataInputStream; +import java.io.IOException; + +import static java.lang.String.format; + +/** + * + */ +public class AttrData { + + int name_cpx; + byte data[]; + ClassData cls; + public AttrData(ClassData cls) { + this.cls = cls; + } + + /** + * attributeTag + *

+ * returns either -1 (not found), or the hashed integer tag tag. + */ + public static int attributeTag(String tagname) { + int intgr = Tables.attrtagValue(tagname); + + if (intgr == 0) { + return -1; + } + + return intgr; + } + + public void read(int name_cpx, int attrlen, DataInputStream in) throws IOException { + this.name_cpx = name_cpx; + data = new byte[attrlen]; + TraceUtils.traceln(format("AttrData:#%d len=%d", name_cpx, attrlen)); + in.readFully(data); + } +} diff --git a/src/org/openjdk/asmtools/jdis/BootstrapMethodData.java b/src/org/openjdk/asmtools/jdis/BootstrapMethodData.java new file mode 100644 index 0000000..67d2d61 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/BootstrapMethodData.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.JasmTokens; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * + */ +public class BootstrapMethodData extends Indenter { + + int bsm_index; + ArrayList bsm_args_indexes; + + // internal references + private Options options = Options.OptionObject(); + private ClassData cls; + private PrintWriter out; + + public BootstrapMethodData(ClassData cls) { + this.cls = cls; + out = cls.out; + } + + + /*========================================================*/ + /* Read Methods */ + + /** + * + * read + * + * read and resolve the bootstrap method data called from ClassData. precondition: + * NumFields has already been read from the stream. + * + */ + public void read(DataInputStream in) throws IOException { + // read the Methods CP indexes + bsm_index = in.readUnsignedShort(); + int arg_num = in.readUnsignedShort(); + bsm_args_indexes = new ArrayList<>(arg_num); + for (int i = 0; i < arg_num; i++) { + bsm_args_indexes.add(in.readUnsignedShort()); + } + } + + + /*========================================================*/ + /* Print Methods */ + public void print() throws IOException { + out.print(getIndentString() + JasmTokens.Token.BOOTSTRAPMETHOD.parseKey() + " #" + bsm_index); + for (int i = 0; i < bsm_args_indexes.size(); i++) { + out.print(" #" + bsm_args_indexes.get(i)); + } + out.println(";"); + } +} diff --git a/src/org/openjdk/asmtools/jdis/ClassArrayData.java b/src/org/openjdk/asmtools/jdis/ClassArrayData.java new file mode 100644 index 0000000..5b73fff --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/ClassArrayData.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * Base class of the "classes[]" data of attributes + *

+ * JEP 181 (Nest-based Access Control): class file 55.0 + * NestMembers_attribute { + * u2 attribute_name_index; + * u4 attribute_length; + * u2 number_of_classes; + * u2 classes[number_of_classes]; + * } + *

+ * JEP 360 (Sealed types): class file 59.65535 + * PermittedSubclasses_attribute { + * u2 attribute_name_index; + * u4 attribute_length; + * u2 number_of_classes; + * u2 classes[number_of_classes]; + * } + *

+ */ +public class ClassArrayData extends Indenter { + String name; + ClassData cls; + int[] classes; + private Options options = Options.OptionObject(); + + protected ClassArrayData(ClassData cls, String attrName) { + this.cls = cls; + this.name = attrName; + } + + public ClassArrayData read(DataInputStream in, int attribute_length) throws IOException, ClassFormatError { + int number_of_classes = in.readUnsignedShort(); + if (attribute_length != 2 + number_of_classes * 2) { + throw new ClassFormatError(name + "_attribute: Invalid attribute length"); + } + classes = new int[number_of_classes]; + for (int i = 0; i < number_of_classes; i++) { + classes[i] = in.readUnsignedShort(); + } + return this; + } + + public void print() { + String indexes = ""; + String names = ""; + boolean pr_cpx = options.contains(Options.PR.CPX); + cls.out.print(getIndentString() + name + " "); + for (int i = 0; i < classes.length; i++) { + if (pr_cpx) { + indexes += (indexes.isEmpty() ? "" : ", ") + "#" + classes[i]; + } + names += (names.isEmpty() ? "" : ", ") + cls.pool.StringValue(classes[i]); + } + if (pr_cpx) { + cls.out.print(indexes + "; // "); + } + cls.out.print(names); + if (pr_cpx) { + cls.out.println(); + } else { + cls.out.println(";"); + } + } + +} diff --git a/src/org/openjdk/asmtools/jdis/ClassData.java b/src/org/openjdk/asmtools/jdis/ClassData.java new file mode 100644 index 0000000..52e2fa2 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/ClassData.java @@ -0,0 +1,529 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.asmutils.HexUtils; +import org.openjdk.asmtools.common.Tool; +import org.openjdk.asmtools.jasm.Modifiers; + +import java.io.*; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.stream.Collectors; + +import static java.lang.String.format; +import static org.openjdk.asmtools.jasm.RuntimeConstants.*; +import static org.openjdk.asmtools.jasm.Tables.*; + +/** + * Central class data for of the Java Disassembler + */ +public class ClassData extends MemberData { + + // Owner of this ClassData + protected Tool tool; + + // ----------------------------- + // Header Info + // ----------------------------- + // Version info + protected int minor_version, major_version; + + // Constant Pool index to this class + protected int this_cpx; + + // Constant Pool index to this classes parent (super) + protected int super_cpx; + + // Constant Pool index to a file reference to the Java source + protected int source_cpx = 0; + + // ----------------------------- + // The Constant Pool + // ----------------------------- + protected ConstantPool pool; + + // ----------------------------- + // Interfaces,Fields,Methods && Attributes + // ----------------------------- + // The interfaces this class implements + protected int[] interfaces; + + // The fields of this class + protected ArrayList fields; + + // The methods of this class + protected ArrayList methods; + + // The record attribute of this class (since class file 58.65535) + protected RecordData record; + + // The inner-classes of this class + protected ArrayList innerClasses; + + // The bootstrapmethods this class implements + protected ArrayList bootstrapMethods; + + //The module this class file presents + protected ModuleData moduleData; + + // The NestHost of this class (since class file: 55.0) + protected NestHostData nestHost; + + // The NestMembers of this class (since class file: 55.0) + protected NestMembersData nestMembers; + + // The PermittedSubclasses of this class (JEP 360 (Sealed types): class file 59.65535) + protected PermittedSubclassesData permittedSubclassesData; + + // other parsing fields + protected PrintWriter out; + protected String pkgPrefix = ""; + // source file data + private TextLines sourceLines = null; + private Path classFile = null; + + public ClassData(PrintWriter out, Tool tool) { + this.out = out; + this.tool = tool; + memberType = "ClassData"; + TraceUtils.traceln("printOptions=" + options.toString()); + pool = new ConstantPool(this); + init(this); + } + + public void read(File in) throws IOException { + try ( DataInputStream dis = new DataInputStream(new FileInputStream(in))){ + read(dis); + } + classFile = in.toPath(); + } + + public void read(String in) throws IOException { + try ( DataInputStream dis = new DataInputStream(new FileInputStream(in))){ + read(dis); + } + classFile = Paths.get(in); + } + + /** + * Read and resolve the field data + */ + protected void readFields(DataInputStream in) throws IOException { + int nfields = in.readUnsignedShort(); + TraceUtils.traceln("nfields=" + nfields); + fields = new ArrayList<>(nfields); + for (int k = 0; k < nfields; k++) { + FieldData field = new FieldData(this); + TraceUtils.traceln(" FieldData: #" + k); + field.read(in); + fields.add(field); + } + } + + /** + * Read and resolve the method data + */ + protected void readMethods(DataInputStream in) throws IOException { + int nmethods = in.readUnsignedShort(); + TraceUtils.traceln("nmethods=" + nmethods); + methods = new ArrayList<>(nmethods); + for (int k = 0; k < nmethods; k++) { + MethodData method = new MethodData(this); + TraceUtils.traceln(" MethodData: #" + k); + method.read(in); + methods.add(method); + } + } + + /** + * Read and resolve the interface data + */ + protected void readInterfaces(DataInputStream in) throws IOException { + // Read the interface names + int numinterfaces = in.readUnsignedShort(); + TraceUtils.traceln("numinterfaces=" + numinterfaces); + interfaces = new int[numinterfaces]; + for (int i = 0; i < numinterfaces; i++) { + int intrf_cpx = in.readShort(); + TraceUtils.traceln(" intrf_cpx[" + i + "]=" + intrf_cpx); + interfaces[i] = intrf_cpx; + } + } + + /** + * Read and resolve the attribute data + */ + @Override + protected boolean handleAttributes(DataInputStream in, AttrTag attrtag, int attrlen) throws IOException { + // Read the Attributes + boolean handled = true; + switch (attrtag) { + case ATT_SourceFile: + // Read SourceFile Attr + if (attrlen != 2) { + throw new ClassFormatError("ATT_SourceFile: Invalid attribute length"); + } + source_cpx = in.readUnsignedShort(); + break; + case ATT_InnerClasses: + // Read InnerClasses Attr + int num1 = in.readUnsignedShort(); + if (2 + num1 * 8 != attrlen) { + throw new ClassFormatError("ATT_InnerClasses: Invalid attribute length"); + } + innerClasses = new ArrayList<>(num1); + for (int j = 0; j < num1; j++) { + InnerClassData innerClass = new InnerClassData(this); + innerClass.read(in); + innerClasses.add(innerClass); + } + break; + case ATT_BootstrapMethods: + // Read BootstrapMethods Attr + int num2 = in.readUnsignedShort(); + bootstrapMethods = new ArrayList<>(num2); + for (int j = 0; j < num2; j++) { + BootstrapMethodData bsmData = new BootstrapMethodData(this); + bsmData.read(in); + bootstrapMethods.add(bsmData); + } + break; + case ATT_Module: + // Read Module Attribute + moduleData = new ModuleData(this); + moduleData.read(in); + break; + case ATT_NestHost: + // Read NestHost Attribute (since class file: 55.0) + nestHost = new NestHostData(this).read(in, attrlen); + break; + case ATT_NestMembers: + // Read NestMembers Attribute (since class file: 55.0) + nestMembers = new NestMembersData(this).read(in, attrlen); + break; + case ATT_Record: + record = new RecordData(this).read(in); + break; + case ATT_PermittedSubclasses: + // Read PermittedSubclasses Attribute (JEP 360 (Sealed types): class file 59.65535) + permittedSubclassesData = new PermittedSubclassesData(this).read(in, attrlen); + break; + default: + handled = false; + break; + } + return handled; + } + + /** + * Read and resolve the class data + */ + private void read(DataInputStream in) throws IOException { + // Read the header + int magic = in.readInt(); + if (magic != JAVA_MAGIC) { + throw new ClassFormatError("wrong magic: " + HexUtils.toHex(magic) + ", expected " + HexUtils.toHex(JAVA_MAGIC)); + } + minor_version = in.readUnsignedShort(); + major_version = in.readUnsignedShort(); + + // Read the constant pool + pool.read(in); + access = in.readUnsignedShort(); // & MM_CLASS; // Q + this_cpx = in.readUnsignedShort(); + super_cpx = in.readUnsignedShort(); + TraceUtils.traceln("access=" + access + " " + Modifiers.accessString(access, CF_Context.CTX_INNERCLASS) + + " this_cpx=" + this_cpx + + " super_cpx=" + super_cpx); + + // Read the interfaces + readInterfaces(in); + + // Read the fields + readFields(in); + + // Read the methods + readMethods(in); + + // Read the attributes + readAttributes(in); + // + TraceUtils.traceln("", "<< Reading is done >>", ""); + } + + /** + * Read and resolve the attribute data + */ + public String getSrcLine(int lnum) { + if (sourceLines == null) { + return null; // impossible call + } + String line; + try { + line = sourceLines.getLine(lnum); + } catch (ArrayIndexOutOfBoundsException e) { + line = "Line number " + lnum + " is out of bounds"; + } + return line; + } + + private void printAnnotations(List annotations) { + if (annotations != null) { + for (T ad : annotations) { + ad.print(out, ""); + out.println(); + } + } + } + + @Override + public void print() throws IOException { + int k, l; + String className = ""; + String sourceName = null; + if( isModuleUnit() ) { + // Print the Annotations + printAnnotations(visibleAnnotations); + printAnnotations(invisibleAnnotations); + } else { + className = pool.getClassName(this_cpx); + int pkgPrefixLen = className.lastIndexOf("/") + 1; + // Write the header + // package-info compilation unit + if (className.endsWith("package-info")) { + // Print the Annotations + printAnnotations(visibleAnnotations); + printAnnotations(invisibleAnnotations); + printAnnotations(visibleTypeAnnotations); + printAnnotations(invisibleTypeAnnotations); + if (pkgPrefixLen != 0) { + pkgPrefix = className.substring(0, pkgPrefixLen); + out.print("package " + pkgPrefix.substring(0, pkgPrefixLen - 1) + " "); + out.print("version " + major_version + ":" + minor_version + ";"); + } + out.println(); + return; + } + if (pkgPrefixLen != 0) { + pkgPrefix = className.substring(0, pkgPrefixLen); + out.println("package " + pkgPrefix.substring(0, pkgPrefixLen - 1) + ";"); + className = pool.getShortClassName(this_cpx, pkgPrefix); + } + out.println(); + // Print the Annotations + printAnnotations(visibleAnnotations); + printAnnotations(invisibleAnnotations); + printAnnotations(visibleTypeAnnotations); + printAnnotations(invisibleTypeAnnotations); + if ((access & ACC_SUPER) != 0) { + out.print("super "); + access = access & ~ACC_SUPER; + } + } +// see if we are going to print: abstract interface class +// then replace it with just: interface +printHeader: + { +printSugar: + { + if ((access & ACC_ABSTRACT) == 0) { + break printSugar; + } + if ((access & ACC_INTERFACE) == 0) { + break printSugar; + } + if (options.contains(Options.PR.CPX)) { + break printSugar; + } + if (this_cpx == 0) { + break printSugar; + } + + // make sure the this_class is a valid class ref + ConstantPool.Constant this_const = pool.getConst(this_cpx); + if (this_const == null || this_const.tag != ConstantPool.TAG.CONSTANT_CLASS) { + break printSugar; + } + + // all conditions met, print syntactic sugar: + out.print(Modifiers.accessString(access & ~ACC_ABSTRACT, CF_Context.CTX_CLASS)); + if (isSynthetic) { + out.print("synthetic "); + } + if (isDeprecated) { + out.print("deprecated "); + } + out.print(" " + pool.getShortClassName(this_cpx, pkgPrefix)); + break printHeader; + } + + if(isModuleUnit()) { + out.print(moduleData.getModuleHeader()); + } else { + // not all conditions met, print header in ordinary way: + out.print(Modifiers.accessString(access, CF_Context.CTX_CLASS)); + if (isSynthetic) { + out.print("synthetic "); + } + if (isDeprecated) { + out.print("deprecated "); + } + if (options.contains(Options.PR.CPX)) { + out.print("\t#" + this_cpx + " //"); + } + pool.PrintConstant(out, this_cpx); + } + } + out.println(); + if(!isModuleUnit()) { + if (!pool.getClassName(super_cpx).equals("java/lang/Object")) { + out.print("\textends "); + pool.printlnClassId(out, super_cpx); + out.println(); + } + } + l = interfaces.length; + + if (l > 0) { + for (k = 0; k < l; k++) { + if (k == 0) { + out.print("\timplements "); + } else { + out.print("\t\t "); + } + boolean printComma = (l > 1 && k < (l - 1)); + pool.printlnClassId(out, interfaces[k], printComma); + out.println(); + } + } + out.println("\tversion " + major_version + ":" + minor_version); + out.println("{"); + + if ((options.contains(Options.PR.SRC)) && (source_cpx != 0)) { + sourceName = pool.getString(source_cpx); + if (sourceName != null) { + sourceLines = new TextLines(classFile.getParent(), sourceName); + } + } + + // Print the constant pool + if (options.contains(Options.PR.CP)) { + pool.print(out); + } + // Don't print fields, methods, inner classes and bootstrap methods if it is module-info entity + if ( !isModuleUnit() ) { + + // Print the fields + printMemberDataList(fields); + + // Print the methods + printMemberDataList(methods); + + // Print the Record (since class file 58.65535 JEP 359) + if( record != null ) { + record.print(); + } + + // Print PermittedSubclasses Attribute (JEP 360 (Sealed types): class file 59.65535) + if( permittedSubclassesData != null) { + permittedSubclassesData.print(); + } + // Print the NestHost (since class file: 55.0) + if(nestHost != null) { + nestHost.print(); + } + // Print the NestMembers (since class file: 55.0) + if( nestMembers != null) { + nestMembers.print(); + } + // Print the inner classes + if (innerClasses != null && !innerClasses.isEmpty()) { + for (InnerClassData icd : innerClasses) { + icd.print(); + } + out.println(); + } + // Print the BootstrapMethods + // + // Only print these if printing extended constants + if ((options.contains(Options.PR.CPX)) && bootstrapMethods != null && !bootstrapMethods.isEmpty()) { + for (BootstrapMethodData bsmdd : bootstrapMethods) { + bsmdd.print(); + } + out.println(); + } + out.println(format("} // end Class %s%s", + className, + sourceName != null ? " compiled from \"" + sourceName +"\"" : "")); + } else { + // Print module attributes + moduleData.print(); + out.print("} // end Module "); + out.print( moduleData.getModuleName()); + if(moduleData.getModuleVersion() != null) + out.print(" @" + moduleData.getModuleVersion()); + out.println(); + } + + List issues = getIssues(); + if( !issues.isEmpty() ) { + + throw issues.get(0); + } + } // end ClassData.print() + + // Gets the type of processed binary + private boolean isModuleUnit() { + return moduleData != null; + } + + private void printMemberDataList( List list) throws IOException { + if( list != null ) { + int count = list.size(); + if( count > 0 ) { + for( int i=0; i < count; i++ ) { + MemberData md = list.get(i); + md.setIndent(Options.BODY_INDENT); + if( i !=0 && md.getAnnotationsCount() > 0 ) + out.println(); + md.print(); + } + out.println(); + } + } + } + + private List getIssues() { + return this.pool.pool.stream(). + filter(Objects::nonNull). + filter(c->c.getIssue() != null). + map(ConstantPool.Constant::getIssue). + collect(Collectors.toList()); + } + +}// end class ClassData + diff --git a/src/org/openjdk/asmtools/jdis/CodeData.java b/src/org/openjdk/asmtools/jdis/CodeData.java new file mode 100644 index 0000000..ffc4039 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/CodeData.java @@ -0,0 +1,743 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.Tables; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.HashMap; + +import static org.openjdk.asmtools.jasm.OpcodeTables.Opcode; +import static org.openjdk.asmtools.jasm.OpcodeTables.opcode; +import static org.openjdk.asmtools.jasm.Tables.*; +import static org.openjdk.asmtools.jasm.Tables.AttrTag.ATT_RuntimeInvisibleTypeAnnotations; +import static org.openjdk.asmtools.jasm.Tables.AttrTag.ATT_RuntimeVisibleTypeAnnotations; +import static org.openjdk.asmtools.jdis.Utils.commentString; + +/** + * Code data for a code attribute in method members in a class of the Java Disassembler + */ +public class CodeData extends Indenter { + + /** + * Raw byte array for the byte codes + */ + protected byte[] code; + /** + * Limit for the stack size + */ + protected int max_stack; + + /* CodeData Fields */ + /** + * Limit for the number of local vars + */ + protected int max_locals; + /** + * The remaining attributes of this class + */ + protected ArrayList attrs = new ArrayList<>(0); // AttrData + + // internal references + protected ClassData cls; + protected MethodData meth; + /** + * (parsed) Trap table, describes exceptions caught + */ + private ArrayList trap_table = new ArrayList<>(0); // TrapData + /** + * (parsed) Line Number table, describes source lines associated with ByteCode indexes + */ + private ArrayList lin_num_tb = new ArrayList<>(0); // LineNumData + /** + * (parsed) Local Variable table, describes variable scopes associated with ByteCode + * indexes + */ + private ArrayList loc_var_tb = new ArrayList<>(0); // LocVarData + /** + * (parsed) stack map table, describes compiler hints for stack rep, associated with + * ByteCode indexes + */ + private ArrayList stack_map = null; + /** + * The visible type annotations for this method + */ + private ArrayList visibleTypeAnnotations; + /** + * The invisible type annotations for this method + */ + private ArrayList invisibleTypeAnnotations; + + /** + * (parsed) reversed bytecode index hash, associates labels with ByteCode indexes + */ + private HashMap iattrs = new HashMap<>(); + private PrintWriter out; + public CodeData(MethodData meth) { + this.meth = meth; + this.cls = meth.cls; + this.out = cls.out; + } + + private static int align(int n) { + return (n + 3) & ~3; + } + /*-------------------------------------------------------- */ + + private int getbyte(int pc) { + return code[pc]; + } + + private int getUbyte(int pc) { + return code[pc] & 0xFF; + } + + private int getShort(int pc) { + return (code[pc] << 8) | (code[pc + 1] & 0xFF); + } + + private int getUShort(int pc) { + return ((code[pc] << 8) | (code[pc + 1] & 0xFF)) & 0xFFFF; + } + + private int getInt(int pc) { + return (getShort(pc) << 16) | (getShort(pc + 2) & 0xFFFF); + } + + protected iAtt get_iAtt(int pc) { + Integer PC = pc; + iAtt res = iattrs.get(PC); + if (res == null) { + res = new iAtt(this); + iattrs.put(PC, res); + } + return res; + } + + /*========================================================*/ + /* Read Methods */ + private void readLineNumTable(DataInputStream in) throws IOException { + int len = in.readInt(); // attr_length + int numlines = in.readUnsignedShort(); + lin_num_tb = new ArrayList<>(numlines); + TraceUtils.traceln(3, "CodeAttr: LineNumTable[" + numlines + "] len=" + len); + for (int l = 0; l < numlines; l++) { + lin_num_tb.add(new LineNumData(in)); + } + } + + private void readLocVarTable(DataInputStream in) throws IOException { + int len = in.readInt(); // attr_length + int numlines = in.readUnsignedShort(); + loc_var_tb = new ArrayList<>(numlines); + TraceUtils.traceln(3, "CodeAttr: LocalVariableTable[" + numlines + "] len=" + len); + for (int l = 0; l < numlines; l++) { + loc_var_tb.add(new LocVarData(in)); + } + } + + private void readTrapTable(DataInputStream in) throws IOException { + int trap_table_len = in.readUnsignedShort(); + TraceUtils.traceln(3, "CodeAttr: TrapTable[" + trap_table_len + "]"); + trap_table = new ArrayList<>(trap_table_len); + for (int l = 0; l < trap_table_len; l++) { + trap_table.add(new TrapData(in, l)); + } + } + + private void readStackMap(DataInputStream in) throws IOException { + int len = in.readInt(); // attr_length + int stack_map_len = in.readUnsignedShort(); + TraceUtils.traceln(3, "CodeAttr: Stack_Map: attrlen=" + len + " num=" + stack_map_len); + stack_map = new ArrayList<>(stack_map_len); + StackMapData.prevFramePC = 0; + for (int k = 0; k < stack_map_len; k++) { + stack_map.add(new StackMapData(this, in)); + } + } + + private void readStackMapTable(DataInputStream in) throws IOException { + int len = in.readInt(); // attr_length + int stack_map_len = in.readUnsignedShort(); + TraceUtils.traceln(3, "CodeAttr: Stack_Map_Table: attrlen=" + len + " num=" + stack_map_len); + stack_map = new ArrayList<>(stack_map_len); + StackMapData.prevFramePC = 0; + for (int k = 0; k < stack_map_len; k++) { + stack_map.add(new StackMapData(this, in, true)); + } + } + + private void readTypeAnnotations(DataInputStream in, boolean isInvisible) throws IOException { + int attrLength = in.readInt(); + // Read Type Annotations Attr + int count = in.readShort(); + ArrayList tannots = new ArrayList<>(count); + TraceUtils.traceln(3, "CodeAttr: Runtime" + + (isInvisible ? "Inv" : "V") + + "isibleTypeAnnotation: attrlen=" + + attrLength + " num=" + count); + for (int index = 0; index < count; index++) { + TraceUtils.traceln("\t\t\t[" + index +"]:"); + TypeAnnotationData tannot = new TypeAnnotationData(isInvisible, cls); + tannot.read(in); + tannots.add(tannot); + } + if (isInvisible) { + invisibleTypeAnnotations = tannots; + } else { + visibleTypeAnnotations = tannots; + } + } + + /** + * read + *

+ * read and resolve the code attribute data called from MethodData. precondition: + * NumFields has already been read from the stream. + */ + public void read(DataInputStream in, int codeattrlen) throws IOException { + + // Read the code in the Code Attribute + max_stack = in.readUnsignedShort(); + max_locals = in.readUnsignedShort(); + int codelen = in.readInt(); + TraceUtils.traceln(3, "CodeAttr: Codelen=" + codelen + + " fulllen=" + codeattrlen + + " max_stack=" + max_stack + + " max_locals=" + max_locals); + + // read the raw code bytes + code = new byte[codelen]; + in.read(code, 0, codelen); + + //read the trap table + readTrapTable(in); + + // Read any attributes of the Code Attribute + int nattr = in.readUnsignedShort(); + TraceUtils.traceln(3, "CodeAttr: add.attr:" + nattr); + for (int k = 0; k < nattr; k++) { + int name_cpx = in.readUnsignedShort(); + // verify the Attrs name + ConstantPool.Constant name_const = cls.pool.getConst(name_cpx); + if (name_const != null && name_const.tag == ConstantPool.TAG.CONSTANT_UTF8) { + String attrname = cls.pool.getString(name_cpx); + TraceUtils.traceln(3, "CodeAttr: attr: " + attrname); + // process the attr + AttrTag attrtag = attrtag(attrname); + switch (attrtag) { + case ATT_LineNumberTable: + readLineNumTable(in); + break; + case ATT_LocalVariableTable: + readLocVarTable(in); + break; + case ATT_StackMap: + readStackMap(in); + break; + case ATT_StackMapTable: + readStackMapTable(in); + break; + case ATT_RuntimeVisibleTypeAnnotations: + case ATT_RuntimeInvisibleTypeAnnotations: + readTypeAnnotations(in, attrtag == ATT_RuntimeInvisibleTypeAnnotations); + break; + default: + AttrData attr = new AttrData(cls); + int attrlen = in.readInt(); // attr_length + attr.read(name_cpx, attrlen, in); + attrs.add(attr); + break; + } + } + } + } + + /*========================================================*/ + /* Code Resolution Methods */ + private int checkForLabelRef(int pc) { + // throws IOException { + int opc = getUbyte(pc); + Opcode opcode = opcode(opc); + switch (opcode) { + case opc_tableswitch: { + int tb = align(pc + 1); + int default_skip = getInt(tb); /* default skip pamount */ + + int low = getInt(tb + 4); + int high = getInt(tb + 8); + int count = high - low; + for (int i = 0; i <= count; i++) { + get_iAtt(pc + getInt(tb + 12 + 4 * i)).referred = true; + } + get_iAtt(default_skip + pc).referred = true; + return tb - pc + 16 + count * 4; + } + case opc_lookupswitch: { + int tb = align(pc + 1); + int default_skip = getInt(tb); /* default skip pamount */ + + int npairs = getInt(tb + 4); + for (int i = 1; i <= npairs; i++) { + get_iAtt(pc + getInt(tb + 4 + i * 8)).referred = true; + } + get_iAtt(default_skip + pc).referred = true; + return tb - pc + (npairs + 1) * 8; + } + case opc_jsr: + case opc_goto: + case opc_ifeq: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + case opc_iflt: + case opc_ifne: + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_icmplt: + case opc_if_acmpeq: + case opc_if_acmpne: + case opc_ifnull: + case opc_ifnonnull: + get_iAtt(pc + getShort(pc + 1)).referred = true; + return 3; + case opc_jsr_w: + case opc_goto_w: + get_iAtt(pc + getInt(pc + 1)).referred = true; + return 5; + case opc_wide: + case opc_nonpriv: + case opc_priv: + int opc2 = (opcode.value() << 8) + getUbyte(pc + 1); + opcode = opcode(opc2); + } + try { + int opclen = opcode.length(); + return opclen == 0 ? 1 : opclen; // bugfix for 4614404 + } catch (ArrayIndexOutOfBoundsException e) { + return 1; + } + } // end checkForLabelRef + + private void loadLabelTable() { + for (int pc = 0; pc < code.length; ) { + pc = pc + checkForLabelRef(pc); + } + } + + private void loadLineNumTable() { + for (LineNumData entry : lin_num_tb) { + get_iAtt(entry.start_pc).lnum = entry.line_number; + } + } + + private void loadStackMap() { + for (StackMapData entry : stack_map) { + get_iAtt(entry.start_pc).stackMapEntry = entry; + } + } + + private void loadLocVarTable() { + for (LocVarData entry : loc_var_tb) { + get_iAtt(entry.start_pc).add_var(entry); + get_iAtt(entry.start_pc + entry.length).add_endvar(entry); + } + } + + private void loadTrapTable() { + for (TrapData entry : trap_table) { + get_iAtt(entry.start_pc).add_trap(entry); + get_iAtt(entry.end_pc).add_endtrap(entry); + get_iAtt(entry.handler_pc).add_handler(entry); + } + } + + /*========================================================*/ + /* Print Methods */ + private void PrintConstant(int cpx) { + out.print("\t"); + cls.pool.PrintConstant(out, cpx); + } + + private void PrintCommentedConstant(int cpx) { + out.print(commentString(cls.pool.ConstantStrValue(cpx))); + } + + private int printInstr(int pc) { + boolean pr_cpx = meth.options.contains(Options.PR.CPX); + int opc = getUbyte(pc); + int opc2; + Opcode opcode = opcode(opc); + Opcode opcode2; + String mnem; + switch (opcode) { + case opc_nonpriv: + case opc_priv: + opc2 = getUbyte(pc + 1); + int finalopc = (opc << 8) + opc2; + opcode2 = opcode(finalopc); + if (opcode2 == null) { +// assume all (even nonexistent) priv and nonpriv instructions +// are 2 bytes long + mnem = opcode.parsekey() + " " + opc2; + } else { + mnem = opcode2.parsekey(); + } + out.print(mnem); + return 2; + case opc_wide: { + opc2 = getUbyte(pc + 1); + int finalopcwide = (opc << 8) + opc2; + opcode2 = opcode(finalopcwide); + if (opcode2 == null) { +// nonexistent opcode - but we have to print something + out.print("bytecode " + opcode); + return 1; + } else { + mnem = opcode2.parsekey(); + } + out.print(mnem + " " + getUShort(pc + 2)); + if (opcode2 == Opcode.opc_iinc_w) { + out.print(", " + getShort(pc + 4)); + return 6; + } + return 4; + } + } + mnem = opcode.parsekey(); + if (mnem == null) { +// nonexistent opcode - but we have to print something + out.print("bytecode " + opcode); + return 1; + } + if (opcode.value() > Opcode.opc_jsr_w.value()) { +// pseudo opcodes should be printed as bytecodes + out.print("bytecode " + opcode); + return 1; + } + out.print(opcode.parsekey()); +// TraceUtils.traceln("****** [CodeData.printInstr]: got an '" + opcode.parseKey() + "' [" + opc + "] instruction ****** "); + switch (opcode) { + case opc_aload: + case opc_astore: + case opc_fload: + case opc_fstore: + case opc_iload: + case opc_istore: + case opc_lload: + case opc_lstore: + case opc_dload: + case opc_dstore: + case opc_ret: + out.print("\t" + getUbyte(pc + 1)); + return 2; + case opc_iinc: + out.print("\t" + getUbyte(pc + 1) + ", " + getbyte(pc + 2)); + return 3; + case opc_tableswitch: { + int tb = align(pc + 1); + int default_skip = getInt(tb); /* default skip pamount */ + + int low = getInt(tb + 4); + int high = getInt(tb + 8); + int count = high - low; + out.print("{ //" + low + " to " + high); + for (int i = 0; i <= count; i++) { + out.print("\n\t\t" + (i + low) + ": " + meth.lP + (pc + getInt(tb + 12 + 4 * i)) + ";"); + } + out.print("\n\t\tdefault: " + meth.lP + (default_skip + pc) + " }"); + return tb - pc + 16 + count * 4; + } + case opc_lookupswitch: { + int tb = align(pc + 1); + int default_skip = getInt(tb); + int npairs = getInt(tb + 4); + out.print("{ //" + npairs); + for (int i = 1; i <= npairs; i++) { + out.print("\n\t\t" + getInt(tb + i * 8) + ": " + meth.lP + (pc + getInt(tb + 4 + i * 8)) + ";"); + } + out.print("\n\t\tdefault: " + meth.lP + (default_skip + pc) + " }"); + return tb - pc + (npairs + 1) * 8; + } + case opc_newarray: + int tp = getUbyte(pc + 1); + BasicType type = basictype(tp); + switch (type) { + case T_BOOLEAN: + out.print(" boolean"); + break; + case T_BYTE: + out.print(" byte"); + break; + case T_CHAR: + out.print(" char"); + break; + case T_SHORT: + out.print(" short"); + break; + case T_INT: + out.print(" int"); + break; + case T_LONG: + out.print(" long"); + break; + case T_FLOAT: + out.print(" float"); + break; + case T_DOUBLE: + out.print(" double"); + break; + case T_CLASS: + out.print(" class"); + break; + default: + out.print(" BOGUS TYPE:" + type); + } + return 2; + case opc_ldc_w: + case opc_ldc2_w: { + // added printing of the tag: Method/Interface to clarify + // interpreting CONSTANT_MethodHandle_info:reference_kind + // Example: ldc_w Dynamic REF_invokeStatic:Method CondyIndy.condy_bsm + cls.pool.setPrintTAG(true); + int index = getUShort(pc + 1); + if (pr_cpx) { + out.print("\t#" + index + "; //"); + } + PrintConstant(index); + cls.pool.setPrintTAG(false); + return 3; + } + case opc_anewarray: + case opc_instanceof: + case opc_checkcast: + case opc_new: + case opc_putstatic: + case opc_getstatic: + case opc_putfield: + case opc_getfield: + case opc_invokevirtual: + case opc_invokespecial: + case opc_invokestatic: { + int index = getUShort(pc + 1); + if (pr_cpx) { + out.print("\t#" + index + "; //"); + } + PrintConstant(index); + return 3; + } + case opc_sipush: + out.print("\t" + getShort(pc + 1)); + return 3; + case opc_bipush: + out.print("\t" + getbyte(pc + 1)); + return 2; + case opc_ldc: { + // added printing of the tag: Method/Interface to clarify + // interpreting CONSTANT_MethodHandle_info:reference_kind + // Example: ldc Dynamic REF_invokeStatic:Method CondyIndy.condy_bsm + cls.pool.setPrintTAG(true); + int index = getUbyte(pc + 1); + if (pr_cpx) { + out.print("\t#" + index + "; //"); + } + PrintConstant(index); + cls.pool.setPrintTAG(false); + return 2; + } + case opc_invokeinterface: { + int index = getUShort(pc + 1), nargs = getUbyte(pc + 3); + if (pr_cpx) { + out.print("\t#" + index + ", " + nargs + "; //"); + PrintConstant(index); + } else { + PrintConstant(index); + out.print(", " + nargs); // args count + } + return 5; + } + case opc_invokedynamic: { // JSR-292 + cls.pool.setPrintTAG(true); + int index = getUShort(pc + 1); + // getUbyte(pc + 3); // reserved byte + // getUbyte(pc + 4); // reserved byte + if (pr_cpx) { + out.print("\t#" + index + ";\t"); + PrintCommentedConstant(index); + } else { + PrintConstant(index); + } + cls.pool.setPrintTAG(false); + return 5; + } + case opc_multianewarray: { + int index = getUShort(pc + 1), dimensions = getUbyte(pc + 3); + if (pr_cpx) { + out.print("\t#" + index + ", " + dimensions + "; //"); + PrintConstant(index); + } else { + PrintConstant(index); + out.print(", " + dimensions); // dimensions count + } + return 4; + } + case opc_jsr: + case opc_goto: + case opc_ifeq: + case opc_ifge: + case opc_ifgt: + case opc_ifle: + case opc_iflt: + case opc_ifne: + case opc_if_icmpeq: + case opc_if_icmpne: + case opc_if_icmpge: + case opc_if_icmpgt: + case opc_if_icmple: + case opc_if_icmplt: + case opc_if_acmpeq: + case opc_if_acmpne: + case opc_ifnull: + case opc_ifnonnull: + out.print("\t" + meth.lP + (pc + getShort(pc + 1))); + return 3; + case opc_jsr_w: + case opc_goto_w: + out.print("\t" + meth.lP + (pc + getInt(pc + 1))); + return 5; + default: + return 1; + } + } // end printInstr + + /** + * print + *

+ * prints the code data to the current output stream. called from MethodData. + */ + public void print() throws IOException { + if (!lin_num_tb.isEmpty()) { + loadLineNumTable(); + } + if (stack_map != null) { + loadStackMap(); + } + if (!meth.options.contains(Options.PR.PC)) { + loadLabelTable(); + } + loadTrapTable(); + if (!loc_var_tb.isEmpty()) { + loadLocVarTable(); + } + + out.println(); + out.println("\tstack " + max_stack + " locals " + max_locals); + + // Need to print ParamAnnotations here. + meth.printPAnnotations(); + + out.println(getIndentString() + "{"); + + iAtt iatt = iattrs.get(0); + for (int pc = 0; pc < code.length; ) { + if (iatt != null) { + iatt.printBegins(); // equ. print("\t"); + } else { + out.print("\t"); + } + if (meth.options.contains(Options.PR.PC)) { + out.print(pc + ":\t"); + } else if ((iatt != null) && iatt.referred) { + out.print(meth.lP + pc + ":\t"); + } else { + out.print("\t"); + } + if (iatt != null) { + iatt.printStackMap(); + } + pc = pc + printInstr(pc); + out.println(";"); + iatt = iattrs.get(pc); + if (iatt != null) { + iatt.printEnds(); + } + } + // the right brace can be labelled: + if (iatt != null) { + iatt.printBegins(); // equ. print("\t"); + if (iatt.referred) { + out.print(meth.lP + code.length + ":\t"); + } + iatt.printStackMap(); + out.println(); + } + // print TypeAnnotations + if (visibleTypeAnnotations != null) { + out.println(); + for (TypeAnnotationData visad : visibleTypeAnnotations) { + visad.print(out, getIndentString()); + out.println(); + } + } + if (invisibleTypeAnnotations != null) { + for (TypeAnnotationData invisad : invisibleTypeAnnotations) { + invisad.print(out, getIndentString()); + out.println(); + } + } + // end of code + out.println(getIndentString() + "}"); + } + + + public static class LocVarData { + + short start_pc, length, name_cpx, sig_cpx, slot; + + public LocVarData(DataInputStream in) throws IOException { + start_pc = in.readShort(); + length = in.readShort(); + name_cpx = in.readShort(); + sig_cpx = in.readShort(); + slot = in.readShort(); + } + } + + /* Code Data inner classes */ + class LineNumData { + + short start_pc, line_number; + + public LineNumData(DataInputStream in) throws IOException { + start_pc = in.readShort(); + line_number = in.readShort(); + } + } + +} diff --git a/src/org/openjdk/asmtools/jdis/ConstantPool.java b/src/org/openjdk/asmtools/jdis/ConstantPool.java new file mode 100644 index 0000000..92dbb07 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/ConstantPool.java @@ -0,0 +1,1073 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.asmutils.HexUtils; +import org.openjdk.asmtools.asmutils.StringUtils; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.Collections; +import java.util.Hashtable; +import java.util.stream.Collectors; + +import static java.lang.String.format; +import static org.openjdk.asmtools.jdis.Utils.commentString; + +/** + * + * ConstantPool + * + * Class representing the ConstantPool + */ +public class ConstantPool { + + private static final Hashtable taghash = new Hashtable<>(); + private static final Hashtable subtaghash = new Hashtable<>(); + + private boolean printTAG = false; + + public void setPrintTAG(boolean value) { + this.printTAG = value; + } + + public String getPrintedTAG(TAG tag) { + return (this.printTAG) ? tag.tagname + " " : "" ; + } + + class Indent { + private int length, offset, step; + + void inc() { length+=step; } + + void dec() { length-=step; } + + Indent(int offset, int step) { + this.length = 0; + this.step = step; + this.offset = offset; + } + + int size() { return offset + length; } + + /** + * Creates indent string based on current indent size. + */ + private String get() { + return Collections.nCopies(size(), "\t").stream().collect(Collectors.joining()); + } + } + + private final Indent indent = new Indent(2, 1); + + /** + * TAG + * + * A Tag descriptor of constants in the constant pool + * + */ + public enum TAG { + CONSTANT_UTF8 ((byte) 1, "Asciz", "CONSTANT_UTF8"), + CONSTANT_UNICODE ((byte) 2, "unicorn", "CONSTANT_UNICODE"), + CONSTANT_INTEGER ((byte) 3, "int", "CONSTANT_INTEGER"), + CONSTANT_FLOAT ((byte) 4, "float", "CONSTANT_FLOAT"), + CONSTANT_LONG ((byte) 5, "long", "CONSTANT_LONG"), + CONSTANT_DOUBLE ((byte) 6, "double", "CONSTANT_DOUBLE"), + CONSTANT_CLASS ((byte) 7, "class", "CONSTANT_CLASS"), + CONSTANT_STRING ((byte) 8, "String", "CONSTANT_STRING"), + CONSTANT_FIELD ((byte) 9, "Field", "CONSTANT_FIELD"), + CONSTANT_METHOD ((byte) 10, "Method", "CONSTANT_METHOD"), + CONSTANT_INTERFACEMETHOD ((byte) 11, "InterfaceMethod", "CONSTANT_INTERFACEMETHOD"), + CONSTANT_NAMEANDTYPE ((byte) 12, "NameAndType", "CONSTANT_NAMEANDTYPE"), + CONSTANT_METHODHANDLE ((byte) 15, "MethodHandle", "CONSTANT_METHODHANDLE"), + CONSTANT_METHODTYPE ((byte) 16, "MethodType", "CONSTANT_METHODTYPE"), + CONSTANT_DYNAMIC ((byte) 17, "Dynamic", "CONSTANT_DYNAMIC"), + CONSTANT_INVOKEDYNAMIC ((byte) 18, "InvokeDynamic", "CONSTANT_INVOKEDYNAMIC"), + CONSTANT_MODULE ((byte) 19, "Module", "CONSTANT_MODULE"), + CONSTANT_PACKAGE ((byte) 20, "Package", "CONSTANT_PACKAGE"); + + private final Byte value; + private final String tagname; + private final String printval; + + TAG(byte val, String tgname, String print) { + value = val; + tagname = tgname; + printval = print; + } + + public byte value() { + return value; + } + + public String tagname() { + return tagname; + } + + public String description() { + return printval; + } + + @Override + public String toString() { + return "<" + tagname + "> "; + } + + }; + + + /** + * SUBTAG + * + * A Tag descriptor of form method-handle constants + * + */ + static public enum SUBTAG { + REF_GETFIELD ((byte) 1, "REF_getField", "REF_GETFIELD"), + REF_GETSTATIC ((byte) 2, "REF_getStatic", "REF_GETSTATIC"), + REF_PUTFIELD ((byte) 3, "REF_putField", "REF_PUTFIELD"), + REF_PUTSTATIC ((byte) 4, "REF_putStatic", "REF_PUTSTATIC"), + REF_INVOKEVIRTUAL ((byte) 5, "REF_invokeVirtual", "REF_INVOKEVIRTUAL"), + REF_INVOKESTATIC ((byte) 6, "REF_invokeStatic", "REF_INVOKESTATIC"), + REF_INVOKESPECIAL ((byte) 7, "REF_invokeSpecial", "REF_INVOKESPECIAL"), + REF_NEWINVOKESPECIAL ((byte) 8, "REF_newInvokeSpecial", "REF_NEWINVOKESPECIAL"), + REF_INVOKEINTERFACE ((byte) 9, "REF_invokeInterface", "REF_INVOKEINTERFACE"); + + private final Byte value; + private final String tagname; + private final String printval; + + SUBTAG(byte val, String tgname, String print) { + value = val; + tagname = tgname; + printval = print; +// subtaghash.put(new Byte(val), this); + } + + public byte value() { + return value; + } + + public String tagname() { + return tagname; + } + + public String description() { + return printval; + } + + @Override + public String toString() { + return "<" + tagname + "> "; + } + }; + + static { + + // Class initializer Code + // + // Make sure all of the tags get initialized before being used. + taghash.put(TAG.CONSTANT_UTF8.value(), TAG.CONSTANT_UTF8); + taghash.put(TAG.CONSTANT_UNICODE.value(), TAG.CONSTANT_UNICODE); + taghash.put(TAG.CONSTANT_INTEGER.value(), TAG.CONSTANT_INTEGER); + taghash.put(TAG.CONSTANT_FLOAT.value(), TAG.CONSTANT_FLOAT); + taghash.put(TAG.CONSTANT_LONG.value(), TAG.CONSTANT_LONG); + taghash.put(TAG.CONSTANT_DOUBLE.value(), TAG.CONSTANT_DOUBLE); + taghash.put(TAG.CONSTANT_CLASS.value(), TAG.CONSTANT_CLASS); + taghash.put(TAG.CONSTANT_STRING.value(), TAG.CONSTANT_STRING); + taghash.put(TAG.CONSTANT_FIELD.value(), TAG.CONSTANT_FIELD); + taghash.put(TAG.CONSTANT_METHOD.value(), TAG.CONSTANT_METHOD); + taghash.put(TAG.CONSTANT_INTERFACEMETHOD.value(), TAG.CONSTANT_INTERFACEMETHOD); + taghash.put(TAG.CONSTANT_NAMEANDTYPE.value(), TAG.CONSTANT_NAMEANDTYPE); + taghash.put(TAG.CONSTANT_METHODHANDLE.value(), TAG.CONSTANT_METHODHANDLE); + taghash.put(TAG.CONSTANT_METHODTYPE.value(), TAG.CONSTANT_METHODTYPE); + taghash.put(TAG.CONSTANT_DYNAMIC.value(), TAG.CONSTANT_DYNAMIC); + taghash.put(TAG.CONSTANT_INVOKEDYNAMIC.value(), TAG.CONSTANT_INVOKEDYNAMIC); + taghash.put(TAG.CONSTANT_MODULE.value(), TAG.CONSTANT_MODULE); + taghash.put(TAG.CONSTANT_PACKAGE.value(), TAG.CONSTANT_PACKAGE); + + subtaghash.put(SUBTAG.REF_GETFIELD.value(), SUBTAG.REF_GETFIELD); + subtaghash.put(SUBTAG.REF_GETSTATIC.value(), SUBTAG.REF_GETSTATIC); + subtaghash.put(SUBTAG.REF_PUTFIELD.value(), SUBTAG.REF_PUTFIELD); + subtaghash.put(SUBTAG.REF_PUTSTATIC.value(), SUBTAG.REF_PUTSTATIC); + subtaghash.put(SUBTAG.REF_INVOKEVIRTUAL.value(), SUBTAG.REF_INVOKEVIRTUAL); + subtaghash.put(SUBTAG.REF_INVOKESTATIC.value(), SUBTAG.REF_INVOKESTATIC); + subtaghash.put(SUBTAG.REF_INVOKESPECIAL.value(), SUBTAG.REF_INVOKESPECIAL); + subtaghash.put(SUBTAG.REF_NEWINVOKESPECIAL.value(), SUBTAG.REF_NEWINVOKESPECIAL); + subtaghash.put(SUBTAG.REF_INVOKEINTERFACE.value(), SUBTAG.REF_INVOKEINTERFACE); + + } + + /** + * + * Constant + * + * Base class of all constant entries + * + */ + public class Constant { + + /** + * tag the descriptor for the constant + */ + public TAG tag; + + public Constant(TAG tagval) { + tag = tagval; + } + + public String stringVal() { + return ""; + } + + public void print(PrintWriter out) { + out.print(tag.tagname + "\t"); + } + + public int size() { + return 1; + } + + @Override + public String toString() { + return ""; + } + + private IOException issue; + + public IOException getIssue() { + return issue; + } + + public void setIssue(IOException value) { + issue = value; + } + + } + + /* -------------------------------------------------------- */ + /* Constant Sub-classes */ + /** + * + * CP_Str + * + * Constant entries that contain String data. usually is a CONSTANT_UTF8 + * + */ + class CP_Str extends Constant { + + String value; + + CP_Str(TAG tagval, String str) { + super(tagval); + this.value = str; + } + + @Override + public String stringVal() { + return StringUtils.Utf8ToString(value); + } + + @Override + public void print(PrintWriter out) { + super.print(out); + out.println(stringVal() + ";"); + } + } + + /** + * + * CP_Int + * + * Constant entries that contain Integer data. usually is a CONSTANT_INTEGER + * + */ + class CP_Int extends Constant { + + Integer value; + + CP_Int(TAG tagval, int intval) { + super(tagval); + this.value = intval; + } + + @Override + public String stringVal() { + if (cd.options.contains(Options.PR.HEX)) { + return HexUtils.toHex(value.intValue()); + } + return value.toString(); + } + + @Override + public void print(PrintWriter out) { + super.print(out); + out.println(stringVal() + ";"); + } + } + + /** + * + * CP_Long + * + * Constant entries that contain LongInteger data. usually is a CONSTANT_LONG + * + * These take up 2 slots in the constant pool. + * + */ + class CP_Long extends Constant { + + Long value; + + CP_Long(TAG tagval, long intval) { + super(tagval); + this.value = intval; + } + + @Override + public String stringVal() { + if (cd.options.contains(Options.PR.HEX)) { + return HexUtils.toHex(value.longValue()) + 'l'; + } + return value.toString() + 'l'; + } + + @Override + public void print(PrintWriter out) { + super.print(out); + out.println(stringVal() + ";"); + } + + @Override + public int size() { + return 2; + } + } + + /** + * + * CP_Float + * + * Constant entries that contain Float data. usually is a CONSTANT_FLOAT + * + */ + class CP_Float extends Constant { + + Float value; + + CP_Float(TAG tagval, float fltvl) { + super(tagval); + this.value = fltvl; + } + + @Override + public String stringVal() { + if (cd.options.contains(Options.PR.HEX)) { + return "bits " + HexUtils.toHex(Float.floatToIntBits(value.floatValue())); + } + String sf = (value).toString(); + if (value.isNaN() || value.isInfinite()) { + return sf; + } + return sf + "f"; + } + + @Override + public void print(PrintWriter out) { + super.print(out); + out.println(stringVal() + ";"); + } + } + + /** + * + * CP_Double + * + * Constant entries that contain double-precision float data. usually is a + * CONSTANT_DOUBLE + * + * These take up 2 slots in the constant pool. + * + */ + class CP_Double extends Constant { + + Double value; + + CP_Double(TAG tagval, double fltvl) { + super(tagval); + this.value = fltvl; + } + + @Override + public String stringVal() { + if (cd.options.contains(Options.PR.HEX)) { + return "bits " + HexUtils.toHex(Double.doubleToLongBits(value.doubleValue())) + 'l'; + } + String sd = value.toString(); + if (value.isNaN() || value.isInfinite()) { + return sd; + } + return sd + "d"; + } + + @Override + public void print(PrintWriter out) { + super.print(out); + out.println(stringVal() + ";"); + } + + @Override + public int size() { + return 2; + } + } + + /** + * + * CPX + * + * Constant entries that contain a single constant-pool index. Usually, this includes: + * CONSTANT_CLASS CONSTANT_METHODTYPE CONSTANT_STRING CONSTANT_MODULE CONSTANT_PACKAGE + * + */ + class CPX extends Constant { + + int value; + + CPX(TAG tagval, int cpx) { + super(tagval); + this.value = cpx; + } + + @Override + public String stringVal() { + String str = "UnknownTag"; + switch (tag) { + case CONSTANT_CLASS: + str = getShortClassName(getClassName(this), cd.pkgPrefix); + break; + case CONSTANT_PACKAGE: + case CONSTANT_MODULE: + str = getString(value); + break; + case CONSTANT_METHODTYPE: + case CONSTANT_STRING: + str = StringValue(value); + break; + default: + break; + } + return str; + } + + @Override + public void print(PrintWriter out) { + super.print(out); + switch (tag) { + case CONSTANT_CLASS: + case CONSTANT_STRING: + case CONSTANT_METHODTYPE: + case CONSTANT_PACKAGE: + case CONSTANT_MODULE: + out.println("#" + (value) + ";\t// " + stringVal()); + break; + } + } + } + + /** + * + * CPX2 + * + * Constant entries that contain two constant-pool indices. Usually, this includes: + * CONSTANT_FIELD CONSTANT_METHOD CONSTANT_INTERFACEMETHOD CONSTANT_NAMEANDTYPE + * CONSTANT_METHODHANDLE CONSTANT_DYNAMIC CONSTANT_INVOKEDYNAMIC + * + */ + class CPX2 extends Constant { + + int value1, value2; + + CPX2(TAG tagval, int cpx1, int cpx2) { + super(tagval); + this.value1 = cpx1; + this.value2 = cpx2; + } + + @Override + public String stringVal() { + + String str = "UnknownTag"; + switch (tag) { + case CONSTANT_FIELD: + // CODETOOLS-7902660: the tag Field is not necessary while printing static parameters of a bsm + // Example: MethodHandle REF_getField:ClassName.FieldName:"I" + str = getShortClassName(getClassName(value1), cd.pkgPrefix) + "." + StringValue(value2); + break; + case CONSTANT_METHOD: + case CONSTANT_INTERFACEMETHOD: + // CODETOOLS-7902648: added printing of the tag: Method/Interface to clarify + // interpreting CONSTANT_MethodHandle_info:reference_kind + // Example: invokedynamic InvokeDynamic REF_invokeStatic:Method java/lang/runtime/ObjectMethods.bootstrap + str = getPrintedTAG(tag) + getShortClassName(getClassName(value1), cd.pkgPrefix) + "." + StringValue(value2); + break; + case CONSTANT_NAMEANDTYPE: + str = getName(value1) + ":" + StringValue(value2); + break; + case CONSTANT_METHODHANDLE: + str = subtagToString(value1) + ":" + StringValue(value2); + break; + case CONSTANT_DYNAMIC: + case CONSTANT_INVOKEDYNAMIC: + int bsm_attr_idx = value1; + int nape_idx = value2; + BootstrapMethodData bsmData; + try { + bsmData = cd.bootstrapMethods.get(bsm_attr_idx); + } catch (NullPointerException npe) { + return ""; + } catch (IndexOutOfBoundsException ioob) { + return ""; + } + StringBuilder bsm_args_str = new StringBuilder(); + String offsetParm,offsetBrace; + int bsm_ref = bsmData.bsm_index; + int bsm_args_len = bsmData.bsm_args_indexes.size(); + if (bsm_args_len > 0) { + bsm_args_str.append(" {\n"); + offsetBrace = indent.get(); + indent.inc(); + offsetParm = indent.get(); + for (int i = 0; i < bsm_args_len; i++) { + int bsm_arg_idx = bsmData.bsm_args_indexes.get(i); + Constant cnt = pool.get(bsm_arg_idx); + if (cnt.equals(this)) { + String s = "circular reference to " + cnt.tag.tagname() + " #" + bsm_arg_idx; + bsm_args_str.append(offsetParm).append(" <").append(s).append(">"); + cnt.setIssue(new IOException(s)); + } else { + bsm_args_str.append(offsetParm).append(ConstantStrValue(bsm_arg_idx)); + if (i + 1 < bsm_args_len) { + bsm_args_str.append(","); + } + } + bsm_args_str.append('\n'); + } + indent.dec(); + bsm_args_str.append(offsetBrace).append("}"); + } + str = StringValue(bsm_ref) + ":" + StringValue(nape_idx) + bsm_args_str.toString(); + default: + break; + } + return str; + } + + + + @Override + public void print(PrintWriter out) { + super.print(out); + switch (tag) { + case CONSTANT_FIELD: + case CONSTANT_METHOD: + case CONSTANT_INTERFACEMETHOD: + out.println("#" + value1 + ".#" + value2 + ";\t// " + stringVal()); + break; + case CONSTANT_METHODHANDLE: + out.println(value1 + ":#" + value2 + ";\t// " + stringVal()); + break; + case CONSTANT_NAMEANDTYPE: + out.println("#" + value1 + ":#" + value2 + ";\t// " + stringVal()); + break; + case CONSTANT_DYNAMIC: + case CONSTANT_INVOKEDYNAMIC: + out.println(value1 + ":#" + value2 + ";\t" + commentString(stringVal())); + break; + default: + break; + } + } + + public boolean refersClassMember() { + return tag == TAG.CONSTANT_FIELD || tag == TAG.CONSTANT_METHOD || tag == TAG.CONSTANT_INTERFACEMETHOD; + } + } + + /* -------------------------------------------------------- */ + /* ConstantPool Fields */ + /** + * The actual pool of Constants + */ + public ArrayList pool; + /** + * Reference to the class data + */ + private ClassData cd; + + + /* -------------------------------------------------------- */ + /* ConstantPool Methods */ + + /* ConstantPool Constructors */ + public ConstantPool(ClassData cd) { + pool = null; + this.cd = cd; + } + + public ConstantPool(ClassData cd, int size) { + pool = new ArrayList<>(size); + this.cd = cd; + } + + /** + * + * read + * + * decodes a ConstantPool and it's constants from a data stream. + * + */ + void read(DataInputStream in) throws IOException { + int length = in.readUnsignedShort(); + pool = new ArrayList<>(length); + pool.add(0, null); + TraceUtils.traceln("CP len=" + length); + for (int i = 1; i < length; i++) { + byte tag = in.readByte(); + TAG tagobj = taghash.get(tag); + TraceUtils.traceln("CP entry #" + i + " + tagindex=" + tag + " tag=" + tagobj); + switch (tagobj) { + case CONSTANT_UTF8: + pool.add(i, new CP_Str(tagobj, in.readUTF())); + break; + case CONSTANT_INTEGER: + pool.add(i, new CP_Int(tagobj, in.readInt())); + break; + case CONSTANT_LONG: + pool.add(i, new CP_Long(tagobj, in.readLong())); + // handle null entry to account for Longs taking up 2 CP slots + i += 1; + pool.add(null); + break; + case CONSTANT_FLOAT: + pool.add(i, new CP_Float(tagobj, in.readFloat())); + break; + case CONSTANT_DOUBLE: + pool.add(i, new CP_Double(tagobj, in.readDouble())); + // handle null entry to account for Doubles taking up 2 CP slots + i += 1; + pool.add(null); + break; + case CONSTANT_CLASS: + case CONSTANT_STRING: + case CONSTANT_METHODTYPE: + case CONSTANT_PACKAGE: + case CONSTANT_MODULE: + pool.add(i, new CPX(tagobj, in.readUnsignedShort())); + break; + case CONSTANT_FIELD: + case CONSTANT_METHOD: + case CONSTANT_INTERFACEMETHOD: + case CONSTANT_NAMEANDTYPE: + case CONSTANT_DYNAMIC: + case CONSTANT_INVOKEDYNAMIC: + pool.add(i, new CPX2(tagobj, in.readUnsignedShort(), in.readUnsignedShort())); + break; + case CONSTANT_METHODHANDLE: + pool.add(i, new CPX2(tagobj, in.readUnsignedByte(), in.readUnsignedShort())); + break; + + default: + throw new ClassFormatError("invalid constant type: " + (int) tag); + } + } + } + + /** + * + * inbounds + * + * bounds-check a CP index. + * + */ + private boolean inbounds(int cpx) { + return !(cpx == 0 || cpx >= pool.size()); + } + + /** + * + * getConst + * + * Public getter - Safely gets a Constant from the CP at a given index. + * + */ + public Constant getConst(int cpx) { + if (inbounds(cpx)) { + return pool.get(cpx); + } else { + return null; + } + } + + /** + * + * StringTag + * + * Public string val - Safely gets the string-rep of a Constant from the CP at a given + * index. + * + */ + public String StringTag(int cpx) { + String str = "Incorrect CP index:" + cpx; + if (inbounds(cpx)) { + Constant cns = pool.get(cpx); + if (cns != null) { + str = cns.tag.tagname; + } + } + return str; + } + + /** + * + * getString + * + * Public string val - Safely gets the string-rep of a ConstantUTF8 from the CP at a + * given index. + * + * Returns either null (if invalid), or the string value of the UTF8 + * + */ + public String getString(int cpx) { + String str = null; + if (inbounds(cpx)) { + Constant cns = pool.get(cpx); + if (cns != null && cns.tag == TAG.CONSTANT_UTF8) { + CP_Str cns1 = (CP_Str) cns; + str = cns1.value; + } + } + return str; + } + + /** + * + * getModule + * + * Public string val - Safely gets the string-rep of a ConstantModule from the CP at a + * given index. + * + * Returns either null (if invalid), or the string value of the ConstantModule + * + */ + public String getModule(int cpx) { + String str = null; + if (inbounds(cpx)) { + Constant cns = pool.get(cpx); + if (cns != null && cns.tag == TAG.CONSTANT_MODULE) { + str = cns.stringVal(); + } + } + return str; + } + + /** + * + * getPackage + * + * Public string val - Safely gets the string-rep of a ConstantPackage from the CP at a + * given index. + * + * Returns either null (if invalid), or the string value of the ConstantPackage + * + */ + public String getPackage(int cpx) { + String str = null; + if (inbounds(cpx)) { + Constant cns = pool.get(cpx); + if (cns != null && cns.tag == TAG.CONSTANT_PACKAGE) { + str = cns.stringVal(); + } + } + return str; + } + + /** + * + * getTypeName + * + * Safely gets a Java name from a ConstantUTF8 from the CP at a given index. + * + * Returns either null (if invalid), or the Java name value of the UTF8 + * + */ + public String getName(int cpx) { + String str = getString(cpx); + if (str == null) { + return ""; + } + + return Utils.javaName(str); + } + + /** + * + * getClassName + * + * Safely gets a Java class name from a ConstantClass from the CP at a given index. + * + * Returns either the Java class name, or a CP index reference string. + * + */ + public String getClassName(int cpx) { + String res = "#" + cpx; + if (cpx == 0) { + return res; + } + if (!inbounds(cpx)) { + return res; + } + Constant cns = pool.get(cpx); + if (cns == null || cns.tag != TAG.CONSTANT_CLASS) { + return res; + } + + return getClassName((CPX) cns); + } + + /** + * + * getClassName + * + * Safely gets a Java class name from a ConstantClass from a CPX2 constant pool + * object. (eg. Method/Field/Interface Ref) + * + * Returns either the Java class name, or a CP index reference string. + * + */ + public String getClassName(CPX2 classConst) { + return _getClassName(classConst.value1); + } + + /** + * + * getClassName + * + * Safely gets a Java class name from a ConstantClass from a CPX constant pool object. + * (eg. Class Ref) + * + * Returns either the Java class name, or a CP index reference string. + * + */ + public String getClassName(CPX classConst) { + return _getClassName(classConst.value); + } + + /** + * + * _getClassName + * + * Helper for getting class name. Checks bounds, does name conversion. + * + */ + private String _getClassName(int nameIndex) { + String res = "#" + nameIndex; + if (!inbounds(nameIndex)) { + return res; + } + Constant nameconst = pool.get(nameIndex); + if (nameconst == null || nameconst.tag != TAG.CONSTANT_UTF8) { + return res; + } + CP_Str name = (CP_Str) nameconst; + + String classname = name.value; + + if (Utils.isClassArrayDescriptor(classname)) { + classname = "\"" + classname + "\""; + } + return classname; + } + + /** + * + * getShortClassName + * + * shortens a class name (if the class is in the given package). works with a + * string-encoded classname. + * + */ + public String getShortClassName(String className, String pkgPrefix) { + if (className.startsWith(pkgPrefix)) { + return className.substring(pkgPrefix.length()); + } + return className; + } + + /** + * + * getShortClassName + * + * shortens a class name (if the class is in the given package). works with a CP index + * to a ConstantClass. + * + */ + public String getShortClassName(int cpx, String pkgPrefix) { + String name = Utils.javaName(getClassName(cpx)); + return getShortClassName(name, pkgPrefix); + } + + /** + * + * decodeClassDescriptor + * + * Pulls the class name out of a string (at the CP index). (drops any array + * descriptors, and the class descriptors ("L" and ";") + * + */ + public String decodeClassDescriptor(int cpx) { + // enum type is encoded as a descriptor + // need to remove '"'s and L (class descriptor) + + // TODO: might have to count '['s at the beginning for Arrays + String rawEnumName = getName(cpx); + int len = rawEnumName.length(); + int begin = (rawEnumName.startsWith("\"L")) ? 2 : 0; + int end = (begin > 0) ? len - 2 : len; + return rawEnumName.substring(begin, end); + } + + /** + * + * subtagToString + * + * Getter that safely gets the string descriptor of a subtag + * + */ + private String subtagToString(int subtag) { + SUBTAG st = subtaghash.get((byte) subtag); + if (st == null) { + return "BOGUS_SUBTAG:" + subtag; + } + return st.tagname; + } + + /** + * + * StringValue + * + * Safely gets the string value of any Constant at any CP index. + * + */ + public String StringValue(int cpx) { + if (cpx == 0) { + return "#0"; + } + if (!inbounds(cpx)) { + return ""; + } + Constant cnst = pool.get(cpx); + if (cnst == null) { + return ""; + } + return cnst.stringVal(); + } + + /** + * ConstantStrValue + * + * Safely gets the string value of any Constant at any CP index. This string is either + * a Constant's String value, or a CP index reference string. The Constant string has + * a tag descriptor in the beginning. + * + */ + public String ConstantStrValue(int cpx) { + if (cpx == 0) { + return "#0"; + } + if (!inbounds(cpx)) { + return "#" + cpx; + } + Constant cns = pool.get(cpx); + if (cns == null) { + return "#" + cpx; + } + if (cns instanceof CPX2) { + CPX2 cns2 = (CPX2) cns; + if (cns2.value1 == cd.this_cpx && cns2.refersClassMember()) { + cpx = cns2.value2; + } + } + return cns.tag.tagname + " " + StringValue(cpx); + } + + /** + * prints the entire constant pool. + */ + public void print(PrintWriter out) throws IOException { + int cpx = 0; + for (Constant cns : pool) { + if (cpx == 0) { + cpx += 1; + continue; + } + + out.print("\tconst #" + cpx + " = "); + + if (cns == null) { + // do something + out.println("null"); + cpx += 1; + } else { + cns.print(out); + cpx += cns.size(); + } + } + } + + /** + * prints the Constant value at a given CP index. + */ + void PrintConstant(PrintWriter out, int cpx) { + out.print(ConstantStrValue(cpx)); + } + + /** + * prints a constant value, with the print format based on the print options. + */ + public void printlnClassId(PrintWriter out, int cpx) throws IOException { + printlnClassId(out, cpx, false); + } + + public void printlnClassId(PrintWriter out, int cpx, boolean addComma) throws IOException { + if (!cd.options.contains(Options.PR.CPX)) { + out.print(getShortClassName(cpx, cd.pkgPrefix) + (addComma ? "," : "")); + } else { + out.print("\t#" + cpx + (addComma ? "," : "") + " //"); + PrintConstant(out, cpx); + } + } + +} diff --git a/src/org/openjdk/asmtools/jdis/FieldData.java b/src/org/openjdk/asmtools/jdis/FieldData.java new file mode 100644 index 0000000..13b663d --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/FieldData.java @@ -0,0 +1,126 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.JasmTokens; +import org.openjdk.asmtools.jasm.Modifiers; + +import java.io.DataInputStream; +import java.io.IOException; + +import static java.lang.String.format; +import static org.openjdk.asmtools.jasm.Tables.AttrTag; +import static org.openjdk.asmtools.jasm.Tables.CF_Context; +import static org.openjdk.asmtools.jdis.TraceUtils.traceln; + +/** + * Field data for field members in a class of the Java Disassembler + */ +public class FieldData extends MemberData { + + // CP index to the field name + protected int name_cpx; + // CP index to the field type + protected int type_cpx; + // CP index to the field value + protected int value_cpx = 0; + + public FieldData(ClassData cls) { + super(cls); + memberType = "FieldData"; + } + + @Override + protected boolean handleAttributes(DataInputStream in, AttrTag attrtag, int attrlen) throws IOException { + // Read the Attributes + boolean handled = true; + switch (attrtag) { + case ATT_Signature: + if( signature != null ) { + traceln("Record attribute: more than one attribute Signature are in component.attribute_info_attributes[attribute_count]"); + traceln("Last one will be used."); + } + signature = new SignatureData(cls).read(in, attrlen); + break; + case ATT_ConstantValue: + if (attrlen != 2) { + throw new ClassFormatError(format("%s: Invalid attribute length #%d", AttrTag.ATT_ConstantValue.printval(), attrlen)); + } + value_cpx = in.readUnsignedShort(); + break; + default: + handled = false; + break; + } + return handled; + } + + /** + * Read and resolve the field data called from ClassData. + * Precondition: NumFields has already been read from the stream. + */ + public void read(DataInputStream in) throws IOException { + // read the Fields CP indexes + access = in.readUnsignedShort(); + name_cpx = in.readUnsignedShort(); + type_cpx = in.readUnsignedShort(); + // Read the attributes + readAttributes(in); + // + TraceUtils.traceln(2, + format("FieldData: name[%d]=%s type[%d]=%s%s", + name_cpx, cls.pool.getString(name_cpx), + type_cpx, cls.pool.getString(type_cpx), + signature != null ? signature : "")); + } + + + /** + * Prints the field data to the current output stream. called from ClassData. + */ + @Override + public void print() throws IOException { + // Print annotations first + super.printAnnotations(getIndentString()); + + StringBuilder bodyPrefix = new StringBuilder(getIndentString()).append(Modifiers.accessString(access, CF_Context.CTX_FIELD)); + StringBuilder tailPrefix = new StringBuilder(); + + if (isSynthetic) { + bodyPrefix.append(JasmTokens.Token.SYNTHETIC.parseKey()).append(' '); + } + if (isDeprecated) { + bodyPrefix.append(JasmTokens.Token.DEPRECATED.parseKey()).append(' '); + } + + // field + bodyPrefix.append(JasmTokens.Token.FIELDREF.parseKey()).append(' '); + + if (value_cpx != 0) { + tailPrefix.append("\t= ").append(cls.pool.ConstantStrValue(value_cpx)); + } + + printVar(bodyPrefix, tailPrefix,name_cpx, type_cpx); + } +} // end FieldData + diff --git a/src/org/openjdk/asmtools/jdis/Indenter.java b/src/org/openjdk/asmtools/jdis/Indenter.java new file mode 100644 index 0000000..512393d --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/Indenter.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +public class Indenter { + + private int indentLength; + + public Indenter(int indentLength) { + this.indentLength = indentLength; + } + + public Indenter() { + this.indentLength = Options.BODY_INDENT; + } + /** + * Returns current indentation length. + * + * @return current indentation length. + */ + public int indent() { + return indentLength; + } + + /** + * Increases indentation length. + * + * @param indentLength new indent length + * + * @throws IllegalArgumentException if indentLength is negative. + */ + public Indenter setIndent(int indentLength) { + if (indentLength < 0) { + throw new IllegalArgumentException("indent length can't be negative"); + } + this.indentLength = indentLength; + return this; + } + + /** + * Increases indentation length. + * + * @param increase length to increase by. + * + * @throws IllegalArgumentException if increase is negative. + */ + public Indenter increaseIndent(int increase) { + if (increase < 0) { + throw new IllegalArgumentException("indent length can't be negative"); + } + setIndent(indent() + increase); + return this; + } + + /** + * Decreases indentation length. + * + * @param decrease length to decrease by + * + * @throws IllegalArgumentException if decrease is negative, or if decrease is greater than + * {@link #indent() current indentation length}. + */ + public Indenter decreaseIndent(int decrease) { + if (decrease < 0) { + throw new IllegalArgumentException("decrease can't be negative"); + } + setIndent(indent() - decrease); + return this; + } + + /** + * Creates indent string based on current indent size. + */ + public String getIndentString() { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < indent(); i++) { + sb.append(' '); + } + return sb.toString(); + } +} diff --git a/src/org/openjdk/asmtools/jdis/InnerClassData.java b/src/org/openjdk/asmtools/jdis/InnerClassData.java new file mode 100644 index 0000000..ab9ea20 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/InnerClassData.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import static org.openjdk.asmtools.jasm.Tables.*; +import org.openjdk.asmtools.jasm.Modifiers; +import java.io.DataInputStream; +import java.io.IOException; + +/** + * + */ +class InnerClassData extends Indenter { + + ClassData cls; + int inner_class_info_index; + int outer_class_info_index; + int inner_name_index; + int access; + /*-------------------------------------------------------- */ + + public InnerClassData(ClassData cls) { + this.cls = cls; + } + + public void read(DataInputStream in) throws IOException { + inner_class_info_index = in.readUnsignedShort(); + outer_class_info_index = in.readUnsignedShort(); + inner_name_index = in.readUnsignedShort(); + access = in.readUnsignedShort(); + } // end read + + public void print() throws IOException { + boolean pr_cpx = Options.OptionObject().contains(Options.PR.CPX); + cls.out.print(getIndentString() + Modifiers.accessString(access, CF_Context.CTX_INNERCLASS)); + cls.out.print("InnerClass "); + if (pr_cpx) { + if (inner_name_index != 0) { + cls.out.print("#" + inner_name_index + "= "); + } + cls.out.print("#" + inner_class_info_index); + if (outer_class_info_index != 0) { + cls.out.print(" of #" + outer_class_info_index); + } + cls.out.print("; // "); + } + if (inner_name_index != 0) { + cls.out.print(cls.pool.getName(inner_name_index) + "="); + } + if (inner_class_info_index != 0) { + cls.pool.PrintConstant(cls.out, inner_class_info_index); + } + if (outer_class_info_index != 0) { + cls.out.print(" of "); + cls.pool.PrintConstant(cls.out, outer_class_info_index); + } + if (pr_cpx) { + cls.out.println(); + } else { + cls.out.println(";"); + } + } +} // end InnerClassData + diff --git a/src/org/openjdk/asmtools/jdis/Main.java b/src/org/openjdk/asmtools/jdis/Main.java new file mode 100644 index 0000000..d7a9153 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/Main.java @@ -0,0 +1,139 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.common.Tool; +import org.openjdk.asmtools.util.I18NResourceBundle; +import org.openjdk.asmtools.util.ProductInfo; + +import java.io.DataInputStream; +import java.io.PrintStream; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * Main program of the Java Disassembler :: class to jasm + */ +public class Main extends Tool { + + private Options options; + + public static final I18NResourceBundle i18n + = I18NResourceBundle.getBundleForClass(Main.class); + + public Main(PrintWriter out, PrintWriter err, String programName) { + super(out, err, programName); + // tool specific initialization + options = Options.OptionObject(); + DebugFlag = () -> options.contains(Options.PR.DEBUG); + printCannotReadMsg = (fname) -> error( i18n.getString("jdis.error.cannot_read", fname)); + } + + public Main(PrintStream out, String program) { + this(new PrintWriter(out), new PrintWriter(System.err), program); + } + + @Override + public void usage() { + println(i18n.getString("jdis.usage")); + println(i18n.getString("jdis.opt.g")); + println(i18n.getString("jdis.opt.sl")); + println(i18n.getString("jdis.opt.hx")); + println(i18n.getString("jdis.opt.v")); + println(i18n.getString("jdis.opt.version")); + } + + /** + * Run the disassembler + */ + public synchronized boolean disasm(String argv[]) { + ArrayList files = new ArrayList<>(); + + // Parse arguments + for (int i = 0; i < argv.length; i++) { + String arg = argv[i]; + switch (arg) { + case "-g": + options.setCodeOptions(); + break; + case "-v": + options.set(Options.PR.DEBUG); + break; + case "-sl": + options.set(Options.PR.SRC); + break; + case "-hx": + options.set(Options.PR.HEX); + break; + case "-version": + out.println(ProductInfo.FULL_VERSION); + break; + default: + if (arg.startsWith("-")) { + error(i18n.getString("jdis.error.invalid_option", arg)); + usage(); + return false; + } else { + files.add(arg); + } + break; + } + } + + if (files.isEmpty()) { + usage(); + return false; + } + + for (String fname : files) { + if (fname == null) { + continue; + } // cross out by CompilerChoice.compile + try { + ClassData cc = new ClassData(out, this); + cc.read(fname); + cc.print(); + continue; + } catch (Error ee) { + if (DebugFlag.getAsBoolean()) + ee.printStackTrace(); + error(i18n.getString("jdis.error.fatal_error", fname)); + } catch (Exception ee) { + if (DebugFlag.getAsBoolean()) + ee.printStackTrace(); + error(i18n.getString("jdis.error.fatal_exception", fname)); + } + return false; + } + return true; + } + + /** + * Main program + */ + public static void main(String argv[]) { + Main disassembler = new Main(new PrintWriter(new uEscWriter(System.out)), new PrintWriter(System.err), "jdis"); + boolean result = disassembler.disasm(argv); + System.exit(result ? 0 : 1); + } +} diff --git a/src/org/openjdk/asmtools/jdis/MemberData.java b/src/org/openjdk/asmtools/jdis/MemberData.java new file mode 100644 index 0000000..bea9ba8 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/MemberData.java @@ -0,0 +1,235 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.Tables; +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +import static java.lang.String.format; + +/** + * Base class for ClassData, MethodData, FieldData and RecordData(JEP 360) + */ +public abstract class MemberData extends Indenter { + + // access flags (modifiers) + protected int access; + + // flags + protected boolean isSynthetic = false; + protected boolean isDeprecated = false; + + // Signature can be located in ClassFile, field_info, method_info, and component_info + protected SignatureData signature; + + /** + * The visible annotations for this class, member( field or method) or record component + */ + protected ArrayList visibleAnnotations; + + /** + * The invisible annotations for this class, member( field or method) or record component + */ + protected ArrayList invisibleAnnotations; + + /** + * The visible annotations for this class, member( field or method) or record component + */ + protected ArrayList visibleTypeAnnotations; + + /** + * The invisible annotations for this class, member( field or method) or record component + */ + protected ArrayList invisibleTypeAnnotations; + + /** + * The remaining attributes of this class, member( field or method) or record component + */ + protected ArrayList attrs; + + // internal references + protected final Options options = Options.OptionObject(); + protected final boolean pr_cpx = options.contains(Options.PR.CPX);; + protected ClassData cls; + protected PrintWriter out; + protected String memberType = ""; + + public MemberData(ClassData cls) { + this(); + init(cls); + } + + public MemberData() { + } + + public void init(ClassData cls) { + this.out = cls.out; + this.cls = cls; + } + + protected boolean handleAttributes(DataInputStream in, Tables.AttrTag attrtag, int attrlen) throws IOException { + // sub-classes override + return false; + } + + protected abstract void print() throws IOException; + + final protected int getAnnotationsCount() { + return ((visibleAnnotations == null) ? 0 : visibleAnnotations.size()) + + ((invisibleAnnotations == null) ? 0 : invisibleAnnotations.size()) + + ((visibleTypeAnnotations == null) ? 0 : visibleTypeAnnotations.size()) + + ((invisibleTypeAnnotations == null) ? 0 : invisibleTypeAnnotations.size()); + + } + + final protected void printAnnotations(String initialTab) { + if( getAnnotationsCount() > 0 ) { + if (visibleAnnotations != null) { + for (AnnotationData visad : visibleAnnotations) { + // out.print(initialTab); + visad.print(out, initialTab); + out.println(); + } + } + if (invisibleAnnotations != null) { + for (AnnotationData invisad : invisibleAnnotations) { + invisad.print(out, initialTab); + out.println(); + } + } + + if (visibleTypeAnnotations != null) { + for (TypeAnnotationData visad : visibleTypeAnnotations) { + visad.print(out, initialTab); + out.println(); + } + } + if (invisibleTypeAnnotations != null) { + for (TypeAnnotationData invisad : invisibleTypeAnnotations) { + invisad.print(out, initialTab); + out.println(); + } + } + } + } + + protected void printVar(StringBuilder bodyPrefix, StringBuilder tailPrefix, int name_cpx, int type_cpx) { + if( pr_cpx ) { + bodyPrefix.append('#').append(name_cpx).append(":#").append(type_cpx); + tailPrefix.append(";\t // ").append(cls.pool.getName(name_cpx)).append(':').append(cls.pool.getName(type_cpx)); + + } else { + bodyPrefix.append(cls.pool.getName(name_cpx)).append(':').append(cls.pool.getName(type_cpx)); + tailPrefix.append(';'); + } + + if (signature != null) { + signature.print(bodyPrefix.append(':').toString(), tailPrefix.append( pr_cpx ? ":" : "" ).toString()); + } else { + out.print(bodyPrefix); + out.print(tailPrefix); + } + out.println(); + } + + protected void readAttributes(DataInputStream in) throws IOException { + // Read the Attributes + int natt = in.readUnsignedShort(); + attrs = new ArrayList<>(natt); + TraceUtils.traceln(format("%s - Attributes[%d]", memberType , natt)); + AttrData attr; + for (int k = 0; k < natt; k++) { + int name_cpx = in.readUnsignedShort(); + attr = new AttrData(cls); + attrs.add(attr); + String attr_name = cls.pool.getString(name_cpx); + TraceUtils.traceln(format(" #%d name[%d]=\"%s\"", k, name_cpx, attr_name)); + Tables.AttrTag tag = Tables.attrtag(attr_name); + int attrlen = in.readInt(); + switch (tag) { + case ATT_Synthetic: + // Read Synthetic Attr + if (attrlen != 0) { + throw new ClassFormatError("invalid Synthetic attr length"); + } + isSynthetic = true; + break; + case ATT_Deprecated: + // Read Deprecated Attr + if (attrlen != 0) { + throw new ClassFormatError("invalid Deprecated attr length"); + } + isDeprecated = true; + break; + case ATT_RuntimeVisibleAnnotations: + case ATT_RuntimeInvisibleAnnotations: + // Read Annotations Attr + int cnt = in.readShort(); + ArrayList annots = new ArrayList<>(cnt); + boolean invisible = (tag == Tables.AttrTag.ATT_RuntimeInvisibleAnnotations); + for (int i = 0; i < cnt; i++) { + TraceUtils.traceln(" AnnotationData: #" + i); + AnnotationData annot = new AnnotationData(invisible, cls); + annot.read(in); + annots.add(annot); + } + + if (invisible) { + invisibleAnnotations = annots; + } else { + visibleAnnotations = annots; + } + break; + case ATT_RuntimeVisibleTypeAnnotations: + case ATT_RuntimeInvisibleTypeAnnotations: + // Read Type Annotations Attr + int tcnt = in.readShort(); + ArrayList tannots = new ArrayList<>(tcnt); + boolean tinvisible = (tag == Tables.AttrTag.ATT_RuntimeInvisibleTypeAnnotations); + for (int tindex = 0; tindex < tcnt; tindex++) { + TraceUtils.traceln(" TypeAnnotationData: #" + tindex); + TypeAnnotationData tannot = new TypeAnnotationData(tinvisible, cls); + tannot.read(in); + tannots.add(tannot); + } + + if (tinvisible) { + invisibleTypeAnnotations = tannots; + } else { + visibleTypeAnnotations = tannots; + } + break; + default: + boolean handled = handleAttributes(in, tag, attrlen); + if (!handled) { + attr.read(name_cpx, attrlen, in); + } + break; + + } + } + } +} diff --git a/src/org/openjdk/asmtools/jdis/MethodData.java b/src/org/openjdk/asmtools/jdis/MethodData.java new file mode 100644 index 0000000..e9a995d --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/MethodData.java @@ -0,0 +1,329 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.Modifiers; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; + +import static org.openjdk.asmtools.jasm.JasmTokens.Token; +import static org.openjdk.asmtools.jasm.Tables.AttrTag; +import static org.openjdk.asmtools.jasm.Tables.CF_Context; + +/** + * Method data for method members in a class of the Java Disassembler + */ +public class MethodData extends MemberData { + + /** + * CP index to the method name + */ + protected int name_cpx; + + /** + * CP index to the method type + */ + protected int sig_cpx; + protected String lP; // labelPrefix + /** + * The parameter names for this method + */ + protected ArrayList paramNames; + /** + * The visible parameter annotations for this method + */ + protected ParameterAnnotationData visibleParameterAnnotations; + /** + * The invisible parameter annotations for this method + */ + protected ParameterAnnotationData invisibleParameterAnnotations; + /** + * The invisible parameter annotations for this method + */ + protected AnnotationElement.AnnotValue defaultAnnotation; + /** + * The code data for this method. May be null + */ + private CodeData code; + /** + * The exception table (thrown exceptions) for this method. May be null + */ + private int[] exc_table = null; + + public MethodData(ClassData cls) { + super(cls); + memberType = "MethodData"; + lP = (options.contains(Options.PR.LABS)) ? "L" : ""; + paramNames = null; + } + + /*========================================================*/ + /* Read Methods */ + @Override + protected boolean handleAttributes(DataInputStream in, AttrTag attrtag, int attrlen) throws IOException { + // Read the Attributes + boolean handled = true; + switch (attrtag) { + case ATT_Code: + code = new CodeData(this); + code.read(in, attrlen); + break; + case ATT_Exceptions: + readExceptions(in); + break; + case ATT_MethodParameters: + readMethodParameters(in); + break; + case ATT_RuntimeVisibleParameterAnnotations: + case ATT_RuntimeInvisibleParameterAnnotations: + boolean invisible = (attrtag == AttrTag.ATT_RuntimeInvisibleParameterAnnotations); + ParameterAnnotationData pannots = new ParameterAnnotationData(cls, invisible); + pannots.read(in); + if (invisible) { + invisibleParameterAnnotations = pannots; + } else { + visibleParameterAnnotations = pannots; + } + break; + case ATT_AnnotationDefault: + defaultAnnotation = AnnotationElement.readValue(in, cls, false); + break; + default: + handled = false; + break; + } + return handled; + } + + /** + * read + * read and resolve the method data called from ClassData. + * Precondition: NumFields has already been read from the stream. + */ + public void read(DataInputStream in) throws IOException { + // read the Methods CP indexes + access = in.readUnsignedShort(); // & MM_METHOD; // Q + name_cpx = in.readUnsignedShort(); + sig_cpx = in.readUnsignedShort(); + TraceUtils.traceln(2,"MethodData: {modifiers}: " + Modifiers.toString(access, CF_Context.CTX_METHOD), + " MethodData: name[" + name_cpx + "]=" + cls.pool.getString(name_cpx) + " sig[" + sig_cpx + "]=" + cls.pool.getString(sig_cpx)); + // Read the attributes + readAttributes(in); + } + + private void readExceptions(DataInputStream in) throws IOException { + // this is not really a CodeAttr attribute, it's part of the CodeAttr + int exc_table_len = in.readUnsignedShort(); + TraceUtils.traceln(3,"ExceptionsAttr[" + exc_table_len + "]"); + exc_table = new int[exc_table_len]; + for (int l = 0; l < exc_table_len; l++) { + int exc = in.readShort(); + TraceUtils.traceln(4,"throws:#" + exc); + exc_table[l] = exc; + } + } + + private void readMethodParameters(DataInputStream in) throws IOException { + // this is not really a CodeAttr attribute, it's part of the CodeAttr + int num_params = in.readUnsignedByte(); + TraceUtils.traceln(3,"MethodParametersAttr[" + num_params + "]"); + paramNames = new ArrayList<>(num_params); + for (int l = 0; l < num_params; l++) { + short pname_cpx = (short) in.readUnsignedShort(); + int paccess = in.readUnsignedShort(); + TraceUtils.traceln(4,"P[" + l + "] ={ name[" + pname_cpx + "]: " + cls.pool.getString(pname_cpx) + + " modifiers [" + paccess + "]: " + Modifiers.toString(paccess, CF_Context.CTX_METHOD) + "}"); + paramNames.add(l, new ParamNameData(pname_cpx, paccess)); + } + } + + /** + * printPAnnotations + *

+ * prints the parameter annotations for this method. called from CodeAttr (since JASM + * code integrates the PAnnotation Syntax inside the method body). + */ + // This is called from the CodeAttr + public void printPAnnotations() throws IOException { + int visSize = 0; + int invisSize = 0; + int pNumSize = 0; + + if (visibleParameterAnnotations != null) { + visSize = visibleParameterAnnotations.numParams(); + } + if (invisibleParameterAnnotations != null) { + invisSize = invisibleParameterAnnotations.numParams(); + } + if (paramNames != null) { + pNumSize = paramNames.size(); + } + + int maxParams; + maxParams = (pNumSize > invisSize) ? pNumSize : invisSize; + maxParams = (visSize > maxParams) ? visSize : maxParams; + + for (int paramNum = 0; paramNum < maxParams; paramNum++) { + ArrayList visAnnots = null; + if (visibleParameterAnnotations != null && paramNum < visSize) { + visAnnots = visibleParameterAnnotations.get(paramNum); + } + ArrayList invisAnnots = null; + if (invisibleParameterAnnotations != null && paramNum < invisSize) { + invisAnnots = invisibleParameterAnnotations.get(paramNum); + } + ParamNameData pname = (paramNames == null) ? null : paramNames.get(paramNum); + + boolean nullAnnots = ((visAnnots == null) && (invisAnnots == null)); + if (pname != null && pname.name_cpx == 0) { + pname = null; + } + + // Print the Param number (header) + if ((pname != null) || !nullAnnots) { + out.print("\t" + paramNum + ": "); + } else { + continue; + } + + boolean firstTime = true; + + // Print the Parameter name + if (pname != null) { + out.print(Token.PARAM_NAME.parseKey()); + out.print(Token.LBRACE.parseKey()); + out.print(cls.pool.getString(pname.name_cpx)); + out.print(" "); + out.print(Modifiers.toString(pname.access, CF_Context.CTX_METHOD)); + out.print(Token.RBRACE.parseKey()); + out.print(" "); + } + + // Print any visible param annotations + if (visAnnots != null) { + for (AnnotationData annot : visAnnots) { + if (!firstTime) { + out.print("\t "); + } + annot.print(out, getIndentString()); +// out.println(); + firstTime = false; + } + } + + // Print any invisible param annotations + if (invisAnnots != null) { + for (AnnotationData annot : invisAnnots) { + if (!firstTime) { + out.print("\t "); + } + annot.print(out, getIndentString()); +// out.println(); + firstTime = false; + } + } + + // Reset the line, if there were parameters + if ((pname != null) || !nullAnnots) { + out.println(); + } + + } + + } + + /** + * Prints the method data to the current output stream. called from ClassData. + */ + @Override + public void print() throws IOException { + + printAnnotations(getIndentString()); + + out.print(getIndentString() + Modifiers.accessString(access, CF_Context.CTX_METHOD)); + + if (isSynthetic) { + out.print(Token.SYNTHETIC.parseKey() + " "); + } + if (isDeprecated) { + out.print(Token.DEPRECATED.parseKey() + " "); + } + out.print(Token.METHODREF.parseKey() + " "); + + if (pr_cpx) { + // print the CPX method descriptor + out.print("#" + name_cpx + ":#" + sig_cpx + + ((code == null && exc_table == null && defaultAnnotation == null) ? ";" : "") + + "\t // " + cls.pool.getName(name_cpx) + ":" + cls.pool.getName(sig_cpx)); + } else { + out.print(cls.pool.getName(name_cpx) + ":" + cls.pool.getName(sig_cpx) + + ((code == null && exc_table == null && defaultAnnotation == null) ? ";" : "")); + } + // followed by default annotation + if (defaultAnnotation != null) { + out.print(" default { "); + defaultAnnotation.print(out, getIndentString()); + out.print(" }" + ((code == null && exc_table == null) ? ";" : " ")); + } + // followed by exception table + printExceptionTable(); + + if (code != null) { + code.print(); + } else { + if( exc_table != null ) { + out.print(';'); + } + out.println(); + } + } + + private void printExceptionTable() { + if (exc_table != null) { + out.print("\n\tthrows "); + int len = exc_table.length; + for (int exceptNum = 0; exceptNum < len; exceptNum++) { + out.print(cls.pool.getClassName(exc_table[exceptNum])); + if (exceptNum < len - 1) { + out.print(", "); + } + } + } + } + + /** + * MethodParamData + */ + class ParamNameData { + + public int access; + public int name_cpx; + + public ParamNameData(int name, int access) { + this.access = access; + this.name_cpx = name; + } + } +} // end MethodData diff --git a/src/org/openjdk/asmtools/jdis/ModuleData.java b/src/org/openjdk/asmtools/jdis/ModuleData.java new file mode 100644 index 0000000..a164b8f --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/ModuleData.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2016, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.*; + +import org.openjdk.asmtools.common.Module; +import org.openjdk.asmtools.common.Tool; +import org.openjdk.asmtools.jasm.JasmTokens; + +import static org.openjdk.asmtools.jdis.Main.i18n; + +/** + * The module attribute data. + */ +public class ModuleData { + + // internal references + private final Tool tool; + + private ConstantPool pool; + private PrintWriter out; + private Module module; + + public ModuleData(ClassData clsData) { + this.tool = clsData.tool; + this.pool = clsData.pool; + this.out = clsData.out; + } + + public String getModuleName() { + return module == null ? "N/A" : module.getModuleName(); + } + + public String getModuleVersion() { return module.getModuleVersion(); } + + public String getModuleHeader() { + if ( module == null ) { + return "N/A"; + } else { + StringBuilder sb = new StringBuilder(module.getModuleFlags()); + sb.append(JasmTokens.Token.MODULE.parseKey()).append(" "); + sb.append(module.getModuleName()); + if (module.getModuleVersion() != null) + sb.append("// @").append(module.getModuleVersion()); + return sb.toString(); + } + } + + /** + * Reads and resolve the method's attribute data called from ClassData. + */ + public void read(DataInputStream in) throws IOException { + int index, moduleFlags, versionIndex; + String moduleName, version; + Module.Builder builder; + try { + // u2 module_name_index; + index = in.readUnsignedShort(); + moduleName = pool.getModule(index); + // u2 module_flags; + moduleFlags = in.readUnsignedShort(); + // u2 module_version_index; + versionIndex = in.readUnsignedShort(); + version = pool.getString(versionIndex); + builder = new Module.Builder(moduleName, moduleFlags, version); + } catch (IOException ioe) { + tool.error(i18n.getString("jdis.error.invalid_header")); + throw ioe; + } + + try { + int requires_count = in.readUnsignedShort(); + for (int i = 0; i < requires_count; i++) { + index = in.readUnsignedShort(); + int requiresFlags = in.readUnsignedShort(); + versionIndex = in.readUnsignedShort(); + + moduleName = pool.getModule(index); + version = pool.getString(versionIndex); + builder.require(moduleName, requiresFlags, version); + } + } catch (IOException ioe) { + tool.error(i18n.getString("jdis.error.invalid_requires")); + throw ioe; + } + + try { + int exports_count = in.readUnsignedShort(); + if (exports_count > 0) { + for (int i = 0; i < exports_count; i++) { + index = in.readUnsignedShort(); + String packageName = pool.getPackage(index); + int exportsFlags = in.readUnsignedShort(); + int exports_to_count = in.readUnsignedShort(); + if (exports_to_count > 0) { + Set targets = new HashSet<>(exports_to_count); + for (int j = 0; j < exports_to_count; j++) { + int exports_to_index = in.readUnsignedShort(); + targets.add(pool.getModule(exports_to_index)); + } + builder.exports(packageName, exportsFlags, targets); + } else { + builder.exports(packageName, exportsFlags); + } + } + } + } catch (IOException ioe) { + tool.error(i18n.getString("jdis.error.invalid_exports")); + throw ioe; + } + + try { + int opens_count = in.readUnsignedShort(); + if (opens_count > 0) { + for (int i = 0; i < opens_count; i++) { + index = in.readUnsignedShort(); + String packageName = pool.getPackage(index); + int opensFlags = in.readUnsignedShort(); + int opens_to_count = in.readUnsignedShort(); + if (opens_to_count > 0) { + Set targets = new HashSet<>(opens_to_count); + for (int j = 0; j < opens_to_count; j++) { + int opens_to_index = in.readUnsignedShort(); + targets.add(pool.getModule(opens_to_index)); + } + builder.opens(packageName, opensFlags, targets); + } else { + builder.opens(packageName, opensFlags); + } + } + } + } catch (IOException ioe) { + tool.error(i18n.getString("jdis.error.invalid_opens")); + throw ioe; + } + + try { + int uses_count = in.readUnsignedShort(); + if (uses_count > 0) { + for (int i = 0; i < uses_count; i++) { + index = in.readUnsignedShort(); + String serviceName = pool.getClassName(index); + builder.uses(serviceName); + } + } + } catch (IOException ioe) { + tool.error(i18n.getString("jdis.error.invalid_uses")); + throw ioe; + } + + try { + int provides_count = in.readUnsignedShort(); + if (provides_count > 0) { + for (int i = 0; i < provides_count; i++) { + index = in.readUnsignedShort(); + String serviceName = pool.getClassName(index); + int provides_with_count = in.readUnsignedShort(); + Set implNames = new HashSet<>(provides_with_count); + for (int j = 0; j < provides_with_count; j++) { + int provides_with_index = in.readUnsignedShort(); + implNames.add(pool.getClassName(provides_with_index)); + } + builder.provides(serviceName, implNames); + } + } + } catch (IOException ioe) { + tool.error(i18n.getString("jdis.error.invalid_provides")); + throw ioe; + } + module = builder.build(); + } + + /* Print Methods */ + public void print() { + if (module != null) + out.println(module.toString()); + } +} diff --git a/src/org/openjdk/asmtools/jdis/NestHostData.java b/src/org/openjdk/asmtools/jdis/NestHostData.java new file mode 100644 index 0000000..b47e857 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/NestHostData.java @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + + +import org.openjdk.asmtools.jasm.JasmTokens; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * The NestHost attribute data + *

+ * since class file 55.0 (JEP 181) + */ +public class NestHostData extends Indenter{ + ClassData cls; + int host_class_index; + private Options options = Options.OptionObject(); + + public NestHostData(ClassData cls) { + this.cls = cls; + } + + public NestHostData read(DataInputStream in, int attribute_length) throws IOException, ClassFormatError { + if (attribute_length != 2) { + throw new ClassFormatError("ATT_NestHost: Invalid attribute length"); + } + host_class_index = in.readUnsignedShort(); + return this; + } + + public void print() { + boolean pr_cpx = options.contains(Options.PR.CPX); + cls.out.print(getIndentString() + JasmTokens.Token.NESTHOST.parseKey() + " "); + if (pr_cpx) { + cls.out.print("#" + host_class_index + "; //"); + } + if (pr_cpx) { + cls.pool.PrintConstant(cls.out, host_class_index); + cls.out.println(); + } else { + cls.out.println(cls.pool.StringValue(host_class_index) + ";"); + } + } +} diff --git a/src/org/openjdk/asmtools/jdis/NestMembersData.java b/src/org/openjdk/asmtools/jdis/NestMembersData.java new file mode 100644 index 0000000..7f4aa3d --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/NestMembersData.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.JasmTokens; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * The NestMembers attribute data + *

+ * JEP 181 (Nest-based Access Control): class file 55.0 + * NestMembers_attribute { + * u2 attribute_name_index; + * u4 attribute_length; + * u2 number_of_classes; + * u2 classes[number_of_classes]; + * } + */ +public class NestMembersData extends ClassArrayData { + public NestMembersData(ClassData cls) { + super(cls, JasmTokens.Token.NESTMEMBERS.parseKey()); + } + + public NestMembersData read(DataInputStream in, int attribute_length) throws IOException, ClassFormatError { + return (NestMembersData) super.read(in, attribute_length); + } +} diff --git a/src/org/openjdk/asmtools/jdis/Options.java b/src/org/openjdk/asmtools/jdis/Options.java new file mode 100644 index 0000000..de3fb7a --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/Options.java @@ -0,0 +1,93 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.util.EnumSet; + +/** + * The singleton class to share global options among jdis classes. + */ +public class Options { + + public static final int BODY_INDENT = 2; + + /* Options Fields */ + private static Options ref; + + public enum PR { + + CP, // print Constant Pool + LNT, // print Line Number table + PC, // print Program Counter - for all instr + LABS, // print Labels (as identifiers) + CPX, // print CP index along with arguments + SRC, // print Source Line as comment + HEX, // print numbers as hexadecimals + VAR, // print local variables declarations + DEBUG; // Debug flag + }; + + static private final EnumSet JASM = EnumSet.of(PR.LABS); // default options + static private final EnumSet CODE = EnumSet.of( + PR.CP, + PR.LNT, + PR.PC, + PR.CPX, + PR.VAR + ); + + static private EnumSet printOptions = JASM; + /*-------------------------------------------------------- */ + + private Options() { + } + + public static Options OptionObject() { + if (ref == null) { + ref = new Options(); + } + return ref; + } + + public void set(PR val) { + printOptions.add(val); + } + + public void setCodeOptions() { + printOptions.addAll(CODE); + } + + public boolean contains(PR val) { + return printOptions.contains(val); + } + + public boolean debug() { + return printOptions.contains(PR.DEBUG); + } + + @Override + public String toString() { + return printOptions.toString(); + } + +} diff --git a/src/org/openjdk/asmtools/jdis/ParameterAnnotationData.java b/src/org/openjdk/asmtools/jdis/ParameterAnnotationData.java new file mode 100644 index 0000000..717b852 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/ParameterAnnotationData.java @@ -0,0 +1,110 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * + */ +public class ParameterAnnotationData { + /*-------------------------------------------------------- */ + /* AnnotData Fields */ + + private boolean invisible = false; + private static final String initialTab = ""; + private ArrayList> array = null; + + private ClassData cls; + /*-------------------------------------------------------- */ + + public ParameterAnnotationData(ClassData cls, boolean invisible) { + this.cls = cls; + this.invisible = invisible; + } + + public int numParams() { + if (array == null) { + return 0; + } + + return array.size(); + } + + public ArrayList get(int i) { + return array.get(i); + } + + public void read(DataInputStream in) throws IOException { + int numParams = in.readByte(); + TraceUtils.traceln(" ParameterAnnotationData[" + numParams + "]"); + array = new ArrayList<>(numParams); + for (int paramNum = 0; paramNum < numParams; paramNum++) { + + int numAnnots = in.readShort(); + TraceUtils.traceln(" Param#[" + paramNum + "]: numAnnots=" + numAnnots); + + if (numAnnots > 0) { + // read annotation + ArrayList p_annots = new ArrayList<>(numAnnots); + for (int annotIndex = 0; annotIndex < numAnnots; annotIndex++) { + AnnotationData annot = new AnnotationData(invisible, cls); + annot.read(in); + p_annots.add(annot); + } + array.add(paramNum, p_annots); + } else { + array.add(paramNum, null); + } + } + } + + // Don't need to do this -- + // we need to print annotations (both vis and invisible) per each param number + public void print(PrintWriter out, String tab) { + if (array != null && array.size() > 0) { + out.println(); + int paramNum = 0; + for (ArrayList p_annot : array) { + if (p_annot != null && p_annot.size() > 0) { + out.print("\t" + paramNum + ": "); + boolean firstTime = true; + for (AnnotationData annot : p_annot) { + if (!firstTime) { + out.print("\t "); + } + annot.print(out, initialTab); + firstTime = false; + } + } + + paramNum += 1; + out.println(); + } + } + } + +} diff --git a/src/org/openjdk/asmtools/jdis/PermittedSubclassesData.java b/src/org/openjdk/asmtools/jdis/PermittedSubclassesData.java new file mode 100644 index 0000000..f53e8bb --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/PermittedSubclassesData.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.JasmTokens; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * The PermittedSubclasses attribute data + *

+ * JEP 360 (Sealed types): class file 59.65535 + * PermittedSubclasses_attribute { + * u2 attribute_name_index; + * u4 attribute_length; + * u2 number_of_classes; + * u2 classes[number_of_classes]; + * } + */ +public class PermittedSubclassesData extends ClassArrayData { + public PermittedSubclassesData(ClassData cls) { + super(cls, JasmTokens.Token.PERMITTEDSUBCLASSES.parseKey()); + } + + public PermittedSubclassesData read(DataInputStream in, int attribute_length) throws IOException, ClassFormatError { + return (PermittedSubclassesData) super.read(in, attribute_length); + } +} diff --git a/src/org/openjdk/asmtools/jdis/RecordData.java b/src/org/openjdk/asmtools/jdis/RecordData.java new file mode 100644 index 0000000..3bcb07e --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/RecordData.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 2019, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.JasmTokens; +import org.openjdk.asmtools.jasm.Tables; + +import java.io.DataInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static java.lang.String.format; +import static org.openjdk.asmtools.jasm.JasmTokens.Token.*; +import static org.openjdk.asmtools.jdis.TraceUtils.traceln; + +/** + * The Record attribute data + *

+ * since class file 58.65535 (JEP 359) + */ +public class RecordData extends Indenter { + + + private final ClassData cls; + private List components; + + public RecordData(ClassData cls) { + this.cls = cls; + } + + public RecordData read(DataInputStream in) throws IOException { + int count = in.readUnsignedShort(); + traceln("components=" + count); + components = new ArrayList<>(count); + for (int i = 0; i < count; i++) { + components.add(new Component(cls).read(in)); + } + return this; + } + + /** + * Prints the record data to the current output stream. called from ClassData. + */ + public void print() throws IOException { + int count = components.size(); + if (count > 0) { + cls.out.println(getIndentString() + RECORD.parseKey() + getIndentString() + LBRACE.parseKey()); + for (int i = 0; i < count; i++) { + Component cn = components.get(i); + cn.setIndent(indent() * 2); + if (i != 0 && cn.getAnnotationsCount() > 0) + cn.out.println(); + cn.print(); + } + cls.out.println(getIndentString() + RBRACE.parseKey()); + cls.out.println(); + } + } + + private class Component extends MemberData { + // CP index to the name + private int name_cpx; + // CP index to the type descriptor + private int type_cpx; + + public Component(ClassData cls) { + super(cls); + memberType = "RecordData"; + } + + @Override + protected boolean handleAttributes(DataInputStream in, Tables.AttrTag attrtag, int attrlen) throws IOException { + // Read the Attributes + boolean handled = true; + switch (attrtag) { + case ATT_Signature: + if( signature != null ) { + traceln("Record attribute: more than one attribute Signature are in component.attribute_info_attributes[attribute_count]"); + traceln("Last one will be used."); + } + signature = new SignatureData(cls).read(in, attrlen); + break; + default: + handled = false; + break; + } + return handled; + } + + /** + * Read and resolve the component data called from ClassData. + */ + public Component read(DataInputStream in) throws IOException { + // read the Component CP indexes + name_cpx = in.readUnsignedShort(); + type_cpx = in.readUnsignedShort(); + traceln(2, "RecordComponent: name[" + name_cpx + "]=" + cls.pool.getString(name_cpx) + + " descriptor[" + type_cpx + "]=" + cls.pool.getString(type_cpx)); + // Read the attributes + readAttributes(in); + return this; + } + + /** + * Prints the component data to the current output stream. called from RecordData. + */ + public void print() throws IOException { + // print component's attributes + super.printAnnotations(getIndentString()); + // print component + StringBuilder bodyPrefix = new StringBuilder(getIndentString()); + StringBuilder tailPrefix = new StringBuilder(); + if (isSynthetic) { + bodyPrefix.append(JasmTokens.Token.SYNTHETIC.parseKey()).append(' '); + } + if (isDeprecated) { + bodyPrefix.append(JasmTokens.Token.DEPRECATED.parseKey()).append(' '); + } + // component + bodyPrefix.append(JasmTokens.Token.COMPONENT.parseKey()).append(' '); + + printVar(bodyPrefix, tailPrefix,name_cpx, type_cpx); + } + } +} diff --git a/src/org/openjdk/asmtools/jdis/SignatureData.java b/src/org/openjdk/asmtools/jdis/SignatureData.java new file mode 100644 index 0000000..2d93713 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/SignatureData.java @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2018, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + + +import org.openjdk.asmtools.jasm.Tables; + +import java.io.DataInputStream; +import java.io.IOException; + +import static java.lang.String.format; + +/** + * The Signature attribute data + *

+ * since class file 49.0 + */ +public class SignatureData { + ClassData cls; + int signature_index; + private Options options = Options.OptionObject(); + + public SignatureData(ClassData cls) { + this.cls = cls; + } + + public SignatureData read(DataInputStream in, int attribute_length) throws IOException, ClassFormatError { + if (attribute_length != 2) { + throw new ClassFormatError(format("%s: Invalid attribute length #%d", Tables.AttrTag.ATT_Signature.printval(), attribute_length)); + } + signature_index = in.readUnsignedShort(); + return this; + } + + public void print(String bodyPrefix, String commentPrefix) { + boolean pr_cpx = options.contains(Options.PR.CPX); + if (pr_cpx) { + cls.out.print(format("%s#%d%s%s", bodyPrefix, signature_index, commentPrefix, cls.pool.StringValue(signature_index))); + } else { + cls.out.print(format("%s%s%s", bodyPrefix, cls.pool.getName(signature_index), commentPrefix)); + } + } + + @Override + public String toString() { + return format("signature[%d]=%s", signature_index, cls.pool.StringValue(signature_index)); + } +} diff --git a/src/org/openjdk/asmtools/jdis/StackMapData.java b/src/org/openjdk/asmtools/jdis/StackMapData.java new file mode 100644 index 0000000..2d3b9e9 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/StackMapData.java @@ -0,0 +1,146 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import static java.lang.String.format; +import static org.openjdk.asmtools.jasm.Tables.*; +import static org.openjdk.asmtools.jdis.TraceUtils.mapToHexString; +import static org.openjdk.asmtools.jdis.TraceUtils.traceln; + +import java.io.DataInputStream; +import java.io.EOFException; +import java.io.IOException; + +/** + * represents one entry of StackMap attribute + */ +class StackMapData { + static int prevFramePC = 0; + boolean isStackMapTable = false; + StackMapFrameType stackFrameType = null; + int start_pc; + int[] lockMap; + int[] stackMap; + + public StackMapData(CodeData code, DataInputStream in) throws IOException { + start_pc = in.readUnsignedShort(); + lockMap = readMap(code, in); + stackMap = readMap(code, in); + traceln(2, format("stack_map_entry:pc=%d numloc=%s numstack=%s", + start_pc, mapToHexString(lockMap), mapToHexString(stackMap))); + } + + public StackMapData(CodeData code, DataInputStream in, + boolean isStackMapTable) throws IOException { + this.isStackMapTable = isStackMapTable; + int ft_val = in.readUnsignedByte(); + StackMapFrameType frame_type = stackMapFrameType(ft_val); + int offset = 0; + switch (frame_type) { + case SAME_FRAME: + // type is same_frame; + offset = ft_val; + traceln(2, format("same_frame=%d", ft_val)); + break; + case SAME_FRAME_EX: + // type is same_frame_extended; + offset = in.readUnsignedShort(); + traceln(2, format("same_frame_extended=%d, offset=%d", ft_val, offset)); + break; + case SAME_LOCALS_1_STACK_ITEM_FRAME: + // type is same_locals_1_stack_item_frame + offset = ft_val - 64; + stackMap = readMapElements(code, in, 1); + traceln(2, format("same_locals_1_stack_item_frame=%d, offset=%d, numstack=%s", + ft_val, offset, mapToHexString(stackMap))); + break; + case SAME_LOCALS_1_STACK_ITEM_EXTENDED_FRAME: + // type is same_locals_1_stack_item_frame_extended + offset = in.readUnsignedShort(); + stackMap = readMapElements(code, in, 1); + traceln(2, format("same_locals_1_stack_item_frame_extended=%d, offset=%d, numstack=%s", + ft_val, offset, mapToHexString(stackMap))); + break; + case CHOP_1_FRAME: + case CHOP_2_FRAME: + case CHOP_3_FRAME: + // type is chop_frame + offset = in.readUnsignedShort(); + traceln(2, format("chop_frame=%d offset=%d", ft_val, offset)); + break; + case APPEND_FRAME: + // type is append_frame + offset = in.readUnsignedShort(); + lockMap = readMapElements(code, in, ft_val - 251); + traceln(2, format("append_frame=%d offset=%d numlock=%s", + ft_val, offset, mapToHexString(lockMap))); + break; + case FULL_FRAME: + // type is full_frame + offset = in.readUnsignedShort(); + lockMap = readMap(code, in); + stackMap = readMap(code, in); + traceln(2, format("full_frame=%d offset=%d numloc=%s numstack=%s", + ft_val, offset, mapToHexString(lockMap), mapToHexString(stackMap))); + break; + default: + TraceUtils.traceln("incorrect frame_type argument"); + + } + stackFrameType = frame_type; + start_pc = prevFramePC == 0 ? offset : prevFramePC + offset + 1; + prevFramePC = start_pc; + } + + private int[] readMap(CodeData code, DataInputStream in) throws IOException { + int num = in.readUnsignedShort(); + return readMapElements(code, in, num); + } + + private int[] readMapElements(CodeData code, DataInputStream in, int num) throws IOException { + int[] map = new int[num]; + for (int k = 0; k < num; k++) { + int mt_val = 0; + try { + mt_val = in.readUnsignedByte(); + } catch (EOFException eofe) { + throw eofe; + } + StackMapType maptype = stackMapType(mt_val, null); + switch (maptype) { + case ITEM_Object: + mt_val = mt_val | (in.readUnsignedShort() << 8); + break; + case ITEM_NewObject: { + int pc = in.readUnsignedShort(); + code.get_iAtt(pc).referred = true; + mt_val = mt_val | (pc << 8); + break; + } + } + map[k] = mt_val; + } + return map; + } + +} diff --git a/src/org/openjdk/asmtools/jdis/TextLines.java b/src/org/openjdk/asmtools/jdis/TextLines.java new file mode 100644 index 0000000..36de99a --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/TextLines.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; + +/** + * A container for the java sources tied to an jasm output when -sl in on + */ +public class TextLines { + + final Path file; + List lines; + + public TextLines(Path directory, String sourceFileName) { + file = directory == null ? Paths.get(sourceFileName) : directory.resolve(sourceFileName); + try { + lines = Files.readAllLines(file, StandardCharsets.UTF_8); + } catch (IOException ignore) {} + } + + public String getLine(int index) { + if( lines != null ) { + if (index < 1 || index >= lines.size()) { + return String.format("Line number %d is out of range in \"%s\"", index, file); + } + return lines.get(index - 1); + } + return String.format("\"%s\" not found", file); + } +} diff --git a/src/org/openjdk/asmtools/jdis/TraceUtils.java b/src/org/openjdk/asmtools/jdis/TraceUtils.java new file mode 100644 index 0000000..3d3ec3d --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/TraceUtils.java @@ -0,0 +1,99 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.asmutils.HexUtils; + +import java.util.Arrays; +import java.util.stream.Collectors; + +import static java.lang.String.format; + +public class TraceUtils { + + public static String prefixString = "\t"; + + public static void trace(String s) { + if (!(Options.OptionObject()).debug()) { + return; + } + System.out.print(s); + } + + public static void trace(int prefixLength, String s) { + if (!(Options.OptionObject()).debug()) { + return; + } + System.out.print((prefixLength > 0) ? new String(new char[prefixLength]).replace("\0", prefixString) + s : s); + } + + public static void traceln(String s) { + if (!(Options.OptionObject()).debug()) { + return; + } + System.out.println(s); + } + + public static void traceln(String... lines) { + if (!(Options.OptionObject()).debug()) { + return; + } + + if (lines.length == 0) { + System.out.println(); + } else { + for (String s : lines) { + System.out.println(s); + } + } + } + + public static void traceln(int prefixLength, String s) { + if (!(Options.OptionObject()).debug()) { + return; + } + System.out.println((prefixLength > 0) ? new String(new char[prefixLength]).replace("\0", prefixString) + s : s); + } + + public static void traceln(int prefixLength, String... lines) { + if (!(Options.OptionObject()).debug()) { + return; + } + if (lines.length == 0) { + System.out.println(); + } else { + String prefix = (prefixLength > 0) ? new String(new char[prefixLength]).replace("\0", prefixString) : ""; + for (String s : lines) { + System.out.println(prefix + s); + } + } + } + + public static String mapToHexString(int[] array) { + return format("%d %s", + array.length, + Arrays.stream(array). + mapToObj(val -> HexUtils.toHex(val)). + collect(Collectors.joining(" "))); + } +} diff --git a/src/org/openjdk/asmtools/jdis/TrapData.java b/src/org/openjdk/asmtools/jdis/TrapData.java new file mode 100644 index 0000000..fd3691e --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/TrapData.java @@ -0,0 +1,55 @@ +/* + * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.DataInputStream; +import java.io.IOException; + +/** + * Exception table entry (JVMS 4.7.3 The Code Attribute) describes one exception handler + * in the code array {@link CodeData}. + */ +class TrapData { + + int num; + + // exception_table + int start_pc, // u2 + end_pc, // u2 + handler_pc, // u2 + catch_cpx; // u2 + + public TrapData(DataInputStream in, int num) throws IOException { + this.num = num; + start_pc = in.readUnsignedShort(); + end_pc = in.readUnsignedShort(); + handler_pc = in.readUnsignedShort(); + catch_cpx = in.readUnsignedShort(); + } + + /* returns recommended identifier + */ + public String ident() { + return "t" + num; + } +} diff --git a/src/org/openjdk/asmtools/jdis/TypeAnnotationData.java b/src/org/openjdk/asmtools/jdis/TypeAnnotationData.java new file mode 100644 index 0000000..cab4e50 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/TypeAnnotationData.java @@ -0,0 +1,283 @@ +/* + * Copyright (c) 1996, 2020, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.TypeAnnotationTargetInfoData; +import org.openjdk.asmtools.jasm.TypeAnnotationTypePathData; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.PrintWriter; + +import static org.openjdk.asmtools.jasm.TypeAnnotationTargetInfoData.*; +import static org.openjdk.asmtools.jasm.TypeAnnotationTypes.*; + +/** + * Type Annotation data is a specific kind of AnnotationData. As well as the normal data + * items needed to present an annotation, Type annotations require a TargetInfo + * descriptor. This descriptor is based on a TargetType, and it optionally may contain a + * location descriptor (when the Type is embedded in a collection). + *

+ * The TypeAnnotationData class is based on JDis's AnnotationData class, and contains the + * (jasm) class for representing TargetInfo. + */ +public class TypeAnnotationData extends AnnotationData { + + private static TTVis TT_Visitor = new TTVis(); + private TypeAnnotationTargetInfoData targetInfo; + private TypeAnnotationTypePathData typePath; + + public TypeAnnotationData(boolean invisible, ClassData cls) { + super(invisible, cls); + targetInfo = null; + typePath = new TypeAnnotationTypePathData(); + visAnnotToken = "@T+"; + invAnnotToken = "@T-"; + dataName = "TypeAnnotationData"; + } + + @Override + public void read(DataInputStream in) throws IOException { + + int ttype = in.readUnsignedByte(); + ETargetType targetType = ETargetType.getTargetType(ttype); + + if (targetType == null) { + // Throw some kind of error for bad target type index + throw new IOException("Bad target type: " + ttype + " in TypeAnnotationData"); + } + + // read the target info + TT_Visitor.init(in); + TT_Visitor.visitExcept(targetType); + targetInfo = TT_Visitor.getTargetInfo(); + + // read the target path info + int len = in.readUnsignedByte(); + TraceUtils.traceln(4,"[TypeAnnotationData.read]: Reading Location (length = " + len + ")."); + TraceUtils.trace(4,"[TypeAnnotationData.read]: [ "); + for (int i = 0; i < len; i++) { + int pathType = in.readUnsignedByte(); + String pk = (getPathKind(pathType)).parseKey(); + char pathArgIndex = (char) in.readUnsignedByte(); + typePath.addTypePathEntry(new TypePathEntry(pathType, pathArgIndex)); + TraceUtils.trace(" " + pk + "(" + pathType + "," + pathArgIndex + "), "); + } + TraceUtils.traceln("] "); + super.read(in); + } + + @Override + protected void printBody(PrintWriter out, String tab) { + // For a type annotation, print out brackets, + // print out the (regular) annotation name/value pairs, + // then print out the target types. + out.print(" {"); + super.printBody(out, ""); + targetInfo.print(out, tab); + typePath.print(out, tab); + out.print(tab + "}"); + } + + /** + * TTVis + *

+ * Target Type visitor, used for constructing the target-info within a type + * annotation. visitExcept() is the entry point. ti is the constructed target info. + */ + private static class TTVis extends TypeAnnotationTargetVisitor { + + private TypeAnnotationTargetInfoData targetInfo = null; + private IOException IOProb = null; + private DataInputStream in; + + public void init(DataInputStream in) { + this.in = in; + } + + public int scanByteVal() { + int val = 0; + try { + val = in.readUnsignedByte(); + } catch (IOException e) { + IOProb = e; + } + return val; + } + + public int scanShortVal() { + int val = 0; + try { + val = in.readUnsignedShort(); + } catch (IOException e) { + IOProb = e; + } + return val; + } + + //This is the entry point for a visitor that tunnels exceptions + public void visitExcept(ETargetType tt) throws IOException { + IOProb = null; + targetInfo = null; + + TraceUtils.traceln(4,"Target Type: " + tt.parseKey()); + visit(tt); + + if (IOProb != null) { + throw IOProb; + } + } + + public TypeAnnotationTargetInfoData getTargetInfo() { + return targetInfo; + } + + private boolean error() { + return IOProb != null; + } + + @Override + public void visit_type_param_target(ETargetType tt) { + TraceUtils.trace(4,"Type Param Target: "); + int byteval = scanByteVal(); // param index + TraceUtils.traceln("{ param_index: " + byteval + "}"); + if (!error()) { + targetInfo = new type_parameter_target(tt, byteval); + } + } + + @Override + public void visit_supertype_target(ETargetType tt) { + TraceUtils.trace(4,"SuperType Target: "); + int shortval = scanShortVal(); // type index + TraceUtils.traceln("{ type_index: " + shortval + "}"); + if (!error()) { + targetInfo = new supertype_target(tt, shortval); + } + } + + @Override + public void visit_typeparam_bound_target(ETargetType tt) { + TraceUtils.trace(4,"TypeParam Bound Target: "); + int byteval1 = scanByteVal(); // param index + if (error()) { + return; + } + int byteval2 = scanByteVal(); // bound index + if (error()) { + return; + } + TraceUtils.traceln("{ param_index: " + byteval1 + " bound_index: " + byteval2 + "}"); + targetInfo = new type_parameter_bound_target(tt, byteval1, byteval2); + } + + @Override + public void visit_empty_target(ETargetType tt) { + TraceUtils.traceln(4,"Empty Target: "); + if (!error()) { + targetInfo = new empty_target(tt); + } + } + + @Override + public void visit_methodformalparam_target(ETargetType tt) { + TraceUtils.trace(4,"MethodFormalParam Target: "); + int byteval = scanByteVal(); // param index + TraceUtils.traceln("{ param_index: " + byteval + "}"); + if (!error()) { + targetInfo = new formal_parameter_target(tt, byteval); + } + } + + @Override + public void visit_throws_target(ETargetType tt) { + TraceUtils.trace(4,"Throws Target: "); + int shortval = scanShortVal(); // exception index + TraceUtils.traceln("{ exception_index: " + shortval + "}"); + if (!error()) { + targetInfo = new throws_target(tt, shortval); + } + } + + @Override + public void visit_localvar_target(ETargetType tt) { + TraceUtils.traceln(4,"LocalVar Target: "); + int tblsize = scanShortVal(); // table length (short) + if (error()) { + return; + } + localvar_target locvartab = new localvar_target(tt, tblsize); + targetInfo = locvartab; + + for (int i = 0; i < tblsize; i++) { + int shortval1 = scanShortVal(); // startPC + if (error()) { + return; + } + int shortval2 = scanShortVal(); // length + if (error()) { + return; + } + int shortval3 = scanShortVal(); // CPX + TraceUtils.trace(4,"LocalVar[" + i + "]: "); + TraceUtils.traceln("{ startPC: " + shortval1 + ", length: " + shortval2 + ", CPX: " + shortval3 + "}"); + locvartab.addEntry(shortval1, shortval2, shortval3); + } + } + + @Override + public void visit_catch_target(ETargetType tt) { + TraceUtils.trace(4,"Catch Target: "); + int shortval = scanShortVal(); // catch index + TraceUtils.traceln("{ catch_index: " + shortval + "}"); + if (!error()) { + targetInfo = new catch_target(tt, shortval); + } + } + + @Override + public void visit_offset_target(ETargetType tt) { + TraceUtils.trace(4,"Offset Target: "); + int shortval = scanShortVal(); // offset index + TraceUtils.traceln("{ offset_index: " + shortval + "}"); + if (!error()) { + targetInfo = new offset_target(tt, shortval); + } + } + + @Override + public void visit_typearg_target(ETargetType tt) { + TraceUtils.trace(4,"TypeArg Target: "); + int shortval = scanShortVal(); // offset + if (error()) { + return; + } + int byteval = scanByteVal(); // type index + if (error()) { + return; + } + TraceUtils.traceln("{ offset: " + shortval + " type_index: " + byteval + "}"); + targetInfo = new type_argument_target(tt, shortval, byteval); + } + } +} diff --git a/src/org/openjdk/asmtools/jdis/Utils.java b/src/org/openjdk/asmtools/jdis/Utils.java new file mode 100644 index 0000000..dbbc222 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/Utils.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +/** + * + */ +public class Utils { + + static public String javaName(String name) { + if (name == null) { + return "null"; + } + int len = name.length(); + if (len == 0) { + return "\"\""; + } + char cc = '/'; +fullname: + { // xxx/yyy/zzz + for (int k = 0; k < len; k++) { + char c = name.charAt(k); + if (cc == '/') { + if (!Character.isJavaIdentifierStart(c) && c != '-') { + break fullname; + } + } else if (c != '/') { + if (!Character.isJavaIdentifierPart(c) && c != '-') { + break fullname; + } + } + cc = c; + } + return name; + } + return "\"" + name + "\""; + } + + static public boolean isClassArrayDescriptor(String name) { + boolean retval = false; + if (name != null) { + if (name.startsWith("[")) { + retval = true; + } + } + + return retval; + } + + static public String commentString(String str) { + return commentString(str,"// "); + } + + static public String commentString(String str, String prefix) { + return prefix + str.replace("\n", "\n" + prefix); + } + +} diff --git a/src/org/openjdk/asmtools/jdis/i18n.properties b/src/org/openjdk/asmtools/jdis/i18n.properties new file mode 100644 index 0000000..95922d3 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/i18n.properties @@ -0,0 +1,47 @@ +# Copyright (c) 2014, 2019 Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +jdis.usage=\ +Usage: java -jar asmtools.jar jdis [options] FILE.class... > FILE.jasm\n\ +where possible options include: + +jdis.opt.g=\ +\ -g detailed output format +jdis.opt.sl=\ +\ -sl source lines in comments +jdis.opt.v=\ +\ -v debug mode +jdis.opt.hx=\ +\ -hx hexadecimal numbers +jdis.opt.version=\ +\ -version prints the version info + +jdis.error.invalid_option=invalid option: {0} +jdis.error.cannot_read=cannot read {0} +jdis.error.fatal_error=fatal error in file: {0} +jdis.error.fatal_exception=fatal exception in file: {0} + +jdis.error.invalid_header=Invalid Module's attributes table +jdis.error.invalid_requires=Invalid requires table +jdis.error.invalid_exports=Invalid exports table +jdis.error.invalid_opens=Invalid opens table +jdis.error.invalid_uses=Invalid uses table +jdis.error.invalid_provides=Invalid provides table diff --git a/src/org/openjdk/asmtools/jdis/iAtt.java b/src/org/openjdk/asmtools/jdis/iAtt.java new file mode 100644 index 0000000..b3bcdb0 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/iAtt.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import org.openjdk.asmtools.jasm.RuntimeConstants; +import static org.openjdk.asmtools.jasm.Tables.*; +import static org.openjdk.asmtools.jasm.OpcodeTables.*; +import org.openjdk.asmtools.jasm.Tables; +import java.io.IOException; +import java.io.PrintWriter; +import java.util.ArrayList; + +/** + * instruction attributes + */ +class iAtt { + + /*-------------------------------------------------------- */ + /* iAtt Fields */ + private Options options; + + short lnum = 0; + boolean referred = false; // from some other instruction + ArrayList vars; + ArrayList endvars; + ArrayList handlers; + ArrayList traps; + ArrayList endtraps; + StackMapData stackMapEntry; + CodeData code; + ClassData cls; + PrintWriter out; // =cls.out; + /*-------------------------------------------------------- */ + + public iAtt(CodeData code) { + this.code = code; + this.cls = code.meth.cls; + out = cls.out; + options = cls.options; + } + + void add_var(CodeData.LocVarData var) { + if (vars == null) { + vars = new ArrayList<>(4); + } + vars.add(var); + } + + void add_endvar(CodeData.LocVarData endvar) { + if (endvars == null) { + endvars = new ArrayList<>(4); + } + endvars.add(endvar); + } + + void add_trap(TrapData trap) { + if (traps == null) { + traps = new ArrayList<>(4); + } + traps.add(trap); + } + + void add_endtrap(TrapData endtrap) { + if (endtraps == null) { + endtraps = new ArrayList<>(4); + } + endtraps.add(endtrap); + } + + void add_handler(TrapData endtrap) { + if (handlers == null) { + handlers = new ArrayList<>(4); + } + handlers.add(endtrap); + } + + public void printEnds() throws IOException { +// prints additional information for instruction: +// end of local variable and trap scopes; + int len; + if ((endvars != null) && (options.contains(Options.PR.VAR))) { + len = endvars.size() - 1; + out.print("\t\tendvar"); + for (CodeData.LocVarData line : endvars) { + out.print(" " + line.slot); + if (len-- > 0) { + out.print(","); + } + } + out.println(";"); + } + + if (endtraps != null) { + len = endtraps.size() - 1; + out.print("\t\tendtry"); + for (TrapData line : endtraps) { + out.print(" " + line.ident()); + if (len-- > 0) { + out.print(","); + } + } + out.println(";"); + } + } + + public void printBegins() + throws IOException { +// prints additional information for instruction: +// source line number; +// start of exception handler; +// begin of locvar and trap scopes; + boolean eitherOpt = options.contains(Options.PR.LNT) || options.contains(Options.PR.SRC); + boolean bothOpt = options.contains(Options.PR.LNT) && options.contains(Options.PR.SRC); + int k; + + if ((lnum != 0) && eitherOpt) { + if (bothOpt) { + out.println("// " + lnum + ": " + cls.getSrcLine(lnum)); + } else if (options.contains(Options.PR.LNT)) { + out.print(lnum); + } else if (options.contains(Options.PR.SRC)) { + out.println("// " + cls.getSrcLine(lnum)); + } + } + out.print("\t"); + if (handlers != null) { + for (TrapData line : handlers) { + out.print("\tcatch " + line.ident()); + out.print(" " + cls.pool.getClassName(line.catch_cpx) + ";\n\t"); + } + } + if (traps != null) { + int len = traps.size() - 1; + out.print("\ttry"); + for (TrapData line : traps) { + out.print(" " + line.ident()); + if (len-- > 0) { + out.print(","); + } + } + out.print(";\n\t"); + } + if ((vars != null) && options.contains(Options.PR.VAR)) { + for (CodeData.LocVarData line : vars) { + out.println("\tvar " + line.slot + "; // " + cls.pool.getName(line.name_cpx) + ":" + cls.pool.getName(line.sig_cpx)); + out.print("\t"); + } + } + } + + public void printMapList(int[] map) throws IOException { + boolean pr_cpx = options.contains(Options.PR.CPX); + + for (int k = 0; k < map.length; k++) { + int fullmaptype = map[k]; + int mt_val = fullmaptype & 0xFF; + StackMapType maptype = stackMapType(mt_val, out); + int argument = fullmaptype >> 8; + switch (maptype) { + case ITEM_Object: + if (pr_cpx) { + out.print(" #" + argument); + } else { + out.print(" "); + cls.pool.PrintConstant(out, argument); + } + break; + case ITEM_NewObject: + if (pr_cpx) { + out.print(" " + mt_val); + } else { + out.print(" " + maptype.printval()); + } + out.print(" " + code.meth.lP + argument); + break; + default: + if (pr_cpx) { + out.print(" " + mt_val); + } else { + out.print(" " + maptype.printval()); + } + } + out.print((k == (map.length - 1) ? ';' : ',')); + } + } + + public void printStackMap() throws IOException { + if (stackMapEntry == null) { + return; + } + boolean printed = false; + if (stackMapEntry.stackFrameType != null) { + out.print(Opcode.opc_stack_frame_type.parsekey()); // opcNamesTab[opc_stackframetype]); + out.print(" " + stackMapEntry.stackFrameType.parsekey() + ';'); + out.print("\n\t\t"); + printed = true; + } + int[] map = stackMapEntry.lockMap; + if ((map != null) && (map.length > 0)) { + out.print(Opcode.opc_locals_map.parsekey()); + printMapList(map); + out.print("\n\t\t"); + printed = true; + } + map = stackMapEntry.stackMap; + if ((map != null) && (map.length > 0)) { + out.print(Opcode.opc_stack_map.parsekey()); + printMapList(map); + out.print("\n\t\t"); + printed = true; + } + if (!printed) { +// empty attribute should be printed anyway - it should not +// be eliminated after jdis/jasm cycle + out.print(Opcode.opc_locals_map.parsekey() + " ;\n\t\t"); + } + } +} diff --git a/src/org/openjdk/asmtools/jdis/uEscWriter.java b/src/org/openjdk/asmtools/jdis/uEscWriter.java new file mode 100644 index 0000000..92d5935 --- /dev/null +++ b/src/org/openjdk/asmtools/jdis/uEscWriter.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1996, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.jdis; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.Writer; + +/** + * + */ +public class uEscWriter extends Writer { + /*-------------------------------------------------------- */ + /* uEscWriter Fields */ + + static final char[] hexTable = "0123456789ABCDEF".toCharArray(); + OutputStream out; + byte[] tmpl; + /*-------------------------------------------------------- */ + + public uEscWriter(OutputStream out) { + this.out = out; + tmpl = new byte[6]; + tmpl[0] = (byte) '\\'; + tmpl[1] = (byte) 'u'; + } + + @Override + public synchronized void write(int c) throws IOException { + if (c < 128) { + out.write(c); + return; + } + // write \udddd + byte[] tmpll = tmpl; + for (int k = 3; k >= 0; k--) { + tmpll[5 - k] = (byte) hexTable[(c >> 4 * k) & 0xF]; + } + out.write(tmpll, 0, 6); + } + + @Override + public synchronized void write(char[] cc, int ofs, int len) throws IOException { + for (int k = ofs; k < len; k++) { + write(cc[k]); + } + } + + @Override + public void flush() { + } + + @Override + public void close() { + } +} // end uEscWriter + diff --git a/src/org/openjdk/asmtools/util/I18NResourceBundle.java b/src/org/openjdk/asmtools/util/I18NResourceBundle.java new file mode 100644 index 0000000..3350b6a --- /dev/null +++ b/src/org/openjdk/asmtools/util/I18NResourceBundle.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.util; + +import java.text.MessageFormat; +import java.util.Enumeration; +import java.util.Locale; +import java.util.MissingResourceException; +import java.util.ResourceBundle; + +/** + * A class that lazily opens a package-specific resource bundle containing localization + * data for a class. + */ +public class I18NResourceBundle extends ResourceBundle { + + /** + * Get a package-specific resource bundle for a class containing localization data. + * The bundle is named i18n.properties in the same package as the given class. + * + * @param c the class for which to obtain the resource bundle + * @return the appropriate resource bundle for the class + */ + public static I18NResourceBundle getBundleForClass(Class c) { + String cn = c.getName(); + int dot = cn.lastIndexOf('.'); + String rn = (dot == -1 ? "i18n" : cn.substring(0, dot) + ".i18n"); + return new I18NResourceBundle(rn, c.getClassLoader()); + } + + /** + * Create a resource bundle for the given name. The actual resource bundle will not be + * loaded until it is needed. + * + * @param name The name of the actual resource bundle to use. + */ + private I18NResourceBundle(String name, ClassLoader cl) { + this.name = name; + this.classLoader = cl; + } + + /** + * Get an entry from the resource bundle. If the resource cannot be found, a message + * is printed to the console and the result will be a string containing the method + * parameters. + * + * @param key the name of the entry to be returned + * @param arg an argument to be formatted into the result using + * {@link java.text.MessageFormat#format} + * @return the formatted string + */ + public String getString(String key, Object arg) { + return getString(key, new Object[]{arg}); + } + + /** + * Get an entry from the resource bundle. If the resource cannot be found, a message + * is printed to the console and the result will be a string containing the method + * parameters. + * + * @param key the name of the entry to be returned + * @param args an array of arguments to be formatted into the result using + * {@link java.text.MessageFormat#format} + * @return the formatted string + */ + public String getString(String key, Object... args) { + try { + return MessageFormat.format(getString(key), args); + } catch (MissingResourceException e) { + System.err.println("WARNING: missing resource: " + key + " for " + name); + StringBuffer sb = new StringBuffer(key); + for (int i = 0; i < args.length; i++) { + sb.append('\n'); + sb.append(args.toString()); + } + return sb.toString(); + } + } + + /** + * Get an entry from the bundle, returning null if it is not found. + * + * @param key the name of the entry to be returned + * @return the value of the entry, or null if it is not found. + */ + public String getOptionalString(String key) { + if (delegate == null) { + delegate = ResourceBundle.getBundle(name, Locale.getDefault(), classLoader); + } + try { + String s = (String) (delegate.getObject(key)); + if (s != null) { + System.out.println("i18n: " + key); + } + return s; + } catch (MissingResourceException e) { + return null; + } + } + + /** + * A required internal method for ResourceBundle. Load the actual resource bundle, if + * it has not yet been loaded, then hand the request off to that bundle. If the + * resource cannot be found, a message is printed to the console and the result will + * be the original tag. + */ + protected Object handleGetObject(String key) throws MissingResourceException { + try { + if (delegate == null) { + delegate = ResourceBundle.getBundle(name, Locale.getDefault(), classLoader); + } + return delegate.getObject(key); + } catch (MissingResourceException e) { + System.err.println("WARNING: missing resource: " + key + " for " + name); + return key; + } + } + + /** + * A required internal method for ResourceBundle. Load the actual resource bundle, if + * it has not yet been loaded, then hand the request off to that bundle. + */ + public Enumeration getKeys() { + if (delegate == null) { + delegate = ResourceBundle.getBundle(name); + } + return delegate.getKeys(); + } + + /** + * Returns the name of this bundle (useful for methods using bundle name instead of + * instance, such as Logger creation, + * + * @return the name of this resource bundle + */ + public String getName() { + return name; + } + + private String name; + private ResourceBundle delegate; + private ClassLoader classLoader; +} diff --git a/src/org/openjdk/asmtools/util/ProductInfo.java b/src/org/openjdk/asmtools/util/ProductInfo.java new file mode 100644 index 0000000..77425c2 --- /dev/null +++ b/src/org/openjdk/asmtools/util/ProductInfo.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2009, 2014, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.openjdk.asmtools.util; + +import java.util.ResourceBundle; + +/** + * Class providing an access to the product info. + * productinfo.properties will be generated during the build + */ +public class ProductInfo { + + static { + init(); + } + + ; + + /** + * Returns the value of the specified property + */ + public static String getProperty(String propName) { + try { + return bundle.getString(propName); + } catch (java.util.MissingResourceException e) { + return null; + } + } + + /** + * Version of the product in the short format, like 5.0 + */ + public static final String VERSION = version(); + + /** + * Full version of the product, including build number and date of creation + */ + public static final String FULL_VERSION = fullVersion(); + + private static final String BUNDLE_NAME = "org.openjdk.asmtools.util.productinfo"; + + private static ResourceBundle bundle; + + /** + * Initializes the bundle + */ + private static void init() { + bundle = ResourceBundle.getBundle(BUNDLE_NAME); + } + + private static String version() { + return getProperty("PRODUCT_VERSION"); + } + + private static String fullVersion() { + return getProperty("PRODUCT_NAME_LONG") + ", version " + version() + + " " + getProperty("PRODUCT_MILESTONE") + + " " + getProperty("PRODUCT_BUILDNUMBER") + + " (" + getProperty("PRODUCT_DATE") + ")"; + + } + +} diff --git a/src/org/openjdk/asmtools/util/productinfo.properties b/src/org/openjdk/asmtools/util/productinfo.properties new file mode 100644 index 0000000..16a46d9 --- /dev/null +++ b/src/org/openjdk/asmtools/util/productinfo.properties @@ -0,0 +1,27 @@ +# Copyright (c) 2014 Oracle and/or its affiliates. All rights reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA +# or visit www.oracle.com if you need additional information or have any +# questions. + +PRODUCT_NAME = %%PRODUCT_NAME%% +PRODUCT_VERSION = %%PRODUCT_VERSION%% +PRODUCT_MILESTONE = %%PRODUCT_MILESTONE%% +PRODUCT_BUILDNUMBER = %%PRODUCT_BUILDNUMBER%% +PRODUCT_NAME_LONG = %%PRODUCT_NAME_LONG%% +PRODUCT_DATE = %%BUILD_DATE%%